File size: 3,057 Bytes
02f8344
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
const STORAGE_KEY = 'shadowing-progress'
const META_KEY = 'shadowing-video-meta'

/**
 * @typedef {Object} ShadowingProgress
 * @property {string} video_id
 * @property {number} currentSegment
 * @property {number[]} dictationScores
 * @property {number[]} pronunciationScores
 * @property {number[]} flaggedSegments
 */

export function loadProgress(videoId) {
  try {
    const raw = localStorage.getItem(STORAGE_KEY)
    const all = raw ? JSON.parse(raw) : {}
    return all[videoId] || {
      video_id: videoId,
      currentSegment: 0,
      dictationScores: [],
      pronunciationScores: [],
      flaggedSegments: [],
    }
  } catch {
    return {
      video_id: videoId,
      currentSegment: 0,
      dictationScores: [],
      pronunciationScores: [],
      flaggedSegments: [],
    }
  }
}

export function saveProgress(videoId, patch) {
  try {
    const raw = localStorage.getItem(STORAGE_KEY)
    const all = raw ? JSON.parse(raw) : {}
    all[videoId] = { ...loadProgress(videoId), ...patch, video_id: videoId }
    localStorage.setItem(STORAGE_KEY, JSON.stringify(all))
  } catch { /* ignore */ }
}

export function saveVideoMeta(videoId, { title, level, sourceUrl } = {}) {
  try {
    const raw = localStorage.getItem(META_KEY)
    const all = raw ? JSON.parse(raw) : {}
    all[videoId] = {
      ...all[videoId],
      title: title ?? all[videoId]?.title,
      level: level ?? all[videoId]?.level,
      sourceUrl: sourceUrl ?? all[videoId]?.sourceUrl,
      lastVisitedAt: new Date().toISOString(),
    }
    localStorage.setItem(META_KEY, JSON.stringify(all))
  } catch { /* ignore */ }
}

/** Local fallback history from progress/meta keys. */
export function listLocalHistory() {
  try {
    const progRaw = localStorage.getItem(STORAGE_KEY)
    const metaRaw = localStorage.getItem(META_KEY)
    const prog = progRaw ? JSON.parse(progRaw) : {}
    const meta = metaRaw ? JSON.parse(metaRaw) : {}
    return Object.keys(prog)
      .map((videoId) => ({
        video_id: videoId,
        title: meta[videoId]?.title || videoId,
        level: meta[videoId]?.level || 'Intermediate',
        segment_count: 0,
        last_viewed_at: meta[videoId]?.lastVisitedAt || null,
        _local: true,
      }))
      .sort((a, b) => {
        const ta = a.last_viewed_at ? new Date(a.last_viewed_at).getTime() : 0
        const tb = b.last_viewed_at ? new Date(b.last_viewed_at).getTime() : 0
        return tb - ta
      })
  } catch {
    return []
  }
}

export function updateLocalHistoryMeta(videoId, { title, level } = {}) {
  saveVideoMeta(videoId, { title, level })
}

export function removeLocalHistory(videoId) {
  try {
    const progRaw = localStorage.getItem(STORAGE_KEY)
    const metaRaw = localStorage.getItem(META_KEY)
    const prog = progRaw ? JSON.parse(progRaw) : {}
    const meta = metaRaw ? JSON.parse(metaRaw) : {}
    delete prog[videoId]
    delete meta[videoId]
    localStorage.setItem(STORAGE_KEY, JSON.stringify(prog))
    localStorage.setItem(META_KEY, JSON.stringify(meta))
  } catch { /* ignore */ }
}