feat(qt): release-aware album links, an unavailable-release page, Copy submenus and Buy on Qobuz
Unavailable tracks (8): playlist rows now say when the WHOLE release is gone (`releaseUnavailable`, one probe per distinct album of the pulled rows through the library's helper); TrackRow's album link and "Go to album" go dark on it instead of opening an empty page. Every row that opens an album passes the names it knows (`openAlbumFrom`), so when the catalog answers 404 the page names the release: AlbumView renders an "unavailable" arm — heading, "This release is no longer available on Qobuz." — with the best-ranked alternatives Qobuz still sells (the replacement flow's ranking, exposed as `ranked_alternatives`) in a rail; a network failure gets the same page with "Couldn't load this release." and no rail. Copy (9): the player bar's "+" and the track row menus (shared TrackRow and the artist's Popular Tracks rows) gain a Copy entry with a submenu — "Track name" or "Track - Album - Artist" — through the shared clipboard carrier; CardMenu marks `submenu: true` entries with a chevron and the host reopens the second menu on the same anchor. Row menus keep both lazy. Buy on Qobuz (10): the album header shows a store action when the catalog says the release is purchasable and it is not already owned; the track menus and the player bar carry the same entry for catalog rows and resolve it at click time — the store page opens in the browser, or a toast says the release is not sold. The link is `https://www.qobuz.com/album/-/{id}`, which the store answers with a 301 to the localised page (verified). cc
This commit is contained in:
11 files changed
+502
-18
No files matched your search
@@ -657,7 +657,7 @@ Rectangle {
|
||||
favorite: root.npFavorite
|
||||
ephemeral: root.npEphemeral
|
||||
tooltip: root.tooltip
|
||||
onAddRequested: function (anchorItem) { addMenu.openBelowRight(anchorItem) }
|
||||
onAddRequested: function (anchorItem) { root.addAnchor = anchorItem; addMenu.openBelowRight(anchorItem) }
|
||||
onTrackInfoRequested: root.openTrackInfo()
|
||||
}
|
||||
}
|
||||
@@ -680,7 +680,7 @@ Rectangle {
|
||||
playCircle: true
|
||||
favorite: root.npFavorite
|
||||
tooltip: root.tooltip
|
||||
onAddRequested: function (anchorItem) { addMenu.openBelowRight(anchorItem) }
|
||||
onAddRequested: function (anchorItem) { root.addAnchor = anchorItem; addMenu.openBelowRight(anchorItem) }
|
||||
onTrackInfoRequested: root.openTrackInfo()
|
||||
}
|
||||
}
|
||||
@@ -908,6 +908,26 @@ Rectangle {
|
||||
// "Add to…" flyout behind the transport "+" (TransportControls.slint's
|
||||
// add-menu), on the shared CardMenu surface. Same seven entries, same
|
||||
// order, same icons.
|
||||
// The "+" button that opened the add-menu: the Copy submenu reopens on it.
|
||||
property Item addAnchor: null
|
||||
QbzClipboard { id: npClipboard }
|
||||
CardMenu {
|
||||
id: npCopyMenu
|
||||
menuWidth: 232
|
||||
entries: [
|
||||
{ "label": QbzSession.tr("Track name", QbzSession.trRev), "icon": "copy", "action": "copy-title" },
|
||||
{ "label": QbzSession.tr("Track - Album - Artist", QbzSession.trRev), "icon": "clipboard", "action": "copy-full" }
|
||||
]
|
||||
onPicked: function (a) {
|
||||
var parts = [QbzPlayer.npTitle || ""]
|
||||
if (a === "copy-full") {
|
||||
if ((QbzPlayer.npAlbum || "") !== "") parts.push(QbzPlayer.npAlbum)
|
||||
if ((QbzPlayer.npArtist || "") !== "") parts.push(QbzPlayer.npArtist)
|
||||
}
|
||||
npClipboard.copy(parts.join(" - "))
|
||||
}
|
||||
}
|
||||
|
||||
CardMenu {
|
||||
id: addMenu
|
||||
menuWidth: 232
|
||||
@@ -963,6 +983,23 @@ Rectangle {
|
||||
"action": "album-favorite"
|
||||
})
|
||||
}
|
||||
// "Buy on Qobuz" (2026-09-13): catalog tracks only; store_qt
|
||||
// resolves whether the release is sold and toasts when not.
|
||||
if (root.npSource === "qobuz") {
|
||||
m.push({
|
||||
"label": QbzSession.tr("Buy on Qobuz", QbzSession.trRev),
|
||||
"icon": "shopping-bag",
|
||||
"action": "buy"
|
||||
})
|
||||
}
|
||||
// Copy (2026-09-13): a submenu — the track name, or
|
||||
// "Track - Album - Artist".
|
||||
m.push({
|
||||
"label": QbzSession.tr("Copy", QbzSession.trRev),
|
||||
"icon": "copy",
|
||||
"action": "copy",
|
||||
"submenu": true
|
||||
})
|
||||
return m.concat(root.abEntries())
|
||||
}
|
||||
onPicked: function (a) {
|
||||
@@ -978,6 +1015,10 @@ Rectangle {
|
||||
} else if (a === "album-favorite") {
|
||||
if (QbzPlayer.npAlbumId !== "")
|
||||
QbzLibrary.libraryToggleFavorite("album", QbzPlayer.npAlbumId)
|
||||
} else if (a === "buy") {
|
||||
if (id !== "") QbzAlbum.buyTrack(id)
|
||||
} else if (a === "copy") {
|
||||
if (root.addAnchor) npCopyMenu.openBelowRight(root.addAnchor)
|
||||
} else if (a === "ab-mark") {
|
||||
QbzPlayer.abMark()
|
||||
} else if (a === "ab-clear") {
|
||||
|
||||
Reference in new issue
Block a user