vc=82 — subscription-feed enrichment via lightweight stream_metadata

enrich_feed_item now calls the new strawcore stream_metadata() path (Android
/player + videoDetails read only) instead of the full stream_info. The full
path ran the JS sig/nsig deobf, an extra WEB /player metadata round-trip, the
iOS client, and stream/manifest/caption extraction — then kept only view_count
+ duration_seconds. Those two come from the same videoDetails the lightweight
path reads (populate_microformat never touches them), so the values are
identical; the feed just stops paying for the discarded work — ~one heavy
round-trip dropped per enriched item per refresh.

FFI surface (enrichFeedItem -> EnrichedFeedMetadata) unchanged. Needs
strawcore 30f24d2 (pushed first; CI clones strawcore main).
This commit is contained in:
Sulkta 2026-06-21 06:56:06 -07:00
parent f51b4fbb77
commit a636977512
3 changed files with 45 additions and 10 deletions

View file

@ -7,6 +7,7 @@
use strawcore_core::youtube::linkhandler::stream::extract_video_id;
use strawcore_core::youtube::stream_extractor::stream_info as core_stream_info;
use strawcore_core::youtube::stream_extractor::stream_metadata as core_stream_metadata;
use crate::error::StrawcoreError;
use crate::search::SearchItem;
@ -69,6 +70,23 @@ pub async fn stream_info(input: String) -> Result<StreamInfo, StrawcoreError> {
Ok(map_stream_info(video_id, core))
}
/// Metadata-only fetch — returns `(view_count, duration_seconds)` from the
/// Android `videoDetails`, skipping the JS-deobf / streams / extra WEB
/// round-trip the full `stream_info` pays. Backs `enrich_feed_item`, which
/// only needs those two fields. Non-negative clamping mirrors
/// `map_stream_info` so the values are identical to the full path's.
pub async fn stream_metadata(input: String) -> Result<(i64, i64), StrawcoreError> {
log::info!("strawcore::stream_metadata input_len={}", input.len());
crate::runtime::ensure_initialized();
let video_id = resolve_video_id(&input)?;
let core = tokio::task::spawn_blocking(move || core_stream_metadata(&video_id))
.await
.map_err(|e| StrawcoreError::Extractor {
msg: format!("join: {e}"),
})??;
Ok((clamp_nonneg(core.view_count), core.duration_seconds.max(0)))
}
fn resolve_video_id(input: &str) -> Result<String, StrawcoreError> {
let trimmed = input.trim();
// Bare 11-char id?