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:
@@ -13,9 +13,12 @@ importing the other.
|
||||
"""
|
||||
|
||||
import re
|
||||
import shutil
|
||||
import uuid
|
||||
from pathlib import Path
|
||||
|
||||
import anyio
|
||||
|
||||
from app.domain.errors import NotFoundError
|
||||
from app.domain.ports import TrackRepository
|
||||
|
||||
@@ -60,6 +63,12 @@ def hls_segment_path(root: Path, track_id: uuid.UUID, name: str) -> Path | None:
|
||||
return hls_dir(root, track_id) / name
|
||||
|
||||
|
||||
def remove_track_cache(root: Path, track_id: uuid.UUID) -> None:
|
||||
"""Delete every cached rendition for a track (Opus + HLS). Best-effort — used
|
||||
when a track is deleted so its transcode cache doesn't dangle forever."""
|
||||
shutil.rmtree(track_cache_dir(root, track_id), ignore_errors=True)
|
||||
|
||||
|
||||
class TranscodeService:
|
||||
"""Request-side cache lookups for transcoded renditions."""
|
||||
|
||||
@@ -83,14 +92,16 @@ class TranscodeService:
|
||||
if bitrate is None:
|
||||
return None
|
||||
path = opus_path(self._root, track_id, bitrate)
|
||||
return path if path.exists() else None
|
||||
exists = await anyio.to_thread.run_sync(path.exists)
|
||||
return path if exists else None
|
||||
|
||||
async def hls_playlist(self, track_id: uuid.UUID) -> Path | None:
|
||||
"""Cached HLS playlist if generated, else ``None`` (validates the track
|
||||
exists so an unknown id 404s rather than silently missing)."""
|
||||
await self._require_streamable(track_id)
|
||||
path = hls_playlist_path(self._root, track_id)
|
||||
return path if path.exists() else None
|
||||
exists = await anyio.to_thread.run_sync(path.exists)
|
||||
return path if exists else None
|
||||
|
||||
def hls_segment(self, track_id: uuid.UUID, name: str) -> Path | None:
|
||||
path = hls_segment_path(self._root, track_id, name)
|
||||
|
||||
Reference in New Issue
Block a user