mirror of
https://github.com/R0m1k3/xtremflow.git
synced 2026-10-16 19:58:54 +02:00
feat: introduce EPG API with caching and TV & Recordings UI with EPG Guide, Recordings, and Season Passes tabs.
This commit is contained in:
1 parent
fa88746db6
commit
3232a3a226
3 files changed
+300
-263
No files matched your search
+27
-11
@@ -28,7 +28,8 @@ class EpgApi {
|
||||
try {
|
||||
final playlist = await _getPlaylist(request);
|
||||
if (playlist == null) {
|
||||
return Response.forbidden(json.encode({'error': 'Playlist non trouvée'}),
|
||||
return Response.forbidden(
|
||||
json.encode({'error': 'Playlist non trouvée'}),
|
||||
headers: {'Content-Type': 'application/json'});
|
||||
}
|
||||
|
||||
@@ -38,11 +39,13 @@ class EpgApi {
|
||||
'$dns/player_api.php?username=${playlist.username}&password=${playlist.password}'
|
||||
'&action=get_epg&stream_id=$channelId&limit=48';
|
||||
|
||||
final response = await http.get(Uri.parse(url)).timeout(const Duration(seconds: 15));
|
||||
final response =
|
||||
await http.get(Uri.parse(url)).timeout(const Duration(seconds: 15));
|
||||
|
||||
if (response.statusCode != 200) {
|
||||
return Response(response.statusCode,
|
||||
body: json.encode({'error': 'Erreur API Xtream: ${response.statusCode}'}),
|
||||
body: json
|
||||
.encode({'error': 'Erreur API Xtream: ${response.statusCode}'}),
|
||||
headers: {'Content-Type': 'application/json'});
|
||||
}
|
||||
|
||||
@@ -57,7 +60,8 @@ class EpgApi {
|
||||
expiresAt: DateTime.now().add(const Duration(minutes: 30)),
|
||||
);
|
||||
|
||||
return Response.ok(jsonStr, headers: {'Content-Type': 'application/json', 'X-Cache': 'MISS'});
|
||||
return Response.ok(jsonStr,
|
||||
headers: {'Content-Type': 'application/json', 'X-Cache': 'MISS'});
|
||||
} catch (e) {
|
||||
return Response.internalServerError(
|
||||
body: json.encode({'error': 'Erreur lors de la récupération EPG: $e'}),
|
||||
@@ -80,24 +84,36 @@ class EpgApi {
|
||||
final startRaw = item['start'] as String? ?? '';
|
||||
final endRaw = item['stop'] as String? ?? item['end'] as String? ?? '';
|
||||
|
||||
// Normaliser les dates pour le frontend (Xtream format support)
|
||||
final start = startRaw.contains(' ') && !startRaw.contains('T')
|
||||
? startRaw.replaceFirst(' ', 'T')
|
||||
: startRaw;
|
||||
final end = endRaw.contains(' ') && !endRaw.contains('T')
|
||||
? endRaw.replaceFirst(' ', 'T')
|
||||
: endRaw;
|
||||
|
||||
// Décoder le titre (base64 si nécessaire)
|
||||
String title = item['title'] as String? ?? '';
|
||||
try {
|
||||
title = utf8.decode(base64Decode(title));
|
||||
} catch (_) {
|
||||
// n'est pas du base64, garder tel quel
|
||||
}
|
||||
if (title.isNotEmpty) {
|
||||
final decoded = utf8.decode(base64Decode(title));
|
||||
if (decoded.isNotEmpty) title = decoded;
|
||||
}
|
||||
} catch (_) {}
|
||||
|
||||
String description = item['description'] as String? ?? '';
|
||||
try {
|
||||
description = utf8.decode(base64Decode(description));
|
||||
if (description.isNotEmpty) {
|
||||
final decoded = utf8.decode(base64Decode(description));
|
||||
if (decoded.isNotEmpty) description = decoded;
|
||||
}
|
||||
} catch (_) {}
|
||||
|
||||
return {
|
||||
'title': title,
|
||||
'description': description,
|
||||
'start': startRaw,
|
||||
'end': endRaw,
|
||||
'start': start,
|
||||
'end': end,
|
||||
'channel_id': channelId,
|
||||
};
|
||||
}).toList();
|
||||
|
||||
Reference in new issue
Block a user