From e6281ff912274cb2398e4a9800d61d0f56b822bc Mon Sep 17 00:00:00 2001 From: Michael SCHAL Date: Tue, 9 Dec 2025 18:36:44 +0100 Subject: [PATCH] feat: Add mobile-specific Riverpod providers for Xtream services, including live channels, movies, series, and series info. --- .../iptv/screens/mobile_playlist_screen.dart | 4 ++ .../screens/mobile_series_detail_screen.dart | 2 +- .../iptv/screens/native_player_screen.dart | 37 ++++++++++++++----- .../iptv/widgets/mobile_movies_tab.dart | 4 +- .../iptv/widgets/mobile_series_tab.dart | 4 +- .../providers/mobile_xtream_providers.dart | 16 +++++--- 6 files changed, 47 insertions(+), 20 deletions(-) diff --git a/lib/mobile/features/iptv/screens/mobile_playlist_screen.dart b/lib/mobile/features/iptv/screens/mobile_playlist_screen.dart index c4bd907..1552b47 100644 --- a/lib/mobile/features/iptv/screens/mobile_playlist_screen.dart +++ b/lib/mobile/features/iptv/screens/mobile_playlist_screen.dart @@ -493,6 +493,10 @@ class _PlaylistCard extends StatelessWidget { Icons.chevron_right, color: AppColors.textTertiary, ), + IconButton( + icon: const Icon(Icons.more_vert, color: AppColors.textSecondary), + onPressed: onLongPress, // Re-use the options sheet logic + ), ], ), ), diff --git a/lib/mobile/features/iptv/screens/mobile_series_detail_screen.dart b/lib/mobile/features/iptv/screens/mobile_series_detail_screen.dart index 30372a5..3649f42 100644 --- a/lib/mobile/features/iptv/screens/mobile_series_detail_screen.dart +++ b/lib/mobile/features/iptv/screens/mobile_series_detail_screen.dart @@ -51,7 +51,7 @@ class _MobileSeriesDetailScreenState extends ConsumerState { bool _isLoading = true; String? _errorMessage; late int _currentIndex; - late XtreamServiceMobile _xtreamService; + XtreamServiceMobile? _xtreamService; // Make nullable to handle async init @override void initState() { super.initState(); _currentIndex = widget.initialIndex; - _xtreamService = XtreamServiceMobile(); - _xtreamService.setPlaylist(widget.playlist); // Lock to landscape for video playback SystemChrome.setPreferredOrientations([ @@ -59,11 +58,31 @@ class _NativePlayerScreenState extends ConsumerState { ]); SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersiveSticky); - _initializePlayer(); + // Initialize service and then player + _initializeServiceAndPlayer(); + } + + Future _initializeServiceAndPlayer() async { + try { + final dir = await getApplicationDocumentsDirectory(); + _xtreamService = XtreamServiceMobile(dir.path); + _xtreamService!.setPlaylist(widget.playlist); + + await _initializePlayer(); + } catch (e) { + if (mounted) { + setState(() { + _isLoading = false; + _errorMessage = "Service Error: $e"; + }); + } + } } Future _initializePlayer() async { try { + if (_xtreamService == null) return; // Should not happen if called correctly + setState(() { _isLoading = true; _errorMessage = null; @@ -82,13 +101,13 @@ class _NativePlayerScreenState extends ConsumerState { if (widget.streamType == StreamType.live) { // Live TV - use HLS format for best mobile compatibility - streamUrl = _xtreamService.getLiveStreamUrl(currentStreamId); + streamUrl = _xtreamService!.getLiveStreamUrl(currentStreamId); } else if (widget.streamType == StreamType.vod) { // Movies - streamUrl = _xtreamService.getVodStreamUrl(currentStreamId, widget.containerExtension); + streamUrl = _xtreamService!.getVodStreamUrl(currentStreamId, widget.containerExtension); } else { // Series - streamUrl = _xtreamService.getSeriesStreamUrl(currentStreamId, widget.containerExtension); + streamUrl = _xtreamService!.getSeriesStreamUrl(currentStreamId, widget.containerExtension); } debugPrint('NativePlayer: Loading stream: $streamUrl'); @@ -183,13 +202,13 @@ class _NativePlayerScreenState extends ConsumerState { setState(() { _currentIndex = index; }); - _initializePlayer(); + _initializeServiceAndPlayer(); // Re-trigger init purely for player part } @override void dispose() { _disposeControllers(); - _xtreamService.dispose(); + _xtreamService?.dispose(); // Restore normal orientation SystemChrome.setPreferredOrientations([ diff --git a/lib/mobile/features/iptv/widgets/mobile_movies_tab.dart b/lib/mobile/features/iptv/widgets/mobile_movies_tab.dart index 7d3136c..a0bb0b9 100644 --- a/lib/mobile/features/iptv/widgets/mobile_movies_tab.dart +++ b/lib/mobile/features/iptv/widgets/mobile_movies_tab.dart @@ -77,7 +77,7 @@ class _MobileMoviesTabState extends ConsumerState { setState(() => _isSearching = true); try { - final service = ref.read(mobileXtreamServiceProvider(widget.playlist)); + final service = await ref.read(mobileXtreamServiceProvider(widget.playlist).future); final results = await service.searchMovies(query); if (mounted && _searchQuery == query) { @@ -98,7 +98,7 @@ class _MobileMoviesTabState extends ConsumerState { setState(() => _isLoading = true); try { - final service = ref.read(mobileXtreamServiceProvider(widget.playlist)); + final service = await ref.read(mobileXtreamServiceProvider(widget.playlist).future); final newMovies = await service.getMoviesPaginated( offset: _currentOffset, limit: _pageSize, diff --git a/lib/mobile/features/iptv/widgets/mobile_series_tab.dart b/lib/mobile/features/iptv/widgets/mobile_series_tab.dart index f6ef673..4519d28 100644 --- a/lib/mobile/features/iptv/widgets/mobile_series_tab.dart +++ b/lib/mobile/features/iptv/widgets/mobile_series_tab.dart @@ -77,7 +77,7 @@ class _MobileSeriesTabState extends ConsumerState { setState(() => _isSearching = true); try { - final service = ref.read(mobileXtreamServiceProvider(widget.playlist)); + final service = await ref.read(mobileXtreamServiceProvider(widget.playlist).future); final results = await service.searchSeries(query); if (mounted && _searchQuery == query) { @@ -98,7 +98,7 @@ class _MobileSeriesTabState extends ConsumerState { setState(() => _isLoading = true); try { - final service = ref.read(mobileXtreamServiceProvider(widget.playlist)); + final service = await ref.read(mobileXtreamServiceProvider(widget.playlist).future); final newSeries = await service.getSeriesPaginated( offset: _currentOffset, limit: _pageSize, diff --git a/lib/mobile/providers/mobile_xtream_providers.dart b/lib/mobile/providers/mobile_xtream_providers.dart index 4fbbb7a..4825b4d 100644 --- a/lib/mobile/providers/mobile_xtream_providers.dart +++ b/lib/mobile/providers/mobile_xtream_providers.dart @@ -7,27 +7,31 @@ import '../../features/iptv/models/xtream_models.dart'; export '../../features/iptv/models/xtream_models.dart'; /// Mobile-specific Xtream service provider (no dart:html dependency) -final mobileXtreamServiceProvider = Provider.family((ref, playlist) { - final service = XtreamServiceMobile(); +import 'package:path_provider/path_provider.dart'; + +/// Mobile-specific Xtream service provider (no dart:html dependency) +final mobileXtreamServiceProvider = FutureProvider.family((ref, playlist) async { + final dir = await getApplicationDocumentsDirectory(); + final service = XtreamServiceMobile(dir.path); service.setPlaylist(playlist); return service; }); /// Mobile-specific live channels provider final mobileLiveChannelsProvider = FutureProvider.family>, PlaylistConfig>((ref, playlist) async { - final service = ref.read(mobileXtreamServiceProvider(playlist)); + final service = await ref.watch(mobileXtreamServiceProvider(playlist).future); return service.getLiveChannels(); }); /// Mobile-specific movies pagination provider final mobileMoviesProvider = FutureProvider.family, PlaylistConfig>((ref, playlist) async { - final service = ref.read(mobileXtreamServiceProvider(playlist)); + final service = await ref.watch(mobileXtreamServiceProvider(playlist).future); return service.getMoviesPaginated(offset: 0, limit: 100); }); /// Mobile-specific series pagination provider final mobileSeriesProvider = FutureProvider.family, PlaylistConfig>((ref, playlist) async { - final service = ref.read(mobileXtreamServiceProvider(playlist)); + final service = await ref.watch(mobileXtreamServiceProvider(playlist).future); return service.getSeriesPaginated(offset: 0, limit: 100); }); @@ -56,6 +60,6 @@ class SeriesInfoRequest { } final mobileSeriesInfoByPlaylistProvider = FutureProvider.family((ref, request) async { - final service = ref.read(mobileXtreamServiceProvider(request.playlist)); + final service = await ref.watch(mobileXtreamServiceProvider(request.playlist).future); return service.getSeriesInfo(request.seriesId); });