From d1a481bf933c1322c4d87723d5959299166638ac Mon Sep 17 00:00:00 2001 From: Michael SCHAL Date: Sat, 6 Dec 2025 09:45:22 +0100 Subject: [PATCH] feat: Implement Xtream IPTV integration including providers, a dedicated player screen, EPG overlay, and live TV channel browsing. --- .../iptv/providers/xtream_provider.dart | 25 +++++++++++++++++++ lib/features/iptv/screens/player_screen.dart | 10 ++++++-- lib/features/iptv/widgets/epg_overlay.dart | 7 +++++- lib/features/iptv/widgets/live_tv_tab.dart | 1 + 4 files changed, 40 insertions(+), 3 deletions(-) diff --git a/lib/features/iptv/providers/xtream_provider.dart b/lib/features/iptv/providers/xtream_provider.dart index f92f72d..632f38f 100644 --- a/lib/features/iptv/providers/xtream_provider.dart +++ b/lib/features/iptv/providers/xtream_provider.dart @@ -61,3 +61,28 @@ final epgProvider = FutureProvider.family.autoDispose((ref, String streamId) asy final service = ref.watch(activeXtreamServiceProvider); return await service.getShortEpg(streamId); }); + +/// EPG request key for family provider (combines playlist and streamId) +class EpgRequestKey { + final PlaylistConfig playlist; + final String streamId; + + const EpgRequestKey({required this.playlist, required this.streamId}); + + @override + bool operator ==(Object other) => + identical(this, other) || + other is EpgRequestKey && + runtimeType == other.runtimeType && + playlist == other.playlist && + streamId == other.streamId; + + @override + int get hashCode => playlist.hashCode ^ streamId.hashCode; +} + +/// Provider for EPG with playlist context +final epgByPlaylistProvider = FutureProvider.family.autoDispose((ref, EpgRequestKey key) async { + final service = ref.watch(xtreamServiceProvider(key.playlist)); + return await service.getShortEpg(key.streamId); +}); diff --git a/lib/features/iptv/screens/player_screen.dart b/lib/features/iptv/screens/player_screen.dart index 2164666..8fc4b99 100644 --- a/lib/features/iptv/screens/player_screen.dart +++ b/lib/features/iptv/screens/player_screen.dart @@ -5,6 +5,7 @@ import 'package:chewie/chewie.dart'; import '../providers/xtream_provider.dart'; import '../widgets/epg_overlay.dart'; import '../../../core/models/iptv_models.dart'; +import '../../../core/models/playlist_config.dart'; /// Stream type enum for player enum StreamType { live, vod, series } @@ -15,11 +16,13 @@ class PlayerScreen extends ConsumerStatefulWidget { final String title; final StreamType streamType; final String containerExtension; + final PlaylistConfig playlist; const PlayerScreen({ super.key, required this.streamId, required this.title, + required this.playlist, this.streamType = StreamType.live, this.containerExtension = 'm3u8', }); @@ -42,7 +45,7 @@ class _PlayerScreenState extends ConsumerState { Future _initializePlayer() async { try { - final xtreamService = ref.read(activeXtreamServiceProvider); + final xtreamService = ref.read(xtreamServiceProvider(widget.playlist)); // Generate stream URL based on type late String streamUrl; @@ -177,7 +180,10 @@ class _PlayerScreenState extends ConsumerState { bottom: 80, left: 0, right: 0, - child: EpgOverlay(streamId: widget.streamId), + child: EpgOverlay( + streamId: widget.streamId, + playlist: widget.playlist, + ), ), ], ), diff --git a/lib/features/iptv/widgets/epg_overlay.dart b/lib/features/iptv/widgets/epg_overlay.dart index d8b3eb9..0599398 100644 --- a/lib/features/iptv/widgets/epg_overlay.dart +++ b/lib/features/iptv/widgets/epg_overlay.dart @@ -1,19 +1,24 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import '../../../core/models/playlist_config.dart'; import '../providers/xtream_provider.dart'; /// EPG overlay widget showing "Now" and "Next" programs class EpgOverlay extends ConsumerWidget { final String streamId; + final PlaylistConfig playlist; const EpgOverlay({ super.key, required this.streamId, + required this.playlist, }); @override Widget build(BuildContext context, WidgetRef ref) { - final epgAsync = ref.watch(epgProvider(streamId)); + final epgAsync = ref.watch(epgByPlaylistProvider( + EpgRequestKey(playlist: playlist, streamId: streamId), + )); return epgAsync.when( data: (epgEntries) { diff --git a/lib/features/iptv/widgets/live_tv_tab.dart b/lib/features/iptv/widgets/live_tv_tab.dart index e738af7..3e9a84c 100644 --- a/lib/features/iptv/widgets/live_tv_tab.dart +++ b/lib/features/iptv/widgets/live_tv_tab.dart @@ -255,6 +255,7 @@ class _LiveTVTabState extends ConsumerState builder: (context) => PlayerScreen( streamId: channel.streamId, title: channel.name, + playlist: widget.playlist, streamType: StreamType.live, ), ),