M4 — channel browse + context-menu drill-down
Sidecar ChannelVideos op via rustypipe channel_videos(). Returns the channel metadata block (id, name, subscribers, banner) alongside the items array — same VideoItem shape as search. Addon refactor: _add_video_items is now the shared listing builder. Both _search_directory and _channel_directory call it. Each video result gets a 'Go to <channel>' context-menu entry that Container.Update's to ?action=channel&id=<channel_id> — so from any search result, the user can drill into that channel's recent uploads without going back through search. Smoke verified on the Pi via Files.GetDirectory: LTT channel (UCXuqSBlHAE6Xw-yeJA0Tunw) returned 30 recent videos. Addon version 0.0.7.
This commit is contained in:
parent
1b18c67fff
commit
d463781aae
5 changed files with 143 additions and 38 deletions
|
|
@ -50,6 +50,12 @@ enum Request {
|
|||
#[serde(default = "default_search_limit")]
|
||||
limit: u32,
|
||||
},
|
||||
/// List a channel's recent videos. `id` is a YouTube channel ID (UC…).
|
||||
ChannelVideos {
|
||||
id: String,
|
||||
#[serde(default = "default_search_limit")]
|
||||
limit: u32,
|
||||
},
|
||||
}
|
||||
|
||||
fn default_search_limit() -> u32 {
|
||||
|
|
@ -163,6 +169,10 @@ async fn handle_line(line: &str) -> Response {
|
|||
Ok(v) => Response::ok(v),
|
||||
Err(e) => e.into(),
|
||||
},
|
||||
Request::ChannelVideos { id, limit } => match resolve::channel_videos(&id, limit).await {
|
||||
Ok(v) => Response::ok(v),
|
||||
Err(e) => e.into(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -39,6 +39,43 @@ pub(crate) async fn search(query: &str, limit: u32) -> Result<Value, HandlerErro
|
|||
}))
|
||||
}
|
||||
|
||||
/// List a channel's recent videos. Returns the same VideoItem shape as
|
||||
/// `search`, plus channel metadata (name, subscribers, description, banner).
|
||||
pub(crate) async fn channel_videos(channel_id: &str, limit: u32) -> Result<Value, HandlerError> {
|
||||
use rustypipe::client::RustyPipe;
|
||||
|
||||
let rp = RustyPipe::new();
|
||||
let ch = rp
|
||||
.query()
|
||||
.channel_videos(channel_id)
|
||||
.await
|
||||
.map_err(|e| classify_rustypipe_error(&e))?;
|
||||
|
||||
let items_json: Vec<Value> = ch
|
||||
.content
|
||||
.items
|
||||
.iter()
|
||||
.take(limit as usize)
|
||||
.filter_map(|v| serde_json::to_value(v).ok())
|
||||
.collect();
|
||||
|
||||
tracing::info!(channel_id, count = items_json.len(), "channel_videos ok");
|
||||
|
||||
Ok(serde_json::json!({
|
||||
"source": "rustypipe",
|
||||
"channel": {
|
||||
"id": ch.id,
|
||||
"name": ch.name,
|
||||
"description": ch.description,
|
||||
"subscribers": ch.subscriber_count,
|
||||
"video_count": ch.video_count,
|
||||
"avatar": ch.avatar,
|
||||
"banner": ch.banner,
|
||||
},
|
||||
"items": items_json,
|
||||
}))
|
||||
}
|
||||
|
||||
/// DASH-ready resolve: returns rustypipe's full `video_only_streams` +
|
||||
/// `audio_streams` arrays + `details`. The Python addon builds an MPD
|
||||
/// from these and hands it to inputstream.adaptive — unlocks 1080p+ via
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue