84 lines
2.1 KiB
Python
84 lines
2.1 KiB
Python
"""Story audio cache storage helpers."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from dataclasses import dataclass
|
|
from datetime import datetime, timezone
|
|
from pathlib import Path
|
|
|
|
from app.core.config import settings
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class AudioCacheMetadata:
|
|
"""Metadata about one cached story audio file."""
|
|
|
|
exists: bool
|
|
path: str | None = None
|
|
size_bytes: int | None = None
|
|
updated_at: datetime | None = None
|
|
|
|
|
|
def build_story_audio_path(story_id: int) -> str:
|
|
"""Build the cache path for a story audio file."""
|
|
|
|
return str(Path(settings.story_audio_cache_dir) / f"story-{story_id}.mp3")
|
|
|
|
|
|
def audio_cache_exists(audio_path: str | None) -> bool:
|
|
"""Whether the cached audio file exists on disk."""
|
|
|
|
return bool(audio_path) and Path(audio_path).is_file()
|
|
|
|
|
|
def read_audio_cache(audio_path: str) -> bytes:
|
|
"""Read cached story audio bytes."""
|
|
|
|
return Path(audio_path).read_bytes()
|
|
|
|
|
|
def get_audio_cache_metadata(audio_path: str | None) -> AudioCacheMetadata:
|
|
"""Return cache metadata without reading the audio bytes."""
|
|
|
|
if not audio_path:
|
|
return AudioCacheMetadata(exists=False)
|
|
|
|
path = Path(audio_path)
|
|
if not path.is_file():
|
|
return AudioCacheMetadata(exists=False, path=str(path))
|
|
|
|
stat = path.stat()
|
|
return AudioCacheMetadata(
|
|
exists=True,
|
|
path=str(path),
|
|
size_bytes=stat.st_size,
|
|
updated_at=datetime.fromtimestamp(stat.st_mtime, tz=timezone.utc),
|
|
)
|
|
|
|
|
|
def delete_audio_cache(audio_path: str | None) -> bool:
|
|
"""Delete cached story audio if it exists."""
|
|
|
|
if not audio_path:
|
|
return False
|
|
|
|
path = Path(audio_path)
|
|
if not path.is_file():
|
|
return False
|
|
|
|
path.unlink()
|
|
return True
|
|
|
|
|
|
def write_story_audio_cache(story_id: int, audio_data: bytes) -> str:
|
|
"""Persist story audio and return the saved file path."""
|
|
|
|
final_path = Path(build_story_audio_path(story_id))
|
|
final_path.parent.mkdir(parents=True, exist_ok=True)
|
|
|
|
temp_path = final_path.with_suffix(".tmp")
|
|
temp_path.write_bytes(audio_data)
|
|
temp_path.replace(final_path)
|
|
|
|
return str(final_path)
|