aniqove / frontend /src /utils /watchProgress.js
mwask's picture
Upload 59 files
d571830 verified
Raw
History Blame Contribute Delete
1.85 kB
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;
}