feat: add new UI tabs for movies and series, including mobile-specific versions with search and pagination.

This commit is contained in:
Michael committed 2025-12-08 20:43:41 +01:00
1 parent d48d25f65e
commit 6a71f58a36
29 files changed
+78 -78

No files matched your search

+1 -1
View File
@@ -410,7 +410,7 @@ class _UsersTab extends ConsumerWidget {
child: Icon(
user.isAdmin ? Icons.admin_panel_settings : Icons.person,
color: user.isAdmin ? Colors.black : AppColors.textPrimary,
size: 24
size: 24,
),
),
const SizedBox(width: 16),
@@ -48,7 +48,7 @@ class PlaybackPositionsNotifier extends StateNotifier<PlaybackPositions> {
if (positionsJson != null) {
final decoded = jsonDecode(positionsJson) as Map<String, dynamic>;
final positions = decoded.map((key, value) =>
MapEntry(key, (value as num).toDouble())
MapEntry(key, (value as num).toDouble()),
);
state = PlaybackPositions(positions: positions);
@@ -210,7 +210,7 @@ class _DashboardScreenState extends ConsumerState<DashboardScreen> with SingleTi
fontWeight: FontWeight.w300,
fontSize: 16,
color: Colors.white.withOpacity(0.8),
height: 0.8
height: 0.8,
),
),
],
+3 -3
View File
@@ -95,7 +95,7 @@ class _PlayerScreenState extends ConsumerState<PlayerScreen> {
_totalDuration = duration > 0 ? duration : 1;
});
ref.read(playbackPositionsProvider.notifier).savePosition(
_contentId, currentTime, duration
_contentId, currentTime, duration,
);
} else if (type == 'playback_status') {
final status = data['status'];
@@ -674,7 +674,7 @@ class _PlayerScreenState extends ConsumerState<PlayerScreen> {
appBar: _showControls ? AppBar(
title: Text(widget.channels != null && widget.channels!.isNotEmpty
? widget.channels![_currentIndex].name
: widget.title),
: widget.title,),
backgroundColor: Colors.transparent,
foregroundColor: Colors.white,
elevation: 0,
@@ -775,7 +775,7 @@ class _PlayerScreenState extends ConsumerState<PlayerScreen> {
? Icons.volume_off
: _volume < 0.5 ? Icons.volume_down : Icons.volume_up,
color: Colors.white,
size: 20
size: 20,
),
onPressed: _toggleMute,
tooltip: _volume == 0 ? 'Unmute' : 'Mute',
@@ -180,7 +180,7 @@ class _PlaylistCardState extends State<_PlaylistCard> {
color: Colors.black.withOpacity(0.3),
blurRadius: 20,
offset: const Offset(0, 10),
)
),
] : [],
),
padding: const EdgeInsets.all(24),
@@ -19,7 +19,7 @@ class XtreamService {
_dio = Dio(BaseOptions(
connectTimeout: const Duration(seconds: 15),
receiveTimeout: const Duration(seconds: 15),
));
),);
// Setup caching for API responses
_cacheOptions = CacheOptions(
+1 -1
View File
@@ -50,7 +50,7 @@ class _ChannelCardState extends ConsumerState<ChannelCard>
super.initState();
_pulseController = AnimationController(
vsync: this,
duration: const Duration(milliseconds: 1000)
duration: const Duration(milliseconds: 1000),
)..repeat(reverse: true);
// Fetch EPG immediately when card is built
+1 -1
View File
@@ -20,7 +20,7 @@ class EpgOverlay extends ConsumerWidget {
Widget build(BuildContext context, WidgetRef ref) {
final epgAsync = ref.watch(epgByPlaylistProvider(
EpgRequestKey(playlist: playlist, streamId: streamId),
));
),);
return epgAsync.when(
data: (epgEntries) {
+1 -1
View File
@@ -165,7 +165,7 @@ class _LiveTVTabState extends ConsumerState<LiveTVTab>
if (_searchQuery.isNotEmpty)
GestureDetector(
onTap: () => _searchController.clear(),
child: const Icon(Icons.close, size: 16, color: AppColors.textSecondary)
child: const Icon(Icons.close, size: 16, color: AppColors.textSecondary),
),
],
),
+2 -2
View File
@@ -141,8 +141,8 @@ class _MoviesTabState extends ConsumerState<MoviesTab> {
}
/// Proxy HTTP images through backend to avoid CORS/mixed-content issues
String? _getProxiedImageUrl(String? originalUrl) {
if (originalUrl == null || originalUrl.isEmpty) return null;
String _getProxiedImageUrl(String? originalUrl) {
if (originalUrl == null || originalUrl.isEmpty) return '';
if (originalUrl.startsWith('http://')) {
return '/api/xtream/$originalUrl';
}
+2 -2
View File
@@ -141,8 +141,8 @@ class _SeriesTabState extends ConsumerState<SeriesTab> {
}
/// Proxy HTTP images through backend to avoid CORS/mixed-content issues
String? _getProxiedImageUrl(String? originalUrl) {
if (originalUrl == null || originalUrl.isEmpty) return null;
String _getProxiedImageUrl(String? originalUrl) {
if (originalUrl == null || originalUrl.isEmpty) return '';
if (originalUrl.startsWith('http://')) {
return '/api/xtream/$originalUrl';
}