Support X and Twitter video URLs with reliable resolved playback

Share yt-dlp resolution across YouTube and X/Twitter, preserve separate audio/video inputs and headers, and use finite duration metadata directly. Prefer direct HTTPS formats for X, support multi-video posts and explicit selections, and enforce single-connection hosts before resolution and on resolved sources.

Include the related recorded-media probing, seeking recovery, and metadata polling fixes, with regression coverage for playback and protected sources.
This commit is contained in:
2026-10-23 00:26:57 -07:00
parent 91e96894b7
commit 87286c748b
8 changed files with 722 additions and 64 deletions
+12 -4
View File
@@ -77,7 +77,13 @@ The regression history matters:
Default code behavior is `split` when no mode is set. The Compose example uses `relay` because it is the mode to try for IPTV streams.
Finite-duration sessions are treated as recorded video and seekable when metadata probing is enabled. Metadata probing is enabled by default except in `relay`, where it defaults off to preserve the one-upstream-connection behavior for IPTV-style streams. When the configured mode is `relay` and duration metadata is known, recorded sessions switch to the seek-capable `split` path; live/unknown-duration streams stay in relay mode.
Finite-duration sessions are treated as recorded video and seekable once duration metadata is available. Metadata probing defaults off for unknown/live URLs in `relay` to preserve one upstream connection, but local files, YouTube and X/Twitter URLs without resolved duration metadata, recognized media file extensions, and Jellyfin download/stream URLs are probed automatically. Likely recorded sources retry failed probes up to three times in the background, keeping metadata pending until they finish. The timeout grows by a factor of three per attempt, capped at 30 seconds (default 4, 12, then 30 seconds), with 1- and 2-second retry delays. The frontend polls for the full pending period, including after stream reconnects.
When configured for `relay`, recognized recorded files use `split` immediately, before duration is known. HTTP inputs for these files allow automatic range seeking from startup, including when `FFMPEG_INPUT_SEEKABLE=0` is configured for live streams. This avoids reading an entire MP4 through a pipe to find trailing metadata. Other sessions with known duration also use `split`; unknown/live streams stay in relay mode.
YouTube and X/Twitter sessions use the shared yt-dlp resolver and `split`, with separate resolved video and audio URLs when needed, subject to `SINGLE_CONNECTION_HOSTS` protections. Recognize `x.com` and `twitter.com`, including their `www`, `m`, and `mobile` variants. X posts can return playlists even with `--no-playlist`; use `--playlist-items 1` and unwrap the first entry, while preserving explicit `/video/N` selections. X/Twitter defaults to direct HTTPS formats (preferring combined MP4s), since the source proxy does not rewrite relative HLS segment URLs. Direct MP4 formats may omit codec names: only the explicit value `none` means a stream is absent. The audio proxy must use the audio URL and its headers; frames and probes use the video URL and its headers. Enable yt-dlp's Node runtime explicitly with `--js-runtimes node:<process.execPath>`. Use yt-dlp's finite, non-live duration metadata directly to avoid an extra ffprobe connection.
IPTV subscriptions such as Oceanstream permit only one stream per customer. `SINGLE_CONNECTION_HOSTS` lists protected hostnames, including their subdomains; these sessions always use `relay`, never metadata probing or `split`, even for recorded-file URLs or when global probing/split is enabled. Seeking and recovery must not bypass this rule. Unknown live streams with probing disabled must not gain a metadata probe during recovery. The deployed list also includes the configured IPTV aliases `line.oscreen.cc` and `yeahtv.live`.
## ffmpeg Pipelines
@@ -175,17 +181,18 @@ Runtime:
- `PORT`: HTTP port, default `3000`.
- `FFMPEG_PATH`: ffmpeg binary path, default `ffmpeg`.
- `YT_DLP_PATH`: yt-dlp binary path, default `yt-dlp`.
- `YT_DLP_FORMAT`: yt-dlp format selector for YouTube URLs, default `best[ext=mp4][vcodec!=none][acodec!=none]/best[vcodec!=none][acodec!=none]/best`.
- `YT_DLP_FORMAT`: format selector override for YouTube and X/Twitter. YouTube defaults to `bestvideo[protocol=https]+bestaudio[protocol=https]/bestvideo+bestaudio/best`; X/Twitter defaults to `best[protocol=https]/bestvideo[protocol=https]+bestaudio[protocol=https]`. Separate audio/video inputs and combined sources are supported.
- `YT_DLP_TIMEOUT_MS`: yt-dlp resolution timeout, default `45000`.
- `FFMPEG_LOG_LEVEL`: ffmpeg log level, default `warning`.
- `FFMPEG_INPUT_SEEKABLE`: HTTP input seekable option, default `0`.
- `FFMPEG_INPUT_SEEKABLE`: HTTP input seekable option for unknown/live sources, default `0`; recognized recorded sources use automatic range seeking.
- `FFMPEG_HTTP_RECONNECT`: enable ffmpeg HTTP reconnect options for HTTP inputs, default `1`.
- `FFMPEG_HTTP_RECONNECT_DELAY_MAX`: max ffmpeg reconnect delay, default `2`.
- `FFMPEG_HTTP_RECONNECT_MAX_RETRIES`: max ffmpeg reconnect retries, default `4`, applied only when the installed ffmpeg supports `reconnect_max_retries`.
- `FFMPEG_HTTP_RECONNECT_ON_HTTP_ERROR`: HTTP status list for reconnect, default `5xx`.
- `METADATA_PROBE_ENABLED`: probe session duration with ffprobe, default `1` except in `relay`, where default is `0`.
- `METADATA_PROBE_TIMEOUT_MS`: ffprobe duration timeout, default `4000`.
- `METADATA_PROBE_TIMEOUT_MS`: initial ffprobe duration timeout, default `4000`; recorded-source retry timeouts increase up to `30000`.
- `PLAYBACK_CONNECTION_MODE`: `split`, `relay`, or `single`.
- `SINGLE_CONNECTION_HOSTS`: comma-separated subscription hosts that must use relay with no metadata probes or seeking; also matches their subdomains.
- `RECENT_URLS_PATH`: recent URL JSON path.
- `RECENT_URL_LIMIT`: recent URL count, default `12`.
- `FAVORITES_PATH`: favorites JSON path.
@@ -227,6 +234,7 @@ Basic validation:
```sh
node --check server/index.js
npm test
docker compose -f docker-compose-example.yml config
docker build -t frame-stream-player .
```