mirror of
https://github.com/NoCrypt/migu.git
synced 2026-04-17 23:01:23 +00:00
refactor: switch episode progress tracking database from Dexie.js to LocalStorage
This commit is contained in:
parent
b140fbc03d
commit
35da5a2ca6
9 changed files with 60 additions and 70 deletions
|
|
@ -2,9 +2,9 @@
|
|||
import { statusColorMap } from '@/modules/anime.js'
|
||||
import EpisodePreviewCard from './EpisodePreviewCard.svelte'
|
||||
import { hoverClick } from '@/modules/click.js'
|
||||
import { since } from '@/modules/util'
|
||||
import { since } from '@/modules/util.js'
|
||||
import { getContext } from 'svelte'
|
||||
import { liveAnimeEpisodeProgress } from "@/modules/animeprogress";
|
||||
import { liveAnimeEpisodeProgress } from '@/modules/animeprogress.js'
|
||||
export let data
|
||||
|
||||
let preview = false
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<script>
|
||||
import { statusColorMap, formatMap } from '@/modules/anime.js'
|
||||
import { since } from '@/modules/util'
|
||||
import { liveAnimeEpisodeProgress } from "@/modules/animeprogress";
|
||||
import { liveAnimeEpisodeProgress } from '@/modules/animeprogress.js'
|
||||
export let data
|
||||
|
||||
const media = data.media
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
import { formatMap, setStatus, playMedia, getMediaMaxEp } from '@/modules/anime.js'
|
||||
import { alRequest } from '@/modules/anilist.js'
|
||||
import { click } from '@/modules/click.js'
|
||||
import { liveAnimeEpisodeProgress } from "@/modules/animeprogress";
|
||||
import { liveAnimeEpisodeProgress } from '@/modules/animeprogress.js'
|
||||
export let media
|
||||
|
||||
let hide = true
|
||||
|
|
@ -84,7 +84,7 @@
|
|||
src={`https://www.youtube-nocookie.com/embed/${media.trailer?.id}?autoplay=1&controls=0&mute=1&disablekb=1&loop=1&vq=medium&playlist=${media.trailer?.id}`}
|
||||
/> -->
|
||||
{#if progress && $progress > 0}
|
||||
<div class="progress container-fluid position-absolute" style="margin-top: -1.5rem">
|
||||
<div class=" position-absolute" style="margin-top: -1.5rem">
|
||||
<div class="progress-bar" style="width: {$progress}%"></div>
|
||||
</div>
|
||||
{/if}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
import { formatMap, statusColorMap, getMediaMaxEp } from '@/modules/anime.js'
|
||||
import { hoverClick } from '@/modules/click.js'
|
||||
import { countdown } from '@/modules/util.js'
|
||||
import { liveAnimeEpisodeProgress } from "@/modules/animeprogress";
|
||||
import { liveAnimeEpisodeProgress } from '@/modules/animeprogress.js'
|
||||
|
||||
import { page } from '@/App.svelte'
|
||||
|
||||
|
|
|
|||
|
|
@ -1,59 +1,68 @@
|
|||
import Dexie, { liveQuery } from 'dexie';
|
||||
import { writable, derived } from 'simple-store-svelte'
|
||||
|
||||
export const db = new Dexie('miru');
|
||||
// Maximum number of entries to keep in LocalStorage
|
||||
const maxEntries = 1000
|
||||
|
||||
db.version(1).stores({
|
||||
animeEpisodeProgress: '[mediaId+episode], currentTime, safeduration, createdAt, updatedAt', // mediaId and episode is the composite key
|
||||
});
|
||||
// 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 liveQuery(async () => {
|
||||
if (!mediaId) return {}
|
||||
const results = await db.animeEpisodeProgress.where({ mediaId }).toArray();
|
||||
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 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 liveQuery(async () => {
|
||||
return derived(animeProgressStore, (data) => {
|
||||
if (!mediaId || !episode) return 0
|
||||
const result = await getAnimeProgress(mediaId, episode)
|
||||
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) {
|
||||
return db.animeEpisodeProgress.where({ mediaId, episode }).first();
|
||||
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 async function setAnimeProgress ({mediaId, episode, currentTime, safeduration}) {
|
||||
// If you want to update the record if it already exists:
|
||||
const existing = await getAnimeProgress(mediaId, episode);
|
||||
export function setAnimeProgress({ mediaId, episode, currentTime, safeduration }) {
|
||||
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) {
|
||||
return await db.animeEpisodeProgress.update([ mediaId, episode ], {
|
||||
currentTime,
|
||||
safeduration,
|
||||
updatedAt: Date.now(),
|
||||
});
|
||||
existing.currentTime = currentTime
|
||||
existing.safeduration = safeduration
|
||||
existing.updatedAt = Date.now()
|
||||
} else {
|
||||
data.push({ mediaId, episode, currentTime, safeduration, createdAt: Date.now(), updatedAt: Date.now() })
|
||||
}
|
||||
|
||||
// If you want to insert a new record if it does not exist:
|
||||
return await db.animeEpisodeProgress.put({
|
||||
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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@
|
|||
"anitomyscript": "github:ThaUnknown/anitomyscript#42290c4b3f256893be08a4e89051f448ff5e9d00",
|
||||
"bottleneck": "^2.19.5",
|
||||
"browser-event-target-emitter": "^1.0.1",
|
||||
"dexie": "4.0.1-beta.6",
|
||||
"jassub": "latest",
|
||||
"js-levenshtein": "^1.1.6",
|
||||
"p2pt": "github:ThaUnknown/p2pt#modernise",
|
||||
|
|
@ -24,4 +23,4 @@
|
|||
"video-deband": "^1.0.5",
|
||||
"webpack-merge": "^5.10.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -203,7 +203,7 @@
|
|||
async function loadAnimeProgress () {
|
||||
const mediaId = current?.media?.media?.id
|
||||
const episode = current?.media?.episode
|
||||
if (mediaId !== media?.media?.id || episode !== media?.episode) return
|
||||
if (current?.media.failed || mediaId !== media?.media?.id || episode !== media?.episode) return
|
||||
const animeProgress = await getAnimeProgress(mediaId, episode)
|
||||
if (!animeProgress) return
|
||||
const currentTime = Math.max(animeProgress.currentTime - 5, 0) // Load 5 seconds before
|
||||
|
|
@ -212,8 +212,9 @@
|
|||
|
||||
function saveAnimeProgress () {
|
||||
const mediaId = current?.media?.media?.id
|
||||
const episode = current?.media.episode
|
||||
if (mediaId !== media?.media?.id || episode !== media?.episode || buffering || paused || video.readyState < 4) return
|
||||
const episode = current?.media?.episode
|
||||
if (current?.media?.failed || mediaId !== media?.media?.id || episode !== media?.episode) return
|
||||
if (buffering || paused || video.readyState < 4) return
|
||||
setAnimeProgress({ mediaId, episode, currentTime: video.currentTime, safeduration })
|
||||
}
|
||||
setInterval(saveAnimeProgress, 1000)
|
||||
|
|
|
|||
|
|
@ -2,8 +2,8 @@
|
|||
import { since } from '@/modules/util'
|
||||
import { click } from '@/modules/click.js'
|
||||
import { getEpisodeNumberByAirDate } from '@/modules/providers/tosho.js'
|
||||
import { alRequest } from '@/modules/anilist'
|
||||
import { liveAnimeProgress } from "@/modules/animeprogress";
|
||||
import { alRequest } from '@/modules/anilist.js'
|
||||
import { liveAnimeProgress } from '@/modules/animeprogress.js'
|
||||
|
||||
export let media
|
||||
|
||||
|
|
|
|||
|
|
@ -151,9 +151,6 @@ importers:
|
|||
browser-event-target-emitter:
|
||||
specifier: ^1.0.1
|
||||
version: 1.0.1
|
||||
dexie:
|
||||
specifier: 4.0.1-beta.6
|
||||
version: 4.0.1-beta.6
|
||||
jassub:
|
||||
specifier: latest
|
||||
version: 1.7.15
|
||||
|
|
@ -2137,7 +2134,6 @@ packages:
|
|||
|
||||
/bindings@1.5.0:
|
||||
resolution: {integrity: sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==}
|
||||
requiresBuild: true
|
||||
dependencies:
|
||||
file-uri-to-path: 1.0.0
|
||||
dev: true
|
||||
|
|
@ -2324,7 +2320,6 @@ packages:
|
|||
|
||||
/boolean@3.2.0:
|
||||
resolution: {integrity: sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw==}
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
|
|
@ -3465,10 +3460,6 @@ packages:
|
|||
resolution: {integrity: sha512-KqFl6pOgOW+Y6wJgu80rHpo2/3H07vr8ntR9rkkFIRETewbf5GaYYcakYfiKz89K+sLsuPkQIZaXDMjUObZwWg==}
|
||||
dev: true
|
||||
|
||||
/dexie@4.0.1-beta.6:
|
||||
resolution: {integrity: sha512-NOexH4Rn8mrdSg/bn3YNFdPzQ1vPNxgPYLGWGU46z26NYGW1XmC0hhjjttwx9jYwba1K9Ypo1ZbZLNKtK6INSg==}
|
||||
dev: false
|
||||
|
||||
/diff@4.0.2:
|
||||
resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==}
|
||||
engines: {node: '>=0.3.1'}
|
||||
|
|
@ -3867,7 +3858,6 @@ packages:
|
|||
|
||||
/es6-error@4.1.1:
|
||||
resolution: {integrity: sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==}
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
|
|
@ -4376,7 +4366,6 @@ packages:
|
|||
|
||||
/file-uri-to-path@1.0.0:
|
||||
resolution: {integrity: sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==}
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
|
|
@ -5840,7 +5829,6 @@ packages:
|
|||
/matcher@3.0.0:
|
||||
resolution: {integrity: sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng==}
|
||||
engines: {node: '>=10'}
|
||||
requiresBuild: true
|
||||
dependencies:
|
||||
escape-string-regexp: 4.0.0
|
||||
dev: true
|
||||
|
|
@ -6191,7 +6179,6 @@ packages:
|
|||
|
||||
/node-addon-api@1.7.2:
|
||||
resolution: {integrity: sha512-ibPK3iA+vaY1eEjESkQkM0BbCqFOaZMiXRTtdB0u7b4djtY6JnsjvPdUHVMg6xQt3B8fpTTWHI9A+ADjM9frzg==}
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
|
|
@ -7281,7 +7268,6 @@ packages:
|
|||
/roarr@2.15.4:
|
||||
resolution: {integrity: sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A==}
|
||||
engines: {node: '>=8.0'}
|
||||
requiresBuild: true
|
||||
dependencies:
|
||||
boolean: 3.2.0
|
||||
detect-node: 2.1.0
|
||||
|
|
@ -7419,7 +7405,6 @@ packages:
|
|||
|
||||
/semver-compare@1.0.0:
|
||||
resolution: {integrity: sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==}
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
|
|
@ -7472,7 +7457,6 @@ packages:
|
|||
/serialize-error@7.0.1:
|
||||
resolution: {integrity: sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==}
|
||||
engines: {node: '>=10'}
|
||||
requiresBuild: true
|
||||
dependencies:
|
||||
type-fest: 0.13.1
|
||||
dev: true
|
||||
|
|
@ -7825,7 +7809,6 @@ packages:
|
|||
|
||||
/sprintf-js@1.1.3:
|
||||
resolution: {integrity: sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==}
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
|
|
@ -8486,7 +8469,6 @@ packages:
|
|||
/type-fest@0.13.1:
|
||||
resolution: {integrity: sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==}
|
||||
engines: {node: '>=10'}
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
|
|
@ -8832,7 +8814,6 @@ packages:
|
|||
|
||||
/webidl-conversions@4.0.2:
|
||||
resolution: {integrity: sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==}
|
||||
requiresBuild: true
|
||||
dev: false
|
||||
optional: true
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue