Files
iptv-android-tv-hermes/app/SPECIFICATIONS.md
T

15 KiB

iMedia TV+ / Xtream Google TV Player (Hermes Edition)

Spécifications, architecture et guide du projet

Projet : Lecteur IPTV Xtream pour Google TV / Android TV Package Android : ma.tkgraph.xtreamtv Plateforme cible : Google TV / Android TV Langage : Kotlin Interface : Jetpack Compose / Compose TV Lecteur vidéo : AndroidX Media3 ExoPlayer API IPTV : Xtream Codes compatible Date du document : 11/06/2026 Version : 2.0 (Hermes)


1. Objectif du projet

Développer une application Google TV complète pour lire des contenus IPTV depuis un serveur Xtream, en mettant l'accent sur :

  • Qualité logicielle : tests, architecture propre, sécurité
  • Expérience utilisateur : navigation fluide, télécommande, réactivité
  • Résilience : gestion d'erreur, cache, fallbacks gracieux
  • Maintenabilité : documentation, historique, conventions

Fonctionnalités couvertes :

  • Connexion à un serveur Xtream + mémorisation sécurisée des identifiants
  • Lecture Live TV avec EPG court
  • Guide TV global + Replay / Catch-up TV
  • VOD et Séries avec catalogues complets
  • Favoris persistants (Live, VOD, Séries)
  • Chaîne épinglée au démarrage
  • Navigation complète à la télécommande Google TV
  • Support multilingue (français, anglais, arabe)
  • Cache local EPG pour performances

2. Architecture

2.1. Diagramme de flux

flowchart TB
    subgraph UI ["Couche UI (Compose TV)"]
        Login[LoginScreen]
        Home[HomeScreen]
        Player[PlayerScreen]
        Guide[ProgramGuideScreen]
        Replay[ReplayGuideScreen]
        Vod[VodScreen]
        Series[SeriesScreen]
    end

    subgraph ViewModel ["Couche ViewModel"]
        VM_Home[HomeViewModel]
        VM_Player[PlayerViewModel]
        VM_Vod[VodViewModel]
        VM_Series[SeriesViewModel]
    end

    subgraph Repository ["Couche Repository"]
        Repo_Auth[AuthRepository]
        Repo_Live[LiveRepository]
        Repo_Vod[VodRepository]
        Repo_Series[SeriesRepository]
        Repo_Epg[EpgRepository]
    end

    subgraph Data ["Couche Data"]
        API[XtreamApi<br/>Retrofit]
        Cache[EpgCache<br/>Room / SQLite]
        Storage[ConfigStorage<br/>EncryptedSharedPreferences]
    end

    subgraph External ["Externe"]
        Xtream[Xtream Server]
    end

    Login --> VM_Home
    Home --> VM_Home
    Player --> VM_Player
    Guide --> VM_Player
    Replay --> VM_Player
    Vod --> VM_Vod
    Series --> VM_Series

    VM_Home --> Repo_Auth
    VM_Home --> Repo_Live
    VM_Home --> Repo_Epg
    VM_Player --> Repo_Live
    VM_Player --> Repo_Epg
    VM_Vod --> Repo_Vod
    VM_Series --> Repo_Series

    Repo_Auth --> Storage
    Repo_Live --> API
    Repo_Vod --> API
    Repo_Series --> API
    Repo_Epg --> API
    Repo_Epg --> Cache

    API --> Xtream
    Storage -.->|Fallback| API

2.2. Flux de navigation

flowchart LR
    Splash[Splash<br/>iMedia TV+] --> Login[LoginScreen]
    Login --> Home[HomeScreen]
    
    Home -->|Live TV| LiveList[Liste Chaînes]
    Home -->|Guide TV| GuideScreen[Guide TV Global]
    Home -->|VOD| VodScreen[Catalogue VOD]
    Home -->|Séries| SeriesScreen[Catalogue Séries]
    
    LiveList --> Player[PlayerScreen]
    GuideScreen --> Player
    ReplayGuide --> Player
    VodScreen --> Player
    SeriesScreen --> Player
    
    Player -->|Retour 1| Overlay
    Player -->|Retour 2| PreviousScreen
    
    Home -->|Retour| ConfirmQuit{Confirmer?}

