From 9c8e89a274968724710e7f0432e37465a3a8dee8 Mon Sep 17 00:00:00 2001
From: frost768 <droidplus99@gmail.com>
Date: Thu, 27 Apr 2023 21:54:36 +0300
Subject: [PATCH] lint fixes

---
 src/__tests__/subtitles/subtitles.test.ts     |  4 +-
 src/backend/providers/flixhq.ts               |  8 ++--
 src/backend/providers/superstream/index.ts    |  8 ++--
 src/components/Dropdown.tsx                   |  2 +-
 src/components/SearchBar.tsx                  |  4 +-
 src/components/layout/Backdrop.tsx            |  2 +-
 src/components/layout/Navigation.tsx          |  2 +-
 src/components/media/EpisodeButton.tsx        |  4 +-
 src/components/media/MediaCard.tsx            |  2 +-
 src/components/popout/FloatingCard.tsx        |  2 +-
 src/utils/thumbnailCreator.ts                 | 41 +++++++++++++++++++
 src/video/components/VideoPlayer.tsx          |  2 +-
 .../actions/VolumeAdjustedAction.tsx          |  2 +-
 .../list-entries/QualityDisplayAction.tsx     |  2 +-
 .../components/parts/VideoErrorBoundary.tsx   |  2 +-
 .../components/parts/VideoPlayerError.tsx     |  2 +-
 src/views/developer/VideoTesterView.tsx       |  2 +-
 src/views/media/MediaErrorView.tsx            |  2 +-
 src/views/media/MediaView.tsx                 |  6 +--
 src/views/notfound/NotFoundView.tsx           |  8 ++--
 src/views/search/HomeView.tsx                 |  4 +-
 src/views/search/SearchLoadingView.tsx        |  2 +-
 src/views/search/SearchResultsView.tsx        |  2 +-
 src/views/search/SearchView.tsx               |  2 +-
 24 files changed, 80 insertions(+), 37 deletions(-)
 create mode 100644 src/utils/thumbnailCreator.ts

diff --git a/src/__tests__/subtitles/subtitles.test.ts b/src/__tests__/subtitles/subtitles.test.ts
index 50270d5c..69934f8f 100644
--- a/src/__tests__/subtitles/subtitles.test.ts
+++ b/src/__tests__/subtitles/subtitles.test.ts
@@ -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,
diff --git a/src/backend/providers/flixhq.ts b/src/backend/providers/flixhq.ts
index 61568af3..f0f559bd 100644
--- a/src/backend/providers/flixhq.ts
+++ b/src/backend/providers/flixhq.ts
@@ -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";
 
diff --git a/src/backend/providers/superstream/index.ts b/src/backend/providers/superstream/index.ts
index 0730708b..435c36bb 100644
--- a/src/backend/providers/superstream/index.ts
+++ b/src/backend/providers/superstream/index.ts
@@ -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);
 
