feat(subscription-gate): extend offline grace window 3 → 30 days
Matches Qobuz's own mobile app posture. The previous 3-day window was both user-hostile (punishes anyone on a flaky network) and a compliance red flag (shorter windows read as 'we're hiding something', longer windows read as 'we're lenient for a reason'). 30 days is the exact parity answer. The useful protection against bulk-download-and-ghost attacks comes from the CMAF-at-rest offline cache (v2 format), not from aggressive revalidation. This grace window is a belt over the suspenders. Changes: - GRACE_PERIOD_SECS: 3 → 30 days in config/subscription_state.rs, with a comment explaining the reasoning for future readers. - Log message in session_lifecycle no longer says "3 days" (it quoted a constant, now says "grace window"). - Disclaimer string in all 5 locales (en/es/de/fr/pt) updated from "3 days" to "30 days" so the Offline Playback settings screen reflects the actual behavior. No behavior changes for users with an active subscription. Users who were within the old 3-day window don't lose cache — the window extended, not contracted. cc
This commit is contained in:
7 files changed
+20
-7
No files matched your search
@@ -9,7 +9,20 @@ use serde::{Deserialize, Serialize};
|
||||
use std::path::Path;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
const GRACE_PERIOD_SECS: i64 = 3 * 24 * 60 * 60; // 3 days
|
||||
/// How long we keep honoring offline access after the server first
|
||||
/// reports an invalid subscription.
|
||||
///
|
||||
/// Qobuz's own mobile app gives a 30-day offline grace window, so we
|
||||
/// match that — this is the correct posture for the compliance story
|
||||
/// with Qobuz (we're not weaker than them, but we're also not
|
||||
/// suspiciously more lenient). Shorter windows would punish users on
|
||||
/// flaky networks; longer windows would be a compliance red flag.
|
||||
///
|
||||
/// NOTE: the useful protection against "bulk-download and ghost"
|
||||
/// attacks comes from the CMAF-at-rest offline cache (v2 format), not
|
||||
/// from aggressive revalidation. This grace period is a belt on top of
|
||||
/// those suspenders, not the primary defense.
|
||||
const GRACE_PERIOD_SECS: i64 = 30 * 24 * 60 * 60; // 30 days — Qobuz mobile parity
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
|
||||
@@ -189,7 +189,7 @@ pub async fn activate_session(app: &tauri::AppHandle, user_id: u64) -> Result<()
|
||||
};
|
||||
|
||||
if should_purge {
|
||||
log::warn!("[SessionLifecycle] Subscription invalid for >3 days. Purging offline cache.");
|
||||
log::warn!("[SessionLifecycle] Subscription invalid beyond the grace window. Purging offline cache.");
|
||||
if let Err(e) = crate::offline_cache::commands::purge_all_cached_files(
|
||||
offline_cache.inner(),
|
||||
library.inner(),
|
||||
|
||||
@@ -1408,7 +1408,7 @@
|
||||
},
|
||||
"offlineLibrary": {
|
||||
"title": "Offline-Bibliothek",
|
||||
"disclaimer": "Die Offline-Bibliothek ist ein temporärer Wiedergabe-Cache für das Offline-Hören, solange Sie ein gültiges Qobuz-Abonnement haben. Sie erhalten keine Lizenz zum Behalten oder Weiterverteilen der Inhalte. Wenn Ihr Abonnement ungültig wird, entfernt QBZ alle zwischengespeicherten Inhalte nach 3 Tagen.",
|
||||
"disclaimer": "Die Offline-Bibliothek ist ein temporärer Wiedergabe-Cache für das Offline-Hören, solange Sie ein gültiges Qobuz-Abonnement haben. Sie erhalten keine Lizenz zum Behalten oder Weiterverteilen der Inhalte. Wenn Ihr Abonnement ungültig wird, entfernt QBZ alle zwischengespeicherten Inhalte nach 30 Tagen.",
|
||||
"cachedTracks": "Offline-Titel",
|
||||
"cachedTracksDesc": "Zwischengespeicherte Titel für die Offline-Wiedergabe",
|
||||
"clearCache": "Alles löschen",
|
||||
|
||||
@@ -1414,7 +1414,7 @@
|
||||
},
|
||||
"offlineLibrary": {
|
||||
"title": "Offline Playback",
|
||||
"disclaimer": "The offline cache is a temporary playback cache for offline listening while you have a valid Qobuz subscription. You do not receive a license to keep or redistribute the content. If your subscription becomes invalid, QBZ will remove all cached content after 3 days.",
|
||||
"disclaimer": "The offline cache is a temporary playback cache for offline listening while you have a valid Qobuz subscription. You do not receive a license to keep or redistribute the content. If your subscription becomes invalid, QBZ will remove all cached content after 30 days.",
|
||||
"cachedTracks": "Offline Tracks",
|
||||
"cachedTracksDesc": "Cached tracks for offline playback",
|
||||
"showInLibrary": "Show in Local Library",
|
||||
|
||||
@@ -1414,7 +1414,7 @@
|
||||
},
|
||||
"offlineLibrary": {
|
||||
"title": "Reproducción Offline",
|
||||
"disclaimer": "El caché offline es un almacenamiento temporal de reproducción para escucha sin conexión mientras tengas una suscripción válida de Qobuz. No recibes una licencia para conservar o redistribuir el contenido. Si tu suscripción deja de ser válida, QBZ eliminará todo el contenido en caché después de 3 días.",
|
||||
"disclaimer": "El caché offline es un almacenamiento temporal de reproducción para escucha sin conexión mientras tengas una suscripción válida de Qobuz. No recibes una licencia para conservar o redistribuir el contenido. Si tu suscripción deja de ser válida, QBZ eliminará todo el contenido en caché después de 30 días.",
|
||||
"cachedTracks": "Pistas Offline",
|
||||
"cachedTracksDesc": "Pistas en caché para reproducción offline",
|
||||
"showInLibrary": "Mostrar en Biblioteca Local",
|
||||
|
||||
@@ -1408,7 +1408,7 @@
|
||||
},
|
||||
"offlineLibrary": {
|
||||
"title": "Bibliothèque hors ligne",
|
||||
"disclaimer": "La bibliothèque hors ligne est un cache de lecture temporaire pour l'écoute hors ligne lorsque vous avez un abonnement Qobuz valide. Vous ne recevez pas de licence pour conserver ou redistribuer le contenu. Si votre abonnement devient invalide, QBZ supprimera tout le contenu mis en cache après 3 jours.",
|
||||
"disclaimer": "La bibliothèque hors ligne est un cache de lecture temporaire pour l'écoute hors ligne lorsque vous avez un abonnement Qobuz valide. Vous ne recevez pas de licence pour conserver ou redistribuer le contenu. Si votre abonnement devient invalide, QBZ supprimera tout le contenu mis en cache après 30 jours.",
|
||||
"cachedTracks": "Titres hors ligne",
|
||||
"cachedTracksDesc": "Titres mis en cache pour lecture hors ligne",
|
||||
"showInLibrary": "Afficher dans la bibliothèque locale",
|
||||
|
||||
@@ -1408,7 +1408,7 @@
|
||||
},
|
||||
"offlineLibrary": {
|
||||
"title": "Reprodução Offline",
|
||||
"disclaimer": "O cache offline é um cache temporário de reprodução para ouvir offline enquanto você tem uma assinatura Qobuz válida. Você não recebe uma licença para manter ou redistribuir o conteúdo. Se sua assinatura se tornar inválida, o QBZ removerá todo o conteúdo em cache após 3 dias.",
|
||||
"disclaimer": "O cache offline é um cache temporário de reprodução para ouvir offline enquanto você tem uma assinatura Qobuz válida. Você não recebe uma licença para manter ou redistribuir o conteúdo. Se sua assinatura se tornar inválida, o QBZ removerá todo o conteúdo em cache após 30 dias.",
|
||||
"cachedTracks": "Faixas Offline",
|
||||
"cachedTracksDesc": "Faixas em cache para reprodução offline",
|
||||
"showInLibrary": "Mostrar na Biblioteca Local",
|
||||
|
||||
Reference in new issue
Block a user