add continue watching and bookmarks back

This commit is contained in:
Jelle van Snik 2023-01-16 21:19:49 +01:00
parent ca169769bb
commit a369682a26
8 changed files with 99 additions and 97 deletions

View File

@ -8,8 +8,6 @@ export interface MediaCardProps {
linkable?: boolean;
}
// TODO add progress back
function MediaCardContent({ media, linkable }: MediaCardProps) {
return (
<div

View File

@ -35,6 +35,8 @@ if (key) {
// TODO general todos:
// - localize everything
// - add titles to pages
// - find place for bookmarks
// - find place for progress bar for "continue watching" section
ReactDOM.render(
<React.StrictMode>

View File

@ -63,15 +63,7 @@ export function BookmarkContextProvider(props: { children: ReactNode }) {
if (bookmarked) {
const itemIndex = getBookmarkIndexFromMedia(data.bookmarks, media);
if (itemIndex === -1) {
const item = {
id: media.id,
type: media.type,
// providerId: media.providerId,
title: media.title,
year: media.year,
// episodeId: media.episodeId,
// seasonId: media.seasonId,
};
const item: MWMediaMeta = { ...media };
data.bookmarks.push(item);
}
} else {

View File

@ -9,7 +9,6 @@ export const BookmarkStore = versionedStoreBuilder()
version: 1,
migrate() {
return {
// TODO actually migrate
bookmarks: [],
};
},

View File

@ -1,5 +1,6 @@
import { MWMediaMeta, MWMediaType } from "@/backend/metadata/types";
import React, {
import { DetailedMeta } from "@/backend/metadata/getmeta";
import { MWMediaMeta } from "@/backend/metadata/types";
import {
createContext,
ReactNode,
useCallback,
@ -9,7 +10,16 @@ import React, {
} from "react";
import { VideoProgressStore } from "./store";
interface WatchedStoreItem extends MWMediaMeta {
interface MediaItem {
meta: MWMediaMeta;
series?: {
episode: number;
season: number;
};
}
interface WatchedStoreItem {
item: MediaItem;
progress: number;
percentage: number;
}
@ -19,18 +29,11 @@ export interface WatchedStoreData {
}
interface WatchedStoreDataWrapper {
updateProgress(media: MWMediaMeta, progress: number, total: number): void;
updateProgress(media: MediaItem, progress: number, total: number): void;
getFilteredWatched(): WatchedStoreItem[];
watched: WatchedStoreData;
}
export function getWatchedFromPortable(
items: WatchedStoreItem[],
media: MWMediaMeta
): WatchedStoreItem | undefined {
return undefined;
}
const WatchedContext = createContext<WatchedStoreDataWrapper>({
updateProgress: () => {},
getFilteredWatched: () => [],
@ -62,49 +65,39 @@ export function WatchedContextProvider(props: { children: ReactNode }) {
const contextValue = useMemo(
() => ({
updateProgress(
media: MWMediaMeta,
progress: number,
total: number
): void {
// setWatched((data: WatchedStoreData) => {
// let item = getWatchedFromPortable(data.items, media);
// if (!item) {
// item = {
// mediaId: media.mediaId,
// mediaType: media.mediaType,
// providerId: media.providerId,
// title: media.title,
// year: media.year,
// percentage: 0,
// progress: 0,
// episodeId: media.episodeId,
// seasonId: media.seasonId,
// };
// data.items.push(item);
// }
// // update actual item
// item.progress = progress;
// item.percentage = Math.round((progress / total) * 100);
// return data;
// });
updateProgress(media: MediaItem, progress: number, total: number): void {
setWatched((data: WatchedStoreData) => {
let item = data.items.find((v) => v.item.meta.id === media.meta.id);
if (!item) {
item = {
item: {
...media,
meta: { ...media.meta },
series: media.series ? { ...media.series } : undefined,
},
progress: 0,
percentage: 0,
};
data.items.push(item);
}
// update actual item
item.progress = progress;
item.percentage = Math.round((progress / total) * 100);
return data;
});
},
getFilteredWatched() {
// remove disabled providers
// let filtered = watched.items.filter(
// (item) => getProviderMetadata(item.providerId)?.enabled
// );
let filtered = watched.items;
// // get highest episode number for every anime/season
// get highest episode number for every anime/season
const highestEpisode: Record<string, [number, number]> = {};
const highestWatchedItem: Record<string, WatchedStoreItem> = {};
filtered = filtered.filter((item) => {
if ([MWMediaType.ANIME, MWMediaType.SERIES].includes(item.type)) {
const key = `${item.type}-${item.id}`;
if (item.item.series) {
const key = item.item.meta.id;
const current: [number, number] = [
item.episodeId ? parseInt(item.episodeId, 10) : -1,
item.seasonId ? parseInt(item.seasonId, 10) : -1,
item.item.series.episode,
item.item.series.season,
];
let existing = highestEpisode[key];
if (!existing) {
@ -127,7 +120,7 @@ export function WatchedContextProvider(props: { children: ReactNode }) {
},
watched,
}),
[watched]
[watched, setWatched]
);
return (
@ -140,3 +133,23 @@ export function WatchedContextProvider(props: { children: ReactNode }) {
export function useWatchedContext() {
return useContext(WatchedContext);
}
export function useWatchedItem(meta: DetailedMeta | null) {
const { watched, updateProgress } = useContext(WatchedContext);
const item = useMemo(
() => watched.items.find((v) => meta && v.item.meta.id === meta?.meta.id),
[watched, meta]
);
const callback = useCallback(
(progress: number, total: number) => {
if (meta) {
// TODO add series support
updateProgress({ meta: meta.meta }, progress, total);
}
},
[updateProgress, meta]
);
return { updateProgress: callback, watchedItem: item };
}

View File

@ -8,7 +8,6 @@ export const VideoProgressStore = versionedStoreBuilder()
.addVersion({
version: 1,
migrate() {
// TODO add migration back
return {
items: [],
};
@ -17,7 +16,6 @@ export const VideoProgressStore = versionedStoreBuilder()
.addVersion({
version: 2,
migrate() {
// TODO actually migrate
return {
items: [],
};

View File

@ -13,6 +13,8 @@ import { MWMediaType } from "@/backend/metadata/types";
import { useGoBack } from "@/hooks/useGoBack";
import { IconPatch } from "@/components/buttons/IconPatch";
import { Icons } from "@/components/Icon";
import { useWatchedItem } from "@/state/watched";
import { ProgressListenerControl } from "@/components/video/controls/ProgressListenerControl";
import { MediaFetchErrorView } from "./MediaErrorView";
import { MediaScrapeLog } from "./MediaScrapeLog";
import { NotFoundMedia, NotFoundWrapper } from "../notfound/NotFoundView";
@ -102,6 +104,8 @@ export function MediaView() {
});
const [stream, setStream] = useState<MWStream | null>(null);
const { updateProgress, watchedItem } = useWatchedItem(meta);
useEffect(() => {
exec(params.media).then((v) => {
setMeta(v ?? null);
@ -115,8 +119,6 @@ export function MediaView() {
});
}, [exec, params.media]);
// TODO watched store
if (loading) return <MediaViewLoading onGoBack={goBack} />;
if (error) return <MediaFetchErrorView />;
if (!meta || !selected)
@ -142,6 +144,10 @@ export function MediaView() {
<div className="h-screen w-screen">
<DecoratedVideoPlayer title={meta.meta.title} onGoBack={goBack} autoPlay>
<SourceControl source={stream.streamUrl} type={stream.type} />
<ProgressListenerControl
startAt={watchedItem?.progress}
onProgress={updateProgress}
/>
</DecoratedVideoPlayer>
</div>
);

View File

@ -7,6 +7,7 @@ import {
useBookmarkContext,
} from "@/state/bookmark";
import { useWatchedContext } from "@/state/watched";
import { WatchedMediaCard } from "@/components/media/WatchedMediaCard";
function Bookmarks() {
const { t } = useTranslation();
@ -21,52 +22,45 @@ function Bookmarks() {
icon={Icons.BOOKMARK}
>
<MediaGrid>
{/* {bookmarks.map((v) => (
<WatchedMediaCard
key={[v.mediaId, v.providerId].join("|")}
media={v}
/>
))} */}
{bookmarks.map((v) => (
<WatchedMediaCard key={v.id} media={v} />
))}
</MediaGrid>
</SectionHeading>
);
}
// function Watched() {
// const { t } = useTranslation();
// const { getFilteredBookmarks } = useBookmarkContext();
// const { getFilteredWatched } = useWatchedContext();
function Watched() {
const { t } = useTranslation();
const { getFilteredBookmarks } = useBookmarkContext();
const { getFilteredWatched } = useWatchedContext();
// const bookmarks = getFilteredBookmarks();
// const watchedItems = getFilteredWatched().filter(
// (v) => !getIfBookmarkedFromPortable(bookmarks, v)
// );
const bookmarks = getFilteredBookmarks();
const watchedItems = getFilteredWatched().filter(
(v) => !getIfBookmarkedFromPortable(bookmarks, v.item.meta)
);
// if (watchedItems.length === 0) return null;
if (watchedItems.length === 0) return null;
// return (
// <SectionHeading
// title={t("search.continueWatching") || "Continue Watching"}
// icon={Icons.CLOCK}
// >
// <MediaGrid>
// {/* {watchedItems.map((v) => (
// <WatchedMediaCard
// key={[v.mediaId, v.providerId].join("|")}
// media={v}
// series
// />
// ))} */}
// </MediaGrid>
// </SectionHeading>
// );
// }
return (
<SectionHeading
title={t("search.continueWatching") || "Continue Watching"}
icon={Icons.CLOCK}
>
<MediaGrid>
{watchedItems.map((v) => (
<WatchedMediaCard key={v.item.meta.id} media={v.item.meta} />
))}
</MediaGrid>
</SectionHeading>
);
}
export function HomeView() {
return (
<div className="mb-16 mt-32">
{/* <Bookmarks /> */}
{/* <Watched /> */}
<Bookmarks />
<Watched />
</div>
);
}