2.3. Structure des dossiers

app/
 └── src/
     ├── main/
     │   ├── AndroidManifest.xml
     │   ├── java/ma/tkgraph/xtreamtv/
     │   │   ├── MainActivity.kt
     │   │   ├── data/
     │   │   │   ├── model/          ← DTOs Xtream
     │   │   │   ├── network/        ← Retrofit API + interceptors
     │   │   │   ├── repository/     ← Live, VOD, Series, EPG, Auth
     │   │   │   └── storage/        ← EncryptedSharedPreferences
     │   │   ├── ui/
     │   │   │   ├── components/     ← ChannelLogo, CategoryRow, etc.
     │   │   │   ├── screens/        ← Login, Home, Player, Guide, etc.
     │   │   │   └── theme/          ← Thème Material 3
     │   │   └── util/               ← UrlBuilder, CategorySort, Extensions
     │   └── res/
     │       ├── drawable/           ← launch_background.xml
     │       ├── drawable-nodpi/     ← iMedia TV+ splash PNG
     │       ├── mipmap-*/           ← icône app
     │       ├── values/             ← strings.xml (default = EN)
     │       ├── values-fr/          ← strings.xml (français)
     │       └── values-ar/          ← strings.xml (العربية)
     ├── test/                       ← Tests unitaires (JUnit + MockK)
     └── androidTest/                ← Tests UI (Compose Test)

3. Stack technique détaillée

Couche Technologie Justification
Langage Kotlin 2.x Moderne, sûr, natif Android
UI Jetpack Compose + Compose TV (1.7+) Déclaratif, natif TV
Lecteur AndroidX Media3 ExoPlayer 1.5+ Standard Google, HLS/DASH/MP4
Réseau Retrofit 2.11+ / OkHttp 4.12+ Robuste, interceptable
Images Coil 3.x Asynchrone, cache, fallback
Cache EPG Room 2.7+ (SQLite) Performant, requêtes typées
Stockage EncryptedSharedPreferences + AES-256 Sécurité des credentials
Tests unitaires JUnit 5 / MockK / Turbine Kotlin-friendly, Flow test
Tests UI Compose UI Test + Espresso Automatisation UI TV
Logging Timber Structuré, tags, crash
Crash reporting Firebase Crashlytics Monitoring production

4. Environnement de développement

Composant Valeur
OS de développement Windows 11
IDE Android Studio (Ladybug+)
Terminal PowerShell
Appareil cible Google TV / Android TV 12+ (API 31+)
Connexion ADB Wi-Fi ou USB
JDK OpenJDK 21 (via Android Studio)
SDK Android API 36, Build-Tools 36.x
Gradle 9.1+ (wrapper)

4.1. Variables d'environnement

$env:JAVA_HOME = "D:\Program Files\Android\Android Studio\jbr"
$env:ANDROID_HOME = "$env:LOCALAPPDATA\Android\Sdk"
$env:Path = "$env:JAVA_HOME\bin;$env:ANDROID_HOME\platform-tools;$env:ANDROID_HOME\cmdline-tools\latest\bin;D:\Tools\Gradle\gradle-9.1.0\bin;$env:Path"

4.2. Compilation

cd "D:\07-Logiciels\12-IPTV Andoid TV HERMES"
.\gradlew.bat clean --no-daemon
.\gradlew.bat :app:assembleDebug --no-daemon

APK générée : app\build\outputs\apk\debug\app-debug.apk


5. Sécurité

5.1. Stockage des identifiants

Problème de la v1 : identifiants stockés en clair dans SharedPreferences.

Solution Hermes : EncryptedSharedPreferences avec AES-256 GCM.

// data/storage/SecureStorage.kt
val masterKey = MasterKey.Builder(context)
    .setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
    .build()

