mirror of
https://github.com/movie-web/movie-web.git
synced 2024-12-24 18:51:53 +01:00
add continue watching and bookmarks back
This commit is contained in:
parent
ca169769bb
commit
a369682a26
@ -8,8 +8,6 @@ export interface MediaCardProps {
|
||||
linkable?: boolean;
|
||||
}
|
||||
|
||||
// TODO add progress back
|
||||
|
||||
function MediaCardContent({ media, linkable }: MediaCardProps) {
|
||||
return (
|
||||
<div
|
||||
|
@ -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>
|
||||
|
@ -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 {
|
||||
|
@ -9,7 +9,6 @@ export const BookmarkStore = versionedStoreBuilder()
|
||||
version: 1,
|
||||
migrate() {
|
||||
return {
|
||||
// TODO actually migrate
|
||||
bookmarks: [],
|
||||
};
|
||||
},
|
||||
|
@ -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 };
|
||||
}
|
||||
|
@ -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: [],
|
||||
};
|
||||
|
@ -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>
|
||||
);
|
||||
|
@ -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>
|
||||
);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user