lh 2 месяцев назад
Родитель
Сommit
45eef50b9e

+ 49 - 27
app/Http/Controllers/Anime/AnimeController.php

@@ -1526,12 +1526,26 @@ class AnimeController extends BaseController
             ->where('episode_id', $episodeId)
             ->where('pic_task_id', '!=', '')
             ->orderBy('segment_number')
-            ->select('segment_id', 'segment_number', 'pic_task_id', 'pic_task_status', 'img_url', 'audio_url')
+            ->select('segment_id', 'segment_number', 'pic_task_id', 'pic_task_status', 'img_url', 'audio_url', 'dialogue',  'voice_type')
             ->get();
 
         // 判断是否有未完成的任务
-        $count = DB::table('mp_episode_segments')->where('anime_id', $animeId)
+        $uncompleted_pic_count = DB::table('mp_episode_segments')->where('anime_id', $animeId)
             ->where('episode_id', $episodeId)->where('pic_task_id', '<>', '')->whereNotIn('pic_task_status', ['已完成', '失败'])->count('id');
+        
+        // 获取dialogue和voice_type存在但audio_url不存在的分镜个数
+        $uncompleted_audio_count = DB::table('mp_episode_segments')
+            ->where('anime_id', $animeId)
+            ->where('episode_id', $episodeId)
+            ->where('dialogue', '<>', '')
+            ->whereNotNull('dialogue')
+            ->where('voice_type', '<>', '')
+            ->whereNotNull('voice_type')
+            ->where(function($query) {
+                $query->whereNull('audio_url')
+                      ->orWhere('audio_url', '');
+            })
+            ->count('id');
             
         if ($segments->isEmpty()) {
             Utils::throwError('20003:未找到分镜任务数据');
@@ -1542,9 +1556,9 @@ class AnimeController extends BaseController
         $checkInterval = 5; // 每5秒检查一次
         
         // 设置 SSE 响应头并开始长连接
-        return response()->stream(function () use ($segments, $animeId, $episodeId, $startTime, $maxDuration, $checkInterval, $count, $noPicTaskCount) {
+        return response()->stream(function () use ($segments, $animeId, $episodeId, $startTime, $maxDuration, $checkInterval, $uncompleted_pic_count, $uncompleted_audio_count, $noPicTaskCount) {
 
-            if ($count == 0) {
+            if ($uncompleted_pic_count == 0 && $uncompleted_audio_count == 0) {
                 $completedCount = DB::table('mp_episode_segments')->where('anime_id', $animeId)->where('episode_id', $episodeId)->where('pic_task_id', '<>', '')->where('pic_task_status', '已完成')->count('id');
 
                 // 获取剧集信息
@@ -1716,38 +1730,46 @@ class AnimeController extends BaseController
                             }
                         }
                         
-                        // 检查音频任务状态
-                        $audioTask = DB::table('mp_dub_video_tasks')
-                            ->where('alias_segment_id', $segmentId)
-                            ->orderBy('id', 'desc')
-                            ->select('generate_status', 'audio_url')
+                        // 检查音频状态
+                        // 获取分镜的dialogue、voice_type和audio_url信息
+                        $segmentInfo = DB::table('mp_episode_segments')
+                            ->where('segment_id', $segmentId)
+                            ->select('dialogue', 'voice_type', 'audio_url')
                             ->first();
                         
-                        if ($audioTask && in_array($audioTask->generate_status, ['执行成功', '执行失败'])) {
-                            $updateData = [
-                                'updated_at' => date('Y-m-d H:i:s')
-                            ];
+                        $needAudio = false;
+                        $currentAudioUrl = '';
+                        
+                        if ($segmentInfo) {
+                            $dialogue = trim((string)getProp($segmentInfo, 'dialogue'));
+                            $voiceType = trim((string)getProp($segmentInfo, 'voice_type'));
+                            $currentAudioUrl = trim((string)getProp($segmentInfo, 'audio_url'));
                             
-                            if ($audioTask->generate_status === '执行成功' && !empty($audioTask->audio_url)) {
-                                $updateData['audio_url'] = $audioTask->audio_url;
+                            // 如果有对话和音色类型,则需要音频
+                            if (!empty($dialogue) && !empty($voiceType)) {
+                                $needAudio = true;
+                            }
+                        }
+                        
+                        // 判断音频状态
+                        if ($needAudio) {
+                            // 需要音频的情况下,检查audio_url是否有值
+                            if (!empty($currentAudioUrl)) {
                                 $audioCompleted = true;
-                            } else if ($audioTask->generate_status === '执行失败') {
-                                // 失败时清空音频URL
-                                $updateData['audio_url'] = '';
+                                $audioProcessed = true;
+                            } else {
+                                // 需要音频但还没有,继续等待
+                                $audioCompleted = false;
+                                $audioProcessed = false;
                             }
-                            
-                            // 静默更新分镜表的音频URL(不输出通知)
-                            DB::table('mp_episode_segments')
-                                ->where('segment_id', $segmentId)
-                                ->update($updateData);
-                            
-                            $audioProcessed = true;
-                        }else if ($audioUrl) {
+                        } else {
+                            // 不需要音频,直接标记为已处理
+                            $audioCompleted = false;
                             $audioProcessed = true;
                         }
                         
                         // 如果图片和音频都成功完成,标记为已完成
-                        if ($picCompleted && $audioCompleted) {
+                        if ($picCompleted && ($audioCompleted || !$needAudio)) {
                             $completedSegments[] = $segmentId;
                         }
                         

+ 126 - 38
app/Services/Anime/AnimeService.php

@@ -2306,16 +2306,52 @@ class AnimeService
             DB::beginTransaction();
 
             $now = date('Y-m-d H:i:s');
+            
+            // 收集需要创建音频合成任务的segment_id和对应的分镜数据
+            $segment_ids_to_create_task = [];
+            $segments_data = [];
 
+            // 如果有全局更新,获取该角色的所有分镜数据并检查是否有变化
             if (!empty($global_update_data)) {
                 if (!$target_voice_actor) Utils::throwError('20003:角色异常');
 
-                $global_segment_update_data = $global_update_data;
-                $global_segment_update_data['updated_at'] = $now;
-                DB::table('mp_episode_segments')
+                $affected_segments = DB::table('mp_episode_segments')
                     ->where('episode_id', $episode_id)
                     ->where('voice_actor', $target_voice_actor)
-                    ->update($global_segment_update_data);
+                    ->select('segment_id', 'audio_url', 'voice_actor', 'dialogue', 'voice_type', 'voice_name', 'voice_audio_url', 'emotion', 'emotion_type', 'gender', 'speed_ratio', 'loudness_ratio', 'emotion_scale', 'pitch')
+                    ->get();
+                
+                foreach ($affected_segments as $seg) {
+                    $seg = (array)$seg;
+                    $sid = getProp($seg, 'segment_id');
+                    
+                    // 检查参数是否发生变化
+                    $has_changed = false;
+                    foreach ($global_update_data as $field => $new_value) {
+                        $old_value = getProp($seg, $field);
+                        if ($old_value != $new_value) {
+                            $has_changed = true;
+                            break;
+                        }
+                    }
+                    
+                    if ($has_changed) {
+                        $segment_ids_to_create_task[] = $sid;
+                        $segments_data[$sid] = $seg;
+                    }
+                }
+                
+                // 只有在有变化的分镜时才更新
+                if (!empty($segment_ids_to_create_task)) {
+                    $global_segment_update_data = $global_update_data;
+                    $global_segment_update_data['audio_url'] = '';  // 清空音频URL
+                    $global_segment_update_data['updated_at'] = $now;
+                    DB::table('mp_episode_segments')
+                        ->where('episode_id', $episode_id)
+                        ->where('voice_actor', $target_voice_actor)
+                        ->whereIn('segment_id', $segment_ids_to_create_task)
+                        ->update($global_segment_update_data);
+                }
 
                 if ($global_voice_type) {
                     $episode_roles = json_decode((string)getProp($episode, 'roles'), true);
@@ -2343,51 +2379,103 @@ class AnimeService
                 }
             }
 
+            // 如果有单个分镜更新,检查是否有变化
             if (!empty($segment_update_data)) {
-                $segment_update_data['updated_at'] = $now;
-                DB::table('mp_episode_segments')->where('segment_id', $segment_id)->update($segment_update_data);
-            }
-
-            // 收集需要创建音频合成任务的segment_id
-            $segment_ids_to_create_task = [];
-            $segments_data = [];
-
-            // 如果有全局更新,获取该角色的所有分镜数据
-            if (!empty($global_update_data)) {
-                $affected_segments = DB::table('mp_episode_segments')
-                    ->where('episode_id', $episode_id)
-                    ->where('voice_actor', $target_voice_actor)
-                    ->select('segment_id', 'video_url', 'voice_actor', 'dialogue', 'voice_type', 'voice_name', 'emotion', 'emotion_type', 'gender', 'speed_ratio', 'loudness_ratio', 'emotion_scale', 'pitch')
-                    ->get();
+                // 获取当前分镜数据
+                $current_segment = DB::table('mp_episode_segments')
+                    ->where('segment_id', $segment_id)
+                    ->select('segment_id', 'audio_url', 'voice_actor', 'dialogue', 'voice_type', 'voice_name', 'voice_audio_url', 'emotion', 'emotion_type', 'gender', 'speed_ratio', 'loudness_ratio', 'emotion_scale', 'pitch')
+                    ->first();
                 
-                foreach ($affected_segments as $seg) {
-                    $seg = (array)$seg;
-                    $sid = getProp($seg, 'segment_id');
-                    $segment_ids_to_create_task[] = $sid;
-                    $segments_data[$sid] = $seg;
+                if ($current_segment) {
+                    $current_segment = (array)$current_segment;
+                    
+                    // 检查参数是否发生变化
+                    $has_changed = false;
+                    foreach ($segment_update_data as $field => $new_value) {
+                        $old_value = getProp($current_segment, $field);
+                        if ($old_value != $new_value) {
+                            $has_changed = true;
+                            break;
+                        }
+                    }
+                    
+                    if ($has_changed) {
+                        // 如果该分镜还未在列表中,添加进去
+                        if (!in_array($segment_id, $segment_ids_to_create_task)) {
+                            $segment_ids_to_create_task[] = $segment_id;
+                            $segments_data[$segment_id] = $current_segment;
+                        }
+                        
+                        // 更新分镜数据并清空音频URL
+                        $segment_update_data['audio_url'] = '';
+                        $segment_update_data['updated_at'] = $now;
+                        DB::table('mp_episode_segments')->where('segment_id', $segment_id)->update($segment_update_data);
+                    }
                 }
             }
 
-            // 如果有单个分镜更新,添加该分镜数据
-            if (!empty($segment_update_data)) {
-                $segment_ids_to_create_task[] = $segment_id;
-                
-                // 如果该分镜数据还未获取,则获取
-                if (!isset($segments_data[$segment_id])) {
-                    $segment = DB::table('mp_episode_segments')
-                        ->where('segment_id', $segment_id)
-                        ->select('segment_id', 'video_url', 'voice_actor', 'dialogue', 'voice_type', 'voice_name', 'emotion', 'emotion_type', 'gender', 'speed_ratio', 'loudness_ratio', 'emotion_scale', 'pitch')
+            // 为所有有变化的分镜创建音频合成任务
+            if (!empty($segment_ids_to_create_task)) {
+                foreach ($segment_ids_to_create_task as $sid) {
+                    // 重新获取更新后的分镜数据
+                    $updated_segment = DB::table('mp_episode_segments')
+                        ->where('segment_id', $sid)
+                        ->select('voice_actor', 'dialogue', 'voice_type', 'voice_name', 'emotion', 'emotion_type', 'gender', 'speed_ratio', 'loudness_ratio', 'emotion_scale', 'pitch')
                         ->first();
                     
-                    if ($segment) {
-                        $segments_data[$segment_id] = (array)$segment;
+                    if (!$updated_segment) continue;
+                    
+                    $updated_segment = (array)$updated_segment;
+                    
+                    $generate_json = [
+                        'text' => getProp($updated_segment, 'dialogue'),
+                        'role' => getProp($updated_segment, 'voice_actor'),
+                        'voice_type' => getProp($updated_segment, 'voice_type'),
+                        'voice_name' => getProp($updated_segment, 'voice_name'),
+                        'emotion' => getProp($updated_segment, 'emotion'),
+                        'emotion_type' => getProp($updated_segment, 'emotion_type'),
+                        'gender' => getProp($updated_segment, 'gender'),
+                        'speed_ratio' => getProp($updated_segment, 'speed_ratio', 0),
+                        'loudness_ratio' => getProp($updated_segment, 'loudness_ratio', 0),
+                        'emotion_scale' => getProp($updated_segment, 'emotion_scale', 0),
+                        'pitch' => getProp($updated_segment, 'pitch', 0),
+                    ];
+                    
+                    // 插入视频配音合成任务
+                    $dubTaskId = DB::table('mp_dub_video_tasks')->insertGetId([
+                        'alias_segment_id' => $sid,
+                        'generate_status' => '执行中',
+                        'audio_url' => '',
+                        'generate_json' => json_encode($generate_json, 256),
+                        'created_at' => $now,
+                        'updated_at' => $now,
+                    ]);
+                    
+                    if (!$dubTaskId) {
+                        dLog('anime')->error('创建配音任务失败', ['segment_id' => $sid]);
+                        continue;
+                    }
+                    
+                    // 请求远程服务器执行生成
+                    try {
+                        $client = new Client(['timeout' => 300, 'verify' => false]);
+                        $result = $client->get("http://122.9.129.83:5000/api/anime/genAudio?taskId={$dubTaskId}");
+                        $response = $result->getBody()->getContents();
+                        $response_arr = json_decode($response, true);
+                        
+                        if (!isset($response_arr['code']) || (int)$response_arr['code'] !== 0) {
+                            $error_msg = isset($response_arr['msg']) ? $response_arr['msg'] : '未知错误';
+                            dLog('anime')->error('火山生成音频失败', ['error' => $error_msg, 'task_id' => $dubTaskId, 'segment_id' => $sid]);
+                            logDB('anime', 'error', '火山生成音频失败', ['error' => $error_msg, 'task_id' => $dubTaskId, 'segment_id' => $sid]);
+                        }
+                    } catch (\Exception $e) {
+                        dLog('anime')->error('调用音频生成API失败', ['error' => $e->getMessage(), 'task_id' => $dubTaskId, 'segment_id' => $sid]);
+                        logDB('anime', 'error', '调用音频生成API失败', ['error' => $e->getMessage(), 'task_id' => $dubTaskId, 'segment_id' => $sid]);
                     }
                 }
             }
 
-            // 去重
-            $segment_ids_to_create_task = array_unique($segment_ids_to_create_task);
-
             DB::commit();
 
         } catch (\Exception $e) {

+ 1 - 1
app/Services/DeepSeek/DeepSeekService.php

@@ -557,7 +557,7 @@ class DeepSeekService
 
         if ($model == 'r1') {
             $model = 'deepseek-v4-flash'; // r1 映射到 deepseek-v4-flash 思考模式
-            $thinkingMode = 'enabled';
+            $thinkingMode = 'disabled';
         } else {
             $model = 'deepseek-v4-flash'; // 默认映射到 deepseek-v4-flash 非思考模式
             $thinkingMode = 'disabled';