Files
sofa/packages/db/src/queries/user-platforms.ts
T
jake b0172a2b25 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
2026-03-24 20:54:34 -04:00

101 lines
2.8 KiB
TypeScript

import { asc, desc, eq, inArray } from "drizzle-orm";
import { db } from "../client";
import { platformTmdbIds, platforms, userPlatforms } from "../schema";
export function getUserPlatformIds(userId: string): string[] {
return db
.select({ platformId: userPlatforms.platformId })
.from(userPlatforms)
.where(eq(userPlatforms.userId, userId))
.all()
.map((r) => r.platformId);
}
export function getUserPlatforms(userId: string) {
return db
.select({
id: platforms.id,
name: platforms.name,
logoPath: platforms.logoPath,
urlTemplate: platforms.urlTemplate,
isSubscription: platforms.isSubscription,
})
.from(userPlatforms)
.innerJoin(platforms, eq(userPlatforms.platformId, platforms.id))
.where(eq(userPlatforms.userId, userId))
.orderBy(desc(platforms.isSubscription), asc(platforms.name))
.all();
}
export function setUserPlatforms(userId: string, platformIds: string[]): void {
db.transaction((tx) => {
tx.delete(userPlatforms).where(eq(userPlatforms.userId, userId)).run();
if (platformIds.length > 0) {
tx.insert(userPlatforms)
.values(platformIds.map((platformId) => ({ userId, platformId })))
.onConflictDoNothing()
.run();
}
});
}
export function getAllPlatforms() {
return db
.select()
.from(platforms)
.orderBy(desc(platforms.isSubscription), asc(platforms.name))
.all();
}
export function hasUserPlatforms(userId: string): boolean {
return (
db
.select({ platformId: userPlatforms.platformId })
.from(userPlatforms)
.where(eq(userPlatforms.userId, userId))
.limit(1)
.get() != null
);
}
export function platformIdsExist(platformIds: string[]): boolean {
if (platformIds.length === 0) return true;
const unique = [...new Set(platformIds)];
const found = db
.select({ id: platforms.id })
.from(platforms)
.where(inArray(platforms.id, unique))
.all();
return found.length === unique.length;
}
export function getTmdbProviderIdsForPlatform(platformId: string): number[] {
return db
.select({ tmdbProviderId: platformTmdbIds.tmdbProviderId })
.from(platformTmdbIds)
.where(eq(platformTmdbIds.platformId, platformId))
.all()
.map((r) => r.tmdbProviderId);
}
export function getTmdbProviderIdsByPlatformIds(platformIds: string[]): Map<string, number[]> {
if (platformIds.length === 0) return new Map();
const rows = db
.select({
platformId: platformTmdbIds.platformId,
tmdbProviderId: platformTmdbIds.tmdbProviderId,
})
.from(platformTmdbIds)
.where(inArray(platformTmdbIds.platformId, platformIds))
.all();
const map = new Map<string, number[]>();
for (const row of rows) {
const arr = map.get(row.platformId) ?? [];
arr.push(row.tmdbProviderId);
map.set(row.platformId, arr);
}
return map;
}