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
+9 -5
View File
@@ -14,11 +14,11 @@ npm install
npm start
```
Open `http://localhost:3000`, paste a direct HTTP(S) stream URL, and click `Play`. Use `Queue` to add a URL to Recents without starting playback.
Open `http://localhost:3000`, paste a direct HTTP(S) stream URL, YouTube link, or X/Twitter video post URL, and click `Play`. Use `Queue` to add a URL to Recents without starting playback.
Set `LOCAL_VIDEOS=/path/to/videos` before starting the server to enable `Play Local`. The picker lets you navigate directories under that root, search the library, and play selected files through the server-side ffmpeg pipeline as MP3 audio plus JPEG canvas frames.
You need an `ffmpeg` binary with decoders for the stream's video and audio codecs. YouTube page links also need `yt-dlp`; Docker installs it automatically from the upstream master branch at image build time. If your stream is H.264 or HEVC, make sure your installed `ffmpeg` actually includes those decoders. You can point the app at different binaries with:
You need an `ffmpeg` binary with decoders for the stream's video and audio codecs. YouTube and X/Twitter page links also need `yt-dlp`; Docker installs it automatically from the upstream master branch at image build time. If your stream is H.264 or HEVC, make sure your installed `ffmpeg` actually includes those decoders. You can point the app at different binaries with:
```sh
FFMPEG_PATH=/path/to/ffmpeg npm start
@@ -52,9 +52,11 @@ To use local playback in Docker, mount a host video directory into the container
docker logs -f frame-stream-player
```
The app sets `FFMPEG_INPUT_SEEKABLE=0` by default so `ffmpeg` reads stream inputs sequentially and avoids extra HTTP range connections. If a specific VOD file requires seeking for metadata, set `FFMPEG_INPUT_SEEKABLE=-1` to restore ffmpeg's automatic behavior. For ffmpeg-owned HTTP inputs, reconnect handling is enabled by default with `FFMPEG_HTTP_RECONNECT=1`. `FFMPEG_HTTP_RECONNECT_MAX_RETRIES` is applied only when the installed `ffmpeg` supports that HTTP protocol option.
The app sets `FFMPEG_INPUT_SEEKABLE=0` by default so `ffmpeg` reads unknown/live stream inputs sequentially and avoids extra HTTP range connections. Recognized recorded files and sessions with a known duration automatically allow HTTP range requests from the start, so ffmpeg can jump to MP4 metadata instead of downloading the entire file first. For ffmpeg-owned HTTP inputs, reconnect handling is enabled by default with `FFMPEG_HTTP_RECONNECT=1`. `FFMPEG_HTTP_RECONNECT_MAX_RETRIES` is applied only when the installed `ffmpeg` supports that HTTP protocol option.
YouTube URLs are resolved server-side with `yt-dlp` before they enter the existing ffmpeg pipeline. Recents and favorites keep the original YouTube URL, while the short-lived playback session uses the resolved media URL and headers returned by `yt-dlp`. Tune the selected format with `YT_DLP_FORMAT` and the resolver timeout with `YT_DLP_TIMEOUT_MS`.
YouTube and X/Twitter URLs are resolved server-side with `yt-dlp`, with the app's Node runtime explicitly enabled for JavaScript challenges. Both `x.com` and `twitter.com` links work, including their `www`, `m`, and `mobile` variants. For posts with multiple videos, the first video plays unless the URL selects one with `/video/N`. The YouTube default selector is `bestvideo[protocol=https]+bestaudio[protocol=https]/bestvideo+bestaudio/best`. X/Twitter defaults to `best[protocol=https]/bestvideo[protocol=https]+bestaudio[protocol=https]`, preferring progressive MP4s because the source proxy does not rewrite relative HLS segment URLs. Audio and video can have separate URLs and request headers: each is proxied to its corresponding ffmpeg worker in `split` mode. Combined audio/video URLs also work. Finite, non-live duration metadata from yt-dlp enables seeking without another source probe. Recents and favorites keep the original page URL. Tune the selected format with `YT_DLP_FORMAT` and the resolver timeout with `YT_DLP_TIMEOUT_MS`.
Set `SINGLE_CONNECTION_HOSTS` to a comma-separated list of IPTV subscription hosts, for example `oceanstream.me`. Matching hosts and their subdomains always use `relay`, skip metadata probes, and disable seeking and recovery probes, even if the URL looks like a recorded file or the global mode is `split`. This protects subscriptions that permit only one upstream stream. Include any alternate provider hostnames in the list.
JPEG frames are dropped when the browser WebSocket falls behind instead of letting stale frames queue indefinitely. Tune the server-side backlog cap with `MAX_WS_BUFFER_BYTES`; the default is `2097152`.
@@ -68,7 +70,9 @@ Available playback modes:
- `relay`: One source connection from the backend, then the compressed input bytes are teed into separate audio and frame `ffmpeg` workers. This is intended for IPTV hosts that stop early or reject multiple active connections.
- `single`: One source connection and one `ffmpeg` worker with both audio and frame outputs. This is the simplest one-connection fallback, but audio and frame delivery can affect each other.
Finite-duration streams are treated as recorded video and become seekable once metadata is available. Metadata probing is enabled by default except in `relay`, where `METADATA_PROBE_ENABLED=0` avoids extra upstream connections. Set `METADATA_PROBE_ENABLED=1` if you want recorded relay sessions to become seekable and switch to the seek-capable `split` path.
Finite-duration streams are treated as recorded video and become seekable once metadata is available. Metadata probing is enabled by default except in `relay`, where it is disabled for unknown/live URLs to avoid extra upstream connections. Local files, YouTube and X/Twitter URLs without resolved duration metadata, recognized media file extensions, and Jellyfin download/stream URLs are probed automatically. Set `METADATA_PROBE_ENABLED=1` to probe other URLs as well.
Recognized recorded files use `split` immediately when the configured mode is `relay`, even while duration detection is pending. Other relay sessions switch to `split` once a finite duration is known. Failed probes for likely recorded sources retry up to three times in the background, with increasing timeouts (by default 4, 12, then 30 seconds) and short delays. `METADATA_PROBE_TIMEOUT_MS` sets the initial timeout; each attempt is capped at 30 seconds. Seeking becomes available when the probe succeeds, without restarting playback.
Relay mode uses bounded per-worker input queues so one branch can briefly lag without immediately stalling the other. Tune the cap with `MAX_RELAY_BRANCH_QUEUE_BYTES`; the default is `8388608`.