From ab4ff6c25a13df334b8b5e365cd9eb70177293f6 Mon Sep 17 00:00:00 2001 From: Shuming Liu Date: Sat, 12 Sep 2026 11:28:32 +0800 Subject: [PATCH] webm file supported --- sentence_api/processing.py | 55 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/sentence_api/processing.py b/sentence_api/processing.py index 1c2fe60..3fe7871 100644 --- a/sentence_api/processing.py +++ b/sentence_api/processing.py @@ -29,6 +29,60 @@ from .transcription import ( logger = logging.getLogger(__name__) MOSS_ALGORITHM_VERSION = "moss-punctuation-v3" SILENCE_ALGORITHM_VERSION = "silence-rms-v1" +_CUES_ID = b"\x1e\x53\xb6\x80" +_CLUSTER_ID = b"\x1f\x43\xb6\x75" +_HEADER_SCAN_SIZE = 64 * 1024 +_WEBM_SUFFIXES = {".webm", ".mkv"} + + +def _webm_cues_at_front(filepath: Path) -> bool: + """Return True if WebM/MKV CUES appear before the first Cluster.""" + try: + with open(filepath, "rb") as f: + header = f.read(_HEADER_SCAN_SIZE) + except OSError: + return True # can't read → assume OK, don't touch + cues_pos = header.find(_CUES_ID) + cluster_pos = header.find(_CLUSTER_ID) + if cues_pos != -1 and (cluster_pos == -1 or cues_pos < cluster_pos): + return True + return False + + +def _ensure_webm_cues_front(filepath: Path) -> None: + """Re-mux a WebM/MKV file (stream copy) if CUES are not at the front. + + YouTube-dl / yt-dlp writes CUES at the end by default, which slows HTTP + Range seeking. This runs once during upload processing and takes seconds. + """ + if filepath.suffix.lower() not in _WEBM_SUFFIXES: + return + if _webm_cues_at_front(filepath): + return + ffmpeg = shutil.which("ffmpeg") + if not ffmpeg: + logger.warning("ffmpeg not found; skipping WebM CUES optimization.") + return + tmp = filepath.with_suffix(".cues_tmp" + filepath.suffix) + try: + result = subprocess.run( + [ffmpeg, "-y", "-loglevel", "error", + "-i", str(filepath), "-c", "copy", + "-cues_to_front", "1", str(tmp)], + capture_output=True, timeout=300, + ) + if result.returncode == 0 and tmp.is_file() and tmp.stat().st_size > 0: + tmp.replace(filepath) + logger.info("WebM CUES moved to front: %s", filepath.name) + else: + logger.warning( + "WebM CUES re-mux failed for %s: rc=%d stderr=%s", + filepath.name, result.returncode, result.stderr.decode()[-200:], + ) + except (subprocess.TimeoutExpired, OSError) as exc: + logger.warning("WebM CUES re-mux error for %s: %s", filepath.name, exc) + finally: + tmp.unlink(missing_ok=True) class VideoProcessor: @@ -55,6 +109,7 @@ class VideoProcessor: self.repository.mark_processing(video_hash) work_path: Optional[Path] = None try: + _ensure_webm_cues_front(media_path) if self.transcriber.available: work_path = self.settings.work_dir / f"{video_hash}-{uuid.uuid4().hex}.wav" extract_audio(media_path, work_path)