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
+70
View File
@@ -0,0 +1,70 @@
import { createContext, useContext, type ReactNode } from 'react'
import type { CinemetaEpisode } from '../api/cinemeta'
import type { FillerFlag } from './anime-filler'
/**
* Per-page-scoped enrichment for an episode list. DetailPage builds this
* once for the current series and provides it; EpisodeRow + EpisodesPanel
* read via the hooks so we don't have to drill three things through three
* layers of components.
*
* Carries:
* - `cinemetaMap`: Cinemeta video lookup keyed by `${season}:${episode}`
* - `fillerOf`: pure function (season, episode) -> FillerFlag, computed
* against the bundled anime-filler list using absolute episode number
* derivation from the season-length map the provider builds.
*/
type CinemetaMap = Map<string, CinemetaEpisode>
type FillerOf = (season: number | null | undefined, episode: number | null | undefined) => FillerFlag
interface EpisodeMetaValue {
cinemetaMap: CinemetaMap
fillerOf: FillerOf
}
const DEFAULT: EpisodeMetaValue = {
cinemetaMap: new Map(),
fillerOf: () => null,
}
const EpisodeMetaContext = createContext<EpisodeMetaValue>(DEFAULT)
export function EpisodeMetaProvider({
cinemetaMap,
fillerOf,
children,
}: {
cinemetaMap: CinemetaMap
fillerOf: FillerOf
children: ReactNode
}) {
return (
<EpisodeMetaContext.Provider value={{ cinemetaMap, fillerOf }}>
{children}
</EpisodeMetaContext.Provider>
)
}
export function useEpisodeMeta(): EpisodeMetaValue {
return useContext(EpisodeMetaContext)
}
/** Convenience: pull a single episode's Cinemeta entry from context. */
export function useCinemetaEpisode(
season: number | null | undefined,
episode: number | null | undefined,
): CinemetaEpisode | null {
const { cinemetaMap } = useEpisodeMeta()
if (season == null || episode == null) return null
return cinemetaMap.get(`${season}:${episode}`) || null
}
/** Convenience: classify a single episode against the filler list. */
export function useFillerFlag(
season: number | null | undefined,
episode: number | null | undefined,
): FillerFlag {
const { fillerOf } = useEpisodeMeta()
return fillerOf(season, episode)
}