Browse Source

完成分镜管理等接口

lh 4 months ago
parent
commit
dabb9364b9

+ 32 - 0
app/Http/Controllers/Anime/AnimeController.php

@@ -150,6 +150,38 @@ class AnimeController extends BaseController
         return $this->success(['img_url'=>$result]);
         return $this->success(['img_url'=>$result]);
     }
     }
 
 
+    // 添加分镜
+    public function addSegment(Request $request) {
+        // 忽略所有超时限制
+        set_time_limit(0);
+        ini_set('max_execution_time', '0');
+
+        $data = $request->all();
+        $result = $this->AnimeService->addSegment($data);
+        return $this->success($result);
+    }
+
+    // 复制分镜
+    public function copySegment(Request $request) {
+        $data = $request->all();
+        $result = $this->AnimeService->copySegment($data);
+        return $this->success(['success'=>$result ? 1: 0]);
+    }
+
+    // 移动分镜
+    public function moveSegment(Request $request) {
+        $data = $request->all();
+        $result = $this->AnimeService->moveSegment($data);
+        return $this->success(['success'=>$result ? 1: 0]);
+    }
+
+    // 删除分镜
+    public function delSegment(Request $request) {
+        $data = $request->all();
+        $result = $this->AnimeService->delSegment($data);
+        return $this->success(['success'=>$result ? 1: 0]);
+    }
+
     // public function scriptList(Request $request) {
     // public function scriptList(Request $request) {
     //     $data = $request->all();
     //     $data = $request->all();
     //     $result = $this->AnimeService->scriptList($data);
     //     $result = $this->AnimeService->scriptList($data);

+ 1 - 1
app/Http/Controllers/DeepSeek/DeepSeekController.php

@@ -461,7 +461,7 @@ class DeepSeekController extends BaseController
         ]);
         ]);
     }
     }
 
 
-    // 新建对话
+    // 调整大纲
     public function reGenerateAnime(Request $request) {
     public function reGenerateAnime(Request $request) {
         // 忽略所有超时限制
         // 忽略所有超时限制
         set_time_limit(0);
         set_time_limit(0);

+ 362 - 0
app/Services/Anime/AnimeService.php

@@ -874,6 +874,368 @@ class AnimeService
         return $img_url;
         return $img_url;
     }
     }
 
 
