Phase 7 — strawcore wrapper now bridges to Sulkta-OSS/strawcore-core
Replaces the rustypipe-backed extraction with calls into the new
NPE-port crate. The UniFFI surface Kotlin sees is unchanged:
suspend fun search(query: String): List<SearchItem>
suspend fun streamInfo(input: String): StreamInfo
suspend fun channelInfo(input: String): ChannelInfo
fun initLogging() // also wires the strawcore-core Downloader
fun helloFromRust(name: String): String
rust/strawcore/
* Cargo.toml — dropped rustypipe + rquickjs-sys direct dep;
added strawcore-core path dep (../../../strawcore)
* src/error.rs — From<strawcore_core::ExtractionError>, mapping
ContentUnavailable variants to typed
StrawcoreError cases (AgeRestricted, GeoRestricted,
Private, RequiresLogin) instead of bucketing all
to Extractor
* src/runtime.rs — Once-guarded ReqwestDownloader init via
NewPipe::init_full
* src/search.rs — search() spawn_blocks core search_extractor::search
against SearchFilter::Videos
* src/stream.rs — stream_info() resolves URL → video_id via
strawcore_core::linkhandler::stream, then
spawn_blocks core stream_extractor::stream_info,
then maps StreamInfo → wrapper DTOs (combined/
video_only/audio_only/dash/hls)
* src/channel.rs — channel_info() parses input via
strawcore_core::linkhandler::channel (handle /
custom-url / legacy-user resolution lives in
core), then spawn_blocks core channel::channel_info
Build verified: wrapper compiles linking strawcore-core, uniffi-bindgen
generates Kotlin bindings with the same suspend fun + data class
surface Kotlin already consumes. Android NDK cross-compile + APK + on-
device smoke pending (needs build-host container).
This commits onto rollback/vc18-back-to-NPE — the existing Kotlin code
still calls NewPipeExtractor directly. Switching the Kotlin side to
consume the rust wrapper is a separate cutover.
This commit is contained in:
parent
c1d7fffb1f
commit
fb89b22685
7 changed files with 250 additions and 309 deletions
|
|
@ -1,13 +1,13 @@
|
|||
// Phase U-2 — search via rustypipe, exposed to Kotlin as a suspend fun.
|
||||
//
|
||||
// `SearchItem` mirrors the existing Kotlin `StreamItem` field for field so
|
||||
// `SearchViewModel` can swap NewPipeExtractor for this with a one-line
|
||||
// change. We map only Video items from rustypipe's result (it also returns
|
||||
// channels and playlists which we don't need for the search list yet).
|
||||
// Phase 7 — search via Sulkta-OSS/strawcore-core. Exposed to Kotlin
|
||||
// as a suspend fun. SearchItem field shape is unchanged from Phase U-2
|
||||
// so Kotlin callers (SearchViewModel) keep working with no code
|
||||
// changes.
|
||||
|
||||
use strawcore_core::stream::StreamInfoItem;
|
||||
use strawcore_core::youtube::linkhandler::search::SearchFilter;
|
||||
use strawcore_core::youtube::search_extractor;
|
||||
|
||||
use crate::error::StrawcoreError;
|
||||
use rustypipe::client::RustyPipe;
|
||||
use rustypipe::model::YouTubeItem;
|
||||
|
||||
#[derive(Debug, Clone, uniffi::Record)]
|
||||
pub struct SearchItem {
|
||||
|
|
@ -22,44 +22,40 @@ pub struct SearchItem {
|
|||
pub view_count: i64,
|
||||
}
|
||||
|
||||
fn yt_video_url(id: &str) -> String {
|
||||
format!("https://www.youtube.com/watch?v={}", id)
|
||||
}
|
||||
|
||||
fn yt_channel_url(id: &str) -> String {
|
||||
format!("https://www.youtube.com/channel/{}", id)
|
||||
pub(crate) fn from_core(item: StreamInfoItem) -> SearchItem {
|
||||
let uploader_url = if item.uploader_url.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(item.uploader_url)
|
||||
};
|
||||
let thumbnail = item
|
||||
.thumbnails
|
||||
.last()
|
||||
.map(|i| i.url().to_string());
|
||||
SearchItem {
|
||||
url: item.url,
|
||||
title: item.name,
|
||||
uploader: item.uploader_name,
|
||||
uploader_url,
|
||||
thumbnail,
|
||||
duration_seconds: item.duration_seconds,
|
||||
view_count: if item.view_count < 0 {
|
||||
0
|
||||
} else {
|
||||
item.view_count
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
#[uniffi::export(async_runtime = "tokio")]
|
||||
pub async fn search(query: String) -> Result<Vec<SearchItem>, StrawcoreError> {
|
||||
log::info!("strawcore::search query={}", query);
|
||||
let rp = RustyPipe::new();
|
||||
let results = rp.query().search(query).await?;
|
||||
|
||||
let items: Vec<SearchItem> = results
|
||||
.items
|
||||
.items
|
||||
.into_iter()
|
||||
.filter_map(|item| match item {
|
||||
YouTubeItem::Video(v) => Some(SearchItem {
|
||||
url: yt_video_url(&v.id),
|
||||
title: v.name,
|
||||
uploader: v
|
||||
.channel
|
||||
.as_ref()
|
||||
.map(|c| c.name.clone())
|
||||
.unwrap_or_default(),
|
||||
uploader_url: v.channel.as_ref().map(|c| yt_channel_url(&c.id)),
|
||||
thumbnail: v.thumbnail.first().map(|t| t.url.clone()),
|
||||
duration_seconds: v.duration.unwrap_or(0) as i64,
|
||||
view_count: v.view_count.unwrap_or(0) as i64,
|
||||
}),
|
||||
// Channels + playlists are dropped at U-2; future phases can
|
||||
// surface them as a "channels you might like" row.
|
||||
_ => None,
|
||||
})
|
||||
.collect();
|
||||
|
||||
log::info!("strawcore::search returned {} videos", items.len());
|
||||
Ok(items)
|
||||
let result = tokio::task::spawn_blocking(move || {
|
||||
search_extractor::search(&query, SearchFilter::Videos)
|
||||
})
|
||||
.await
|
||||
.map_err(|e| StrawcoreError::Extractor {
|
||||
msg: format!("join: {e}"),
|
||||
})??;
|
||||
Ok(result.videos.into_iter().map(from_core).collect())
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue