first commit
This commit is contained in:
+157
@@ -0,0 +1,157 @@
|
||||
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");
|
||||
}
|
||||
Reference in New Issue
Block a user