diff --git a/README.md b/README.md index 6e9149cb5..b2fac8a9a 100644 --- a/README.md +++ b/README.md @@ -329,6 +329,9 @@ Multi-device playback control using Qobuz's real-time streaming protocol. Full telemetry — one-way pull) - **Discogs** artwork and metadata for the local library - Desktop notifications with artwork +- **Listening history** is stored only on your disk, per Qobuz account; it + never leaves the machine unless you enable a scrobbler (Settings › + Integrations › Privacy to pause or clear it) ### Immersive player diff --git a/crates/qbz-app/src/settings/scrobblers.rs b/crates/qbz-app/src/settings/scrobblers.rs index d7e2b93bd..3375488d2 100644 --- a/crates/qbz-app/src/settings/scrobblers.rs +++ b/crates/qbz-app/src/settings/scrobblers.rs @@ -98,6 +98,25 @@ impl ScrobblerSettings { pub fn listenbrainz_active(&self) -> bool { self.enabled && self.listenbrainz_enabled && self.listenbrainz_is_authed() } + + /// Whether QBZ may READ the user's listening history from an external + /// service (Recommendations). ONE rule, the same as sending: connected + /// AND scrobbling on. A connected-but-disabled account used to be read + /// silently through `*_is_authed()`; the scrobbling toggle now governs + /// both directions, and its Settings text says so. + pub fn history_read_allowed(&self, service: HistoryService) -> bool { + match service { + HistoryService::LastFm => self.lastfm_active(), + HistoryService::ListenBrainz => self.listenbrainz_active(), + } + } +} + +/// The external services whose history Recommendations may read. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum HistoryService { + LastFm, + ListenBrainz, } pub struct ScrobblerSettingsStore { 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 91d36286e..6b3845a91 100644 --- a/crates/qbz-i18n/translations/de/LC_MESSAGES/qbz-ui.po +++ b/crates/qbz-i18n/translations/de/LC_MESSAGES/qbz-ui.po @@ -1941,8 +1941,8 @@ msgstr "Dadurch werden alle {} gesperrten Künstler entfernt. Dies kann nicht r msgid "SCROBBLERS" msgstr "SCROBBLER" -msgid "Send your plays to Last.fm and ListenBrainz. Works for Qobuz, local, and Plex tracks." -msgstr "Senden Sie Ihre Wiedergaben an Last.fm und ListenBrainz. Funktioniert für Qobuz-, lokale und Plex-Titel." +msgid "Send your plays to Last.fm and ListenBrainz. Works for Qobuz, local, and Plex tracks. This switch also decides whether Recommendations may read your listening history from those services." +msgstr "Senden Sie Ihre Wiedergaben an Last.fm und ListenBrainz. Funktioniert für Qobuz-, lokale und Plex-Titel. Dieser Schalter entscheidet auch, ob Empfehlungen Ihren Hörverlauf von diesen Diensten lesen dürfen." msgid "LAST.FM" msgstr "LAST.FM" @@ -6194,3 +6194,24 @@ msgstr "Nur fehlende Titel" msgid "Keeps existing offline copies and downloads tracks that are not available yet." msgstr "Behält vorhandene Offline-Kopien bei und lädt noch nicht verfügbare Titel herunter." + +msgid "PRIVACY" +msgstr "DATENSCHUTZ" + +msgid "Listening history" +msgstr "Hörverlauf" + +msgid "Keep a local log of what you play: track, source, how much of it you heard and when. It stays on this machine, per account, and never leaves it unless you enable a scrobbler." +msgstr "Führt ein lokales Protokoll Ihrer Wiedergaben: Titel, Quelle, wie viel davon Sie gehört haben und wann. Es bleibt auf diesem Rechner, pro Konto, und verlässt ihn nie, sofern Sie keinen Scrobbler aktivieren." + +msgid "Clear listening history" +msgstr "Hörverlauf löschen" + +msgid "Deletes every play recorded on this machine for this account. Scrobbles already sent are not affected." +msgstr "Löscht jede auf diesem Rechner für dieses Konto aufgezeichnete Wiedergabe. Bereits gesendete Scrobbles sind nicht betroffen." + +msgid "Listening history cleared." +msgstr "Hörverlauf gelöscht." + +msgid "Couldn't clear listening history." +msgstr "Hörverlauf konnte nicht gelöscht werden." diff --git a/crates/qbz-i18n/translations/en/LC_MESSAGES/qbz-ui.po b/crates/qbz-i18n/translations/en/LC_MESSAGES/qbz-ui.po index d638b1acc..dece75e89 100644 --- a/crates/qbz-i18n/translations/en/LC_MESSAGES/qbz-ui.po +++ b/crates/qbz-i18n/translations/en/LC_MESSAGES/qbz-ui.po @@ -1811,3 +1811,27 @@ msgstr "Only missing tracks" msgid "Keeps existing offline copies and downloads tracks that are not available yet." msgstr "Keeps existing offline copies and downloads tracks that are not available yet." + +msgid "Send your plays to Last.fm and ListenBrainz. Works for Qobuz, local, and Plex tracks. This switch also decides whether Recommendations may read your listening history from those services." +msgstr "Send your plays to Last.fm and ListenBrainz. Works for Qobuz, local, and Plex tracks. This switch also decides whether Recommendations may read your listening history from those services." + +msgid "PRIVACY" +msgstr "PRIVACY" + +msgid "Listening history" +msgstr "Listening history" + +msgid "Keep a local log of what you play: track, source, how much of it you heard and when. It stays on this machine, per account, and never leaves it unless you enable a scrobbler." +msgstr "Keep a local log of what you play: track, source, how much of it you heard and when. It stays on this machine, per account, and never leaves it unless you enable a scrobbler." + +msgid "Clear listening history" +msgstr "Clear listening history" + +msgid "Deletes every play recorded on this machine for this account. Scrobbles already sent are not affected." +msgstr "Deletes every play recorded on this machine for this account. Scrobbles already sent are not affected." + +msgid "Listening history cleared." +msgstr "Listening history cleared." + +msgid "Couldn't clear listening history." +msgstr "Couldn't clear listening history." 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 9652c5349..7293fd7ba 100644 --- a/crates/qbz-i18n/translations/es/LC_MESSAGES/qbz-ui.po +++ b/crates/qbz-i18n/translations/es/LC_MESSAGES/qbz-ui.po @@ -1947,8 +1947,8 @@ msgstr "Esto quitará los {} artistas de la lista negra. Esta acción no se pued msgid "SCROBBLERS" msgstr "SCROBBLERS" -msgid "Send your plays to Last.fm and ListenBrainz. Works for Qobuz, local, and Plex tracks." -msgstr "Envía tus reproducciones a Last.fm y ListenBrainz. Funciona con pistas de Qobuz, locales y de Plex." +msgid "Send your plays to Last.fm and ListenBrainz. Works for Qobuz, local, and Plex tracks. This switch also decides whether Recommendations may read your listening history from those services." +msgstr "Envía tus reproducciones a Last.fm y ListenBrainz. Funciona con pistas de Qobuz, locales y de Plex. Este interruptor también decide si Recomendaciones puede leer tu historial de escucha de esos servicios." msgid "LAST.FM" msgstr "LAST.FM" @@ -6200,3 +6200,24 @@ msgstr "Solo las pistas faltantes" msgid "Keeps existing offline copies and downloads tracks that are not available yet." msgstr "Conserva las copias sin conexión existentes y descarga las pistas que aún no están disponibles." + +msgid "PRIVACY" +msgstr "PRIVACIDAD" + +msgid "Listening history" +msgstr "Historial de escucha" + +msgid "Keep a local log of what you play: track, source, how much of it you heard and when. It stays on this machine, per account, and never leaves it unless you enable a scrobbler." +msgstr "Guarda un registro local de lo que reproduces: pista, fuente, cuánto escuchaste y cuándo. Se queda en esta computadora, por cuenta, y nunca sale de aquí a menos que actives un scrobbler." + +msgid "Clear listening history" +msgstr "Borrar historial de escucha" + +msgid "Deletes every play recorded on this machine for this account. Scrobbles already sent are not affected." +msgstr "Elimina todas las reproducciones registradas en esta computadora para esta cuenta. Los scrobbles ya enviados no se ven afectados." + +msgid "Listening history cleared." +msgstr "Historial de escucha borrado." + +msgid "Couldn't clear listening history." +msgstr "No se pudo borrar el historial de escucha." 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 027c6a353..d77a0dc4d 100644 --- a/crates/qbz-i18n/translations/fr/LC_MESSAGES/qbz-ui.po +++ b/crates/qbz-i18n/translations/fr/LC_MESSAGES/qbz-ui.po @@ -1941,8 +1941,8 @@ msgstr "Cela supprimera les {} artistes de la liste noire. Cette action est irr msgid "SCROBBLERS" msgstr "SCROBBLERS" -msgid "Send your plays to Last.fm and ListenBrainz. Works for Qobuz, local, and Plex tracks." -msgstr "Envoyez vos écoutes vers Last.fm et ListenBrainz. Fonctionne pour les titres Qobuz, locaux et Plex." +msgid "Send your plays to Last.fm and ListenBrainz. Works for Qobuz, local, and Plex tracks. This switch also decides whether Recommendations may read your listening history from those services." +msgstr "Envoyez vos écoutes vers Last.fm et ListenBrainz. Fonctionne pour les titres Qobuz, locaux et Plex. Cet interrupteur décide aussi si les Recommandations peuvent lire votre historique d’écoute depuis ces services." msgid "LAST.FM" msgstr "LAST.FM" @@ -6192,3 +6192,24 @@ msgstr "Uniquement les pistes manquantes" msgid "Keeps existing offline copies and downloads tracks that are not available yet." msgstr "Conserve les copies hors ligne existantes et télécharge les pistes qui ne sont pas encore disponibles." + +msgid "PRIVACY" +msgstr "CONFIDENTIALITÉ" + +msgid "Listening history" +msgstr "Historique d’écoute" + +msgid "Keep a local log of what you play: track, source, how much of it you heard and when. It stays on this machine, per account, and never leaves it unless you enable a scrobbler." +msgstr "Conserve un journal local de ce que vous écoutez : titre, source, durée écoutée et moment. Il reste sur cette machine, par compte, et ne la quitte jamais sauf si vous activez un scrobbler." + +msgid "Clear listening history" +msgstr "Effacer l’historique d’écoute" + +msgid "Deletes every play recorded on this machine for this account. Scrobbles already sent are not affected." +msgstr "Supprime toutes les écoutes enregistrées sur cette machine pour ce compte. Les scrobbles déjà envoyés ne sont pas concernés." + +msgid "Listening history cleared." +msgstr "Historique d’écoute effacé." + +msgid "Couldn't clear listening history." +msgstr "Impossible d’effacer l’historique d’écoute." 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 79f66364f..6b4d379cd 100644 --- a/crates/qbz-i18n/translations/ja/LC_MESSAGES/qbz-ui.po +++ b/crates/qbz-i18n/translations/ja/LC_MESSAGES/qbz-ui.po @@ -4027,8 +4027,8 @@ msgstr "ブラックリストに登録されたアーティストは、検索結 msgid "Blacklist filtering is disabled. Blacklisted artists are shown everywhere until you re-enable it." msgstr "ブラックリストフィルタリングは無効です。再度有効にするまで、ブラックリストに登録されたアーティストはあらゆる場所に表示されます。" -msgid "Send your plays to Last.fm and ListenBrainz. Works for Qobuz, local, and Plex tracks." -msgstr "再生履歴をLast.fmとListenBrainzに送信します。Qobuz、ローカル、Plexのトラックに対応しています。" +msgid "Send your plays to Last.fm and ListenBrainz. Works for Qobuz, local, and Plex tracks. This switch also decides whether Recommendations may read your listening history from those services." +msgstr "再生履歴をLast.fmとListenBrainzに送信します。Qobuz、ローカル、Plexのトラックに対応しています。このスイッチは、おすすめがそれらのサービスから再生履歴を読み取れるかどうかも決めます。" msgid "Show what you're listening to as your Discord status. Opt-in — Discord must be running." msgstr "聴いている曲をDiscordのステータスとして表示します。オプトイン制で、Discordが起動している必要があります。" @@ -6176,3 +6176,24 @@ msgstr "不足しているトラックのみ" msgid "Keeps existing offline copies and downloads tracks that are not available yet." msgstr "既存のオフラインコピーを保持し、まだ利用できないトラックだけをダウンロードします。" + +msgid "PRIVACY" +msgstr "プライバシー" + +msgid "Listening history" +msgstr "再生履歴" + +msgid "Keep a local log of what you play: track, source, how much of it you heard and when. It stays on this machine, per account, and never leaves it unless you enable a scrobbler." +msgstr "再生した内容(トラック、ソース、どれだけ聴いたか、いつ聴いたか)をローカルに記録します。この記録はアカウントごとにこのマシンに保存され、スクロブラーを有効にしない限り外部には送信されません。" + +msgid "Clear listening history" +msgstr "再生履歴を消去" + +msgid "Deletes every play recorded on this machine for this account. Scrobbles already sent are not affected." +msgstr "このアカウントについてこのマシンに記録されたすべての再生を削除します。送信済みのスクロブルには影響しません。" + +msgid "Listening history cleared." +msgstr "再生履歴を消去しました。" + +msgid "Couldn't clear listening history." +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 e3e1330b3..462a3bbec 100644 --- a/crates/qbz-i18n/translations/nl/LC_MESSAGES/qbz-ui.po +++ b/crates/qbz-i18n/translations/nl/LC_MESSAGES/qbz-ui.po @@ -1941,8 +1941,8 @@ msgstr "Dit verwijdert alle {} artiesten van de zwarte lijst. Dit kan niet onged msgid "SCROBBLERS" msgstr "SCROBBLERS" -msgid "Send your plays to Last.fm and ListenBrainz. Works for Qobuz, local, and Plex tracks." -msgstr "Stuur je afgespeelde nummers naar Last.fm en ListenBrainz. Werkt voor Qobuz-, lokale en Plex-nummers." +msgid "Send your plays to Last.fm and ListenBrainz. Works for Qobuz, local, and Plex tracks. This switch also decides whether Recommendations may read your listening history from those services." +msgstr "Stuur je afgespeelde nummers naar Last.fm en ListenBrainz. Werkt voor Qobuz-, lokale en Plex-nummers. Deze schakelaar bepaalt ook of Aanbevelingen je luistergeschiedenis van die diensten mag lezen." msgid "LAST.FM" msgstr "LAST.FM" @@ -6182,3 +6182,24 @@ msgstr "Alleen ontbrekende nummers" msgid "Keeps existing offline copies and downloads tracks that are not available yet." msgstr "Behoudt bestaande offlinekopieën en downloadt nummers die nog niet beschikbaar zijn." + +msgid "PRIVACY" +msgstr "PRIVACY" + +msgid "Listening history" +msgstr "Luistergeschiedenis" + +msgid "Keep a local log of what you play: track, source, how much of it you heard and when. It stays on this machine, per account, and never leaves it unless you enable a scrobbler." +msgstr "Houd een lokaal logboek bij van wat je afspeelt: nummer, bron, hoeveel je ervan hebt gehoord en wanneer. Het blijft op deze computer, per account, en verlaat die nooit tenzij je een scrobbler inschakelt." + +msgid "Clear listening history" +msgstr "Luistergeschiedenis wissen" + +msgid "Deletes every play recorded on this machine for this account. Scrobbles already sent are not affected." +msgstr "Verwijdert elke afspeelactie die op deze computer voor dit account is vastgelegd. Al verzonden scrobbles worden niet beïnvloed." + +msgid "Listening history cleared." +msgstr "Luistergeschiedenis gewist." + +msgid "Couldn't clear listening history." +msgstr "Kon de luistergeschiedenis niet wissen." 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 a66454075..5b7427932 100644 --- a/crates/qbz-i18n/translations/pt/LC_MESSAGES/qbz-ui.po +++ b/crates/qbz-i18n/translations/pt/LC_MESSAGES/qbz-ui.po @@ -1941,8 +1941,8 @@ msgstr "Isto irá remover todos os {} artistas da lista negra. Esta ação não msgid "SCROBBLERS" msgstr "SCROBBLERS" -msgid "Send your plays to Last.fm and ListenBrainz. Works for Qobuz, local, and Plex tracks." -msgstr "Envie as suas reproduções para o Last.fm e o ListenBrainz. Funciona com faixas do Qobuz, locais e do Plex." +msgid "Send your plays to Last.fm and ListenBrainz. Works for Qobuz, local, and Plex tracks. This switch also decides whether Recommendations may read your listening history from those services." +msgstr "Envie as suas reproduções para o Last.fm e o ListenBrainz. Funciona com faixas do Qobuz, locais e do Plex. Este interruptor também decide se as Recomendações podem ler o seu histórico de audição desses serviços." msgid "LAST.FM" msgstr "LAST.FM" @@ -6192,3 +6192,24 @@ msgstr "Apenas faixas ausentes" msgid "Keeps existing offline copies and downloads tracks that are not available yet." msgstr "Mantém as cópias off-line existentes e baixa as faixas que ainda não estão disponíveis." + +msgid "PRIVACY" +msgstr "PRIVACIDADE" + +msgid "Listening history" +msgstr "Histórico de audição" + +msgid "Keep a local log of what you play: track, source, how much of it you heard and when. It stays on this machine, per account, and never leaves it unless you enable a scrobbler." +msgstr "Mantém um registo local do que reproduz: faixa, origem, quanto ouviu e quando. Fica nesta máquina, por conta, e nunca sai daqui a menos que ative um scrobbler." + +msgid "Clear listening history" +msgstr "Limpar histórico de audição" + +msgid "Deletes every play recorded on this machine for this account. Scrobbles already sent are not affected." +msgstr "Elimina todas as reproduções registadas nesta máquina para esta conta. Os scrobbles já enviados não são afetados." + +msgid "Listening history cleared." +msgstr "Histórico de audição limpo." + +msgid "Couldn't clear listening history." +msgstr "Não foi possível limpar o histórico de audição." 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 e49900a0d..47de7e045 100644 --- a/crates/qbz-i18n/translations/ru/LC_MESSAGES/qbz-ui.po +++ b/crates/qbz-i18n/translations/ru/LC_MESSAGES/qbz-ui.po @@ -4054,8 +4054,8 @@ msgstr "Исполнители из чёрного списка скрыты и msgid "Blacklist filtering is disabled. Blacklisted artists are shown everywhere until you re-enable it." msgstr "Фильтрация по чёрному списку отключена. Исполнители из чёрного списка показываются везде, пока вы её снова не включите." -msgid "Send your plays to Last.fm and ListenBrainz. Works for Qobuz, local, and Plex tracks." -msgstr "Отправляйте свои прослушивания в Last.fm и ListenBrainz. Работает для треков Qobuz, локальных и Plex." +msgid "Send your plays to Last.fm and ListenBrainz. Works for Qobuz, local, and Plex tracks. This switch also decides whether Recommendations may read your listening history from those services." +msgstr "Отправляйте свои прослушивания в Last.fm и ListenBrainz. Работает для треков Qobuz, локальных и Plex. Этот переключатель также определяет, может ли раздел «Рекомендации» читать вашу историю прослушиваний из этих сервисов." msgid "Show what you're listening to as your Discord status. Opt-in — Discord must be running." msgstr "Показывать, что вы слушаете, в статусе Discord. По желанию — Discord должен быть запущен." @@ -6212,3 +6212,24 @@ msgstr "Только недостающие треки" msgid "Keeps existing offline copies and downloads tracks that are not available yet." msgstr "Сохраняет существующие офлайн-копии и загружает ещё недоступные треки." + +msgid "PRIVACY" +msgstr "КОНФИДЕНЦИАЛЬНОСТЬ" + +msgid "Listening history" +msgstr "История прослушиваний" + +msgid "Keep a local log of what you play: track, source, how much of it you heard and when. It stays on this machine, per account, and never leaves it unless you enable a scrobbler." +msgstr "Ведёт локальный журнал того, что вы слушаете: трек, источник, сколько из него вы прослушали и когда. Он хранится на этом компьютере, отдельно для каждой учётной записи, и никогда не покидает его, если вы не включите скробблер." + +msgid "Clear listening history" +msgstr "Очистить историю прослушиваний" + +msgid "Deletes every play recorded on this machine for this account. Scrobbles already sent are not affected." +msgstr "Удаляет все прослушивания, записанные на этом компьютере для этой учётной записи. Уже отправленные скробблы не затрагиваются." + +msgid "Listening history cleared." +msgstr "История прослушиваний очищена." + +msgid "Couldn't clear listening history." +msgstr "Не удалось очистить историю прослушиваний." diff --git a/crates/qbz-qt/qml/settings/IntegrationsSettings.qml b/crates/qbz-qt/qml/settings/IntegrationsSettings.qml index d9b97fd86..ea705c285 100644 --- a/crates/qbz-qt/qml/settings/IntegrationsSettings.qml +++ b/crates/qbz-qt/qml/settings/IntegrationsSettings.qml @@ -19,6 +19,9 @@ Column { id: root property var doc: ({}) + /// The view-level SettingsConfirmHost (SettingsView.qml), for the + /// destructive "Clear listening history" row. Null in previews. + property var confirmHost: null QbzTheme { id: theme } @@ -65,6 +68,42 @@ Column { SettingsSpacer { } + // ============================ PRIVACY ================================ + // The local listen log (qbz_app::listen_log): one row per play, on this + // disk only, per account. Default ON. "Clear" is DELETE + VACUUM. The + // toggle's state comes from the per-user store (`listen_meta.paused`), + // not from a pref, so it follows the account. + GroupHeader { text: QbzSession.tr("PRIVACY", QbzSession.trRev) } + SettingRow { + label: QbzSession.tr("Listening history", QbzSession.trRev) + description: QbzSession.tr("Keep a local log of what you play: track, source, how much of it you heard and when. It stays on this machine, per account, and never leaves it unless you enable a scrobbler.", QbzSession.trRev) + QbzToggle { + checked: root.doc.listenHistoryEnabled !== false + onToggled: function (v) { QbzBridge.settingsBool("listen-history", v) } + } + } + SettingRow { + label: QbzSession.tr("Clear listening history", QbzSession.trRev) + description: QbzSession.tr("Deletes every play recorded on this machine for this account. Scrobbles already sent are not affected.", QbzSession.trRev) + SettingsButton { + danger: true + text: QbzSession.tr("Clear", QbzSession.trRev) + onClicked: { + if (!root.confirmHost) { + QbzBridge.integrationsAction("listen-history-clear") + return + } + root.confirmHost.ask( + QbzSession.tr("Clear listening history", QbzSession.trRev), + QbzSession.tr("Deletes every play recorded on this machine for this account. Scrobbles already sent are not affected.", QbzSession.trRev), + QbzSession.tr("Clear", QbzSession.trRev), + function () { QbzBridge.integrationsAction("listen-history-clear") }) + } + } + } + + SettingsSpacer { } + // ========================== SCROBBLERS =============================== // Master header row (toggle + collapse chevron), then the body gated // on enabled && !collapsed (IntegrationsSettings.slint:113-169). @@ -88,7 +127,7 @@ Column { } Text { width: parent.width - text: QbzSession.tr("Send your plays to Last.fm and ListenBrainz. Works for Qobuz, local, and Plex tracks.", QbzSession.trRev) + text: QbzSession.tr("Send your plays to Last.fm and ListenBrainz. Works for Qobuz, local, and Plex tracks. This switch also decides whether Recommendations may read your listening history from those services.", QbzSession.trRev) color: theme.textMuted font.pixelSize: 12 wrapMode: Text.WordWrap diff --git a/crates/qbz-qt/qml/settings/SettingsView.qml b/crates/qbz-qt/qml/settings/SettingsView.qml index 3c0568341..6a95ead80 100644 --- a/crates/qbz-qt/qml/settings/SettingsView.qml +++ b/crates/qbz-qt/qml/settings/SettingsView.qml @@ -353,6 +353,7 @@ Item { IntegrationsSettings { width: parent.width doc: root.doc + confirmHost: confirmHost } } } diff --git a/crates/qbz-qt/src/integrations_qt.rs b/crates/qbz-qt/src/integrations_qt.rs index 0eca55d46..8d4ee29ba 100644 --- a/crates/qbz-qt/src/integrations_qt.rs +++ b/crates/qbz-qt/src/integrations_qt.rs @@ -509,6 +509,16 @@ pub async fn listenbrainz_set_token(token: &str) { /// integrations_action dispatch (non-toggle rows). pub async fn handle_action(_runtime: &Arc>, action: &str) { match action { + // Privacy: "Clear listening history" (confirmed in QML first). + // DELETE + VACUUM on the per-user listen_log.db; the toggle state and + // the install's origin id survive. + "listen-history-clear" => match crate::listen_log_qt::clear().await { + Ok(()) => crate::toast_qt::success(qbz_i18n::t("Listening history cleared.")), + Err(e) => { + log::warn!("[qbz-qt] clear listening history failed: {e}"); + crate::toast_qt::error(qbz_i18n::t("Couldn't clear listening history.")); + } + }, // Last.fm two-step browser auth (scrobble.rs `lastfm_connect`): // request token -> authorize URL in the browser -> Finish exchanges // it for a session. diff --git a/crates/qbz-qt/src/recommendations_qt.rs b/crates/qbz-qt/src/recommendations_qt.rs index b785e35a4..188d81f07 100644 --- a/crates/qbz-qt/src/recommendations_qt.rs +++ b/crates/qbz-qt/src/recommendations_qt.rs @@ -658,9 +658,15 @@ fn apply_artist_rails( async fn run(force: bool) { let cfg = crate::integrations_qt::scrobble_settings(); + use qbz_app::settings::scrobblers::HistoryService; let lastfm_client = LastFmClient::new(); let lb_client = ListenBrainzClient::new(); - if cfg.listenbrainz_is_authed() { + // History READ gate = the scrobbling gate (`*_active()`): a connected + // account with scrobbling off is not read either. One rule, both + // directions; the toggle's Settings text names it. + let lastfm_allowed = cfg.history_read_allowed(HistoryService::LastFm); + let lb_allowed = cfg.history_read_allowed(HistoryService::ListenBrainz); + if lb_allowed { lb_client .restore_token( cfg.listenbrainz_token.clone(), @@ -674,9 +680,9 @@ async fn run(force: bool) { // already treat as "no MusicBrainz" — zero requests leave the machine. let mb_client = crate::app().core().musicbrainz_client(); - // CONNECTED, not merely enabled: a `None` handle is what makes the row + // CONNECTED AND scrobbling on: a `None` handle is what makes the row // absent AND silent (the builders early-return before any request). - let lastfm = if cfg.lastfm_is_authed() && !cfg.lastfm_username.is_empty() { + let lastfm = if lastfm_allowed && !cfg.lastfm_username.is_empty() { Some(LastFmHandle { username: cfg.lastfm_username.clone(), client: &lastfm_client, @@ -684,7 +690,7 @@ async fn run(force: bool) { } else { None }; - let listenbrainz = if cfg.listenbrainz_is_authed() && !cfg.listenbrainz_username.is_empty() { + let listenbrainz = if lb_allowed && !cfg.listenbrainz_username.is_empty() { Some(ListenBrainzHandle { username: cfg.listenbrainz_username.clone(), client: &lb_client, diff --git a/crates/qbz-qt/src/settings_qt.rs b/crates/qbz-qt/src/settings_qt.rs index 2f22c5d04..871883e13 100644 --- a/crates/qbz-qt/src/settings_qt.rs +++ b/crates/qbz-qt/src/settings_qt.rs @@ -1956,6 +1956,9 @@ pub struct SettingsDoc { pub scrobble_ui_collapsed: bool, #[serde(rename = "allowLoggedOutScrobbling")] pub allow_logged_out_scrobbling: bool, + /// Privacy: the local listen log (`listen_log.db`) is recording. + #[serde(rename = "listenHistoryEnabled")] + pub listen_history_enabled: bool, #[serde(rename = "lastfmEnabled")] pub lastfm_enabled: bool, #[serde(rename = "lastfmAuthed")] @@ -2422,6 +2425,7 @@ pub async fn publish_snapshot() { scrobble_enabled: scrobble_snap.enabled, scrobble_ui_collapsed: scrobble_snap.ui_collapsed, allow_logged_out_scrobbling: scrobble_snap.allow_logged_out_scrobbling, + listen_history_enabled: crate::listen_log_qt::is_enabled(), lastfm_enabled: scrobble_snap.lastfm_enabled, lastfm_authed: scrobble_snap.lastfm_is_authed(), lastfm_username: scrobble_snap.lastfm_username.clone(), @@ -2912,6 +2916,10 @@ pub async fn settings_bool(runtime: &Arc>, key: &str, "scrobble-logged-out" => { crate::integrations_qt::set_logged_out_scrobbling(value).map(|_| Apply::None) } + // --- Privacy: the local listen log ------------------------------ + "listen-history" => crate::listen_log_qt::set_enabled(value) + .await + .map(|_| Apply::None), "lastfm-enable" => crate::integrations_qt::set_lastfm_enabled(value).map(|_| Apply::None), "listenbrainz-enable" => { crate::integrations_qt::set_listenbrainz_enabled(value).map(|_| Apply::None)