feat: Add mobile-specific Riverpod providers for Xtream services, including live channels, movies, series, and series info.

This commit is contained in:
Michael committed 2025-12-09 18:36:44 +01:00
1 parent 51f73d102d
commit e6281ff912
6 files changed
+47 -20

No files matched your search

@@ -493,6 +493,10 @@ class _PlaylistCard extends StatelessWidget {
Icons.chevron_right, Icons.chevron_right,
color: AppColors.textTertiary, color: AppColors.textTertiary,
), ),
IconButton(
icon: const Icon(Icons.more_vert, color: AppColors.textSecondary),
onPressed: onLongPress, // Re-use the options sheet logic
),
], ],
), ),
), ),
@@ -51,7 +51,7 @@ class _MobileSeriesDetailScreenState extends ConsumerState<MobileSeriesDetailScr
_error = null; _error = null;
}); });
final service = ref.read(mobileXtreamServiceProvider(widget.playlist)); final service = await ref.read(mobileXtreamServiceProvider(widget.playlist).future);
final info = await service.getSeriesInfo(widget.series.seriesId.toString()); final info = await service.getSeriesInfo(widget.series.seriesId.toString());
if (mounted) { if (mounted) {
@@ -3,6 +3,7 @@ import 'package:flutter/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:video_player/video_player.dart'; import 'package:video_player/video_player.dart';
import 'package:chewie/chewie.dart'; import 'package:chewie/chewie.dart';
import 'package:path_provider/path_provider.dart';
import '../../../../core/models/playlist_config.dart'; import '../../../../core/models/playlist_config.dart';
import '../../../../core/models/iptv_models.dart'; import '../../../../core/models/iptv_models.dart';
import '../../../../core/theme/app_colors.dart'; import '../../../../core/theme/app_colors.dart';
@@ -43,14 +44,12 @@ class _NativePlayerScreenState extends ConsumerState<NativePlayerScreen> {
bool _isLoading = true; bool _isLoading = true;
String? _errorMessage; String? _errorMessage;
late int _currentIndex; late int _currentIndex;
late XtreamServiceMobile _xtreamService; XtreamServiceMobile? _xtreamService; // Make nullable to handle async init
@override @override
void initState() { void initState() {
super.initState(); super.initState();
_currentIndex = widget.initialIndex; _currentIndex = widget.initialIndex;
_xtreamService = XtreamServiceMobile();
_xtreamService.setPlaylist(widget.playlist);
// Lock to landscape for video playback // Lock to landscape for video playback
SystemChrome.setPreferredOrientations([ SystemChrome.setPreferredOrientations([
@@ -59,11 +58,31 @@ class _NativePlayerScreenState extends ConsumerState<NativePlayerScreen> {
]); ]);
SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersiveSticky); SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersiveSticky);
_initializePlayer(); // Initialize service and then player
_initializeServiceAndPlayer();
}
Future<void> _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<void> _initializePlayer() async { Future<void> _initializePlayer() async {
try { try {
if (_xtreamService == null) return; // Should not happen if called correctly
setState(() { setState(() {
_isLoading = true; _isLoading = true;
_errorMessage = null; _errorMessage = null;
@@ -82,13 +101,13 @@ class _NativePlayerScreenState extends ConsumerState<NativePlayerScreen> {
if (widget.streamType == StreamType.live) { if (widget.streamType == StreamType.live) {
// Live TV - use HLS format for best mobile compatibility // Live TV - use HLS format for best mobile compatibility
streamUrl = _xtreamService.getLiveStreamUrl(currentStreamId); streamUrl = _xtreamService!.getLiveStreamUrl(currentStreamId);
} else if (widget.streamType == StreamType.vod) { } else if (widget.streamType == StreamType.vod) {
// Movies // Movies
streamUrl = _xtreamService.getVodStreamUrl(currentStreamId, widget.containerExtension); streamUrl = _xtreamService!.getVodStreamUrl(currentStreamId, widget.containerExtension);
} else { } else {
// Series // Series
streamUrl = _xtreamService.getSeriesStreamUrl(currentStreamId, widget.containerExtension); streamUrl = _xtreamService!.getSeriesStreamUrl(currentStreamId, widget.containerExtension);
} }
debugPrint('NativePlayer: Loading stream: $streamUrl'); debugPrint('NativePlayer: Loading stream: $streamUrl');
@@ -183,13 +202,13 @@ class _NativePlayerScreenState extends ConsumerState<NativePlayerScreen> {
setState(() { setState(() {
_currentIndex = index; _currentIndex = index;
}); });
_initializePlayer(); _initializeServiceAndPlayer(); // Re-trigger init purely for player part
} }
@override @override
void dispose() { void dispose() {
_disposeControllers(); _disposeControllers();
_xtreamService.dispose(); _xtreamService?.dispose();
// Restore normal orientation // Restore normal orientation
SystemChrome.setPreferredOrientations([ SystemChrome.setPreferredOrientations([
@@ -77,7 +77,7 @@ class _MobileMoviesTabState extends ConsumerState<MobileMoviesTab> {
setState(() => _isSearching = true); setState(() => _isSearching = true);
try { try {
final service = ref.read(mobileXtreamServiceProvider(widget.playlist)); final service = await ref.read(mobileXtreamServiceProvider(widget.playlist).future);
final results = await service.searchMovies(query); final results = await service.searchMovies(query);
if (mounted && _searchQuery == query) { if (mounted && _searchQuery == query) {
@@ -98,7 +98,7 @@ class _MobileMoviesTabState extends ConsumerState<MobileMoviesTab> {
setState(() => _isLoading = true); setState(() => _isLoading = true);
try { try {
final service = ref.read(mobileXtreamServiceProvider(widget.playlist)); final service = await ref.read(mobileXtreamServiceProvider(widget.playlist).future);
final newMovies = await service.getMoviesPaginated( final newMovies = await service.getMoviesPaginated(
offset: _currentOffset, offset: _currentOffset,
limit: _pageSize, limit: _pageSize,
@@ -77,7 +77,7 @@ class _MobileSeriesTabState extends ConsumerState<MobileSeriesTab> {
setState(() => _isSearching = true); setState(() => _isSearching = true);
try { try {
final service = ref.read(mobileXtreamServiceProvider(widget.playlist)); final service = await ref.read(mobileXtreamServiceProvider(widget.playlist).future);
final results = await service.searchSeries(query); final results = await service.searchSeries(query);
if (mounted && _searchQuery == query) { if (mounted && _searchQuery == query) {
@@ -98,7 +98,7 @@ class _MobileSeriesTabState extends ConsumerState<MobileSeriesTab> {
setState(() => _isLoading = true); setState(() => _isLoading = true);
try { try {
final service = ref.read(mobileXtreamServiceProvider(widget.playlist)); final service = await ref.read(mobileXtreamServiceProvider(widget.playlist).future);
final newSeries = await service.getSeriesPaginated( final newSeries = await service.getSeriesPaginated(
offset: _currentOffset, offset: _currentOffset,
limit: _pageSize, limit: _pageSize,
@@ -7,27 +7,31 @@ import '../../features/iptv/models/xtream_models.dart';
export '../../features/iptv/models/xtream_models.dart'; export '../../features/iptv/models/xtream_models.dart';
/// Mobile-specific Xtream service provider (no dart:html dependency) /// Mobile-specific Xtream service provider (no dart:html dependency)
final mobileXtreamServiceProvider = Provider.family<XtreamServiceMobile, PlaylistConfig>((ref, playlist) { import 'package:path_provider/path_provider.dart';
final service = XtreamServiceMobile();
/// Mobile-specific Xtream service provider (no dart:html dependency)
final mobileXtreamServiceProvider = FutureProvider.family<XtreamServiceMobile, PlaylistConfig>((ref, playlist) async {
final dir = await getApplicationDocumentsDirectory();
final service = XtreamServiceMobile(dir.path);
service.setPlaylist(playlist); service.setPlaylist(playlist);
return service; return service;
}); });
/// Mobile-specific live channels provider /// Mobile-specific live channels provider
final mobileLiveChannelsProvider = FutureProvider.family<Map<String, List<Channel>>, PlaylistConfig>((ref, playlist) async { final mobileLiveChannelsProvider = FutureProvider.family<Map<String, List<Channel>>, PlaylistConfig>((ref, playlist) async {
final service = ref.read(mobileXtreamServiceProvider(playlist)); final service = await ref.watch(mobileXtreamServiceProvider(playlist).future);
return service.getLiveChannels(); return service.getLiveChannels();
}); });
/// Mobile-specific movies pagination provider /// Mobile-specific movies pagination provider
final mobileMoviesProvider = FutureProvider.family<List<Movie>, PlaylistConfig>((ref, playlist) async { final mobileMoviesProvider = FutureProvider.family<List<Movie>, 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); return service.getMoviesPaginated(offset: 0, limit: 100);
}); });
/// Mobile-specific series pagination provider /// Mobile-specific series pagination provider
final mobileSeriesProvider = FutureProvider.family<List<Series>, PlaylistConfig>((ref, playlist) async { final mobileSeriesProvider = FutureProvider.family<List<Series>, 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); return service.getSeriesPaginated(offset: 0, limit: 100);
}); });
@@ -56,6 +60,6 @@ class SeriesInfoRequest {
} }
final mobileSeriesInfoByPlaylistProvider = FutureProvider.family<SeriesInfo, SeriesInfoRequest>((ref, request) async { final mobileSeriesInfoByPlaylistProvider = FutureProvider.family<SeriesInfo, SeriesInfoRequest>((ref, request) async {
final service = ref.read(mobileXtreamServiceProvider(request.playlist)); final service = await ref.watch(mobileXtreamServiceProvider(request.playlist).future);
return service.getSeriesInfo(request.seriesId); return service.getSeriesInfo(request.seriesId);
}); });