YouTube custom-channel reliability, plus channel-numbering fixes for accounts with no chnum_start configured anywhere.
📺 YouTube custom channels
YouTube expanded its PO Token requirement to the web, web_safari, ios, tv, and mweb yt-dlp player clients, which broke YouTube custom-channel resolution entirely ("No video formats found!") — not just for live streams, for ordinary videos too.
- Switched to trying the
android_vrandandroidclients first — both still PO-token-free as of this release — with the old web-based clients kept as a last-resort fallback. - Added a weekly job that refreshes yt-dlp from GitHub master inside the running container (YouTube's extractor breaks often enough that a month-old install goes stale between image rebuilds), and made sure it actually reaches play-time: it now signals gunicorn to gracefully recycle its workers afterward, since a worker that already imported yt_dlp otherwise keeps serving the stale copy until it naturally recycles.
- Fixed two robustness gaps in that same job: its own timeout no longer collides with the job-level timeout (which could've orphaned a half-finished install), and it now runs on the same queue as scrapes so it can't race a concurrent YouTube scrape's package import.
🔢 Channel numbering
- Fixed channels with a literal
|in their name (e.g. "FOX LOCAL Dallas | Fort Worth") being silently dropped from both the standard and Gracenote M3U outputs — not just showing a blank Ch# in the admin list. A legacy Roku ID-encoding check was too broad and misidentified ordinary channel names as Gracenote claims. - Fixed channels being silently skipped entirely (missing
tvg-chno, blank admin Ch#) when a source has nochnum_startconfigured and there's no global fallback either — previously affected any install where numbering was never explicitly set up on every source.
🧹 Internal cleanup
Consolidated five near-identical copies of the channel-number cursor-assignment logic in app/generators/m3u.py into two shared helpers. Verified behavior-preserving via 15,000+ randomized test cases plus a byte-for-byte diff of real M3U output before/after.