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:
foxyzandClaude Opus 5.5 committed 2026-10-18 02:05:03 +03:00
1 parent 7715dc479a
commit ec0849eb01
19 files changed
+1894 -101

No files matched your search

+76 -62
View File
@@ -592,74 +592,88 @@ void mountRelayRoutes(
app.get('/r/:sid/:token/:mediaId/:seg/p/:piece', (final req) => viewer(req, tracked: true, piece: true));
}
/// A relayed byte range written on the raw connection: the status line
/// and headers go out with the first body bytes, or after [headerWait]
/// without them (dart:io would hold them until body bytes come, and an
/// ffmpeg whose request gets no headers within its 5 s timeout has lost
/// the file's size: every later seek fails with "size -38"). Then the
/// body as the buffer has it; a read waiting on missing bytes keeps the
/// connection open meanwhile. Always `connection: close` and closed after
/// the body: ffmpeg asks for close, and one that is not told so reuses the
/// A relayed byte range on the raw connection ([writeRelayRange]).
Hijack _rangeResponse(int status, Map<String, String> headers, int length, Stream<Uint8List>? body,
{required void Function(Object e) onBroke}) =>
Hijack((ch) => writeRelayRange(ch.stream, ch.sink, status, headers, length, body, onBroke: onBroke));
/// Writes a relayed byte range on a raw connection ([input] / [out]: the
/// server's `/r` answers, and the source's own loopback endpoint in
/// shared_source.dart): the status line and headers go out with the
/// first body bytes, or after [headerWait] without them (an HTTP stack
/// would hold them until body bytes come, and an ffmpeg whose request
/// gets no headers within its 5 s timeout has lost the file's size:
/// every later seek fails with "size -38"). Then the body as the buffer
/// has it; a read waiting on missing bytes keeps the connection open
/// meanwhile. Always `connection: close` and closed after the body:
/// ffmpeg asks for close, and one that is not told so reuses the
/// connection the server closed for its next seek and fails it the same
/// way. A relay that ends ([RelayEnded]) closes the connection plainly;
/// any other failure ([onBroke]) resets it. The client going away cancels
/// the read.
Hijack _rangeResponse(int status, Map<String, String> headers, int length, Stream<Uint8List>? body,
{required void Function(Object e) onBroke, Duration headerWait = const Duration(milliseconds: RelayConstants.relayHeaderWaitMs)}) {
return Hijack((ch) async {
final out = ch.sink;
final head = StringBuffer('HTTP/1.1 $status ${status == 206 ? 'Partial Content' : 'OK'}\r\n')
..write('date: ${HttpDate.format(DateTime.now())}\r\n');
headers.forEach((k, v) {
if (k != 'content-length' && k != 'connection') head.write('$k: $v\r\n');
});
head.write('content-length: $length\r\nconnection: close\r\n\r\n');
final it = body == null ? null : StreamIterator(body);
// A read cancelled while it waits ends with what it was waiting for
// (its relay ending, say): moot then.
Future<void> stop() async => it?.cancel().catchError((_) {});
// The client closing (a seek elsewhere, the player gone) ends the read.
final input = ch.stream.listen(null,
onDone: () => unawaited(stop()), onError: (_) => unawaited(stop()), cancelOnError: true);
var reset = false;
// The first bytes go out with the headers when they come in time.
Future<bool>? first = it?.moveNext();
if (first != null) {
await Future.any([first.then((_) {}, onError: (_) {}), Future<void>.delayed(headerWait)]);
}
try {
out.add(latin1.encode('$head'));
if (out is IOSink) await out.flush();
while (it != null) {
final bool more;
try {
final next = first ?? it.moveNext();
first = null;
more = await next;
} catch (e) {
onBroke(e);
reset = e is! RelayEnded;
break;
}
if (!more) break;
out.add(it.current);
if (out is IOSink) await out.flush();
}
} catch (_) {
// The client went away mid-write: nothing to say.
reset = true;
} finally {
await input.cancel().catchError((_) {});
await stop();
if (reset && out is Socket) {
out.destroy();
} else {
await out.close().catchError((_) {});
}
}
/// the read. Never throws.
Future<void> writeRelayRange(Stream<List<int>> input, StreamSink<List<int>> out, int status,
Map<String, String> headers, int length, Stream<Uint8List>? body,
{required void Function(Object e) onBroke,
Duration headerWait = const Duration(milliseconds: RelayConstants.relayHeaderWaitMs)}) async {
final head = StringBuffer('HTTP/1.1 $status ${_reason(status)}\r\n')
..write('date: ${HttpDate.format(DateTime.now())}\r\n');
headers.forEach((k, v) {
if (k != 'content-length' && k != 'connection') head.write('$k: $v\r\n');
});
head.write('content-length: $length\r\nconnection: close\r\n\r\n');
final it = body == null ? null : StreamIterator(body);
// A read cancelled while it waits ends with what it was waiting for
// (its relay ending, say): moot then.
Future<void> stop() async => it?.cancel().catchError((_) {});
// The client closing (a seek elsewhere, the player gone) ends the read.
final listening = input.listen(null,
onDone: () => unawaited(stop()), onError: (_) => unawaited(stop()), cancelOnError: true);
var reset = false;
// The first bytes go out with the headers when they come in time.
Future<bool>? first = it?.moveNext();
if (first != null) {
await Future.any([first.then((_) {}, onError: (_) {}), Future<void>.delayed(headerWait)]);
}
try {
out.add(latin1.encode('$head'));
if (out is IOSink) await out.flush();
while (it != null) {
final bool more;
try {
final next = first ?? it.moveNext();
first = null;
more = await next;
} catch (e) {
onBroke(e);
reset = e is! RelayEnded;
break;
}
if (!more) break;
out.add(it.current);
if (out is IOSink) await out.flush();
}
} catch (_) {
// The client went away mid-write: nothing to say.
reset = true;
} finally {
await listening.cancel().catchError((_) {});
await stop();
if (reset && out is Socket) {
out.destroy();
} else {
await out.close().catchError((_) {});
}
}
}
String _reason(int status) => switch (status) {
200 => 'OK',
206 => 'Partial Content',
404 => 'Not Found',
416 => 'Range Not Satisfiable',
_ => 'Status',
};
Response _plain(int status) => Response(status, body: Body.fromString('status $status'));
final _readFailThrottle = LogThrottle(burst: 3);