feat(qt): find an available version for a track Qobuz pulled

Reachable from a dead row's context menu, in a playlist the user owns. The
reference had this and shipped it with four defects; none are ported.

It was unranked -- Qobuz's raw relevance order shown to the human. The weighted
matcher that already lives in qbz-playlist-import does the ranking now, and its
ISRC short-circuit matters most: licensing churn usually republishes the SAME
recording under a new id with the SAME ISRC, and the pulled track keeps its
isrc (confirmed in the 2026-08-17 capture of album 0886443985094). That case
needs no human judgement, so the ISRC lookup runs first and pins its hit to the
top.

It removed before it added, so a failed add lost the track outright. The order
here is add, reposition, remove.

It did not preserve position -- the computed index went to a console.log and
the replacement landed at the end. It lands where the dead row was, through the
new updateTracksPosition verb. Every failure in that step is a warning and a
different success toast: the repair still stands at the end of the playlist,
exactly where the reference always put it. Position is a nicety, never a reason
to abort a repair.

Two guards. If the ISRC lookup answers with the dead track itself -- it can,
since a pulled track keeps its metadata and search still indexes it -- then
"replace" would be a no_duplicate no-op followed by a remove, and the user
would lose the row while being told it was repaired. And when the remove fails
the playlist permanently holds both rows: that is not rolled back, because a
rollback is a second write that can fail the same way and would delete the only
good copy. The list is refreshed so the user SEES both, and the toast says the
old one has to go by hand rather than claiming a reload will fix it.

cc
This commit is contained in:
vicrodh committed 2026-08-17 14:41:39 -06:00
1 parent 7780efa5db
commit b2c74c4140
7 files changed
+1310 -1

No files matched your search

+13
View File
@@ -701,6 +701,19 @@ Rectangle {
PlaylistImportModal {
anchors.fill: parent
}
// TrackReplacementModal — "Find available version" for a track Qobuz
// pulled from the catalogue (2026-08-17 unavailable-tracks contract §6),
// opened from the playlist row's context menu.
//
// Out here for the strongest form of its neighbours' reason: the apply
// RELOADS the playlist underneath it (`refresh_after_membership_change`),
// so a modal parented into the playlist view would be destroyed by its own
// success — while a write is still settling. Self-gates on
// QbzTrackReplace.replaceJson, so while closed it is an invisible,
// non-interactive Item.
TrackReplacementModal {
anchors.fill: parent
}
MyQbzModals {
anchors.fill: parent
}