mirror of
https://github.com/R0m1k3/xtremflow.git
synced 2026-10-21 22:18:04 +02:00
Multi-IPTV : déclarer l'IPTV active de façon synchrone dans AuthedHttp
AuthedHttp lisait l'IPTV dans localStorage, écrit par un écouteur Riverpod enregistré tardivement : après un changement d'IPTV l'en-tête X-Playlist-Id pouvait rester périmé. AuthedHttp.usePlaylist pose l'IPTV en mémoire dès la sélection et à chaque construction du tableau de bord. Auto NBA reçoit la playlist, recharge sa règle quand elle change et nomme l'IPTV sur laquelle les matchs sont enregistrés. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
a6322b57b8
commit
6368fd6ee2
6 files changed
+58
-7
No files matched your search
@@ -24,12 +24,30 @@ class AuthedHttp {
|
|||||||
return (t != null && t.isNotEmpty) ? t : null;
|
return (t != null && t.isNotEmpty) ? t : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// IPTV sélectionnée (persistée par `playlistPersistenceProvider`).
|
/// IPTV active, posée de façon synchrone par l'écran de sélection et le
|
||||||
|
/// tableau de bord (voir [usePlaylist]).
|
||||||
///
|
///
|
||||||
/// Envoyée à chaque appel : sans elle le serveur prenait la playlist la
|
/// POURQUOI : `localStorage['selected_playlist']` n'est écrit que par un
|
||||||
/// plus récente, et guide, enregistrements et Auto NBA visaient la
|
/// écouteur Riverpod enregistré tardivement ; après un changement d'IPTV
|
||||||
/// mauvaise IPTV quand plusieurs étaient configurées.
|
/// il restait périmé, le serveur retombait sur la playlist par défaut et
|
||||||
|
/// les deux IPTV partageaient la même règle Auto NBA et les mêmes
|
||||||
|
/// enregistrements.
|
||||||
|
static String? _currentPlaylistId;
|
||||||
|
|
||||||
|
/// Déclare l'IPTV active et la persiste immédiatement.
|
||||||
|
static void usePlaylist(String id, {Map<String, dynamic>? json}) {
|
||||||
|
if (id.isEmpty) return;
|
||||||
|
_currentPlaylistId = id;
|
||||||
|
if (json != null) {
|
||||||
|
try {
|
||||||
|
html.window.localStorage['selected_playlist'] = jsonEncode(json);
|
||||||
|
} catch (_) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// IPTV envoyée au serveur : celle déclarée, sinon la dernière persistée.
|
||||||
static String? get selectedPlaylistId {
|
static String? get selectedPlaylistId {
|
||||||
|
if (_currentPlaylistId != null) return _currentPlaylistId;
|
||||||
try {
|
try {
|
||||||
final stored = html.window.localStorage['selected_playlist'];
|
final stored = html.window.localStorage['selected_playlist'];
|
||||||
if (stored == null) return null;
|
if (stored == null) return null;
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import '../../auth/providers/auth_provider.dart';
|
import '../../auth/providers/auth_provider.dart';
|
||||||
|
import '../../../core/api/authed_http.dart';
|
||||||
import '../../../core/models/playlist_config.dart';
|
import '../../../core/models/playlist_config.dart';
|
||||||
import '../../../core/theme/app_colors.dart';
|
import '../../../core/theme/app_colors.dart';
|
||||||
import '../../../core/widgets/glass_container.dart';
|
import '../../../core/widgets/glass_container.dart';
|
||||||
@@ -43,6 +44,8 @@ class _DashboardScreenState extends ConsumerState<DashboardScreen> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
// Toutes les requêtes /api/* de ce tableau de bord visent cette IPTV.
|
||||||
|
AuthedHttp.usePlaylist(widget.playlist.id, json: widget.playlist.toJson());
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
backgroundColor: AppColors.background,
|
backgroundColor: AppColors.background,
|
||||||
body: Stack(
|
body: Stack(
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:google_fonts/google_fonts.dart';
|
import 'package:google_fonts/google_fonts.dart';
|
||||||
import '../../../core/services/playlist_api_service.dart';
|
import '../../../core/services/playlist_api_service.dart';
|
||||||
|
import '../../../core/api/authed_http.dart';
|
||||||
import '../../../core/models/playlist_config.dart';
|
import '../../../core/models/playlist_config.dart';
|
||||||
import '../../../core/theme/app_colors.dart';
|
import '../../../core/theme/app_colors.dart';
|
||||||
import '../../../core/theme/app_theme.dart';
|
import '../../../core/theme/app_theme.dart';
|
||||||
@@ -163,6 +164,10 @@ class PlaylistSelectionScreen extends ConsumerWidget {
|
|||||||
child: _PlaylistCard(
|
child: _PlaylistCard(
|
||||||
playlist: playlist,
|
playlist: playlist,
|
||||||
onTap: () {
|
onTap: () {
|
||||||
|
AuthedHttp.usePlaylist(
|
||||||
|
playlist.id,
|
||||||
|
json: playlist.toJson(),
|
||||||
|
);
|
||||||
selectPlaylist(ref, playlist);
|
selectPlaylist(ref, playlist);
|
||||||
context.go('/dashboard');
|
context.go('/dashboard');
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -52,6 +52,8 @@ class _RecordingsTabState extends State<RecordingsTab>
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
// Guide, enregistrements, season passes et Auto NBA visent cette IPTV.
|
||||||
|
AuthedHttp.usePlaylist(widget.playlist.id);
|
||||||
return Container(
|
return Container(
|
||||||
color: Colors.black,
|
color: Colors.black,
|
||||||
child: Column(
|
child: Column(
|
||||||
@@ -112,7 +114,7 @@ class _RecordingsTabState extends State<RecordingsTab>
|
|||||||
_EpgGuideView(playlist: widget.playlist),
|
_EpgGuideView(playlist: widget.playlist),
|
||||||
_RecordingsListView(playlist: widget.playlist),
|
_RecordingsListView(playlist: widget.playlist),
|
||||||
const _SeasonPassesView(),
|
const _SeasonPassesView(),
|
||||||
const _AutoNbaView(),
|
_AutoNbaView(playlist: widget.playlist),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -1752,7 +1754,9 @@ const _mois = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
class _AutoNbaView extends StatefulWidget {
|
class _AutoNbaView extends StatefulWidget {
|
||||||
const _AutoNbaView();
|
/// IPTV dont la règle est affichée et qui capture les matchs choisis.
|
||||||
|
final PlaylistConfig playlist;
|
||||||
|
const _AutoNbaView({required this.playlist});
|
||||||
@override
|
@override
|
||||||
State<_AutoNbaView> createState() => _AutoNbaViewState();
|
State<_AutoNbaView> createState() => _AutoNbaViewState();
|
||||||
}
|
}
|
||||||
@@ -1773,6 +1777,21 @@ class _AutoNbaViewState extends State<_AutoNbaView> {
|
|||||||
_load();
|
_load();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void didUpdateWidget(covariant _AutoNbaView oldWidget) {
|
||||||
|
super.didUpdateWidget(oldWidget);
|
||||||
|
// Autre IPTV : autre règle, autres matchs choisis.
|
||||||
|
if (oldWidget.playlist.id != widget.playlist.id) {
|
||||||
|
AuthedHttp.usePlaylist(widget.playlist.id);
|
||||||
|
setState(() {
|
||||||
|
_rule = null;
|
||||||
|
_games = [];
|
||||||
|
_diag = null;
|
||||||
|
});
|
||||||
|
_load();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> _load() async {
|
Future<void> _load() async {
|
||||||
setState(() {
|
setState(() {
|
||||||
_isLoading = _rule == null;
|
_isLoading = _rule == null;
|
||||||
@@ -2427,7 +2446,8 @@ class _AutoNbaViewState extends State<_AutoNbaView> {
|
|||||||
children: [
|
children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Text(
|
child: Text(
|
||||||
'Matchs NBA en direct sur beIN Sports FR',
|
'Matchs NBA en direct sur beIN Sports FR · '
|
||||||
|
'enregistrés sur « ${widget.playlist.name} »',
|
||||||
style: GoogleFonts.fraunces(
|
style: GoogleFonts.fraunces(
|
||||||
color: AppColors.onSurface54,
|
color: AppColors.onSurface54,
|
||||||
fontSize: 13,
|
fontSize: 13,
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
import '../../../../core/api/authed_http.dart';
|
||||||
import '../../../../core/models/playlist_config.dart';
|
import '../../../../core/models/playlist_config.dart';
|
||||||
import '../../../widgets/mobile_scaffold.dart';
|
import '../../../widgets/mobile_scaffold.dart';
|
||||||
import '../../../theme/mobile_theme.dart';
|
import '../../../theme/mobile_theme.dart';
|
||||||
@@ -26,6 +27,8 @@ class _MobileDashboardScreenState extends ConsumerState<MobileDashboardScreen> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
// Toutes les requêtes /api/* de ce tableau de bord visent cette IPTV.
|
||||||
|
AuthedHttp.usePlaylist(widget.playlist.id, json: widget.playlist.toJson());
|
||||||
return Theme(
|
return Theme(
|
||||||
data: MobileTheme.darkTheme,
|
data: MobileTheme.darkTheme,
|
||||||
child: MobileScaffold(
|
child: MobileScaffold(
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import 'package:go_router/go_router.dart';
|
|||||||
import '../../../../features/iptv/providers/xtream_provider.dart';
|
import '../../../../features/iptv/providers/xtream_provider.dart';
|
||||||
import 'package:google_fonts/google_fonts.dart';
|
import 'package:google_fonts/google_fonts.dart';
|
||||||
import '../../../../core/services/playlist_api_service.dart';
|
import '../../../../core/services/playlist_api_service.dart';
|
||||||
|
import '../../../../core/api/authed_http.dart';
|
||||||
import '../../../../core/models/playlist_config.dart';
|
import '../../../../core/models/playlist_config.dart';
|
||||||
import '../../../../features/auth/providers/auth_provider.dart';
|
import '../../../../features/auth/providers/auth_provider.dart';
|
||||||
import '../../../../core/theme/app_colors.dart';
|
import '../../../../core/theme/app_colors.dart';
|
||||||
@@ -108,6 +109,7 @@ class MobilePlaylistSelectionScreen extends ConsumerWidget {
|
|||||||
return _MobilePlaylistCard(
|
return _MobilePlaylistCard(
|
||||||
playlist: playlist,
|
playlist: playlist,
|
||||||
onTap: () {
|
onTap: () {
|
||||||
|
AuthedHttp.usePlaylist(playlist.id, json: playlist.toJson());
|
||||||
// Choix + persistance dans le même geste : l'en-tête
|
// Choix + persistance dans le même geste : l'en-tête
|
||||||
// X-Playlist-Id doit partir dès la première requête.
|
// X-Playlist-Id doit partir dès la première requête.
|
||||||
selectPlaylist(ref, playlist);
|
selectPlaylist(ref, playlist);
|
||||||
|
|||||||
Reference in new issue
Block a user