Bläddra i källkod

导出全部分镜接口新增音频的导出

lh 5 månader sedan
förälder
incheckning
256d661208
1 ändrade filer med 129 tillägg och 3 borttagningar
  1. 129 3
      app/Http/Controllers/Anime/AnimeController.php

+ 129 - 3
app/Http/Controllers/Anime/AnimeController.php

@@ -1089,14 +1089,14 @@ class AnimeController extends BaseController
                 Utils::throwError('20003:未找到剧集信息');
                 Utils::throwError('20003:未找到剧集信息');
             }
             }
             
             
-            // 获取所有分镜图片
+            // 获取所有分镜图片和音频
             $segments = DB::table('mp_episode_segments')
             $segments = DB::table('mp_episode_segments')
                 ->where('anime_id', $animeId)
                 ->where('anime_id', $animeId)
                 ->where('episode_id', $episodeId)
                 ->where('episode_id', $episodeId)
                 ->whereNotNull('img_url')
                 ->whereNotNull('img_url')
                 ->where('img_url', '!=', '')
                 ->where('img_url', '!=', '')
                 ->orderBy('segment_number')
                 ->orderBy('segment_number')
-                ->select('segment_id', 'segment_number', 'img_url', 'act_number', 'act_title')
+                ->select('segment_id', 'segment_number', 'img_url', 'audio_url', 'act_number', 'act_title')
                 ->get();
                 ->get();
             
             
             if ($segments->isEmpty()) {
             if ($segments->isEmpty()) {
@@ -1176,6 +1176,121 @@ class AnimeController extends BaseController
                 Utils::throwError('20003:没有成功下载任何图片');
                 Utils::throwError('20003:没有成功下载任何图片');
             }
             }
             
             
