info('开始检查视频生成任务状态...'); // 执行50s $time_start = time(); try { $count = DB::table('mp_generate_video_tasks')->where('status', 'processing')->count('id'); while ($count > 0) { $time_diff = time() - $time_start; sleep(3); if ($time_diff > 50) break; $videoGenerationService->updatePendingTasks(); $count = DB::table('mp_generate_video_tasks')->where('status', 'processing')->count('id'); } // 更新分镜表中的视频任务状态 $this->updateSegmentVideoStatus(); dLog('command')->info('视频任务状态检查完成'); } catch (\Exception $e) { dLog('command')->error('视频任务状态检查失败: ' . $e->getMessage()); logDB('command', 'error', '定时任务:视频生成任务状态检查失败', ['error' => $e->getMessage()]); return 1; } return 0; } /** * 更新分镜表中的视频任务状态 * * @return void */ private function updateSegmentVideoStatus() { try { // 获取所有状态为"生成中"的分镜(兼容分镜模式和全能模式) $segments = DB::table('mp_episode_segments') ->where('video_task_status', '生成中') ->whereNotNull('video_task_id') ->where('video_task_id', '<>', '') ->where('created_at', '<', date('Y-m-d H:i:s', strtotime('-20 hours'))) ->select('id', 'segment_id', 'video_task_id', 'audio_duration') ->get(); if ($segments->isEmpty()) { return; } dLog('command')->info('开始更新分镜视频任务状态', ['count' => $segments->count()]); $successCount = 0; $failedCount = 0; $processingCount = 0; foreach ($segments as $segment) { $videoTaskId = $segment->video_task_id; // 兼容两种模式:优先使用segment_id(分镜模式),如果为空则使用id(全能模式的act_id) $recordId = $segment->id; $segmentId = $segment->segment_id ?: $segment->id; // 查询视频生成任务状态 $videoTask = DB::table('mp_generate_video_tasks') ->where('id', $videoTaskId) ->select('id', 'status', 'result_url', 'compressed_url', 'last_frame_url', 'error_message', 'result_json', 'extra_params', 'alias_segment_id', 'alias_act_id') ->first(); if (!$videoTask) { dLog('command')->warning('分镜关联的视频任务不存在', [ 'record_id' => $recordId, 'segment_id' => $segmentId, 'video_task_id' => $videoTaskId ]); continue; } $updateData = ['updated_at' => date('Y-m-d H:i:s')]; // 根据任务状态更新分镜表 if ($videoTask->status === 'success') { $updateData['video_task_status'] = '已完成'; $updateData['origin_video_url'] = $videoTask->result_url; $updateData['video_url'] = $videoTask->compressed_url ?: $videoTask->result_url; $updateData['current_type'] = 2; // 如果有尾帧图片,也更新 if (!empty($videoTask->last_frame_url)) { $updateData['last_frame_url'] = $videoTask->last_frame_url; } // 计算视频时长 $video_duration = 0; if (!empty($videoTask->result_json)) { $resultJson = is_array($videoTask->result_json) ? $videoTask->result_json : json_decode($videoTask->result_json, true); if (isset($resultJson['duration'])) { // 如果直接返回duration,使用该值 $video_duration = (int)$resultJson['duration']; } elseif (isset($resultJson['frames']) && isset($resultJson['framespersecond'])) { // 如果返回frames和framespersecond,计算时长 $frames = (int)$resultJson['frames']; $fps = (int)$resultJson['framespersecond']; if ($fps > 0) { $video_duration = (int)floor($frames / $fps); } } } // 如果没有计算出时长,使用任务表中的设定值 if ($video_duration <= 0 && !empty($videoTask->extra_params)) { $extraParams = is_array($videoTask->extra_params) ? $videoTask->extra_params : json_decode($videoTask->extra_params, true); if (isset($extraParams['duration'])) { $video_duration = (int)$extraParams['duration']; } } // 只有当video_duration > 0时才添加到更新数据中 if ($video_duration > 0) { $updateData['video_duration'] = $video_duration; $updateData['video_time_point_start'] = 0; $segment_id = getProp($videoTask, 'alias_segment_id'); $act_id = getProp($videoTask, 'alias_act_id'); if ($segment_id) { // 分镜模式:使用segment_id查询 $audio_duration = DB::table('mp_episode_segments')->where('segment_id', $segmentId)->value('audio_duration'); } elseif ($act_id) { // 全能模式(全能模式):如果传入了video_duration直接返回,否则查询audio_duration if ($video_duration !== null && $video_duration > 0) { $audio_duration = $video_duration; } $audio_duration = DB::table('mp_episode_segments')->where('id', $segmentId)->value('audio_duration'); } $updateData['video_time_point_end'] = $audio_duration ?? 0; } $successCount++; // 更新分镜视频成功后新增对话记录 $this->addVideoGenerationRecords($videoTask, $segmentId, $videoTask->result_url); dLog('command')->info('分镜视频生成成功', [ 'record_id' => $recordId, 'segment_id' => $segmentId, 'video_task_id' => $videoTaskId, 'video_url' => $videoTask->result_url, 'video_duration' => $video_duration ]); } elseif ($videoTask->status === 'failed') { $updateData['video_task_status'] = '失败'; $failedCount++; dLog('command')->error('分镜视频生成失败', [ 'record_id' => $recordId, 'segment_id' => $segmentId, 'video_task_id' => $videoTaskId, 'error' => $videoTask->error_message ]); logDB('command', 'error', '分镜视频生成失败', [ 'record_id' => $recordId, 'segment_id' => $segmentId, 'video_task_id' => $videoTaskId, 'error' => $videoTask->error_message ]); } else { // 仍在处理中,不更新 $processingCount++; continue; } // 更新分镜表(使用主键id更新,兼容两种模式) DB::table('mp_episode_segments') ->where('id', $recordId) ->update($updateData); } dLog('command')->info('分镜视频任务状态更新完成', [ 'total' => $segments->count(), 'success' => $successCount, 'failed' => $failedCount, 'processing' => $processingCount ]); } catch (\Exception $e) { dLog('command')->error('更新分镜视频任务状态异常: ' . $e->getMessage()); logDB('command', 'error', '更新分镜视频任务状态异常', [ 'error' => $e->getMessage(), 'trace' => $e->getTraceAsString() ]); } } /** * 添加视频生成对话记录 * @param array $task 任务数据 * @param string|int $segmentIdOrActId segment_id(分镜模式)或 act_id(全能模式) * @param string $videoUrl 视频URL * @return void */ private function addVideoGenerationRecords($task, $segmentIdOrActId, $videoUrl) { try { $segment_id = getProp($task, 'alias_segment_id'); $act_id = getProp($task, 'alias_act_id'); $reference_images = getProp($task, 'ref_image_url'); if (!$reference_images) $reference_images = json_encode([], 256); if (!empty($segment_id)) { // 分镜模式:使用segment_id查询 $segment = DB::table('mp_episode_segments as a')->leftJoin('mp_animes as b', 'a.anime_id', 'b.id') ->where('a.segment_id', $segmentIdOrActId) ->select('b.user_id as uid', 'a.anime_id', 'a.episode_number', 'a.tail_frame', 'a.segment_id') ->first(); } elseif (!empty($act_id)) { // 全能模式:使用id查询 $segment = DB::table('mp_episode_segments as a')->leftJoin('mp_animes as b', 'a.anime_id', 'b.id') ->where('a.id', $segmentIdOrActId) ->select('b.user_id as uid', 'a.anime_id', 'a.episode_number', 'a.act_content', 'a.act_show_content', 'a.id as act_id') ->first(); } else { dLog('generate')->warning('无法确定查询模式,任务缺少segment_id和act_id', ['segment_id_or_act_id' => $segmentIdOrActId]); return; } if (!$segment) { dLog('generate')->warning('分镜信息不存在,无法添加对话记录', ['segment_id_or_act_id' => $segmentIdOrActId]); return; } $now = date('Y-m-d H:i:s'); // 根据模式确定保存的字段和值 if (!empty($segment_id)) { // 分镜模式:保存segment_id字段 $recordField = 'segment_id'; $recordValue = $segmentIdOrActId; $userContent = '图片转视频'; $assistantContent = $segment->tail_frame ?: ''; } else { // 全能模式:保存act_id字段 $recordField = 'act_id'; $recordValue = $segmentIdOrActId; $userContent = '片段转视频'; $assistantContent = $segment->act_show_content ? $segment->act_show_content : $segment->act_content; if (!$assistantContent) $assistantContent = ''; } // 保存对话记录 $records = [ [ 'uid' => $segment->uid, 'anime_id' => $segment->anime_id, 'sequence' => $segment->episode_number, 'role' => 'user', 'content' => $userContent, $recordField => $recordValue, 'video_url' => '', 'reference_images' => json_encode([], 256), 'created_at' => $now, 'updated_at' => $now ], [ 'uid' => $segment->uid, 'anime_id' => $segment->anime_id, 'sequence' => $segment->episode_number, 'role' => 'assistant', 'content' => $assistantContent, $recordField => $recordValue, 'video_url' => $videoUrl, 'reference_images' => $reference_images, 'created_at' => $now, 'updated_at' => $now ] ]; DB::table('mp_anime_records')->insert($records); dLog('generate')->info('视频生成对话记录添加成功', [ 'segment_id_or_act_id' => $segmentIdOrActId, 'video_url' => $videoUrl, 'uid' => $segment->uid, 'anime_id' => $segment->anime_id ]); } catch (\Exception $e) { dLog('generate')->error('添加视频生成对话记录失败: ' . $e->getMessage(), [ 'segment_id' => $segmentIdOrActId, 'video_url' => $videoUrl, 'error' => $e->getMessage() ]); } } }