94182cdd534284de250a9407ed4d02d55e14438d
Cold adversarial review of fe342a6a found the blanket loadAllPages-everywhere fix
dangerous for the three getLibraryBrowseItems pickers (RerunCollectionsScreen,
PlaylistsScreen, FillerPresetsScreen): paging Episode/Song/Image/Movie/MusicVideo
to completeness can mean ~200 serial requests against a 20k-row library, each more
expensive than the last, to populate a <select> with thousands of <option> nodes.
- Class A (bounded-by-construction lists: rerun collections, multi-collections,
playlists) keep loadAllPages. Class B (media-library pickers) now fetch ONE
bounded page and surface truncation via a `Showing the first N of M` hint wired
to the real totalCount, instead of paging to completeness or truncating silently.
- loadAllPages: reports `{ items, complete }` instead of just `T[]` so a caller
can no longer mistake a defensive empty-page break for a full list (F4); accepts
an optional AbortSignal so a superseded loop stops issuing further page requests
(F2); baseParams is now required via a conditional rest-tuple whenever the
loader's params type has a field beyond pageNum/pageSize (F6); pushes into the
accumulator instead of re-spreading it every page (F7).
- MultiCollectionsScreen/RerunCollectionsScreen/SchedulesScreen: add a seqRef +
AbortController guard around the list/bootstrap loads so a stale loadAllPages
loop can't resolve after a newer one and resurrect deleted rows (F3); log and
surface an incomplete load rather than rendering it as whole.
- docs/spa-conventions.md §3b rewritten for the Class A / Class B split; new
decision record docs/decisions/records/spa/list-completeness-vs-bounded-pickers.md
(spa.list-completeness-vs-bounded-pickers), catalog regenerated.
- Tests: paging.test.ts covers null/undefined totalCount, null page, a
short-but-non-empty page, a page-2 rejection, the complete:false flag, and
cancellation (asserting fetch call COUNT stays put after abort), plus a
compile-time @ts-expect-error pinning the F6 typing fix. Screen-level tests
pin a real second HTTP request for a >100-item Class A list
(MultiCollectionsScreen) and exactly one /library/browse request plus the
truncation hint for a Class B picker (RerunCollectionsScreen).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
harden(421,559): percent-encode access_token in IPTV URLs, redact from logs, no-store on tokened manifests (#574)
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.
