Cleanup: drop playlist + suggestion + dead client constants + suppress_unused stubs
Round-2 cruft audit punch list — mechanical deletes, no behavior change.
Whole modules deleted (no wrapper consumer):
* youtube/playlist_extractor.rs (297 LOC) — full playlist extraction
* youtube/linkhandler/playlist.rs (81 LOC) — playlist URL parser
* youtube/suggestion_extractor.rs (91 LOC) — search-as-you-type
* tests/stream_phase4_offline.rs (186 LOC) — tautological test
Dead pub fns + enum variants + constants:
* WEB_REMIX_* constants (3) + WEB_MUSIC_ANALYTICS_* constants (3)
* InnertubeClientRequestInfo::of_web_music_analytics_charts_client
factory + its charts_client_omits_platform_and_screen test
* SearchFilter::Music{Songs,Videos,Albums,Playlists,Artists} variants
(5 of 9 cases) + uses_music_endpoint helper + the search_extractor
'music search not implemented' reject branch
* Two #[allow(dead_code)] _suppress_unused stub fns and the imports
they were keeping alive (std::sync::Arc in js/extractor.rs,
NetworkError in stream_extractor.rs)
Renamed:
* search_extractor::test_helpers -> renderer_helpers. Mis-named:
it's production code called from channel.rs, not a test fixture.
potoken/ kept and documented as the designed Phase-5 extension point
for YouTube bot-detection — wrapper's Android side hasn't registered
a real provider yet, but the trait + global slot stay so when YT
forces po_token universally the integration is one Kotlin patch away,
not a Rust-side rewrite.
~580 LOC removed from production. Wrapper does not need to change.
This commit is contained in:
parent
0c75d64707
commit
12b8865929
13 changed files with 27 additions and 760 deletions
|
|
@ -1,186 +0,0 @@
|
|||
// Phase 4 offline tests for the stream-extraction parsing layer.
|
||||
//
|
||||
// Live YT extraction is gated behind the `online-tests` feature; these
|
||||
// tests exercise the JSON-walking and URL post-processing using a
|
||||
// hand-crafted player-response shaped like what YT actually returns
|
||||
// (videoDetails + streamingData.formats[] + streamingData.adaptiveFormats[]
|
||||
// + dashManifestUrl + captions). No network.
|
||||
|
||||
use serde_json::json;
|
||||
use strawcore::stream::DeliveryMethod;
|
||||
use strawcore::youtube::itag::MediaFormat;
|
||||
use strawcore::youtube::stream_extractor;
|
||||
|
||||
fn synthetic_android_response(video_id: &str) -> serde_json::Value {
|
||||
json!({
|
||||
"playabilityStatus": { "status": "OK" },
|
||||
"videoDetails": {
|
||||
"videoId": video_id,
|
||||
"title": "NCS Spektrem — Shine",
|
||||
"shortDescription": "Royalty-free music for streamers.",
|
||||
"lengthSeconds": "240",
|
||||
"viewCount": "42000000",
|
||||
"author": "NoCopyrightSounds",
|
||||
"channelId": "UC_aEa8K-EOJ3D6gOs7HcyNg",
|
||||
"isLive": false,
|
||||
"thumbnail": {
|
||||
"thumbnails": [
|
||||
{"url": "https://i.ytimg.com/vi/x/default.jpg", "width": 120, "height": 90},
|
||||
{"url": "https://i.ytimg.com/vi/x/maxresdefault.jpg", "width": 1920, "height": 1080}
|
||||
]
|
||||
}
|
||||
},
|
||||
"captions": {
|
||||
"playerCaptionsTracklistRenderer": {
|
||||
"captionTracks": [
|
||||
{
|
||||
"baseUrl": "https://www.youtube.com/api/timedtext?lang=en&v=x",
|
||||
"languageCode": "en",
|
||||
"name": {"simpleText": "English"},
|
||||
"kind": "asr"
|
||||
},
|
||||
{
|
||||
"baseUrl": "https://www.youtube.com/api/timedtext?lang=de&v=x",
|
||||
"languageCode": "de",
|
||||
"name": {"simpleText": "Deutsch"}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"streamingData": {
|
||||
"dashManifestUrl": "https://manifest.googlevideo.com/api/manifest/dash/foo/yes",
|
||||
"formats": [
|
||||
{
|
||||
"itag": 22,
|
||||
"url": "https://r1.googlevideo.com/videoplayback?expire=1&itag=22&c=ANDROID&n=ENCODEDNTOKEN",
|
||||
"mimeType": "video/mp4; codecs=\"avc1.64001F, mp4a.40.2\"",
|
||||
"bitrate": 1234567,
|
||||
"width": 1280,
|
||||
"height": 720,
|
||||
"fps": 30,
|
||||
"contentLength": "12345678"
|
||||
}
|
||||
],
|
||||
"adaptiveFormats": [
|
||||
{
|
||||
"itag": 140,
|
||||
"url": "https://r1.googlevideo.com/videoplayback?expire=1&itag=140&c=ANDROID&n=AUDIONTOKEN",
|
||||
"mimeType": "audio/mp4; codecs=\"mp4a.40.2\"",
|
||||
"averageBitrate": 128000,
|
||||
"contentLength": "4321000",
|
||||
"audioTrack": {
|
||||
"id": "en.4",
|
||||
"displayName": "English original",
|
||||
"audioIsDefault": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"itag": 251,
|
||||
"url": "https://r2.googlevideo.com/videoplayback?expire=1&itag=251&c=ANDROID&n=OPUSNTOKEN",
|
||||
"mimeType": "audio/webm; codecs=\"opus\"",
|
||||
"averageBitrate": 160000,
|
||||
"contentLength": "5555555"
|
||||
},
|
||||
{
|
||||
"itag": 137,
|
||||
"url": "https://r3.googlevideo.com/videoplayback?expire=1&itag=137&c=ANDROID&n=VIDEONTOKEN",
|
||||
"mimeType": "video/mp4; codecs=\"avc1.640028\"",
|
||||
"bitrate": 2500000,
|
||||
"width": 1920,
|
||||
"height": 1080,
|
||||
"fps": 30,
|
||||
"contentLength": "98765432"
|
||||
},
|
||||
{
|
||||
"itag": 999999,
|
||||
"url": "https://x/?itag=999999",
|
||||
"mimeType": "video/webm"
|
||||
}
|
||||
]
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Reaching the parsing fns requires a NewPipe::downloader configured,
|
||||
// because the orchestrator's first step is the live Android POST. We
|
||||
// don't want to hit the network in these tests, so the public
|
||||
// stream_info entry point doesn't run here. Instead we test the
|
||||
// behaviour-significant parsing helpers directly via the public test
|
||||
// surface that exposes them. Since those are currently private, we cover
|
||||
// the parsing layer through observable outputs by stitching a minimal
|
||||
// "post-android-call" mock path.
|
||||
//
|
||||
// We get there by checking that the synthetic response JSON shape is
|
||||
// what the orchestrator would see, and we verify the orchestrator's
|
||||
// individual helpers against it via the public `stream_extractor` module
|
||||
// — for the helpers that need NewPipe-init the smoke is implicitly
|
||||
// covered by Phase 1 + Phase 2 tests already.
|
||||
//
|
||||
// Concretely below: lightweight JSON-shape assertions that mirror what
|
||||
// populate_video_details / populate_streams would extract. If we change
|
||||
// the JSON wire-shape contract this catches it.
|
||||
|
||||
#[test]
|
||||
fn synthetic_response_has_expected_video_details_shape() {
|
||||
let r = synthetic_android_response("n4tK7LYFxI0");
|
||||
assert_eq!(r["videoDetails"]["videoId"], "n4tK7LYFxI0");
|
||||
assert_eq!(r["videoDetails"]["title"], "NCS Spektrem — Shine");
|
||||
assert_eq!(r["videoDetails"]["lengthSeconds"], "240");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn synthetic_response_has_dash_manifest_url() {
|
||||
let r = synthetic_android_response("n4tK7LYFxI0");
|
||||
let url = r["streamingData"]["dashManifestUrl"].as_str().unwrap();
|
||||
assert!(url.starts_with("https://manifest.googlevideo.com"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn synthetic_response_has_progressive_and_adaptive_formats() {
|
||||
let r = synthetic_android_response("n4tK7LYFxI0");
|
||||
let progressive = r["streamingData"]["formats"].as_array().unwrap();
|
||||
assert_eq!(progressive.len(), 1);
|
||||
assert_eq!(progressive[0]["itag"], 22);
|
||||
|
||||
let adaptive = r["streamingData"]["adaptiveFormats"].as_array().unwrap();
|
||||
let itags: Vec<u64> = adaptive
|
||||
.iter()
|
||||
.map(|f| f["itag"].as_u64().unwrap())
|
||||
.collect();
|
||||
assert!(itags.contains(&140));
|
||||
assert!(itags.contains(&251));
|
||||
assert!(itags.contains(&137));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn options_default_disables_ios() {
|
||||
let opts = stream_extractor::ExtractOptions::default();
|
||||
assert!(!opts.fetch_ios_client);
|
||||
assert!(opts.android_streaming_pot.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn known_itags_lookup_ok() {
|
||||
use strawcore::youtube::itag::lookup;
|
||||
assert!(lookup(22).is_some()); // progressive 720p mp4
|
||||
assert!(lookup(140).is_some()); // m4a 128
|
||||
assert!(lookup(251).is_some()); // opus 160
|
||||
assert!(lookup(137).is_some()); // 1080p video-only mp4
|
||||
assert!(lookup(999999).is_none()); // unknown
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn known_itag_140_is_aac_128() {
|
||||
use strawcore::youtube::itag::{lookup, ItagType};
|
||||
let it = lookup(140).unwrap();
|
||||
assert_eq!(it.item_type, ItagType::Audio);
|
||||
assert_eq!(it.format, MediaFormat::M4A);
|
||||
assert_eq!(it.avg_bitrate_kbps, Some(128));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn delivery_method_progressive_vs_dash() {
|
||||
// Sanity that the enum is what the consumer expects to discriminate
|
||||
// (StraawApp's Media3 routing logic depends on this).
|
||||
assert_ne!(DeliveryMethod::Progressive, DeliveryMethod::Dash);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue