# 1:1 UI Copy Pattern — homeflix-tv-app → NexStream (2026-05-21)

## Source Repo
`https://github.com/azad25/homeflix-tv-app` — Android TV / Kotlin / Jetpack Compose / Hilt / Coil / ExoPlayer 3

Same tech stack as NexStream. Components can be adapted 1:1 with field-name and package mapping.

## Field Mapping (homeflix Media → NexStream MetaPreview)

| homeflix `Media` field | NexStream `MetaPreview` field | Type change |
|------------------------|------------------------------|-------------|
| `title: String` | `name: String` | Same |
| `year: Int?` | `releaseInfo: String?` | Int → String (e.g., `"2024"`) |
| `rating: Float` | `imdbRating: String?` | Float → String (e.g., `"8.5"`) |
| `genreNames: List<String>` | `genres: List<String>?` | Nullable |
| `description: String?` | `description: String?` | Same |
| `id: Int` | `id: String` | Int → String |
| `type: MediaType` | `type: String` | Enum → String (`"movie"`, `"series"`) |
| `quality: String?` | NOT AVAILABLE | Drop the badge |
| `certification: String?` | NOT AVAILABLE | Drop the badge |
| `runtime: Int?` | NOT AVAILABLE | Drop |

## Image URL Construction

homeflix uses `ApiUtils.getPosterUrl()`, `ApiUtils.getBannerUrl()`, `ApiUtils.getLogoUrl()` — these construct TMDB URLs internally.

NexStream equivalent:
```kotlin
val posterBase = "https://image.tmdb.org/t/p/w342"
val backdropBase = "https://image.tmdb.org/t/p/w1280"
val logoBase = "https://image.tmdb.org/t/p/w500"

val posterUrl = item.poster?.let { "$posterBase$it" }
val backdropUrl = item.background?.let { "$backdropBase$it" }
val logoUrl = item.logo?.let { "$logoBase$it" }
```

## Serialization Differences

| homeflix | NexStream | Action |
|----------|-----------|--------|
| Gson `@SerializedName` | kotlinx.serialization `@SerialName` | Strip Gson annotations — project has no Gson dependency |
| `coil.compose.AsyncImage` | `coil3.compose.AsyncImage` | Coil 3 package name |
| `coil.request.ImageRequest` | `coil3.request.ImageRequest` | Coil 3 package name |
| `.crossfade(true)` on ImageRequest.Builder | NOT AVAILABLE in Coil 3 | Strip the `.crossfade()` call |

## Components Copied 1:1 (and where)

| homeflix Source File | → | NexStream Target File |
|----------------------|---|----------------------|
| `NetflixSideNavigation.kt` (138 lines) | → | `ui/components/netflix/NetflixSideNavigation.kt` |
| `NetflixHeroSection.kt` (404 lines) | → | `ui/components/netflix/NetflixHeroSection.kt` |
| `MediaCard.kt` / `NetflixMediaCard()` (186 lines) | → | `ui/components/netflix/NetflixMediaCard.kt` |
| `MediaRow.kt` (143 lines) | → | Inlined in `NetflixHomeScreen.kt` as `HomeRowSection()` |
| `ContinueWatchingRow.kt` (359 lines) | → | `ui/components/netflix/ContinueWatchingCard.kt` |
| `NetflixHomeScreen.kt` (416 lines) | → | `ui/screens/home/NetflixHomeScreen.kt` |
| `SearchScreen.kt` (724 lines) | → | `ui/screens/search/NetflixSearchScreen.kt` |

## Navigation Adaptation

homeflix uses `Screen.Home.route`, `Screen.Search.route`, etc. with its own sealed class.
NexStream uses `Screen.Home.route`, etc. from `com.nexstream.app.ui.navigation.Screen`.

Key difference: NexStream screens take `navController: NavHostController` not `NavController`.
Also: `Screen.Player.createRoute(type, id, streamIndex: Int)` — streamIndex is Int, not String.

## Service Patterns Copied from Flixorent

| Flixorent Source | → | NexStream Target |
|-----------------|---|-----------------|
| `RealDebridApi.kt` | → | `data/debrid/RealDebridApiV2.kt` |
| `PremiumizeApi.kt` | → | `data/debrid/PremiumizeApiV2.kt` |
| `TorBoxApi.kt` | → | `data/debrid/TorBoxApiV2.kt` |
| `TraktTokenManager.kt` | → | `data/trakt/TraktTokenManager.kt` |
| `TraktApi.kt` | → | `data/trakt/TraktSyncApi.kt` |

## Build-Fix Iteration Pattern Used

1. Write files in batch using `execute_code` + `write_file`
2. Run `./gradlew assembleDebug 2>&1 | tail -40`
3. Read top error, fix, rebuild
4. Repeat until green

Most common fixes in this session (in order):
1. `coil.compose` → `coil3.compose` (Coil 3 import)
2. Strip `@SerializedName` (Gson not available)
3. Strip `.crossfade()` (not in Coil 3)
4. Fix field names: `year`→`releaseInfo`, `rating`→`imdbRating`, `genreNames`→`genres`
5. Fix type mismatch: `"0"` → `0` for streamIndex
6. Fix Kotlin string escaping: `\"` in text strings
7. Rename clashing DTOs: `TorBoxTorrent`→`TorBoxTorrentV2`, `TraktHistoryItem`→`TraktSyncHistoryItem`

## Stale File Cleanup

The old `ui/components/NetflixSideNavigation.kt` (outside the `netflix/` package) was a duplicate from a previous session and was removed. The canonical copy lives at `ui/components/netflix/NetflixSideNavigation.kt`.
