8.4 KiB
8.4 KiB
Shifted Seasons Handling
This file defines the behavioral contract for mapping source season and episode numbering to target season and episode numbering through stored shifted-season rules.
Primary sources:
requirements/project.mdrequirements/architecture.md- actual tool code in
src/ffx/
Secondary source:
SCRATCHPAD.md, used only to clarify current hardening gaps and not as the primary contract source.
Scope
- Persisting shifted-season rules in SQLite.
- Treating shifted-season rules as show-level data rather than pattern-level data.
- Matching source season and episode numbers against one stored rule.
- Applying additive season and episode offsets to produce target numbering.
- Using shifted target numbering during
convertfor TMDB episode lookup and generated season and episode filename tokens. - Managing shifted-season rules from the Textual show-editing workflow.
Out Of Scope
- General filename parsing rules for detecting season and episode values.
- Standalone
renamecommand behavior, which currently uses explicit rename inputs rather than stored shifted-season rules. - Stream or track mapping behavior unrelated to season and episode numbering.
Terms
shifted-season rule: one persisted row that belongs to one show and defines how one source-numbering range maps into target numbering.source numbering: the season and episode values detected from the current source file or supplied as source-side conversion inputs before shifting.target numbering: the season and episode values after one matching shifted-season rule has been applied.original season: the source-domain season number a shifted-season rule is eligible to match.episode range: the optional source-domain episode interval covered by one shifted-season rule.open bound: an unbounded start or end of the episode range. Current storage uses-1as the internal sentinel for an open bound.sibling shifted-season rules: all shifted-season rules stored for the same show.
Rules
SHIFTED_SEASONS_HANDLING-0001: The domain model shall treat shifted-season rules as children of a show. Shifted-season rules shall not belong to patterns.SHIFTED_SEASONS_HANDLING-0002: Each persisted shifted-season rule shall belong to exactly one show.SHIFTED_SEASONS_HANDLING-0003: A shifted-season rule shall carry these fields:original_season,first_episode,last_episode,season_offset, andepisode_offset.SHIFTED_SEASONS_HANDLING-0004:season_offsetandepisode_offsetshall be additive signed integers applied to matched source numbering to produce target numbering.SHIFTED_SEASONS_HANDLING-0005: A shifted-season rule shall match a source tuple only when:- the source season equals
original_season, - the source episode is greater than or equal to
first_episodewhen the lower bound is closed, - the source episode is less than or equal to
last_episodewhen the upper bound is closed.
- the source season equals
SHIFTED_SEASONS_HANDLING-0006: An open lower or upper episode bound shall represent an unbounded side of the covered source episode range.SHIFTED_SEASONS_HANDLING-0007: If one shifted-season rule matches, target numbering shall be:target season = source season + season_offsettarget episode = source episode + episode_offset
SHIFTED_SEASONS_HANDLING-0008: If no shifted-season rule matches, source numbering shall pass through unchanged.SHIFTED_SEASONS_HANDLING-0009: Shifted-season handling shall operate in a source-to-target numbering model. Stored rules map detected source numbering to the target numbering used by conversion-facing metadata and output naming.SHIFTED_SEASONS_HANDLING-0010: Pattern matching may identify the owning show, but shifted-season rule selection shall depend on the show and source numbering, not on which pattern matched.SHIFTED_SEASONS_HANDLING-0011: For one show and oneoriginal_season, shifted-season rules shall not overlap in their effective episode coverage. At most one rule may apply to any one source season and episode tuple.SHIFTED_SEASONS_HANDLING-0012: If a shifted-season rule uses two closed episode bounds,last_episodeshall be greater than or equal tofirst_episode.SHIFTED_SEASONS_HANDLING-0013: Shifted-season rule evaluation shall be deterministic. Released behavior shall not depend on arbitrary database row order when more than one stored rule could match.SHIFTED_SEASONS_HANDLING-0014: Duringconvert, when show, season, and episode values are available and stored shifting is active, the shifted target numbering shall drive:- TMDB episode lookup
- season and episode filename tokens such as
S01E02 - generated episode basenames that include season and episode numbering
SHIFTED_SEASONS_HANDLING-0015: When conversion is supplied explicit target-domain season or episode values for TMDB naming, the system shall not apply stored shifting on top of those already-targeted values.SHIFTED_SEASONS_HANDLING-0016: Operator-facing show editing shall expose list, add, edit, and delete flows for shifted-season rules as part of the show-management workflow.SHIFTED_SEASONS_HANDLING-0017: User-facing shifted-season editing should present open episode bounds as a natural empty-state input rather than forcing operators to type the internal sentinel directly.
Acceptance
- A show can exist with zero or more shifted-season rules.
- A shifted-season rule is stored against one show, not against one pattern.
- A source tuple matching one stored rule yields exactly one shifted target season and episode tuple derived by additive offsets.
- A source tuple matching no stored rule retains its original season and episode values.
- Two shifted-season rules for the same show and original season cannot both be valid if they cover overlapping episode ranges.
- A rule with closed bounds such as
first_episode=1andlast_episode=10rejects an inverted interval such as20..10. - A show with several patterns still uses one shared shifted-season rule set, because shifted-season ownership is show-scoped.
- During
convert, shifted numbering is what TMDB episode lookup and generated season and episode tokens see when stored shifting is active. - The TUI show-management flow can display and maintain shifted-season rules for the current show.
Current Code Fit
src/ffx/model/shifted_season.pydefines the persistedShiftedSeasonentity withshow_id,original_season, episode bounds, and additive offsets.src/ffx/model/show.pyimplements the one-to-manyShow -> ShiftedSeasonrelationship, which already aligns with show-level ownership.src/ffx/shifted_season_controller.pyimplements create, update, lookup, delete, sibling retrieval, and the runtimeshiftSeason(...)mapping step.src/ffx/show_details_screen.py,src/ffx/shifted_season_details_screen.py, andsrc/ffx/shifted_season_delete_screen.pyprovide the current Textual CRUD flow for managing show-scoped shifted-season rules.src/ffx/cli.pyappliesshiftSeason(...)duringconvertbefore TMDB episode lookup and before output season and episode suffix generation.- The current
convertimplementation disables stored shifting whenever its TMDB override bucket is present, including cases such as--showwithout an explicit target season or episode override, so current behavior is broader than the minimum bypass contract stated above. - The current code does not fully satisfy the intended validation contract yet: overlap rejection and update-time range validation are not hardened sufficiently, and deterministic selection depends too much on invalid overlap state not being present.
Risks
- The current CLI groups
--show,--season, and--episodeunder one override bucket used for TMDB-related behavior. The exact source-domain versus target-domain semantics of each override should stay documented clearly so stored shifting is neither skipped nor double-applied unexpectedly. - Current modern automated test coverage for shifted-season behavior is light, so validation and convert-time numbering behavior are not yet strongly locked down by focused tests.
- Existing databases created before stricter validation may already contain invalid overlapping or inverted shifted-season rules, so migration and repair paths should continue to treat explicit validation failures as recoverable operator signals.