diff --git a/lib/features/iptv/providers/xtream_provider.dart b/lib/features/iptv/providers/xtream_provider.dart index db73f02..1616345 100644 --- a/lib/features/iptv/providers/xtream_provider.dart +++ b/lib/features/iptv/providers/xtream_provider.dart @@ -57,14 +57,48 @@ final selectedPlaylistProvider = StateProvider((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'); + } } } diff --git a/lib/features/iptv/services/xtream_service.dart b/lib/features/iptv/services/xtream_service.dart index 174c274..5aa4a9d 100644 --- a/lib/features/iptv/services/xtream_service.dart +++ b/lib/features/iptv/services/xtream_service.dart @@ -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 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'); }