103 lines
2.7 KiB
Markdown
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.
|