diff --git a/common/components/cards/SmallCard.svelte b/common/components/cards/SmallCard.svelte
index 3769cb5..8d6624e 100644
--- a/common/components/cards/SmallCard.svelte
+++ b/common/components/cards/SmallCard.svelte
@@ -37,6 +37,7 @@
{#if media.mediaListEntry?.status}
diff --git a/common/modules/animeprogress.js b/common/modules/animeprogress.js
new file mode 100644
index 0000000..66e3603
--- /dev/null
+++ b/common/modules/animeprogress.js
@@ -0,0 +1,69 @@
+import { writable, derived } from 'simple-store-svelte'
+
+// Maximum number of entries to keep in LocalStorage
+const maxEntries = 1000
+
+// LocalStorage is structured as an array of objects with the following properties:
+// mediaId, episode, currentTime, safeduration, createdAt, updatedAt
+function loadFromLocalStorage() {
+ const data = localStorage.getItem('animeEpisodeProgress')
+ return data ? JSON.parse(data) : []
+}
+
+function saveToLocalStorage(data) {
+ localStorage.setItem('animeEpisodeProgress', JSON.stringify(data))
+ animeProgressStore.set(data)
+}
+
+const animeProgressStore = writable(loadFromLocalStorage())
+
+// Return an object with the progress of each episode in percent (0-100), keyed by episode number
+export function liveAnimeProgress (mediaId){
+ return derived(animeProgressStore, (data) => {
+ if (!mediaId) return {}
+ const results = data.filter(item => item.mediaId === mediaId)
+ if (!results) return {}
+ // Return an object with the episode as the key and the progress as the value
+ return Object.fromEntries(results.map(result => [
+ result.episode,
+ Math.ceil(result.currentTime / result.safeduration * 100)
+ ]))
+ })
+}
+
+// Return an individual episode's progress in percent (0-100)
+export function liveAnimeEpisodeProgress (mediaId, episode) {
+ return derived(animeProgressStore, (data) => {
+ if (!mediaId || !episode) return 0
+ const result = data.find(item => item.mediaId === mediaId && item.episode === episode)
+ if (!result) return 0
+ return Math.ceil(result.currentTime / result.safeduration * 100)
+ })
+}
+
+// Return an individual episode's record { mediaId, episode, currentTime, safeduration, createdAt, updatedAt }
+export function getAnimeProgress(mediaId, episode) {
+ const data = loadFromLocalStorage()
+ return data.find(item => item.mediaId === mediaId && item.episode === episode)
+}
+
+// Set an individual episode's progress
+export function setAnimeProgress({ mediaId, episode, currentTime, safeduration }) {
+ if (!mediaId || !episode || !currentTime || !safeduration) return
+ const data = loadFromLocalStorage()
+ // Update the existing entry or create a new one
+ const existing = data.find(item => item.mediaId === mediaId && item.episode === episode)
+ if (existing) {
+ existing.currentTime = currentTime
+ existing.safeduration = safeduration
+ existing.updatedAt = Date.now()
+ } else {
+ data.push({ mediaId, episode, currentTime, safeduration, createdAt: Date.now(), updatedAt: Date.now() })
+ }
+ // Remove the oldest entries if we have too many
+ while (data.length > maxEntries) {
+ const oldest = data.reduce((a, b) => a.updatedAt < b.updatedAt ? a : b)
+ data.splice(data.indexOf(oldest), 1)
+ }
+ saveToLocalStorage(data)
+}
diff --git a/common/package.json b/common/package.json
index 02c9e26..6cfc163 100644
--- a/common/package.json
+++ b/common/package.json
@@ -23,4 +23,4 @@
"video-deband": "^1.0.5",
"webpack-merge": "^5.10.0"
}
-}
\ No newline at end of file
+}
diff --git a/common/views/Player/Player.svelte b/common/views/Player/Player.svelte
index 1daed25..7c98754 100644
--- a/common/views/Player/Player.svelte
+++ b/common/views/Player/Player.svelte
@@ -1,5 +1,6 @@
{#each episodeOrder ? episodeList : [...episodeList].reverse() as { episode, image, summary, rating, title, length, airdate }}
{@const completed = userProgress >= episode}
{@const target = userProgress + 1 === episode}
+ {@const progress = $animeProgress?.[episode] ?? 0}
play(episode)}>
{#if image}
@@ -67,6 +71,10 @@
+ {:else if progress}
+
{/if}
{summary || ''}