Просмотр исходного кода

1.完成超分逻辑的适配2.优化ffmpeg超分公共方法的视频获取方式

lh 3 дней назад
Родитель
Сommit
63902a74a7
2 измененных файлов с 659 добавлено и 44 удалено
  1. 91 32
      app/Libs/Helpers.php
  2. 568 12
      app/Services/AIGeneration/AIVideoGenerationService.php

+ 91 - 32
app/Libs/Helpers.php

@@ -3404,6 +3404,7 @@ function handleEpisodeContentForAce($originalContent) {
         'art_style' => '',
         'roles' => [],
         'scenes' => [],
+        'props' => [],
         'acts' => []
     ];
     
@@ -3570,6 +3571,43 @@ function handleEpisodeContentForAce($originalContent) {
         }
     }
     
+    // 提取道具列表 - 兼容多种格式
+    if (preg_match('/###\s*道具列表\s*\n(.*?)(?=\n\s*###[^#]|\z)/s', $originalContent, $propsMatch)) {
+        $propsText = trim($propsMatch[1]);
+        $propLines = explode("\n", $propsText);
+        
+        foreach ($propLines as $line) {
+            $line = trim($line);
+            if (empty($line)) continue;
+            
+            // 兼容中文冒号:和英文冒号:
+            if (preg_match('/^([^::]+)[::](.+)$/u', $line, $propMatch)) {
+                $prop = trim($propMatch[1]);
+                $description = trim($propMatch[2]);
+                $picPrompt = null;
+                
+                // 检查描述末尾是否有{道具图片提示词}格式
+                if (preg_match('/^(.*?)\{([^}]+)\}\s*$/u', $description, $promptMatch)) {
+                    // 匹配格式:道具描述{道具图片提示词}
+                    $description = trim($promptMatch[1]);
+                    $picPrompt = trim($promptMatch[2]);
+                }
+                
+                $propData = [
+                    'prop' => $prop,
+                    'description' => $description
+                ];
+                
+                // 如果有道具图片提示词,添加到数组中
+                if ($picPrompt) {
+                    $propData['pic_prompt'] = $picPrompt;
+                }
+                
+                $result['props'][] = $propData;
+            }
+        }
+    }
+    
     // 提取分镜剧本 - 兼容多种格式(旧格式:###分镜剧本,新格式:###分段剧本)
     $storyboardPattern = '/###\s*(?:分镜剧本|分段剧本)\s*\n(.*?)(?=\n\s*###[^#]|\z)/s';
     if (preg_match($storyboardPattern, $originalContent, $storyboardMatch)) {
@@ -4431,16 +4469,18 @@ function mapErrorMessage($errorMessage)
 }
 
 /**
- * 将480p视频升级到720p
- * 使用FFmpeg保持宽高比和画质尽量不丢失
+ * 将480p视频升级到720p(使用FFmpeg本地超分)
  * 
- * @param string $videoUrl 视频URL地址
- * @param string $prefix 上传文件夹前缀(如:'videos')
- * @return string 返回升级后的视频URL,失败返回原视频URL
+ * @param string|resource $videoSource 视频URL地址或视频内容(字符串)
+ * @param string $prefix 上传文件夹前缀(如:'video')
+ * @param bool $isContent 是否为视频内容而非URL(默认false)
+ * @return string 返回升级后的视频URL,失败返回原视频URL或空字符串
  */
