mirror of
https://github.com/R0m1k3/xtremflow.git
synced 2026-10-11 17:30:00 +02:00
EPG — les trois chemins (backend /api/epg, XtreamService.getShortEpg et getShortEPG) n'utilisaient que get_epg et get_short_epg. Beaucoup de panneaux Xtream ne connaissent pas get_epg : ils répondent 200 avec le bloc d'authentification, sans epg_listings, ce qui produisait un guide vide indiscernable d'une chaîne sans programme. get_short_epg y renvoie epg_listings vide en permanence. - backend : get_simple_data_table en premier, get_short_epg en repli - client : bascule automatique et mémorisée vers le tableau complet dès qu'un panneau est muet sur l'action légère, pour ne pas doubler les requêtes sur chaque tuile de grille - dates dérivées des *_timestamp epoch et émises en ISO-8601 UTC : les champs texte sont dans le fuseau du panneau, sans indicateur de zone, et étaient relus comme de l'heure locale — le guide était décalé et les enregistrements planifiés depuis le guide l'étaient aussi - cache backend : la clé incluait seulement le channelId, deux playlists partageant un stream_id se servaient mutuellement leur guide UI — les deux halos d'ambiance du dashboard étaient restés de l'ancien thème : primary (#FFB68C) à 40 % posé sur le coin haut-gauche, donc pile derrière la sidebar, et info (#A3B8C4, bleu-gris) à 35 % dans une palette entièrement chaude. Ramenés à des braises ember à 8 % / 6 %. - fond via AppColors.backgroundGradient - onglet actif rempli en primaryContainer (surface) et non primary (teinte claire réservée au texte/icônes) - sidebar en niveau flottant : en niveau 1 son fond #181310 se confondait avec le haut du dégradé #1A1310 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
75 lines
2.6 KiB
Dart
75 lines
2.6 KiB
Dart
import 'package:test/test.dart';
|
|
import '../api/epg_api.dart';
|
|
|
|
/// Extrait réel d'une réponse `get_simple_data_table` (panneau Xtream) :
|
|
/// titres/descriptions en base64, horodatages epoch UTC doublés d'une chaîne
|
|
/// exprimée dans le fuseau du panneau — ici Europe/Amsterdam (UTC+2).
|
|
const _sampleListing = {
|
|
'epg_listings': [
|
|
{
|
|
'id': '336987829',
|
|
'epg_id': '104',
|
|
'title': 'Sm91cm5hbCBkZSAyMGg=', // "Journal de 20h"
|
|
'lang': '',
|
|
'start': '2026-08-11 20:00:00',
|
|
'end': '2026-08-11 20:35:00',
|
|
'description': 'TCdpbmZvIGR1IHNvaXI=', // "L'info du soir"
|
|
'channel_id': 'TF1.fr',
|
|
'start_timestamp': '1786471200', // 2026-08-11T18:00:00Z
|
|
'stop_timestamp': '1786473300', // 2026-08-11T18:35:00Z
|
|
},
|
|
],
|
|
};
|
|
|
|
void main() {
|
|
final api = EpgApi((_) async => null);
|
|
|
|
group('transformEpgData', () {
|
|
test('convertit les horodatages epoch en ISO-8601 UTC', () {
|
|
final result = api.transformEpgData(_sampleListing, '845452');
|
|
final programme = (result['programmes'] as List).single as Map;
|
|
|
|
// Sans cette conversion, le client relisait « 2026-08-11T20:00:00 »
|
|
// comme de l'heure locale et décalait tout le guide de 2 h.
|
|
expect(programme['start'], '2026-08-11T18:00:00.000Z');
|
|
expect(programme['end'], '2026-08-11T18:35:00.000Z');
|
|
});
|
|
|
|
test('décode les titres et descriptions base64', () {
|
|
final result = api.transformEpgData(_sampleListing, '845452');
|
|
final programme = (result['programmes'] as List).single as Map;
|
|
|
|
expect(programme['title'], 'Journal de 20h');
|
|
expect(programme['description'], "L'info du soir");
|
|
expect(programme['channel_id'], '845452');
|
|
});
|
|
|
|
test('retombe sur les champs texte quand les epoch manquent', () {
|
|
final result = api.transformEpgData({
|
|
'epg_listings': [
|
|
{
|
|
'title': 'Sm91cm5hbCBkZSAyMGg=',
|
|
'start': '2026-08-11 20:00:00',
|
|
'stop': '2026-08-11 20:35:00',
|
|
},
|
|
],
|
|
}, '845452');
|
|
final programme = (result['programmes'] as List).single as Map;
|
|
|
|
expect(programme['start'], '2026-08-11T20:00:00');
|
|
expect(programme['end'], '2026-08-11T20:35:00');
|
|
});
|
|
|
|
test('rend une liste vide sur un payload sans epg_listings', () {
|
|
// Ce que renvoie un panneau qui ne connaît pas l'action demandée :
|
|
// un 200 contenant le bloc d'authentification.
|
|
final result = api.transformEpgData({
|
|
'user_info': {'auth': 1},
|
|
'server_info': {'url': 'tit.example'},
|
|
}, '845452');
|
|
|
|
expect(result['programmes'], isEmpty);
|
|
});
|
|
});
|
|
}
|