+    public function addSegment($data) {
+        $prev_segment_id = getProp($data, 'prev_segment_id');
+        $segment = DB::table('mp_episode_segments')->where('segment_id', $prev_segment_id)->first();
+        $anime_id = getProp($segment, 'anime_id');
+        $episode_number = getProp($segment, 'episode_number');
+        $segment_number = getProp($segment, 'segment_number');
+        $segment_content = getProp($data, 'segment_content');
+        $img_url = getProp($data, 'img_url');
+        $video_url = getProp($data, 'video_url');
+        $ratio = getProp($data, 'ratio');
+        if (!in_array($ratio, ['1:1', '4:3', '3:2', '16:9', '21:9'])) {
+            Utils::throwError('1002:画面比例选择不正确,只支持1:1、4:3、3:2、16:9、21:9');
+        }
+        
+        if (!$anime_id || !$episode_number) {
+            Utils::throwError('1002:请选择分镜');
+        }
+
+        // 根据画面比例给宽高赋值
+        switch ($ratio) {
+            case '1:1':
+                $width = 2048;
+                $height = 2048;
+                break;
+            case '4:3':
+                $width = 2304;
+                $height = 1728;
+                break;
+            case '3:2':
+                $width = 2496;
+                $height = 1664;
+                break;
+            case '16:9':
+                $width = 2560;
+                $height = 1440;
+                break;
+            case '21:9':
+                $width = 3024;
+                $height = 1296;
+                break;
+            default:
+                $width = 2048;
+                $height = 2048;
+        }
+
+        // 获取动漫的角色信息(包含参考图)
+        $anime = DB::table('mp_animes')
+            ->where('id', $anime_id)
+            ->select('roles', 'scenes')
+            ->first();
+            
+        if (!$anime || !$anime->roles) {
+            Utils::throwError('20003:未找到角色信息');
+        }
+
+        $roles = json_decode($anime->roles, true);
+        if (!$roles) {
+            Utils::throwError('20003:角色信息格式错误');
+        }
+        // 构建角色名称到参考图的映射
+        $role_map = [];
+        foreach ($roles as $role) {
+            $role_name = getProp($role, 'role');
+            $role_url = getProp($role, 'url');
+            if (!empty($role_name) && !empty($role_url)) {
+                $role_map[$role_name] = $role_url;
+            }
+        }
+
+        $scenes = json_decode($anime->scenes, true);
+        if (!$scenes) {
+            Utils::throwError('20003:角色信息格式错误');
+        }
+        // 构建角色名称到参考图的映射
+        $scene_map = [];
+        foreach ($scenes as $scene) {
+            $scene_name = getProp($scene, 'scene');
+            $scene_url = getProp($scene, 'url');
+            if (!empty($scene_name) && !empty($scene_url)) {
+                $scene_map[$scene_name] = $scene_url;
+            }
+        }
+
+        $segment_id = $segment->segment_id;
+        $segment_content = $segment->segment_content;
+        
+        if (empty($segment_content)) {
+            Utils::throwError('20003:分镜内容不存在,请填写内容后重试');
+        }
+
+        try {
+            DB::beginTransaction();
+
+            $new_segment_number = $segment_number + 1;
+            $segment_id = date('YmdHis') . mt_rand(1000, 9999) . str_pad($new_segment_number, 3, "0", STR_PAD_LEFT);
+            // 分镜剧本数组
+            $insert_data = [
+                'anime_id'      => $anime_id,
+                'episode_id'    => getProp($segment, 'episode_id'),
+                'episode_number' => $episode_number,
+                'act_number'    => getProp($segment, 'act_number'),
+                'act_title'     => getProp($segment, 'act_title'),
+                'segment_id'    => $segment_id,
+                'segment_number' => $new_segment_number,
+                'segment_content' => $segment_content,
+                'img_url'       => $img_url,
+                'video_url'     => $video_url,
+                'created_at' => date('Y-m-d H:i:s'),
+                'updated_at' => date('Y-m-d H:i:s')
+            ];
+
+            // 更新分镜剧本信息
+            $this->handleEpisodeContent($insert_data);
+
+            // 如果没有上传图片则使用当前描述进行生成
+            if (!$img_url) {
+                // 解析分镜内容,提取画面描述作为主要提示词
+                $prompt = $segment_content;
+                $ref_img_urls = [];
+                // 分镜场景
+                $scene = getProp($insert_data, 'scene');
+                if ($scene) {
+                    $matched_scene = '';
+                    if (isset($scene_map[$scene])) {
+                        $img_index = count($ref_img_urls) + 1;
+                        $prompt = "场景:$scene(图$img_index)\n$prompt";
+                        $ref_img_urls[] = $scene_map[$scene];
+                        $matched_scene = $scene;
+                    }
+                    $update_data['scene'] = $scene;
+                }
+
+                // 分镜角色
+                $characters = getProp($insert_data, 'characters');
+                if ($characters) {
+                    $update_data['characters'] = $characters;
+                    $characters = str_replace(['、', ',', ';'], ',', $characters);
+                    $characters = explode(',', $characters);
+                    
+                    // 从分镜内容中提取涉及的角色
+                    $segment_characters = $this->extractCharactersFromSegment($segment_content, array_keys($role_map), $characters);
+                    
+                    // 获取匹配角色的参考图
+                    foreach ($segment_characters as $character) {
+                        if (isset($role_map[$character])) {
+                            // 将$prompt中出现的角色替换成角色(图1、2、3,序号是$ref_img_urls的个数加1)
+                            $img_index = count($ref_img_urls) + 1;
+                            $prompt = str_replace($character, $character . '(图' . $img_index . ')', $prompt);
+                            $ref_img_urls[] = $role_map[$character];
+                        }
+                    }
+                }
+                
+                
+                // 如果没有找到匹配的角色参考图,不使用参考图
+                if (empty($ref_img_urls)) {
+                    $ref_img_urls = [];
+                }
+
+                // 准备生成任务参数
+                $params = [
+                    'alias_segment_id' => $segment_id,
+                    'prompt' => $prompt,
+                    'ref_img_urls' => $ref_img_urls,
+                    'width' => $width,
+                    'height' => $height,
+                ];
+                
+                // 调用AI图片生成服务
+                $task = $this->aiImageGenerationService->createImageGenerationTask($params);
+                $task_id = $task->id;
+                // $task_id = 'whatever';
+
+                if (!$task_id) {
+                    Utils::throwError('20003:创建生成任务失败!');
+                }
+                $insert_data['pic_task_status'] = '生成中';
+                $insert_data['pic_task_id'] = $task_id;
+            }
+            
+            // 先更新序号
+            $boolen = DB::table('mp_episode_segments')->where('anime_id', $anime_id)->where('episode_number', $episode_number)->where('segment_number', '>', $segment_number)->increment('segment_number');
+            if (!$boolen) {
+                Utils::throwError('20003:更新序号失败');
+            }
+
+            $boolen2 = DB::table('mp_episode_segments')->insert($insert_data);
+            if (!$boolen2) {
+                Utils::throwError('20003:新增分镜失败!');
+            }
+
+            // 如果是第一集的首帧图片,则存入待更新数组
+            if ($boolen2 && (int)$episode_number === 1 && (int)$new_segment_number === 1) {
+                Redis::sadd('anime_first_frame_urls', $segment_id);
+            }
+            
+        } catch (\Exception $e) {
+            DB::rollBack();
+            Utils::throwError('20003:'.$e->getMessage());
+        }
+
+        DB::commit();
+
+        if (isset($task_id)) $img_url = $this->loopGetPicTaskResult($task_id);
+        if (!$img_url) Utils::throwError('20003:图片生成中,请稍后刷新');
+        $segment = DB::table('mp_episode_segments')->where('segment_id', $segment_id)->first();
+        $segment = $segment ? (array)$segment : [];
+        return $segment;
+    }
+
+    public function copySegment($data) {
+        $segment_id = getProp($data, 'segment_id');
+        $segment = DB::table('mp_episode_segments')->where('segment_id', $segment_id)->first();
+        if (!$segment) Utils::throwError('20003:请选择分镜');
+        $anime_id = getProp($segment, 'anime_id');
+        $episode_number = getProp($segment, 'episode_number');
+        $segment_number = getProp($segment, 'segment_number');
+
+        try {
+            DB::beginTransaction();
+
+            $segment = (array)$segment;
+            unset($segment['id']);
+            $segment['segment_number'] += 1;
+            $segment['segment_id'] = date('YmdHis') . mt_rand(1000, 9999) . str_pad($segment['segment_number'], 3, "0", STR_PAD_LEFT);
+            $segment['pic_task_id'] = $segment['pic_task_status'] = $segment['video_task_id'] = $segment['video_task_status'] = '';
+            $segment['created_at'] = $segment['updated_at'] = date('Y-m-d H:i:s');
+
+            // 更新其他分镜序号
+            $boolen2 = DB::table('mp_episode_segments')->where('anime_id', $anime_id)->where('episode_number', $episode_number)->where('segment_number', '>', $segment_number)->increment('segment_number');
+            if (!$boolen2) {
+                Utils::throwError('20003:更新其他分镜序号失败');
+            }
+
+            // 复制分镜
+            $boolen = DB::table('mp_episode_segments')->insert($segment);
+            if (!$boolen) {
+                Utils::throwError('20003:复制分镜失败');
+            }
+
+            
+        }catch (\Exception $e) {
+            DB::rollBack();
+            Utils::throwError('20003:'.$e->getMessage());
+        }
+
+        DB::commit();
+        return true;
+    }
+
+    public function moveSegment($data) {
+        $segment_id = getProp($data, 'segment_id');
+        $target_segment_id = getProp($data, 'target_segment_id');
+        
+        // 获取源分镜信息
+        $source_segment = DB::table('mp_episode_segments')->where('segment_id', $segment_id)->first();
+        if (!$source_segment) Utils::throwError('20003:请选择分镜');
+        
+        // 获取目标分镜信息
+        $target_segment = DB::table('mp_episode_segments')->where('segment_id', $target_segment_id)->first();
+        if (!$target_segment) Utils::throwError('20003:目标分镜不存在');
+        
+        $anime_id = getProp($source_segment, 'anime_id');
+        $episode_number = getProp($source_segment, 'episode_number');
+        $source_segment_number = getProp($source_segment, 'segment_number');
+        $target_segment_number = getProp($target_segment, 'segment_number');
+        
+        // 验证源分镜和目标分镜必须属于同一个动漫的同一集
+        $target_anime_id = getProp($target_segment, 'anime_id');
+        $target_episode_number = getProp($target_segment, 'episode_number');
+        
+        if ($anime_id != $target_anime_id || $episode_number != $target_episode_number) {
+            Utils::throwError('20003:只能在同一动漫的同一集内移动分镜');
+        }
+        
+        // 如果源分镜和目标分镜相同,无需移动
+        if ($source_segment_number == $target_segment_number) {
+            return true;
+        }
+
+        try {
+            DB::beginTransaction();
+
+            if ($source_segment_number < $target_segment_number) {
+                // 向后移动:源分镜移动到目标分镜之后
+                // 1. 将源分镜和目标分镜之间的所有分镜序号减1
+                DB::table('mp_episode_segments')
+                    ->where('anime_id', $anime_id)
+                    ->where('episode_number', $episode_number)
+                    ->where('segment_number', '>', $source_segment_number)
+                    ->where('segment_number', '<=', $target_segment_number)
+                    ->decrement('segment_number');
+                
+                // 2. 将源分镜移动到目标分镜之后
+                $new_segment_number = $target_segment_number;
+                
+            } else {
+                // 向前移动:源分镜移动到目标分镜之后
+                // 1. 将目标分镜之后到源分镜之前的所有分镜序号加1
+                DB::table('mp_episode_segments')
+                    ->where('anime_id', $anime_id)
+                    ->where('episode_number', $episode_number)
+                    ->where('segment_number', '>', $target_segment_number)
+                    ->where('segment_number', '<', $source_segment_number)
+                    ->increment('segment_number');
+                
+                // 2. 将源分镜移动到目标分镜之后
+                $new_segment_number = $target_segment_number + 1;
+            }
+            
+            // 3. 更新源分镜的序号
+            $update_result = DB::table('mp_episode_segments')
+                ->where('segment_id', $segment_id)
+                ->update([
+                    'segment_number' => $new_segment_number,
+                    'updated_at' => date('Y-m-d H:i:s')
+                ]);
+                
+            if (!$update_result) {
+                Utils::throwError('20003:更新分镜序号失败');
+            }
+
+            DB::commit();
+            return true;
+            
+        } catch (\Exception $e) {
+            DB::rollBack();
+            Utils::throwError('20003:' . $e->getMessage());
+        }
+    }
+
+    public function delSegment($data) {
+        $segment_id = getProp($data, 'segment_id');
+        $segment = DB::table('mp_episode_segments')->where('segment_id', $segment_id)->first();
+        if (!$segment) Utils::throwError('20003:请选择分镜');
+        $anime_id = getProp($segment, 'anime_id');
+        $episode_number = getProp($segment, 'episode_number');
+        $segment_number = getProp($segment, 'segment_number');
+
+        try {
+            DB::beginTransaction();
+
+            // 删除分镜
+            $boolen = DB::table('mp_episode_segments')->where('segment_id', $segment_id)->delete();
+            if (!$boolen) {
+                Utils::throwError('20003:删除分镜失败');
+            }
+
+            // 更新其他分镜序号
+            $boolen2 = DB::table('mp_episode_segments')->where('anime_id', $anime_id)->where('episode_number', $episode_number)->where('segment_number', '>', $segment_number)->decrement('segment_number');
+            if (!$boolen2) {
+                Utils::throwError('20003:更新其他分镜序号失败');
+            }
+        }catch (\Exception $e) {
+            DB::rollBack();
+            Utils::throwError('20003:'.$e->getMessage());
+        }
+
+        DB::commit();
+        return true;
+    }
+
     // 轮训获取图片任务结果
     // 轮训获取图片任务结果
     private function loopGetPicTaskResult($task_id) {
     private function loopGetPicTaskResult($task_id) {
         $start_count = 0;
         $start_count = 0;

+ 4 - 0
routes/api.php

@@ -144,6 +144,10 @@ Route::group(['middleware' => ['bindToken', 'bindExportToken', 'checkLogin']], f
         Route::post('chatChangeImg', [AnimeController::class, 'chatChangeImg']);
         Route::post('chatChangeImg', [AnimeController::class, 'chatChangeImg']);
         Route::post('batchSetSegmentTasks', [AnimeController::class, 'batchSetSegmentTasks']);
         Route::post('batchSetSegmentTasks', [AnimeController::class, 'batchSetSegmentTasks']);
         Route::post('reGenerateSegment', [AnimeController::class, 'reGenerateSegment']);
         Route::post('reGenerateSegment', [AnimeController::class, 'reGenerateSegment']);
+        Route::post('addSegment', [AnimeController::class, 'addSegment']);
+        Route::post('copySegment', [AnimeController::class, 'copySegment']);
+        Route::post('moveSegment', [AnimeController::class, 'moveSegment']);
+        Route::post('delSegment', [AnimeController::class, 'delSegment']);
     });
     });
     
     
 });
 });