video_editor_task.py 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111
  1. import json
  2. import logging
  3. import os
  4. import subprocess
  5. import time
  6. from typing import Any
  7. from app.service.task.base_task import BaseTask
  8. from app.service.task.task_param import TaskParam
  9. from app.service.task.task_result import TaskResult
  10. from app.util.download_util import get_relative_path
  11. from app.util.file_util import copy_file, move_file
  12. from app.util.res_util import get_resources_path
  13. logger = logging.getLogger(__name__)
  14. class VideoEditorTask(BaseTask):
  15. def get_type(self) -> str:
  16. return "video_editor"
  17. def execute(self, param: TaskParam) -> TaskResult:
  18. images = param.inputs.get("images") if isinstance(param.inputs.get("images"), list) else []
  19. videos = param.inputs.get("videos") if isinstance(param.inputs.get("videos"), list) else []
  20. logger.info("[video_editor] Starting: %d images, %d videos, resources=%s",
  21. len(images), len(videos), param.resources)
  22. t0 = time.time()
  23. try:
  24. src = os.path.join(get_resources_path(), "video_editor", "compile")
  25. dst = os.path.join(param.workspace, "compile")
  26. copy_file(src, dst)
  27. os.chmod(dst, 0o755)
  28. logger.info("[video_editor] Copied compile to workspace")
  29. self._update_json(param.workspace, param.resources, videos, images)
  30. logger.info("[video_editor] Updated JSON config with media paths")
  31. self._execute_compile(param.workspace, param.resources)
  32. output_path = self._move_output(param.workspace, param.resources, param.step_id)
  33. logger.info("[video_editor] Completed: output=%s in %.2fs", output_path, time.time() - t0)
  34. return TaskResult.success({"videos": [output_path]})
  35. except Exception as e:
  36. logger.error("[video_editor] Failed in %.2fs: %s", time.time() - t0, e, exc_info=True)
  37. return TaskResult.failure(str(e))
  38. @staticmethod
  39. def _update_json(workspace: str, resources: str, videos: list, images: list):
  40. json_path = os.path.join(workspace, resources)
  41. with open(json_path, "r", encoding="utf-8") as f:
  42. data = json.load(f)
  43. video_track = data.get("videoTrack", [])
  44. vi, ii = 0, 0
  45. for track in video_track:
  46. t = track.get("type")
  47. if t == "video" and vi < len(videos):
  48. logger.info("[video_editor] Assigning video[%d] -> track path: %s", vi, videos[vi])
  49. track["path"] = videos[vi]
  50. vi += 1
  51. elif t == "image" and ii < len(images):
  52. logger.info("[video_editor] Assigning image[%d] -> track path: %s", ii, images[ii])
  53. track["path"] = images[ii]
  54. ii += 1
  55. with open(json_path, "w", encoding="utf-8") as f:
  56. json.dump(data, f, indent=2, ensure_ascii=False)
  57. @staticmethod
  58. def _execute_compile(workspace: str, resources: str):
  59. cmd = f"./compile -m {resources}"
  60. logger.info("[video_editor] Executing: %s (cwd=%s)", cmd, workspace)
  61. cmd_t0 = time.time()
  62. result = subprocess.run(
  63. ["sh", "-c", cmd], cwd=workspace, capture_output=True, text=True,
  64. )
  65. cmd_elapsed = time.time() - cmd_t0
  66. if result.stdout:
  67. logger.info("[video_editor] stdout: %s", result.stdout.strip())
  68. if result.stderr:
  69. logger.warning("[video_editor] stderr: %s", result.stderr.strip())
  70. if result.returncode != 0:
  71. logger.error("[video_editor] compile failed: exit_code=%d in %.2fs", result.returncode, cmd_elapsed)
  72. else:
  73. logger.info("[video_editor] compile completed: exit_code=0 in %.2fs", cmd_elapsed)
  74. @staticmethod
  75. def _move_output(workspace: str, resources: str, step_id: str) -> str | None:
  76. json_path = os.path.join(workspace, resources)
  77. with open(json_path, "r", encoding="utf-8") as f:
  78. data = json.load(f)
  79. output_cfg = data.get("output", {})
  80. output_path = output_cfg.get("path")
  81. if not output_path:
  82. logger.warning("[video_editor] No output path found in config")
  83. return None
  84. src = os.path.join(workspace, output_path)
  85. dest_dir = os.path.join(workspace, step_id)
  86. os.makedirs(dest_dir, exist_ok=True)
  87. dest = os.path.join(dest_dir, os.path.basename(output_path))
  88. move_file(src, dest)
  89. rel_path = get_relative_path(dest, workspace)
  90. logger.info("[video_editor] Output moved: %s -> %s", output_path, rel_path)
  91. return rel_path