refactor(domain): Phase 3 — TVShow/Movie aggregates become TMDB-only
Filesystem-side concerns (file paths, tracks, quality, mode, added_at) move to the releases/ domain added in Phase 1; the TMDB aggregates now carry only identity + TMDB catalog facts. Domain entities: - TVShow: tmdb_id: TmdbId required (primary key), imdb_id: ImdbId | None optional, status: str = "unknown" added. - Season: episode_count: int = 0 added (TMDB-cached); audio_tracks, subtitle_tracks, mode property removed. - Episode: slimmed to identity + title. file_path/file_size/tracks removed. No longer inherits MediaWithTracks. - Movie: tmdb_id required, imdb_id optional. file_path/file_size/quality/ added_at/audio_tracks/subtitle_tracks removed. get_filename() now returns "Title.Year" — quality moves to MovieRelease. Builders: - TVShowBuilder requires tmdb_id: TmdbId; imdb_id/status optional. - SeasonBuilder.set_episode_count(int) replaces set_audio_tracks / set_subtitle_tracks. No-coercion contract: TVShow(tmdb_id=1396) raises — callers pass TmdbId(1396). No ergonomic shim per the no-shims rule. Cascade fixes: - MediaOrganizer test fixtures updated to new Movie/TVShow shapes. - Movie.get_filename() re-added (without Quality) so MediaOrganizer keeps working until Phase 4 rewires it through MovieRelease. Quarantined (deleted in Phase 4 alongside v1 dot_alfred): - tests/application/library/test_rescan.py — module-level skip. - tests/infrastructure/persistence/dot_alfred/test_repository.py — module-level skip. - tests/infrastructure/persistence/dot_alfred/test_serializer.py — module-level skip. Suite: 1216 passed, 11 skipped (8 pre-existing + 3 Phase 3 quarantines), 4 xfailed. CHANGELOG updated under [Unreleased].
This commit is contained in:
@@ -15,6 +15,74 @@ callers).
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### Changed
|
||||
|
||||
- **`.alfred` v2 — Phase 3: `TVShow` / `Movie` aggregates become
|
||||
TMDB-only.** Third phase of `specs/dot_alfred_v2.md` on branch
|
||||
`refactor/dot-alfred-v2`. Filesystem-side concerns (file paths,
|
||||
tracks, quality, mode, `added_at`) move to the `releases/` domain
|
||||
added in Phase 1; the TMDB aggregates now carry only identity +
|
||||
TMDB catalog facts.
|
||||
- **`TVShow`** — `tmdb_id: TmdbId` is now the **required primary
|
||||
key**; `imdb_id: ImdbId | None` is the optional secondary anchor.
|
||||
Added `status: str = "unknown"` (raw TMDB string, default matches
|
||||
the v2 library-index auto-heal placeholder). `episode_count`
|
||||
aggregates the TMDB-cached counts on each `Season` (was: sum of
|
||||
materialized `Episode` objects).
|
||||
- **`Season`** — added `episode_count: int = 0` (TMDB-cached,
|
||||
authoritative). **Removed**: `audio_tracks`, `subtitle_tracks`,
|
||||
and the `mode` property (release mode now lives only on
|
||||
`SeasonRelease.mode` — single source of truth).
|
||||
- **`Episode`** — slimmed to identity + title. **Removed**:
|
||||
`file_path`, `file_size`, `audio_tracks`, `subtitle_tracks`. The
|
||||
`MediaWithTracks` mixin is no longer in `Episode`'s MRO; on-disk
|
||||
facts live on the matching `EpisodeRelease` keyed by
|
||||
`(season_number, episode_number)`.
|
||||
- **`Movie`** — `tmdb_id: TmdbId` required, `imdb_id` optional.
|
||||
**Removed**: `file_path`, `file_size`, `quality`, `added_at`,
|
||||
`audio_tracks`, `subtitle_tracks`. `get_filename()` now returns
|
||||
`"Title.Year"` (quality lives on `MovieRelease` and is appended
|
||||
by a release-aware caller — Phase 4 wires this through
|
||||
`MediaOrganizer`).
|
||||
- **`TVShowBuilder` / `SeasonBuilder`** — constructor requires
|
||||
`tmdb_id: TmdbId`; `imdb_id` and `status` are optional.
|
||||
`SeasonBuilder.set_episode_count(int)` replaces the old
|
||||
`set_audio_tracks` / `set_subtitle_tracks` (tracks no longer
|
||||
persisted on `Season`).
|
||||
- **`MovieRelease` carries `added_at: datetime`** (required).
|
||||
Bumped `dot_alfred/v2` `SCHEMA_VERSION` from `1` → `2` to add
|
||||
`added_at: datetime` to `MovieReleaseSidecar`. Round-trip via
|
||||
Pydantic `mode="json"` (datetime ↔ ISO 8601 string). No migration
|
||||
code shipped — no v2.1 sidecars exist in the wild yet.
|
||||
- **No-coercion `TmdbId` contract.** `TVShow(tmdb_id=1396)` now raises
|
||||
— callers pass `TmdbId(1396)`. Same for `imdb_id: ImdbId | None`
|
||||
on `TVShow`/`Movie`. Honest type contract, no ergonomic shim.
|
||||
|
||||
### Removed
|
||||
|
||||
- `Season.mode` property (derive from `SeasonRelease.mode` instead).
|
||||
- `Episode.file_path` / `file_size` / `audio_tracks` /
|
||||
`subtitle_tracks`.
|
||||
- `Movie.file_path` / `file_size` / `quality` / `added_at` /
|
||||
`audio_tracks` / `subtitle_tracks`.
|
||||
|
||||
### Internal
|
||||
|
||||
- v1 dot_alfred package (`bridge.py`, `repository.py`,
|
||||
`serializer.py`, `sidecar.py`), the abstract `TVShowRepository` /
|
||||
`MovieRepository` ports typed against the pre-Phase-3 aggregates,
|
||||
and `alfred/application/library/rescan.py` are **intentionally
|
||||
left in tree as a known-red island**. Their tests
|
||||
(`tests/infrastructure/persistence/dot_alfred/test_repository.py`,
|
||||
`test_serializer.py`, `tests/application/library/test_rescan.py`)
|
||||
are module-level skipped with a Phase 4 reference. Phase 4 rewrites
|
||||
`rescan_show` / introduces `rescan_movie` on top of the v2
|
||||
release repositories + library index, then deletes the v1 stack +
|
||||
the abstract ports + the quarantined tests in one swing.
|
||||
- Test suite: 1216 passed, 11 skipped (8 pre-existing + 3 Phase-3
|
||||
quarantines), 4 xfailed. v2 round-trip tests now reference
|
||||
`SCHEMA_VERSION` instead of hard-coded `1` for future-proofing.
|
||||
|
||||
### Added
|
||||
|
||||
- **`.alfred` v2 — Phase 2: new persistence package + TMDB client
|
||||
|
||||
Reference in New Issue
Block a user