feat: 新增代码
This commit is contained in:
0
video_worker/app/utils/__init__.py
Normal file
0
video_worker/app/utils/__init__.py
Normal file
40
video_worker/app/utils/ffmpeg_utils.py
Normal file
40
video_worker/app/utils/ffmpeg_utils.py
Normal file
@@ -0,0 +1,40 @@
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def run_cmd(cmd: list[str]) -> None:
|
||||
proc = subprocess.run(cmd, capture_output=True, text=True)
|
||||
if proc.returncode != 0:
|
||||
raise RuntimeError(f"Command failed: {' '.join(cmd)}\nSTDOUT: {proc.stdout}\nSTDERR: {proc.stderr}")
|
||||
|
||||
|
||||
def frames_to_video(frames_pattern: str, fps: int, output_video_path: Path) -> None:
|
||||
output_video_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
cmd = [
|
||||
"ffmpeg",
|
||||
"-y",
|
||||
"-framerate",
|
||||
str(fps),
|
||||
"-i",
|
||||
frames_pattern,
|
||||
"-pix_fmt",
|
||||
"yuv420p",
|
||||
str(output_video_path),
|
||||
]
|
||||
run_cmd(cmd)
|
||||
|
||||
|
||||
def extract_first_frame(video_path: Path, first_frame_path: Path) -> None:
|
||||
first_frame_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
cmd = [
|
||||
"ffmpeg",
|
||||
"-y",
|
||||
"-i",
|
||||
str(video_path),
|
||||
"-vf",
|
||||
"select=eq(n\\,0)",
|
||||
"-vframes",
|
||||
"1",
|
||||
str(first_frame_path),
|
||||
]
|
||||
run_cmd(cmd)
|
||||
24
video_worker/app/utils/files.py
Normal file
24
video_worker/app/utils/files.py
Normal file
@@ -0,0 +1,24 @@
|
||||
import json
|
||||
from pathlib import Path
|
||||
from typing import Any, Dict
|
||||
|
||||
|
||||
TASK_VIDEO_NAME = "video.mp4"
|
||||
TASK_FIRST_FRAME_NAME = "first_frame.jpg"
|
||||
TASK_METADATA_NAME = "metadata.json"
|
||||
TASK_LOG_NAME = "run.log"
|
||||
|
||||
|
||||
def ensure_dir(path: Path) -> Path:
|
||||
path.mkdir(parents=True, exist_ok=True)
|
||||
return path
|
||||
|
||||
|
||||
def task_output_dir(base_output_dir: Path, task_id: str) -> Path:
|
||||
return ensure_dir(base_output_dir / task_id)
|
||||
|
||||
|
||||
def write_json(path: Path, data: Dict[str, Any]) -> None:
|
||||
path.parent.mkdir(parents=True, exist_ok=True)
|
||||
with path.open("w", encoding="utf-8") as f:
|
||||
json.dump(data, f, ensure_ascii=False, indent=2)
|
||||
12
video_worker/app/utils/image_utils.py
Normal file
12
video_worker/app/utils/image_utils.py
Normal file
@@ -0,0 +1,12 @@
|
||||
from pathlib import Path
|
||||
|
||||
from PIL import Image, ImageDraw, ImageFont
|
||||
|
||||
|
||||
def make_dummy_frame(path: Path, width: int, height: int, text: str, step: int) -> None:
|
||||
image = Image.new("RGB", (width, height), color=(25 + step * 5 % 200, 40, 60))
|
||||
draw = ImageDraw.Draw(image)
|
||||
font = ImageFont.load_default()
|
||||
draw.text((16, 16), text, fill=(240, 240, 240), font=font)
|
||||
draw.text((16, 38), f"frame={step}", fill=(220, 220, 220), font=font)
|
||||
image.save(path, format="JPEG", quality=90)
|
||||
24
video_worker/app/utils/logger.py
Normal file
24
video_worker/app/utils/logger.py
Normal file
@@ -0,0 +1,24 @@
|
||||
import logging
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def build_logger(name: str, log_level: str = "INFO", log_file: Path | None = None) -> logging.Logger:
|
||||
logger = logging.getLogger(name)
|
||||
logger.setLevel(getattr(logging, log_level.upper(), logging.INFO))
|
||||
|
||||
if logger.handlers:
|
||||
return logger
|
||||
|
||||
formatter = logging.Formatter("%(asctime)s | %(levelname)s | %(name)s | %(message)s")
|
||||
|
||||
stream_handler = logging.StreamHandler()
|
||||
stream_handler.setFormatter(formatter)
|
||||
logger.addHandler(stream_handler)
|
||||
|
||||
if log_file is not None:
|
||||
log_file.parent.mkdir(parents=True, exist_ok=True)
|
||||
file_handler = logging.FileHandler(log_file, encoding="utf-8")
|
||||
file_handler.setFormatter(formatter)
|
||||
logger.addHandler(file_handler)
|
||||
|
||||
return logger
|
||||
Reference in New Issue
Block a user