Playlist Integration!!!
This commit is contained in:
@@ -42,6 +42,14 @@ pub struct ViewsResponse {
|
|||||||
pub items: Vec<JfItem>,
|
pub items: Vec<JfItem>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
struct PlaylistItemsResponse {
|
||||||
|
#[serde(rename = "Items", default)]
|
||||||
|
items: Vec<JfItem>,
|
||||||
|
#[serde(rename = "TotalRecordCount")]
|
||||||
|
total_record_count: Option<usize>,
|
||||||
|
}
|
||||||
|
|
||||||
impl JellyfinClient {
|
impl JellyfinClient {
|
||||||
pub fn from_config(cfg: &Config) -> Result<Self, String> {
|
pub fn from_config(cfg: &Config) -> Result<Self, String> {
|
||||||
let token = cfg
|
let token = cfg
|
||||||
@@ -200,6 +208,44 @@ impl JellyfinClient {
|
|||||||
total: parsed.total_record_count.unwrap_or(0),
|
total: parsed.total_record_count.unwrap_or(0),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn playlist_items(
|
||||||
|
&self,
|
||||||
|
user_id: &str,
|
||||||
|
playlist_id: &str,
|
||||||
|
start_index: usize,
|
||||||
|
limit: usize,
|
||||||
|
) -> Result<PagedItems, String> {
|
||||||
|
let url = format!("{}/Playlists/{}/Items", self.base_url, playlist_id);
|
||||||
|
|
||||||
|
let resp = self
|
||||||
|
.client
|
||||||
|
.get(url)
|
||||||
|
.header("X-Emby-Authorization", self.auth_header_value())
|
||||||
|
.query(&[
|
||||||
|
("UserId", user_id),
|
||||||
|
("StartIndex", &start_index.to_string()),
|
||||||
|
("Limit", &limit.to_string()),
|
||||||
|
("Fields", "PrimaryImageAspectRatio,SortName,ProductionYear,IndexNumber,ParentIndexNumber"),
|
||||||
|
])
|
||||||
|
.send()
|
||||||
|
.map_err(|e| format!("request failed: {e}"))?;
|
||||||
|
|
||||||
|
if !resp.status().is_success() {
|
||||||
|
let status = resp.status();
|
||||||
|
let text = resp.text().unwrap_or_default();
|
||||||
|
return Err(format!("playlist_items failed: {status} {text}"));
|
||||||
|
}
|
||||||
|
|
||||||
|
let parsed: PlaylistItemsResponse = resp
|
||||||
|
.json()
|
||||||
|
.map_err(|e| format!("bad response json: {e}"))?;
|
||||||
|
|
||||||
|
Ok(PagedItems {
|
||||||
|
items: parsed.items,
|
||||||
|
total: parsed.total_record_count.unwrap_or(0),
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// What kind of library list we want.
|
/// What kind of library list we want.
|
||||||
|
|||||||
36
src/main.rs
36
src/main.rs
@@ -571,6 +571,7 @@ fn handle_key(app: &mut App, code: KeyCode) {
|
|||||||
"Movies" => core::jellyfin::LibraryQuery::movies(),
|
"Movies" => core::jellyfin::LibraryQuery::movies(),
|
||||||
"Shows" => core::jellyfin::LibraryQuery::shows(),
|
"Shows" => core::jellyfin::LibraryQuery::shows(),
|
||||||
"Music" => core::jellyfin::LibraryQuery::music(),
|
"Music" => core::jellyfin::LibraryQuery::music(),
|
||||||
|
"Playlists" => core::jellyfin::LibraryQuery::all().with_item_types("Playlist"),
|
||||||
_ => { app.status = format!("Not wired yet: {}", cat); return; }
|
_ => { app.status = format!("Not wired yet: {}", cat); return; }
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -606,7 +607,6 @@ fn handle_key(app: &mut App, code: KeyCode) {
|
|||||||
open_children(app, format!("{} > {}", title, it.name), q);
|
open_children(app, format!("{} > {}", title, it.name), q);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Shows -> Seasons -> Episodes
|
// Shows -> Seasons -> Episodes
|
||||||
if ty == "Series" {
|
if ty == "Series" {
|
||||||
let q = core::jellyfin::LibraryQuery::all()
|
let q = core::jellyfin::LibraryQuery::all()
|
||||||
@@ -622,7 +622,6 @@ fn handle_key(app: &mut App, code: KeyCode) {
|
|||||||
open_children(app, format!("{} > {}", title, it.name), q);
|
open_children(app, format!("{} > {}", title, it.name), q);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// MusicArtist -> MusicAlbum -> Audio
|
// MusicArtist -> MusicAlbum -> Audio
|
||||||
if ty == "MusicArtist" {
|
if ty == "MusicArtist" {
|
||||||
let q = core::jellyfin::LibraryQuery::all()
|
let q = core::jellyfin::LibraryQuery::all()
|
||||||
@@ -643,6 +642,39 @@ fn handle_key(app: &mut App, code: KeyCode) {
|
|||||||
open_children(app, format!("{} > {}", title, it.name), q);
|
open_children(app, format!("{} > {}", title, it.name), q);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if ty == "UserView" {
|
||||||
|
// Most UserViews are libraries like Shows/Movies/Music/Playlists.
|
||||||
|
// For Playlists view, drill into playlists.
|
||||||
|
let q = core::jellyfin::LibraryQuery::all()
|
||||||
|
.with_parent(it.id.clone())
|
||||||
|
.with_item_types("Playlist");
|
||||||
|
open_children(app, format!("{} > {}", title, it.name), q);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ty == "Playlist" {
|
||||||
|
let cfg = match &app.config { Some(c) => c.clone(), None => { app.status="No config".into(); return; } };
|
||||||
|
let user_id = match cfg.user_id.as_deref() { Some(u) => u, None => { app.status="Missing user_id".into(); return; } };
|
||||||
|
let client = match core::jellyfin::JellyfinClient::from_config(&cfg) { Ok(c)=>c, Err(e)=>{ app.status=e; return; } };
|
||||||
|
|
||||||
|
match client.playlist_items(user_id, &it.id, 0, PAGE_SIZE) {
|
||||||
|
Ok(page) => {
|
||||||
|
app.view_stack.push(View::Library {
|
||||||
|
title: format!("{} > {}", title, it.name),
|
||||||
|
// store a query placeholder; playlist paging could be done later if you want
|
||||||
|
query: core::jellyfin::LibraryQuery::all(),
|
||||||
|
items: page.items,
|
||||||
|
selected: 0,
|
||||||
|
start_index: 0,
|
||||||
|
total: page.total,
|
||||||
|
page_size: PAGE_SIZE,
|
||||||
|
loading: false,
|
||||||
|
});
|
||||||
|
app.status = "Loaded".into();
|
||||||
|
}
|
||||||
|
Err(e) => app.status = format!("Playlist load failed: {e}"),
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
// Playables -> mpv
|
// Playables -> mpv
|
||||||
// Playables: do nothing on Enter (use 'p' to play)
|
// Playables: do nothing on Enter (use 'p' to play)
|
||||||
if ty == "Episode" || ty == "Audio" || ty == "Movie" {
|
if ty == "Episode" || ty == "Audio" || ty == "Movie" {
|
||||||
|
|||||||
Reference in New Issue
Block a user