feat: refactor platforms to support multiple TMDB provider IDs per platform

- Replace `tmdbProviderId` (single int) with a many-to-many `platformTmdbProviders` join table so one platform entry can map to multiple TMDB provider IDs (e.g. a single "Max" platform covers several regional TMDB IDs)
- Add `isSubscription` flag to platforms to distinguish subscription services from transactional ones, replacing the old `displayOrder` field in API responses
- Add `getPlatformTmdbIds` / `getPlatformTmdbIdMap` helpers in `@sofa/core/platforms` and thread them through discover, explore, and platform list procedures
- Replace `providerId: number` with `platformId: string` (UUID) in discover input schema so the client never needs to resolve TMDB IDs
- Add `scripts/sync-tmdb-providers.ts` script to pull live provider data from TMDB and update `platforms.json`
- Split native title detail availability into separate "Stream" and "Buy or Rent" sections matching the web pattern
- Add two new DB migrations for the join table and platform schema changes
This commit is contained in:
2026-03-24 20:54:34 -04:00
parent 7fedccb4fd
commit b0172a2b25
58 changed files with 8291 additions and 907 deletions
+22 -32
View File
@@ -2046,7 +2046,7 @@
},
"offerType": {
"type": "string",
"description": "Offer type: flatrate, rent, buy, free, ads"
"description": "Offer category: stream or purchase"
},
"watchUrl": {
"anyOf": [
@@ -4343,16 +4343,12 @@
"type": "string",
"description": "Platform ID"
},
"tmdbProviderId": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
],
"description": "TMDB provider ID"
"tmdbProviderIds": {
"type": "array",
"items": {
"type": "number"
},
"description": "TMDB provider IDs"
},
"name": {
"type": "string",
@@ -4372,7 +4368,7 @@
},
"required": [
"id",
"tmdbProviderId",
"tmdbProviderIds",
"name",
"logoPath"
]
@@ -4861,14 +4857,12 @@
"allowReserved": true
},
{
"name": "providerId",
"name": "platformId",
"in": "query",
"required": false,
"schema": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "TMDB watch provider ID"
"type": "string",
"description": "Platform ID to filter by"
},
"allowEmptyValue": true,
"allowReserved": true
@@ -6773,16 +6767,12 @@
"type": "string",
"description": "Display name"
},
"tmdbProviderId": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
],
"description": "TMDB provider ID (null for custom platforms)"
"tmdbProviderIds": {
"type": "array",
"items": {
"type": "number"
},
"description": "TMDB provider IDs mapped to this platform"
},
"logoPath": {
"anyOf": [
@@ -6795,17 +6785,17 @@
],
"description": "Logo image path"
},
"displayOrder": {
"type": "number",
"description": "Sort order"
"isSubscription": {
"type": "boolean",
"description": "True for subscription services, false for purchase/rental stores"
}
},
"required": [
"id",
"name",
"tmdbProviderId",
"tmdbProviderIds",
"logoPath",
"displayOrder"
"isSubscription"
],
"description": "A streaming platform"
}