fix(qt): four AUDIBLE parity rows — stream errors, seek clamp, label shuffle, Plex keys, Library queue
PARITY-DEBT #7, #15, #17, #4 and #5. - **#7** the stream error was drained and only logged, so an ALSA / exclusive failure — guaranteed inside Flatpak and Snap — froze the bar at 0:00 with no explanation. It now toasts, with the sandbox-aware rewrite the reference carries (playback.rs:5093-5115). This is the support issue that fix was written for. - **#15** seek was clamped to the full duration instead of the downloaded fraction, so the head could be dragged into bytes that do not exist. The poll loop publishes `npSeekableMax` and both bars clamp to it, matching SeekBar.slint:93-98. The cache overlay was already drawn and purely decorative. - **#17** a label page's Shuffle called `set_shuffle(true)`, turning the GLOBAL shuffle mode on and leaving it on for everything played afterwards. The reference shuffles the vector in place and never touches the mode. - **#4** a `plex:<hash>` album-boundary key was passed where a Plex rating key belongs, so the fetch 404'd and the PREVIOUS track kept playing under the new card. Guarded at both sites, 1:1 with playback.rs:668-680 (`b5c1a76e`). - **#5** clicking a track in Library played exactly one track and stopped. It now queues the whole VISIBLE list from the clicked row (`order_by_visible`). The Library row play needed one edit outside its author's lane, which the singleton xref caught before the build did: `QbzLibrary.libraryPlayVisible` was called from QML and implemented in `library_qt`, but never declared on the bridge. That is precisely the failure the xref exists for — QML resolves singleton members lazily, so a missing invokable compiles clean and throws when the user clicks. The QML had shipped a `typeof === "function"` feature-detect with a fallback, so it degraded instead of throwing; the invokable now exists and the fallback is dead code by design. cc
This commit is contained in:
11 files changed
+759
-60
No files matched your search
@@ -155,6 +155,29 @@ Rectangle {
|
||||
return m + ":" + (s < 10 ? "0" : "") + s
|
||||
}
|
||||
|
||||
// --- Seek clamp (closes PARITY-DEBT #15, QML half) ---------------------
|
||||
// SeekBar.slint:24-26 / 93-98: while a track is still downloading the seek
|
||||
// target is LOCKED to the furthest fraction that has arrived, and the
|
||||
// cursor turns not-allowed over the region that has not. The source is
|
||||
// NowPlayingState.seekable-max (state.slint:4402), fed by
|
||||
// playback.rs:5304 `buffer_progress.clamp(0,1)` and published here as
|
||||
// QbzPlayer.npSeekableMax.
|
||||
//
|
||||
// A fully-available track (local, cached, or a finished download) reports
|
||||
// 1.0, so Math.min() is a no-op and seeking stays completely free — the
|
||||
// clamp can never fight a local track.
|
||||
function clamp01(v) {
|
||||
return Math.min(Math.max(v, 0), 1)
|
||||
}
|
||||
// SeekBar.slint:98 — Math.min(clamp01(mouse-x / width), seekable-max).
|
||||
function seekTarget(fraction) {
|
||||
return Math.min(root.clamp01(fraction), QbzPlayer.npSeekableMax)
|
||||
}
|
||||
// SeekBar.slint:93 — clamp01(mouse-x / width) > seekable-max.
|
||||
function beyondSeekable(fraction) {
|
||||
return root.clamp01(fraction) > QbzPlayer.npSeekableMax
|
||||
}
|
||||
|
||||
// --- Track Info (album/TrackInfoModal.slint) ---------------------------
|
||||
// The (i) button and the song-card title open the MODAL (scrim + centered
|
||||
// card), which is what the .slint does: the bars fire
|
||||
@@ -263,9 +286,21 @@ Rectangle {
|
||||
anchors.right: seekTrack.right
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
cursorShape: QbzPlayer.npHasTrack ? Qt.PointingHandCursor : Qt.ArrowCursor
|
||||
onPressed: if (QbzPlayer.npHasTrack) QbzPlayer.seek(Math.min(Math.max(mouseX / width, 0), 1))
|
||||
onPositionChanged: if (pressed && QbzPlayer.npHasTrack) QbzPlayer.seek(Math.min(Math.max(mouseX / width, 0), 1))
|
||||
// Hover is what keeps mouseX live for the cursor binding
|
||||
// below (PlayerBar.slint:180 -> SeekBar.slint's TouchArea
|
||||
// always tracks mouse-x).
|
||||
hoverEnabled: true
|
||||
// No-drop cursor over the not-yet-downloaded region
|
||||
// (SeekBar.slint:93-95). The npHasTrack arm is the Qt bar's
|
||||
// own idle guard and stays first.
|
||||
cursorShape: !QbzPlayer.npHasTrack
|
||||
? Qt.ArrowCursor
|
||||
: (root.beyondSeekable(mouseX / width) ? Qt.ForbiddenCursor
|
||||
: Qt.PointingHandCursor)
|
||||
// Lock the seek target to what has downloaded while streaming
|
||||
// (SeekBar.slint:96-99).
|
||||
onPressed: if (QbzPlayer.npHasTrack) QbzPlayer.seek(root.seekTarget(mouseX / width))
|
||||
onPositionChanged: if (pressed && QbzPlayer.npHasTrack) QbzPlayer.seek(root.seekTarget(mouseX / width))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user