val prefs = EncryptedSharedPreferences.create(
    context,
    "imedia_secure_prefs",
    masterKey,
    EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
    EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
)

5.2. Règles de sécurité

  • Aucun log contenant username, password ou server_url
  • Timeout réseau configuré (15s connect, 30s read)
  • Validation côté client avant envoi : URL bien formée, champs non vides
  • Pas de stockage en cache HTTP des credentials
  • OkHttp interceptor pour masquer les auth dans les logs debug
  • Préparation au HTTPS (certificats, SSR)

6. Gestion d'erreurs

6.1. Stratégie générale

Scénario Comportement Message utilisateur
Réseau indisponible Toast + fallback cache EPG "Connexion impossible. Vérifiez votre réseau."
Timeout API (15s) Retry 1x, puis message "Le serveur ne répond pas. Réessayez."
Auth échouée Effacer champs, focus sur login "Identifiants incorrects ou serveur invalide."
Flux vidéo mort 503 overlay, retour menu "Flux indisponible pour cette chaîne."
EPG vide Afficher "Aucun programme" "Aucun programme trouvé pour cette chaîne."
Données corrompues Fallback valeurs par défaut "Des données sont manquantes — certains affichages peuvent être incomplets."

6.2. État UI avec sealed class

sealed class UiState<out T> {
    object Idle : UiState<Nothing>()
    object Loading : UiState<Nothing>()
    data class Success<T>(val data: T) : UiState<T>()
    data class Error(val message: String, val retry: Boolean = true) : UiState<Nothing>()
}

6.3. Intercepteur réseau

class ErrorInterceptor : Interceptor {
    override fun intercept(chain: Interceptor.Chain): Response {
        val request = chain.request()
        return try {
            val response = chain.proceed(request)
            if (!response.isSuccessful) {
                throw XtreamHttpException(response.code, response.message)
            }
            response
        } catch (e: SocketTimeoutException) {
            throw XtreamTimeoutException("Le serveur Xtream est injoignable")
        } catch (e: UnknownHostException) {
            throw XtreamNetworkException("Aucune connexion réseau")
        }
    }
}

7. Cache EPG

7.1. Problème

Le guide TV global peut nécessiter des centaines d'appels API (1 par chaîne). Sans cache, l'écran est inutilisable.

7.2. Solution : Room (SQLite)

@Entity(tableName = "epg_cache")
data class EpgCacheEntry(
    @PrimaryKey val id: Long,
    val streamId: Int,
    val channelName: String,
    val title: String,
    val description: String?,
    val startTimestamp: Long,
    val endTimestamp: Long,
    val cachedAt: Long = System.currentTimeMillis()
)

7.3. Politique de cache

Type Durée Invalidation
EPG court (live) 2 minutes Rafraîchi au scroll
EPG complet (guide) 15 minutes Pull-to-refresh manuel
Catégories 1 heure Au lancement
Listes chaînes 5 minutes Changement de catégorie

7.4. Flux EPG

flowchart TD
    A[Ouvre Guide TV] --> B{Cache valide?}
    B -->|Oui < 15min| C[Afficher depuis Room]
    B -->|Non| D[Requête API par chaîne]
    D --> E[Stocker dans Room]
    E --> C

8. Tests

8.1. Tests unitaires (JUnit 5 + MockK)

Fichier Test Ce qu'on vérifie
AuthRepositoryTest.kt Connexion valide/invalide Repository retourne Success ou Error
AuthRepositoryTest.kt Timeout réseau Erreur lisible, pas de crash
EpgRepositoryTest.kt Cache hit/miss Appel API si cache expiré, sinon données locales
CategorySortUtilsTest.kt Tri FR/AR/autres Ordre exact des catégories
XtreamUrlBuilderTest.kt URLs Live/VOD/Series Format correct de chaque URL
SecureStorageTest.kt Lecture/écriture chiffrée Données diffférentes en clair vs stocké
PlayerViewModelTest.kt Zapping gauche/droite Chaîne suivante/précédente correcte
FavoritesRepositoryTest.kt Ajout/suppression favoris Persistance après redémarrage

