154 lines
5.9 KiB
Markdown
154 lines
5.9 KiB
Markdown
# Gestion d'erreurs — iMedia TV+
|
|
|
|
## Architecture
|
|
|
|
```mermaid
|
|
flowchart TB
|
|
API[Serveur Xtream] -->|Timeout / 4xx / 5xx| Interceptor[OkHttp ErrorInterceptor]
|
|
Interceptor -->|Exception typée| Repository
|
|
Repository -->|Result / UiState.Error| ViewModel
|
|
ViewModel -->|Message localisé| UI[Écran Compose]
|
|
UI -->|Snackbar / Toast / Overlay| User[Utilisateur]
|
|
```
|
|
|
|
## Hiérarchie d'erreurs
|
|
|
|
```kotlin
|
|
/**
|
|
* Toutes les erreurs Xtream sont des sealed classes
|
|
* pour un `when` exhaustif dans l'UI.
|
|
*/
|
|
sealed class XtreamException(message: String, cause: Throwable? = null) : Exception(message, cause) {
|
|
class NetworkException(message: String) : XtreamException(message)
|
|
class TimeoutException(message: String) : XtreamException(message)
|
|
class AuthException(message: String) : XtreamException(message)
|
|
class ServerException(val code: Int, message: String) : XtreamException(message)
|
|
class ParseException(message: String, cause: Throwable) : XtreamException(message, cause)
|
|
class StreamUnavailableException(message: String) : XtreamException(message)
|
|
}
|
|
```
|
|
|
|
## Table des erreurs
|
|
|
|
| N° | Condition | Exception | Code UI | Message FR | Message EN |
|
|
|----|-----------|-----------|---------|------------|------------|
|
|
| 1 | Serveur injoignable | `NetworkException` | Erreur réseau | "Impossible de se connecter au serveur. Vérifiez votre réseau." | "Cannot reach server. Check your network." |
|
|
| 2 | Timeout dépassé (15s) | `TimeoutException` | Erreur réseau | "Le serveur ne répond pas. Réessayez plus tard." | "Server is not responding. Try again later." |
|
|
| 3 | Auth invalide | `AuthException` | Login bloqué | "Identifiants incorrects ou serveur invalide." | "Invalid credentials or server." |
|
|
| 4 | Flux vidéo cassé | `StreamUnavailableException` | Overlay erreur | "Flux indisponible pour cette chaîne." | "Stream unavailable for this channel." |
|
|
| 5 | HTTP 4xx | `ServerException` | Toast | "Erreur serveur (code X). Contactez votre fournisseur." | "Server error (code X). Contact your provider." |
|
|
| 6 | HTTP 5xx | `ServerException` | Toast | "Le serveur Xtream rencontre un problème." | "Xtream server is having issues." |
|
|
| 7 | JSON mal formé | `ParseException` | Fallback silencieux | "Données reçues incorrectes." | "Received data is malformed." |
|
|
| 8 | EPG vide | N/A (pas une erreur) | Texte vide | "Aucun programme trouvé pour cette chaîne." | "No program found for this channel." |
|
|
| 9 | Cache corrompu | N/A | Suppression + reload | "Mise à jour des données..." | "Updating data..." |
|
|
|
|
## État loading global
|
|
|
|
```kotlin
|
|
/**
|
|
* État unique pour TOUS les écrans.
|
|
* Permet un traitement uniforme dans les composants UI.
|
|
*/
|
|
sealed interface UiState<out T> {
|
|
data object Idle : UiState<Nothing>
|
|
data object Loading : UiState<Nothing>
|
|
data class Success<T>(val data: T) : UiState<T>
|
|
data class Error(
|
|
val message: String, // Message affiché à l'utilisateur
|
|
val retry: Boolean = true, // Afficher un bouton "Réessayer" ?
|
|
val exception: XtreamException? = null // Pour logging uniquement
|
|
) : UiState<Nothing>
|
|
}
|
|
```
|
|
|
|
## Intercepteur OkHttp
|
|
|
|
```kotlin
|
|
class XtreamErrorInterceptor : Interceptor {
|
|
override fun intercept(chain: Interceptor.Chain): Response {
|
|
val request = chain.request()
|
|
return try {
|
|
val response = chain.proceed(request)
|
|
if (!response.isSuccessful) {
|
|
when (response.code) {
|
|
401, 403 -> throw XtreamException.AuthException(
|
|
"Authentication failed"
|
|
)
|
|
in 400..499 -> throw XtreamException.ServerException(
|
|
response.code, "Client error"
|
|
)
|
|
in 500..599 -> throw XtreamException.ServerException(
|
|
response.code, "Server error"
|
|
)
|
|
}
|
|
}
|
|
response
|
|
} catch (e: SocketTimeoutException) {
|
|
throw XtreamException.TimeoutException("Connection timed out")
|
|
} catch (e: ConnectException) {
|
|
throw XtreamException.NetworkException("Cannot connect")
|
|
} catch (e: UnknownHostException) {
|
|
throw XtreamException.NetworkException("DNS resolution failed")
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
## Comportement UI par erreur
|
|
|
|
### Écran de connexion
|
|
```
|
|
[Champs vides]
|
|
❌ Bouton Connexion désactivé
|
|
|
|
[Erreur réseau]
|
|
✅ Bouton actif, message rouge sous le formulaire
|
|
✅ Champs conservés (pas de reset)
|
|
|
|
[Auth refusée]
|
|
✅ Message : "Identifiants incorrects"
|
|
✅ Focus revient sur le champ mot de passe
|
|
```
|
|
|
|
### Écran Live TV
|
|
```
|
|
[Erreur chargement chaînes]
|
|
✅ Toast 3s : "Impossible de charger les chaînes"
|
|
✅ Cache affiché si disponible
|
|
|
|
[Chaîne sans flux]
|
|
✅ Overlay : "Flux indisponible" + bouton "Chaîne suivante"
|
|
```
|
|
|
|
### Lecteur vidéo
|
|
```
|
|
[Flux mort pendant lecture]
|
|
✅ Remplacer par écran noir + message
|
|
✅ Bouton "Chaîne suivante" visible
|
|
✅ Retour possible vers le menu
|
|
```
|
|
|
|
## Règles de logging
|
|
|
|
| Niveau | Usage | Exemple |
|
|
|--------|-------|---------|
|
|
| `Timber.d` | Debug uniquement (pas en prod) | "Loading 42 live channels" |
|
|
| `Timber.i` | Événements importants | "User logged in: user123" |
|
|
| `Timber.w` | Anomalies non bloquantes | "EPG cache expired for stream 99" |
|
|
| `Timber.e` | Erreurs utilisateur | "Stream unavailable for stream 42" |
|
|
|
|
**❌ Interdit** : `Log.d("PASSWORD", password)` ou tout log contenant des credentials.
|
|
|
|
## Annexes
|
|
|
|
### Codes Xtream que l'on peut rencontrer
|
|
|
|
| Code | Signification | Action |
|
|
|------|--------------|--------|
|
|
| `auth = "0"` | Compte invalide | Message login |
|
|
| `status = "Expired"` | Abonnement expiré | Message distinct |
|
|
| `status = "Disabled"` | Compte désactivé | Message distinct |
|
|
| `tv_archive = 0` | Pas d'archive | Cacher bouton Replay |
|
|
| `has_archive = false` | Pas d'archive | Cacher bouton Replay |
|
|
| Réponse vide | Serveur HS ou mauvaise URL | Erreur réseau générique |
|