Files
embycovers/homescreen_editor/lib/server/tmdb-api.js
T
2026-06-08 21:58:16 +12:00

124 lines
4.0 KiB
JavaScript

import { readFileSync, existsSync } from 'fs';
import { resolve } from 'path';
const CONFIG_PATH = resolve('config.json');
const TMDB_BASE = 'https://api.themoviedb.org/3';
function loadConfig() {
if (!existsSync(CONFIG_PATH)) return {};
try {
return JSON.parse(readFileSync(CONFIG_PATH, 'utf-8'));
} catch {
return {};
}
}
export function loadTmdbConfig() {
const config = loadConfig();
return {
tmdbApiKey: String(config?.tmdbApiKey || '').trim()
};
}
export function hasTmdbConfig() {
return !!loadTmdbConfig().tmdbApiKey;
}
async function fetchTmdb(pathname, params = {}) {
const { tmdbApiKey } = loadTmdbConfig();
if (!tmdbApiKey) {
throw new Error('TMDB API key not configured');
}
const url = new URL(`${TMDB_BASE}${pathname.startsWith('/') ? pathname : `/${pathname}`}`);
for (const [key, value] of Object.entries({
api_key: tmdbApiKey,
language: 'en-US',
include_adult: 'false',
...params
})) {
if (value === undefined || value === null || value === '') continue;
url.searchParams.set(key, String(value));
}
const response = await fetch(url, {
headers: { Accept: 'application/json' }
});
if (!response.ok) {
const text = await response.text().catch(() => response.statusText);
throw new Error(text || `${response.status} ${response.statusText}`);
}
return response.json();
}
function normalizeTmdbSearchItem(item, mediaType) {
return {
tmdbId: Number(item?.id || 0) || null,
name: item?.title || item?.name || '',
year: Number(String(item?.release_date || item?.first_air_date || '').slice(0, 4)) || null,
mediaType,
overview: item?.overview || '',
genreIds: Array.isArray(item?.genre_ids) ? item.genre_ids : [],
originalLanguage: item?.original_language || '',
popularity: Number(item?.popularity || 0),
voteAverage: Number(item?.vote_average || 0),
voteCount: Number(item?.vote_count || 0)
};
}
export async function searchTmdbByTitle({ mediaType, name, year }) {
const searchType = mediaType === 'tv' ? 'tv' : 'movie';
const payload = await fetchTmdb(`/search/${searchType}`, {
query: name,
page: 1,
...(year ? (searchType === 'movie' ? { year } : { first_air_date_year: year }) : {})
});
return (payload?.results || []).map((item) => normalizeTmdbSearchItem(item, searchType));
}
export async function fetchTmdbSimilar({ mediaType, tmdbId, page = 1 }) {
const pathType = mediaType === 'tv' ? 'tv' : 'movie';
const payload = await fetchTmdb(`/${pathType}/${encodeURIComponent(tmdbId)}/similar`, { page });
return (payload?.results || []).map((item) => normalizeTmdbSearchItem(item, pathType));
}
export async function fetchTmdbRecommendations({ mediaType, tmdbId, page = 1 }) {
const pathType = mediaType === 'tv' ? 'tv' : 'movie';
const payload = await fetchTmdb(`/${pathType}/${encodeURIComponent(tmdbId)}/recommendations`, { page });
return (payload?.results || []).map((item) => normalizeTmdbSearchItem(item, pathType));
}
export async function fetchTmdbDetails({ mediaType, tmdbId }) {
const pathType = mediaType === 'tv' ? 'tv' : 'movie';
return fetchTmdb(`/${pathType}/${encodeURIComponent(tmdbId)}`);
}
export async function fetchTmdbKeywords({ mediaType, tmdbId }) {
const pathType = mediaType === 'tv' ? 'tv' : 'movie';
const payload = await fetchTmdb(`/${pathType}/${encodeURIComponent(tmdbId)}/keywords`);
return payload?.keywords || payload?.results || [];
}
export async function fetchTmdbCredits({ mediaType, tmdbId }) {
if (mediaType === 'tv') {
return fetchTmdb(`/tv/${encodeURIComponent(tmdbId)}/aggregate_credits`);
}
return fetchTmdb(`/movie/${encodeURIComponent(tmdbId)}/credits`);
}
export async function fetchTmdbGenres(mediaType) {
const pathType = mediaType === 'tv' ? 'tv' : 'movie';
const payload = await fetchTmdb(`/genre/${pathType}/list`, { language: 'en' });
return payload?.genres || [];
}
export async function fetchTmdbDiscover({ mediaType, params = {} }) {
const pathType = mediaType === 'tv' ? 'tv' : 'movie';
const payload = await fetchTmdb(`/discover/${pathType}`, params);
return (payload?.results || []).map((item) => normalizeTmdbSearchItem(item, pathType));
}