forked from Foxyz/sync_replay_project
Read once: a link passed on is fetched from the site once, for the putter's player and the relay
Before, the member passing a page link on downloaded it twice, independently: their mpv straight from the CDN and the relay's pulls again, with a different format pick and without yt-dlp's cookies. The two drifted (the putter played on while viewers stalled, or the reverse) and doubled the connections the CDN saw. Now one SharedSource per track reads the site once (resuming after cuts, backing off on 412/429/5xx and silence) into a bounded buffer (64 MiB ahead, 16 behind) that serves both the putter's mpv through a loopback endpoint and the server's pulls; the putter plays the relay's own format pick, and the relay sends yt-dlp's cookies. Up next's warm relay uses the same reader. Pieces tracks (HLS/DASH) keep two fetches for now. Measured in the opt-in e2e: half the CDN traffic, no 412s under a lockout CDN, viewer seeks 0.29 s instead of 4 s there. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
7715dc479a
commit
ec0849eb01
19 files changed
+1894
-101
No files matched your search
@@ -38,6 +38,11 @@ abstract class RelayConstants {
|
||||
/// first bytes, under mpv's 5 s `network-timeout`: a header that never
|
||||
/// comes in time costs ffmpeg the file's size for good.
|
||||
static const int relayHeaderWaitMs = 4000;
|
||||
/// The source's own buffer of a link it reads once for its player and
|
||||
/// the upload (`SharedSource`), all tracks together besides the pins:
|
||||
/// the two read within [sharedAheadBytes] of each other.
|
||||
static const int sharedAheadBytes = 64 << 20;
|
||||
static const int sharedBackBytes = 16 << 20;
|
||||
/// The warm relay (Up next's first relayed item, before it goes up)
|
||||
/// keeps this much ahead of and behind its viewers' preloads, besides
|
||||
/// the pins: about what a preload player reads ahead (48 MiB), never the
|
||||
|
||||
@@ -65,15 +65,21 @@ class YtDlpFormat {
|
||||
/// Relayed as pieces (see relay_pieces.dart), not as a byte stream.
|
||||
bool get isPieces => _piecesProtocols.contains(protocol);
|
||||
|
||||
/// From one of yt-dlp's formats: its `http_headers` plus its `cookies`
|
||||
/// as a Cookie header, the same a player gets ([YtDlpPlay.parse]).
|
||||
static YtDlpFormat _of(Map f) {
|
||||
final headers = f['http_headers'];
|
||||
final opts = f['downloader_options'];
|
||||
final chunk = opts is Map ? opts['http_chunk_size'] : null;
|
||||
final h = f['height'] is num ? (f['height'] as num).toInt() : 0;
|
||||
final cookie = cookieHeaderOf(f['cookies']);
|
||||
return YtDlpFormat(
|
||||
id: '${f['format_id'] ?? ''}',
|
||||
url: Uri.parse(f['url'] as String),
|
||||
headers: headers is Map ? {for (final e in headers.entries) '${e.key}': '${e.value}'} : const {},
|
||||
headers: {
|
||||
if (headers is Map) for (final e in headers.entries) '${e.key}': '${e.value}',
|
||||
if (cookie.isNotEmpty) 'Cookie': cookie,
|
||||
},
|
||||
height: h == 0 ? null : h,
|
||||
ext: '${f['ext']}',
|
||||
chunkSize: chunk is num && chunk > 0 ? chunk.toInt() : null,
|
||||
|
||||
@@ -262,6 +262,25 @@ void main() {
|
||||
expect(pick(['avc1.640033', 'vp09.00.40.08']), 'v1');
|
||||
});
|
||||
|
||||
test('what a relay reads sends what a player sends: each format\'s headers with its cookies', () {
|
||||
const cookies = 'SESSDATA=abc; Domain=.bilibili.com; Path=/; Secure; Expires=1999999999; buvid3=xyz; Domain=.bilibili.com';
|
||||
Map<String, Object?> f(String id, String vcodec, String acodec, int? height) => {
|
||||
'format_id': id, 'url': 'https://cdn/$id.m4s', 'protocol': 'https', 'ext': 'mp4',
|
||||
'vcodec': vcodec, 'acodec': acodec, 'height': height,
|
||||
'http_headers': {'Referer': 'https://www.bilibili.com/', 'User-Agent': 'UA'},
|
||||
'cookies': cookies,
|
||||
};
|
||||
final v = f('30080', 'avc1.640032', 'none', 1080), a = f('30280', 'none', 'mp4a.40.2', null);
|
||||
final info = YtDlpInfo.parse({'title': 'Page', 'formats': [v, a], 'requested_formats': [v, a]});
|
||||
const want = {'Referer': 'https://www.bilibili.com/', 'User-Agent': 'UA', 'Cookie': 'SESSDATA=abc; buvid3=xyz'};
|
||||
expect([for (final t in info.tracks) t.format.id], ['30080', '30280']);
|
||||
for (final t in info.tracks) {
|
||||
expect(t.format.headers, want, reason: t.kind);
|
||||
}
|
||||
expect(info.play!.headers, want);
|
||||
expect(info.formats['30080']!.describe(), isNot(contains('SESSDATA')), reason: 'the log names headers only');
|
||||
});
|
||||
|
||||
test('what a player opens: yt-dlp\'s pick, an EDL for video + audio, headers with the cookies, subtitles but danmaku', () {
|
||||
final split = YtDlpInfo.parse({
|
||||
'title': 'Page',
|
||||
|
||||
Reference in new issue
Block a user