mirror of
https://github.com/R0m1k3/xtremflow.git
synced 2026-10-20 05:28:05 +02:00
Merge pull request #10 from R0m1k3/antigravity
chore: Stop explicitly copying pubspec.lock as flutter pub get genera…
This commit is contained in:
commit
4ad68c7a50
9 files changed
+96
-8
No files matched your search
+2
-1
@@ -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
|
||||||
|
# Note: pubspec.lock will be generated by flutter pub get if missing
|
||||||
COPY pubspec.yaml ./
|
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
|
||||||
|
|||||||
@@ -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});
|
||||||
|
|||||||
@@ -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
@@ -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),
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in new issue
Block a user