mirror of
https://github.com/R0m1k3/xtremflow.git
synced 2026-10-18 12:49:54 +02:00
feat: Add Movies tab with search, pagination, and hero carousel for movie browsing.
This commit is contained in:
1 parent
4f484be343
commit
fcf6a8283d
3 files changed
+11
-23
No files matched your search
@@ -55,10 +55,16 @@ class Movie {
|
|||||||
});
|
});
|
||||||
|
|
||||||
factory Movie.fromJson(Map<String, dynamic> json) {
|
factory Movie.fromJson(Map<String, dynamic> json) {
|
||||||
|
// Essayer plusieurs champs possibles pour l'image (différentes API Xtream utilisent différents noms)
|
||||||
|
final imageUrl = json['stream_icon']?.toString() ??
|
||||||
|
json['cover']?.toString() ??
|
||||||
|
json['movie_image']?.toString() ??
|
||||||
|
json['cover_big']?.toString();
|
||||||
|
|
||||||
return Movie(
|
return Movie(
|
||||||
streamId: json['stream_id']?.toString() ?? '',
|
streamId: json['stream_id']?.toString() ?? '',
|
||||||
name: json['name']?.toString() ?? 'Unknown',
|
name: json['name']?.toString() ?? 'Unknown',
|
||||||
streamIcon: json['stream_icon']?.toString(),
|
streamIcon: imageUrl,
|
||||||
categoryId: json['category_id']?.toString(),
|
categoryId: json['category_id']?.toString(),
|
||||||
categoryName: json['category_name']?.toString() ?? '',
|
categoryName: json['category_name']?.toString() ?? '',
|
||||||
containerExtension: json['container_extension']?.toString() ?? 'mp4',
|
containerExtension: json['container_extension']?.toString() ?? 'mp4',
|
||||||
|
|||||||
@@ -157,15 +157,6 @@ class _MoviesTabState extends ConsumerState<MoviesTab> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
String? _getProxiedIconUrl(String? originalUrl) {
|
|
||||||
if (originalUrl == null || originalUrl.isEmpty) return null;
|
|
||||||
// Proxy toutes les URLs externes (http et https) via notre serveur
|
|
||||||
if (originalUrl.startsWith('http://') || originalUrl.startsWith('https://')) {
|
|
||||||
return '/api/xtream/$originalUrl';
|
|
||||||
}
|
|
||||||
return originalUrl;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final settings = ref.watch(iptvSettingsProvider);
|
final settings = ref.watch(iptvSettingsProvider);
|
||||||
@@ -184,7 +175,7 @@ class _MoviesTabState extends ConsumerState<MoviesTab> {
|
|||||||
final heroItems = displayMovies.take(5).map((m) => HeroItem(
|
final heroItems = displayMovies.take(5).map((m) => HeroItem(
|
||||||
id: m.streamId,
|
id: m.streamId,
|
||||||
title: m.name,
|
title: m.name,
|
||||||
imageUrl: _getProxiedIconUrl(m.streamIcon) ?? '',
|
imageUrl: m.streamIcon ?? '',
|
||||||
subtitle: m.rating != null ? '${_formatRating(m.rating)} ★' : null,
|
subtitle: m.rating != null ? '${_formatRating(m.rating)} ★' : null,
|
||||||
onMoreInfo: () {
|
onMoreInfo: () {
|
||||||
_playMovie(m);
|
_playMovie(m);
|
||||||
@@ -300,7 +291,7 @@ class _MoviesTabState extends ConsumerState<MoviesTab> {
|
|||||||
|
|
||||||
return MediaCard(
|
return MediaCard(
|
||||||
title: movie.name,
|
title: movie.name,
|
||||||
imageUrl: _getProxiedIconUrl(movie.streamIcon),
|
imageUrl: movie.streamIcon,
|
||||||
|
|
||||||
subtitle: movie.rating != null ? '${_formatRating(movie.rating)} ★' : null,
|
subtitle: movie.rating != null ? '${_formatRating(movie.rating)} ★' : null,
|
||||||
rating: _formatRating(movie.rating),
|
rating: _formatRating(movie.rating),
|
||||||
|
|||||||
@@ -152,15 +152,6 @@ class _SeriesTabState extends ConsumerState<SeriesTab> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
String? _getProxiedIconUrl(String? originalUrl) {
|
|
||||||
if (originalUrl == null || originalUrl.isEmpty) return null;
|
|
||||||
// Proxy toutes les URLs externes (http et https) via notre serveur
|
|
||||||
if (originalUrl.startsWith('http://') || originalUrl.startsWith('https://')) {
|
|
||||||
return '/api/xtream/$originalUrl';
|
|
||||||
}
|
|
||||||
return originalUrl;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final settings = ref.watch(iptvSettingsProvider);
|
final settings = ref.watch(iptvSettingsProvider);
|
||||||
@@ -182,7 +173,7 @@ class _SeriesTabState extends ConsumerState<SeriesTab> {
|
|||||||
final heroItems = displaySeries.take(5).map((s) => HeroItem(
|
final heroItems = displaySeries.take(5).map((s) => HeroItem(
|
||||||
id: s.seriesId.toString(), // ID logic might differ for Series
|
id: s.seriesId.toString(), // ID logic might differ for Series
|
||||||
title: s.name,
|
title: s.name,
|
||||||
imageUrl: _getProxiedIconUrl(s.cover) ?? '',
|
imageUrl: s.cover ?? '',
|
||||||
subtitle: s.rating != null ? '${_formatRating(s.rating)} ★' : null,
|
subtitle: s.rating != null ? '${_formatRating(s.rating)} ★' : null,
|
||||||
onMoreInfo: () => _openSeries(s),
|
onMoreInfo: () => _openSeries(s),
|
||||||
)).toList();
|
)).toList();
|
||||||
@@ -290,7 +281,7 @@ class _SeriesTabState extends ConsumerState<SeriesTab> {
|
|||||||
final serie = displaySeries[index];
|
final serie = displaySeries[index];
|
||||||
return MediaCard(
|
return MediaCard(
|
||||||
title: serie.name,
|
title: serie.name,
|
||||||
imageUrl: _getProxiedIconUrl(serie.cover),
|
imageUrl: serie.cover,
|
||||||
|
|
||||||
subtitle: serie.rating != null ? '${_formatRating(serie.rating)} ★' : null,
|
subtitle: serie.rating != null ? '${_formatRating(serie.rating)} ★' : null,
|
||||||
rating: _formatRating(serie.rating),
|
rating: _formatRating(serie.rating),
|
||||||
|
|||||||
Reference in new issue
Block a user