2.7 KiB
src/core/subtitles.py
External subtitle sidecars — attach, discover, keep in sync on rename.
Curator does not mux/embed subtitles. Jellyfin plays an external subtitle file natively as long as it shares the video's filename stem, with an optional language code and flag suffix::
Title (rok) [tmdbid-N].mkv
Title (rok) [tmdbid-N].cs.srt # language
Title (rok) [tmdbid-N].cs.forced.srt # + a flag (forced / sdh / default)
Subtitle presence is a filesystem fact, like NFO — nothing is cached in the
pool index. What Curator does own: copying a picked file into place with the
right name (attach_subtitle), and moving a subtitle along whenever its
video gets renamed so it never silently detaches (rename_subtitles_with).
Source: src/core/subtitles.py
Classes
SubtitleFile
class · line 30
class SubtitleFile:
One subtitle sidecar found next to a video.
SubtitleFile.label
property · line 38
label -> str
Human-readable jazyk (flag) for display, e.g. cs (forced).
Functions
subtitle_filename
function · line 44
subtitle_filename(video_stem: str, ext: str, language: Optional[str]=None, flag: Optional[str]=None) -> str
Canonical subtitle filename: {video_stem}[.language][.flag]{ext}.
_parse_subtitle_name
function · line 57
_parse_subtitle_name(video_stem: str, filename: str) -> Optional[tuple[Optional[str], Optional[str]]]
(language, flag) if filename is a subtitle sidecar of video_stem.
find_subtitles
function · line 83
find_subtitles(video_path: Path) -> list[SubtitleFile]
Every external subtitle sidecar sitting next to video_path, sorted.
attach_subtitle
function · line 101
attach_subtitle(video_path: Path, source: Path, *, language: Optional[str]=None, flag: Optional[str]=None, move: bool=False) -> Path
Copy (or move) source next to video_path, named to match it.
An existing subtitle already at that exact name (e.g. replacing the Czech track) is overwritten; nothing else next to the video is touched.
Raises:
ValueError: source's extension isn't a recognised subtitle format.
rename_subtitles_with
function · line 128
rename_subtitles_with(old_video_path: Path, new_video_path: Path) -> list[Path]
Move every subtitle sidecar of old_video_path to match new_video_path.
Call this wherever a video gets renamed/moved so an attached subtitle never silently detaches (its stem would stop matching and Jellyfin would drop it). No-op when the video's path didn't actually change. Returns the new paths.