92 lines
2.8 KiB
TypeScript
92 lines
2.8 KiB
TypeScript
/**
|
|
* Fanart.tv client. Requires a personal API key (free, no email required
|
|
* but you do need to register at https://fanart.tv/get-an-api-key/ and
|
|
* obtain a "personal" key). The key is stored in user preferences and
|
|
* supplied to every request.
|
|
*
|
|
* Most-useful endpoints for our chrome:
|
|
* - /v3/movies/{tmdb_or_imdb_id} movie artwork (logos, clearart, banners)
|
|
* - /v3/tv/{tvdb_id} TV artwork (logos, clearart, banners)
|
|
*
|
|
* Each artwork item carries `lang` so we can pick English versions first
|
|
* when present, falling back to whatever the highest-voted entry is.
|
|
*/
|
|
|
|
const BASE = 'https://webservice.fanart.tv/v3'
|
|
|
|
export interface FanartImage {
|
|
id: string
|
|
url: string
|
|
lang?: string
|
|
likes?: string
|
|
/* Some artwork types include season number (TV) */
|
|
season?: string
|
|
}
|
|
|
|
export interface FanartMovieResponse {
|
|
name?: string
|
|
tmdb_id?: string
|
|
imdb_id?: string
|
|
hdmovielogo?: FanartImage[]
|
|
hdmovieclearart?: FanartImage[]
|
|
movielogo?: FanartImage[]
|
|
movieart?: FanartImage[]
|
|
movieposter?: FanartImage[]
|
|
moviebackground?: FanartImage[]
|
|
moviedisc?: FanartImage[]
|
|
moviebanner?: FanartImage[]
|
|
moviethumb?: FanartImage[]
|
|
}
|
|
|
|
export interface FanartTvResponse {
|
|
name?: string
|
|
thetvdb_id?: string
|
|
hdtvlogo?: FanartImage[]
|
|
clearlogo?: FanartImage[]
|
|
hdclearart?: FanartImage[]
|
|
clearart?: FanartImage[]
|
|
showbackground?: FanartImage[]
|
|
tvthumb?: FanartImage[]
|
|
tvbanner?: FanartImage[]
|
|
characterart?: FanartImage[]
|
|
seasonposter?: FanartImage[]
|
|
seasonbanner?: FanartImage[]
|
|
seasonthumb?: FanartImage[]
|
|
tvposter?: FanartImage[]
|
|
}
|
|
|
|
async function fetchJson<T>(url: string, apiKey: string): Promise<T | null> {
|
|
if (!apiKey) return null
|
|
try {
|
|
const res = await fetch(url, { headers: { 'api-key': apiKey } })
|
|
if (!res.ok) return null
|
|
return (await res.json()) as T
|
|
} catch {
|
|
return null
|
|
}
|
|
}
|
|
|
|
export function fanartMovie(idTmdbOrImdb: string, apiKey: string): Promise<FanartMovieResponse | null> {
|
|
if (!idTmdbOrImdb) return Promise.resolve(null)
|
|
return fetchJson<FanartMovieResponse>(
|
|
`${BASE}/movies/${encodeURIComponent(idTmdbOrImdb)}`,
|
|
apiKey,
|
|
)
|
|
}
|
|
|
|
export function fanartTv(tvdbId: string, apiKey: string): Promise<FanartTvResponse | null> {
|
|
if (!tvdbId) return Promise.resolve(null)
|
|
return fetchJson<FanartTvResponse>(`${BASE}/tv/${encodeURIComponent(tvdbId)}`, apiKey)
|
|
}
|
|
|
|
/**
|
|
* Pick the best image from a list: prefer English entries, then
|
|
* highest `likes` count, falling back to first in list.
|
|
*/
|
|
export function pickBestFanartImage(images?: FanartImage[]): FanartImage | null {
|
|
if (!images || images.length === 0) return null
|
|
const eng = images.filter(i => (i.lang || '').toLowerCase() === 'en')
|
|
const pool = eng.length > 0 ? eng : images
|
|
return [...pool].sort((a, b) => Number(b.likes ?? 0) - Number(a.likes ?? 0))[0] || null
|
|
}
|