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" > - + {props.options.map((opt) => ( 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 (
-
+
@@ -52,7 +52,7 @@ export function SearchBarInput(props: SearchBarProps) { placeholder={props.placeholder} /> -
+
{createPortal( -
+
{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`, }} > -
+
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 = {
{props.action ?? null}
-

+

{props.title}

{props.description}

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 { + 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) { +

{source.source.quality}

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 (
-
+
-
+
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 ( -
+
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() { {t("media.errors.failedMeta")} -
+
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 }) { {t("videoPlayer.loading")} -
+
@@ -68,7 +68,7 @@ function MediaViewScraping(props: MediaViewScrapingProps) { {props.meta.meta.title} -
+
@@ -134,7 +134,7 @@ export function MediaViewPlayer(props: MediaViewPlayerProps) { } return ( -
+
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: { {t("notFound.genericTitle")} {props.video ? ( -
+
) : ( @@ -46,7 +46,7 @@ export function NotFoundMedia() { className="mb-6 text-xl text-bink-600" /> {t("notFound.media.title")} -

{t("notFound.media.description")}

+

{t("notFound.media.description")}

); @@ -62,7 +62,7 @@ export function NotFoundProvider() { className="mb-6 text-xl text-bink-600" /> {t("notFound.provider.title")} -

+

{t("notFound.provider.description")}

@@ -80,7 +80,7 @@ export function NotFoundPage() { className="mb-6 text-xl text-bink-600" /> {t("notFound.page.title")} -

{t("notFound.page.description")}

+

{t("notFound.page.description")}

); 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() { }} />
-
+
{t("v3.newDomain")}
@@ -186,7 +186,7 @@ function NewDomainModal() {

{t("v3.tireless")}

-
+
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 ( +
-
+