Document channel architecture, M3U/XMLTV integration with Jellyfin, and fork maintenance strategy for the archived upstream. Also includes CLAUDE.md updates for implementer workflow and project boundaries. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
4.5 KiB
Channel Architecture
Channel Entity
Defined in ErsatzTV.Core/Domain/Channel.cs. Key fields:
- Identity:
Number(e.g., "1", "2.1"),Name,UniqueId(GUID for M3U/XMLTV) - Encoding:
FFmpegProfileId— video/audio codec, bitrate, resolution, hardware acceleration - Streaming:
StreamingMode(TransportStream, HLS Direct, HLS Segmenter, TS Hybrid) - Behavior:
PlayoutMode(Continuous vs OnDemand),IdleBehavior(StopOnDisconnect vs KeepRunning) - Visual:
WatermarkId,FallbackFillerId, artwork (logos) - Mirroring:
PlayoutSource(Generated vs Mirror) — a mirror channel copies another with optional time offset - Display:
Group,Categories,ShowInEpg,IsEnabled - Audio/Subtitle defaults: preferred language codes, subtitle mode
Content Sources
Channels get content through a Playout → ProgramSchedule → ProgramScheduleItem chain.
Collection Types
| Type | Description |
|---|---|
Collection |
Manual grouping of media items with custom playback order |
MultiCollection |
Aggregate of collections + smart collections |
SmartCollection |
Query-based (Lucene.Net) dynamic filtering |
Playlist |
Ordered items with per-item config (count, fillers, playback order) |
TelevisionShow / TelevisionSeason |
Structured TV hierarchy |
Movie, Episode, MusicVideo, OtherVideo, Song, Image |
Individual media items |
RerunCollection |
Wraps any collection with separate first-run/rerun playback orders |
SearchQuery |
Dynamic results from a search |
RemoteStream |
External stream URLs |
Media Sources
Media items are imported from configured libraries (Jellyfin, Plex, Emby, or local filesystem). Each source type has its own entity variants (e.g., JellyfinMovie, PlexEpisode).
Scheduling
Schedule Kinds
- Classic: Traditional ProgramSchedule with items — the most common
- Block: Template-based block scheduling
- Sequential: Strict sequential ordering
- Scripted: External script-driven playout
- ExternalJson: Playout defined by external JSON file
Schedule Item Types
Each ProgramScheduleItem is one of four concrete types:
- One — Play exactly 1 item per cycle
- Multiple — Play N items (fixed count, collection size, or playlist item size)
- Duration — Fill a time window (with tail mode: none, offline, slate, or filler)
- Flood — Play items continuously until the next fixed-start item
Items can have StartType of Fixed (anchored to clock time) or Dynamic (follows previous item).
Playback Orders
Chronological, Random, Shuffle, ShuffleInOrder, MultiEpisodeShuffle, SeasonEpisode, RandomRotation, Marathon (group by show/season/artist/album/director).
Filler System
FillerPreset defines content to fill gaps. Each schedule item can have:
- PreRoll — before main content
- MidRoll — during (chapter breaks)
- PostRoll — after main content
- Tail — pad remaining time in a duration block
- Fallback — channel-level default when nothing else available
Filler modes: Duration, Count, Pad (to nearest minute), RandomCount.
Alternate Schedules
ProgramScheduleAlternate overrides the main schedule for specific days of week, days of month, months of year, or date ranges. Useful for seasonal programming or weekend variations.
Playout Pipeline
Channel
└── Playout
├── ProgramSchedule
│ └── ProgramScheduleItems (One|Multiple|Duration|Flood)
│ └── Content source (Collection, Playlist, SmartCollection, etc.)
├── PlayoutItems (generated — the actual timeline)
│ └── MediaItem + start/finish times + filler kind + watermarks
├── PlayoutGaps (time periods with no content)
└── ProgramScheduleAlternates (day/date overrides)
The scheduling engine (ErsatzTV.Core/Scheduling/) resolves schedule items into concrete PlayoutItem entries with precise start/finish times. Each PlayoutItem references a specific MediaItem and includes trim points (InPoint/OutPoint), filler classification, and per-item audio/subtitle overrides.
Watermarks
ChannelWatermark supports modes: Permanent, Intermittent, OpacityExpression. Image sources: custom upload, channel logo, or built-in resource. Positioned with percentage-based margins and z-index.
Note: ChannelLogoGenerator.GenerateChannelLogoUrl() hardcodes localhost for watermark logo fetching — see issue #1 for details.