17c25e75faa81b2c5cddea20ac845c86bad0c794
Build ErsatzTV Image / Formatting (changed .cs conform to .editorconfig) (pull_request) Successful in 30s
PR Gates / CI image pin matches docker/ci (pull_request) Successful in 33s
PR Gates / Docs update reminder (pull_request) Successful in 37s
Review verdict / Set review-verdict status (pull_request) Successful in 36s
Build ErsatzTV Image / API docs in sync (OpenAPI + endpoint index) (pull_request) Successful in 1m33s
PR Gates / Script tests (pytest) (pull_request) Successful in 1m7s
Build ErsatzTV Image / Functional E2E (curl + UI contracts) (pull_request) Successful in 21m13s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (pull_request) Successful in 24m42s
Build ErsatzTV Image / Build & test (.NET) (pull_request) Successful in 27m7s
Build ErsatzTV Image / Build & push image (amd64) (pull_request) Has been skipped
review-verdict/h10 Review-verdict: MERGEABLE @ 17c25e7 (base: main)
PR Gates / decisions lifecycle (pull_request) Successful in 13s
The gate is npm run typecheck (tsc -b --pretty false, project-mode) — the
prior fix for the 3 node:*-unresolvable errors deferred a decision rather
than resolving it, per instruction not to change tsconfig unilaterally.
Evaluated and rejected: adding "node" to tsconfig.app.json's `types` (makes
Node globals type-visible to production browser code, dissolving a
deliberate separation the repo documents) and a file-local
`/// <reference types="node" />` (empirically leaks Node's ambient
`setTimeout` into the whole tsc -b program, breaking 3 unrelated
window.setTimeout mocks — confirmed by trying it).
Adopted instead: Vite's `import.meta.glob('/src/**/*.{ts,tsx,mts,cts}',
{ query: '?raw', import: 'default', eager: true })`, resolved natively by
Vite/vitest at transform time — no node:fs, no node:path, no node:url, no
tsconfig change, no cross-project references, and the guard test stays
exactly where it is (`src/api/`). `vite/client` types (hence
`import.meta.glob`) were already wired in via `src/vite-env.d.ts`.
`isScannableSourceFileName` is unchanged and still the single place the
glob's results are filtered through — the extension set moved into the glob
literal, but discovery still runs every matched file through this same
named, tested predicate rather than a second copy of the logic.
Verified the discovery mechanism swap changes nothing observable: a
side-by-side comparison test (old fs-based walk vs new glob-based walk, both
run against the current repo, not committed — temporary) found byte-identical
results — 19 call sites, 136 scannable files, zero set difference in either
direction. Re-ran both required plants against the new mechanism:
1. Duplicate at-cap literal appended to builder/ChannelBuilder.tsx (an
already-registered file): caught —
`UNREGISTERED (1) ... + builder/ChannelBuilder.tsx:2058:54:literal:100`.
2. New file with a `https://` string (no false match), a shorthand
`{ pageSize }`, and two literal pageSize values in one ternary's two
branches on a single line: all 3 real sites caught at distinct columns —
`+ screens/_fakeDefectPlant.ts:4:30:shorthand:pageSize`,
`+ screens/_fakeDefectPlant.ts:8:22:literal:111`,
`+ screens/_fakeDefectPlant.ts:8:48:literal:222`.
Also re-verified the .mts/.cts discovery pin still fails when the extension
set is narrowed back to `.ts`/`.tsx` only (2 of 14 predicate cases fail, as
before). All three plants reverted after verification; registry/predicate
otherwise untouched.
Full local gate: `npm run lint` clean, `npm run typecheck` (tsc -b) clean —
zero errors, including the 3 node:* ones this commit resolves — `npx vitest
run` 110 files / 1078 tests passed (re-run three times; two runs hit
transient vitest worker-pool timeouts from overlapping background processes
on this machine, unrelated to the code — a clean sequential run passed in
full both before and after).
Merge pull request 'fix(649): point the ENFORCED review-verdict gate at the shared PR-file enumeration' (#666) from fix/649-enforced-verdict-guard into main
Merge pull request 'fix(649): point the ENFORCED review-verdict gate at the shared PR-file enumeration' (#666) from fix/649-enforced-verdict-guard into main
Merge pull request 'chore(deps): update dependency humanizer.core to 3.0.10' (#328) from renovate/humanizer-monorepo into main
ErsatzTV
ErsatzTV lets you transform your media library into a personalized, live TV experience - complete with EPG, channel scheduling, and seamless streaming to all your devices. Rediscover your content, your way.
How It Works
- Install ErsatzTV: Download and set up the server on your system.
- Add Your Media: Connect your media libraries and collections.
- Create Channels: Design and schedule your own live channels.
- Stream Anywhere: Watch on any device with IPTV and EPG support.
Key Features
- Custom channels: Create and schedule your own live TV channels.
- IPTV & EPG: Stream with IPTV and Electronic Program Guide support.
- Hardware Transcoding: High-performance streaming with hardware acceleration (NVENC, QSV, VAAPI, AMF, VideoToolbox)
- Media Server Integration: Connect Plex, Jellyfin, Emby and more.
- Music & Subtitles: Mix music videos and enjoy subtitle support.
- Open Source: Free, open, and community-driven project.
Documentation
Documentation is available at ersatztv.org.
License
This project is inspired by pseudotv-plex and the dizquetv fork and is released under the zlib license.
