mirror of
https://github.com/movie-web/movie-web.git
synced 2024-12-27 08:11:48 +01:00
lint fixes
This commit is contained in:
parent
bf135a2bdf
commit
9c8e89a274
@ -1,10 +1,12 @@
|
||||
import { describe, it } from "vitest";
|
||||
|
||||
import {
|
||||
isSupportedSubtitle,
|
||||
getMWCaptionTypeFromUrl,
|
||||
isSupportedSubtitle,
|
||||
parseSubtitles,
|
||||
} from "@/backend/helpers/captions";
|
||||
import { MWCaptionType } from "@/backend/helpers/streams";
|
||||
|
||||
import {
|
||||
ass,
|
||||
multilineSubtitlesTestVtt,
|
||||
|
@ -1,13 +1,13 @@
|
||||
import { compareTitle } from "@/utils/titleMatch";
|
||||
|
||||
import { proxiedFetch } from "../helpers/fetch";
|
||||
import { registerProvider } from "../helpers/register";
|
||||
import { MWCaption, MWStreamQuality, MWStreamType } from "../helpers/streams";
|
||||
import { MWMediaType } from "../metadata/types";
|
||||
import {
|
||||
getMWCaptionTypeFromUrl,
|
||||
isSupportedSubtitle,
|
||||
} from "../helpers/captions";
|
||||
import { proxiedFetch } from "../helpers/fetch";
|
||||
import { registerProvider } from "../helpers/register";
|
||||
import { MWCaption, MWStreamQuality, MWStreamType } from "../helpers/streams";
|
||||
import { MWMediaType } from "../metadata/types";
|
||||
|
||||
const flixHqBase = "https://api.consumet.org/meta/tmdb";
|
||||
|
||||
|
@ -1,6 +1,10 @@
|
||||
import CryptoJS from "crypto-js";
|
||||
import { customAlphabet } from "nanoid";
|
||||
|
||||
import {
|
||||
getMWCaptionTypeFromUrl,
|
||||
isSupportedSubtitle,
|
||||
} from "@/backend/helpers/captions";
|
||||
import { proxiedFetch } from "@/backend/helpers/fetch";
|
||||
import { registerProvider } from "@/backend/helpers/register";
|
||||
import {
|
||||
@ -11,10 +15,6 @@ import {
|
||||
} from "@/backend/helpers/streams";
|
||||
import { MWMediaType } from "@/backend/metadata/types";
|
||||
import { compareTitle } from "@/utils/titleMatch";
|
||||
import {
|
||||
getMWCaptionTypeFromUrl,
|
||||
isSupportedSubtitle,
|
||||
} from "@/backend/helpers/captions";
|
||||
|
||||
const nanoid = customAlphabet("0123456789abcdef", 32);
|
||||
|
||||
|
@ -37,7 +37,7 @@ export function Dropdown(props: DropdownProps) {
|
||||
leaveFrom="opacity-100"
|
||||
leaveTo="opacity-0"
|
||||
>
|
||||
<Listbox.Options className="absolute top-10 left-0 right-0 z-10 mt-1 max-h-60 overflow-auto rounded-md bg-denim-500 py-1 text-white shadow-lg ring-1 ring-black ring-opacity-5 scrollbar-thin scrollbar-track-denim-400 scrollbar-thumb-denim-200 focus:outline-none sm:top-10 sm:text-sm">
|
||||
<Listbox.Options className="absolute left-0 right-0 top-10 z-10 mt-1 max-h-60 overflow-auto rounded-md bg-denim-500 py-1 text-white shadow-lg ring-1 ring-black ring-opacity-5 scrollbar-thin scrollbar-track-denim-400 scrollbar-thumb-denim-200 focus:outline-none sm:top-10 sm:text-sm">
|
||||
{props.options.map((opt) => (
|
||||
<Listbox.Option
|
||||
className={({ active }) =>
|
||||
|
@ -40,7 +40,7 @@ export function SearchBarInput(props: SearchBarProps) {
|
||||
|
||||
return (
|
||||
<div className="relative flex flex-col rounded-[28px] bg-denim-400 transition-colors focus-within:bg-denim-400 hover:bg-denim-500 sm:flex-row sm:items-center">
|
||||
<div className="pointer-events-none absolute left-5 top-0 bottom-0 flex max-h-14 items-center">
|
||||
<div className="pointer-events-none absolute bottom-0 left-5 top-0 flex max-h-14 items-center">
|
||||
<Icon icon={Icons.SEARCH} />
|
||||
</div>
|
||||
|
||||
@ -52,7 +52,7 @@ export function SearchBarInput(props: SearchBarProps) {
|
||||
placeholder={props.placeholder}
|
||||
/>
|
||||
|
||||
<div className="px-4 py-4 pt-0 sm:py-2 sm:px-2">
|
||||
<div className="px-4 py-4 pt-0 sm:px-2 sm:py-2">
|
||||
<DropdownButton
|
||||
icon={Icons.SEARCH}
|
||||
open={dropdownOpen}
|
||||
|
@ -100,7 +100,7 @@ export function BackdropContainer(
|
||||
return (
|
||||
<div ref={root}>
|
||||
{createPortal(
|
||||
<div className="pointer-events-none fixed top-0 left-0 z-[999]">
|
||||
<div className="pointer-events-none fixed left-0 top-0 z-[999]">
|
||||
<Backdrop active={props.active} {...props} />
|
||||
<div ref={copy} className="pointer-events-auto absolute">
|
||||
{props.children}
|
||||
|
@ -24,7 +24,7 @@ export function Navigation(props: NavigationProps) {
|
||||
top: `${bannerHeight}px`,
|
||||
}}
|
||||
>
|
||||
<div className="fixed left-0 right-0 flex items-center justify-between py-5 px-7">
|
||||
<div className="fixed left-0 right-0 flex items-center justify-between px-7 py-5">
|
||||
<div
|
||||
className={`${
|
||||
props.bg ? "opacity-100" : "opacity-0"
|
||||
|
@ -9,12 +9,12 @@ export function Episode(props: EpisodeProps) {
|
||||
return (
|
||||
<div
|
||||
onClick={props.onClick}
|
||||
className={`transition-[background-color, transform, box-shadow] relative mr-3 mb-3 inline-flex h-10 w-10 cursor-pointer select-none items-center justify-center overflow-hidden rounded bg-denim-500 font-bold text-white hover:bg-denim-400 active:scale-110 ${
|
||||
className={`transition-[background-color, transform, box-shadow] relative mb-3 mr-3 inline-flex h-10 w-10 cursor-pointer select-none items-center justify-center overflow-hidden rounded bg-denim-500 font-bold text-white hover:bg-denim-400 active:scale-110 ${
|
||||
props.active ? "shadow-[inset_0_0_0_2px] shadow-bink-500" : ""
|
||||
}`}
|
||||
>
|
||||
<div
|
||||
className="absolute bottom-0 top-0 left-0 bg-bink-500 bg-opacity-50"
|
||||
className="absolute bottom-0 left-0 top-0 bg-bink-500 bg-opacity-50"
|
||||
style={{
|
||||
width: `${props.progress || 0}%`,
|
||||
}}
|
||||
|
@ -61,7 +61,7 @@ function MediaCardContent({
|
||||
{series ? (
|
||||
<div
|
||||
className={[
|
||||
"absolute right-2 top-2 rounded-md bg-denim-200 py-1 px-2 transition-colors",
|
||||
"absolute right-2 top-2 rounded-md bg-denim-200 px-2 py-1 transition-colors",
|
||||
closable ? "" : "group-hover:bg-denim-500",
|
||||
].join(" ")}
|
||||
>
|
||||
|
@ -167,7 +167,7 @@ export const FloatingCardView = {
|
||||
<div>{props.action ?? null}</div>
|
||||
</div>
|
||||
|
||||
<h2 className="mt-8 mb-2 text-3xl font-bold text-white">
|
||||
<h2 className="mb-2 mt-8 text-3xl font-bold text-white">
|
||||
{props.title}
|
||||
</h2>
|
||||
<p>{props.description}</p>
|
||||
|
41
src/utils/thumbnailCreator.ts
Normal file
41
src/utils/thumbnailCreator.ts
Normal file
@ -0,0 +1,41 @@
|
||||
export default async function extractThumbnails(
|
||||
videoUrl: string,
|
||||
numThumbnails: number
|
||||
): Promise<string[]> {
|
||||
const video = document.createElement("video");
|
||||
video.src = videoUrl;
|
||||
video.crossOrigin = "anonymous";
|
||||
|
||||
// Wait for the video metadata to load
|
||||
const metadata = await new Promise((resolve, reject) => {
|
||||
video.addEventListener("loadedmetadata", resolve);
|
||||
video.addEventListener("error", reject);
|
||||
});
|
||||
console.log(metadata);
|
||||
|
||||
const canvas = document.createElement("canvas");
|
||||
canvas.width = video.videoWidth;
|
||||
canvas.height = video.videoHeight;
|
||||
const ctx = canvas.getContext("2d");
|
||||
const thumbnails = [];
|
||||
if (!ctx) return [""];
|
||||
|
||||
for (let i = 0; i < numThumbnails; i += 1) {
|
||||
const time = ((i + 1) / (numThumbnails + 1)) * video.duration;
|
||||
|
||||
// Seek to the specified time
|
||||
video.currentTime = time;
|
||||
await new Promise((resolve) => {
|
||||
video.addEventListener("seeked", resolve);
|
||||
});
|
||||
|
||||
// Draw the video frame on the canvas
|
||||
ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
|
||||
|
||||
// Convert the canvas to a data URL and add it to the list of thumbnails
|
||||
const thumbnailUrl = canvas.toDataURL("image/jpeg", 0.8);
|
||||
thumbnails.push(thumbnailUrl);
|
||||
}
|
||||
|
||||
return thumbnails;
|
||||
}
|
@ -120,7 +120,7 @@ export function VideoPlayer(props: Props) {
|
||||
<Transition
|
||||
animation="slide-down"
|
||||
show={show}
|
||||
className="pointer-events-auto absolute inset-x-0 top-0 flex flex-col py-6 px-8 pb-2"
|
||||
className="pointer-events-auto absolute inset-x-0 top-0 flex flex-col px-8 py-6 pb-2"
|
||||
>
|
||||
<HeaderAction
|
||||
showControls={isMobile}
|
||||
|
@ -14,7 +14,7 @@ export function VolumeAdjustedAction() {
|
||||
videoInterface.volumeChangedWithKeybind
|
||||
? "mt-10 scale-100 opacity-100"
|
||||
: "mt-5 scale-75 opacity-0",
|
||||
"absolute left-1/2 z-[100] flex -translate-x-1/2 items-center space-x-4 rounded-full bg-bink-300 bg-opacity-50 py-2 px-5 transition-all duration-100",
|
||||
"absolute left-1/2 z-[100] flex -translate-x-1/2 items-center space-x-4 rounded-full bg-bink-300 bg-opacity-50 px-5 py-2 transition-all duration-100",
|
||||
].join(" ")}
|
||||
>
|
||||
<Icon
|
||||
|
@ -8,7 +8,7 @@ export function QualityDisplayAction() {
|
||||
if (!source.source) return null;
|
||||
|
||||
return (
|
||||
<div className="rounded-md bg-denim-300 py-1 px-2 transition-colors">
|
||||
<div className="rounded-md bg-denim-300 px-2 py-1 transition-colors">
|
||||
<p className="text-center text-xs font-bold text-slate-300 transition-colors">
|
||||
{source.source.quality}
|
||||
</p>
|
||||
|
@ -64,7 +64,7 @@ export class VideoErrorBoundary extends Component<
|
||||
|
||||
return (
|
||||
<div className="absolute inset-0 bg-denim-100">
|
||||
<div className="pointer-events-auto absolute inset-x-0 top-0 flex flex-col py-6 px-8 pb-2">
|
||||
<div className="pointer-events-auto absolute inset-x-0 top-0 flex flex-col px-8 py-6 pb-2">
|
||||
<VideoPlayerHeader
|
||||
media={this.props.media}
|
||||
onClick={this.props.onGoBack}
|
||||
|
@ -32,7 +32,7 @@ export function VideoPlayerError(props: VideoPlayerErrorProps) {
|
||||
{err?.name}: {err?.description}
|
||||
</p>
|
||||
</div>
|
||||
<div className="pointer-events-auto absolute inset-x-0 top-0 flex flex-col py-6 px-8 pb-2">
|
||||
<div className="pointer-events-auto absolute inset-x-0 top-0 flex flex-col px-8 py-6 pb-2">
|
||||
<VideoPlayerHeader media={meta?.meta.meta} onClick={props.onGoBack} />
|
||||
</div>
|
||||
</div>
|
||||
|
@ -51,7 +51,7 @@ export default function VideoTesterView() {
|
||||
|
||||
if (video) {
|
||||
return (
|
||||
<div className="fixed top-0 left-0 h-[100dvh] w-screen">
|
||||
<div className="fixed left-0 top-0 h-[100dvh] w-screen">
|
||||
<Helmet>
|
||||
<html data-full="true" />
|
||||
</Helmet>
|
||||
|
@ -14,7 +14,7 @@ export function MediaFetchErrorView() {
|
||||
<Helmet>
|
||||
<title>{t("media.errors.failedMeta")}</title>
|
||||
</Helmet>
|
||||
<div className="fixed inset-x-0 top-0 py-6 px-8">
|
||||
<div className="fixed inset-x-0 top-0 px-8 py-6">
|
||||
<VideoPlayerHeader onClick={goBack} />
|
||||
</div>
|
||||
<ErrorMessage>
|
||||
|
@ -34,7 +34,7 @@ function MediaViewLoading(props: { onGoBack(): void }) {
|
||||
<Helmet>
|
||||
<title>{t("videoPlayer.loading")}</title>
|
||||
</Helmet>
|
||||
<div className="absolute inset-x-0 top-0 py-6 px-8">
|
||||
<div className="absolute inset-x-0 top-0 px-8 py-6">
|
||||
<VideoPlayerHeader onClick={props.onGoBack} />
|
||||
</div>
|
||||
<div className="flex flex-col items-center">
|
||||
@ -68,7 +68,7 @@ function MediaViewScraping(props: MediaViewScrapingProps) {
|
||||
<Helmet>
|
||||
<title>{props.meta.meta.title}</title>
|
||||
</Helmet>
|
||||
<div className="absolute inset-x-0 top-0 py-6 px-8">
|
||||
<div className="absolute inset-x-0 top-0 px-8 py-6">
|
||||
<VideoPlayerHeader onClick={props.onGoBack} media={props.meta.meta} />
|
||||
</div>
|
||||
<div className="flex flex-col items-center transition-opacity duration-200">
|
||||
@ -134,7 +134,7 @@ export function MediaViewPlayer(props: MediaViewPlayerProps) {
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="fixed top-0 left-0 h-[100dvh] w-screen">
|
||||
<div className="fixed left-0 top-0 h-[100dvh] w-screen">
|
||||
<Helmet>
|
||||
<html data-full="true" />
|
||||
</Helmet>
|
||||
|
@ -23,7 +23,7 @@ export function NotFoundWrapper(props: {
|
||||
<title>{t("notFound.genericTitle")}</title>
|
||||
</Helmet>
|
||||
{props.video ? (
|
||||
<div className="absolute inset-x-0 top-0 py-6 px-8">
|
||||
<div className="absolute inset-x-0 top-0 px-8 py-6">
|
||||
<VideoPlayerHeader onClick={goBack} />
|
||||
</div>
|
||||
) : (
|
||||
@ -46,7 +46,7 @@ export function NotFoundMedia() {
|
||||
className="mb-6 text-xl text-bink-600"
|
||||
/>
|
||||
<Title>{t("notFound.media.title")}</Title>
|
||||
<p className="mt-5 mb-12 max-w-sm">{t("notFound.media.description")}</p>
|
||||
<p className="mb-12 mt-5 max-w-sm">{t("notFound.media.description")}</p>
|
||||
<ArrowLink to="/" linkText={t("notFound.backArrow")} />
|
||||
</div>
|
||||
);
|
||||
@ -62,7 +62,7 @@ export function NotFoundProvider() {
|
||||
className="mb-6 text-xl text-bink-600"
|
||||
/>
|
||||
<Title>{t("notFound.provider.title")}</Title>
|
||||
<p className="mt-5 mb-12 max-w-sm">
|
||||
<p className="mb-12 mt-5 max-w-sm">
|
||||
{t("notFound.provider.description")}
|
||||
</p>
|
||||
<ArrowLink to="/" linkText={t("notFound.backArrow")} />
|
||||
@ -80,7 +80,7 @@ export function NotFoundPage() {
|
||||
className="mb-6 text-xl text-bink-600"
|
||||
/>
|
||||
<Title>{t("notFound.page.title")}</Title>
|
||||
<p className="mt-5 mb-12 max-w-sm">{t("notFound.page.description")}</p>
|
||||
<p className="mb-12 mt-5 max-w-sm">{t("notFound.page.description")}</p>
|
||||
<ArrowLink to="/" linkText={t("notFound.backArrow")} />
|
||||
</NotFoundWrapper>
|
||||
);
|
||||
|
@ -169,7 +169,7 @@ function NewDomainModal() {
|
||||
}}
|
||||
/>
|
||||
<div className="relative flex items-center justify-center">
|
||||
<div className="rounded-full bg-bink-200 py-4 px-12 text-center text-sm font-bold text-white md:text-xl">
|
||||
<div className="rounded-full bg-bink-200 px-12 py-4 text-center text-sm font-bold text-white md:text-xl">
|
||||
{t("v3.newDomain")}
|
||||
</div>
|
||||
</div>
|
||||
@ -186,7 +186,7 @@ function NewDomainModal() {
|
||||
</p>
|
||||
<p>{t("v3.tireless")}</p>
|
||||
</div>
|
||||
<div className="mt-16 mb-6 flex items-center justify-center">
|
||||
<div className="mb-6 mt-16 flex items-center justify-center">
|
||||
<Button icon={Icons.PLAY} onClick={() => closeModal()}>
|
||||
{t("v3.leaveAnnouncement")}
|
||||
</Button>
|
||||
|
@ -8,7 +8,7 @@ export function SearchLoadingView() {
|
||||
const [query] = useSearchQuery();
|
||||
return (
|
||||
<Loading
|
||||
className="mt-40 mb-24 "
|
||||
className="mb-24 mt-40 "
|
||||
text={
|
||||
t(`search.loading_${query.type}`) ||
|
||||
t("search.loading") ||
|
||||
|
@ -18,7 +18,7 @@ function SearchSuffix(props: { failed?: boolean; results?: number }) {
|
||||
const icon: Icons = props.failed ? Icons.WARNING : Icons.EYE_SLASH;
|
||||
|
||||
return (
|
||||
<div className="mt-40 mb-24 flex flex-col items-center justify-center space-y-3 text-center">
|
||||
<div className="mb-24 mt-40 flex flex-col items-center justify-center space-y-3 text-center">
|
||||
<IconPatch
|
||||
icon={icon}
|
||||
className={`text-xl ${props.failed ? "text-red-400" : "text-bink-600"}`}
|
||||
|
@ -33,7 +33,7 @@ export function SearchView() {
|
||||
<Navigation bg={showBg} />
|
||||
<ThinContainer>
|
||||
<div className="mt-44 space-y-16 text-center">
|
||||
<div className="absolute left-0 bottom-0 right-0 flex h-0 justify-center">
|
||||
<div className="absolute bottom-0 left-0 right-0 flex h-0 justify-center">
|
||||
<div className="absolute bottom-4 h-[100vh] w-[3000px] rounded-[100%] bg-denim-300 md:w-[200vw]" />
|
||||
</div>
|
||||
<div className="relative z-10 mb-16">
|
||||
|
Loading…
Reference in New Issue
Block a user