Files
Curator/docs/modules/core.subtitles.md

103 lines
2.7 KiB
Markdown

# `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`](../../src/core/subtitles.py)
## Classes
### `SubtitleFile`
*class · line 30*
```python
class SubtitleFile:
```
One subtitle sidecar found next to a video.
#### `SubtitleFile.label`
*property · line 38*
```python
label -> str
```
Human-readable `jazyk (flag)` for display, e.g. `cs (forced)`.
## Functions
### `subtitle_filename`
*function · line 44*
```python
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*
```python
_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*
```python
find_subtitles(video_path: Path) -> list[SubtitleFile]
```
Every external subtitle sidecar sitting next to `video_path`, sorted.
### `attach_subtitle`
*function · line 101*
```python
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*
```python
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.