Protocol 11: split-track relay (YouTube video+audio), chunked upstream reads

Relayed media can carry 2-4 byte tracks (video, audio): yt-dlp picks a
split pair when no single-file format exists (h264 first), the source
channel serves each track, the server keeps a buffer per track (budget
split by size, 8 MiB floor) and viewers open an mpv edl:// of the track
URLs. Expired URLs re-resolve once for all tracks. Upstream GETs are cut
into <= http_chunk_size (10 MiB) ranges: YouTube throttles larger ones.
Live streams and DRM are refused; HLS/DASH pieces are a follow-up.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
foxyzandClaude Opus 5.5 committed 2026-10-09 19:59:41 +03:00
1 parent b2b77a6b3f
commit b038706dff
19 files changed
+945 -286

No files matched your search

+5 -4
View File
@@ -48,11 +48,12 @@ class SyncServer {
/// Upstream connections of the Jellyfin relay; created on first use.
HttpClient? _relayClient;
SyncServer(this.config) {
/// [ytDlp] replaces the server's own (tests).
SyncServer(this.config, {YtDlp? ytDlp}) {
hub = ServerHub(sessions, config.secret,
hostKey: config.hostKey,
jellyfin: config.jellyfin,
ytDlp: YtDlp(serverYtDlpDir(config.ytDlpDir), enabled: config.ytDlp));
ytDlp: ytDlp ?? YtDlp(serverYtDlpDir(config.ytDlpDir), enabled: config.ytDlp));
}
bool get isRunning => _server != null;
@@ -103,8 +104,8 @@ class SyncServer {
return 'http://${authority(dialledHost)}/m/$sid/$token/$name';
}, relayUrlBuilder: (sid, token, itemId, dialledHost) =>
'http://${authority(dialledHost)}/j/$sid/$token/${Uri.encodeComponent(itemId)}',
relayMediaUrlBuilder: (sid, token, mediaId, name, dialledHost) =>
'http://${authority(dialledHost)}/r/$sid/$token/$mediaId/${relayUrlName(name)}');
relayMediaUrlBuilder: (sid, token, mediaId, name, tracks, dialledHost) =>
relayViewerUrl('http://${authority(dialledHost)}/r/$sid/$token/$mediaId', name, tracks: tracks));
_app.fallback = (final req) => Response.notFound(
body: Body.fromString('not found'),
);