Adds GET /api/guide?start=<DateTimeOffset>&end=<DateTimeOffset> returning per-channel programme arrays as JSON, sourced from Playout.Items via a projection shared with the XMLTV writer so the two surfaces cannot drift.
Shared extraction: ChannelGuideProjector (guide-group/filler-merge, flood + block modes) and ChannelGuideMetadata (title/subtitle/category), consumed by both the refactored RefreshChannelDataHandler (XMLTV) and the new GetChannelGuideDataHandler (JSON). IncludeGuideMetadata() consolidates the 21-chain eager-load spec.
XMLTV byte-identical: ChannelGuideGoldenTests pass with zero golden regeneration; uncovered branches verified by literal-copy inspection in review.
Respects ShowInEpg, skips ExternalJson playouts, orders channels by decimal number, shifts mirror-channel programmes by PlayoutOffset without mutating loaded entities.
start defaults to now; end defaults to start + XmltvDaysToBuild. subTitle/category nullable; fillerKind included.
Tests: 11 new (9 handler incl. mirror-offset/no-mutation regression, 2 controller). Both test projects green under TZ=UTC.
Review verdict
Adversarial review: mergeable, no blockers. Notes recorded on #102/#85: fillerKind is almost always None (shared projector merges filler — the no-drift requirement); per-request eager-load cost flagged as a follow-up before #85 ships.
## Summary
Adds `GET /api/guide?start=<DateTimeOffset>&end=<DateTimeOffset>` returning per-channel programme arrays as JSON, sourced from `Playout.Items` via a projection **shared with the XMLTV writer** so the two surfaces cannot drift.
- **Shared extraction**: `ChannelGuideProjector` (guide-group/filler-merge, flood + block modes) and `ChannelGuideMetadata` (title/subtitle/category), consumed by both the refactored `RefreshChannelDataHandler` (XMLTV) and the new `GetChannelGuideDataHandler` (JSON). `IncludeGuideMetadata()` consolidates the 21-chain eager-load spec.
- **XMLTV byte-identical**: `ChannelGuideGoldenTests` pass with zero golden regeneration; uncovered branches verified by literal-copy inspection in review.
- Respects `ShowInEpg`, skips ExternalJson playouts, orders channels by decimal number, shifts mirror-channel programmes by `PlayoutOffset` without mutating loaded entities.
- `start` defaults to now; `end` defaults to `start` + XmltvDaysToBuild. `subTitle`/`category` nullable; `fillerKind` included.
- OpenAPI v1.json regenerated (`/api/guide` + 3 `ChannelGuide*` schemas).
- Tests: 11 new (9 handler incl. mirror-offset/no-mutation regression, 2 controller). Both test projects green under TZ=UTC.
## Review verdict
Adversarial review: **mergeable, no blockers**. Notes recorded on #102/#85: `fillerKind` is almost always `None` (shared projector merges filler — the no-drift requirement); per-request eager-load cost flagged as a follow-up before #85 ships.
fixes #102
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Add GET /api/guide returning a per-channel EPG grid (ChannelGuideResponseModel).
start defaults to now, end defaults to now + XmltvDaysToBuild.
Extract the guide-group/filler-merge projection (ChannelGuideProjector) and
programme-metadata resolution (ChannelGuideMetadata) plus the metadata eager-load
(IncludeGuideMetadata) out of RefreshChannelDataHandler so the XMLTV cache builder
and the JSON guide share one source of truth and cannot drift. XMLTV output is
unchanged (ChannelGuideGoldenTests pass without regeneration).
The JSON handler mirrors XMLTV semantics: ShowInEpg-only channels, decimal channel
ordering, ExternalJson playouts skipped, mirror playout offset applied to copies
(never mutating the loaded entities). SubTitle/Category are nullable.
refs #102
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Add GetChannelGuideDataHandlerTests coverage for the mirror-channel path:
asserts programme Start/Stop shift by PlayoutOffset and that the source
channel's seeded PlayoutItem entities are not mutated, pinning the
WithPlayoutOffset copy fix.
- ChannelController.GetGuide Swagger description said end defaults to "now
plus XmltvDaysToBuild"; it actually defaults to start plus that window
(matters when start is supplied). Reworded and regenerated v1.json.
timothy
merged commit 6f6f37b7f6 into main2026-07-04 07:43:19 +02:00
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Summary
Adds
GET /api/guide?start=<DateTimeOffset>&end=<DateTimeOffset>returning per-channel programme arrays as JSON, sourced fromPlayout.Itemsvia a projection shared with the XMLTV writer so the two surfaces cannot drift.ChannelGuideProjector(guide-group/filler-merge, flood + block modes) andChannelGuideMetadata(title/subtitle/category), consumed by both the refactoredRefreshChannelDataHandler(XMLTV) and the newGetChannelGuideDataHandler(JSON).IncludeGuideMetadata()consolidates the 21-chain eager-load spec.ChannelGuideGoldenTestspass with zero golden regeneration; uncovered branches verified by literal-copy inspection in review.ShowInEpg, skips ExternalJson playouts, orders channels by decimal number, shifts mirror-channel programmes byPlayoutOffsetwithout mutating loaded entities.startdefaults to now;enddefaults tostart+ XmltvDaysToBuild.subTitle/categorynullable;fillerKindincluded./api/guide+ 3ChannelGuide*schemas).Review verdict
Adversarial review: mergeable, no blockers. Notes recorded on #102/#85:
fillerKindis almost alwaysNone(shared projector merges filler — the no-drift requirement); per-request eager-load cost flagged as a follow-up before #85 ships.fixes #102
🤖 Generated with Claude Code