feat(releases): Phase 1 — new filesystem release domain + TmdbId VO
First step of specs/dot_alfred_v2.md. Introduces a separate bounded context (alfred/domain/releases/) for the filesystem-side aggregates, disjoint from TMDB identity which stays in tv_shows/ and movies/. The link between the two worlds is TmdbId, used as the natural key in the persistence layer (no domain-level reference). New package alfred/domain/releases/: - value_objects: EpisodeRange (covers SxxE01E02E03 multi-episode files via start/end inclusive range, with count/numbers/is_single helpers), ReleaseMode enum (PACK = N video files direct in the season folder, EPISODIC = N sub-folders). - entities: TrackProfile, EpisodeRelease, SeasonRelease (with episode_count() summing each EpisodeRange.count()), SeriesRelease (tmdb_id primary anchor, optional imdb_id secondary), MovieRelease. All frozen dataclasses. - builders: SeasonReleaseBuilder + SeriesReleaseBuilder mirroring the v1 TVShowBuilder pattern. Builders sort episodes by range start on emit and reject overlapping ranges (two files claiming the same TMDB slot). from_existing() seeds a builder from an existing frozen aggregate for round-trip edits. - repositories: abstract ports (SeriesReleaseRepository, MovieReleaseRepository); concrete .alfred sidecar impls arrive in Phase 2. New shared VO alfred/domain/shared/value_objects.py::TmdbId — positive int, rejects bool/str/float, symmetric with the existing ImdbId VO. 73 unit tests cover VO validation, entity invariants, builder sort + overlap detection, and from_existing() round-trips. v1 code paths are untouched at this stage; the new domain coexists with the old TVShow aggregate until Phase 3 refactors it.
This commit is contained in:
@@ -17,6 +17,33 @@ callers).
|
||||
|
||||
### Added
|
||||
|
||||
- **`.alfred` v2 — Phase 1: new `releases/` domain.** First step of
|
||||
`specs/dot_alfred_v2.md` on branch `refactor/dot-alfred-v2`. The
|
||||
new `alfred/domain/releases/` package introduces a filesystem-only
|
||||
bounded context separated from TMDB identity (the existing
|
||||
`tv_shows` / `movies` domains). It hosts:
|
||||
- **`EpisodeRange` VO** — covers single-episode files
|
||||
(`EpisodeRange(E02, E02)`) and multi-episode files
|
||||
(`EpisodeRange(E02, E04)` for `SxxE02E03E04.mkv`), with
|
||||
`count()` / `numbers()` / `is_single()` helpers.
|
||||
- **`ReleaseMode` enum** — `PACK` (N video files directly in the
|
||||
season folder) vs `EPISODIC` (N sub-folders, one episode each);
|
||||
classified by the walker, never re-derived.
|
||||
- **Aggregates** — `TrackProfile`, `EpisodeRelease`,
|
||||
`SeasonRelease` (with `episode_count()` summing each file's
|
||||
range), `SeriesRelease`, `MovieRelease`. All frozen
|
||||
dataclasses; mutation via `SeasonReleaseBuilder` /
|
||||
`SeriesReleaseBuilder` (mirror the v1 `TVShowBuilder` pattern,
|
||||
including `from_existing()` round-trip).
|
||||
- **Abstract ports** — `SeriesReleaseRepository`,
|
||||
`MovieReleaseRepository` (concrete `DotAlfred*` arrive in
|
||||
Phase 2).
|
||||
- **`TmdbId` VO** added to `alfred/domain/shared/value_objects.py`
|
||||
(positive int, rejects bool/str/float — symmetry with `ImdbId`).
|
||||
- 73 unit tests covering VO validation, entity invariants, builder
|
||||
sort + overlap detection, and `from_existing()` round-trips. v1
|
||||
code paths untouched at this stage; new domain coexists.
|
||||
|
||||
- **`rescan_show` orchestrator
|
||||
(`alfred/application/library/rescan.py`).** Step 4 of the
|
||||
`specs/dot_alfred.md` plan. Walks an Alfred-managed show folder,
|
||||
|
||||
Reference in New Issue
Block a user