refactor(transcode): thread fs ops + atomic cache publish

- run blocking Path.exists/read_text off the event loop via anyio.to_thread
- publish HLS renditions by building into a temp dir then atomic rename, so a
  reader never sees a playlist referencing half-written segments
- per-writer temp name for opus so two concurrent jobs can't interleave
- drop a track's cached renditions on delete so they don't dangle

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Цвылев Александр Вадимович
2026-07-29 10:53:35 +03:00
parent d16c6085c9
commit 9a78cf5261
5 changed files with 46 additions and 6 deletions
+5 -1
View File
@@ -7,6 +7,7 @@ quality renditions; HLS segments are AAC-in-MPEG-TS (broad player support).
"""
import asyncio
import uuid
from pathlib import Path
import anyio
@@ -30,7 +31,10 @@ class FfmpegTranscoder:
async def to_opus(self, src: Path, dest: Path, *, bitrate_kbps: int) -> None:
await anyio.to_thread.run_sync(_mkdir, dest.parent)
tmp = dest.with_suffix(dest.suffix + ".part")
# Per-writer temp name (not a shared ``.part``) so two concurrent jobs for
# the same rendition can't interleave into one file — each writes its own
# temp and the last atomic replace wins, both leaving a valid output.
tmp = dest.with_name(f"{dest.name}.{uuid.uuid4().hex}.part")
await self._run(
"-i", str(src),
"-vn", "-c:a", "libopus", "-b:a", f"{bitrate_kbps}k",