formatters, device profile, media matching, subtitle utils, syncplay, trakt

This commit is contained in:
2026-03-24 10:48:59 +02:00
parent 292b3f42cf
commit 996a85de76
41 changed files with 4306 additions and 0 deletions
+61
View File
@@ -0,0 +1,61 @@
import { getTmdbImageUrl } from '../api/tmdb'
import type { BaseItemDto } from '../api/types'
/**
* Convert a TMDB result row (movie / tv / multi) into a synthetic
* BaseItemDto so it can render through the same PosterCard pipeline as
* Jellyfin items. When a `libraryMap` (TMDB id -> Jellyfin item) is
* provided, items already in the user's library are flagged via
* `_inLibrary` and their `Id` is rewritten to the local Jellyfin id so
* clicking opens the existing detail page instead of the synthetic one.
*/
export function mapTmdbToJf(
items: any[],
libraryMap?: Map<string, { id: string; name: string; type: string }>,
): BaseItemDto[] {
return items.map(m => {
const local = libraryMap?.get(String(m.id))
const isSeries = m.media_type === 'tv' || !!m.first_air_date
// Encode kind in the synthetic id so DetailPage can route directly
// to the right TMDB endpoint without guessing. Library matches keep
// the Jellyfin id as before.
const syntheticId = `tmdb-${isSeries ? 'tv' : 'movie'}-${m.id}`
return {
Id: local?.id || syntheticId,
Name: m.title || m.name,
Type: isSeries ? 'Series' : 'Movie',
ProductionYear: m.release_date
? parseInt(m.release_date)
: m.first_air_date
? parseInt(m.first_air_date)
: undefined,
Overview: m.overview,
ImageTags: {},
ProviderIds: { Tmdb: String(m.id) },
CommunityRating: typeof m.vote_average === 'number' ? m.vote_average : undefined,
_tmdbPoster: m.poster_path ? getTmdbImageUrl(m.poster_path, 'w342') : undefined,
_tmdbBackdrop: m.backdrop_path ? getTmdbImageUrl(m.backdrop_path, 'w780') : undefined,
_inLibrary: !!local,
_tmdbId: String(m.id),
} as any as BaseItemDto
})
}
/**
* Detect whether a route id is a synthetic TMDB id (from the discovery
* rows) and parse out kind + numeric id when so. Returns null for
* regular Jellyfin ids.
*/
export function parseTmdbRouteId(id: string | undefined | null): { kind: 'movie' | 'tv'; tmdbId: number } | null {
if (!id) return null
// New format: tmdb-movie-123 / tmdb-tv-123
const match = id.match(/^tmdb-(movie|tv)-(\d+)$/)
if (match) {
return { kind: match[1] as 'movie' | 'tv', tmdbId: Number(match[2]) }
}
// Legacy format: tmdb-123 (kind unknown - default to movie). Persists
// until any saved data with the old id refreshes.
const legacy = id.match(/^tmdb-(\d+)$/)
if (legacy) return { kind: 'movie', tmdbId: Number(legacy[1]) }
return null
}