invidious/spec/streaming_fallback_spec.cr
naoNao89 8ed07a58d4 [Videos] Patch missing streamingData sections from fallback (formats + adaptive)
- YouTube increasingly serves SABR/modified DASH. When the primary player response lacks usable stream URLs, Invidious falls back to alternate clients. Historically only adaptiveFormats were patched in fallback. This change patches whichever sections are actually missing (adaptiveFormats and/or formats), but only when the fallback contains usable entries (url or signatureCipher/cipher), avoiding overwriting valid primary data.
- Adds a debug log (fallback_patched) to indicate which client patched which sections and a playback_404 triage log with stream counts to ease diagnosis.
- Fixes #5420
2025-08-18 22:58:05 +07:00

95 lines
3.0 KiB
Crystal

require "spectator"
# Bring in the helper under test
require "../src/invidious/videos/parser.cr"
Spectator.describe Invidious::Videos::ParserHelpers do
def json_any_hash(h : Hash(String, JSON::Any))
h
end
def json_any_array(a : Array(JSON::Any))
JSON::Any.new(a)
end
def json_any_str(s : String)
JSON::Any.new(s)
end
def json_any_obj(h : Hash(String, JSON::Any))
JSON::Any.new(h)
end
it "patches formats when primary missing and fallback has usable formats" do
primary_sd = {
"formats" => JSON::Any.new([] of JSON::Any),
"adaptiveFormats" => JSON::Any.new([] of JSON::Any),
} of String => JSON::Any
fallback_sd = {
"formats" => JSON::Any.new([
JSON::Any.new({"url" => json_any_str("https://example.com/video.mp4")}),
] of JSON::Any),
"adaptiveFormats" => JSON::Any.new([
JSON::Any.new({"url" => json_any_str("https://example.com/audio.m4a")}),
] of JSON::Any),
} of String => JSON::Any
res = Invidious::Videos::ParserHelpers.patch_streaming_data_if_missing!(primary_sd, fallback_sd)
expect(res[:patched_formats]).to be_true
expect(res[:patched_adaptive]).to be_true
# Ensure formats now have a non-empty URL
first_fmt = primary_sd["formats"].as_a[0].as_h
expect(first_fmt["url"].as_s).to_not be_empty
end
it "does not overwrite valid primary data" do
primary_sd = {
"formats" => JSON::Any.new([
JSON::Any.new({"url" => json_any_str("https://primary/video.mp4")}),
] of JSON::Any),
"adaptiveFormats" => JSON::Any.new([
JSON::Any.new({"url" => json_any_str("https://primary/audio.m4a")}),
] of JSON::Any),
} of String => JSON::Any
fallback_sd = {
"formats" => JSON::Any.new([
JSON::Any.new({"url" => json_any_str("https://fallback/video.mp4")}),
] of JSON::Any),
"adaptiveFormats" => JSON::Any.new([
JSON::Any.new({"url" => json_any_str("https://fallback/audio.m4a")}),
] of JSON::Any),
} of String => JSON::Any
res = Invidious::Videos::ParserHelpers.patch_streaming_data_if_missing!(primary_sd, fallback_sd)
expect(res[:patched_formats]).to be_false
expect(res[:patched_adaptive]).to be_false
# Primary values should remain
expect(primary_sd["formats"].as_a[0].as_h["url"].as_s).to eq("https://primary/video.mp4")
expect(primary_sd["adaptiveFormats"].as_a[0].as_h["url"].as_s).to eq("https://primary/audio.m4a")
end
it "handles fallback without formats gracefully" do
primary_sd = {
"formats" => JSON::Any.new([] of JSON::Any),
"adaptiveFormats" => JSON::Any.new([] of JSON::Any),
} of String => JSON::Any
fallback_sd = {
"adaptiveFormats" => JSON::Any.new([
JSON::Any.new({"url" => json_any_str("https://example.com/audio.m4a")}),
] of JSON::Any),
} of String => JSON::Any
res = Invidious::Videos::ParserHelpers.patch_streaming_data_if_missing!(primary_sd, fallback_sd)
expect(res[:patched_adaptive]).to be_true
expect(res[:patched_formats]).to be_false
end
end