chore: sync code and project files
This commit is contained in:
102
modules/preview_proxy.py
Normal file
102
modules/preview_proxy.py
Normal file
@@ -0,0 +1,102 @@
|
||||
"""
|
||||
Generate and cache low-bitrate preview proxies for browser playback.
|
||||
|
||||
Goal:
|
||||
- Improve Remotion Player preview smoothness by serving smaller/faster-to-decode videos.
|
||||
- Keep original `source_path` for accurate export; only swap `source_url` for preview.
|
||||
|
||||
Design:
|
||||
- Deterministic cache key based on (path, mtime, size).
|
||||
- Proxy lives under config.TEMP_DIR / "proxy".
|
||||
- Generated with ffmpeg: scale/pad + fps downsample + faststart + no audio.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import hashlib
|
||||
import os
|
||||
from pathlib import Path
|
||||
from typing import Optional, Tuple
|
||||
|
||||
import config
|
||||
from modules import ffmpeg_utils
|
||||
|
||||
|
||||
def _key_for_file(path: str) -> str:
|
||||
st = os.stat(path)
|
||||
raw = f"{path}|{st.st_mtime_ns}|{st.st_size}".encode("utf-8")
|
||||
return hashlib.sha1(raw).hexdigest() # short, deterministic
|
||||
|
||||
|
||||
def ensure_video_proxy(
|
||||
source_path: str,
|
||||
*,
|
||||
target_w: int = 540,
|
||||
target_h: int = 960,
|
||||
target_fps: int = 24,
|
||||
crf: int = 28,
|
||||
preset: str = "veryfast",
|
||||
) -> Tuple[Optional[str], Optional[str]]:
|
||||
"""
|
||||
Ensure a preview proxy exists for source_path.
|
||||
|
||||
Returns: (proxy_path, proxy_url) or (None, None) if source_path invalid.
|
||||
"""
|
||||
if not source_path or not os.path.exists(source_path):
|
||||
return None, None
|
||||
|
||||
proxy_dir = Path(config.TEMP_DIR) / "proxy"
|
||||
proxy_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
key = _key_for_file(source_path)
|
||||
out_name = f"proxy_{key}.mp4"
|
||||
out_path = proxy_dir / out_name
|
||||
|
||||
if out_path.exists() and out_path.stat().st_size > 1024:
|
||||
return str(out_path), f"/static/temp/proxy/{out_name}"
|
||||
|
||||
vf = (
|
||||
f"scale={target_w}:{target_h}:force_original_aspect_ratio=decrease,"
|
||||
f"pad={target_w}:{target_h}:(ow-iw)/2:(oh-ih)/2:black,"
|
||||
f"fps={target_fps}"
|
||||
)
|
||||
|
||||
cmd = [
|
||||
ffmpeg_utils.FFMPEG_PATH,
|
||||
"-y",
|
||||
"-i",
|
||||
source_path,
|
||||
"-an", # preview 不要音轨,减少解码负担(旁白/BGM 走单独轨道)
|
||||
"-vf",
|
||||
vf,
|
||||
"-c:v",
|
||||
"libx264",
|
||||
"-preset",
|
||||
preset,
|
||||
"-crf",
|
||||
str(crf),
|
||||
"-tune",
|
||||
"fastdecode",
|
||||
"-pix_fmt",
|
||||
"yuv420p",
|
||||
"-movflags",
|
||||
"+faststart",
|
||||
str(out_path),
|
||||
]
|
||||
|
||||
ffmpeg_utils._run_ffmpeg(cmd)
|
||||
if out_path.exists() and out_path.stat().st_size > 1024:
|
||||
return str(out_path), f"/static/temp/proxy/{out_name}"
|
||||
return None, None
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user