# Catalog Relay Routing: ctmdb.* Prefix Fix

## The Bug

Android TV app shows 404 errors for catalog rows from the TMDB Collections Stremio addon:
```
Catalog service HTTP failure for collections/ctmdb.newReleases: 404
Catalog service HTTP failure for collections/ctmdb.popular: 404
Catalog service HTTP failure for collections/ctmdb.topRated: 404
```

The `ctmdb.*` catalog IDs come from an external Stremio addon (`tmdb-collections`). The Android app proxies them through a Cloudflare Worker relay, but the worker only accepts `movie|series|tv` types, not `collections`.

## Root Cause (3-layer)

1. **Worker regex too narrow**: `/^\/v1\/catalog\/(movie|series|tv)\/([^/]+)$/` — rejects `collections` type
2. **Android routing to worker unnecessarily**: `isNativeCatalogId()` doesn't include `ctmdb.` prefix, so these catalogs go through the expensive worker relay instead of being handled locally
3. **Worker doesn't know ctmdb IDs**: Even if the regex matched, `fetchCatalogWithKey()` has no case for `ctmdb.newReleases` → returns empty `[]`

## Fix: Handle Locally, Skip Worker

The `ctmdb.*` catalogs are just TMDB catalogs with a different prefix. They should use the local TMDB API directly, bypassing the worker entirely.

### Step 1: Add `ctmdb.` to native catalog detection

```kotlin
// AddonRepository.kt — Tier 1 check
if (catalogId.startsWith("tmdb.") || catalogId.startsWith("ctmdb.") || catalogId.startsWith("mdblist.")) {
    val tmdbResult = tmdbCatalog.getCatalogRow(type, catalogId, extra)
    if (tmdbResult.isNotEmpty()) return Result.success(tmdbResult)
}
```

### Step 2: Add `ctmdb.` to isNativeCatalogId (skip worker relay)

```kotlin
private fun isNativeCatalogId(catalogId: String): Boolean =
    catalogId.startsWith("tmdb.") ||
        catalogId.startsWith("ctmdb.") ||  // ← ADDED
        catalogId.startsWith("trakt.") ||
        catalogId.startsWith("anilist.") ||
        catalogId.startsWith("mdblist.")
```

### Step 3: Normalize ctmdb → tmdb prefix

```kotlin
// TmdbCatalogRepository.kt
private fun normalizeCatalogId(catalogId: String): String = when (catalogId) {
    "netflix", "ott_netflix" -> "tmdb.ott.netflix"
    // ... existing mappings ...
    else -> catalogId.replaceFirst("ctmdb.", "tmdb.")  // ← strip 'c' prefix
}
```

### Step 4 (safety net): Worker regex accepts collections

```typescript
// catalog-worker/src/index.ts
const match = url.pathname.match(
    /^\/v1\/catalog\/(movie|series|tv|collections)\/([^/]+)$/
);
export type CatalogType = 'movie' | 'series' | 'tv' | 'collections';
```

## Why This Works

`ctmdb.popular` → normalized to `tmdb.popular` → matches `case 'tmdb.popular'` in `fetchCatalogWithKey()` → calls `/movie/popular` on TMDB API → returns correct results.

## General Rule

When an Android TV app uses a Cloudflare Worker relay for catalog data, check whether each catalog ID prefix should actually be handled locally. Local TMDB API calls are faster (no network hop to worker) and don't depend on worker deployment state.