-function upgrade480pTo720p($videoUrl, $prefix = 'videos')
+function upgrade480pTo720p($videoSource, $prefix = 'video', $isContent = false)
 {
-    if (env('APP_ENV') == 'local') return $videoUrl;
+    if (env('APP_ENV') == 'local') {
+        return $isContent ? '' : $videoSource;
+    }
 
     try {
         // 创建临时目录
@@ -4452,38 +4492,57 @@ function upgrade480pTo720p($videoUrl, $prefix = 'videos')
 
         // 生成唯一文件名
         $uniqueId = uniqid('video_upgrade_') . bin2hex(random_bytes(4));
-        $videoExt = getVideoExtFromUrl($videoUrl);
-        $inputFile = $tempDir . '/' . $uniqueId . '_input' . $videoExt;
-        $outputFile = $tempDir . '/' . $uniqueId . '_output.mp4';
-
-        // 下载视频到本地
-        dLog('video_upgrade')->info('开始下载视频进行升级', ['url' => $videoUrl]);
-        $client = new \GuzzleHttp\Client(['timeout' => 300]);
-        $response = $client->get($videoUrl);
         
-        $fileContent = $response->getBody()->getContents();
-        if (file_put_contents($inputFile, $fileContent) === false) {
-            dLog('video_upgrade')->error('视频文件写入失败', [
-                'url' => $videoUrl,
-                'file' => $inputFile
-            ]);
-            return $videoUrl;
+        // 处理输入源
+        if ($isContent) {
+            // 如果是视频内容,直接写入临时文件
+            dLog('video_upgrade')->info('检测到视频内容,写入临时文件');
+            $inputFile = $tempDir . '/' . $uniqueId . '_input.mp4';
+            
+            if (file_put_contents($inputFile, $videoSource) === false) {
+                dLog('video_upgrade')->error('视频内容写入临时文件失败');
+                return '';
+            }
+            
+            $videoUrl = '视频内容';
+        } else {
+            // 如果是URL,下载视频
+            $videoUrl = $videoSource;
+            $videoExt = getVideoExtFromUrl($videoUrl);
+            $inputFile = $tempDir . '/' . $uniqueId . '_input' . $videoExt;
+
+            // 下载视频到本地
+            dLog('video_upgrade')->info('开始下载视频进行升级', ['url' => $videoUrl]);
+            $client = new \GuzzleHttp\Client(['timeout' => 300]);
+            $response = $client->get($videoUrl);
+            
+            $fileContent = $response->getBody()->getContents();
+            if (file_put_contents($inputFile, $fileContent) === false) {
+                dLog('video_upgrade')->error('视频文件写入失败', [
+                    'url' => $videoUrl,
+                    'file' => $inputFile
+                ]);
+                return $videoUrl;
+            }
         }
         
         chmod($inputFile, 0664);
 
         if (!file_exists($inputFile)) {
-            dLog('video_upgrade')->error('视频下载失败', ['url' => $videoUrl]);
-            return $videoUrl;
+            dLog('video_upgrade')->error('视频文件不存在', ['file' => $inputFile]);
+            return $isContent ? '' : $videoSource;
         }
 
         $inputFileSize = filesize($inputFile);
-        dLog('video_upgrade')->info('视频下载成功,准备升级到720p', [
-            'url' => $videoUrl,
+        dLog('video_upgrade')->info('视频准备完成,开始升级到720p', [
+            'source' => $videoUrl,
             'size' => $inputFileSize,
             'size_mb' => round($inputFileSize / 1024 / 1024, 2) . 'MB'
         ]);
 
+        // 输出文件路径
+        $outputFile = $tempDir . '/' . $uniqueId . '_output.mp4';
+
         // 使用FFmpeg将视频升级到720p,使用高质量的Lanczos算法和锐化滤镜
         // 滤镜说明:
         // 1. scale=-2:720:flags=lanczos: 使用Lanczos算法缩放,保持宽高比,高度720,宽度自动计算(-2确保是偶数)
@@ -4505,7 +4564,7 @@ function upgrade480pTo720p($videoUrl, $prefix = 'videos')
         $ffmpegPath = env('FFMPEG_PATH', 'ffmpeg');
         $ffmpegCmd = "$ffmpegPath -i \"$inputFile\" -vf \"scale=-2:720:flags=lanczos,unsharp=5:5:1.0:5:5:0.5,hqdn3d=1.5:1.5:6:6\" -c:v libx264 -preset veryslow -crf 16 -tune film -profile:v high -level 4.1 -pix_fmt yuv420p -c:a copy -movflags +faststart -y \"$outputFile\" 2>&1";
         
-        dLog('video_upgrade')->info('开始升级视频到720p', ['command' => $ffmpegCmd]);
+        dLog('video_upgrade')->info('开始FFmpeg升级视频到720p', ['command' => $ffmpegCmd]);
         $output = shell_exec($ffmpegCmd);
 
         if (!file_exists($outputFile)) {
@@ -4514,7 +4573,7 @@ function upgrade480pTo720p($videoUrl, $prefix = 'videos')
                 'output' => $output
             ]);
             @unlink($inputFile);
-            return $videoUrl;
+            return $isContent ? '' : $videoSource;
         }
         
         chmod($outputFile, 0664);
@@ -4538,7 +4597,7 @@ function upgrade480pTo720p($videoUrl, $prefix = 'videos')
 
         if (!$uploadedUrl) {
             dLog('video_upgrade')->error('升级后的视频上传失败');
-            return $videoUrl;
+            return $isContent ? '' : $videoSource;
         }
 
         dLog('video_upgrade')->info('升级后的视频上传成功', ['url' => $uploadedUrl]);
@@ -4546,7 +4605,7 @@ function upgrade480pTo720p($videoUrl, $prefix = 'videos')
 
     } catch (\Exception $e) {
         dLog('video_upgrade')->error('视频升级过程出错', [
-            'url' => $videoUrl,
+            'source' => $isContent ? '视频内容' : $videoSource,
             'error' => $e->getMessage()
         ]);
         
@@ -4558,7 +4617,7 @@ function upgrade480pTo720p($videoUrl, $prefix = 'videos')
             @unlink($outputFile);
         }
         
-        // 出错时返回原视频URL
-        return $videoUrl;
+        // 出错时返回原视频URL或空字符串
+        return $isContent ? '' : $videoSource;
     }
 }

