perf(extract): add lightweight stream_metadata() for feed backfill
stream_metadata(video_id) is the cheap tail of stream_info_with: the same Android /player fetch + playability + decoy checks, then populate_video_details only — skipping the JS sig/nsig eval, the extra WEB /player metadata round-trip, the iOS client, and stream/manifest/caption extraction. view_count + duration_seconds come from the same videoDetails the full path uses (populate_microformat never touches either), so they are identical to what stream_info returns — without the ~500ms JS/stream tail or the redundant WEB round-trip. Backs the subscription-feed view-count/duration backfill, which discarded everything but those two fields.
This commit is contained in:
parent
5e0ec08928
commit
57efb7326a
1 changed files with 62 additions and 0 deletions
|
|
@ -198,6 +198,68 @@ pub fn stream_info_with(
|
|||
Ok(info)
|
||||
}
|
||||
|
||||
/// Metadata-only fetch — the Android `/player` `videoDetails` and nothing
|
||||
/// else. Returns a `StreamInfo` populated with title / uploader / view
|
||||
/// count / duration / thumbnails but EMPTY stream lists.
|
||||
///
|
||||
/// This is the cheap tail of `stream_info_with`: it shares the exact same
|
||||
/// Android fetch + playability + decoy checks, then runs only
|
||||
/// `populate_video_details`. It deliberately skips the heavy work the full
|
||||
/// path does after that: the JS signature/nsig eval (`signature_timestamp`
|
||||
/// plus the per-format throttling deobfuscation), the extra WEB `/player`
|
||||
/// metadata round-trip (`fetch_web_metadata`), the iOS client fetch, and
|
||||
/// the stream / manifest / caption extraction.
|
||||
///
|
||||
/// `view_count` and `duration_seconds` are read from the SAME
|
||||
/// `videoDetails` the full path uses, and `populate_microformat` (the only
|
||||
/// later step that could touch them) never writes either field — so for
|
||||
/// those two values this returns exactly what `stream_info` would, just
|
||||
/// without the ~500ms JS/stream tail and the redundant WEB round-trip.
|
||||
/// Built for the subscription-feed view-count/duration backfill.
|
||||
pub fn stream_metadata(video_id: &str) -> Result<StreamInfo, ExtractionError> {
|
||||
let localization = NewPipe::preferred_localization();
|
||||
let content_country = NewPipe::preferred_content_country();
|
||||
|
||||
// Same anonymous-or-provider token resolution as the full path so the
|
||||
// playability + decoy checks behave identically. With no PoTokenProvider
|
||||
// registered (the default) this resolves to None → anonymous reel.
|
||||
let provider = po_token_provider();
|
||||
let android_token: Option<PoTokenResult> = options_or_provider(None, None, None, || {
|
||||
provider
|
||||
.as_ref()
|
||||
.and_then(|p| p.get_android_client_po_token(video_id).ok().flatten())
|
||||
});
|
||||
|
||||
let android_cpn = generate_content_playback_nonce();
|
||||
let player_response = fetch_android(
|
||||
video_id,
|
||||
&localization,
|
||||
&content_country,
|
||||
&android_cpn,
|
||||
android_token
|
||||
.as_ref()
|
||||
.map(|t| t.player_request_po_token.as_str()),
|
||||
android_token.as_ref().map(|t| t.visitor_data.as_str()),
|
||||
)?;
|
||||
|
||||
check_playability_status(&player_response)?;
|
||||
if is_player_response_not_valid(&player_response, video_id) {
|
||||
return Err(ExtractionError::Other(
|
||||
"ANDROID player response is not valid (decoy detected)".into(),
|
||||
));
|
||||
}
|
||||
|
||||
let mut info = StreamInfo {
|
||||
service_id: 0,
|
||||
url: format!("https://www.youtube.com/watch?v={video_id}"),
|
||||
video_id: video_id.to_string(),
|
||||
stream_type: Some(StreamType::VideoStream),
|
||||
..StreamInfo::default()
|
||||
};
|
||||
populate_video_details(&mut info, &player_response);
|
||||
Ok(info)
|
||||
}
|
||||
|
||||
fn options_or_provider(
|
||||
opt_player_token: Option<&str>,
|
||||
opt_streaming_token: Option<&str>,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue