Commit Graph
78 Commits
Author SHA1 Message Date
timothyandClaude Opus 4.8 f54c9ae195 refactor(395): dedup Scripted≡YAML enumerator construction into ContentEnumeratorBuilder
PR Gates / CI image pin matches docker/ci (pull_request) Successful in 9s
PR Gates / Docs update reminder (pull_request) Successful in 13s
Build ErsatzTV Image / API docs in sync (OpenAPI + endpoint index) (pull_request) Successful in 1m25s
Build ErsatzTV Image / Formatting (changed .cs conform to .editorconfig) (pull_request) Successful in 1m21s
Build ErsatzTV Image / Build & test (.NET) (pull_request) Successful in 7m36s
PR Gates / decisions lifecycle (pull_request) Failing after 14s
Build ErsatzTV Image / Build & push image (amd64) (pull_request) Has been cancelled
Build ErsatzTV Image / Functional E2E (curl contracts) (pull_request) Has been cancelled
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (pull_request) Has been cancelled
Extract the byte-identical PlaybackOrder -> IMediaCollectionEnumerator switch
shared by SchedulingEngine.EnumeratorForContent (Scripted) and
EnumeratorCache.GetEnumeratorForContent (Sequential/YAML) into one static
per-family seam, mirroring #380's ShuffleSourceBuilder. Each engine keeps its own
"not supported" warning on the None branch, so the per-engine message is unchanged.
Adds ContentEnumeratorBuilderTests pinning the block-shuffle-not-classic trap and
the unsupported-order -> None (#70) contract across all 8 unsupported orders.

Corrects the testing.scripted-playout-golden-deferred decision record: Scripted's
external-process + HTTP pipeline is integration-only (deferred to #563), but the
in-process SchedulingEngine it drives IS unit-testable (ScriptedScheduleController
is a 1:1 pass-through) -- the earlier "un-golden-able by construction" framing
conflated transport with engine. docs/testing.md reframed to match.

[decisions-edit]

fixes #395

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 20:21:52 +02:00
timothyandClaude Opus 4.8 0f34c86afa feat(403): make unsupported PlaybackOrder loud at build time + tripwire
Build ErsatzTV Image / CI image pin matches docker/ci (pull_request) Successful in 9s
Build ErsatzTV Image / Docs update reminder (pull_request) Successful in 5s
Build ErsatzTV Image / decisions.md append-only (pull_request) Successful in 6s
Build ErsatzTV Image / API docs in sync (OpenAPI + endpoint index) (pull_request) Successful in 17s
Build ErsatzTV Image / Build & test (.NET) (pull_request) Successful in 7m21s
Build ErsatzTV Image / Functional E2E (curl contracts) (pull_request) Successful in 14m14s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (pull_request) Successful in 18m31s
Build ErsatzTV Image / Build & push image (amd64) (pull_request) Has been skipped
Build ErsatzTV Image / Formatting (changed .cs conform to .editorconfig) (pull_request) Successful in 37m32s
Adding a new PlaybackOrder was unsafe by construction: three build-time
dispatch sites turned an unknown value into an enumerator silently.
Classic substituted RandomizedMediaCollectionEnumerator (the // TODO
default arm), PlaylistEnumerator had no default arm so the item was
dropped, and BlockPlayoutBuilder's allow-list continue skipped it.
(#70 already made YAML/Scripted log a warning and MultiCollectionGroup
throws.)

- each silent site now logs a Warning naming the order + engine + the
  fallback taken; the fallback itself is preserved so a live channel
  never goes dark on one misconfigured item and scheduler goldens do
  not move.
- PlaylistEnumerator.Create gained an optional Option<ILogger> (it was
  static with no logger -- why the drop was unreportable); loggered
  callers pass it.
- BlockPlayoutBuilder gained an explicit Random arm (it previously
  reached an enumerator only via the coincidental _ => fallback) and a
  loud defensive fallback.
- new PlaybackOrderSupport matrix (per SchedulingEngineKind) + tripwire
  PlaybackOrderSupportTests: Supported ∪ Unsupported must partition the
  enum for every engine, so a new order fails the test until classified.
  BlockPlayoutBuilder consumes the matrix for its allow-list.
- write-path rejection left unchanged (#70 closed the persistence hole;
  the perimeter has been wrong three times per decisions.md); reverse
  _ => None mappings reviewed and deferred (different axis; making them
  loud would warn on legit enumerator types).

docs/decisions.md updated.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-18 18:23:21 +02:00
0295a8a9f6 style(70): strip UTF-8 BOM from the .cs files this PR touches
CI's Formatting job failed: 19 touched files carried a BOM, which .editorconfig
forbids (charset=utf-8). Pure encoding change — one byte per file, no semantic
diff (verified: every hunk is `-namespace` -> `+namespace`).

Self-inflicted. The patches that edited these legacy files wrote them back as
utf-8-sig to "preserve the existing style", but the #311 fix-as-you-touch gate
requires a file to be normalized when you touch it — that is the whole point of
scoping the gate to changed files instead of reformatting the ~2500 legacy BOM
files at once. dotnet format leaves the EF-generated Designer/snapshot files
alone as generated code, and its verify skips them the same way, so they stay as
ef emitted them.

Two corrections to what I believed going in:
- `dotnet format --include` does NOT no-op here. It reported `error CHARSET` for
  each file and exit 2, reproducing CI exactly, and fixed them in place. The note
  claiming otherwise is wrong for this invocation.
- My first BOM check reported all files clean. The od pattern was wrong; reading
  the first three bytes directly found 19. A detector that can only say "ok" is
  worse than no detector.

Core.Tests 565, ErsatzTV.Tests 1673, Architecture.Tests 5 — all passed. API
artifacts still in sync.

Refs #70

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-17 17:00:46 +00:00
f394c721a8 feat(70): refuse WeightedShuffle where it isn't implemented, report where it can't be
WeightedShuffle is implemented for classic schedule items only, and every other
engine mishandles an order it doesn't know *silently*: PlaylistEnumerator has no
default arm so the item is dropped from the playlist; BlockPlayoutBuilder filters
block items against an allow-list and `continue`s past the rest; YAML and Scripted
return None, which their callers' foreach reads as "no content". A weighted order
degrading to unweighted random or to nothing is the worst failure mode here,
because the output is supposed to look arbitrary — nobody would notice.

Rather than change those shipped fallbacks (a real defect class, but pre-existing
and wider than this feature — filed separately, non-goal here), this closes the
new exposure at the write path: ReplacePlaylistItems and ReplaceBlockItems reject
WeightedShuffle with an error naming where it is available. If it can't be
persisted where it isn't handled, the silent sites never see it.

Defence in depth for the two engines that address orders by name: YAML and
Scripted now log a warning when a parsed order falls through unhandled, so an
empty schedule explains itself. Enum.Parse accepts "weightedShuffle" the moment
the value exists, so the gate above can't cover them. EnumeratorForContent becomes
an instance method to reach the logger.

ProgramScheduleItemCommandBase lists WeightedShuffle explicitly as valid for multi
collections — it already passed by falling through the switch, and implicit-by-
omission is how this subsystem grew its silent paths.

Core.Tests: 558 passed, 0 failed.

Refs #70

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-17 17:00:46 +00:00
Jason DoveandGitHub cd2bb0f2e0 fix playout build failures due to playlist enumerator access (#2801) 2026-01-26 14:44:07 -06:00
Jason DoveandGitHub 08ceb53b2b make count an expression in classic schedules (#2794)
* make count an expression in classic schedules

* add tests
2026-01-20 09:50:45 -06:00
Jason DoveandGitHub 9fea25a77d allow string values for count instruction in sequential schedules (#2741)
* allow string values for count instruction in sequential schedules

* fix potential div by zero
2025-12-30 18:19:33 -06:00
Jason DoveandGitHub 2f0cd1eb6c update dependencies (#2726) 2025-12-19 13:56:26 -06:00
Jason DoveandGitHub ec0d8ea6ac work around sequential schedule validation limit (#2655)
* remove readalltext

* remove unused method

* remove fileexists

* remove folderexists

* remove readalllines

* remove fake local file system

* show playlist name in playout build errors

* add basic sequential schedule validator tests

* work around sequential schedule validation limit
2025-11-24 12:08:43 -06:00
Jason DoveandGitHub 5045a411b1 fix sequential schedule building across offsets (#2584) 2025-10-31 09:20:09 -05:00
Jason DoveandGitHub 144b3fe80b fix remote stream durations in playouts (#2515) 2025-10-11 08:30:24 -05:00
Jason DoveandGitHub 3e8ac9914c refactor playout build errors (#2480)
* refactor classic playout builds

* refactor sequential playout builds

* refactor block playout building

* don't fail building an empty block schedule

* fix scripted playout build errors
2025-10-02 13:36:39 -05:00
Jason DoveandGitHub 598de5d5d6 add playout build status (#2476)
* add playout build status

* show build status in playout list

* update changelog
2025-10-01 21:40:39 -05:00
Jason DoveandGitHub 245165c9d9 add rerun collection type (#2421)
* rename collection type

* split collections into separate pages

* add rerun collection types, migration, editor

* add rerun to classic schedule items

* rerun plumbing in classic playout builder

* start to implement rerun enumerator

* add scheduledAt to enumerator movenext

* maintain rerun history in db

* fix shuffle

* fix rerun allowed playback orders

* fix updating rerun collections

* update changelog; fix editing

* update changelog
2025-09-15 18:27:55 +00:00
Jason DoveandGitHub 0d69dd58a4 add classic schedule marathon (#2400) 2025-09-09 15:38:07 +00:00
Jason DoveandGitHub ef5de99f9c add create_playlist, pre_roll_on, pre_roll_off to scripted schedules (#2387) 2025-09-06 20:47:44 +00:00
Jason DoveandGitHub eadacc7f8c add stop_before_end and offline_tail to pad_to_next (#2359) 2025-08-31 23:03:46 +00:00
Jason DoveandGitHub 1c07df5bc3 use cancellation tokens in many places (#2350)
* use cancellation tokens everywhere

* more cancellation tokens
2025-08-27 03:20:35 +00:00
Jason DoveandGitHub 4e2310d008 add all content sources to scripted schedules (#2340)
* add show content

* add multi collection content

* add smart collection content

* add playlist content

* fix infinite loop

* add marathon content
2025-08-24 14:39:34 +00:00
Jason DoveandGitHub bbddd50f00 add new scheduling engine, basic scripted schedule system (#2337)
* start to add content to scheduling engine

* add first content instruction

* add search content

* allow scripted schedule creation

* don't use scheduling engine in sequential playout builder, yet
2025-08-24 03:11:58 +00:00
Jason DoveandGitHub 53f281ce32 add xmltv block behavior setting (#2336)
* replace playout externaljsonfile and templatefile with schedulefile

* add scripted schedule-based playout

* wip - not functional yet

* temp disable scripted playout creation

* allow fast-forwarding block playouts

* add xmltv block behavior setting
2025-08-23 20:16:12 +00:00
Jason DoveandGitHub e06ee54070 rename yaml playout to sequential schedule (#2335)
* clarify some schedule and playout terms

* more renaming
2025-08-23 14:27:32 +00:00
Jason DoveandGitHub 1e7f9a5709 fix saving yaml playout history (#2327)
* fix saving yaml playout history

* cleanup
2025-08-17 01:21:07 +00:00
Jason DoveandGitHub 5d081ceeff fix editorconfig and run code cleanup (#2324)
* fix formatting rules

* reformat ersatztv

* reformat ersatztv.application

* reformat ersatztv.core

* refactor ersatztv.core.tests

* reformat ersatztv.ffmpeg

* reformat ersatztv.ffmpeg.tests

* reformat ersatztv.infrastructure

* cleanup infra mysql

* cleanup infra sqlite

* cleanup infra tests

* cleanup ersatztv.scanner

* cleanup ersatztv.scanner.tests

* sln cleanup

* update dependencies
2025-08-16 14:44:48 +00:00
Jason DoveandGitHub f4eadae8ff set variables from yaml playout graphics_on instruction (#2284) 2025-08-08 23:02:13 +00:00
Jason DoveandGitHub 2dc5bf58a7 add graphics_on and graphics_off yaml playout instructions (#2283) 2025-08-08 20:22:07 +00:00
Jason DoveandGitHub 9f3db05c17 fix graphics engine on vaapi (#2281) 2025-08-08 14:15:46 +00:00
Jason DoveandGitHub c7fcaf8886 refactor playout building (#2273)
* refactor playout building

* remove playout items
2025-08-07 15:20:26 +00:00
Jason DoveandGitHub 5a5c049835 support multiple watermarks in yaml schedules (#2267)
* add multiple watermarks per playout item

* fixes

* update yaml playout watermark to support multiple watermarks

* use graphics engine for intermittent watermarks
2025-08-06 21:22:20 +00:00
Jason DoveandGitHub 93dca6e0e0 fix framerate check for remote streams (#2264) 2025-08-05 14:35:13 +00:00
Jason DoveandGitHub 3b955255ce fix building yaml playouts with no imports (#2249) 2025-08-04 03:43:30 +00:00
Jason DoveandGitHub 16dd2c2d81 add yaml import section (#2248) 2025-08-04 02:21:56 +00:00
Jason DoveandGitHub aa3bd3b750 add yaml playout rewind instruction (#2243) 2025-08-03 13:29:44 +00:00
Jason DoveandGitHub f04b7ead09 fix yaml playout builds (#2241) 2025-08-03 01:18:27 +00:00
Jason DoveandGitHub 7bd0de99e1 fix gaps in yaml playouts (#2235)
* dont run multiple dotnet builds in background

* fix gaps in yaml playouts
2025-08-02 03:42:42 +00:00
Jason DoveandGitHub 8430a3048c fix yaml playout builds after refactor (#2233) 2025-08-02 03:12:37 +00:00
Jason DoveandGitHub 06d9e59a7a add yaml mid roll instruction (#2232)
* refactor filler expression

* add yaml mid roll instruction

* schedule midroll for yaml count and all instructions

* update changelog
2025-08-02 00:16:50 +00:00
Jason DoveandGitHub 12c88a006d add yaml post_roll instruction (#2230) 2025-08-01 17:02:36 +00:00
Jason DoveandGitHub 093abf7ad8 add yaml playout pre_roll instruction (#2228)
* add yaml playout pre_roll instruction

* add basic yaml validation

* validate all yaml playout content items

* fix yaml to json conversion

* update changelog
2025-08-01 15:20:10 +00:00
Jason DoveandGitHub deff33c76c fix pad_to_next always running over time (#2207) 2025-07-26 20:22:40 +00:00
Jason DoveandGitHub 43e1cbd919 yaml playout watermarks (#2149) 2025-07-14 03:25:20 +00:00
Jason DoveandGitHub 7e30444857 dependencies and code cleanup (#2117)
* fix validation in new form layout

* pin mediatr to last oss version

* update dependencies

* cleanup code in core

* cleanup code in ffmpeg

* cleanup code in infra

* cleanup code in scanner

* cleanup code in application

* cleanup main code

* cleanup test code

* solution-wide code cleanup
2025-07-06 15:56:17 +00:00
Jason DoveandGitHub a6fa93d44e fix nvidia compatibility with ffmpeg 7.2+ (#2108)
* tweak random seed

* fix dotnet install in docker test

* fix nvidia compatibility with ffmpeg 7.2+
2025-07-03 15:08:18 +00:00
Jason DoveandGitHub 5a9d27e196 make yaml playout count an expression (#2102) 2025-07-01 16:24:11 +00:00
Jason DoveandGitHub 3e07bc6136 fix history for playlists in yaml playouts (#2097) 2025-07-01 00:47:53 +00:00
Jason DoveandGitHub d6bfc2fd05 marathon playout history fixes (#2096) 2025-06-30 21:39:31 +00:00
Jason DoveandGitHub 037cee873f yaml marathon history (#2094)
* better playlist tests

* fix history for marathon content in yaml playouts
2025-06-30 19:05:09 +00:00
Jason DoveandGitHub 7457301d3e yaml playout skip items expression (#2092) 2025-06-29 14:31:56 +00:00
Jason DoveandGitHub 6cc2f1de17 yaml playout improvements (#2088)
* add stop_before_end

* more yaml playout improvements
2025-06-28 16:27:58 +00:00
Jason DoveandGitHub f80069bb97 add custom channel stream selector system (#2076)
* add some basic channel stream selector models

* change windows ffmpeg url

* implement basic stream selection

* fixes
2025-06-27 03:00:59 +00:00