+ 568 - 12
app/Services/AIGeneration/AIVideoGenerationService.php

@@ -313,6 +313,9 @@ class AIVideoGenerationService
                 case 'zzengine':
                     $this->updateUnifiedApiTask($task);
                     break;
+                case 'video_enhance':
+                    $this->updateVideoEnhanceTask($task);
+                    break;
                 default:
                     break;
             }
@@ -2260,7 +2263,7 @@ class AIVideoGenerationService
             'api_type' => 'zzengine',
             'extra_params' => [
                 'task_type' => 'video',
-                'model_code' => $params['model_code'] ?? 'zhizhen-20-mini',
+                'model_code' => $params['model_code'] ?? 'zhizhen-20',
                 'callback_url' => $params['callback_url'] ?? '',
                 'video_duration' => $params['video_duration'] ?? 5,
                 'video_ratio' => $params['video_ratio'] ?? '9:16',
@@ -2300,7 +2303,7 @@ class AIVideoGenerationService
             // 构建统一API请求参数
             $apiParams = [
                 'task_type' => 'video',
-                'model_code' => $extraParams['model_code'] ?? 'zhizhen-20-mini',
+                'model_code' => $extraParams['model_code'] ?? 'zhizhen-20',
                 'prompt' => $task->prompt,
                 'video_duration' => $extraParams['video_duration'] ?? 5,
                 'video_resolution' => $task->video_resolution,
@@ -2479,17 +2482,40 @@ class AIVideoGenerationService
                 }
                 
                 if ($videoUrl) {
-                    // 保存视频到TOS
+                    // 保存视频到TOS并超分
                     try {
-                        $video_name = 'ai_generation_' . time() . '_' . uniqid();
-                        $video_ext = getVideoExtFromUrl($videoUrl);
-                        $video_name = $video_name . $video_ext;
-                        $url = uploadStreamByTos('video', file_get_contents($videoUrl), $video_name);
-                        $returnData['result_url'] = $url;
+                        // 下载视频内容
+                        $videoContent = file_get_contents($videoUrl);
                         
-                        // 压缩视频
-                        $compressed_video_url = compressVideo($url);
-                        $returnData['compressed_url'] = $compressed_video_url ?: $url;
+                        // 使用upgrade480pTo720p方法进行超分(传入视频内容)
+                        dLog('generate')->info('开始对视频进行480p->720p超分', [
+                            'original_url' => $videoUrl,
+                            'size' => round(strlen($videoContent) / 1024 / 1024, 2) . 'MB'
+                        ]);
+                        
+                        $url = upgrade480pTo720p($videoContent, 'video', true);
+                        
+                        if ($url) {
+                            // 超分成功
+                            dLog('generate')->info('视频超分成功', ['enhanced_url' => $url]);
+                            $returnData['result_url'] = $url;
+                            
+                            // 压缩视频
+                            $compressed_video_url = compressVideo($url);
+                            $returnData['compressed_url'] = $compressed_video_url ?: $url;
+                        } else {
+                            // 超分失败,使用原视频
+                            dLog('generate')->warning('视频超分失败,使用原视频');
+                            $video_name = 'ai_generation_' . time() . '_' . uniqid();
+                            $video_ext = getVideoExtFromUrl($videoUrl);
+                            $video_name = $video_name . $video_ext;
+                            $url = uploadStreamByTos('video', $videoContent, $video_name);
+                            $returnData['result_url'] = $url;
+                            
+                            // 压缩视频
+                            $compressed_video_url = compressVideo($url);
+                            $returnData['compressed_url'] = $compressed_video_url ?: $url;
+                        }
                     } catch (\Exception $e) {
                         dLog('generate')->error('保存视频到TOS失败', ['error' => $e->getMessage()]);
                         $returnData['result_url'] = $videoUrl; // 如果保存失败,使用原URL
@@ -2563,7 +2589,37 @@ class AIVideoGenerationService
         if ($statusInfo['status'] === 'success') {
             logDB('generate', 'info', '统一API视频生成任务成功', ['id' => $task->id, 'result_url' => $statusInfo['result_url']]);
             
-            // 同步调整分镜视频状态和结果(兼容全能模式)
+            // // 判断是否为480p视频,如果是则触发超分逻辑
+            // if ($task->video_resolution === '480P') {
+            //     logDB('video_enhance', 'info', '检测到480p视频生成成功,准备自动触发超分', [
+            //         'task_id' => $task->id
+            //     ]);
+                
+            //     // 先更新任务状态为成功
+            //     $task->updateStatus(MpGenerateVideoTask::STATUS_SUCCESS, $statusInfo);
+                
+            //     // 调用超分任务提交方法
+            //     $enhanceResult = $this->submitVideoEnhanceTask($task->id);
+                
+            //     if ($enhanceResult['success']) {
+            //         logDB('video_enhance', 'info', '480p视频超分任务已自动提交', [
+            //             'original_task_id' => $task->id,
+            //             'enhance_task_id' => $enhanceResult['enhance_task_id']
+            //         ]);
+            //     } else {
+            //         logDB('video_enhance', 'warning', '480p视频超分任务提交失败,保持原480p视频', [
+            //             'task_id' => $task->id,
+            //             'error' => $enhanceResult['message']
+            //         ]);
+                    
+            //         // 超分失败时,仍然更新分镜表为480p视频
+            //         $this->update480pVideoToSegment($task, $statusInfo);
+            //     }
+                
+            //     // 480p视频已触发超分,直接返回,不继续后续更新操作
+            //     return;
+            // }
+            
             $segment_id = $this->getSegmentIdFromTask($task);
             if ($segment_id && isset($statusInfo['result_url'])) {
                 try {
@@ -2679,5 +2735,505 @@ class AIVideoGenerationService
             }
         }
     }
+
+    /**
+     * 更新480p视频到分镜表(超分失败时的降级处理)
+     * 
+     * @param MpGenerateVideoTask $task
+     * @param array $statusInfo
+     * @return void
+     */
+    private function update480pVideoToSegment($task, $statusInfo): void
+    {
+        $segment_id = $this->getSegmentIdFromTask($task);
+        if (!$segment_id || !isset($statusInfo['result_url'])) {
+            return;
+        }
+        
+        try {
+            DB::beginTransaction();
+            
+            $now = date('Y-m-d H:i:s');
+            
+            // 构建更新数据
+            $segmentUpdateData = [
+                'origin_video_url' => $statusInfo['result_url'],
+                'video_task_status' => '已完成',
+                'current_type' => 2,
+                'last_frame_url' => $statusInfo['last_frame_url'] ?? '',
+                'updated_at' => $now
+            ];
+            
+            // 使用查询任务时已压缩的视频URL,避免重复压缩
+            $segmentUpdateData['video_url'] = $statusInfo['compressed_url'] ?? $statusInfo['result_url'];
+            
+            // 只有当video_duration存在且大于0时才更新
+            if (isset($statusInfo['video_duration']) && $statusInfo['video_duration'] > 0) {
+                $segmentUpdateData['video_duration'] = $statusInfo['video_duration'];
+                $segmentUpdateData['video_time_point_start'] = 0;
+                
+                // 获取audio_duration(兼容全能模式)
+                $audio_duration = $this->getAudioDuration($task, $segment_id, $statusInfo['video_duration'] ?? null);
+                $segmentUpdateData['video_time_point_end'] = $audio_duration ?? 0;
+            }
+            
+            // 更新分镜表(兼容act模式)
+            $this->updateSegmentTable($task, $segmentUpdateData);
+            
+            // 添加视频生成对话记录
+            $this->addVideoGenerationRecords($task, $segment_id, $statusInfo['result_url']);
+            
+            DB::commit();
+            
+        } catch (\Exception $e) {
+            DB::rollBack();
+            dLog('generate')->error('480p视频更新分镜表失败', [
+                'segment_id' => $segment_id,
+                'task_id' => $task->id,
+                'error' => $e->getMessage()
+            ]);
+        }
+    }
+
+    /**
+     * 提交视频超分任务(480p -> 720p)
+     * 
+     * @param int $taskId mp_generate_video_tasks表的任务ID
+     * @return array 包含成功状态和新任务ID或错误信息
+     */
+    public function submitVideoEnhanceTask(int $taskId): array
+    {
+        try {
+            // 查询原任务
+            $originalTask = MpGenerateVideoTask::find($taskId);
+            if (!$originalTask) {
+                return [
+                    'success' => false,
+                    'message' => '原任务不存在'
+                ];
+            }
+
+            // 检查任务是否已完成
+            if ($originalTask->status !== MpGenerateVideoTask::STATUS_SUCCESS) {
+                return [
+                    'success' => false,
+                    'message' => '只能对已成功完成的任务进行超分处理'
+                ];
+            }
+
+            // 检查分辨率是否为480p
+            if ($originalTask->video_resolution !== '480P') {
+                return [
+                    'success' => false,
+                    'message' => '只支持480P视频超分到720P'
+                ];
+            }
+
+            // 检查是否有视频URL
+            if (empty($originalTask->result_url)) {
+                return [
+                    'success' => false,
+                    'message' => '原任务没有视频URL'
+                ];
+            }
+
+            // 获取API Key
+            $apiKey = env('VOLC_AI_MEDIAKIT_KEY');
+            if (empty($apiKey)) {
+                return [
+                    'success' => false,
+                    'message' => '火山引擎 MediaKit API Key 未配置'
+                ];
+            }
+
+            // 构建请求参数
+            $requestParams = [
+                'video_url' => $originalTask->result_url,
+                'resolution' => '720p',
+                'bitrate_level' => 'medium'
+            ];
+
+            // 调用超分API
+            $client = new Client(['verify' => false, 'timeout' => 120]);
+            $response = $client->post('https://mediakit.cn-beijing.volces.com/api/v1/tools/enhance-video-fast', [
+                'headers' => [
+                    'Authorization' => 'Bearer ' . $apiKey,
+                    'Content-Type' => 'application/json',
+                ],
+                'json' => $requestParams
+            ]);
+
+            $responseData = json_decode($response->getBody(), true);
+            
+            dLog('generate')->info('视频超分任务提交响应', [
+                'original_task_id' => $taskId,
+                'response' => $responseData
+            ]);
+
+            // 检查API响应
+            if (!isset($responseData['success']) || $responseData['success'] !== true) {
+                $errorMsg = '超分任务提交失败';
+                if (isset($responseData['error'])) {
+                    $errorMsg .= ': ' . ($responseData['error']['message'] ?? json_encode($responseData['error']));
+                }
+                
+                logDB('video_enhance', 'error', $errorMsg, [
+                    'original_task_id' => $taskId,
+                    'response' => $responseData
+                ]);
+
+                return [
+                    'success' => false,
+                    'message' => $errorMsg
+                ];
+            }
+
+            // 使用事务创建新的超分任务记录并更新原任务
+            try {
+                DB::beginTransaction();
+                
+                // 创建新的超分任务记录
+                $enhanceTask = MpGenerateVideoTask::create([
+                    'task_id' => $responseData['task_id'],
+                    'prompt' => $originalTask->prompt,
+                    'video_resolution' => '720P', // 目标分辨率
+                    'status' => MpGenerateVideoTask::STATUS_PROCESSING,
+                    'api_type' => 'video_enhance',
+                    'extra_params' => [
+                        'original_task_id' => $originalTask->id,
+                        'original_video_url' => $originalTask->result_url,
+                        'enhance_type' => '480p_to_720p',
+                        'request_id' => $responseData['request_id'] ?? '',
+                        'api_response' => $responseData
+                    ]
+                ]);
+
+                // 更新原任务的video_enhance_id字段
+                $originalTask->update([
+                    'video_enhance_id' => $enhanceTask->id
+                ]);
+
+                DB::commit();
+
+                logDB('video_enhance', 'info', '视频超分任务创建成功', [
+                    'original_task_id' => $taskId,
+                    'enhance_task_id' => $enhanceTask->id,
+                    'api_task_id' => $responseData['task_id']
+                ]);
+
+                return [
+                    'success' => true,
+                    'message' => '超分任务提交成功',
+                    'enhance_task_id' => $enhanceTask->id,
+                    'api_task_id' => $responseData['task_id']
+                ];
+                
+            } catch (\Exception $e) {
+                DB::rollBack();
+                logDB('video_enhance', 'error', '创建超分任务失败', [
+                    'original_task_id' => $taskId,
+                    'error' => $e->getMessage()
+                ]);
+                
+                return [
+                    'success' => false,
+                    'message' => '创建超分任务失败: ' . $e->getMessage()
+                ];
+            }
+
+        } catch (\GuzzleHttp\Exception\RequestException $e) {
+            $errorMsg = 'API请求失败';
+            if ($e->hasResponse()) {
+                $errorResponse = json_decode($e->getResponse()->getBody()->getContents(), true);
+                $errorMsg .= ': ' . ($errorResponse['msg'] ?? $errorResponse['message'] ?? $e->getMessage());
+            } else {
+                $errorMsg .= ': ' . $e->getMessage();
+            }
+
+            logDB('video_enhance', 'error', $errorMsg, ['task_id' => $taskId]);
+
+            return [
+                'success' => false,
+                'message' => $errorMsg
+            ];
+        } catch (\Exception $e) {
+            logDB('video_enhance', 'error', '超分任务提交异常', [
+                'task_id' => $taskId,
+                'error' => $e->getMessage()
+            ]);
+
+            return [
+                'success' => false,
+                'message' => '系统错误: ' . $e->getMessage()
+            ];
+        }
+    }
+
+    /**
+     * 查询视频超分任务状态
+     * 
+     * @param MpGenerateVideoTask $task
+     * @return array
+     */
+    public function queryVideoEnhanceTaskStatus(MpGenerateVideoTask $task): array
+    {
+        try {
+            $apiKey = env('VOLC_AI_MEDIAKIT_KEY');
+            if (empty($apiKey)) {
+                return [
+                    'status' => 'failed',
+                    'error_message' => 'API Key 未配置'
+                ];
+            }
+
+            // 调用查询任务信息API
+            $client = new Client(['verify' => false, 'timeout' => 120]);
+            $response = $client->get('https://mediakit.cn-beijing.volces.com/api/v1/tasks/' . $task->task_id, [
+                'headers' => [
+                    'Authorization' => 'Bearer ' . $apiKey,
+                ]
+            ]);
+
+            $responseData = json_decode($response->getBody(), true);
+
+            dLog('generate')->info('视频超分任务状态查询', [
+                'task_id' => $task->id,
+                'api_task_id' => $task->task_id,
+                'response' => $responseData
+            ]);
+
+            // 检查响应
+            if (!isset($responseData['success']) || $responseData['success'] !== true) {
+                return [
+                    'status' => 'failed',
+                    'error_message' => isset($responseData['error']) ? 
+                        ($responseData['error']['message'] ?? '查询失败') : '查询失败'
+                ];
+            }
+
+            $taskData = $responseData;
+            $taskStatus = $taskData['status'] ?? '';
+
+            $returnData = [
+                'status' => 'processing',
+                'error_message' => null,
+                'result_url' => null,
+                'result_json' => $responseData
+            ];
+
+            // 解析任务状态
+            if ($taskStatus === 'completed' && isset($taskData['result']['video_url'])) {
+                // 任务成功,保存视频
+                $videoUrl = $taskData['result']['video_url'];
+                
+                // 将视频保存到TOS
+                $videoName = 'enhanced_video_' . time() . '_' . uniqid() . '.mp4';
+                $savedUrl = uploadStreamByTos('video', file_get_contents($videoUrl), $videoName);
+                
+                $returnData['status'] = 'success';
+                $returnData['result_url'] = $savedUrl;
+                
+                // 视频时长
+                if (isset($taskData['result']['duration'])) {
+                    $returnData['video_duration'] = (int)$taskData['result']['duration'];
+                }
+                
+                // 压缩视频(可选)
+                $compressedUrl = compressVideo($savedUrl);
+                $returnData['compressed_url'] = $compressedUrl ?: $savedUrl;
+
+            } elseif ($taskStatus === 'failed') {
+                $returnData['status'] = 'failed';
+                $returnData['error_message'] = '视频超分失败';
+            } elseif (in_array($taskStatus, ['pending', 'processing', 'queued'])) {
+                $returnData['status'] = 'processing';
+            }
+
+            return $returnData;
+
+        } catch (\GuzzleHttp\Exception\RequestException $e) {
+            $errorMsg = 'API查询失败';
+            if ($e->hasResponse()) {
+                $errorResponse = json_decode($e->getResponse()->getBody()->getContents(), true);
+                $errorMsg .= ': ' . ($errorResponse['msg'] ?? $errorResponse['message'] ?? $e->getMessage());
+            } else {
+                $errorMsg .= ': ' . $e->getMessage();
+            }
+            
+            return [
+                'status' => 'processing',
+                'error_message' => $errorMsg
+            ];
+        } catch (\Exception $e) {
+            return [
+                'status' => 'processing',
+                'error_message' => $e->getMessage()
+            ];
+        }
+    }
+
+    /**
+     * 更新视频超分任务状态
+     * 
+     * @param MpGenerateVideoTask $task
+     * @return void
+     */
+    private function updateVideoEnhanceTask($task): void
+    {
+        $statusInfo = $this->queryVideoEnhanceTaskStatus($task);
+        if (!isset($statusInfo['status'])) return;
+
+        if ($statusInfo['status'] === 'success') {
+            logDB('video_enhance', 'info', '视频超分任务成功', [
+                'id' => $task->id,
+                'result_url' => $statusInfo['result_url']
+            ]);
+
+            // 获取原始480p任务
+            $extraParams = is_array($task->extra_params) ? $task->extra_params : json_decode($task->extra_params, true);
+            $originalTaskId = $extraParams['original_task_id'] ?? null;
+            
+            if (!$originalTaskId) {
+                // 如果没有原始任务ID,只更新超分任务本身
+                $task->updateStatus(MpGenerateVideoTask::STATUS_SUCCESS, $statusInfo);
+                return;
+            }
+
+            $originalTask = MpGenerateVideoTask::find($originalTaskId);
+            if (!$originalTask) {
+                logDB('video_enhance', 'warning', '原始480p任务不存在', [
+                    'enhance_task_id' => $task->id,
+                    'original_task_id' => $originalTaskId
+                ]);
+                $task->updateStatus(MpGenerateVideoTask::STATUS_SUCCESS, $statusInfo);
+                return;
+            }
+
+            // 使用与updateUnifiedApiTask完全一致的逻辑
+            $segment_id = $this->getSegmentIdFromTask($originalTask);
+            if ($segment_id && isset($statusInfo['result_url'])) {
+                try {
+                    DB::beginTransaction();
+                    
+                    // 更新超分任务状态
+                    $task->updateStatus(MpGenerateVideoTask::STATUS_SUCCESS, $statusInfo);
+                    
+                    // 更新原始480p任务状态
+                    $originalTask->updateStatus(MpGenerateVideoTask::STATUS_SUCCESS, $statusInfo);
+                    
+                    $now = date('Y-m-d H:i:s');
+                    
+                    // 构建更新数据(使用超分后的720p视频)
+                    $segmentUpdateData = [
+                        'origin_video_url' => $statusInfo['result_url'],
+                        'video_task_status' => '已完成',
+                        'current_type' => 2,
+                        'last_frame_url' => $statusInfo['last_frame_url'] ?? '',
+                        'updated_at' => $now
+                    ];
+                    
+                    // 使用查询任务时已压缩的视频URL,避免重复压缩
+                    $segmentUpdateData['video_url'] = $statusInfo['compressed_url'] ?? $statusInfo['result_url'];
+                    
+                    // 只有当video_duration存在且大于0时才更新
+                    if (isset($statusInfo['video_duration']) && $statusInfo['video_duration'] > 0) {
+                        $segmentUpdateData['video_duration'] = $statusInfo['video_duration'];
+                        $segmentUpdateData['video_time_point_start'] = 0;
+                        
+                        // 获取audio_duration(兼容全能模式)
+                        $audio_duration = $this->getAudioDuration($originalTask, $segment_id, $statusInfo['video_duration'] ?? null);
+                        $segmentUpdateData['video_time_point_end'] = $audio_duration ?? 0;
+                    }
+                    
+                    // 更新分镜表(兼容act模式)
+                    $this->updateSegmentTable($originalTask, $segmentUpdateData);
+                    
+                    // 添加视频生成对话记录
+                    $this->addVideoGenerationRecords($originalTask, $segment_id, $statusInfo['result_url']);
+                    
+                    // 如果是全能模式的首帧图,则提取视频第一帧并更新anime表
+                    $act_id = getProp($originalTask, 'alias_act_id');
+                    if (!empty($act_id) && Redis::sismember('anime_act_first_frame_urls', $act_id)) {
+                        $anime_id = DB::table('mp_episode_segments')->where('id', $act_id)->value('anime_id');
+                        if ($anime_id) {
+                            // 提取视频第一帧
+                            $videoUrl = $segmentUpdateData['video_url'];
+                            $firstFrameUrl = getVideoFirstFrame($videoUrl, 'anime_first_frames', 'tos');
+                            
+                            if ($firstFrameUrl) {
+                                // 更新anime表
+                                DB::table('mp_animes')->where('id', $anime_id)->update([
+                                    'first_frame_url' => $firstFrameUrl,
+                                    'updated_at' => date('Y-m-d H:i:s')
+                                ]);
+                                
+                                dLog('video_frame')->info('全能模式首帧图更新成功(超分任务)', [
+                                    'act_id' => $act_id,
+                                    'anime_id' => $anime_id,
+                                    'first_frame_url' => $firstFrameUrl
+                                ]);
+                            }
+                        }
+                    }
+                    
+                    DB::commit();
+                    // 从Redis中移除
+                    Redis::srem('anime_act_first_frame_urls', $act_id);
+
+                } catch (\Exception $e) {
+                    DB::rollBack();
+                    dLog('video_enhance')->error('超分任务处理失败', [
+                        'segment_id' => $segment_id,
+                        'enhance_task_id' => $task->id,
+                        'original_task_id' => $originalTaskId,
+                        'error' => $e->getMessage()
+                    ]);
+                }
+            } else {
+                // 没有segment_id的情况下,只更新任务状态
+                try {
+                    DB::beginTransaction();
+                    
+                    $task->updateStatus(MpGenerateVideoTask::STATUS_SUCCESS, $statusInfo);
+                    $originalTask->updateStatus(MpGenerateVideoTask::STATUS_SUCCESS, $statusInfo);
+                    
+                    DB::commit();
+                } catch (\Exception $e) {
+                    DB::rollBack();
+                    dLog('video_enhance')->error('超分任务状态更新失败', [
+                        'enhance_task_id' => $task->id,
+                        'original_task_id' => $originalTaskId,
+                        'error' => $e->getMessage()
+                    ]);
+                }
+            }
+
+        } elseif ($statusInfo['status'] === 'failed') {
+            logDB('video_enhance', 'error', '视频超分任务失败', [
+                'id' => $task->id,
+                'error' => $statusInfo['error_message']
+            ]);
+            
+            $task->updateStatus(MpGenerateVideoTask::STATUS_FAILED, [
+                'error_message' => $statusInfo['error_message'],
+                'result_json' => $statusInfo['result_json'] ?? []
+            ]);
+            
+            // 不更新原始任务状态,保留480p任务的成功状态
+        }
+
+        // 超时检查:48小时
+        $processingTime = now()->diffInHours($task->created_at);
+        if ($processingTime > 48 && $task->status === MpGenerateVideoTask::STATUS_PROCESSING) {
+            logDB('video_enhance', 'warning', '视频超分任务超时', [
+                'id' => $task->id,
+                'processing_hours' => $processingTime
+            ]);
+            
+            $task->updateStatus(MpGenerateVideoTask::STATUS_FAILED, [
+                'error_message' => '任务处理超时(超过48小时)'
+            ]);
+        }
+    }
 }