fix: stabilize playback memory, local library sync and Qt settings

Add shared playback memory profiles, bounded disk-backed playback and storage
selection for desktop and daemon. Preserve gapless during streaming-only playback
and provide explicit cache eviction with playback confirmation.

Stop watcher read events and unchanged scans from rebuilding the local catalog.
Restore playlist/sidebar updates, folder availability feedback and player layout.
Add appearance presets and a radio-based cache settings panel with translated
trade-offs, plus consistent clipboard menus across visible Qt text editors.

Validate with Rust regressions, QML interaction tests, isolated release startup
and idle-library checks; extend the shared local and CI gates.

cx
This commit is contained in:
vicrodh committed 2026-09-13 02:57:21 -06:00
1 parent 3cf1f32177
commit e291eb3af4
118 files changed
+7128 -883

No files matched your search

+3 -2
View File
@@ -66,7 +66,8 @@ Rectangle {
readonly property bool pulled: root.item.qobuzUnavailable === true
readonly property int cacheStatus: root.item.cacheStatus !== undefined
? root.item.cacheStatus : 0
readonly property bool pulledDead: root.pulled && root.cacheStatus !== 3
readonly property bool pulledDead: (root.pulled && root.cacheStatus !== 3)
|| root.item.sourceUnavailable === true
/// `modifiers` rides straight off the mouse event: Shift is what turns
/// a click into a range (controls/SelectionModel.qml).
signal toggleSelect(int modifiers)
@@ -123,7 +124,7 @@ Rectangle {
/// below all resolve a catalog id.
readonly property bool catalogRow: {
var src = root.item.source || ""
return src !== "local" && src !== "plex"
return src === "" || src === "qobuz"
}
function menuEntries() {
+1
View File
@@ -1484,6 +1484,7 @@ Rectangle {
tintName: "muted"
}
TextInput {
QbzTextEditMenu { }
width: parent.width - 21
height: parent.height
color: theme.textPrimary
+9 -5
View File
@@ -148,10 +148,12 @@ Rectangle {
it._albumLc = String(it.album || "").toLowerCase()
it._genreLc = String(it.genre || "").toLowerCase()
if (it.kind === "track" || it.kind === "album") {
if (it.group === "favorites" || it.group === "purchases") {
var tabKey = it.kind + ":" + it.id
if (it.group === "favorites" || it.group === "purchases"
|| (it.kind === "album" && isLocalFeedItem(it))) {
var tabKey = membershipKey
if (it.group === "purchases") tabPurchased[tabKey] = true
else tabFavorites[tabKey] = true
else if (it.group === "favorites" || it.isFavorite === true)
tabFavorites[tabKey] = true
if (!tabSeen[tabKey]) {
tabSeen[tabKey] = true
totals[it.kind + "s"]++
@@ -674,13 +676,15 @@ Rectangle {
var x = feed[i]
var keep = false
if (sourceTab && x.kind === sourceKind) {
var sourceKey = sourceKind + ":" + x.id
var sourceKey = x._membershipKey
var isPurchased = purchased[sourceKey] === true
var isFavorite = favorites[sourceKey] === true
var included = matchesSources(isPurchased, isFavorite, false)
var localRelease = sourceKind === "album" && isLocalFeedItem(x)
if (localRelease && !showLocal && hideableLocalAlbum(x)) included = false
// One representative row per included entity. A purchase row
// wins even in Favorites-only so its quality badge survives.
keep = included && ((isPurchased && x.group === "purchases")
keep = included && (localRelease || (isPurchased && x.group === "purchases")
|| (!isPurchased && x.group === "favorites"))
if (keep) {
if (tabSeen[sourceKey]) keep = false
@@ -556,6 +556,7 @@ Rectangle {
height: parent.height
clip: true
TextInput {
QbzTextEditMenu { }
id: searchInput
anchors.fill: parent
color: theme.textPrimary
@@ -122,6 +122,7 @@ Rectangle {
height: parent.height
clip: true
TextInput {
QbzTextEditMenu { }
id: mpSearch
anchors.fill: parent
color: theme.textPrimary
+1
View File
@@ -737,6 +737,7 @@ Rectangle {
spacing: 6
QbzIcon { name: "search"; width: 13; height: 13; anchors.verticalCenter: parent.verticalCenter; tintName: "muted" }
TextInput {
QbzTextEditMenu { }
width: parent.width - 19
height: parent.height
color: theme.textPrimary
+1
View File
@@ -692,6 +692,7 @@ Rectangle {
border.color: customMinutes.activeFocus
? theme.accent : theme.borderSubtle
TextInput {
QbzTextEditMenu { }
id: customMinutes
anchors.fill: parent
anchors.leftMargin: 9
@@ -114,6 +114,19 @@ Item {
// may additionally show the catalog mark in the mixed feed.
source: cell.item.source
sources: cell.item.sources || []
catalogAffordances: !cell.view.isLocalFeedItem(cell.item)
localMode: cell.view.isLocalFeedItem(cell.item)
localPlaylistAffordance: localMode
hostFavorite: localMode && badgeSources.some(function (source) {
return ["local", "plex", "jellyfin", "subsonic", "navidrome",
"gonic", "airsonic", "astiga"].indexOf(source) >= 0
})
onFavoriteRequested: QbzLocal.albumToggleFavorite(cell.item.id,
cell.item.title || "", cell.item.artist || "", artworkUrl,
JSON.stringify(badgeSources))
onOpenRequested: QbzAlbum.openAlbum(cell.item.id)
onPlayRequested: QbzPlayer.playAlbum(cell.item.id)
onEnqueueRequested: function (mode) { QbzPlayer.enqueueAlbum(cell.item.id, mode) }
showSourceBadge: cell.view.showSourceBadges
}
}
@@ -259,6 +259,7 @@ Rectangle {
border.color: nameField.activeFocus
? theme.accent : theme.surfaceElevated
TextInput {
QbzTextEditMenu { }
id: nameField
anchors.fill: parent
anchors.leftMargin: 12
@@ -53,6 +53,7 @@
// consumer.
import QtQuick
import "../../controls"
import com.blitzfc.qbz
import "../../theme"
@@ -267,6 +268,7 @@ Item {
tintName: "muted"
}
TextInput {
QbzTextEditMenu { }
id: searchInput
width: parent.width - 22 - (clearBtn.visible ? clearBtn.width + 8 : 0)
height: 21