SegmentCheckAudioCommand.php 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  1. <?php
  2. namespace App\Console\Anime;
  3. use Illuminate\Console\Command;
  4. use Illuminate\Support\Facades\DB;
  5. use GuzzleHttp\Client;
  6. class SegmentCheckAudioCommand extends Command
  7. {
  8. /**
  9. * The name and signature of the console command.
  10. *
  11. * @var string
  12. */
  13. protected $signature = 'Segment:checkAudio';
  14. /**
  15. * The console command description.
  16. *
  17. * @var string
  18. */
  19. protected $description = '检查分镜音频信息';
  20. public function __construct()
  21. {
  22. parent::__construct();
  23. }
  24. /**
  25. * Execute the console command.
  26. *
  27. * @return int
  28. */
  29. public function handle()
  30. {
  31. dLog('generate')->info('====================开始检查分镜音频信息====================');
  32. $time_start = microtime(true);
  33. $timeout = 50; // 超时时间50秒
  34. $check_interval = 3; // 每3秒检查一次
  35. while (true) {
  36. // 检查是否超时
  37. $elapsed_time = microtime(true) - $time_start;
  38. if ($elapsed_time >= $timeout) {
  39. dLog('generate')->info('检查超时,退出循环');
  40. break;
  41. }
  42. // 1. 查询mp_dub_video_tasks表中1分钟之前创建的generate_status=执行中的任务,并重新调用API
  43. $oneMinuteAgo = date('Y-m-d H:i:s', strtotime('-1 minute'));
  44. $pendingTasks = DB::table('mp_dub_video_tasks')
  45. ->where('generate_status', '执行中')
  46. ->where('created_at', '<=', $oneMinuteAgo)
  47. ->limit(10) // 每次处理10个任务
  48. ->get();
  49. if ($pendingTasks->isNotEmpty()) {
  50. dLog('generate')->info('找到待重试的音频任务', ['count' => $pendingTasks->count()]);
  51. foreach ($pendingTasks as $task) {
  52. sleep(1);
  53. try {
  54. $dubTaskId = $task->id;
  55. // 请求远程服务器执行生成
  56. $client = new Client(['timeout' => 300, 'verify' => false]);
  57. $result = $client->get("http://122.9.129.83:5000/api/anime/genAudio?taskId={$dubTaskId}");
  58. $response = $result->getBody()->getContents();
  59. $response_arr = json_decode($response, true);
  60. if (!isset($response_arr['code']) || (int)$response_arr['code'] !== 0) {
  61. $error_msg = isset($response_arr['msg']) ? $response_arr['msg'] : '未知错误';
  62. dLog('anime')->error('火山生成音频失败', ['error' => $error_msg, 'task_id' => $dubTaskId]);
  63. logDB('anime', 'error', '火山生成音频失败', ['error' => $error_msg, 'task_id' => $dubTaskId]);
  64. // 标记任务为失败
  65. DB::table('mp_dub_video_tasks')
  66. ->where('id', $dubTaskId)
  67. ->update([
  68. 'generate_status' => '失败',
  69. 'updated_at' => date('Y-m-d H:i:s')
  70. ]);
  71. } else {
  72. dLog('generate')->info('音频任务重试成功', ['task_id' => $dubTaskId]);
  73. }
  74. } catch (\Exception $e) {
  75. dLog('anime')->error('重试音频任务异常', [
  76. 'task_id' => $task->id,
  77. 'error' => $e->getMessage()
  78. ]);
  79. }
  80. }
  81. }
  82. // 2. 查询mp_episode_segments表中img_url不为空但audio_url为空的分镜
  83. $segmentsNeedAudio = DB::table('mp_episode_segments')
  84. ->whereNotNull('img_url')
  85. ->where('img_url', '!=', '')
  86. ->where(function($query) {
  87. $query->whereNull('audio_url')
  88. ->orWhere('audio_url', '');
  89. })
  90. ->limit(10) // 每次处理10个分镜
  91. ->get();
  92. if ($segmentsNeedAudio->isNotEmpty()) {
  93. dLog('generate')->info('找到需要生成音频的分镜', ['count' => $segmentsNeedAudio->count()]);
  94. foreach ($segmentsNeedAudio as $segment) {
  95. sleep(1);
  96. try {
  97. $segment_id = $segment->segment_id;
  98. $dialogue = getProp($segment, 'dialogue');
  99. $now = date('Y-m-d H:i:s');
  100. // 如果有对话内容,创建视频配音合成任务
  101. if (!empty($dialogue)) {
  102. $generate_json = [
  103. 'text' => $dialogue,
  104. 'role' => getProp($segment, 'voice_actor'),
  105. 'voice_type' => getProp($segment, 'voice_type'),
  106. 'voice_name' => getProp($segment, 'voice_name'),
  107. 'emotion' => getProp($segment, 'emotion'),
  108. 'emotion_type' => getProp($segment, 'emotion_type'),
  109. 'gender' => getProp($segment, 'gender'),
  110. 'speed_ratio' => getProp($segment, 'speed_ratio', 0),
  111. 'loudness_ratio' => getProp($segment, 'loudness_ratio', 0),
  112. 'emotion_scale' => getProp($segment, 'emotion_scale', 0),
  113. 'pitch' => getProp($segment, 'pitch', 0),
  114. ];
  115. // 插入视频配音合成任务
  116. $dubTaskId = DB::table('mp_dub_video_tasks')->insertGetId([
  117. 'alias_segment_id' => $segment_id,
  118. 'generate_status' => '执行中',
  119. 'audio_url' => '',
  120. 'generate_json' => json_encode($generate_json, 256),
  121. 'created_at' => $now,
  122. 'updated_at' => $now,
  123. ]);
  124. if (!$dubTaskId) {
  125. dLog('anime')->error('创建配音任务失败', ['segment_id' => $segment_id]);
  126. continue;
  127. }
  128. // 请求远程服务器执行生成
  129. $client = new Client(['timeout' => 300, 'verify' => false]);
  130. $result = $client->get("http://122.9.129.83:5000/api/anime/genAudio?taskId={$dubTaskId}");
  131. $response = $result->getBody()->getContents();
  132. $response_arr = json_decode($response, true);
  133. if (!isset($response_arr['code']) || (int)$response_arr['code'] !== 0) {
  134. $error_msg = isset($response_arr['msg']) ? $response_arr['msg'] : '未知错误';
  135. dLog('anime')->error('火山生成音频失败', ['error' => $error_msg, 'task_id' => $dubTaskId, 'segment_id' => $segment_id]);
  136. logDB('anime', 'error', '火山生成音频失败', ['error' => $error_msg, 'task_id' => $dubTaskId, 'segment_id' => $segment_id]);
  137. } else {
  138. dLog('generate')->info('音频任务创建成功', ['task_id' => $dubTaskId, 'segment_id' => $segment_id]);
  139. // 更新分镜表,标记音频正在生成
  140. DB::table('mp_episode_segments')
  141. ->where('segment_id', $segment_id)
  142. ->update([
  143. 'audio_url' => '',
  144. 'updated_at' => $now
  145. ]);
  146. }
  147. } else {
  148. // dialogue为空时,直接写入默认音频信息
  149. DB::table('mp_episode_segments')
  150. ->where('segment_id', $segment_id)
  151. ->update([
  152. 'audio_url' => 'https://zw-audiobook.tos-cn-beijing.volces.com/effects/ellipses_2s.mp3',
  153. 'audio_duration' => 2.0,
  154. 'subtitle_info' => json_encode(['duration' => 2.0, 'words' => []], 256),
  155. 'updated_at' => $now
  156. ]);
  157. dLog('generate')->info('分镜无对话,写入默认音频', ['segment_id' => $segment_id]);
  158. }
  159. } catch (\Exception $e) {
  160. dLog('anime')->error('处理分镜音频异常', [
  161. 'segment_id' => $segment->segment_id,
  162. 'error' => $e->getMessage()
  163. ]);
  164. }
  165. }
  166. }
  167. // 如果没有待处理的任务,退出循环
  168. if ($pendingTasks->isEmpty() && $segmentsNeedAudio->isEmpty()) {
  169. dLog('generate')->info('没有待处理的音频任务,退出循环');
  170. break;
  171. }
  172. // 等待3秒后继续下一次检查
  173. sleep($check_interval);
  174. }
  175. $time_end = microtime(true);
  176. $execution_time = round($time_end - $time_start, 2);
  177. dLog('generate')->info('脚本执行时间: '.round($time_end-$time_start, 2).'秒');
  178. dLog('generate')->info('====================结束检查分镜音频信息====================');
  179. }
  180. }