+            // 处理音频合成
+            $mergedAudioPath = null;
+            try {
+                // 检查 FFmpeg 是否可用
+                exec('which ffmpeg 2>&1', $ffmpegCheck, $ffmpegReturnCode);
+                $hasFfmpeg = ($ffmpegReturnCode === 0);
+                
+                if ($hasFfmpeg) {
+                    // 下载所有分镜的音频文件
+                    $audioFiles = [];
+                    foreach ($segments as $segment) {
+                        if (!empty($segment->audio_url)) {
+                            try {
+                                $context = stream_context_create([
+                                    'http' => [
+                                        'timeout' => 30,
+                                        'follow_location' => 1,
+                                        'ignore_errors' => true
+                                    ],
+                                    'ssl' => [
+                                        'verify_peer' => false,
+                                        'verify_peer_name' => false
+                                    ]
+                                ]);
+                                
+                                $audioData = @file_get_contents($segment->audio_url, false, $context);
+                                
+                                if ($audioData !== false && !empty($audioData)) {
+                                    // 获取音频文件扩展名
+                                    $audioExtension = pathinfo(parse_url($segment->audio_url, PHP_URL_PATH), PATHINFO_EXTENSION);
+                                    if (empty($audioExtension)) {
+                                        $audioExtension = 'wav';
+                                    }
+                                    
+                                    $audioFileName = sprintf('audio_%03d.%s', $segment->segment_number, $audioExtension);
+                                    $audioFilePath = $tempDir . '/' . $audioFileName;
+                                    file_put_contents($audioFilePath, $audioData);
+                                    $audioFiles[] = $audioFilePath;
+                                } else {
+                                    dLog('anime')->warning('音频下载失败', [
+                                        'segment_id' => $segment->segment_id,
+                                        'audio_url' => $segment->audio_url
+                                    ]);
+                                }
+                            } catch (\Exception $e) {
+                                dLog('anime')->error('下载音频异常', [
+                                    'segment_id' => $segment->segment_id,
+                                    'audio_url' => $segment->audio_url,
+                                    'error' => $e->getMessage()
+                                ]);
+                            }
+                        }
+                    }
+                    
+                    // 如果有音频文件,则进行合成
+                    if (!empty($audioFiles)) {
+                        // 创建 FFmpeg concat 文件列表
+                        $concatFile = $tempDir . '/concat.txt';
+                        $concatContent = '';
+                        foreach ($audioFiles as $audioFile) {
+                            // 使用相对路径,避免路径中的特殊字符问题
+                            $concatContent .= "file '" . basename($audioFile) . "'\n";
+                        }
+                        file_put_contents($concatFile, $concatContent);
+                        
+                        // 合成音频文件
+                        $mergedAudioPath = $tempDir . '/tts.wav';
+                        
+                        // 使用 FFmpeg 合并音频,转换为统一的 WAV 格式
+                        $ffmpegCommand = sprintf(
+                            'cd %s && ffmpeg -f concat -safe 0 -i %s -ar 44100 -ac 2 -y %s 2>&1',
+                            escapeshellarg($tempDir),
+                            escapeshellarg(basename($concatFile)),
+                            escapeshellarg(basename($mergedAudioPath))
+                        );
+                        
+                        exec($ffmpegCommand, $ffmpegOutput, $ffmpegReturnCode);
+                        
+                        if ($ffmpegReturnCode !== 0 || !file_exists($mergedAudioPath)) {
+                            dLog('anime')->error('FFmpeg 合成音频失败', [
+                                'command' => $ffmpegCommand,
+                                'output' => implode("\n", $ffmpegOutput),
+                                'return_code' => $ffmpegReturnCode
+                            ]);
+                            $mergedAudioPath = null;
+                        } else {
+                            dLog('anime')->info('音频合成成功', [
+                                'audio_count' => count($audioFiles),
+                                'output_file' => $mergedAudioPath,
+                                'file_size' => filesize($mergedAudioPath)
+                            ]);
+                        }
+                        
+                        // 清理临时音频文件和 concat 文件
+                        foreach ($audioFiles as $audioFile) {
+                            if (file_exists($audioFile)) {
+                                @unlink($audioFile);
+                            }
+                        }
+                        if (file_exists($concatFile)) {
+                            @unlink($concatFile);
+                        }
+                    }
+                } else {
+                    dLog('anime')->warning('FFmpeg 未安装,跳过音频合成');
+                }
+            } catch (\Exception $e) {
+                dLog('anime')->error('音频合成过程异常', [
+                    'error' => $e->getMessage(),
+                    'trace' => $e->getTraceAsString()
+                ]);
+                // 音频合成失败不影响图片导出
+                $mergedAudioPath = null;
+            }
+            
             // 创建zip文件
             // 创建zip文件
             $zipFileName = sprintf(
             $zipFileName = sprintf(
                 '%s.zip',
                 '%s.zip',
@@ -1191,15 +1306,26 @@ class AnimeController extends BaseController
                 Utils::throwError('20003:创建ZIP文件失败');
                 Utils::throwError('20003:创建ZIP文件失败');
             }
             }
             
             
-            // 添加文件到zip
+            // 添加图片文件到zip
             foreach ($downloadedFiles as $file) {
             foreach ($downloadedFiles as $file) {
                 $zip->addFile($file, basename($file));
                 $zip->addFile($file, basename($file));
             }
             }
             
             
+            // 添加合成的音频文件到zip
+            if ($mergedAudioPath && file_exists($mergedAudioPath)) {
+                $zip->addFile($mergedAudioPath, 'tts.wav');
+            }
+            
             $zip->close();
             $zip->close();
             
             
             // 清理临时图片文件
             // 清理临时图片文件
             array_map('unlink', $downloadedFiles);
             array_map('unlink', $downloadedFiles);
+            
+            // 清理合成的音频文件
+            if ($mergedAudioPath && file_exists($mergedAudioPath)) {
+                @unlink($mergedAudioPath);
+            }
+            
             rmdir($tempDir);
             rmdir($tempDir);
             
             
             // 返回文件下载响应
             // 返回文件下载响应