const STORE_KEY = "anicove:watch:progress"; const MAX_TITLES = 30; function read() { try { return JSON.parse(localStorage.getItem(STORE_KEY) || "{}"); } catch { return {}; } } function write(map) { try { localStorage.setItem(STORE_KEY, JSON.stringify(map)); } catch { /* storage full / blocked — fail silently */ } } /** One entry per title (anime or movie), newest episode wins. */ export const CW_KEY = { anime: (id) => `anime:${id}`, movie: (slug) => `movie:${slug}`, }; /** * entry: { * key, type ("anime"|"movie"), title, image (raw path — render via api.img), * href, label (e.g. "E3" / "S1 E2" / "Movie"), category ("sub"|"dub"|""), * position, duration (seconds), updatedAt (epoch ms), * episodeId (anime only), se/ep (movie series only) * } */ export function saveProgress(entry) { if (!entry || !entry.key) return; const map = read(); map[entry.key] = entry; const sorted = Object.values(map).sort((a, b) => (b.updatedAt || 0) - (a.updatedAt || 0)); if (sorted.length > MAX_TITLES) { const drop = new Set(sorted.slice(MAX_TITLES).map((e) => e.key)); for (const k of Object.keys(map)) if (drop.has(k)) delete map[k]; } write(map); } export function removeProgress(key) { if (!key) return; const map = read(); delete map[key]; write(map); } export function getProgress(key) { return read()[key] || null; } /** Meaningful, unfinished entries sorted newest-first. */ export function getContinueWatching() { return Object.values(read()) .filter((e) => e && e.key && e.duration > 0 && e.position > 5 && !isFinished(e)) .sort((a, b) => (b.updatedAt || 0) - (a.updatedAt || 0)); } /** Watched to the end (or effectively complete). */ export function isFinished(entry) { return entry?.duration > 0 && entry.position / entry.duration > 0.92; }