Multi-IPTV : le quota du navigateur ne doit plus écraser le choix d'IPTV

Console du navigateur, à deux playlists :

  QuotaExceededError: Failed to execute 'setItem' on 'Storage':
  Setting the value of 'xtream_cache_<id>_live_channels' exceeded the quota.

Les catalogues mis en cache (chaînes, films, séries), plusieurs Mo par
IPTV, saturent le stockage. L'erreur était avalée, mais le stockage
restait plein : toute écriture suivante échouait, dont celle de
localStorage['selected_playlist']. L'entrée gardait l'IPTV précédente,
AuthedHttp envoyait un X-Playlist-Id périmé, le serveur résolvait la même
playlist pour les deux, et Auto NBA n'avait qu'une règle commune —
désactivée d'un côté, désactivée de l'autre.

- persistSelectedPlaylist purge les catalogues et réessaye avant de
  renoncer ; elle ne lève plus jamais, sans quoi le clic de sélection
  n'aurait plus posé l'état du tout.
- _saveToLocalCache purge les catalogues des autres IPTV puis réessaye :
  un catalogue se recharge depuis le serveur, le choix d'IPTV ne se
  devine pas.

Non vérifié localement : le SDK Flutter de cette machine (3.10.3) est
antérieur au ^3.13.0 exigé par le projet.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
MichaelandClaude Opus 5 committed 2026-10-19 14:15:19 +02:00
1 parent 5169d0843a
commit 52504d4e1a
2 files changed
+61 -8

No files matched your search

@@ -57,14 +57,48 @@ final selectedPlaylistProvider = StateProvider<PlaylistConfig?>((ref) {
return null;
});
/// Préfixe des caches de catalogue (chaînes, films, séries) par IPTV.
const xtreamCachePrefix = 'xtream_cache_';
/// Vide les caches de catalogue pour rendre de la place.
///
/// Ces caches pèsent plusieurs mégaoctets par IPTV et saturent le quota du
/// navigateur ; ils se reconstruisent au prochain chargement.
void clearXtreamCaches() {
final keys = html.window.localStorage.keys
.where((k) => k.startsWith(xtreamCachePrefix))
.toList();
for (final key in keys) {
html.window.localStorage.remove(key);
}
}
/// Écrit l'IPTV courante là où `AuthedHttp` la relit pour poser l'en-tête
/// `X-Playlist-Id`.
///
/// Quota saturé, `setItem` lève : les caches de catalogue remplissent le
/// stockage et faisaient échouer cette écriture-ci, minuscule mais décisive.
/// L'entrée restait sur l'IPTV précédente, l'en-tête désignait la mauvaise
/// playlist, et les deux IPTV retombaient sur la même. Le catalogue cède donc
/// la place, et un échec résiduel ne doit jamais empêcher le changement
/// d'IPTV.
void persistSelectedPlaylist(PlaylistConfig? playlist) {
if (playlist != null) {
html.window.localStorage['selected_playlist'] =
jsonEncode(playlist.toJson());
} else {
html.window.localStorage.remove('selected_playlist');
if (playlist == null) {
try {
html.window.localStorage.remove('selected_playlist');
} catch (_) {}
return;
}
final value = jsonEncode(playlist.toJson());
try {
html.window.localStorage['selected_playlist'] = value;
} catch (_) {
try {
clearXtreamCaches();
html.window.localStorage['selected_playlist'] = value;
} catch (e) {
debugPrint('Impossible de persister l\'IPTV choisie: $e');
}
}
}
+22 -3
View File
@@ -333,12 +333,31 @@ class XtreamService {
}
}
/// Met le catalogue en cache, en cédant la place plutôt qu'en saturant.
///
/// Un catalogue pèse plusieurs mégaoctets et il y en a un par IPTV : à deux
/// playlists, le quota du navigateur était atteint. L'erreur était avalée
/// ici, mais le stockage restait plein — et toute autre écriture échouait
/// à son tour, à commencer par celle de l'IPTV sélectionnée, bien plus
/// importante que ce cache. On purge donc les catalogues des autres IPTV
/// avant de réessayer, puis on renonce : ils se rechargent depuis le
/// serveur, le choix d'IPTV, lui, ne se devine pas.
void _saveToLocalCache(String key, Map<String, dynamic> data) {
if (!kIsWeb) return;
final storageKey = 'xtream_cache_${_currentPlaylist?.id}_$key';
final jsonStr = jsonEncode(data);
try {
final jsonStr = jsonEncode(data);
html.window.localStorage['xtream_cache_${_currentPlaylist?.id}_$key'] =
jsonStr;
html.window.localStorage[storageKey] = jsonStr;
return;
} catch (_) {}
try {
final stale = html.window.localStorage.keys
.where((k) => k.startsWith('xtream_cache_') && k != storageKey)
.toList();
for (final k in stale) {
html.window.localStorage.remove(k);
}
html.window.localStorage[storageKey] = jsonStr;
} catch (e) {
debugPrint('Error saving to local cache ($key): $e');
}