From 5e6daf0e813aa0dbb77862c9a326d6f418f375a0 Mon Sep 17 00:00:00 2001 From: vicrodh <103399283+vicrodh@users.noreply.github.com> Date: Mon, 31 Aug 2026 11:26:17 -0600 Subject: [PATCH] feat(qt): Open containing album on cortinilla track rows MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CortRow gains an albumId — the Qobuz album id for catalog tracks, the album group key for local ones (both are what crate::open_album routes), empty elsewhere. The track menu gates its new entry on the field, the action funnels through cortinillaMenuAction like its siblings, and the new msgid ships in the seven non-English locales. cc --- .../translations/de/LC_MESSAGES/qbz-ui.po | 3 +++ .../translations/es/LC_MESSAGES/qbz-ui.po | 3 +++ .../translations/fr/LC_MESSAGES/qbz-ui.po | 3 +++ .../translations/ja/LC_MESSAGES/qbz-ui.po | 3 +++ .../translations/nl/LC_MESSAGES/qbz-ui.po | 3 +++ .../translations/pt/LC_MESSAGES/qbz-ui.po | 3 +++ .../translations/ru/LC_MESSAGES/qbz-ui.po | 3 +++ crates/qbz-qt/qml/shell/Cortinilla.qml | 25 +++++++++++------- crates/qbz-qt/src/search_local.rs | 5 ++++ crates/qbz-qt/src/search_qt.rs | 26 ++++++++++++++++++- 10 files changed, 67 insertions(+), 10 deletions(-) diff --git a/crates/qbz-i18n/translations/de/LC_MESSAGES/qbz-ui.po b/crates/qbz-i18n/translations/de/LC_MESSAGES/qbz-ui.po index 7e600a0c0..cc09b59a1 100644 --- a/crates/qbz-i18n/translations/de/LC_MESSAGES/qbz-ui.po +++ b/crates/qbz-i18n/translations/de/LC_MESSAGES/qbz-ui.po @@ -446,6 +446,9 @@ msgstr "Entdecken Sie neue Musik aus Ihren meistgespielten Playlists." msgid "Open album" msgstr "Album öffnen" +msgid "Open containing album" +msgstr "Zugehöriges Album öffnen" + msgid "Play" msgstr "Abspielen" diff --git a/crates/qbz-i18n/translations/es/LC_MESSAGES/qbz-ui.po b/crates/qbz-i18n/translations/es/LC_MESSAGES/qbz-ui.po index ba31c045d..996769008 100644 --- a/crates/qbz-i18n/translations/es/LC_MESSAGES/qbz-ui.po +++ b/crates/qbz-i18n/translations/es/LC_MESSAGES/qbz-ui.po @@ -449,6 +449,9 @@ msgstr "Descubre música nueva a partir de tus listas más reproducidas." msgid "Open album" msgstr "Abrir álbum" +msgid "Open containing album" +msgstr "Abrir el álbum contenedor" + msgid "Play" msgstr "Reproducir" diff --git a/crates/qbz-i18n/translations/fr/LC_MESSAGES/qbz-ui.po b/crates/qbz-i18n/translations/fr/LC_MESSAGES/qbz-ui.po index 9cea19a1e..19e55a0c3 100644 --- a/crates/qbz-i18n/translations/fr/LC_MESSAGES/qbz-ui.po +++ b/crates/qbz-i18n/translations/fr/LC_MESSAGES/qbz-ui.po @@ -446,6 +446,9 @@ msgstr "Découvrez de nouvelles musiques à partir de vos playlists les plus éc msgid "Open album" msgstr "Ouvrir l'album" +msgid "Open containing album" +msgstr "Ouvrir l'album d'origine" + msgid "Play" msgstr "Lecture" diff --git a/crates/qbz-i18n/translations/ja/LC_MESSAGES/qbz-ui.po b/crates/qbz-i18n/translations/ja/LC_MESSAGES/qbz-ui.po index d3075de03..f43963733 100644 --- a/crates/qbz-i18n/translations/ja/LC_MESSAGES/qbz-ui.po +++ b/crates/qbz-i18n/translations/ja/LC_MESSAGES/qbz-ui.po @@ -445,6 +445,9 @@ msgstr "よく再生するプレイリストから新しい音楽を見つけま msgid "Open album" msgstr "アルバムを開く" +msgid "Open containing album" +msgstr "収録アルバムを開く" + msgid "Play" msgstr "再生" diff --git a/crates/qbz-i18n/translations/nl/LC_MESSAGES/qbz-ui.po b/crates/qbz-i18n/translations/nl/LC_MESSAGES/qbz-ui.po index 719cd49de..cfd2425ca 100644 --- a/crates/qbz-i18n/translations/nl/LC_MESSAGES/qbz-ui.po +++ b/crates/qbz-i18n/translations/nl/LC_MESSAGES/qbz-ui.po @@ -446,6 +446,9 @@ msgstr "Ontdek nieuwe muziek op basis van je meest afgespeelde afspeellijsten." msgid "Open album" msgstr "Album openen" +msgid "Open containing album" +msgstr "Bijbehorend album openen" + msgid "Play" msgstr "Afspelen" diff --git a/crates/qbz-i18n/translations/pt/LC_MESSAGES/qbz-ui.po b/crates/qbz-i18n/translations/pt/LC_MESSAGES/qbz-ui.po index 537dcef1f..add2e6b34 100644 --- a/crates/qbz-i18n/translations/pt/LC_MESSAGES/qbz-ui.po +++ b/crates/qbz-i18n/translations/pt/LC_MESSAGES/qbz-ui.po @@ -446,6 +446,9 @@ msgstr "Descubra novas músicas a partir das suas playlists mais reproduzidas." msgid "Open album" msgstr "Abrir álbum" +msgid "Open containing album" +msgstr "Abrir o álbum de origem" + msgid "Play" msgstr "Reproduzir" diff --git a/crates/qbz-i18n/translations/ru/LC_MESSAGES/qbz-ui.po b/crates/qbz-i18n/translations/ru/LC_MESSAGES/qbz-ui.po index 46f19e73e..e5e1e2ee1 100644 --- a/crates/qbz-i18n/translations/ru/LC_MESSAGES/qbz-ui.po +++ b/crates/qbz-i18n/translations/ru/LC_MESSAGES/qbz-ui.po @@ -445,6 +445,9 @@ msgstr "Откройте новую музыку из ваших любимых msgid "Open album" msgstr "Открыть альбом" +msgid "Open containing album" +msgstr "Открыть альбом трека" + msgid "Play" msgstr "Воспроизвести" diff --git a/crates/qbz-qt/qml/shell/Cortinilla.qml b/crates/qbz-qt/qml/shell/Cortinilla.qml index 17fc0b512..ac6ee2f7f 100644 --- a/crates/qbz-qt/qml/shell/Cortinilla.qml +++ b/crates/qbz-qt/qml/shell/Cortinilla.qml @@ -102,13 +102,20 @@ Item { artistRows.push({ "label": t("Play", r), "icon": "play-fill", "action": "play" }) return artistRows } - if (row.kind === "track") return [ - { "label": t("Play", r), "icon": "play-fill", "action": "play" }, - { "label": t("Play next", r), "icon": "list-start", "action": "next" }, - { "label": t("Play later", r), "icon": "list-plus", "action": "later" }, - { "label": t("Add to queue", r), "icon": "list-end", "action": "queue" }, - { "label": t("Add to playlist", r), "icon": "list-music", "action": "add-to-playlist" } - ] + if (row.kind === "track") { + var trackRows = [ + { "label": t("Play", r), "icon": "play-fill", "action": "play" }, + { "label": t("Play next", r), "icon": "list-start", "action": "next" }, + { "label": t("Play later", r), "icon": "list-plus", "action": "later" }, + { "label": t("Add to queue", r), "icon": "list-end", "action": "queue" }, + { "label": t("Add to playlist", r), "icon": "list-music", "action": "add-to-playlist" } + ] + // QoL round: gated on the payload actually carrying the album + // (a Qobuz id or a local group key — search_qt::CortRow.albumId). + if ((row.albumId || "") !== "") + trackRows.push({ "label": t("Open containing album", r), "icon": "disc-3", "action": "open-album" }) + return trackRows + } if (row.kind === "playlist") return [ { "label": t("Open playlist", r), "icon": "list-music", "action": "open" }, { "label": t("Play", r), "icon": "play-fill", "action": "play" }, @@ -124,8 +131,8 @@ Item { if (fi === undefined || fi === null) return QbzSearch.cortinillaMenuAction(fi, action) - if ((action === "open" || action === "play" || action === "add-to-playlist") - && root.headerBar) + if ((action === "open" || action === "open-album" || action === "play" + || action === "add-to-playlist") && root.headerBar) root.headerBar.clearSearch() } diff --git a/crates/qbz-qt/src/search_local.rs b/crates/qbz-qt/src/search_local.rs index 56a2fa97b..cc281fafa 100644 --- a/crates/qbz-qt/src/search_local.rs +++ b/crates/qbz-qt/src/search_local.rs @@ -206,6 +206,7 @@ pub(crate) fn derive_local_album_rows( quality_detail: local_quality_detail(t), art_url, art_path, + album_id: String::new(), flat_index: 0, }); } @@ -259,6 +260,7 @@ pub(crate) fn derive_local_artist_rows( quality_detail: String::new(), art_url, art_path, + album_id: String::new(), flat_index: 0, }); } @@ -291,6 +293,9 @@ pub(crate) fn map_local_track_to_cort_row(t: &qbz_library::LocalTrack) -> CortRo quality_detail: local_quality_detail(t), art_url, art_path, + // The same group key the local Albums section uses as its row id — + // what "Open containing album" hands to `crate::open_album`. + album_id: t.album_group_key.clone(), flat_index: 0, } } diff --git a/crates/qbz-qt/src/search_qt.rs b/crates/qbz-qt/src/search_qt.rs index df802e479..eb25af981 100644 --- a/crates/qbz-qt/src/search_qt.rs +++ b/crates/qbz-qt/src/search_qt.rs @@ -566,6 +566,12 @@ pub struct CortRow { pub art_url: String, #[serde(rename = "artPath")] pub art_path: String, + /// TRACK rows only ("Open containing album"): the containing album — a + /// Qobuz album id or a local album group key (both are what + /// `crate::open_album` routes). Empty when unknown; the QML menu gates + /// its entry on that. + #[serde(rename = "albumId")] + pub album_id: String, #[serde(rename = "flatIndex")] pub flat_index: i32, } @@ -673,6 +679,7 @@ fn map_search_all_to_cortinilla(query: &str, results: &SearchAllResults) -> Cort subtitle: m.artist, quality_detail, art_url: m.art_url, + album_id: m.album_id, ..Default::default() } }; @@ -1270,7 +1277,7 @@ pub fn row_menu_action(flat_index: i32, action: &str) { } if !matches!( action, - "play" | "next" | "later" | "queue" | "add-to-playlist" + "play" | "next" | "later" | "queue" | "add-to-playlist" | "open-album" ) { log::warn!("[qbz-qt] cortinilla menu: unknown action {action:?}"); return; @@ -1298,6 +1305,23 @@ pub fn row_menu_action(flat_index: i32, action: &str) { row.source, row.id, ); + if action == "open-album" { + set_cortinilla_open(false); + // `open_album` routes BOTH id spaces (its is_local_feed_id arm + // recognises local group keys), so no source split here. The QML + // entry is gated on a non-empty albumId; an empty one is a stale + // snapshot, not a user path. + if row.kind != "track" || row.album_id.is_empty() { + log::warn!( + "[qbz-qt] cortinilla menu open-album: kind={} albumId empty={}", + row.kind, + row.album_id.is_empty() + ); + return; + } + crate::open_album(row.album_id); + return; + } if action == "add-to-playlist" { set_cortinilla_open(false); if row.kind != "track" {