chore: Stop explicitly copying pubspec.lock as flutter pub get generates it.

This commit is contained in:
Michael committed 2025-12-05 18:44:45 +01:00
1 parent 154e2b2246
commit 5943cd9587
9 files changed
+97 -9

No files matched your search

+3 -2
View File
@@ -14,9 +14,10 @@ RUN git config --global --add safe.directory /app
RUN flutter config --enable-web RUN flutter config --enable-web
# Copy dependency files first for better caching # Copy dependency files first for better caching
COPY pubspec.yaml pubspec.lock ./ # Note: pubspec.lock will be generated by flutter pub get if missing
COPY pubspec.yaml ./
# Get dependencies # Get dependencies (generates pubspec.lock automatically)
RUN flutter pub get RUN flutter pub get
# Copy source code # Copy source code
@@ -71,6 +71,7 @@ class Series {
final String name; final String name;
final String? cover; final String? cover;
final String? categoryId; final String? categoryId;
final String categoryName;
final String? rating; final String? rating;
const Series({ const Series({
@@ -78,6 +79,7 @@ class Series {
required this.name, required this.name,
this.cover, this.cover,
this.categoryId, this.categoryId,
this.categoryName = '',
this.rating, this.rating,
}); });
@@ -87,6 +89,7 @@ class Series {
name: json['name']?.toString() ?? 'Unknown', name: json['name']?.toString() ?? 'Unknown',
cover: json['cover']?.toString(), cover: json['cover']?.toString(),
categoryId: json['category_id']?.toString(), categoryId: json['category_id']?.toString(),
categoryName: json['category_name']?.toString() ?? '',
rating: json['rating']?.toString(), rating: json['rating']?.toString(),
); );
} }
@@ -2,8 +2,16 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../../../core/models/playlist_config.dart'; import '../../../core/models/playlist_config.dart';
import '../services/xtream_service.dart'; import '../services/xtream_service.dart';
/// Singleton Xtream service provider /// Family provider for Xtream service - one instance per playlist
final xtreamServiceProvider = Provider<XtreamService>((ref) { final xtreamServiceProvider = Provider.family<XtreamService, PlaylistConfig>((ref, playlist) {
final service = XtreamService();
service.setPlaylist(playlist);
ref.onDispose(() => service.dispose());
return service;
});
/// Singleton Xtream service provider (legacy, for activeXtreamServiceProvider)
final _singletonXtreamServiceProvider = Provider<XtreamService>((ref) {
final service = XtreamService(); final service = XtreamService();
ref.onDispose(() => service.dispose()); ref.onDispose(() => service.dispose());
return service; return service;
@@ -14,7 +22,7 @@ final selectedPlaylistProvider = StateProvider<PlaylistConfig?>((ref) => null);
/// Provider that watches for playlist changes and updates Xtream service /// Provider that watches for playlist changes and updates Xtream service
final activeXtreamServiceProvider = Provider<XtreamService>((ref) { final activeXtreamServiceProvider = Provider<XtreamService>((ref) {
final service = ref.watch(xtreamServiceProvider); final service = ref.watch(_singletonXtreamServiceProvider);
final playlist = ref.watch(selectedPlaylistProvider); final playlist = ref.watch(selectedPlaylistProvider);
if (playlist != null) { if (playlist != null) {
@@ -28,7 +28,7 @@ class _DashboardScreenState extends ConsumerState<DashboardScreen> {
void initState() { void initState() {
super.initState(); super.initState();
_tabs = [ _tabs = [
LiveTVTab(playlist: widget.playlist), const LiveTVTab(),
MoviesTab(playlist: widget.playlist), MoviesTab(playlist: widget.playlist),
SeriesTab(playlist: widget.playlist), SeriesTab(playlist: widget.playlist),
const SettingsTab(), const SettingsTab(),
@@ -3,6 +3,7 @@ import 'package:dio_cache_interceptor/dio_cache_interceptor.dart';
import 'package:dio_cache_interceptor_hive_store/dio_cache_interceptor_hive_store.dart'; import 'package:dio_cache_interceptor_hive_store/dio_cache_interceptor_hive_store.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 '../models/xtream_models.dart' as xm;
/// Xtream Codes API Service /// Xtream Codes API Service
/// ///
@@ -180,6 +181,68 @@ class XtreamService {
} }
} }
/// Get movies with pagination support
Future<List<xm.Movie>> getMovies({int offset = 0, int limit = 100}) async {
if (_currentPlaylist == null) throw Exception('No playlist configured');
try {
final response = await _dio.get(
_currentPlaylist!.apiBaseUrl,
queryParameters: {
'username': _currentPlaylist!.username,
'password': _currentPlaylist!.password,
'action': 'get_vod_streams',
},
options: Options(extra: _cacheOptions.toExtra()),
);
final List<dynamic> allMovies = response.data as List<dynamic>;
// Apply pagination
final endIndex = (offset + limit) > allMovies.length ? allMovies.length : offset + limit;
if (offset >= allMovies.length) return [];
final paginatedMovies = allMovies.sublist(offset, endIndex);
return paginatedMovies
.map((movieData) => xm.Movie.fromJson(movieData as Map<String, dynamic>))
.toList();
} catch (e) {
throw Exception('Failed to fetch movies: $e');
}
}
/// Get series with pagination support (returns flat list)
Future<List<xm.Series>> getSeriesPaginated({int offset = 0, int limit = 100}) async {
if (_currentPlaylist == null) throw Exception('No playlist configured');
try {
final response = await _dio.get(
_currentPlaylist!.apiBaseUrl,
queryParameters: {
'username': _currentPlaylist!.username,
'password': _currentPlaylist!.password,
'action': 'get_series',
},
options: Options(extra: _cacheOptions.toExtra()),
);
final List<dynamic> allSeries = response.data as List<dynamic>;
// Apply pagination
final endIndex = (offset + limit) > allSeries.length ? allSeries.length : offset + limit;
if (offset >= allSeries.length) return [];
final paginatedSeries = allSeries.sublist(offset, endIndex);
return paginatedSeries
.map((seriesData) => xm.Series.fromJson(seriesData as Map<String, dynamic>))
.toList();
} catch (e) {
throw Exception('Failed to fetch series: $e');
}
}
/// Get short EPG for a specific stream /// Get short EPG for a specific stream
/// ///
/// Returns "Now" and "Next" program info /// Returns "Now" and "Next" program info
+10
View File
@@ -1,3 +1,13 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:google_fonts/google_fonts.dart';
import '../../../core/models/playlist_config.dart';
import '../models/xtream_models.dart';
import '../providers/xtream_provider.dart';
import '../screens/video_player_screen.dart';
class MoviesTab extends ConsumerStatefulWidget {
final PlaylistConfig playlist; final PlaylistConfig playlist;
const MoviesTab({super.key, required this.playlist}); const MoviesTab({super.key, required this.playlist});
+2 -1
View File
@@ -5,6 +5,7 @@ import 'package:google_fonts/google_fonts.dart';
import '../../../core/models/playlist_config.dart'; import '../../../core/models/playlist_config.dart';
import '../services/xtream_service.dart'; import '../services/xtream_service.dart';
import '../models/xtream_models.dart'; import '../models/xtream_models.dart';
import '../providers/xtream_provider.dart';
class SeriesTab extends ConsumerStatefulWidget { class SeriesTab extends ConsumerStatefulWidget {
final PlaylistConfig playlist; final PlaylistConfig playlist;
@@ -52,7 +53,7 @@ class _SeriesTabState extends ConsumerState<SeriesTab> {
try { try {
final service = ref.read(xtreamServiceProvider(widget.playlist)); final service = ref.read(xtreamServiceProvider(widget.playlist));
final newSeries = await service.getSeries( final newSeries = await service.getSeriesPaginated(
offset: _currentOffset, offset: _currentOffset,
limit: _pageSize, limit: _pageSize,
); );
+2 -2
View File
@@ -28,7 +28,7 @@ class MyApp extends ConsumerWidget {
seedColor: Colors.blue, seedColor: Colors.blue,
brightness: Brightness.light, brightness: Brightness.light,
), ),
cardTheme: CardTheme( cardTheme: CardThemeData(
elevation: 2, elevation: 2,
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12), borderRadius: BorderRadius.circular(12),
@@ -41,7 +41,7 @@ class MyApp extends ConsumerWidget {
seedColor: Colors.blue, seedColor: Colors.blue,
brightness: Brightness.dark, brightness: Brightness.dark,
), ),
cardTheme: CardTheme( cardTheme: CardThemeData(
elevation: 2, elevation: 2,
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12), borderRadius: BorderRadius.circular(12),
+2
View File
@@ -30,6 +30,8 @@ dependencies:
video_player: ^2.8.2 video_player: ^2.8.2
video_player_web: ^2.3.0 # Required for web platform video_player_web: ^2.3.0 # Required for web platform
chewie: ^1.7.5 chewie: ^1.7.5
media_kit: ^1.1.10
media_kit_video: ^1.2.4
# UI Components # UI Components
google_fonts: ^6.1.0 google_fonts: ^6.1.0