// Phase 7 — `channel_info(channel_url)` via the new strawcore. // Used by ChannelScreen (single-channel view) AND // SubscriptionFeedViewModel (which fans out across all subscriptions). use strawcore_core::youtube::channel::{channel_info as core_channel_info, ChannelInfo as CoreInfo}; use strawcore_core::youtube::linkhandler::channel as core_link; use crate::error::StrawcoreError; use crate::search::{from_core as search_from_core, SearchItem}; #[derive(Debug, Clone, uniffi::Record)] pub struct ChannelInfo { pub id: String, pub name: String, pub avatar: Option, pub banner: Option, /// -1 = unknown / hidden by the channel. pub subscriber_count: i64, pub description: String, /// Latest videos from the channel (Videos tab, newest first). pub videos: Vec, } #[uniffi::export(async_runtime = "tokio")] pub async fn channel_info(input: String) -> Result { log::info!("strawcore::channel_info input_len={}", input.len()); crate::runtime::ensure_initialized(); let identifier = resolve_channel_identifier(&input)?; let core = tokio::task::spawn_blocking(move || core_channel_info(identifier)) .await .map_err(|e| StrawcoreError::Extractor { msg: format!("join: {e}"), })??; Ok(map_channel(core)) } fn resolve_channel_identifier( input: &str, ) -> Result { let trimmed = input.trim(); // Bare channel ID — UC..., 24 chars. if trimmed.starts_with("UC") && trimmed.len() == 24 { return Ok(core_link::ChannelIdentifier::DirectId(trimmed.into())); } core_link::parse(trimmed).map_err(|e| StrawcoreError::Unsupported { detail: e.to_string(), }) } fn map_channel(c: CoreInfo) -> ChannelInfo { let avatar = c.avatars.last().map(|i| i.url().to_string()); let banner = c.banners.last().map(|i| i.url().to_string()); let videos = c.recent_videos.into_iter().map(search_from_core).collect(); ChannelInfo { id: c.channel_id, name: c.name, avatar, banner, subscriber_count: c.subscriber_count, description: c.description, videos, } }