feat(persistence): add DotAlfredTVShowRepository (filesystem-backed)
Step 3 of specs/dot_alfred.md. Concrete TVShowRepository implementation reading and writing per-show .alfred YAML files under a configurable library_root. Writes are atomic (.alfred.tmp + os.replace), reads tolerate corrupted/wrong-schema sidecars (log + skip), and the repo never invents a folder name — save(show) requires the target folder to exist beforehand (raises ShowFolderUnknown otherwise), matching the spec's MediaOrganizer-then-sidecar split. Cold folders without a sidecar are skipped by find_all and yield None from find_by_imdb_id — the upcoming rescan_show tool (step 4) will own the opt-in rebuild path. A small bridge module translates between the rich domain TVShow (AudioTrack/SubtitleTrack with full ffprobe minutiae) and the compact sidecar shape (language-only audio, embedded-only subs with type derived from is_forced). The bridge is intentionally lossy on probe details the sidecar does not store, per the spec's factual-only philosophy. 20 integration tests on tmp_path: round-trip save/find, cold-folder/unknown-id returns, find_all skipping (corrupted/schema-violating sidecars), delete/exists, atomic write (no .alfred.tmp leftover), overwrite, and folder-name fallbacks (get_folder_name guess + full-scan rescue when renamed).
This commit is contained in:
@@ -17,6 +17,41 @@ callers).
|
||||
|
||||
### Added
|
||||
|
||||
- **`DotAlfredTVShowRepository` — filesystem-backed implementation of
|
||||
the `TVShowRepository` port
|
||||
(`alfred/infrastructure/persistence/dot_alfred/repository.py`).**
|
||||
Step 3 of the `specs/dot_alfred.md` plan. Reads and writes one
|
||||
`.alfred` YAML file per show under a configurable `library_root`.
|
||||
`save(show)` writes atomically (`.alfred.tmp` + `os.replace`) into a
|
||||
folder that **must already exist** — the repository never invents a
|
||||
folder name (the upstream `MediaOrganizer` is in charge of placing
|
||||
files; the repo writes the sidecar next to them). `find_by_imdb_id` /
|
||||
`find_all` walk `library_root/*/`, loading each readable sidecar;
|
||||
folders without a sidecar return `None` / are skipped (no implicit
|
||||
cold scan — that is the job of the upcoming `rescan_show` tool).
|
||||
Corrupted YAML and schema violations are logged and skipped, never
|
||||
raised, so a single bad folder does not break the rest of the
|
||||
library. The repo keeps a tiny in-memory `imdb_id → folder_name`
|
||||
index populated on every successful read/save, so subsequent saves
|
||||
find the right destination without re-walking — useful when the show
|
||||
folder name diverges from `show.get_folder_name()` (custom 1080p / 4K
|
||||
variants). 20 integration tests on `tmp_path` cover the round-trip,
|
||||
cold folder / unknown id returns, multi-show `find_all`, corrupted /
|
||||
wrong-schema skipping, atomic write (no `.alfred.tmp` left behind),
|
||||
overwrite, and folder-name fallbacks.
|
||||
- **Sidecar ↔ TVShow bridge
|
||||
(`alfred/infrastructure/persistence/dot_alfred/bridge.py`).**
|
||||
`to_sidecar(show, folder_paths=...)` summarizes the rich domain
|
||||
`AudioTrack` / `SubtitleTrack` to the sidecar's compact form (unique
|
||||
audio languages in track order; subtitle entries derived from
|
||||
`is_forced` and assumed `source="embedded"`). `from_sidecar(sidecar,
|
||||
title=...)` reconstructs the domain `TVShow` with synthesized tracks
|
||||
— one `AudioTrack` per language, one `SubtitleTrack` per entry, with
|
||||
ffprobe-only fields (`codec`, `channels`, `channel_layout`) left as
|
||||
`None`. The bridge is intentionally lossy on probe minutiae the
|
||||
sidecar does not store; this is the documented trade-off from the
|
||||
factual-only spec.
|
||||
|
||||
- **`.alfred` sidecar serializer
|
||||
(`alfred/infrastructure/persistence/dot_alfred/`).** Implements step 2
|
||||
of the `specs/dot_alfred.md` plan. Pure-dict in/out
|
||||
|
||||
Reference in New Issue
Block a user