mirror of
https://github.com/sussy-code/smov.git
synced 2024-12-21 14:47:41 +01:00
loading screen usescrape
This commit is contained in:
parent
cf83df64bb
commit
2f1058cb9c
7 changed files with 119 additions and 28 deletions
|
@ -37,12 +37,14 @@ registerProvider({
|
||||||
rank: 69,
|
rank: 69,
|
||||||
type: [MWMediaType.MOVIE],
|
type: [MWMediaType.MOVIE],
|
||||||
|
|
||||||
async scrape({ media: { imdbId } }) {
|
async scrape({ progress, media: { imdbId } }) {
|
||||||
|
progress(10);
|
||||||
const streamRes = await fetch(
|
const streamRes = await fetch(
|
||||||
`${
|
`${
|
||||||
conf().CORS_PROXY_URL
|
conf().CORS_PROXY_URL
|
||||||
}https://database.gdriveplayer.us/player.php?imdb=${imdbId}`
|
}https://database.gdriveplayer.us/player.php?imdb=${imdbId}`
|
||||||
).then((d) => d.text());
|
).then((d) => d.text());
|
||||||
|
progress(90);
|
||||||
const page = new DOMParser().parseFromString(streamRes, "text/html");
|
const page = new DOMParser().parseFromString(streamRes, "text/html");
|
||||||
|
|
||||||
const script: HTMLElement | undefined = Array.from(
|
const script: HTMLElement | undefined = Array.from(
|
||||||
|
|
|
@ -22,6 +22,7 @@ export enum Icons {
|
||||||
COMPRESS = "compress",
|
COMPRESS = "compress",
|
||||||
VOLUME = "volume",
|
VOLUME = "volume",
|
||||||
VOLUME_X = "volume_x",
|
VOLUME_X = "volume_x",
|
||||||
|
X = "x",
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IconProps {
|
export interface IconProps {
|
||||||
|
@ -51,6 +52,7 @@ const iconList: Record<Icons, string> = {
|
||||||
compress: `<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 448 512"><!--! Font Awesome Pro 6.2.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --><path fill="currentColor" d="M160 64c0-17.7-14.3-32-32-32s-32 14.3-32 32v64H32c-17.7 0-32 14.3-32 32s14.3 32 32 32h96c17.7 0 32-14.3 32-32V64zM32 320c-17.7 0-32 14.3-32 32s14.3 32 32 32H96v64c0 17.7 14.3 32 32 32s32-14.3 32-32V352c0-17.7-14.3-32-32-32H32zM352 64c0-17.7-14.3-32-32-32s-32 14.3-32 32v96c0 17.7 14.3 32 32 32h96c17.7 0 32-14.3 32-32s-14.3-32-32-32H352V64zM320 320c-17.7 0-32 14.3-32 32v96c0 17.7 14.3 32 32 32s32-14.3 32-32V384h64c17.7 0 32-14.3 32-32s-14.3-32-32-32H320z"/></svg>`,
|
compress: `<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 448 512"><!--! Font Awesome Pro 6.2.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --><path fill="currentColor" d="M160 64c0-17.7-14.3-32-32-32s-32 14.3-32 32v64H32c-17.7 0-32 14.3-32 32s14.3 32 32 32h96c17.7 0 32-14.3 32-32V64zM32 320c-17.7 0-32 14.3-32 32s14.3 32 32 32H96v64c0 17.7 14.3 32 32 32s32-14.3 32-32V352c0-17.7-14.3-32-32-32H32zM352 64c0-17.7-14.3-32-32-32s-32 14.3-32 32v96c0 17.7 14.3 32 32 32h96c17.7 0 32-14.3 32-32s-14.3-32-32-32H352V64zM320 320c-17.7 0-32 14.3-32 32v96c0 17.7 14.3 32 32 32s32-14.3 32-32V384h64c17.7 0 32-14.3 32-32s-14.3-32-32-32H320z"/></svg>`,
|
||||||
volume: `<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 640 512"><!--! Font Awesome Pro 6.2.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --><path fill="currentColor" d="M533.6 32.5C598.5 85.3 640 165.8 640 256s-41.5 170.8-106.4 223.5c-10.3 8.4-25.4 6.8-33.8-3.5s-6.8-25.4 3.5-33.8C557.5 398.2 592 331.2 592 256s-34.5-142.2-88.7-186.3c-10.3-8.4-11.8-23.5-3.5-33.8s23.5-11.8 33.8-3.5zM473.1 107c43.2 35.2 70.9 88.9 70.9 149s-27.7 113.8-70.9 149c-10.3 8.4-25.4 6.8-33.8-3.5s-6.8-25.4 3.5-33.8C475.3 341.3 496 301.1 496 256s-20.7-85.3-53.2-111.8c-10.3-8.4-11.8-23.5-3.5-33.8s23.5-11.8 33.8-3.5zm-60.5 74.5C434.1 199.1 448 225.9 448 256s-13.9 56.9-35.4 74.5c-10.3 8.4-25.4 6.8-33.8-3.5s-6.8-25.4 3.5-33.8C393.1 284.4 400 271 400 256s-6.9-28.4-17.7-37.3c-10.3-8.4-11.8-23.5-3.5-33.8s23.5-11.8 33.8-3.5zM301.1 34.8C312.6 40 320 51.4 320 64V448c0 12.6-7.4 24-18.9 29.2s-25 3.1-34.4-5.3L131.8 352H64c-35.3 0-64-28.7-64-64V224c0-35.3 28.7-64 64-64h67.8L266.7 40.1c9.4-8.4 22.9-10.4 34.4-5.3z"/></svg>`,
|
volume: `<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 640 512"><!--! Font Awesome Pro 6.2.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --><path fill="currentColor" d="M533.6 32.5C598.5 85.3 640 165.8 640 256s-41.5 170.8-106.4 223.5c-10.3 8.4-25.4 6.8-33.8-3.5s-6.8-25.4 3.5-33.8C557.5 398.2 592 331.2 592 256s-34.5-142.2-88.7-186.3c-10.3-8.4-11.8-23.5-3.5-33.8s23.5-11.8 33.8-3.5zM473.1 107c43.2 35.2 70.9 88.9 70.9 149s-27.7 113.8-70.9 149c-10.3 8.4-25.4 6.8-33.8-3.5s-6.8-25.4 3.5-33.8C475.3 341.3 496 301.1 496 256s-20.7-85.3-53.2-111.8c-10.3-8.4-11.8-23.5-3.5-33.8s23.5-11.8 33.8-3.5zm-60.5 74.5C434.1 199.1 448 225.9 448 256s-13.9 56.9-35.4 74.5c-10.3 8.4-25.4 6.8-33.8-3.5s-6.8-25.4 3.5-33.8C393.1 284.4 400 271 400 256s-6.9-28.4-17.7-37.3c-10.3-8.4-11.8-23.5-3.5-33.8s23.5-11.8 33.8-3.5zM301.1 34.8C312.6 40 320 51.4 320 64V448c0 12.6-7.4 24-18.9 29.2s-25 3.1-34.4-5.3L131.8 352H64c-35.3 0-64-28.7-64-64V224c0-35.3 28.7-64 64-64h67.8L266.7 40.1c9.4-8.4 22.9-10.4 34.4-5.3z"/></svg>`,
|
||||||
volume_x: `<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 576 512"><!--! Font Awesome Pro 6.2.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --><path fill="currentColor" d="M301.1 34.8C312.6 40 320 51.4 320 64V448c0 12.6-7.4 24-18.9 29.2s-25 3.1-34.4-5.3L131.8 352H64c-35.3 0-64-28.7-64-64V224c0-35.3 28.7-64 64-64h67.8L266.7 40.1c9.4-8.4 22.9-10.4 34.4-5.3zM425 167l55 55 55-55c9.4-9.4 24.6-9.4 33.9 0s9.4 24.6 0 33.9l-55 55 55 55c9.4 9.4 9.4 24.6 0 33.9s-24.6 9.4-33.9 0l-55-55-55 55c-9.4 9.4-24.6 9.4-33.9 0s-9.4-24.6 0-33.9l55-55-55-55c-9.4-9.4-9.4-24.6 0-33.9s24.6-9.4 33.9 0z"/></svg>`,
|
volume_x: `<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 576 512"><!--! Font Awesome Pro 6.2.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --><path fill="currentColor" d="M301.1 34.8C312.6 40 320 51.4 320 64V448c0 12.6-7.4 24-18.9 29.2s-25 3.1-34.4-5.3L131.8 352H64c-35.3 0-64-28.7-64-64V224c0-35.3 28.7-64 64-64h67.8L266.7 40.1c9.4-8.4 22.9-10.4 34.4-5.3zM425 167l55 55 55-55c9.4-9.4 24.6-9.4 33.9 0s9.4 24.6 0 33.9l-55 55 55 55c9.4 9.4 9.4 24.6 0 33.9s-24.6 9.4-33.9 0l-55-55-55 55c-9.4 9.4-24.6 9.4-33.9 0s-9.4-24.6 0-33.9l55-55-55-55c-9.4-9.4-9.4-24.6 0-33.9s24.6-9.4 33.9 0z"/></svg>`,
|
||||||
|
x: `<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 320 512"><!--! Font Awesome Pro 6.2.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --><path fill="currentColor" d="M310.6 150.6c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0L160 210.7 54.6 105.4c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3L114.7 256 9.4 361.4c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0L160 301.3 265.4 406.6c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3L205.3 256 310.6 150.6z"/></svg>`,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const Icon = memo((props: IconProps) => {
|
export const Icon = memo((props: IconProps) => {
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
import { DotList } from "@/components/text/DotList";
|
import { DotList } from "@/components/text/DotList";
|
||||||
import { MWMediaMeta } from "@/backend/metadata/types";
|
import { MWMediaMeta } from "@/backend/metadata/types";
|
||||||
|
import { mediaTypeToJW } from "@/backend/metadata/justwatch";
|
||||||
|
|
||||||
export interface MediaCardProps {
|
export interface MediaCardProps {
|
||||||
media: MWMediaMeta;
|
media: MWMediaMeta;
|
||||||
|
@ -42,9 +43,9 @@ export function MediaCard(props: MediaCardProps) {
|
||||||
if (!props.linkable) return <span>{content}</span>;
|
if (!props.linkable) return <span>{content}</span>;
|
||||||
return (
|
return (
|
||||||
<Link
|
<Link
|
||||||
to={`/media/${encodeURIComponent(props.media.type)}-${encodeURIComponent(
|
to={`/media/${encodeURIComponent(
|
||||||
props.media.id
|
mediaTypeToJW(props.media.type)
|
||||||
)}`}
|
)}-${encodeURIComponent(props.media.id)}`}
|
||||||
>
|
>
|
||||||
{content}
|
{content}
|
||||||
</Link>
|
</Link>
|
||||||
|
|
|
@ -3,7 +3,7 @@ import { MWStream } from "@/backend/helpers/streams";
|
||||||
import { DetailedMeta } from "@/backend/metadata/getmeta";
|
import { DetailedMeta } from "@/backend/metadata/getmeta";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
|
|
||||||
interface ScrapeEventLog {
|
export interface ScrapeEventLog {
|
||||||
type: "provider" | "embed";
|
type: "provider" | "embed";
|
||||||
errored: boolean;
|
errored: boolean;
|
||||||
percentage: number;
|
percentage: number;
|
||||||
|
|
|
@ -3,7 +3,7 @@ import { BookmarkContextProvider } from "@/state/bookmark";
|
||||||
import { WatchedContextProvider } from "@/state/watched";
|
import { WatchedContextProvider } from "@/state/watched";
|
||||||
|
|
||||||
import { NotFoundPage } from "@/views/notfound/NotFoundView";
|
import { NotFoundPage } from "@/views/notfound/NotFoundView";
|
||||||
import { MediaView } from "@/views/MediaView";
|
import { MediaView } from "@/views/media/MediaView";
|
||||||
import { SearchView } from "@/views/search/SearchView";
|
import { SearchView } from "@/views/search/SearchView";
|
||||||
import { MWMediaType } from "@/backend/metadata/types";
|
import { MWMediaType } from "@/backend/metadata/types";
|
||||||
|
|
||||||
|
|
78
src/views/media/MediaScrapeLog.tsx
Normal file
78
src/views/media/MediaScrapeLog.tsx
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
import { Icon, Icons } from "@/components/Icon";
|
||||||
|
import { ScrapeEventLog } from "@/hooks/useScrape";
|
||||||
|
|
||||||
|
interface MediaScrapeLogProps {
|
||||||
|
events: ScrapeEventLog[];
|
||||||
|
}
|
||||||
|
|
||||||
|
interface MediaScrapePillProps {
|
||||||
|
event: ScrapeEventLog;
|
||||||
|
}
|
||||||
|
|
||||||
|
function MediaScrapePillSkeleton() {
|
||||||
|
return <div className="h-9 w-[220px] rounded-full bg-slate-800 opacity-50" />;
|
||||||
|
}
|
||||||
|
|
||||||
|
function MediaScrapePill({ event }: MediaScrapePillProps) {
|
||||||
|
return (
|
||||||
|
<div className="flex h-9 w-[220px] items-center rounded-full bg-slate-800 p-3 text-denim-700">
|
||||||
|
<div className="mr-2">
|
||||||
|
{!event.errored ? (
|
||||||
|
<svg className="h-[18px] w-[18px] -rotate-90" viewBox="0 0 100 100">
|
||||||
|
<circle
|
||||||
|
className="fill-transparent stroke-denim-700 stroke-[15] transition-[stroke-dashoffset] duration-150"
|
||||||
|
r="40"
|
||||||
|
cx="50"
|
||||||
|
cy="50"
|
||||||
|
style={{
|
||||||
|
strokeDasharray: `${2 * Math.PI * 40} ${2 * Math.PI * 40}`,
|
||||||
|
strokeDashoffset: `${
|
||||||
|
2 * Math.PI * 40 -
|
||||||
|
(event.percentage / 100) * (2 * Math.PI * 40)
|
||||||
|
}`,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
) : (
|
||||||
|
<Icon icon={Icons.X} className="text-[0.85em] text-rose-400" />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className="flex-1 overflow-hidden">
|
||||||
|
<p
|
||||||
|
className={`overflow-hidden text-ellipsis whitespace-nowrap ${
|
||||||
|
event.errored ? "text-rose-400" : ""
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{event.id}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function MediaScrapeLog(props: MediaScrapeLogProps) {
|
||||||
|
return (
|
||||||
|
<div className="relative h-16 w-[400px] overflow-hidden">
|
||||||
|
<div className="absolute inset-0 flex items-center justify-center">
|
||||||
|
<div className="relative flex h-full w-[220px] items-center">
|
||||||
|
<div
|
||||||
|
className="absolute inset-y-0 left-0 flex items-center gap-[16px] transition-transform duration-200"
|
||||||
|
style={{
|
||||||
|
transform: `translateX(${
|
||||||
|
-1 * (220 + 16) * props.events.length
|
||||||
|
}px)`,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<MediaScrapePillSkeleton />
|
||||||
|
{props.events.map((v) => (
|
||||||
|
<MediaScrapePill event={v} key={v.id} />
|
||||||
|
))}
|
||||||
|
<MediaScrapePillSkeleton />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="absolute inset-y-0 left-0 w-40 bg-gradient-to-r from-denim-100 to-transparent" />
|
||||||
|
<div className="absolute inset-y-0 right-0 w-40 bg-gradient-to-l from-denim-100 to-transparent" />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
|
@ -7,9 +7,21 @@ import { VideoPlayerHeader } from "@/components/video/parts/VideoPlayerHeader";
|
||||||
import { DetailedMeta, getMetaFromId } from "@/backend/metadata/getmeta";
|
import { DetailedMeta, getMetaFromId } from "@/backend/metadata/getmeta";
|
||||||
import { JWMediaToMediaType } from "@/backend/metadata/justwatch";
|
import { JWMediaToMediaType } from "@/backend/metadata/justwatch";
|
||||||
import { SourceControl } from "@/components/video/controls/SourceControl";
|
import { SourceControl } from "@/components/video/controls/SourceControl";
|
||||||
|
import { Loading } from "@/components/layout/Loading";
|
||||||
|
import { MediaScrapeLog } from "./MediaScrapeLog";
|
||||||
|
|
||||||
function MediaViewLoading() {
|
function MediaViewLoading(props: { onGoBack(): void }) {
|
||||||
return <p>Loading meta...</p>;
|
return (
|
||||||
|
<div className="relative flex h-screen items-center justify-center">
|
||||||
|
<div className="absolute inset-x-0 top-0 p-6">
|
||||||
|
<VideoPlayerHeader onClick={props.onGoBack} />
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col items-center">
|
||||||
|
<Loading className="mb-4" />
|
||||||
|
<p className="mb-8 text-denim-700">Finding the best video for you</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
interface MediaViewScrapingProps {
|
interface MediaViewScrapingProps {
|
||||||
|
@ -18,7 +30,7 @@ interface MediaViewScrapingProps {
|
||||||
meta: DetailedMeta;
|
meta: DetailedMeta;
|
||||||
}
|
}
|
||||||
function MediaViewScraping(props: MediaViewScrapingProps) {
|
function MediaViewScraping(props: MediaViewScrapingProps) {
|
||||||
const { eventLog, pending, stream } = useScrape(props.meta);
|
const { eventLog, stream } = useScrape(props.meta);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (stream) {
|
if (stream) {
|
||||||
|
@ -26,24 +38,21 @@ function MediaViewScraping(props: MediaViewScrapingProps) {
|
||||||
}
|
}
|
||||||
}, [stream, props]);
|
}, [stream, props]);
|
||||||
|
|
||||||
|
// TODO error screen if no streams found
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div className="relative flex h-screen items-center justify-center">
|
||||||
|
<div className="absolute inset-x-0 top-0 py-6 px-8">
|
||||||
<VideoPlayerHeader
|
<VideoPlayerHeader
|
||||||
onClick={props.onGoBack}
|
onClick={props.onGoBack}
|
||||||
title={props.meta.meta.title}
|
title={props.meta.meta.title}
|
||||||
/>
|
/>
|
||||||
<p>pending: {pending.toString()}</p>
|
|
||||||
<p>
|
|
||||||
stream: {stream?.streamUrl} - {stream?.type} - {stream?.quality}
|
|
||||||
</p>
|
|
||||||
<hr />
|
|
||||||
{eventLog.map((v) => (
|
|
||||||
<div className="rounded-xl p-1 text-white">
|
|
||||||
<p>
|
|
||||||
{v.percentage}% - {v.type} - {v.errored ? "ERROR" : "pending"}
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
))}
|
<div className="flex flex-col items-center">
|
||||||
|
<Loading className="mb-4" />
|
||||||
|
<p className="mb-8 text-denim-700">Finding the best video for you</p>
|
||||||
|
<MediaScrapeLog events={eventLog} />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -70,10 +79,9 @@ export function MediaView() {
|
||||||
}, [setMeta, params]);
|
}, [setMeta, params]);
|
||||||
|
|
||||||
// TODO watched store
|
// TODO watched store
|
||||||
// TODO scrape loading state
|
|
||||||
// TODO error page with video header
|
// TODO error page with video header
|
||||||
|
|
||||||
if (!meta) return <MediaViewLoading />;
|
if (!meta) return <MediaViewLoading onGoBack={goBack} />;
|
||||||
if (!stream)
|
if (!stream)
|
||||||
return (
|
return (
|
||||||
<MediaViewScraping meta={meta} onGoBack={goBack} onStream={setStream} />
|
<MediaViewScraping meta={meta} onGoBack={goBack} onStream={setStream} />
|
Loading…
Reference in a new issue