Move migration out of home into store
This commit is contained in:
parent
8f23240ea1
commit
dd14b575eb
@ -1,7 +1,35 @@
|
|||||||
|
import { DetailedMeta, getMetaFromId } from "@/backend/metadata/getmeta";
|
||||||
|
import { searchForMedia } from "@/backend/metadata/search";
|
||||||
|
import { MWMediaMeta, MWMediaType } from "@/backend/metadata/types";
|
||||||
import { versionedStoreBuilder } from "@/utils/storage";
|
import { versionedStoreBuilder } from "@/utils/storage";
|
||||||
|
import { WatchedStoreData, WatchedStoreItem } from "./context";
|
||||||
|
|
||||||
|
interface OldMediaBase {
|
||||||
|
mediaId: number;
|
||||||
|
mediaType: MWMediaType;
|
||||||
|
percentage: number;
|
||||||
|
progress: number;
|
||||||
|
providerId: string;
|
||||||
|
title: string;
|
||||||
|
year: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface OldMovie extends OldMediaBase {
|
||||||
|
mediaType: MWMediaType.MOVIE;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface OldSeries extends OldMediaBase {
|
||||||
|
mediaType: MWMediaType.SERIES;
|
||||||
|
episodeId: number;
|
||||||
|
seasonId: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface OldData {
|
||||||
|
items: (OldMovie | OldSeries)[];
|
||||||
|
}
|
||||||
|
|
||||||
export const VideoProgressStore = versionedStoreBuilder()
|
export const VideoProgressStore = versionedStoreBuilder()
|
||||||
.setKey("video-progress-v3")
|
.setKey("video-progress")
|
||||||
.addVersion({
|
.addVersion({
|
||||||
version: 0,
|
version: 0,
|
||||||
})
|
})
|
||||||
@ -15,7 +43,11 @@ export const VideoProgressStore = versionedStoreBuilder()
|
|||||||
})
|
})
|
||||||
.addVersion({
|
.addVersion({
|
||||||
version: 2,
|
version: 2,
|
||||||
migrate() {
|
migrate(old: OldData) {
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
// eslint-disable-next-line no-use-before-define
|
||||||
|
migrateV2(old);
|
||||||
|
});
|
||||||
return {
|
return {
|
||||||
items: [],
|
items: [],
|
||||||
};
|
};
|
||||||
@ -27,3 +59,153 @@ export const VideoProgressStore = versionedStoreBuilder()
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
|
async function migrateV2(old: OldData) {
|
||||||
|
const oldData = old;
|
||||||
|
if (!oldData) return;
|
||||||
|
|
||||||
|
const uniqueMedias: Record<string, any> = {};
|
||||||
|
oldData.items.forEach((item: any) => {
|
||||||
|
if (uniqueMedias[item.mediaId]) return;
|
||||||
|
uniqueMedias[item.mediaId] = item;
|
||||||
|
});
|
||||||
|
|
||||||
|
const yearsAreClose = (a: number, b: number) => {
|
||||||
|
return Math.abs(a - b) <= 1;
|
||||||
|
};
|
||||||
|
|
||||||
|
const mediaMetas: Record<string, Record<string, DetailedMeta | null>> = {};
|
||||||
|
|
||||||
|
const relevantItems = await Promise.all(
|
||||||
|
Object.values(uniqueMedias).map(async (item) => {
|
||||||
|
const year = Number(item.year.toString().split("-")[0]);
|
||||||
|
const data = await searchForMedia({
|
||||||
|
searchQuery: `${item.title} ${year}`,
|
||||||
|
type: item.mediaType,
|
||||||
|
});
|
||||||
|
const relevantItem = data.find((res) =>
|
||||||
|
yearsAreClose(Number(res.year), year)
|
||||||
|
);
|
||||||
|
if (!relevantItem) {
|
||||||
|
console.error("No item");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
id: item.mediaId,
|
||||||
|
data: relevantItem,
|
||||||
|
};
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
for (const item of relevantItems.filter(Boolean)) {
|
||||||
|
if (!item) continue;
|
||||||
|
|
||||||
|
let keys: (string | null)[][] = [["0", "0"]];
|
||||||
|
if (item.data.type === "series") {
|
||||||
|
const meta = await getMetaFromId(item.data.type, item.data.id);
|
||||||
|
if (!meta || !meta?.meta.seasons) return;
|
||||||
|
const seasonNumbers = [
|
||||||
|
...new Set(
|
||||||
|
oldData.items
|
||||||
|
.filter((watchedEntry: any) => watchedEntry.mediaId === item.id)
|
||||||
|
.map((watchedEntry: any) => watchedEntry.seasonId)
|
||||||
|
),
|
||||||
|
];
|
||||||
|
const seasons = seasonNumbers
|
||||||
|
.map((num) => ({
|
||||||
|
num,
|
||||||
|
season: meta.meta?.seasons?.[(num as number) - 1],
|
||||||
|
}))
|
||||||
|
.filter(Boolean);
|
||||||
|
keys = seasons
|
||||||
|
.map((season) => (season ? [season.num, season?.season?.id] : []))
|
||||||
|
.filter((entry) => entry.length > 0); // Stupid TypeScript
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!mediaMetas[item.id]) mediaMetas[item.id] = {};
|
||||||
|
await Promise.all(
|
||||||
|
keys.map(async ([key, id]) => {
|
||||||
|
if (!key) return;
|
||||||
|
mediaMetas[item.id][key] = await getMetaFromId(
|
||||||
|
item.data.type,
|
||||||
|
item.data.id,
|
||||||
|
id === "0" || id === null ? undefined : id
|
||||||
|
);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// We've got all the metadata you can dream of now
|
||||||
|
// Now let's convert stuff into the new format.
|
||||||
|
interface WatchedStoreDataWithVersion extends WatchedStoreData {
|
||||||
|
"--version": number;
|
||||||
|
}
|
||||||
|
const newData: WatchedStoreDataWithVersion = {
|
||||||
|
...oldData,
|
||||||
|
items: [],
|
||||||
|
"--version": 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const oldWatched of oldData.items) {
|
||||||
|
if (oldWatched.mediaType === "movie") {
|
||||||
|
if (!mediaMetas[oldWatched.mediaId]["0"]?.meta) continue;
|
||||||
|
|
||||||
|
const newItem: WatchedStoreItem = {
|
||||||
|
item: {
|
||||||
|
meta: mediaMetas[oldWatched.mediaId]["0"]?.meta as MWMediaMeta,
|
||||||
|
},
|
||||||
|
progress: oldWatched.progress,
|
||||||
|
percentage: oldWatched.percentage,
|
||||||
|
watchedAt: Date.now(), // There was no watchedAt in V2
|
||||||
|
};
|
||||||
|
|
||||||
|
oldData.items = oldData.items.filter(
|
||||||
|
(item) => JSON.stringify(item) !== JSON.stringify(oldWatched)
|
||||||
|
);
|
||||||
|
newData.items.push(newItem);
|
||||||
|
} else if (oldWatched.mediaType === "series") {
|
||||||
|
if (!mediaMetas[oldWatched.mediaId][oldWatched.seasonId]?.meta) continue;
|
||||||
|
|
||||||
|
const meta = mediaMetas[oldWatched.mediaId][oldWatched.seasonId]
|
||||||
|
?.meta as MWMediaMeta;
|
||||||
|
|
||||||
|
if (meta.type !== "series") return;
|
||||||
|
|
||||||
|
const newItem: WatchedStoreItem = {
|
||||||
|
item: {
|
||||||
|
meta,
|
||||||
|
series: {
|
||||||
|
episode: Number(oldWatched.episodeId),
|
||||||
|
season: Number(oldWatched.seasonId),
|
||||||
|
seasonId: meta.seasonData.id,
|
||||||
|
episodeId:
|
||||||
|
meta.seasonData.episodes[Number(oldWatched.episodeId) - 1].id,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
progress: oldWatched.progress,
|
||||||
|
percentage: oldWatched.percentage,
|
||||||
|
watchedAt: Date.now(), // There was no watchedAt in V2
|
||||||
|
};
|
||||||
|
|
||||||
|
if (
|
||||||
|
newData.items.find(
|
||||||
|
(item) =>
|
||||||
|
item.item.meta.id === newItem.item.meta.id &&
|
||||||
|
item.item.series?.episodeId === newItem.item.series?.episodeId
|
||||||
|
)
|
||||||
|
)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
oldData.items = oldData.items.filter(
|
||||||
|
(item) => JSON.stringify(item) !== JSON.stringify(oldWatched)
|
||||||
|
);
|
||||||
|
newData.items.push(newItem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(JSON.stringify(old), JSON.stringify(newData));
|
||||||
|
if (JSON.stringify(old.items) !== JSON.stringify(newData.items)) {
|
||||||
|
console.log(newData);
|
||||||
|
VideoProgressStore.get().save(newData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -6,19 +6,11 @@ import {
|
|||||||
getIfBookmarkedFromPortable,
|
getIfBookmarkedFromPortable,
|
||||||
useBookmarkContext,
|
useBookmarkContext,
|
||||||
} from "@/state/bookmark";
|
} from "@/state/bookmark";
|
||||||
import {
|
import { useWatchedContext } from "@/state/watched";
|
||||||
useWatchedContext,
|
|
||||||
WatchedStoreData,
|
|
||||||
WatchedStoreItem,
|
|
||||||
} from "@/state/watched";
|
|
||||||
import { WatchedMediaCard } from "@/components/media/WatchedMediaCard";
|
import { WatchedMediaCard } from "@/components/media/WatchedMediaCard";
|
||||||
import { EditButton } from "@/components/buttons/EditButton";
|
import { EditButton } from "@/components/buttons/EditButton";
|
||||||
import { useCallback, useState } from "react";
|
import { useState } from "react";
|
||||||
import { useAutoAnimate } from "@formkit/auto-animate/react";
|
import { useAutoAnimate } from "@formkit/auto-animate/react";
|
||||||
import { VideoProgressStore } from "@/state/watched/store";
|
|
||||||
import { searchForMedia } from "@/backend/metadata/search";
|
|
||||||
import { DetailedMeta, getMetaFromId } from "@/backend/metadata/getmeta";
|
|
||||||
import { MWMediaMeta, MWMediaType } from "@/backend/metadata/types";
|
|
||||||
|
|
||||||
function Bookmarks() {
|
function Bookmarks() {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
@ -51,30 +43,6 @@ function Bookmarks() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
interface OldMediaBase {
|
|
||||||
mediaId: number;
|
|
||||||
mediaType: MWMediaType;
|
|
||||||
percentage: number;
|
|
||||||
progress: number;
|
|
||||||
providerId: string;
|
|
||||||
title: string;
|
|
||||||
year: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface OldMovie extends OldMediaBase {
|
|
||||||
mediaType: MWMediaType.MOVIE;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface OldSeries extends OldMediaBase {
|
|
||||||
mediaType: MWMediaType.SERIES;
|
|
||||||
episodeId: number;
|
|
||||||
seasonId: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface OldData {
|
|
||||||
items: (OldMovie | OldSeries)[];
|
|
||||||
}
|
|
||||||
|
|
||||||
function Watched() {
|
function Watched() {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { getFilteredBookmarks } = useBookmarkContext();
|
const { getFilteredBookmarks } = useBookmarkContext();
|
||||||
@ -87,189 +55,6 @@ function Watched() {
|
|||||||
(v) => !getIfBookmarkedFromPortable(bookmarks, v.item.meta)
|
(v) => !getIfBookmarkedFromPortable(bookmarks, v.item.meta)
|
||||||
);
|
);
|
||||||
|
|
||||||
/*
|
|
||||||
AAA
|
|
||||||
*/
|
|
||||||
const watchedLocalstorage = VideoProgressStore.get();
|
|
||||||
const [watched, setWatchedReal] = useState<WatchedStoreData>(
|
|
||||||
watchedLocalstorage as WatchedStoreData
|
|
||||||
);
|
|
||||||
|
|
||||||
const setWatched = useCallback(
|
|
||||||
(data: any) => {
|
|
||||||
setWatchedReal((old) => {
|
|
||||||
let newData = data;
|
|
||||||
if (data.constructor === Function) {
|
|
||||||
newData = data(old);
|
|
||||||
}
|
|
||||||
watchedLocalstorage.save(newData);
|
|
||||||
return newData;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
[setWatchedReal, watchedLocalstorage]
|
|
||||||
);
|
|
||||||
|
|
||||||
(async () => {
|
|
||||||
const oldData: OldData | null = localStorage.getItem("video-progress")
|
|
||||||
? JSON.parse(localStorage.getItem("video-progress") || "")
|
|
||||||
: null;
|
|
||||||
|
|
||||||
if (!oldData) return;
|
|
||||||
|
|
||||||
const uniqueMedias: Record<string, any> = {};
|
|
||||||
oldData.items.forEach((item: any) => {
|
|
||||||
if (uniqueMedias[item.mediaId]) return;
|
|
||||||
uniqueMedias[item.mediaId] = item;
|
|
||||||
});
|
|
||||||
|
|
||||||
const yearsAreClose = (a: number, b: number) => {
|
|
||||||
return Math.abs(a - b) <= 1;
|
|
||||||
};
|
|
||||||
|
|
||||||
const mediaMetas: Record<string, Record<string, DetailedMeta | null>> = {};
|
|
||||||
|
|
||||||
Promise.all(
|
|
||||||
Object.values(uniqueMedias).map(async (item) => {
|
|
||||||
const year = Number(item.year.toString().split("-")[0]);
|
|
||||||
const data = await searchForMedia({
|
|
||||||
searchQuery: `${item.title} ${year}`,
|
|
||||||
type: item.mediaType,
|
|
||||||
});
|
|
||||||
const relevantItem = data.find((res) =>
|
|
||||||
yearsAreClose(Number(res.year), year)
|
|
||||||
);
|
|
||||||
if (!relevantItem) {
|
|
||||||
console.error("No item");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
id: item.mediaId,
|
|
||||||
data: relevantItem,
|
|
||||||
};
|
|
||||||
})
|
|
||||||
).then(async (relevantItems) => {
|
|
||||||
console.log(relevantItems);
|
|
||||||
for (const item of relevantItems.filter(Boolean)) {
|
|
||||||
if (!item) continue;
|
|
||||||
|
|
||||||
let keys: (string | null)[][] = [["0", "0"]];
|
|
||||||
if (item.data.type === "series") {
|
|
||||||
const meta = await getMetaFromId(item.data.type, item.data.id);
|
|
||||||
if (!meta || !meta?.meta.seasons) return;
|
|
||||||
const seasonNumbers = [
|
|
||||||
...new Set(
|
|
||||||
oldData.items
|
|
||||||
.filter((watchedEntry: any) => watchedEntry.mediaId === item.id)
|
|
||||||
.map((watchedEntry: any) => watchedEntry.seasonId)
|
|
||||||
),
|
|
||||||
];
|
|
||||||
const seasons = seasonNumbers
|
|
||||||
.map((num) => ({
|
|
||||||
num,
|
|
||||||
season: meta.meta?.seasons?.[(num as number) - 1],
|
|
||||||
}))
|
|
||||||
.filter(Boolean);
|
|
||||||
keys = seasons
|
|
||||||
.map((season) => (season ? [season.num, season?.season?.id] : []))
|
|
||||||
.filter((entry) => entry.length > 0); // Stupid TypeScript
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!mediaMetas[item.id]) mediaMetas[item.id] = {};
|
|
||||||
await Promise.all(
|
|
||||||
keys.map(async ([key, id]) => {
|
|
||||||
if (!key) return;
|
|
||||||
mediaMetas[item.id][key] = await getMetaFromId(
|
|
||||||
item.data.type,
|
|
||||||
item.data.id,
|
|
||||||
id === "0" || id === null ? undefined : id
|
|
||||||
);
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// We've got all the metadata you can dream of now
|
|
||||||
// Now let's convert stuff into the new format.
|
|
||||||
const newData: WatchedStoreData = JSON.parse(JSON.stringify(watched));
|
|
||||||
|
|
||||||
for (const oldWatched of oldData.items) {
|
|
||||||
if (oldWatched.mediaType === "movie") {
|
|
||||||
if (!mediaMetas[oldWatched.mediaId]["0"]?.meta) continue;
|
|
||||||
|
|
||||||
const newItem: WatchedStoreItem = {
|
|
||||||
item: {
|
|
||||||
meta: mediaMetas[oldWatched.mediaId]["0"]?.meta as MWMediaMeta,
|
|
||||||
},
|
|
||||||
progress: oldWatched.progress,
|
|
||||||
percentage: oldWatched.percentage,
|
|
||||||
watchedAt: Date.now(), // There was no watchedAt in V2
|
|
||||||
};
|
|
||||||
if (
|
|
||||||
newData.items.find(
|
|
||||||
(item) => item.item.meta.id === newItem.item.meta.id
|
|
||||||
)
|
|
||||||
)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
oldData.items = oldData.items.filter(
|
|
||||||
(item) => JSON.stringify(item) !== JSON.stringify(oldWatched)
|
|
||||||
);
|
|
||||||
newData.items.push(newItem);
|
|
||||||
} else if (oldWatched.mediaType === "series") {
|
|
||||||
// console.log(oldWatched);
|
|
||||||
// console.log(mediaMetas[oldWatched.mediaId][oldWatched.seasonId]);
|
|
||||||
|
|
||||||
if (!mediaMetas[oldWatched.mediaId][oldWatched.seasonId]?.meta)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
const meta = mediaMetas[oldWatched.mediaId][oldWatched.seasonId]
|
|
||||||
?.meta as MWMediaMeta;
|
|
||||||
|
|
||||||
if (meta.type !== "series") return;
|
|
||||||
|
|
||||||
// console.log(meta.seasonData);
|
|
||||||
const newItem: WatchedStoreItem = {
|
|
||||||
item: {
|
|
||||||
meta,
|
|
||||||
series: {
|
|
||||||
episode: Number(oldWatched.episodeId),
|
|
||||||
season: Number(oldWatched.seasonId),
|
|
||||||
seasonId: meta.seasonData.id,
|
|
||||||
episodeId:
|
|
||||||
meta.seasonData.episodes[Number(oldWatched.episodeId) - 1].id,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
progress: oldWatched.progress,
|
|
||||||
percentage: oldWatched.percentage,
|
|
||||||
watchedAt: Date.now(), // There was no watchedAt in V2
|
|
||||||
};
|
|
||||||
|
|
||||||
if (
|
|
||||||
newData.items.find(
|
|
||||||
(item) =>
|
|
||||||
item.item.meta.id === newItem.item.meta.id &&
|
|
||||||
item.item.series?.episodeId === newItem.item.series?.episodeId
|
|
||||||
)
|
|
||||||
)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
oldData.items = oldData.items.filter(
|
|
||||||
(item) => JSON.stringify(item) !== JSON.stringify(oldWatched)
|
|
||||||
);
|
|
||||||
newData.items.push(newItem);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (JSON.stringify(newData) !== JSON.stringify(watched)) {
|
|
||||||
localStorage.setItem("video-progress", JSON.stringify(oldData));
|
|
||||||
setWatched(() => newData);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
})();
|
|
||||||
|
|
||||||
/*
|
|
||||||
AAA
|
|
||||||
*/
|
|
||||||
|
|
||||||
if (watchedItems.length === 0) return null;
|
if (watchedItems.length === 0) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
Loading…
x
Reference in New Issue
Block a user