music sorting, search cycling, pause cycling

This commit is contained in:
2026-02-14 16:38:03 -06:00
parent 9a704cbdae
commit 3ae77d34d8
4 changed files with 59 additions and 8 deletions

BIN
new.zip Normal file

Binary file not shown.

Binary file not shown.

View File

@@ -124,7 +124,7 @@ impl JellyfinClient {
if types.contains("Episode") {
sort_by = "ParentIndexNumber,IndexNumber,SortName";
} else if types.contains("Audio") {
sort_by = "IndexNumber,SortName";
sort_by = "SortName";
}
}

View File

@@ -76,6 +76,20 @@ impl SearchMode {
}
}
fn prev(self) -> Self {
use SearchMode::*;
match self {
All => Artists,
ShowsMovies => All,
Shows => ShowsMovies,
Movies => Shows,
Episodes => Movies,
Music => Episodes,
Albums => Music,
Artists => Albums,
}
}
fn label(self) -> &'static str {
use SearchMode::*;
match self {
@@ -385,6 +399,10 @@ fn handle_key(app: &mut App, code: KeyCode) {
search.mode = search.mode.next();
return;
}
KeyCode::Char('?') => {
search.mode = search.mode.prev();
return;
}
KeyCode::Char(c) => { search.input.push(c); return; }
_ => return,
}
@@ -457,6 +475,16 @@ fn handle_key(app: &mut App, code: KeyCode) {
}
return;
}
KeyCode::Char(' ') => {
if let Some(mpv) = &app.mpv {
if let Err(e) = mpv_cycle_pause(mpv) {
app.status = format!("mpv pause: {e}");
}
} else {
app.status = "mpv not running (press 'p' to play)".into();
}
return;
}
KeyCode::Char('p') => {
// p = play selected item (spawn or replace)
let cfg = match &app.config {
@@ -793,8 +821,26 @@ fn ui(frame: &mut Frame, app: &App) {
View::Categories => {
render_categories(frame, app, panes.left_main, panes.right_main);
}
View::Library { title, items, selected, start_index, total, .. } => {
render_library(frame, title, *start_index, *total, items, *selected, panes.left_main, panes.right_main);
View::Library { title, query, items, selected, start_index, total, .. } => {
let is_audio = query
.include_item_types
.as_deref()
.map(|t| t.split(',').any(|x| x.trim() == "Audio"))
.unwrap_or(false);
let show_audio_track_numbers = is_audio && query.parent_id.is_some();
render_library(
frame,
title,
*start_index,
*total,
items,
*selected,
panes.left_main,
panes.right_main,
show_audio_track_numbers,
);
}
View::Login => {
render_login(frame, app, panes.left_main, panes.right_main);
@@ -922,6 +968,7 @@ fn render_library(
selected: usize,
left: Rect,
right: Rect,
show_audio_track_numbers: bool,
) {
let viewport = left.height.saturating_sub(2) as usize;
@@ -934,10 +981,9 @@ fn render_library(
matches!(item_type, Some("Movie" | "Series" | "MusicAlbum"))
}
fn left_number_prefix(it: &core::jellyfin::JfItem) -> String {
fn left_number_prefix(it: &core::jellyfin::JfItem, show_audio_track_numbers: bool) -> String {
match it.item_type.as_deref() {
Some("Episode") => {
// season x episode (01x02)
match (it.parent_index_number, it.index_number) {
(Some(season), Some(ep)) => format!("{:02}x{:02} ", season, ep),
(_, Some(ep)) => format!("{:02} ", ep),
@@ -945,7 +991,9 @@ fn render_library(
}
}
Some("Audio") => {
// track number
if !show_audio_track_numbers {
return String::new();
}
match it.index_number {
Some(n) => format!("{:02}. ", n),
None => String::new(),
@@ -958,7 +1006,7 @@ fn render_library(
let list_items: Vec<ListItem> = items[start..end]
.iter()
.map(|it| {
let prefix = left_number_prefix(it);
let prefix = left_number_prefix(it, show_audio_track_numbers);
let year = if show_year_for(it.item_type.as_deref()) {
it.production_year.map(|y| format!(" ({y})")).unwrap_or_default()
@@ -1100,7 +1148,6 @@ fn spawn_mpv(cfg: &core::config::Config, first_item_id: &str) -> Result<MpvSessi
Err("mpv IPC socket did not appear".to_string())
}
fn mpv_load_item(cfg: &core::config::Config, mpv: &MpvSession, item_id: &str) -> Result<(), String> {
let base = cfg.base_url.trim_end_matches('/');
let url = format!("{}/Items/{}/Download", base, item_id);
@@ -1137,6 +1184,10 @@ fn mpv_toggle_fullscreen(mpv: &MpvSession) -> Result<(), String> {
// mpv_cmd(&mpv.socket_path, r#"{"command":["set_property","fullscreen","yes"]}"#)
}
fn mpv_cycle_pause(mpv: &MpvSession) -> Result<(), String> {
mpv_cmd(&mpv.socket_path, r#"{"command":["cycle","pause"]}"#)
}
struct Panes {
top: Rect,
left_main: Rect,