|
|
@@ -874,6 +874,368 @@ class AnimeService
|
|
|
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) {
|
|
|
$start_count = 0;
|