8.2. Tests UI (Compose UI Test)

Fichier Test
LoginScreenTest.kt Remplissage formulaire → bouton actif
LoginScreenTest.kt Erreur → message d'erreur affiché
HomeScreenTest.kt Navigation entre catégories OK
HomeScreenTest.kt Favori accessible via télécommande
PlayerScreenTest.kt Overlay disparaît après 3s

8.3. Lancer les tests

# Tests unitaires uniquement
.\gradlew.bat test --no-daemon

# Tests UI (appareil/émulateur connecté)
.\gradlew.bat connectedAndroidTest --no-daemon

# Les deux
.\gradlew.bat test connectedAndroidTest --no-daemon

9. Internationalisation (i18n)

9.1. Langues supportées

Langue Dossier Statut
Anglais (défaut) values/ ✅ Prévu
Français values-fr/ ✅ Prévu
Arabe values-ar/ ✅ Prévu (RTL)

9.2. Exemple

<!-- values/strings.xml -->
<string name="app_name">iMedia TV+</string>
<string name="live_tv">Live TV</string>
<string name="connect">Connect</string>
<string name="network_error">Connection lost. Check your network.</string>

<!-- values-fr/strings.xml -->
<string name="app_name">iMedia TV+</string>
<string name="live_tv">Direct TV</string>
<string name="connect">Connexion</string>
<string name="network_error">Connexion perdue. Vérifiez votre réseau.</string>

<!-- values-ar/strings.xml -->
<string name="app_name">iMedia TV+</string>
<string name="live_tv">البث المباشر</string>
<string name="connect">اتصال</string>
<string name="network_error">انقطع الاتصال. تحقق من شبكتك.</string>

10. Logging et Monitoring

10.1. Timber

// Application.kt
class XtreamApplication : Application() {
    override fun onCreate() {
        super.onCreate()
        if (BuildConfig.DEBUG) {
            Timber.plant(Timber.DebugTree())
        } else {
            Timber.plant(CrashReportingTree())
        }
    }
}

Règles :

  • Timber.d / Timber.i pour debug et infos
  • Timber.w pour anomalies non bloquantes
  • Timber.e pour erreurs utilisateur
  • Jamais de log de credentials, tokens, ou données sensibles

10.2. Crash reporting

Firebase Crashlytics est prévu pour la version release :

  • Rapports de crash automatiques
  • Logs non-sensibles attachés aux rapports
  • Clés custom : écran actif, dernier état réseau

11. Roadmap

Phase 1 — Fondations (immédiat)

  • Structure du projet
  • Stack technique définie
  • Connexion Xtream + SecureStorage
  • Live TV avec Media3

Phase 2 — Fonctionnalités cœur

  • Catégories + Favoris Live
  • EPG court + Replay
  • Guide TV global
  • VOD + Séries

Phase 3 — Qualité

  • Tests unitaires (couverture > 70%)
  • Cache EPG (Room)
  • Gestion d'erreur complète
  • i18n (FR, EN, AR)

Phase 4 — Production

  • Signing + ProGuard
  • Firebase Crashlytics
  • Bannière Android TV + icônes finales
  • Politique de confidentialité
  • Publication Play Store

12. Conclusion

Cette version Hermes reprend l'excellente base de la v1 et y ajoute :

  1. 🔐 Sécurité : chiffrement AES-256 des credentials
  2. 📦 Cache EPG : Room SQLite pour des performances réelles
  3. 🧪 Tests : stratégie complète (unitaire + UI)
  4. 🌍 i18n : support FR/EN/AR
  5. 📋 Gestion d'erreur : sealed class + intercepteurs + messages lisibles
  6. 📊 Monitoring : Timber + Crashlytics
  7. 📐 Architecture : flux documentés en Mermaid

Le logiciel fonctionnait déjà en v1. Cette version le rend fiable, sécurisé et prêt pour la production.