File size: 1,847 Bytes
d571830
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
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;
}