From a833be0ec7ff17869da30f8e8efcb0e774ec8f2e Mon Sep 17 00:00:00 2001 From: LogicalKarma <> Date: Tue, 26 May 2026 16:51:58 +0300 Subject: [PATCH 1/2] fix(range): use clen query param as total file size, not upstream Content-Length --- src/main.rs | 33 +++++++++++++++++++++++---------- 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/src/main.rs b/src/main.rs index f9447a6..75b783c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -211,6 +211,7 @@ fn parse_range(range_str: &str, total_size: u64) -> Option { fn handle_range_response_correction( response: &mut HttpResponseBuilder, range_str: Option<&String>, + clen: Option, resp: &reqwest::Response, ) -> Option<()> { // Check if this is a range request (either in headers or query string) @@ -224,14 +225,24 @@ fn handle_range_response_correction( return None; } - // Get content length from response headers - let total_size = resp - .headers() - .get("content-length")? - .to_str() - .ok()? - .parse::() - .ok()?; + // total_size must be the FULL file size, taken from the videoplayback URL's + // `clen` query param. The response's Content-Length is the size of the chunk + // returned for the current range (clen - start when start > 0), NOT the full + // file. Using Content-Length as total_size produced Content-Range headers + // where end < start with a too-small denominator -- Chromium's media stack + // rejects those as malformed, which presented as a seek-past-buffer hang on + // itag-18 progressive playback. Fall back to Content-Length only when clen + // is absent (defensive; videoplayback URLs always carry clen in practice). + let total_size = match clen { + Some(c) if c > 0 => c, + _ => resp + .headers() + .get("content-length")? + .to_str() + .ok()? + .parse::() + .ok()?, + }; if total_size == 0 { return None; @@ -482,8 +493,10 @@ async fn index(req: HttpRequest) -> Result> { } // Fix range request handling - convert 200 to 206 if we have a range request - // and ensure Content-Range header is present - handle_range_response_correction(&mut response, range.as_ref(), &resp); + // and ensure Content-Range header is present. `clen` from the videoplayback + // URL is the canonical total file size; passed in to avoid mis-deriving it + // from the upstream chunk's Content-Length. + handle_range_response_correction(&mut response, range.as_ref(), clen, &resp); if rewrite { if let Some(content_type) = resp.headers().get("content-type") { From 17d9f4110bdf3d04e5bec8926d4448855e0f49d6 Mon Sep 17 00:00:00 2001 From: LogicalKarma <> Date: Sat, 30 May 2026 16:41:24 +0300 Subject: [PATCH 2/2] style(range): shorten Content-Range fix comment --- src/main.rs | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/src/main.rs b/src/main.rs index 75b783c..5771ce1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -225,14 +225,9 @@ fn handle_range_response_correction( return None; } - // total_size must be the FULL file size, taken from the videoplayback URL's - // `clen` query param. The response's Content-Length is the size of the chunk - // returned for the current range (clen - start when start > 0), NOT the full - // file. Using Content-Length as total_size produced Content-Range headers - // where end < start with a too-small denominator -- Chromium's media stack - // rejects those as malformed, which presented as a seek-past-buffer hang on - // itag-18 progressive playback. Fall back to Content-Length only when clen - // is absent (defensive; videoplayback URLs always carry clen in practice). + // total_size is the full file size from the videoplayback URL's `clen`. The + // upstream Content-Length is only the current chunk; using it as the total + // produced malformed Content-Range that broke itag-18 seek in Chromium. let total_size = match clen { Some(c) if c > 0 => c, _ => resp @@ -493,9 +488,7 @@ async fn index(req: HttpRequest) -> Result> { } // Fix range request handling - convert 200 to 206 if we have a range request - // and ensure Content-Range header is present. `clen` from the videoplayback - // URL is the canonical total file size; passed in to avoid mis-deriving it - // from the upstream chunk's Content-Length. + // and ensure Content-Range header is present handle_range_response_correction(&mut response, range.as_ref(), clen, &resp); if rewrite {