api clients for jellyfin, tmdb, fanart, etc.
This commit is contained in:
@@ -0,0 +1,91 @@
|
||||
/**
|
||||
* 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
|
||||
}
|
||||
Reference in New Issue
Block a user