mirror of
https://github.com/myronblair/kino-app
synced 2026-06-30 17:50:16 -05:00
75 lines
2.3 KiB
Python
75 lines
2.3 KiB
Python
"""HLS transcoding via ffmpeg. Background-runs and updates DB."""
|
|
import asyncio
|
|
import logging
|
|
import shutil
|
|
from pathlib import Path
|
|
from typing import Optional
|
|
|
|
logger = logging.getLogger("kino.transcode")
|
|
|
|
|
|
async def transcode_to_hls(
|
|
source: Path,
|
|
out_dir: Path,
|
|
on_status,
|
|
):
|
|
"""
|
|
Run ffmpeg to produce HLS playlist + segments.
|
|
`on_status(status, error=None)` is awaited at start/end with status one of
|
|
'running'|'done'|'failed'.
|
|
Uses stream-copy where possible (fast, no re-encode).
|
|
"""
|
|
if not source.is_file():
|
|
await on_status("failed", error=f"Source missing: {source}")
|
|
return
|
|
out_dir.mkdir(parents=True, exist_ok=True)
|
|
playlist = out_dir / "playlist.m3u8"
|
|
|
|
cmd = [
|
|
"ffmpeg", "-y",
|
|
"-i", str(source),
|
|
"-c:v", "copy",
|
|
"-c:a", "copy",
|
|
"-bsf:v", "h264_mp4toannexb",
|
|
"-f", "hls",
|
|
"-hls_time", "6",
|
|
"-hls_list_size", "0",
|
|
"-hls_playlist_type", "vod",
|
|
"-hls_segment_filename", str(out_dir / "seg_%04d.ts"),
|
|
str(playlist),
|
|
]
|
|
|
|
await on_status("running")
|
|
try:
|
|
proc = await asyncio.create_subprocess_exec(
|
|
*cmd, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE,
|
|
)
|
|
_stdout, stderr = await proc.communicate()
|
|
if proc.returncode != 0:
|
|
err = stderr.decode("utf-8", errors="ignore")[-500:]
|
|
logger.error(f"ffmpeg failed: {err}")
|
|
await on_status("failed", error=err[:200])
|
|
# cleanup partial files
|
|
shutil.rmtree(out_dir, ignore_errors=True)
|
|
return
|
|
await on_status("done")
|
|
except FileNotFoundError:
|
|
await on_status("failed", error="ffmpeg not installed")
|
|
except Exception as e:
|
|
logger.exception("transcode crashed")
|
|
await on_status("failed", error=str(e))
|
|
shutil.rmtree(out_dir, ignore_errors=True)
|
|
|
|
|
|
def srt_to_vtt(srt_text: str) -> str:
|
|
"""Convert SRT subtitles to WebVTT format (simple, no styling)."""
|
|
lines = srt_text.replace("\r\n", "\n").split("\n")
|
|
out = ["WEBVTT", ""]
|
|
for line in lines:
|
|
# Replace SRT timestamp commas with VTT dots
|
|
if "-->" in line:
|
|
out.append(line.replace(",", "."))
|
|
else:
|
|
out.append(line)
|
|
return "\n".join(out)
|