|
|
@@ -1051,6 +1051,181 @@ class AnimeController extends BaseController
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
+ * 导出分集的全部图片(下载并打包)
|
|
|
+ */
|
|
|
+ public function exportEpisodeImages(Request $request) {
|
|
|
+ // 忽略所有超时限制
|
|
|
+ set_time_limit(0);
|
|
|
+ ini_set('max_execution_time', '0');
|
|
|
+ ini_set('memory_limit', '512M');
|
|
|
+
|
|
|
+ $data = $request->all();
|
|
|
+
|
|
|
+ // 验证参数
|
|
|
+ $validator = Validator::make($data, [
|
|
|
+ 'anime_id' => 'required|string',
|
|
|
+ 'episode_id' => 'required|string',
|
|
|
+ ], [
|
|
|
+ 'anime_id.required' => '动漫ID不能为空',
|
|
|
+ 'episode_id.required' => '剧集ID不能为空',
|
|
|
+ ]);
|
|
|
+
|
|
|
+ if ($validator->fails()) {
|
|
|
+ Utils::throwError('1002:' . $validator->errors()->first());
|
|
|
+ }
|
|
|
+
|
|
|
+ $animeId = $data['anime_id'];
|
|
|
+ $episodeId = $data['episode_id'];
|
|
|
+
|
|
|
+ try {
|
|
|
+ // 获取剧集信息
|
|
|
+ $episode = DB::table('mp_anime_episodes')
|
|
|
+ ->where('anime_id', $animeId)
|
|
|
+ ->where('id', $episodeId)
|
|
|
+ ->select('episode_number', 'title')
|
|
|
+ ->first();
|
|
|
+
|
|
|
+ if (!$episode) {
|
|
|
+ Utils::throwError('20003:未找到剧集信息');
|
|
|
+ }
|
|
|
+
|
|
|
+ // 获取所有分镜图片
|
|
|
+ $segments = DB::table('mp_episode_segments')
|
|
|
+ ->where('anime_id', $animeId)
|
|
|
+ ->where('episode_id', $episodeId)
|
|
|
+ ->whereNotNull('img_url')
|
|
|
+ ->where('img_url', '!=', '')
|
|
|
+ ->orderBy('segment_number')
|
|
|
+ ->select('segment_id', 'segment_number', 'img_url', 'act_number', 'act_title')
|
|
|
+ ->get();
|
|
|
+
|
|
|
+ if ($segments->isEmpty()) {
|
|
|
+ Utils::throwError('20003:该剧集暂无图片可导出');
|
|
|
+ }
|
|
|
+
|
|
|
+ // 创建临时目录
|
|
|
+ $tempDir = storage_path('app/temp/episode_images_' . time() . '_' . mt_rand(1000, 9999));
|
|
|
+ if (!file_exists($tempDir)) {
|
|
|
+ mkdir($tempDir, 0755, true);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 下载图片
|
|
|
+ $downloadedFiles = [];
|
|
|
+ foreach ($segments as $segment) {
|
|
|
+ try {
|
|
|
+ $imgUrl = $segment->img_url;
|
|
|
+ if (empty($imgUrl)) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 获取图片扩展名
|
|
|
+ $extension = pathinfo(parse_url($imgUrl, PHP_URL_PATH), PATHINFO_EXTENSION);
|
|
|
+ if (empty($extension)) {
|
|
|
+ $extension = 'jpg';
|
|
|
+ }
|
|
|
+
|
|
|
+ // 构建文件名:分镜序号.扩展名
|
|
|
+ $fileName = sprintf(
|
|
|
+ '%03d.%s',
|
|
|
+ $segment->segment_number,
|
|
|
+ $extension
|
|
|
+ );
|
|
|
+ $filePath = $tempDir . '/' . $fileName;
|
|
|
+
|
|
|
+ // 下载图片(使用file_get_contents,兼容性更好)
|
|
|
+ $context = stream_context_create([
|
|
|
+ 'http' => [
|
|
|
+ 'timeout' => 30,
|
|
|
+ 'follow_location' => 1,
|
|
|
+ 'ignore_errors' => true
|
|
|
+ ],
|
|
|
+ 'ssl' => [
|
|
|
+ 'verify_peer' => false,
|
|
|
+ 'verify_peer_name' => false
|
|
|
+ ]
|
|
|
+ ]);
|
|
|
+
|
|
|
+ $imageData = @file_get_contents($imgUrl, false, $context);
|
|
|
+
|
|
|
+ if ($imageData !== false && !empty($imageData)) {
|
|
|
+ file_put_contents($filePath, $imageData);
|
|
|
+ $downloadedFiles[] = $filePath;
|
|
|
+ } else {
|
|
|
+ dLog('anime')->warning('图片下载失败', [
|
|
|
+ 'segment_id' => $segment->segment_id,
|
|
|
+ 'img_url' => $imgUrl
|
|
|
+ ]);
|
|
|
+ }
|
|
|
+
|
|
|
+ } catch (\Exception $e) {
|
|
|
+ dLog('anime')->error('下载图片异常', [
|
|
|
+ 'segment_id' => $segment->segment_id,
|
|
|
+ 'img_url' => $segment->img_url,
|
|
|
+ 'error' => $e->getMessage()
|
|
|
+ ]);
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (empty($downloadedFiles)) {
|
|
|
+ // 清理临时目录
|
|
|
+ if (file_exists($tempDir)) {
|
|
|
+ array_map('unlink', glob("$tempDir/*"));
|
|
|
+ rmdir($tempDir);
|
|
|
+ }
|
|
|
+ Utils::throwError('20003:没有成功下载任何图片');
|
|
|
+ }
|
|
|
+
|
|
|
+ // 创建zip文件
|
|
|
+ $zipFileName = sprintf(
|
|
|
+ '%s.zip',
|
|
|
+ $episode->title ?: '未命名'
|
|
|
+ );
|
|
|
+ $zipFilePath = storage_path('app/temp/' . $zipFileName);
|
|
|
+
|
|
|
+ $zip = new \ZipArchive();
|
|
|
+ if ($zip->open($zipFilePath, \ZipArchive::CREATE | \ZipArchive::OVERWRITE) !== true) {
|
|
|
+ // 清理临时文件
|
|
|
+ array_map('unlink', $downloadedFiles);
|
|
|
+ rmdir($tempDir);
|
|
|
+ Utils::throwError('20003:创建ZIP文件失败');
|
|
|
+ }
|
|
|
+
|
|
|
+ // 添加文件到zip
|
|
|
+ foreach ($downloadedFiles as $file) {
|
|
|
+ $zip->addFile($file, basename($file));
|
|
|
+ }
|
|
|
+
|
|
|
+ $zip->close();
|
|
|
+
|
|
|
+ // 清理临时图片文件
|
|
|
+ array_map('unlink', $downloadedFiles);
|
|
|
+ rmdir($tempDir);
|
|
|
+
|
|
|
+ // 返回文件下载响应
|
|
|
+ return response()->download($zipFilePath, $zipFileName)->deleteFileAfterSend(true);
|
|
|
+
|
|
|
+ } catch (\Exception $e) {
|
|
|
+ dLog('anime')->error('导出分集图片失败', [
|
|
|
+ 'anime_id' => $animeId,
|
|
|
+ 'episode_id' => $episodeId,
|
|
|
+ 'error' => $e->getMessage()
|
|
|
+ ]);
|
|
|
+
|
|
|
+ // 清理可能存在的临时文件
|
|
|
+ if (isset($tempDir) && file_exists($tempDir)) {
|
|
|
+ array_map('unlink', glob("$tempDir/*"));
|
|
|
+ rmdir($tempDir);
|
|
|
+ }
|
|
|
+ if (isset($zipFilePath) && file_exists($zipFilePath)) {
|
|
|
+ unlink($zipFilePath);
|
|
|
+ }
|
|
|
+
|
|
|
+ throw $e;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
* 监控分镜图片和音频生成任务进度(SSE)
|
|
|
*/
|
|
|
public function monitorSegmentTasks(Request $request) {
|