-
v26.11.0
StableBuild ErsatzTV Image / CI image pin matches docker/ci (push) Has been skippedBuild ErsatzTV Image / Docs update reminder (push) Has been skippedBuild ErsatzTV Image / decisions.md append-only (push) Has been skippedBuild ErsatzTV Image / API docs in sync (OpenAPI + endpoint index) (push) Has been skippedBuild ErsatzTV Image / Functional E2E (curl contracts) (push) Has been skippedBuild ErsatzTV Image / Formatting (changed .cs conform to .editorconfig) (push) Has been skippedBuild CI Toolchain Image / Build & push CI image (push) Successful in 21sBuild ErsatzTV Image / Build & test (.NET) (push) Successful in 7m13sBuild ErsatzTV Image / EF migration integrity (SQLite + MySql) (push) Successful in 19m34sBuild ErsatzTV Image / Build & push image (amd64) (push) Successful in 7m53sreleased this
2026-07-20 22:22:04 +02:00 | 637 commits to main since this releaseFixes ~50% channel cold-start failures on Intel QuickSync hardware, unifies the channel logo with the on-screen bug, and hardens the Jellyfin scanner and remote-stream playback paths. 83 commits since v26.10.0.
Upgrade note — a default changes.
QsvPreferNativeDecoderships ON, so every existing QSV FFmpeg profile switches to VA-API decoding on upgrade. This is the intended fix for #498; turn it off per-profile if you need the old strict-QSV decoder back.Hardware acceleration (#498)
- QSV profiles now decode via VA-API while still encoding with QSV, mirroring Jellyfin's "Prefer OS native decoders". The strict QSV decoder failed on imperfect H.264 — measured at 3 fatal decoder-open failures across 6 channel cold-starts on an Intel i7-10700K.
- New per-profile
QsvPreferNativeDecoderfield (default on) + a "Prefer native decoder" checkbox on QSV profiles, carried through the domain, FFmpeg pipeline, REST layer, and SPA.
Channel logo / on-screen bug (#67)
- One shared Channel Bug watermark preset drives both the channel listing logo and the on-screen bug — no more maintaining two images.
- "Use logo as on-screen bug" toggle with a live geometry preview in the channel editor; the watermarks screen shows resolved bug geometry; new channels default to the preset.
Jellyfin scanning
- Support mixed-content libraries — one Jellyfin library holding more than one media kind (#489)
- Resolve
LibraryFolderfrom the DB so music-video scans stop throwingNullReferenceException(#488) - Reconcile removed music videos (#494) and music-video metadata collections (#497) on rescan
Playback resilience
- Probe media-server remote-stream URLs before handing them to ffmpeg; only a redirected 404 fails closed (#473)
- Probe external-JSON remote-stream URLs the same way (#480)
- Cascade
FileNotFoundfrom removed shows/seasons down to their descendants (#476) - Guard media-server library sweeps against successful-but-empty fetches — an empty 200 no longer nukes a library (#477)
Scheduling & UI
- Weighted distribution in the SPA — per-source weight inputs +
WeightedShuffleplayback order (#404) - Render real channel logos in the guide grid and channels list (#464)
- Bound the schedules "Active schedule" selector so it stops overlapping the header (#463)
Performance & instrumentation
- Sub-split the HLS cold-start startup phase into honest buckets, with a stale-playlist guard (#472)
- TTL-cache health-check results;
?refresh=trueforces a fresh run (#431)
CI / tooling
- Format gate now uses
dotnet format whitespace --folder: ~480s → ~0.5s (#469) - Sample true peak-anon memory in the test job rather than cache-inflated
memory.peak(#412) - Deterministic functional-E2E for the playout-build lock 409 +
isLockedprojection (#444)
Infrastructure
- First release deployed to
jazz(192.168.1.29) — the media transcoders moved off bumblebee on 2026-07-20 (server-management#633).docs/ci-cd.mdnow opens with a Hosts table and a name-reuse warning:jazzwas an earlier name for the .99 host, so resolve hostnames by IP, not by name (#501, #507).
Release process: cut on green
main@601d855b;dotnet list package --vulnerable --include-transitiveclean across 15 projects; full local test gate green (3945 passed, 0 failed); carries a dual-provider EF migration (Add_FFmpegProfile_QsvPreferNativeDecoder, SQLite + MySQL);docs/decisions.mdhad no superseded entries to consolidate. Prod follows the floating:prodtag — promoted via KomodoDeployStack media-serversafter the immutable:26.11.0candidate passesscripts/security-scan.shon jazz.Downloads
-
v26.10.0
StableBuild ErsatzTV Image / Functional E2E (curl contracts) (push) Has been skippedBuild ErsatzTV Image / API docs in sync (OpenAPI + endpoint index) (push) Has been skippedBuild ErsatzTV Image / Formatting (changed .cs conform to .editorconfig) (push) Has been skippedBuild ErsatzTV Image / CI image pin matches docker/ci (push) Has been skippedBuild ErsatzTV Image / Docs update reminder (push) Has been skippedBuild ErsatzTV Image / decisions.md append-only (push) Has been skippedBuild CI Toolchain Image / Build & push CI image (push) Failing after 10m41sBuild ErsatzTV Image / Build & test (.NET) (push) Successful in 18m20sBuild ErsatzTV Image / EF migration integrity (SQLite + MySql) (push) Successful in 18m31sBuild ErsatzTV Image / Build & push image (amd64) (push) Successful in 3m58sreleased this
2026-07-18 21:39:25 +02:00 | 722 commits to main since this releaseFirst fork release of the Auto-Tune channel workflow and weighted content distribution, plus scheduling refactors, health-check remediation UX, cold-start instrumentation, and security hardening. 170 commits since v26.9.0.
Auto-Tune (new)
- Backend to enumerate library metadata and preview + bulk-create channels (#69, #379)
- SPA wizard: Configure → Preview → Create (#69, #389)
- Per-channel DetailPanel slide-over + content-source member read endpoint (#386, #384)
- Per-channel overrides in bulk-create (#385)
- Per-source rotation weights + query corrections (#425)
Scheduling & distribution
WeightedShuffleplayback order — weighted / fair-share content distribution (#70)- Per-playout reshuffle + shuffle-state surfacing (#71)
- SmartCollection visual rule builder in the editor (#176)
ShuffleSourceBuilderextraction — removes PlaylistEnumerator's cross-engine reach-in (#380)- Unsupported PlaybackOrder now fails loud at build time + engine-coverage tripwire (#403)
- Golden characterization tests for playout building; clock-boundary padding chars (#163, #77)
Health checks & UI
- Guided remediation for health checks (server-declared {Kind, Target}) (#164)
- Collapsible sidebar + nav-group accordions (#396)
Performance & instrumentation
- HLS cold-start latency instrumentation — phase split (setup/startup/fill) + feature flags (#350)
Security & robustness hardening
- Paginate
GET /api/v1/search/all-itemsto cap DoS exposure (#293) - XML-escape
access_tokenin XMLTV guide output (#376) - Idempotent concurrent
Add*ToCollectioninstead of a composite-PK 500 (#308) - Break the troubleshoot segment-wait loop on ffmpeg failure (#320)
Fixes
- Real per-channel playout count; flag channels that will never play (#72)
- Library-level
LastScanrecorded after a successful local scan; never-scanned reportsnullfor API/MCP parity (#264, #409) - Validate playlist name on rename (#327); reject Mirror channel with a playout — 422 (#401)
- Correct Plex budget-exhausted guidance when no servers exist (#367); log scan cancellation below ERROR (#410)
CI / infra
- Shared CI toolchain image + runner-lane rebalance (#390); compiler-server memory + mysql caps (#406)
- Docs-only heavy-job skip, shallow-checkout safe (#416); skip re-validating an already-green identical merge tree (#420)
- ZAP warning vs failure exit-code handling in security-scan (#338)
Release process: cut on green
main@ df8c5202; no vulnerable transitive packages; decisions.md carried no superseded entries to consolidate. Prod follows the floating:prodtag — promotion via Komodo after the immutable:26.10.0candidate passessecurity-scan.sh.Downloads
-
v26.9.0
StableBuild ErsatzTV Image / Docs update reminder (push) Has been skippedBuild ErsatzTV Image / decisions.md append-only (push) Has been skippedBuild ErsatzTV Image / Functional E2E (curl contracts) (push) Has been skippedBuild ErsatzTV Image / API docs in sync (OpenAPI + endpoint index) (push) Has been skippedBuild ErsatzTV Image / Formatting (changed .cs conform to .editorconfig) (push) Has been skippedBuild ErsatzTV Image / EF migration integrity (SQLite + MySql) (push) Successful in 4m51sBuild ErsatzTV Image / Build & test (.NET) (push) Successful in 6m15sBuild ErsatzTV Image / Build & push image (amd64) (push) Successful in 7m9sreleased this
2026-07-16 22:24:15 +02:00 | 892 commits to main since this releaseFork release continuing from v26.8.0.
Features
- Configurable advertised IPTV base URL for M3U/XMLTV (#340). Optional
iptv.base_urlsetting (Settings → IPTV) that pins absolute M3U (guide/logo/stream) and XMLTV URLs to a fixed public origin instead of echoing the incoming requestHost— the fix for downstream consumers (e.g. Dispatcharr → Kodi) that can't resolve an internal Docker hostname. Blank/unset preserves today's request-derived behavior byte-for-byte; backed byConfigElement, no DB migration.
Fixes
- SPA left-sidebar horizontal scroll + body margin (#377, #373). Body margin reset + nav
box-sizingresets. - Report direct streams as "on air" (#99).
- Plex "authorized but no servers" is now a coherent, recoverable state (#345).
- Protect local-library filesystem path details behind authentication (#371).
Internal / CI / refactor
- App shell + routing extraction and explicit primary-action ownership (#247); Playouts screen extraction (#245).
- Analyzer promotion: incremental latest-
Allenforcement (#375). - Code-coverage collection + summary in CI (#15); advisory functional-E2E curl-contract harness (#299).
- Dependency updates: Humanizer.Core 3.0.10 (#328), JetBrains.ReSharper.GlobalTools 2025.3.4.1 (#329).
- Docs:
decisions.mdconsolidated into topic files underdocs/decisions/(optimistic-concurrency, api-auth-security, release-ci-governance, spa-modularization).
Images:
192.168.1.95:3000/timothy/ersatztv:26.9.0(immutable) /:prod.Downloads
- Configurable advertised IPTV base URL for M3U/XMLTV (#340). Optional
-
v26.8.0
StableBuild ErsatzTV Image / decisions.md append-only (pull_request) Successful in 9sBuild ErsatzTV Image / Docs update reminder (pull_request) Successful in 10sBuild ErsatzTV Image / Formatting (changed .cs conform to .editorconfig) (pull_request) Successful in 12sBuild ErsatzTV Image / API docs in sync (OpenAPI + endpoint index) (pull_request) Successful in 13sBuild ErsatzTV Image / Build & test (.NET) (pull_request) Successful in 9m37sBuild ErsatzTV Image / EF migration integrity (SQLite + MySql) (pull_request) Successful in 10m45sBuild ErsatzTV Image / Build & push image (amd64) (pull_request) Has been skippedBuild ErsatzTV Image / API docs in sync (OpenAPI + endpoint index) (push) Has been skippedBuild ErsatzTV Image / Docs update reminder (push) Has been skippedBuild ErsatzTV Image / decisions.md append-only (push) Has been skippedBuild ErsatzTV Image / Formatting (changed .cs conform to .editorconfig) (push) Has been skippedBuild ErsatzTV Image / Build & test (.NET) (push) Successful in 9m49sBuild ErsatzTV Image / EF migration integrity (SQLite + MySql) (push) Successful in 10m51sBuild ErsatzTV Image / Build & push image (amd64) (push) Successful in 3m47sreleased this
2026-07-13 19:50:39 +02:00 | 956 commits to main since this releaseSecured / versioned ChicoryTV SPA + REST API go-live release (#335). Hardens the authentication, concurrency, and HTTP-contract surface before the API and SPA are treated as a stable public interface.
Features
- Browser SPA session auth (#295) — session-OR-key gate enforced server-side (PR1, #295), plus the SPA session foundation: boot gate, CSRF header, login/setup/logout, machine-key screen and password change (PR2, PR #316).
- SPA API-key entry, sending the key on all methods (#197).
- API versioning with base-url-aware deprecation
Linkheaders (#286); collections scan-status REST surface + authoritative SPA reconcile (#271).
Security
- Enforcing CSP + Permissions-Policy on the host (#319).
- Side-effecting GETs POST-ified — troubleshoot actions and
GET /api/auth/machine-keyno longer mutate on GET (#301). - Repeatable black-box security scan in CI: authenticated ZAP + semgrep (#314).
Fixes
- If-Match now evaluates per RFC 7232 — a valid-but-non-matching precondition returns
412, not400; quote-aware scanner with OWS trimming (#265, PR #322). - Force-write non-If-Match root writers past a concurrent Version bump so rotation survives a race; aggregate ETag rotates on Collection/Playout config siblings (#269, PR #302).
- Content-aware stable child identity for schedule-item replace (#259, PR #276).
Images:
192.168.1.95:3000/timothy/ersatztv:26.8.0.Downloads
-
v26.7.0
StableBuild ErsatzTV Image / Docs update reminder (pull_request) Successful in 11sBuild ErsatzTV Image / EF migration integrity (SQLite + MySql) (pull_request) Successful in 5m47sBuild ErsatzTV Image / Build & test (.NET) (pull_request) Successful in 7m29sBuild ErsatzTV Image / Build & push image (amd64) (pull_request) Has been skippedBuild ErsatzTV Image / Docs update reminder (push) Has been skippedBuild ErsatzTV Image / EF migration integrity (SQLite + MySql) (push) Successful in 4m23sBuild ErsatzTV Image / Build & test (.NET) (push) Successful in 5m48sBuild ErsatzTV Image / Build & push image (amd64) (push) Successful in 5m8sreleased this
2026-07-11 21:37:03 +02:00 | 1063 commits to main since this releaseBlazor-removal release: ChicoryTV becomes the only UI (#91 phase b). Large release (~220 commits since v26.6.0) closing out SPA parity, media-source management, and a batch of scheduling/locking soundness fixes.
Features
- Media sources: SPA management screens + write REST API (#202, PR #262) — local, Plex, Jellyfin/Emby add/edit/scan flows.
- Shared "Add-to" layer — search + media mutation surface across screens (#208/#209, PR #216).
- Playouts: delete/reset/erase; collections custom-order & all-kind adds (#210/#211/#213, PR #214).
- SPA playback troubleshooting screen + status/subtitles/stream-selector endpoints (#145, PR #199).
- Playlist CRUD API + SPA editor (#153, PR #195); multi/rerun-collection editor screens (#151/#152, PR #194).
- Schedules editor full mutation depth + channel-editor gaps (#207/#212/#126, PR #229).
Fixes
- Locking soundness: EntityLocker atomicity + scan-lifecycle honesty (#231/#232, PR #241); release troubleshooting + playout locks on all terminal paths (#233/#234, PR #250).
- Scheduling: stop silently resetting fill-group state; invalidate deco edits (#251/#252, PR #258).
- Reviewer post-merge blockers: empty-lineup bare create (#212) + dirty-guard popstate/shuffle-toggle (#230) (PR #242).
- OpenAPI spec property naming now mirrors the runtime Newtonsoft serializer (#198, PR #201); rerun-collection existence-check per selection type (#193, PR #200).
Internal / CI / refactor
- Optimistic-concurrency contract rolled out across Block, Template, DecoTemplate, Playlist and schedule-items (#253, PRs #263/#268).
- SPA modularization: ChannelsScreen extracted from App.tsx (#244, epic #243 phase 1, PR #249).
- Husky pre-commit / pre-push / commit-msg guardrails (#273); CI build speed-ups (NuGet cache, shallow checkout) (#190/#192); CI MySQL host-port unpin to fix runner collisions (#236); docs-first rule hardened + non-blocking parity-doc reminder (#196).
Images:
192.168.1.95:3000/timothy/ersatztv:26.7.0.Downloads
-
released this
2026-07-08 19:07:40 +02:00 | 1286 commits to main since this releaseFirst release carrying the full SPA-parity feature set beyond the phase-a cutover (v26.5.0).
Highlights since v26.5.0
- Full scheduling parity in the SPA (#144/#162): blocks, scheduling templates, decos + deco templates, playout create/editors, block/template copy.
- Media: browse/search/trash + detail pages + image folder browser (#141/#161).
- Screens: Trakt lists, FFmpeg profile/filler/watermark editors, collections (manual+smart), logs & troubleshooting, channel edit, Classic UI escape hatch (#140–#147).
- Troubleshooting: block-playout history + sequential-YAML schedule validator (#158).
- Fixes: library picker artwork URLs + season drill-in (#180).
- This batch: collection-items API + real SPA listing (#155); multi/rerun collections REST API backend (#151/#152); security-registry assembly scan (#184); parallelized
GET /api/search; Trakt SPA link; onboarding/convention docs + testing map + generated endpoint index (#185).
Still pending (not in this release)
Blazor removal (#91 phase b) remains gated on the multi/rerun + playlist SPA editors and playback troubleshooting (#145) — it lands in v26.7.0.
Images:
192.168.1.95:3000/timothy/ersatztv:26.6.0(immutable) /:prod. Prod's Komodomedia-serversstack follows the floating:prodtag — no manual pin bump; deploy the stack once the immutable:26.6.0candidate passes the release security scans.Downloads
-
v26.5.0
Stablereleased this
2026-07-07 11:23:07 +02:00 | 1396 commits to main since this releaseSPA cutover release (#91 phase a): ChicoryTV becomes the default UI.
Features
- SPA cutover — root route (#91, PR #148):
/and legacy Blazor routes now serve / 302-redirect to the ChicoryTV SPA at/app. The React SPA is the default UI; the legacy Blazor Server UI remains available as a Classic-UI escape hatch (its removal follows in v26.7.0, #91 phase b).
Images:
192.168.1.95:3000/timothy/ersatztv:26.5.0.Downloads
- SPA cutover — root route (#91, PR #148):
-
v26.4.0
Stablereleased this
2026-07-07 10:20:30 +02:00 | 1400 commits to main since this releaseFirst fork release carrying application changes: the REST API foundation and the ChicoryTV React SPA foundation land side by side (~170 commits since v26.3.1).
Features
- ChicoryTV SPA foundation (#59 stack, #120) — new React/Vite single-page app served at
/app, with the ChicoryTV rebrand (#90/#139). - SPA screens: Dashboard with real data sources (#109), Channels (#84), Schedule editor (#86), Playouts (#87), Libraries (#88), Guide/EPG (#85), Settings (#93), Channel Builder (#89).
- REST API buildout — the programmatic surface the SPA (and future MCP) run on:
- Collections (#35/#45), schedule endpoints, playout endpoints (#37/#51, #100/#101/#107/#110), FFmpeg profile endpoints, standardized error contract (#46/#47).
- Channel state / now-playing (#97/#112), JSON channel guide (#102/#119), schedule item duration estimates (#111/#118), artwork upload (#104/#117).
- Media sources + scan status (#103/#106/#115), health endpoints (#108/#114), filler/watermark/graphics list endpoints (#105/#113).
- Library browse + search (#65/#130), channel templates entity + CRUD (#64/#133), composite create-channel endpoint (#63/#134).
- Core: active-session tracking for direct-streaming modes (#99/#121); Jellyfin/Emby music-video support (#42/#55); FFmpeg 8 work (#9/#56).
Security
- Scriban.Signed 6.5.2 → 7.2.5 (GHSA-5wr9-m6jw-xx44) (#137).
- Default-deny on mutating API writes; standardized write authorization.
Internal / CI / refactor
- Docker web-build stage fixed to a valid
node:22-bookworm-slimbase (#122). - Channel logo generator made testable (#39/#53); Renovate patch updates applied (#61).
Images:
192.168.1.95:3000/timothy/ersatztv:26.4.0.Downloads
- ChicoryTV SPA foundation (#59 stack, #120) — new React/Vite single-page app served at
-
v26.3.1
Stablereleased this
2026-06-27 00:35:35 +02:00 | 1573 commits to main since this releaseFirst fork build: upstream ErsatzTV 26.3.0 rebuilt on our infrastructure — no application changes. A patch bump because nothing functional changed.
Internal / CI / refactor
- New Gitea Actions build pipeline; retired upstream GitHub workflows (#4).
- Dockerfile now references the fork's stripped, amd64-only FFmpeg base image (
ersatztv-ffmpeg). - Hardened build pipeline per adversarial review; fixed the Serilog readiness marker for the HTTP smoke test.
- Restore no longer fails on low/moderate/high NuGet audit advisories (criticals still block).
- Removed external dependencies (Bugsnag, Trakt telemetry) (#2840).
- Enshrined the fork versioning scheme; added architecture/fork-maintenance docs (#6) and the Task Completion Protocol.
Images:
192.168.1.95:3000/timothy/ersatztv:26.3.1(=:prod, app-identical to upstream 26.3.0).Downloads