Files
sofa/lib/actions/explore.ts
T
jake 0838e5fb74 Replace hand-written TMDB types with openapi-fetch + generated schema
- Add `openapi-fetch` dependency; generate `lib/tmdb/schema.d.ts` from
  TMDB's OpenAPI spec and delete `lib/tmdb/types.ts`
- Rewrite `lib/tmdb/client.ts` to use the typed fetch client against
  the generated schema, exporting `TmdbMovieDetails`, `TmdbTvDetails`,
  `TmdbVideo`, and `TmdbGenre` directly
- Fix null-safety across services (`credits`, `availability`,
  `metadata`, `person`, `webhooks`) and call sites (explore page,
  search route, explore actions) to handle optional fields produced
  by the stricter generated types
- Update `metadata.ts` and `metadata.test.ts` to import shared types
  from `@/lib/tmdb/client` instead of the removed `types.ts`
- Exclude `lib/tmdb/schema.d.ts` from Biome linting
2026-03-08 14:07:32 -04:00

36 lines
1.1 KiB
TypeScript

"use server";
import { requireSession } from "@/lib/auth/session";
import { discover } from "@/lib/tmdb/client";
import { tmdbImageUrl } from "@/lib/tmdb/image";
export async function discoverByGenre(
mediaType: "movie" | "tv",
genreId: number,
) {
await requireSession();
const results = await discover(mediaType, {
sort_by: "popularity.desc",
"vote_count.gte": "50",
with_genres: String(genreId),
});
// Discover may return movie (title, release_date) or TV (name, first_air_date)
// fields depending on mediaType. The schema types them separately, so widen.
type DiscoverResult = NonNullable<typeof results.results>[number] & {
title?: string;
name?: string;
release_date?: string;
first_air_date?: string;
};
return ((results.results ?? []) as DiscoverResult[])
.filter((r) => r.poster_path)
.map((r) => ({
tmdbId: r.id,
type: mediaType,
title: r.title ?? r.name ?? "",
posterPath: tmdbImageUrl(r.poster_path ?? null, "posters"),
releaseDate: r.release_date ?? r.first_air_date ?? null,
voteAverage: r.vote_average,
}));
}