157 lines
4.4 KiB
TypeScript
157 lines
4.4 KiB
TypeScript
const TMDB_BASE = "https://api.themoviedb.org/3";
|
|
const LANGUAGE = "en-US";
|
|
|
|
export interface TmdbSearchResult {
|
|
id: number;
|
|
title: string;
|
|
release_date: string;
|
|
poster_path: string | null;
|
|
overview: string;
|
|
}
|
|
|
|
export interface TmdbMovieDetails {
|
|
id: number;
|
|
title: string;
|
|
release_date: string;
|
|
runtime: number | null;
|
|
overview: string;
|
|
poster_path: string | null;
|
|
backdrop_path: string | null;
|
|
genres: Array<{ id: number; name: string }>;
|
|
budget: number;
|
|
revenue: number;
|
|
production_countries: Array<{ iso_3166_1: string; name: string }>;
|
|
production_companies: Array<{ name: string }>;
|
|
spoken_languages: Array<{ english_name: string }>;
|
|
release_dates?: {
|
|
results: Array<{
|
|
iso_3166_1: string;
|
|
release_dates: Array<{
|
|
certification: string;
|
|
type: number;
|
|
note: string;
|
|
}>;
|
|
}>;
|
|
};
|
|
keywords?: {
|
|
keywords: Array<{ id: number; name: string }>;
|
|
};
|
|
credits?: {
|
|
cast: Array<{ name: string; character: string }>;
|
|
crew: Array<{ name: string; job: string; department: string }>;
|
|
};
|
|
}
|
|
|
|
function getApiKey(): string {
|
|
const key = process.env.TMDB_API_KEY;
|
|
if (!key) {
|
|
throw new Error("TMDB_API_KEY is not set");
|
|
}
|
|
return key;
|
|
}
|
|
|
|
async function tmdbFetch<T>(path: string, params: Record<string, string> = {}) {
|
|
const url = new URL(`${TMDB_BASE}${path}`);
|
|
url.searchParams.set("api_key", getApiKey());
|
|
url.searchParams.set("language", LANGUAGE);
|
|
|
|
for (const [key, value] of Object.entries(params)) {
|
|
url.searchParams.set(key, value);
|
|
}
|
|
|
|
const response = await fetch(url.toString(), {
|
|
next: { revalidate: 3600 },
|
|
});
|
|
|
|
if (!response.ok) {
|
|
throw new Error(`TMDB request failed: ${response.status}`);
|
|
}
|
|
|
|
return response.json() as Promise<T>;
|
|
}
|
|
|
|
export async function searchMovies(query: string): Promise<TmdbSearchResult[]> {
|
|
if (!query.trim()) {
|
|
return [];
|
|
}
|
|
|
|
const data = await tmdbFetch<{ results: TmdbSearchResult[] }>("/search/movie", {
|
|
query: query.trim(),
|
|
include_adult: "false",
|
|
});
|
|
|
|
return data.results.slice(0, 8);
|
|
}
|
|
|
|
export async function getMovieDetails(movieId: number): Promise<TmdbMovieDetails> {
|
|
return tmdbFetch<TmdbMovieDetails>(`/movie/${movieId}`, {
|
|
append_to_response: "release_dates,credits,keywords",
|
|
});
|
|
}
|
|
|
|
export function getPosterUrl(path: string | null, size: "w185" | "w500" | "original" = "w500") {
|
|
if (!path) {
|
|
return null;
|
|
}
|
|
return `https://image.tmdb.org/t/p/${size}${path}`;
|
|
}
|
|
|
|
export function getUsCertification(movie: TmdbMovieDetails): string | null {
|
|
const us = movie.release_dates?.results.find((r) => r.iso_3166_1 === "US");
|
|
if (!us) {
|
|
return null;
|
|
}
|
|
|
|
const theatrical = us.release_dates.find((r) => r.type === 3 && r.certification);
|
|
if (theatrical?.certification) {
|
|
return theatrical.certification;
|
|
}
|
|
|
|
const any = us.release_dates.find((r) => r.certification);
|
|
return any?.certification ?? null;
|
|
}
|
|
|
|
export function formatMovieContext(movie: TmdbMovieDetails): string {
|
|
const genres = movie.genres.map((g) => g.name).join(", ");
|
|
const keywords = movie.keywords?.keywords.map((k) => k.name).join(", ") ?? "";
|
|
const certification = getUsCertification(movie);
|
|
const directors = movie.credits?.crew
|
|
.filter((c) => c.job === "Director")
|
|
.map((c) => c.name)
|
|
.join(", ");
|
|
const leadCast = movie.credits?.cast
|
|
.slice(0, 5)
|
|
.map((c) => `${c.name} as ${c.character}`)
|
|
.join("; ");
|
|
|
|
const releaseDates = movie.release_dates?.results
|
|
.map((country) => {
|
|
const certs = country.release_dates
|
|
.filter((r) => r.certification)
|
|
.map((r) => r.certification);
|
|
const unique = [...new Set(certs)];
|
|
if (unique.length === 0) {
|
|
return null;
|
|
}
|
|
return `${country.iso_3166_1}: ${unique.join(", ")}`;
|
|
})
|
|
.filter(Boolean)
|
|
.join("\n");
|
|
|
|
return [
|
|
`Title: ${movie.title}`,
|
|
`Year: ${movie.release_date?.slice(0, 4) ?? "Unknown"}`,
|
|
`Runtime: ${movie.runtime ?? "Unknown"} minutes`,
|
|
`Genres: ${genres}`,
|
|
`Overview: ${movie.overview}`,
|
|
certification ? `US Certification: ${certification}` : null,
|
|
keywords ? `Keywords: ${keywords}` : null,
|
|
directors ? `Director(s): ${directors}` : null,
|
|
leadCast ? `Lead cast: ${leadCast}` : null,
|
|
movie.budget ? `Budget: $${movie.budget.toLocaleString("en-US")}` : null,
|
|
movie.revenue ? `Revenue: $${movie.revenue.toLocaleString("en-US")}` : null,
|
|
releaseDates ? `International certifications:\n${releaseDates}` : null,
|
|
]
|
|
.filter(Boolean)
|
|
.join("\n");
|
|
} |