diff --git a/src/components/Dropdown.tsx b/src/components/Dropdown.tsx
index e2fac636..aff10ea4 100644
--- a/src/components/Dropdown.tsx
+++ b/src/components/Dropdown.tsx
@@ -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 }) =>
diff --git a/src/components/SearchBar.tsx b/src/components/SearchBar.tsx
index 2513e409..4940cbc7 100644
--- a/src/components/SearchBar.tsx
+++ b/src/components/SearchBar.tsx
@@ -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}
diff --git a/src/components/layout/Backdrop.tsx b/src/components/layout/Backdrop.tsx
index 7e022347..0286880e 100644
--- a/src/components/layout/Backdrop.tsx
+++ b/src/components/layout/Backdrop.tsx
@@ -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}
diff --git a/src/components/layout/Navigation.tsx b/src/components/layout/Navigation.tsx
index df6d464b..a9a3e0c1 100644
--- a/src/components/layout/Navigation.tsx
+++ b/src/components/layout/Navigation.tsx
@@ -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"
diff --git a/src/components/media/EpisodeButton.tsx b/src/components/media/EpisodeButton.tsx
index f3e4375c..76e38c85 100644
--- a/src/components/media/EpisodeButton.tsx
+++ b/src/components/media/EpisodeButton.tsx
@@ -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}%`,
         }}
diff --git a/src/components/media/MediaCard.tsx b/src/components/media/MediaCard.tsx
index 3769245e..22865717 100644
--- a/src/components/media/MediaCard.tsx
+++ b/src/components/media/MediaCard.tsx
@@ -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(" ")}
             >
diff --git a/src/components/popout/FloatingCard.tsx b/src/components/popout/FloatingCard.tsx
index 5a837d01..b4fd250c 100644
--- a/src/components/popout/FloatingCard.tsx
+++ b/src/components/popout/FloatingCard.tsx
@@ -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>
diff --git a/src/utils/thumbnailCreator.ts b/src/utils/thumbnailCreator.ts
new file mode 100644
index 00000000..159f4349
--- /dev/null
+++ b/src/utils/thumbnailCreator.ts
@@ -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;
+}
diff --git a/src/video/components/VideoPlayer.tsx b/src/video/components/VideoPlayer.tsx
index 6dc8983a..eca0477e 100644
--- a/src/video/components/VideoPlayer.tsx
+++ b/src/video/components/VideoPlayer.tsx
@@ -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}
diff --git a/src/video/components/actions/VolumeAdjustedAction.tsx b/src/video/components/actions/VolumeAdjustedAction.tsx
index a5c547c5..295f3883 100644
--- a/src/video/components/actions/VolumeAdjustedAction.tsx
+++ b/src/video/components/actions/VolumeAdjustedAction.tsx
@@ -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
diff --git a/src/video/components/actions/list-entries/QualityDisplayAction.tsx b/src/video/components/actions/list-entries/QualityDisplayAction.tsx
index c2a58c50..aef30a06 100644
--- a/src/video/components/actions/list-entries/QualityDisplayAction.tsx
+++ b/src/video/components/actions/list-entries/QualityDisplayAction.tsx
@@ -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>
diff --git a/src/video/components/parts/VideoErrorBoundary.tsx b/src/video/components/parts/VideoErrorBoundary.tsx
index 8228f057..5786aa7a 100644
--- a/src/video/components/parts/VideoErrorBoundary.tsx
+++ b/src/video/components/parts/VideoErrorBoundary.tsx
@@ -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}
diff --git a/src/video/components/parts/VideoPlayerError.tsx b/src/video/components/parts/VideoPlayerError.tsx
index d6fd0306..09177ddb 100644
--- a/src/video/components/parts/VideoPlayerError.tsx
+++ b/src/video/components/parts/VideoPlayerError.tsx
@@ -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>
diff --git a/src/views/developer/VideoTesterView.tsx b/src/views/developer/VideoTesterView.tsx
index 4c3cb7e5..b192cd40 100644
--- a/src/views/developer/VideoTesterView.tsx
+++ b/src/views/developer/VideoTesterView.tsx
@@ -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>
diff --git a/src/views/media/MediaErrorView.tsx b/src/views/media/MediaErrorView.tsx
index e79d3521..b9c88012 100644
--- a/src/views/media/MediaErrorView.tsx
+++ b/src/views/media/MediaErrorView.tsx
@@ -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>
diff --git a/src/views/media/MediaView.tsx b/src/views/media/MediaView.tsx
index 132161dd..c55211c7 100644
--- a/src/views/media/MediaView.tsx
+++ b/src/views/media/MediaView.tsx
@@ -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>
diff --git a/src/views/notfound/NotFoundView.tsx b/src/views/notfound/NotFoundView.tsx
index b7dfccf1..21ba4c63 100644
--- a/src/views/notfound/NotFoundView.tsx
+++ b/src/views/notfound/NotFoundView.tsx
@@ -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>
   );
diff --git a/src/views/search/HomeView.tsx b/src/views/search/HomeView.tsx
index bfb64695..a7a9a396 100644
--- a/src/views/search/HomeView.tsx
+++ b/src/views/search/HomeView.tsx
@@ -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>
diff --git a/src/views/search/SearchLoadingView.tsx b/src/views/search/SearchLoadingView.tsx
index 4c59d677..45971860 100644
--- a/src/views/search/SearchLoadingView.tsx
+++ b/src/views/search/SearchLoadingView.tsx
@@ -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") ||
diff --git a/src/views/search/SearchResultsView.tsx b/src/views/search/SearchResultsView.tsx
index 25d347c9..331d4f2d 100644
--- a/src/views/search/SearchResultsView.tsx
+++ b/src/views/search/SearchResultsView.tsx
@@ -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"}`}
diff --git a/src/views/search/SearchView.tsx b/src/views/search/SearchView.tsx
index fce4ce08..3fee80b8 100644
--- a/src/views/search/SearchView.tsx
+++ b/src/views/search/SearchView.tsx
@@ -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">