diff --git a/.eslintrc.js b/.eslintrc.js
index 3bba5beb..d75c57d9 100644
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -8,27 +8,28 @@ const a11yOff = Object.keys(require("eslint-plugin-jsx-a11y").rules).reduce(
module.exports = {
env: {
- browser: true
+ browser: true,
},
extends: [
"airbnb",
"airbnb/hooks",
"plugin:@typescript-eslint/recommended",
- "prettier",
- "plugin:prettier/recommended"
+ "plugin:prettier/recommended",
],
ignorePatterns: ["public/*", "dist/*", "/*.js", "/*.ts"],
parser: "@typescript-eslint/parser",
parserOptions: {
project: "./tsconfig.json",
- tsconfigRootDir: "./"
+ tsconfigRootDir: "./",
},
settings: {
"import/resolver": {
- typescript: {}
- }
+ typescript: {
+ project: "./tsconfig.json",
+ },
+ },
},
- plugins: ["@typescript-eslint", "import"],
+ plugins: ["@typescript-eslint", "import", "prettier"],
rules: {
"react/jsx-uses-react": "off",
"react/react-in-jsx-scope": "off",
@@ -54,16 +55,44 @@ module.exports = {
"@typescript-eslint/no-unused-vars": ["warn", { argsIgnorePattern: "^_" }],
"react/jsx-filename-extension": [
"error",
- { extensions: [".js", ".tsx", ".jsx"] }
+ { extensions: [".js", ".tsx", ".jsx"] },
],
"import/extensions": [
"error",
"ignorePackages",
{
ts: "never",
- tsx: "never"
- }
+ tsx: "never",
+ },
],
- ...a11yOff
- }
+ "import/order": [
+ "error",
+ {
+ groups: [
+ "builtin",
+ "external",
+ "internal",
+ ["sibling", "parent"],
+ "index",
+ "unknown",
+ ],
+ "newlines-between": "always",
+ alphabetize: {
+ order: "asc",
+ caseInsensitive: true,
+ },
+ },
+ ],
+ "sort-imports": [
+ "error",
+ {
+ ignoreCase: false,
+ ignoreDeclarationSort: true,
+ ignoreMemberSort: false,
+ memberSyntaxSortOrder: ["none", "all", "multiple", "single"],
+ allowSeparatedGroups: true,
+ },
+ ],
+ ...a11yOff,
+ },
};
diff --git a/README.md b/README.md
index 1167c00a..aaaa12cb 100644
--- a/README.md
+++ b/README.md
@@ -8,7 +8,7 @@
-movie-web is a web app for watching movies easily. Check it out at **[movie.squeezebox.dev](https://movie.squeezebox.dev)**.
+movie-web is a web app for watching movies easily. Check it out at **[movie-web.app](https://movie-web.app)**.
This service works by displaying video files from third-party providers inside an intuitive and aesthetic user interface.
diff --git a/package.json b/package.json
index a9917a43..a4cb6bf2 100644
--- a/package.json
+++ b/package.json
@@ -2,11 +2,13 @@
"name": "movie-web",
"version": "3.0.14",
"private": true,
- "homepage": "https://movie.squeezebox.dev",
+ "homepage": "https://movie-web.app",
"dependencies": {
"@formkit/auto-animate": "^1.0.0-beta.5",
"@headlessui/react": "^1.5.0",
"@react-spring/web": "^9.7.1",
+ "@sentry/integrations": "^7.49.0",
+ "@sentry/react": "^7.49.0",
"@use-gesture/react": "^10.2.24",
"core-js": "^3.29.1",
"crypto-js": "^4.1.1",
@@ -80,7 +82,7 @@
"eslint-config-airbnb": "19.0.4",
"eslint-config-prettier": "^8.6.0",
"eslint-import-resolver-typescript": "^2.5.0",
- "eslint-plugin-import": "^2.25.4",
+ "eslint-plugin-import": "^2.27.5",
"eslint-plugin-jsx-a11y": "^6.5.1",
"eslint-plugin-prettier": "^4.2.1",
"eslint-plugin-react": "7.29.4",
diff --git a/src/__tests__/providers/providers.test.ts b/src/__tests__/providers/providers.test.ts
index 079821c9..35c77d5d 100644
--- a/src/__tests__/providers/providers.test.ts
+++ b/src/__tests__/providers/providers.test.ts
@@ -1,9 +1,10 @@
import { describe, it } from "vitest";
+
import "@/backend";
-import { getProviders } from "@/backend/helpers/register";
-import { MWMediaType } from "@/backend/metadata/types";
-import { runProvider } from "@/backend/helpers/run";
import { testData } from "@/__tests__/providers/testdata";
+import { getProviders } from "@/backend/helpers/register";
+import { runProvider } from "@/backend/helpers/run";
+import { MWMediaType } from "@/backend/metadata/types";
describe("providers", () => {
const providers = getProviders();
diff --git a/src/backend/embeds/streamm4u.ts b/src/backend/embeds/streamm4u.ts
index b28b7ab8..abdc1486 100644
--- a/src/backend/embeds/streamm4u.ts
+++ b/src/backend/embeds/streamm4u.ts
@@ -1,11 +1,11 @@
import { MWEmbedType } from "@/backend/helpers/embed";
+import { proxiedFetch } from "@/backend/helpers/fetch";
import { registerEmbedScraper } from "@/backend/helpers/register";
import {
+ MWEmbedStream,
MWStreamQuality,
MWStreamType,
- MWEmbedStream,
} from "@/backend/helpers/streams";
-import { proxiedFetch } from "@/backend/helpers/fetch";
const HOST = "streamm4u.club";
const URL_BASE = `https://${HOST}`;
diff --git a/src/backend/helpers/captions.ts b/src/backend/helpers/captions.ts
index b5e40108..c50b8e1c 100644
--- a/src/backend/helpers/captions.ts
+++ b/src/backend/helpers/captions.ts
@@ -1,8 +1,9 @@
+import DOMPurify from "dompurify";
+import { detect, list, parse } from "subsrt-ts";
+import { ContentCaption } from "subsrt-ts/dist/types/handler";
+
import { mwFetch, proxiedFetch } from "@/backend/helpers/fetch";
import { MWCaption } from "@/backend/helpers/streams";
-import DOMPurify from "dompurify";
-import { parse, detect, list } from "subsrt-ts";
-import { ContentCaption } from "subsrt-ts/dist/types/handler";
export const customCaption = "external-custom";
export function makeCaptionId(caption: MWCaption, isLinked: boolean): string {
diff --git a/src/backend/helpers/fetch.ts b/src/backend/helpers/fetch.ts
index 393ff7f8..9ed16b7c 100644
--- a/src/backend/helpers/fetch.ts
+++ b/src/backend/helpers/fetch.ts
@@ -1,6 +1,7 @@
-import { conf } from "@/setup/config";
import { ofetch } from "ofetch";
+import { conf } from "@/setup/config";
+
let proxyUrlIndex = Math.floor(Math.random() * conf().PROXY_URLS.length);
// round robins all proxy urls
diff --git a/src/backend/helpers/provider.ts b/src/backend/helpers/provider.ts
index 95f5e374..6eed4560 100644
--- a/src/backend/helpers/provider.ts
+++ b/src/backend/helpers/provider.ts
@@ -1,7 +1,7 @@
-import { DetailedMeta } from "../metadata/getmeta";
-import { MWMediaType } from "../metadata/types";
import { MWEmbed } from "./embed";
import { MWStream } from "./streams";
+import { DetailedMeta } from "../metadata/getmeta";
+import { MWMediaType } from "../metadata/types";
export type MWProviderScrapeResult = {
stream?: MWStream;
diff --git a/src/backend/metadata/getmeta.ts b/src/backend/metadata/getmeta.ts
index c0c9e92c..215448e5 100644
--- a/src/backend/metadata/getmeta.ts
+++ b/src/backend/metadata/getmeta.ts
@@ -1,13 +1,14 @@
import { FetchError } from "ofetch";
-import { makeUrl, proxiedFetch } from "../helpers/fetch";
+
import {
- formatJWMeta,
JWMediaResult,
JWSeasonMetaResult,
JW_API_BASE,
+ formatJWMeta,
mediaTypeToJW,
} from "./justwatch";
import { MWMediaMeta, MWMediaType } from "./types";
+import { makeUrl, proxiedFetch } from "../helpers/fetch";
type JWExternalIdType =
| "eidr"
diff --git a/src/backend/metadata/search.ts b/src/backend/metadata/search.ts
index 1c3c4598..10cbb285 100644
--- a/src/backend/metadata/search.ts
+++ b/src/backend/metadata/search.ts
@@ -1,13 +1,14 @@
import { SimpleCache } from "@/utils/cache";
-import { proxiedFetch } from "../helpers/fetch";
+
import {
- formatJWMeta,
JWContentTypes,
JWMediaResult,
JW_API_BASE,
+ formatJWMeta,
mediaTypeToJW,
} from "./justwatch";
import { MWMediaMeta, MWQuery } from "./types";
+import { proxiedFetch } from "../helpers/fetch";
const cache = new SimpleCache();
cache.setCompare((a, b) => {
diff --git a/src/backend/providers/flixhq.ts b/src/backend/providers/flixhq.ts
index d9440213..fb5a0bb1 100644
--- a/src/backend/providers/flixhq.ts
+++ b/src/backend/providers/flixhq.ts
@@ -1,4 +1,5 @@
import { compareTitle } from "@/utils/titleMatch";
+
import { proxiedFetch } from "../helpers/fetch";
import { registerProvider } from "../helpers/register";
import {
diff --git a/src/backend/providers/gdriveplayer.ts b/src/backend/providers/gdriveplayer.ts
index a2a0063d..2023b67d 100644
--- a/src/backend/providers/gdriveplayer.ts
+++ b/src/backend/providers/gdriveplayer.ts
@@ -1,9 +1,10 @@
-import { unpack } from "unpacker";
import CryptoJS from "crypto-js";
+import { unpack } from "unpacker";
import { registerProvider } from "@/backend/helpers/register";
-import { MWMediaType } from "@/backend/metadata/types";
import { MWStreamQuality } from "@/backend/helpers/streams";
+import { MWMediaType } from "@/backend/metadata/types";
+
import { proxiedFetch } from "../helpers/fetch";
const format = {
diff --git a/src/backend/providers/m4ufree.ts b/src/backend/providers/m4ufree.ts
index f4e79f5b..0fe5303d 100644
--- a/src/backend/providers/m4ufree.ts
+++ b/src/backend/providers/m4ufree.ts
@@ -1,4 +1,5 @@
import { MWEmbed, MWEmbedType } from "@/backend/helpers/embed";
+
import { proxiedFetch } from "../helpers/fetch";
import { registerProvider } from "../helpers/register";
import { MWMediaType } from "../metadata/types";
diff --git a/src/backend/providers/superstream/index.ts b/src/backend/providers/superstream/index.ts
index 3c499739..bf9ce9dd 100644
--- a/src/backend/providers/superstream/index.ts
+++ b/src/backend/providers/superstream/index.ts
@@ -1,15 +1,15 @@
-import { registerProvider } from "@/backend/helpers/register";
-import { MWMediaType } from "@/backend/metadata/types";
-
-import { customAlphabet } from "nanoid";
import CryptoJS from "crypto-js";
+import { customAlphabet } from "nanoid";
+
import { proxiedFetch } from "@/backend/helpers/fetch";
+import { registerProvider } from "@/backend/helpers/register";
import {
MWCaption,
MWCaptionType,
MWStreamQuality,
MWStreamType,
} from "@/backend/helpers/streams";
+import { MWMediaType } from "@/backend/metadata/types";
import { compareTitle } from "@/utils/titleMatch";
const nanoid = customAlphabet("0123456789abcdef", 32);
diff --git a/src/components/Button.tsx b/src/components/Button.tsx
index 9a74f84d..eefeef01 100644
--- a/src/components/Button.tsx
+++ b/src/components/Button.tsx
@@ -1,6 +1,7 @@
-import { Icon, Icons } from "@/components/Icon";
import { ReactNode } from "react";
+import { Icon, Icons } from "@/components/Icon";
+
interface Props {
icon?: Icons;
onClick?: () => void;
diff --git a/src/components/CaptionColorSelector.tsx b/src/components/CaptionColorSelector.tsx
index 3d286f78..7df66320 100644
--- a/src/components/CaptionColorSelector.tsx
+++ b/src/components/CaptionColorSelector.tsx
@@ -1,4 +1,5 @@
import { useSettings } from "@/state/settings";
+
import { Icon, Icons } from "./Icon";
export const colors = ["#ffffff", "#00ffff", "#ffff00"];
diff --git a/src/components/Dropdown.tsx b/src/components/Dropdown.tsx
index 9321e1fe..e2fac636 100644
--- a/src/components/Dropdown.tsx
+++ b/src/components/Dropdown.tsx
@@ -1,6 +1,6 @@
+import { Listbox, Transition } from "@headlessui/react";
import React, { Fragment } from "react";
-import { Listbox, Transition } from "@headlessui/react";
import { Icon, Icons } from "@/components/Icon";
export interface OptionItem {
diff --git a/src/components/Overlay.tsx b/src/components/Overlay.tsx
index 83e61f8f..2129f68d 100644
--- a/src/components/Overlay.tsx
+++ b/src/components/Overlay.tsx
@@ -1,6 +1,7 @@
-import { Transition } from "@/components/Transition";
import { Helmet } from "react-helmet";
+import { Transition } from "@/components/Transition";
+
export function Overlay(props: { children: React.ReactNode }) {
return (
<>
diff --git a/src/components/SearchBar.tsx b/src/components/SearchBar.tsx
index 2b937549..2513e409 100644
--- a/src/components/SearchBar.tsx
+++ b/src/components/SearchBar.tsx
@@ -1,6 +1,8 @@
-import { MWMediaType, MWQuery } from "@/backend/metadata/types";
import { useState } from "react";
import { useTranslation } from "react-i18next";
+
+import { MWMediaType, MWQuery } from "@/backend/metadata/types";
+
import { DropdownButton } from "./buttons/DropdownButton";
import { Icon, Icons } from "./Icon";
import { TextInputControl } from "./text-inputs/TextInputControl";
diff --git a/src/components/Transition.tsx b/src/components/Transition.tsx
index f7d0d533..b46df9b1 100644
--- a/src/components/Transition.tsx
+++ b/src/components/Transition.tsx
@@ -1,8 +1,8 @@
-import { Fragment, ReactNode } from "react";
import {
Transition as HeadlessTransition,
TransitionClasses,
} from "@headlessui/react";
+import { Fragment, ReactNode } from "react";
type TransitionAnimations =
| "slide-down"
diff --git a/src/components/buttons/DropdownButton.tsx b/src/components/buttons/DropdownButton.tsx
index a49403e8..8e252231 100644
--- a/src/components/buttons/DropdownButton.tsx
+++ b/src/components/buttons/DropdownButton.tsx
@@ -4,10 +4,11 @@ import React, {
useEffect,
useState,
} from "react";
-import { Icon, Icons } from "@/components/Icon";
+import { Icon, Icons } from "@/components/Icon";
import { BackdropContainer, useBackdrop } from "@/components/layout/Backdrop";
-import { ButtonControlProps, ButtonControl } from "./ButtonControl";
+
+import { ButtonControl, ButtonControlProps } from "./ButtonControl";
export interface OptionItem {
id: string;
diff --git a/src/components/buttons/EditButton.tsx b/src/components/buttons/EditButton.tsx
index bcdd3cfd..4571af10 100644
--- a/src/components/buttons/EditButton.tsx
+++ b/src/components/buttons/EditButton.tsx
@@ -1,7 +1,9 @@
-import { Icon, Icons } from "@/components/Icon";
import { useAutoAnimate } from "@formkit/auto-animate/react";
import { useCallback } from "react";
import { useTranslation } from "react-i18next";
+
+import { Icon, Icons } from "@/components/Icon";
+
import { ButtonControl } from "./ButtonControl";
export interface EditButtonProps {
diff --git a/src/components/buttons/IconButton.tsx b/src/components/buttons/IconButton.tsx
index b247a440..0a33a878 100644
--- a/src/components/buttons/IconButton.tsx
+++ b/src/components/buttons/IconButton.tsx
@@ -1,5 +1,6 @@
import { Icon, Icons } from "@/components/Icon";
-import { ButtonControlProps, ButtonControl } from "./ButtonControl";
+
+import { ButtonControl, ButtonControlProps } from "./ButtonControl";
export interface IconButtonProps extends ButtonControlProps {
icon: Icons;
diff --git a/src/components/layout/Backdrop.tsx b/src/components/layout/Backdrop.tsx
index 57719e29..7e022347 100644
--- a/src/components/layout/Backdrop.tsx
+++ b/src/components/layout/Backdrop.tsx
@@ -1,5 +1,6 @@
import React, { createRef, useEffect, useState } from "react";
import { createPortal } from "react-dom";
+
import { useFade } from "@/hooks/useFade";
interface BackdropProps {
diff --git a/src/components/layout/BrandPill.tsx b/src/components/layout/BrandPill.tsx
index 45fe75e2..3c7e1fea 100644
--- a/src/components/layout/BrandPill.tsx
+++ b/src/components/layout/BrandPill.tsx
@@ -1,4 +1,5 @@
import { useTranslation } from "react-i18next";
+
import { Icon, Icons } from "@/components/Icon";
export function BrandPill(props: {
diff --git a/src/components/layout/ErrorBoundary.tsx b/src/components/layout/ErrorBoundary.tsx
index bde7c11d..645a4a9e 100644
--- a/src/components/layout/ErrorBoundary.tsx
+++ b/src/components/layout/ErrorBoundary.tsx
@@ -1,10 +1,11 @@
import { Component } from "react";
+import { Trans, useTranslation } from "react-i18next";
+
import { IconPatch } from "@/components/buttons/IconPatch";
import { Icons } from "@/components/Icon";
import { Link } from "@/components/text/Link";
import { Title } from "@/components/text/Title";
import { conf } from "@/setup/config";
-import { Trans, useTranslation } from "react-i18next";
interface ErrorShowcaseProps {
error: {
diff --git a/src/components/layout/Modal.tsx b/src/components/layout/Modal.tsx
index b3e7a22e..11ed75b7 100644
--- a/src/components/layout/Modal.tsx
+++ b/src/components/layout/Modal.tsx
@@ -1,8 +1,9 @@
-import { Overlay } from "@/components/Overlay";
-import { Transition } from "@/components/Transition";
import { ReactNode } from "react";
import { createPortal } from "react-dom";
+import { Overlay } from "@/components/Overlay";
+import { Transition } from "@/components/Transition";
+
interface Props {
show: boolean;
children?: ReactNode;
diff --git a/src/components/layout/Navigation.tsx b/src/components/layout/Navigation.tsx
index 836c6206..df6d464b 100644
--- a/src/components/layout/Navigation.tsx
+++ b/src/components/layout/Navigation.tsx
@@ -1,10 +1,12 @@
import { ReactNode, useState } from "react";
import { Link } from "react-router-dom";
+
import { IconPatch } from "@/components/buttons/IconPatch";
import { Icons } from "@/components/Icon";
-import { conf } from "@/setup/config";
import { useBannerSize } from "@/hooks/useBanner";
+import { conf } from "@/setup/config";
import SettingsModal from "@/views/SettingsModal";
+
import { BrandPill } from "./BrandPill";
export interface NavigationProps {
diff --git a/src/components/layout/SectionHeading.tsx b/src/components/layout/SectionHeading.tsx
index fda60bae..bd57bb2d 100644
--- a/src/components/layout/SectionHeading.tsx
+++ b/src/components/layout/SectionHeading.tsx
@@ -1,4 +1,5 @@
import { ReactNode } from "react";
+
import { Icon, Icons } from "@/components/Icon";
interface SectionHeadingProps {
diff --git a/src/components/media/MediaCard.tsx b/src/components/media/MediaCard.tsx
index a2e331d4..3769245e 100644
--- a/src/components/media/MediaCard.tsx
+++ b/src/components/media/MediaCard.tsx
@@ -1,10 +1,12 @@
-import { Link } from "react-router-dom";
import { useTranslation } from "react-i18next";
-import { DotList } from "@/components/text/DotList";
-import { MWMediaMeta } from "@/backend/metadata/types";
+import { Link } from "react-router-dom";
+
import { JWMediaToId } from "@/backend/metadata/justwatch";
-import { Icons } from "../Icon";
+import { MWMediaMeta } from "@/backend/metadata/types";
+import { DotList } from "@/components/text/DotList";
+
import { IconPatch } from "../buttons/IconPatch";
+import { Icons } from "../Icon";
export interface MediaCardProps {
media: MWMediaMeta;
diff --git a/src/components/media/WatchedMediaCard.tsx b/src/components/media/WatchedMediaCard.tsx
index 935ceb84..346c77b6 100644
--- a/src/components/media/WatchedMediaCard.tsx
+++ b/src/components/media/WatchedMediaCard.tsx
@@ -1,6 +1,8 @@
+import { useMemo } from "react";
+
import { MWMediaMeta } from "@/backend/metadata/types";
import { useWatchedContext } from "@/state/watched";
-import { useMemo } from "react";
+
import { MediaCard } from "./MediaCard";
export interface WatchedMediaCardProps {
diff --git a/src/components/popout/FloatingCard.tsx b/src/components/popout/FloatingCard.tsx
index 8d894f16..5a837d01 100644
--- a/src/components/popout/FloatingCard.tsx
+++ b/src/components/popout/FloatingCard.tsx
@@ -1,11 +1,14 @@
+import { animated, easings, useSpringValue } from "@react-spring/web";
+import { ReactNode, useCallback, useEffect, useRef, useState } from "react";
+import { useTranslation } from "react-i18next";
+
import { FloatingCardAnchorPosition } from "@/components/popout/positions/FloatingCardAnchorPosition";
import { FloatingCardMobilePosition } from "@/components/popout/positions/FloatingCardMobilePosition";
import { useIsMobile } from "@/hooks/useIsMobile";
import { PopoutSection } from "@/video/components/popouts/PopoutUtils";
-import { useSpringValue, animated, easings } from "@react-spring/web";
-import { ReactNode, useCallback, useEffect, useRef, useState } from "react";
-import { Icon, Icons } from "../Icon";
+
import { FloatingDragHandle, MobilePopoutSpacer } from "./FloatingDragHandle";
+import { Icon, Icons } from "../Icon";
interface FloatingCardProps {
children?: ReactNode;
@@ -133,13 +136,15 @@ export const FloatingCardView = {
action?: React.ReactNode;
backText?: string;
}) {
+ const { t } = useTranslation();
+
let left = (
- {props.backText || "Go back"}
+ {props.backText || t("videoPlayer.popouts.back")}
);
if (props.close)
diff --git a/src/components/popout/FloatingContainer.tsx b/src/components/popout/FloatingContainer.tsx
index 0735d912..73c6f31f 100644
--- a/src/components/popout/FloatingContainer.tsx
+++ b/src/components/popout/FloatingContainer.tsx
@@ -1,4 +1,3 @@
-import { Transition } from "@/components/Transition";
import React, {
ReactNode,
useCallback,
@@ -8,6 +7,8 @@ import React, {
} from "react";
import { createPortal } from "react-dom";
+import { Transition } from "@/components/Transition";
+
interface Props {
children?: ReactNode;
onClose?: () => void;
diff --git a/src/components/popout/FloatingView.tsx b/src/components/popout/FloatingView.tsx
index 4c21f136..d9986318 100644
--- a/src/components/popout/FloatingView.tsx
+++ b/src/components/popout/FloatingView.tsx
@@ -1,6 +1,7 @@
+import { ReactNode } from "react";
+
import { Transition } from "@/components/Transition";
import { useIsMobile } from "@/hooks/useIsMobile";
-import { ReactNode } from "react";
interface Props {
children?: ReactNode;
diff --git a/src/components/popout/positions/FloatingCardAnchorPosition.tsx b/src/components/popout/positions/FloatingCardAnchorPosition.tsx
index 4e022834..b3041b6d 100644
--- a/src/components/popout/positions/FloatingCardAnchorPosition.tsx
+++ b/src/components/popout/positions/FloatingCardAnchorPosition.tsx
@@ -1,6 +1,7 @@
-import { createFloatingAnchorEvent } from "@/components/popout/FloatingAnchor";
import { ReactNode, useCallback, useEffect, useRef, useState } from "react";
+import { createFloatingAnchorEvent } from "@/components/popout/FloatingAnchor";
+
interface AnchorPositionProps {
children?: ReactNode;
id: string;
diff --git a/src/components/popout/positions/FloatingCardMobilePosition.tsx b/src/components/popout/positions/FloatingCardMobilePosition.tsx
index dece3ccc..d4a7f518 100644
--- a/src/components/popout/positions/FloatingCardMobilePosition.tsx
+++ b/src/components/popout/positions/FloatingCardMobilePosition.tsx
@@ -1,4 +1,4 @@
-import { useSpring, animated, config } from "@react-spring/web";
+import { animated, config, useSpring } from "@react-spring/web";
import { useDrag } from "@use-gesture/react";
import { ReactNode, useEffect, useRef, useState } from "react";
diff --git a/src/components/text/ArrowLink.tsx b/src/components/text/ArrowLink.tsx
index 1bcfddcf..ad51319e 100644
--- a/src/components/text/ArrowLink.tsx
+++ b/src/components/text/ArrowLink.tsx
@@ -1,4 +1,5 @@
import { Link as LinkRouter } from "react-router-dom";
+
import { Icon, Icons } from "@/components/Icon";
interface IArrowLinkPropsBase {
diff --git a/src/hooks/useBanner.tsx b/src/hooks/useBanner.tsx
index 9288914e..3781adff 100644
--- a/src/hooks/useBanner.tsx
+++ b/src/hooks/useBanner.tsx
@@ -1,12 +1,12 @@
import {
- ReactNode,
- createContext,
- useState,
- useMemo,
Dispatch,
+ ReactNode,
SetStateAction,
- useEffect,
+ createContext,
useContext,
+ useEffect,
+ useMemo,
+ useState,
} from "react";
import { useMeasure } from "react-use";
diff --git a/src/hooks/useChromecastAvailable.ts b/src/hooks/useChromecastAvailable.ts
index 6190a288..501d6048 100644
--- a/src/hooks/useChromecastAvailable.ts
+++ b/src/hooks/useChromecastAvailable.ts
@@ -1,8 +1,9 @@
///
-import { isChromecastAvailable } from "@/setup/chromecast";
import { useEffect, useRef, useState } from "react";
+import { isChromecastAvailable } from "@/setup/chromecast";
+
export function useChromecastAvailable() {
const [available, setAvailable] = useState(null);
diff --git a/src/hooks/useScrape.ts b/src/hooks/useScrape.ts
index 05f3c387..a375e618 100644
--- a/src/hooks/useScrape.ts
+++ b/src/hooks/useScrape.ts
@@ -1,8 +1,9 @@
+import { useEffect, useState } from "react";
+
import { findBestStream } from "@/backend/helpers/scrape";
import { MWStream } from "@/backend/helpers/streams";
import { DetailedMeta } from "@/backend/metadata/getmeta";
import { MWMediaType } from "@/backend/metadata/types";
-import { useEffect, useState } from "react";
export interface ScrapeEventLog {
type: "provider" | "embed";
diff --git a/src/hooks/useSearchQuery.ts b/src/hooks/useSearchQuery.ts
index 093a99c2..d431a0d0 100644
--- a/src/hooks/useSearchQuery.ts
+++ b/src/hooks/useSearchQuery.ts
@@ -1,7 +1,8 @@
-import { MWMediaType, MWQuery } from "@/backend/metadata/types";
import { useState } from "react";
import { generatePath, useHistory, useRouteMatch } from "react-router-dom";
+import { MWMediaType, MWQuery } from "@/backend/metadata/types";
+
function getInitialValue(params: { type: string; query: string }) {
const type =
Object.values(MWMediaType).find((v) => params.type === v) ||
diff --git a/src/hooks/useVolumeToggle.ts b/src/hooks/useVolumeToggle.ts
index 636b787b..766ae7e6 100644
--- a/src/hooks/useVolumeToggle.ts
+++ b/src/hooks/useVolumeToggle.ts
@@ -1,18 +1,19 @@
+import { useState } from "react";
+
import { useControls } from "@/video/state/logic/controls";
import { useMediaPlaying } from "@/video/state/logic/mediaplaying";
-import { useState } from "react";
export function useVolumeControl(descriptor: string) {
const [storedVolume, setStoredVolume] = useState(1);
const controls = useControls(descriptor);
const mediaPlaying = useMediaPlaying(descriptor);
- const toggleVolume = () => {
+ const toggleVolume = (isKeyboardEvent = false) => {
if (mediaPlaying.volume > 0) {
setStoredVolume(mediaPlaying.volume);
- controls.setVolume(0);
+ controls.setVolume(0, isKeyboardEvent);
} else {
- controls.setVolume(storedVolume > 0 ? storedVolume : 1);
+ controls.setVolume(storedVolume > 0 ? storedVolume : 1, isKeyboardEvent);
}
};
diff --git a/src/index.tsx b/src/index.tsx
index 1ba12d5e..1bf99f70 100644
--- a/src/index.tsx
+++ b/src/index.tsx
@@ -3,12 +3,14 @@ import React, { Suspense } from "react";
import ReactDOM from "react-dom";
import { BrowserRouter, HashRouter } from "react-router-dom";
import type { ReactNode } from "react-router-dom/node_modules/@types/react/index";
-import { ErrorBoundary } from "@/components/layout/ErrorBoundary";
-import { conf } from "@/setup/config";
import { registerSW } from "virtual:pwa-register";
+import { ErrorBoundary } from "@/components/layout/ErrorBoundary";
import App from "@/setup/App";
+import { conf } from "@/setup/config";
+
import "@/setup/ga";
+import "@/setup/sentry";
import "@/setup/i18n";
import "@/setup/index.css";
import "@/backend";
diff --git a/src/setup/App.tsx b/src/setup/App.tsx
index 2d8a99a6..992549e0 100644
--- a/src/setup/App.tsx
+++ b/src/setup/App.tsx
@@ -1,16 +1,16 @@
import { lazy } from "react";
import { Redirect, Route, Switch } from "react-router-dom";
-import { BookmarkContextProvider } from "@/state/bookmark";
-import { WatchedContextProvider } from "@/state/watched";
-import { SettingsProvider } from "@/state/settings";
-import { NotFoundPage } from "@/views/notfound/NotFoundView";
-import { MediaView } from "@/views/media/MediaView";
-import { SearchView } from "@/views/search/SearchView";
import { MWMediaType } from "@/backend/metadata/types";
-import { V2MigrationView } from "@/views/other/v2Migration";
import { BannerContextProvider } from "@/hooks/useBanner";
import { Layout } from "@/setup/Layout";
+import { BookmarkContextProvider } from "@/state/bookmark";
+import { SettingsProvider } from "@/state/settings";
+import { WatchedContextProvider } from "@/state/watched";
+import { MediaView } from "@/views/media/MediaView";
+import { NotFoundPage } from "@/views/notfound/NotFoundView";
+import { V2MigrationView } from "@/views/other/v2Migration";
+import { SearchView } from "@/views/search/SearchView";
function App() {
return (
diff --git a/src/setup/Layout.tsx b/src/setup/Layout.tsx
index 599547f6..23348dfe 100644
--- a/src/setup/Layout.tsx
+++ b/src/setup/Layout.tsx
@@ -1,8 +1,9 @@
+import { ReactNode } from "react";
+import { useTranslation } from "react-i18next";
+
import { Banner } from "@/components/Banner";
import { useBannerSize } from "@/hooks/useBanner";
import { useIsOnline } from "@/hooks/usePing";
-import { ReactNode } from "react";
-import { useTranslation } from "react-i18next";
export function Layout(props: { children: ReactNode }) {
const { t } = useTranslation();
diff --git a/src/setup/config.ts b/src/setup/config.ts
index 8947d43c..f1db01da 100644
--- a/src/setup/config.ts
+++ b/src/setup/config.ts
@@ -1,4 +1,4 @@
-import { APP_VERSION, GITHUB_LINK, DISCORD_LINK } from "./constants";
+import { APP_VERSION, DISCORD_LINK, GITHUB_LINK } from "./constants";
interface Config {
APP_VERSION: string;
diff --git a/src/setup/constants.ts b/src/setup/constants.ts
index 927aecbc..f8a1cc59 100644
--- a/src/setup/constants.ts
+++ b/src/setup/constants.ts
@@ -2,3 +2,5 @@ export const APP_VERSION = import.meta.env.PACKAGE_VERSION;
export const DISCORD_LINK = "https://discord.gg/Jhqt4Xzpfb";
export const GITHUB_LINK = "https://github.com/movie-web/movie-web";
export const GA_ID = "G-44YVXRL61C";
+export const SENTRY_DSN =
+ "https://b267ab7d52674c23af4e4e6cf2956251@o4505053491167232.ingest.sentry.io/4505053495296000";
diff --git a/src/setup/ga.ts b/src/setup/ga.ts
index 219cd3c5..1fbf488b 100644
--- a/src/setup/ga.ts
+++ b/src/setup/ga.ts
@@ -1,4 +1,5 @@
import ReactGA from "react-ga4";
+
import { GA_ID } from "@/setup/constants";
ReactGA.initialize([
diff --git a/src/setup/i18n.ts b/src/setup/i18n.ts
index 3b18e3f5..56ac9b0f 100644
--- a/src/setup/i18n.ts
+++ b/src/setup/i18n.ts
@@ -1,15 +1,27 @@
import i18n from "i18next";
-import { initReactI18next } from "react-i18next";
import LanguageDetector from "i18next-browser-languagedetector";
+import { initReactI18next } from "react-i18next";
// Languages
-import en from "./locales/en/translation.json";
import { captionLanguages } from "./iso6391";
+import en from "./locales/en/translation.json";
+import fr from "./locales/fr/translation.json";
+import nl from "./locales/nl/translation.json";
+import tr from "./locales/tr/translation.json";
const locales = {
en: {
translation: en,
},
+ nl: {
+ translation: nl,
+ },
+ tr: {
+ translation: tr,
+ },
+ fr: {
+ translation: fr,
+ },
};
i18n
// detect user language
diff --git a/src/setup/iso6391.ts b/src/setup/iso6391.ts
index c20580b8..28d42806 100644
--- a/src/setup/iso6391.ts
+++ b/src/setup/iso6391.ts
@@ -217,7 +217,7 @@ export const captionLanguages: CaptionLanguageOption[] = [
id: "none",
englishName: "None",
name: "None",
- nativeName: "No caption language selected",
+ nativeName: "Lorem ipsum",
},
{
id: "aa",
diff --git a/src/setup/locales/en/translation.json b/src/setup/locales/en/translation.json
index f0cf677c..fba37d84 100644
--- a/src/setup/locales/en/translation.json
+++ b/src/setup/locales/en/translation.json
@@ -69,6 +69,7 @@
"playbackSpeed": "Playback speed"
},
"popouts": {
+ "back": "Go back",
"sources": "Sources",
"seasons": "Seasons",
"captions": "Captions",
@@ -108,7 +109,7 @@
},
"settings": {
"title": "Settings",
- "language":"Language",
+ "language": "Language",
"captionLanguage": "Caption Language"
},
"v3": {
diff --git a/src/setup/locales/fr/translation.json b/src/setup/locales/fr/translation.json
index fe9d73eb..44c575bc 100644
--- a/src/setup/locales/fr/translation.json
+++ b/src/setup/locales/fr/translation.json
@@ -16,16 +16,34 @@
"placeholder": "Que voulez-vous voir?"
},
"media": {
- "title": "Impossible de trouver ce média",
- "description": "Nous n'avons pas pu trouver le média que vous avez demandé. Soit il a été supprimé, soit vous avez altéré l'URL."
+ "movie": "Films",
+ "series": "Séries",
+ "stopEditing": "Arrêter l'édition",
+ "errors": {
+ "genericTitle": "Oups, c'est coupé !",
+ "failedMeta": "Impossible de charger les métadonnées",
+ "mediaFailed": "Nous n'avons pas réussi à récupérer le média que vous avez demandé. Veuillez vérifier votre connexion Internet et réessayer.",
+ "videoFailed": "Nous avons rencontré une erreur lors de la lecture de la vidéo que vous avez demandée. Si cela se reproduit, veuillez signaler le problème au serveur <0>Discord0> ou sur <1>GitHub1>."
+ }
},
- "provider": {
- "title": "Ce fournisseur a été désactivé",
- "description": "Nous avons eu des problèmes avec le fournisseur ou bien il était trop instable pour être utilisé, donc nous avons dû le désactiver."
+ "seasons": {
+ "seasonAndEpisode": "S{{saison}} E{{épisode}}"
},
- "page": {
- "title": "Impossible de trouver cette page",
- "description": "Nous avons cherché partout : sous les poubelles, dans le placard, derrière le proxy, mais nous n'avons finalement pas pu trouver la page que vous recherchez."
+ "notFound": {
+ "genericTitle": "Introuvable",
+ "backArrow": "Retour à l'accueil",
+ "media": {
+ "title": "Impossible de trouver ce média",
+ "description": "Nous n'avons pas trouvé le média que vous avez demandé. Soit il a été supprimé, soit vous avez modifié l'URL."
+ },
+ "provider": {
+ "title": "Ce fournisseur a été désactivé",
+ "description": "Nous avons eu des problèmes avec le fournisseur ou il était trop instable pour être utilisé, nous avons donc dû le désactiver."
+ },
+ "page": {
+ "title": "Impossible de trouver cette page",
+ "description": "Nous avons cherché partout : sous les poubelles, dans le placard, derrière le proxy, mais nous n'avons finalement pas trouvé la page que vous cherchez."
+ }
},
"searchBar": {
"movie": "Film",
@@ -51,9 +69,12 @@
"playbackSpeed": "Vitesse"
},
"popouts": {
+ "back": "Retourner",
"sources": "Sources",
"seasons": "Saisons",
"captions": "Sous-titres",
+ "playbackSpeed": "Vitesse de lecture",
+ "customPlaybackSpeed": "Vitesse de lecture personnalisée",
"captionPreferences": {
"title": "Personnaliser",
"delay": "Délai",
@@ -77,13 +98,19 @@
"seasons": "Choisissez la saison que vous voulez regarder",
"episode": "Sélectionnez un épisode",
"captions": "Choisissez une langue de sous-titres",
- "captionPreferences": "Personnalisez l'apparence des sous-titres"
+ "captionPreferences": "Personnalisez l'apparence des sous-titres",
+ "playbackSpeed": "Changer la vitesse de lecture"
}
},
"errors": {
"fatalError": "Le lecteur vidéo a rencontré une erreur fatale, veuillez la signaler au serveur <0>Discord0> ou sur <1>GitHub1>."
}
},
+ "settings": {
+ "title": "Paramètres",
+ "language": "Language",
+ "captionLanguage": "Langue des sous-titres"
+ },
"v3": {
"newSiteTitle": "Nouvelle version disponible!",
"newDomain": "https://movie-web.app",
diff --git a/src/setup/locales/nl/translation.json b/src/setup/locales/nl/translation.json
new file mode 100644
index 00000000..94e796e8
--- /dev/null
+++ b/src/setup/locales/nl/translation.json
@@ -0,0 +1,128 @@
+{
+ "global": {
+ "name": "movie-web"
+ },
+ "search": {
+ "loading_series": "We zoeken je favoriete series...",
+ "loading_movie": "We zoeken je favoriete films...",
+ "loading": "Aan het zoeken...",
+ "allResults": "Dat is het!",
+ "noResults": "We konden helaas niets vinden.",
+ "allFailed": "Het is niet gelukt de media te laden, probeer het nog eens.",
+ "headingTitle": "Zoekresultaten",
+ "bookmarks": "Opgeslagen",
+ "continueWatching": "Kijk verder",
+ "title": "Wat wil je graag kijken?",
+ "placeholder": "Wat wil je graag kijken?"
+ },
+ "media": {
+ "movie": "Film",
+ "series": "Serie",
+ "stopEditing": "Stop met bewerken",
+ "errors": {
+ "genericTitle": "Oeps, hier ging iets mis!",
+ "failedMeta": "Het is niet gelukt de meta-informatie op te halen/",
+ "mediaFailed": "Het is niet gelukt deze media op te halen. Controleer of je een internetverbinding hebt en probeer het nog een keer.",
+ "videoFailed": "Er ging iets mis tijdens het spelen van deze video. Als dit blijft gebeuren, deel het dan in de <0>Discord server0> of maak een <1>GitHub issue1>."
+ }
+ },
+ "seasons": {
+ "seasonAndEpisode": "S{{season}} A{{episode}}"
+ },
+ "notFound": {
+ "genericTitle": "Pagina niet gevonden",
+ "backArrow": "Naar de home-pagina",
+ "media": {
+ "title": "We konden deze media niet vinden.",
+ "description": "We konden dit stukje media niet vinden. Het is mogelijk verwijderd, of jij hebt zelf de URL aangepast."
+ },
+ "provider": {
+ "title": "Deze bron is niet langer beschikbaar",
+ "description": "Deze bron was helaas te instabiel, we hebben hem jammer genoeg uit moeten zetten."
+ },
+ "page": {
+ "title": "Pagina niet gevonden",
+ "description": "We hebben echt alles geprobeerd, zelfs tijdrijzen; echter hebben we deze pagina helaas niet kunnen vinden."
+ }
+ },
+ "searchBar": {
+ "movie": "Films",
+ "series": "Series",
+ "Search": "Zoeken"
+ },
+ "videoPlayer": {
+ "findingBestVideo": "De beste video voor jou aan het zoeken...",
+ "noVideos": "Helaas konden we dat filmpje niet vinden",
+ "loading": "Aan het laden...",
+ "backToHome": "Naar de home-pagina",
+ "backToHomeShort": "Terug",
+ "seasonAndEpisode": "S{{season}} A{{episode}}",
+ "timeLeft": "Nog {{timeLeft}}",
+ "finishAt": "Afgelopen om {{timeFinished}}",
+ "buttons": {
+ "episodes": "Afleveringen",
+ "source": "Bron",
+ "captions": "Ondertiteling",
+ "download": "Download",
+ "settings": "Instellingen",
+ "pictureInPicture": "Beeld-in-beeld",
+ "playbackSpeed": "Afspeelsnelheid"
+ },
+ "popouts": {
+ "back": "Terug",
+ "sources": "Bronnen",
+ "seasons": "Seizoenen",
+ "captions": "Ondertiteling",
+ "playbackSpeed": "Afspeelsnelheid",
+ "customPlaybackSpeed": "Andere snelheden",
+ "captionPreferences": {
+ "title": "Instellingen",
+ "delay": "Vertraging",
+ "fontSize": "Lettergrootte",
+ "opacity": "Doorzichtbaarheid",
+ "color": "Kleur"
+ },
+ "episode": "A{{index}} - {{title}}",
+ "noCaptions": "Geen ondertiteling",
+ "linkedCaptions": "Gelinkte ondertiteling",
+ "customCaption": "Eigen ondertiteling",
+ "uploadCustomCaption": "Ondertiteling uploaden",
+ "noEmbeds": "We hebben geen filmpjes kunnen vinden voor deze bron.",
+
+ "errors": {
+ "loadingWentWong": "Er ging iets mis tijdens het laden van de afleveringen voor {{seasonTitle}}",
+ "embedsError": "Er ging iets mis tijdens het laden van de embeds voor dit dingetje dat je waarschijnlijk leuk vindt"
+ },
+ "descriptions": {
+ "sources": "Welke bron wil je graag gebruiken",
+ "embeds": "Welk filmpje wil je gebruiken?",
+ "seasons": "Welk seizoen wil je kijken?",
+ "episode": "Kies een aflevering",
+ "captions": "Kies een taal voor de ondertiteling",
+ "captionPreferences": "Pas de ondertiteling aan aan je voorkeuren",
+ "playbackSpeed": "Pas de afspeelsnelhijd aan"
+ }
+ },
+ "errors": {
+ "fatalError": "De videospeler is helaas ontploft, rapporteer deze fout op de <0>Discord server0> of op <1>GitHub1>."
+ }
+ },
+ "settings": {
+ "title": "Instellingen",
+ "language": "Taal",
+ "captionLanguage": "Taal voor de Ondertiteling"
+ },
+ "v3": {
+ "newSiteTitle": "De nieuwe versie is uit!",
+ "newDomain": "https://movie-web.app",
+ "newDomainText": "We gaan binnenkort verhuizen naar een nieuw domein: <0>https://movie-web.app0>. Pas je bladwijzers aan naar het nieuwe domein, want het oude domein gaat stoppen met werken op {{date}}.",
+ "tireless": "We hebben mega hard gewerkt aan deze nieuwe versie, dus we hopen dat je er van gaat genieten.",
+ "leaveAnnouncement": "Let's go!"
+ },
+ "casting": {
+ "casting": "Aan het casten..."
+ },
+ "errors": {
+ "offline": "Controleer je internetverbinding"
+ }
+}
diff --git a/src/setup/locales/tr/translation.json b/src/setup/locales/tr/translation.json
new file mode 100644
index 00000000..326cc35d
--- /dev/null
+++ b/src/setup/locales/tr/translation.json
@@ -0,0 +1,128 @@
+{
+ "global": {
+ "name": "movie-web"
+ },
+ "search": {
+ "loading_series": "Favori dizileriniz aranıyor...",
+ "loading_movie": "Favori filmleriniz aranıyor...",
+ "loading": "Yükleniyor...",
+ "allResults": "Bu kadarını bulabildik!",
+ "noResults": "Hiçbir şey bulamadık!",
+ "allFailed": "Medya bulunamadı, tekrar deneyin!",
+ "headingTitle": "Arama sonuçları",
+ "bookmarks": "Yerimleri",
+ "continueWatching": "İzlemeye devam edin",
+ "title": "Ne izlemek istersiniz?",
+ "placeholder": "Ne izlemek istersiniz?"
+ },
+ "media": {
+ "movie": "Film",
+ "series": "Dizi",
+ "stopEditing": "Düzenlemeyi durdur",
+ "errors": {
+ "genericTitle": "Hay aksi, bozuldu!",
+ "failedMeta": "Önbilgi yüklenemedi",
+ "mediaFailed": "İstediğiniz medyaya istek atarken hata oluştu, internet bağlantınızı kontrol edin ve tekrar deneyin.",
+ "videoFailed": "İstediğiniz videoyu oynatırken bir sorunla karşılaştık. Bu durum devam ederse lütfen bunu <0>Discord sunucumuza0> veya <1>GitHub1> üzerinden bildiriniz."
+ }
+ },
+ "seasons": {
+ "seasonAndEpisode": "S{{season}} B{{episode}}"
+ },
+ "notFound": {
+ "genericTitle": "Bulunamadı",
+ "backArrow": "Geri",
+ "media": {
+ "title": "Medya bulunamadı",
+ "description": "İstediğiniz medyayı bulamadık. URL'i yanlış girdiniz ya da medya kaldırıldı."
+ },
+ "provider": {
+ "title": "Bu sağlayıcı devre dışı bırakıldı",
+ "description": "Sağlayıcı ile ilgili bir sorun oluştu ya da kullanılacak kadar stabil değildi bu yüzden devre dışı bırakmak zorunda kaldık."
+ },
+ "page": {
+ "title": "Sayfa bulunamadı",
+ "description": "Her yere baktık: bazanın altına, dolabın içine hatta ara sunucuya ama maalesef aradığınız sayfayı bulamadık."
+ }
+ },
+ "searchBar": {
+ "movie": "Film",
+ "series": "Dizi",
+ "Search": "Ara"
+ },
+ "videoPlayer": {
+ "findingBestVideo": "Sizin için en iyi videoyu buluyoruz...",
+ "noVideos": "Hay aksi, hiçbir video bulamadık",
+ "loading": "Yükleniyor...",
+ "backToHome": "Ana sayfaya dön",
+ "backToHomeShort": "Geri",
+ "seasonAndEpisode": "S{{season}} B{{episode}}",
+ "timeLeft": "{{timeLeft}} kaldı",
+ "finishAt": "{{timeFinished, datetime}}'de/da bitiyor",
+ "buttons": {
+ "episodes": "Bölümler",
+ "source": "Kaynak",
+ "captions": "Altyazılar",
+ "download": "İndir",
+ "settings": "Ayarlar",
+ "pictureInPicture": "Resim içinde Resim",
+ "playbackSpeed": "Oynatma Hızı"
+ },
+ "popouts": {
+ "back": "Geri git",
+ "sources": "Kaynaklar",
+ "seasons": "Sezonlar",
+ "captions": "Altyazılar",
+ "playbackSpeed": "Oynatma hızı",
+ "customPlaybackSpeed": "Özel oynatma hızı",
+ "captionPreferences": {
+ "title": "Kişiselleştirme",
+ "delay": "Gecikme",
+ "fontSize": "Boyut",
+ "opacity": "Opaklık",
+ "color": "Renk"
+ },
+ "episode": "B{{index}} - {{title}}",
+ "noCaptions": "Altyazı yok",
+ "linkedCaptions": "Kaynak Altyazıları",
+ "customCaption": "Özel altyazı",
+ "uploadCustomCaption": "Altyazı yükle",
+ "noEmbeds": "Bu kaynak için gömülü video bulunamadı",
+
+ "errors": {
+ "loadingWentWong": "{{seasonTitle}} için bölümler yüklenirken bir hata oluştu",
+ "embedsError": "İstediğiniz şey için gömülü video bulunurken bir hata oluştu"
+ },
+ "descriptions": {
+ "sources": "Hangi sağlayıcıyı kullanmak istersiniz?",
+ "embeds": "Görüntülemek istediğiniz videoyu seçiniz",
+ "seasons": "İzlemek istediğiniz sezonu seçiniz",
+ "episode": "Bir bölüm seçiniz",
+ "captions": "Altyazı dili seçiniz",
+ "captionPreferences": "Altyazıları istediğiniz gibi ayarlayın",
+ "playbackSpeed": "Oynatma hızınızı değiştirin"
+ }
+ },
+ "errors": {
+ "fatalError": "Video oynatıcıda bir hata oluştu, lütfen bunu <0>Discord sunucumuzda0> ya da <1>GitHub1> üzeriden bildiriniz."
+ }
+ },
+ "settings": {
+ "title": "Ayarlar",
+ "language": "Dil",
+ "captionLanguage": "Altyazı Dili"
+ },
+ "v3": {
+ "newSiteTitle": "Yeni sürüm yayınlandı!",
+ "newDomain": "https://movie-web.app",
+ "newDomainText": "movie-web yakında yeni bir alan adına taşınacak: <0>https://movie-web.app0>. <1>{{date}} tarihinde eski site çalışmayacağı için1> yerimlerinizi güncellemeyi unutmayın.",
+ "tireless": "Bu yeni güncelleme için gece gündüz çalıştık, umarız aylardan beri hazırladığımız bu güncellemeyi beğenirsiniz.",
+ "leaveAnnouncement": "Götür beni!"
+ },
+ "casting": {
+ "casting": "Cihaza aktarılıyor..."
+ },
+ "errors": {
+ "offline": "İnternet bağlantınızı kontrol ediniz"
+ }
+}
diff --git a/src/setup/sentry.tsx b/src/setup/sentry.tsx
new file mode 100644
index 00000000..268b31d7
--- /dev/null
+++ b/src/setup/sentry.tsx
@@ -0,0 +1,16 @@
+import { CaptureConsole, HttpClient } from "@sentry/integrations";
+import * as Sentry from "@sentry/react";
+
+import { conf } from "@/setup/config";
+import { SENTRY_DSN } from "@/setup/constants";
+
+Sentry.init({
+ dsn: SENTRY_DSN,
+ release: `movie-web@${conf().APP_VERSION}`,
+ sampleRate: 0.5,
+ integrations: [
+ new Sentry.BrowserTracing(),
+ new CaptureConsole(),
+ new HttpClient(),
+ ],
+});
diff --git a/src/state/bookmark/context.tsx b/src/state/bookmark/context.tsx
index 6252ceac..9dca821f 100644
--- a/src/state/bookmark/context.tsx
+++ b/src/state/bookmark/context.tsx
@@ -1,6 +1,8 @@
+import { ReactNode, createContext, useContext, useMemo } from "react";
+
import { MWMediaMeta } from "@/backend/metadata/types";
import { useStore } from "@/utils/storage";
-import { createContext, ReactNode, useContext, useMemo } from "react";
+
import { BookmarkStore } from "./store";
import { BookmarkStoreData } from "./types";
diff --git a/src/state/bookmark/store.ts b/src/state/bookmark/store.ts
index 11bec1c5..1b7a2053 100644
--- a/src/state/bookmark/store.ts
+++ b/src/state/bookmark/store.ts
@@ -1,6 +1,7 @@
import { createVersionedStore } from "@/utils/storage";
-import { migrateV1Bookmarks, OldBookmarks } from "../watched/migrations/v2";
+
import { BookmarkStoreData } from "./types";
+import { OldBookmarks, migrateV1Bookmarks } from "../watched/migrations/v2";
export const BookmarkStore = createVersionedStore()
.setKey("mw-bookmarks")
diff --git a/src/state/settings/context.tsx b/src/state/settings/context.tsx
index 8efba361..4810f925 100644
--- a/src/state/settings/context.tsx
+++ b/src/state/settings/context.tsx
@@ -1,6 +1,8 @@
-import { useStore } from "@/utils/storage";
-import { createContext, ReactNode, useContext, useMemo } from "react";
+import { ReactNode, createContext, useContext, useMemo } from "react";
+
import { LangCode } from "@/setup/iso6391";
+import { useStore } from "@/utils/storage";
+
import { SettingsStore } from "./store";
import { MWSettingsData } from "./types";
diff --git a/src/state/settings/store.ts b/src/state/settings/store.ts
index 55ede71e..19d84e15 100644
--- a/src/state/settings/store.ts
+++ b/src/state/settings/store.ts
@@ -1,4 +1,5 @@
import { createVersionedStore } from "@/utils/storage";
+
import { MWSettingsData, MWSettingsDataV1 } from "./types";
export const SettingsStore = createVersionedStore()
diff --git a/src/state/watched/context.tsx b/src/state/watched/context.tsx
index f45baf71..3ce17b2a 100644
--- a/src/state/watched/context.tsx
+++ b/src/state/watched/context.tsx
@@ -1,16 +1,18 @@
-import { DetailedMeta } from "@/backend/metadata/getmeta";
-import { MWMediaType } from "@/backend/metadata/types";
-import { useStore } from "@/utils/storage";
import {
- createContext,
ReactNode,
+ createContext,
useCallback,
useContext,
useMemo,
useRef,
} from "react";
+
+import { DetailedMeta } from "@/backend/metadata/getmeta";
+import { MWMediaType } from "@/backend/metadata/types";
+import { useStore } from "@/utils/storage";
+
import { VideoProgressStore } from "./store";
-import { StoreMediaItem, WatchedStoreItem, WatchedStoreData } from "./types";
+import { StoreMediaItem, WatchedStoreData, WatchedStoreItem } from "./types";
const FIVETEEN_MINUTES = 15 * 60;
const FIVE_MINUTES = 5 * 60;
diff --git a/src/state/watched/migrations/v2.ts b/src/state/watched/migrations/v2.ts
index de3dad44..8f7a56b6 100644
--- a/src/state/watched/migrations/v2.ts
+++ b/src/state/watched/migrations/v2.ts
@@ -2,6 +2,7 @@ import { DetailedMeta, getMetaFromId } from "@/backend/metadata/getmeta";
import { searchForMedia } from "@/backend/metadata/search";
import { MWMediaMeta, MWMediaType } from "@/backend/metadata/types";
import { compareTitle } from "@/utils/titleMatch";
+
import { WatchedStoreData, WatchedStoreItem } from "../types";
interface OldMediaBase {
diff --git a/src/state/watched/store.ts b/src/state/watched/store.ts
index 84eefd67..95adef28 100644
--- a/src/state/watched/store.ts
+++ b/src/state/watched/store.ts
@@ -1,5 +1,6 @@
import { createVersionedStore } from "@/utils/storage";
-import { migrateV2Videos, OldData } from "./migrations/v2";
+
+import { OldData, migrateV2Videos } from "./migrations/v2";
import { WatchedStoreData } from "./types";
export const VideoProgressStore = createVersionedStore()
diff --git a/src/video/components/VideoPlayer.tsx b/src/video/components/VideoPlayer.tsx
index 8e5888a1..6dc8983a 100644
--- a/src/video/components/VideoPlayer.tsx
+++ b/src/video/components/VideoPlayer.tsx
@@ -1,36 +1,38 @@
+import { ReactNode, useCallback, useState } from "react";
+
import { Transition } from "@/components/Transition";
import { useIsMobile } from "@/hooks/useIsMobile";
import { AirplayAction } from "@/video/components/actions/AirplayAction";
import { BackdropAction } from "@/video/components/actions/BackdropAction";
+import { CastingTextAction } from "@/video/components/actions/CastingTextAction";
+import { ChromecastAction } from "@/video/components/actions/ChromecastAction";
import { FullscreenAction } from "@/video/components/actions/FullscreenAction";
import { HeaderAction } from "@/video/components/actions/HeaderAction";
+import { KeyboardShortcutsAction } from "@/video/components/actions/KeyboardShortcutsAction";
import { LoadingAction } from "@/video/components/actions/LoadingAction";
import { MiddlePauseAction } from "@/video/components/actions/MiddlePauseAction";
import { MobileCenterAction } from "@/video/components/actions/MobileCenterAction";
import { PageTitleAction } from "@/video/components/actions/PageTitleAction";
import { PauseAction } from "@/video/components/actions/PauseAction";
+import { PictureInPictureAction } from "@/video/components/actions/PictureInPictureAction";
import { ProgressAction } from "@/video/components/actions/ProgressAction";
import { SeriesSelectionAction } from "@/video/components/actions/SeriesSelectionAction";
import { ShowTitleAction } from "@/video/components/actions/ShowTitleAction";
-import { KeyboardShortcutsAction } from "@/video/components/actions/KeyboardShortcutsAction";
import { SkipTimeAction } from "@/video/components/actions/SkipTimeAction";
import { TimeAction } from "@/video/components/actions/TimeAction";
import { VolumeAction } from "@/video/components/actions/VolumeAction";
import { VideoPlayerError } from "@/video/components/parts/VideoPlayerError";
+import { PopoutProviderAction } from "@/video/components/popouts/PopoutProviderAction";
import {
VideoPlayerBase,
VideoPlayerBaseProps,
} from "@/video/components/VideoPlayerBase";
import { useVideoPlayerDescriptor } from "@/video/state/hooks";
import { useControls } from "@/video/state/logic/controls";
-import { ReactNode, useCallback, useState } from "react";
-import { PopoutProviderAction } from "@/video/components/popouts/PopoutProviderAction";
-import { ChromecastAction } from "@/video/components/actions/ChromecastAction";
-import { CastingTextAction } from "@/video/components/actions/CastingTextAction";
-import { PictureInPictureAction } from "@/video/components/actions/PictureInPictureAction";
+
import { CaptionRendererAction } from "./actions/CaptionRendererAction";
-import { SettingsAction } from "./actions/SettingsAction";
import { DividerAction } from "./actions/DividerAction";
+import { SettingsAction } from "./actions/SettingsAction";
import { VolumeAdjustedAction } from "./actions/VolumeAdjustedAction";
type Props = VideoPlayerBaseProps;
diff --git a/src/video/components/VideoPlayerBase.tsx b/src/video/components/VideoPlayerBase.tsx
index 18f34ab0..62290da2 100644
--- a/src/video/components/VideoPlayerBase.tsx
+++ b/src/video/components/VideoPlayerBase.tsx
@@ -1,15 +1,17 @@
+import { useRef } from "react";
+
import { CastingInternal } from "@/video/components/internal/CastingInternal";
import { WrapperRegisterInternal } from "@/video/components/internal/WrapperRegisterInternal";
import { VideoErrorBoundary } from "@/video/components/parts/VideoErrorBoundary";
import { useInterface } from "@/video/state/logic/interface";
import { useMeta } from "@/video/state/logic/meta";
-import { useRef } from "react";
-import {
- useVideoPlayerDescriptor,
- VideoPlayerContextProvider,
-} from "../state/hooks";
+
import { MetaAction } from "./actions/MetaAction";
import { VideoElementInternal } from "./internal/VideoElementInternal";
+import {
+ VideoPlayerContextProvider,
+ useVideoPlayerDescriptor,
+} from "../state/hooks";
export interface VideoPlayerBaseProps {
children?:
diff --git a/src/video/components/actions/AirplayAction.tsx b/src/video/components/actions/AirplayAction.tsx
index e963166e..58cad17b 100644
--- a/src/video/components/actions/AirplayAction.tsx
+++ b/src/video/components/actions/AirplayAction.tsx
@@ -1,8 +1,10 @@
+import { useCallback } from "react";
+
import { Icons } from "@/components/Icon";
import { useVideoPlayerDescriptor } from "@/video/state/hooks";
import { useControls } from "@/video/state/logic/controls";
import { useMisc } from "@/video/state/logic/misc";
-import { useCallback } from "react";
+
import { VideoPlayerIconButton } from "../parts/VideoPlayerIconButton";
interface Props {
diff --git a/src/video/components/actions/BackdropAction.tsx b/src/video/components/actions/BackdropAction.tsx
index e0a1f9eb..2aa60d38 100644
--- a/src/video/components/actions/BackdropAction.tsx
+++ b/src/video/components/actions/BackdropAction.tsx
@@ -1,8 +1,9 @@
+import React, { useCallback, useEffect, useRef, useState } from "react";
+
import { useVideoPlayerDescriptor } from "@/video/state/hooks";
import { useControls } from "@/video/state/logic/controls";
import { useInterface } from "@/video/state/logic/interface";
import { useMediaPlaying } from "@/video/state/logic/mediaplaying";
-import React, { useCallback, useEffect, useRef, useState } from "react";
interface BackdropActionProps {
children?: React.ReactNode;
diff --git a/src/video/components/actions/CaptionRendererAction.tsx b/src/video/components/actions/CaptionRendererAction.tsx
index 664cffef..5c12b3cb 100644
--- a/src/video/components/actions/CaptionRendererAction.tsx
+++ b/src/video/components/actions/CaptionRendererAction.tsx
@@ -1,9 +1,11 @@
-import { Transition } from "@/components/Transition";
-import { useSettings } from "@/state/settings";
-import { sanitize, parseSubtitles } from "@/backend/helpers/captions";
-import { ContentCaption } from "subsrt-ts/dist/types/handler";
import { useRef } from "react";
import { useAsync } from "react-use";
+import { ContentCaption } from "subsrt-ts/dist/types/handler";
+
+import { parseSubtitles, sanitize } from "@/backend/helpers/captions";
+import { Transition } from "@/components/Transition";
+import { useSettings } from "@/state/settings";
+
import { useVideoPlayerDescriptor } from "../../state/hooks";
import { useProgress } from "../../state/logic/progress";
import { useSource } from "../../state/logic/source";
diff --git a/src/video/components/actions/CastingTextAction.tsx b/src/video/components/actions/CastingTextAction.tsx
index d95a0351..780129b2 100644
--- a/src/video/components/actions/CastingTextAction.tsx
+++ b/src/video/components/actions/CastingTextAction.tsx
@@ -1,7 +1,8 @@
+import { useTranslation } from "react-i18next";
+
import { Icon, Icons } from "@/components/Icon";
import { useVideoPlayerDescriptor } from "@/video/state/hooks";
import { useMisc } from "@/video/state/logic/misc";
-import { useTranslation } from "react-i18next";
export function CastingTextAction() {
const { t } = useTranslation();
diff --git a/src/video/components/actions/ChromecastAction.tsx b/src/video/components/actions/ChromecastAction.tsx
index 7334af67..56106177 100644
--- a/src/video/components/actions/ChromecastAction.tsx
+++ b/src/video/components/actions/ChromecastAction.tsx
@@ -1,8 +1,9 @@
+import { useCallback, useEffect, useRef, useState } from "react";
+
import { Icons } from "@/components/Icon";
import { VideoPlayerIconButton } from "@/video/components/parts/VideoPlayerIconButton";
import { useVideoPlayerDescriptor } from "@/video/state/hooks";
import { useMisc } from "@/video/state/logic/misc";
-import { useCallback, useEffect, useRef, useState } from "react";
interface Props {
className?: string;
diff --git a/src/video/components/actions/DividerAction.tsx b/src/video/components/actions/DividerAction.tsx
index ac1090f7..5778e16f 100644
--- a/src/video/components/actions/DividerAction.tsx
+++ b/src/video/components/actions/DividerAction.tsx
@@ -1,6 +1,6 @@
+import { MWMediaType } from "@/backend/metadata/types";
import { useVideoPlayerDescriptor } from "@/video/state/hooks";
import { useMeta } from "@/video/state/logic/meta";
-import { MWMediaType } from "@/backend/metadata/types";
export function DividerAction() {
const descriptor = useVideoPlayerDescriptor();
diff --git a/src/video/components/actions/FullscreenAction.tsx b/src/video/components/actions/FullscreenAction.tsx
index 8d5a5f3c..feb1a64f 100644
--- a/src/video/components/actions/FullscreenAction.tsx
+++ b/src/video/components/actions/FullscreenAction.tsx
@@ -1,9 +1,11 @@
+import { useCallback } from "react";
+
import { Icons } from "@/components/Icon";
import { canFullscreen } from "@/utils/detectFeatures";
import { useVideoPlayerDescriptor } from "@/video/state/hooks";
import { useControls } from "@/video/state/logic/controls";
import { useInterface } from "@/video/state/logic/interface";
-import { useCallback } from "react";
+
import { VideoPlayerIconButton } from "../parts/VideoPlayerIconButton";
interface Props {
diff --git a/src/video/components/actions/KeyboardShortcutsAction.tsx b/src/video/components/actions/KeyboardShortcutsAction.tsx
index ba5ffc32..9040d8a5 100644
--- a/src/video/components/actions/KeyboardShortcutsAction.tsx
+++ b/src/video/components/actions/KeyboardShortcutsAction.tsx
@@ -1,11 +1,12 @@
import { useEffect, useRef } from "react";
+
+import { useVolumeControl } from "@/hooks/useVolumeToggle";
+import { getPlayerState } from "@/video/state/cache";
import { useVideoPlayerDescriptor } from "@/video/state/hooks";
import { useControls } from "@/video/state/logic/controls";
import { useInterface } from "@/video/state/logic/interface";
-import { getPlayerState } from "@/video/state/cache";
import { useMediaPlaying } from "@/video/state/logic/mediaplaying";
import { useProgress } from "@/video/state/logic/progress";
-import { useVolumeControl } from "@/hooks/useVolumeToggle";
export function KeyboardShortcutsAction() {
const descriptor = useVideoPlayerDescriptor();
@@ -60,7 +61,7 @@ export function KeyboardShortcutsAction() {
// Mute
case "m":
- toggleVolume();
+ toggleVolume(true);
break;
// Decrease volume
diff --git a/src/video/components/actions/MetaAction.tsx b/src/video/components/actions/MetaAction.tsx
index 35b2543d..db5ad6b4 100644
--- a/src/video/components/actions/MetaAction.tsx
+++ b/src/video/components/actions/MetaAction.tsx
@@ -1,9 +1,10 @@
+import { useEffect } from "react";
+
import { MWCaption } from "@/backend/helpers/streams";
import { DetailedMeta } from "@/backend/metadata/getmeta";
import { useVideoPlayerDescriptor } from "@/video/state/hooks";
import { useMeta } from "@/video/state/logic/meta";
import { useProgress } from "@/video/state/logic/progress";
-import { useEffect } from "react";
export type WindowMeta = {
meta: DetailedMeta;
diff --git a/src/video/components/actions/MiddlePauseAction.tsx b/src/video/components/actions/MiddlePauseAction.tsx
index 1ed2fefc..fd5ba4f7 100644
--- a/src/video/components/actions/MiddlePauseAction.tsx
+++ b/src/video/components/actions/MiddlePauseAction.tsx
@@ -1,8 +1,9 @@
+import { useCallback } from "react";
+
import { Icon, Icons } from "@/components/Icon";
import { useVideoPlayerDescriptor } from "@/video/state/hooks";
import { useControls } from "@/video/state/logic/controls";
import { useMediaPlaying } from "@/video/state/logic/mediaplaying";
-import { useCallback } from "react";
export function MiddlePauseAction() {
const descriptor = useVideoPlayerDescriptor();
diff --git a/src/video/components/actions/PageTitleAction.tsx b/src/video/components/actions/PageTitleAction.tsx
index 21a2bf23..b3bf975e 100644
--- a/src/video/components/actions/PageTitleAction.tsx
+++ b/src/video/components/actions/PageTitleAction.tsx
@@ -1,5 +1,7 @@
-import { useVideoPlayerDescriptor } from "@/video/state/hooks";
import { Helmet } from "react-helmet";
+
+import { useVideoPlayerDescriptor } from "@/video/state/hooks";
+
import { useCurrentSeriesEpisodeInfo } from "../hooks/useCurrentSeriesEpisodeInfo";
export function PageTitleAction() {
diff --git a/src/video/components/actions/PauseAction.tsx b/src/video/components/actions/PauseAction.tsx
index d57e4f4d..bfa39b65 100644
--- a/src/video/components/actions/PauseAction.tsx
+++ b/src/video/components/actions/PauseAction.tsx
@@ -1,8 +1,10 @@
+import { useCallback } from "react";
+
import { Icons } from "@/components/Icon";
import { useVideoPlayerDescriptor } from "@/video/state/hooks";
import { useControls } from "@/video/state/logic/controls";
import { useMediaPlaying } from "@/video/state/logic/mediaplaying";
-import { useCallback } from "react";
+
import { VideoPlayerIconButton } from "../parts/VideoPlayerIconButton";
interface Props {
diff --git a/src/video/components/actions/PictureInPictureAction.tsx b/src/video/components/actions/PictureInPictureAction.tsx
index d60ec446..ac1094b7 100644
--- a/src/video/components/actions/PictureInPictureAction.tsx
+++ b/src/video/components/actions/PictureInPictureAction.tsx
@@ -1,13 +1,15 @@
+import { useCallback } from "react";
+import { useTranslation } from "react-i18next";
+
import { Icons } from "@/components/Icon";
import { useIsMobile } from "@/hooks/useIsMobile";
-import { useTranslation } from "react-i18next";
-import { useControls } from "@/video/state/logic/controls";
-import { useVideoPlayerDescriptor } from "@/video/state/hooks";
-import { useCallback } from "react";
import {
canPictureInPicture,
canWebkitPictureInPicture,
} from "@/utils/detectFeatures";
+import { useVideoPlayerDescriptor } from "@/video/state/hooks";
+import { useControls } from "@/video/state/logic/controls";
+
import { VideoPlayerIconButton } from "../parts/VideoPlayerIconButton";
interface Props {
diff --git a/src/video/components/actions/ProgressAction.tsx b/src/video/components/actions/ProgressAction.tsx
index f8d28036..1e4ce3cf 100644
--- a/src/video/components/actions/ProgressAction.tsx
+++ b/src/video/components/actions/ProgressAction.tsx
@@ -1,3 +1,5 @@
+import { useCallback, useEffect, useRef } from "react";
+
import {
makePercentage,
makePercentageString,
@@ -7,7 +9,6 @@ import { getPlayerState } from "@/video/state/cache";
import { useVideoPlayerDescriptor } from "@/video/state/hooks";
import { useControls } from "@/video/state/logic/controls";
import { useProgress } from "@/video/state/logic/progress";
-import { useCallback, useEffect, useRef } from "react";
export function ProgressAction() {
const descriptor = useVideoPlayerDescriptor();
diff --git a/src/video/components/actions/SeriesSelectionAction.tsx b/src/video/components/actions/SeriesSelectionAction.tsx
index 4595eabe..d228b047 100644
--- a/src/video/components/actions/SeriesSelectionAction.tsx
+++ b/src/video/components/actions/SeriesSelectionAction.tsx
@@ -1,12 +1,13 @@
-import { Icons } from "@/components/Icon";
+import { useTranslation } from "react-i18next";
+
import { MWMediaType } from "@/backend/metadata/types";
-import { useVideoPlayerDescriptor } from "@/video/state/hooks";
-import { useMeta } from "@/video/state/logic/meta";
+import { Icons } from "@/components/Icon";
+import { FloatingAnchor } from "@/components/popout/FloatingAnchor";
import { VideoPlayerIconButton } from "@/video/components/parts/VideoPlayerIconButton";
+import { useVideoPlayerDescriptor } from "@/video/state/hooks";
import { useControls } from "@/video/state/logic/controls";
import { useInterface } from "@/video/state/logic/interface";
-import { useTranslation } from "react-i18next";
-import { FloatingAnchor } from "@/components/popout/FloatingAnchor";
+import { useMeta } from "@/video/state/logic/meta";
interface Props {
className?: string;
diff --git a/src/video/components/actions/SettingsAction.tsx b/src/video/components/actions/SettingsAction.tsx
index b012639a..df7b52e4 100644
--- a/src/video/components/actions/SettingsAction.tsx
+++ b/src/video/components/actions/SettingsAction.tsx
@@ -1,11 +1,12 @@
+import { useTranslation } from "react-i18next";
+
import { Icons } from "@/components/Icon";
-import { useVideoPlayerDescriptor } from "@/video/state/hooks";
+import { FloatingAnchor } from "@/components/popout/FloatingAnchor";
+import { useIsMobile } from "@/hooks/useIsMobile";
import { VideoPlayerIconButton } from "@/video/components/parts/VideoPlayerIconButton";
+import { useVideoPlayerDescriptor } from "@/video/state/hooks";
import { useControls } from "@/video/state/logic/controls";
import { useInterface } from "@/video/state/logic/interface";
-import { useIsMobile } from "@/hooks/useIsMobile";
-import { useTranslation } from "react-i18next";
-import { FloatingAnchor } from "@/components/popout/FloatingAnchor";
interface Props {
className?: string;
diff --git a/src/video/components/actions/ShowTitleAction.tsx b/src/video/components/actions/ShowTitleAction.tsx
index a40c3faa..6ab9cac8 100644
--- a/src/video/components/actions/ShowTitleAction.tsx
+++ b/src/video/components/actions/ShowTitleAction.tsx
@@ -1,4 +1,5 @@
import { useVideoPlayerDescriptor } from "@/video/state/hooks";
+
import { useCurrentSeriesEpisodeInfo } from "../hooks/useCurrentSeriesEpisodeInfo";
export function ShowTitleAction() {
diff --git a/src/video/components/actions/SkipTimeAction.tsx b/src/video/components/actions/SkipTimeAction.tsx
index 117d31f0..11a5054d 100644
--- a/src/video/components/actions/SkipTimeAction.tsx
+++ b/src/video/components/actions/SkipTimeAction.tsx
@@ -2,6 +2,7 @@ import { Icons } from "@/components/Icon";
import { useVideoPlayerDescriptor } from "@/video/state/hooks";
import { useControls } from "@/video/state/logic/controls";
import { useProgress } from "@/video/state/logic/progress";
+
import { VideoPlayerIconButton } from "../parts/VideoPlayerIconButton";
interface Props {
diff --git a/src/video/components/actions/TimeAction.tsx b/src/video/components/actions/TimeAction.tsx
index bab9e101..46a27b44 100644
--- a/src/video/components/actions/TimeAction.tsx
+++ b/src/video/components/actions/TimeAction.tsx
@@ -1,11 +1,12 @@
-import { useVideoPlayerDescriptor } from "@/video/state/hooks";
import { useTranslation } from "react-i18next";
+
+import { useIsMobile } from "@/hooks/useIsMobile";
+import { useVideoPlayerDescriptor } from "@/video/state/hooks";
+import { useControls } from "@/video/state/logic/controls";
+import { useInterface } from "@/video/state/logic/interface";
import { useMediaPlaying } from "@/video/state/logic/mediaplaying";
import { useProgress } from "@/video/state/logic/progress";
-import { useInterface } from "@/video/state/logic/interface";
import { VideoPlayerTimeFormat } from "@/video/state/types";
-import { useIsMobile } from "@/hooks/useIsMobile";
-import { useControls } from "@/video/state/logic/controls";
function durationExceedsHour(secs: number): boolean {
return secs > 60 * 60;
diff --git a/src/video/components/actions/VolumeAction.tsx b/src/video/components/actions/VolumeAction.tsx
index c3aee7c1..e6922ab3 100644
--- a/src/video/components/actions/VolumeAction.tsx
+++ b/src/video/components/actions/VolumeAction.tsx
@@ -1,3 +1,5 @@
+import { useCallback, useEffect, useRef, useState } from "react";
+
import { Icon, Icons } from "@/components/Icon";
import {
makePercentage,
@@ -10,7 +12,6 @@ import { useVideoPlayerDescriptor } from "@/video/state/hooks";
import { useControls } from "@/video/state/logic/controls";
import { useInterface } from "@/video/state/logic/interface";
import { useMediaPlaying } from "@/video/state/logic/mediaplaying";
-import { useCallback, useEffect, useRef, useState } from "react";
interface Props {
className?: string;
diff --git a/src/video/components/actions/list-entries/CaptionsSelectionAction.tsx b/src/video/components/actions/list-entries/CaptionsSelectionAction.tsx
index 8dfe2ec3..631eeba0 100644
--- a/src/video/components/actions/list-entries/CaptionsSelectionAction.tsx
+++ b/src/video/components/actions/list-entries/CaptionsSelectionAction.tsx
@@ -1,5 +1,7 @@
-import { Icons } from "@/components/Icon";
import { useTranslation } from "react-i18next";
+
+import { Icons } from "@/components/Icon";
+
import { PopoutListAction } from "../../popouts/PopoutUtils";
interface Props {
diff --git a/src/video/components/actions/list-entries/DownloadAction.tsx b/src/video/components/actions/list-entries/DownloadAction.tsx
index 76910efd..29b5244e 100644
--- a/src/video/components/actions/list-entries/DownloadAction.tsx
+++ b/src/video/components/actions/list-entries/DownloadAction.tsx
@@ -1,10 +1,12 @@
-import { Icons } from "@/components/Icon";
-import { useVideoPlayerDescriptor } from "@/video/state/hooks";
-import { useSource } from "@/video/state/logic/source";
-import { MWStreamType } from "@/backend/helpers/streams";
-import { normalizeTitle } from "@/utils/normalizeTitle";
import { useTranslation } from "react-i18next";
+
+import { MWStreamType } from "@/backend/helpers/streams";
+import { Icons } from "@/components/Icon";
+import { normalizeTitle } from "@/utils/normalizeTitle";
+import { useVideoPlayerDescriptor } from "@/video/state/hooks";
import { useMeta } from "@/video/state/logic/meta";
+import { useSource } from "@/video/state/logic/source";
+
import { PopoutListAction } from "../../popouts/PopoutUtils";
export function DownloadAction() {
diff --git a/src/video/components/actions/list-entries/PlaybackSpeedSelectionAction.tsx b/src/video/components/actions/list-entries/PlaybackSpeedSelectionAction.tsx
index 983f345e..fdc3549f 100644
--- a/src/video/components/actions/list-entries/PlaybackSpeedSelectionAction.tsx
+++ b/src/video/components/actions/list-entries/PlaybackSpeedSelectionAction.tsx
@@ -1,5 +1,7 @@
-import { Icons } from "@/components/Icon";
import { useTranslation } from "react-i18next";
+
+import { Icons } from "@/components/Icon";
+
import { PopoutListAction } from "../../popouts/PopoutUtils";
interface Props {
diff --git a/src/video/components/actions/list-entries/SourceSelectionAction.tsx b/src/video/components/actions/list-entries/SourceSelectionAction.tsx
index 23e20f1c..8e84b020 100644
--- a/src/video/components/actions/list-entries/SourceSelectionAction.tsx
+++ b/src/video/components/actions/list-entries/SourceSelectionAction.tsx
@@ -1,7 +1,9 @@
-import { Icons } from "@/components/Icon";
import { useTranslation } from "react-i18next";
-import { PopoutListAction } from "../../popouts/PopoutUtils";
+
+import { Icons } from "@/components/Icon";
+
import { QualityDisplayAction } from "./QualityDisplayAction";
+import { PopoutListAction } from "../../popouts/PopoutUtils";
interface Props {
onClick?: () => any;
diff --git a/src/video/components/controllers/MetaController.tsx b/src/video/components/controllers/MetaController.tsx
index 9159a0ab..ee6bc696 100644
--- a/src/video/components/controllers/MetaController.tsx
+++ b/src/video/components/controllers/MetaController.tsx
@@ -1,9 +1,10 @@
+import { useEffect } from "react";
+
import { MWCaption } from "@/backend/helpers/streams";
import { MWSeasonWithEpisodeMeta } from "@/backend/metadata/types";
import { useVideoPlayerDescriptor } from "@/video/state/hooks";
import { useControls } from "@/video/state/logic/controls";
import { VideoPlayerMeta } from "@/video/state/types";
-import { useEffect } from "react";
interface MetaControllerProps {
data?: VideoPlayerMeta;
diff --git a/src/video/components/controllers/ProgressListenerController.tsx b/src/video/components/controllers/ProgressListenerController.tsx
index 1a1d2bd1..78e27bc8 100644
--- a/src/video/components/controllers/ProgressListenerController.tsx
+++ b/src/video/components/controllers/ProgressListenerController.tsx
@@ -1,10 +1,11 @@
-import { useEffect, useMemo, useRef } from "react";
import throttle from "lodash.throttle";
+import { useEffect, useMemo, useRef } from "react";
+
import { useVideoPlayerDescriptor } from "@/video/state/hooks";
-import { useMediaPlaying } from "@/video/state/logic/mediaplaying";
-import { useProgress } from "@/video/state/logic/progress";
import { useControls } from "@/video/state/logic/controls";
+import { useMediaPlaying } from "@/video/state/logic/mediaplaying";
import { useMisc } from "@/video/state/logic/misc";
+import { useProgress } from "@/video/state/logic/progress";
interface Props {
startAt?: number;
diff --git a/src/video/components/controllers/SeriesController.tsx b/src/video/components/controllers/SeriesController.tsx
index 481d3ce2..ba01d248 100644
--- a/src/video/components/controllers/SeriesController.tsx
+++ b/src/video/components/controllers/SeriesController.tsx
@@ -1,8 +1,9 @@
-import { useVideoPlayerDescriptor } from "@/video/state/hooks";
-import { useMeta } from "@/video/state/logic/meta";
import { useEffect, useRef } from "react";
import { useHistory } from "react-router-dom";
+import { useVideoPlayerDescriptor } from "@/video/state/hooks";
+import { useMeta } from "@/video/state/logic/meta";
+
interface SeriesControllerProps {
onSelect?: (state: { episodeId?: string; seasonId?: string }) => void;
}
diff --git a/src/video/components/controllers/SourceController.tsx b/src/video/components/controllers/SourceController.tsx
index fe98a685..cf7475b8 100644
--- a/src/video/components/controllers/SourceController.tsx
+++ b/src/video/components/controllers/SourceController.tsx
@@ -1,3 +1,5 @@
+import { useEffect, useRef } from "react";
+
import { getCaptionUrl, makeCaptionId } from "@/backend/helpers/captions";
import {
MWCaption,
@@ -9,7 +11,6 @@ import { useSettings } from "@/state/settings";
import { useInitialized } from "@/video/components/hooks/useInitialized";
import { useVideoPlayerDescriptor } from "@/video/state/hooks";
import { useControls } from "@/video/state/logic/controls";
-import { useEffect, useRef } from "react";
interface SourceControllerProps {
source: string;
diff --git a/src/video/components/hooks/useCurrentSeriesEpisodeInfo.ts b/src/video/components/hooks/useCurrentSeriesEpisodeInfo.ts
index 88ab2e31..6eb51170 100644
--- a/src/video/components/hooks/useCurrentSeriesEpisodeInfo.ts
+++ b/src/video/components/hooks/useCurrentSeriesEpisodeInfo.ts
@@ -1,8 +1,9 @@
-import { MWMediaType } from "@/backend/metadata/types";
-import { useMeta } from "@/video/state/logic/meta";
import { useMemo } from "react";
import { useTranslation } from "react-i18next";
+import { MWMediaType } from "@/backend/metadata/types";
+import { useMeta } from "@/video/state/logic/meta";
+
export function useCurrentSeriesEpisodeInfo(descriptor: string) {
const meta = useMeta(descriptor);
const { t } = useTranslation();
diff --git a/src/video/components/hooks/useInitialized.ts b/src/video/components/hooks/useInitialized.ts
index d46b786c..197b7dc5 100644
--- a/src/video/components/hooks/useInitialized.ts
+++ b/src/video/components/hooks/useInitialized.ts
@@ -1,6 +1,7 @@
-import { useMisc } from "@/video/state/logic/misc";
import { useMemo } from "react";
+import { useMisc } from "@/video/state/logic/misc";
+
export function useInitialized(descriptor: string): { initialized: boolean } {
const misc = useMisc(descriptor);
const initialized = useMemo(() => !!misc.initalized, [misc]);
diff --git a/src/video/components/hooks/useSyncPopouts.ts b/src/video/components/hooks/useSyncPopouts.ts
index a7edee77..2a63b923 100644
--- a/src/video/components/hooks/useSyncPopouts.ts
+++ b/src/video/components/hooks/useSyncPopouts.ts
@@ -1,8 +1,9 @@
-import { ControlMethods, useControls } from "@/video/state/logic/controls";
-import { useInterface } from "@/video/state/logic/interface";
import { useEffect, useRef } from "react";
import { useHistory, useLocation } from "react-router-dom";
+import { ControlMethods, useControls } from "@/video/state/logic/controls";
+import { useInterface } from "@/video/state/logic/interface";
+
function syncRouteToPopout(
location: ReturnType,
controls: ControlMethods
diff --git a/src/video/components/internal/CastingInternal.tsx b/src/video/components/internal/CastingInternal.tsx
index 9de8c1b1..8fc3556d 100644
--- a/src/video/components/internal/CastingInternal.tsx
+++ b/src/video/components/internal/CastingInternal.tsx
@@ -1,10 +1,11 @@
+import { useEffect, useMemo, useRef } from "react";
+
import { useChromecastAvailable } from "@/hooks/useChromecastAvailable";
import { getPlayerState } from "@/video/state/cache";
import { useVideoPlayerDescriptor } from "@/video/state/hooks";
import { updateMisc, useMisc } from "@/video/state/logic/misc";
import { createCastingStateProvider } from "@/video/state/providers/castingStateProvider";
import { setProvider, unsetStateProvider } from "@/video/state/providers/utils";
-import { useEffect, useMemo, useRef } from "react";
export function CastingInternal() {
const descriptor = useVideoPlayerDescriptor();
diff --git a/src/video/components/internal/VideoElementInternal.tsx b/src/video/components/internal/VideoElementInternal.tsx
index dde47b71..8f2e3a92 100644
--- a/src/video/components/internal/VideoElementInternal.tsx
+++ b/src/video/components/internal/VideoElementInternal.tsx
@@ -1,9 +1,10 @@
+import { useEffect, useMemo, useRef } from "react";
+
import { useVideoPlayerDescriptor } from "@/video/state/hooks";
import { useMediaPlaying } from "@/video/state/logic/mediaplaying";
import { useMisc } from "@/video/state/logic/misc";
import { setProvider, unsetStateProvider } from "@/video/state/providers/utils";
import { createVideoStateProvider } from "@/video/state/providers/videoStateProvider";
-import { useEffect, useMemo, useRef } from "react";
interface Props {
autoPlay?: boolean;
diff --git a/src/video/components/internal/WrapperRegisterInternal.tsx b/src/video/components/internal/WrapperRegisterInternal.tsx
index 16457419..3dac4e13 100644
--- a/src/video/components/internal/WrapperRegisterInternal.tsx
+++ b/src/video/components/internal/WrapperRegisterInternal.tsx
@@ -1,7 +1,8 @@
+import { useEffect } from "react";
+
import { getPlayerState } from "@/video/state/cache";
import { useVideoPlayerDescriptor } from "@/video/state/hooks";
import { updateMisc } from "@/video/state/logic/misc";
-import { useEffect } from "react";
export function WrapperRegisterInternal(props: {
wrapper: HTMLDivElement | null;
diff --git a/src/video/components/parts/VideoErrorBoundary.tsx b/src/video/components/parts/VideoErrorBoundary.tsx
index 5c7cf291..8228f057 100644
--- a/src/video/components/parts/VideoErrorBoundary.tsx
+++ b/src/video/components/parts/VideoErrorBoundary.tsx
@@ -1,10 +1,12 @@
+import { Component } from "react";
+import { Trans } from "react-i18next";
+import type { ReactNode } from "react-router-dom/node_modules/@types/react/index";
+
import { MWMediaMeta } from "@/backend/metadata/types";
import { ErrorMessage } from "@/components/layout/ErrorBoundary";
import { Link } from "@/components/text/Link";
import { conf } from "@/setup/config";
-import { Component } from "react";
-import { Trans } from "react-i18next";
-import type { ReactNode } from "react-router-dom/node_modules/@types/react/index";
+
import { VideoPlayerHeader } from "./VideoPlayerHeader";
interface ErrorBoundaryState {
diff --git a/src/video/components/parts/VideoPlayerError.tsx b/src/video/components/parts/VideoPlayerError.tsx
index 6ac7125c..d6fd0306 100644
--- a/src/video/components/parts/VideoPlayerError.tsx
+++ b/src/video/components/parts/VideoPlayerError.tsx
@@ -1,10 +1,12 @@
+import { ReactNode } from "react";
+
import { IconPatch } from "@/components/buttons/IconPatch";
import { Icons } from "@/components/Icon";
import { Title } from "@/components/text/Title";
import { useVideoPlayerDescriptor } from "@/video/state/hooks";
import { useError } from "@/video/state/logic/error";
import { useMeta } from "@/video/state/logic/meta";
-import { ReactNode } from "react";
+
import { VideoPlayerHeader } from "./VideoPlayerHeader";
interface VideoPlayerErrorProps {
diff --git a/src/video/components/parts/VideoPlayerHeader.tsx b/src/video/components/parts/VideoPlayerHeader.tsx
index 950f1a97..8c026c49 100644
--- a/src/video/components/parts/VideoPlayerHeader.tsx
+++ b/src/video/components/parts/VideoPlayerHeader.tsx
@@ -1,16 +1,17 @@
+import { useTranslation } from "react-i18next";
+
import { MWMediaMeta } from "@/backend/metadata/types";
import { IconPatch } from "@/components/buttons/IconPatch";
import { Icon, Icons } from "@/components/Icon";
import { BrandPill } from "@/components/layout/BrandPill";
+import { useBannerSize } from "@/hooks/useBanner";
+import { useIsMobile } from "@/hooks/useIsMobile";
import {
getIfBookmarkedFromPortable,
useBookmarkContext,
} from "@/state/bookmark";
import { AirplayAction } from "@/video/components/actions/AirplayAction";
import { ChromecastAction } from "@/video/components/actions/ChromecastAction";
-import { useTranslation } from "react-i18next";
-import { useIsMobile } from "@/hooks/useIsMobile";
-import { useBannerSize } from "@/hooks/useBanner";
interface VideoPlayerHeaderProps {
media?: MWMediaMeta;
diff --git a/src/video/components/parts/VideoPlayerIconButton.tsx b/src/video/components/parts/VideoPlayerIconButton.tsx
index 156dffd5..a47eeb8d 100644
--- a/src/video/components/parts/VideoPlayerIconButton.tsx
+++ b/src/video/components/parts/VideoPlayerIconButton.tsx
@@ -1,6 +1,7 @@
-import { Icon, Icons } from "@/components/Icon";
import React, { forwardRef } from "react";
+import { Icon, Icons } from "@/components/Icon";
+
export interface VideoPlayerIconButtonProps {
onClick?: (e: React.MouseEvent) => void;
icon: Icons;
diff --git a/src/video/components/parts/VideoPopout.tsx b/src/video/components/parts/VideoPopout.tsx
index 301d0366..cd7aa1f3 100644
--- a/src/video/components/parts/VideoPopout.tsx
+++ b/src/video/components/parts/VideoPopout.tsx
@@ -1,7 +1,8 @@
+import { useEffect, useRef } from "react";
+
import { useVideoPlayerDescriptor } from "@/video/state/hooks";
import { useControls } from "@/video/state/logic/controls";
import { useInterface } from "@/video/state/logic/interface";
-import { useEffect, useRef } from "react";
interface Props {
children?: React.ReactNode;
diff --git a/src/video/components/popouts/CaptionSelectionPopout.tsx b/src/video/components/popouts/CaptionSelectionPopout.tsx
index 3f595757..3b119ef4 100644
--- a/src/video/components/popouts/CaptionSelectionPopout.tsx
+++ b/src/video/components/popouts/CaptionSelectionPopout.tsx
@@ -1,3 +1,6 @@
+import { useMemo, useRef } from "react";
+import { useTranslation } from "react-i18next";
+
import {
customCaption,
getCaptionUrl,
@@ -15,8 +18,7 @@ import { useVideoPlayerDescriptor } from "@/video/state/hooks";
import { useControls } from "@/video/state/logic/controls";
import { useMeta } from "@/video/state/logic/meta";
import { useSource } from "@/video/state/logic/source";
-import { useMemo, useRef } from "react";
-import { useTranslation } from "react-i18next";
+
import { PopoutListEntry, PopoutSection } from "./PopoutUtils";
export function CaptionSelectionPopout(props: {
diff --git a/src/video/components/popouts/CaptionSettingsPopout.tsx b/src/video/components/popouts/CaptionSettingsPopout.tsx
index 3e5a0cd1..a5abe5a6 100644
--- a/src/video/components/popouts/CaptionSettingsPopout.tsx
+++ b/src/video/components/popouts/CaptionSettingsPopout.tsx
@@ -1,13 +1,13 @@
-import { FloatingCardView } from "@/components/popout/FloatingCard";
-import { FloatingView } from "@/components/popout/FloatingView";
-import { useFloatingRouter } from "@/hooks/useFloatingRouter";
-import { useSettings } from "@/state/settings";
import { useTranslation } from "react-i18next";
-import { Slider } from "@/components/Slider";
import CaptionColorSelector, {
colors,
} from "@/components/CaptionColorSelector";
+import { FloatingCardView } from "@/components/popout/FloatingCard";
+import { FloatingView } from "@/components/popout/FloatingView";
+import { Slider } from "@/components/Slider";
+import { useFloatingRouter } from "@/hooks/useFloatingRouter";
+import { useSettings } from "@/state/settings";
export function CaptionSettingsPopout(props: {
router: ReturnType;
diff --git a/src/video/components/popouts/EpisodeSelectionPopout.tsx b/src/video/components/popouts/EpisodeSelectionPopout.tsx
index 02804b84..bd152378 100644
--- a/src/video/components/popouts/EpisodeSelectionPopout.tsx
+++ b/src/video/components/popouts/EpisodeSelectionPopout.tsx
@@ -1,20 +1,22 @@
import { useCallback, useMemo, useState } from "react";
+import { useTranslation } from "react-i18next";
import { useParams } from "react-router-dom";
-import { Icon, Icons } from "@/components/Icon";
-import { useLoading } from "@/hooks/useLoading";
-import { MWMediaType, MWSeasonWithEpisodeMeta } from "@/backend/metadata/types";
+
import { getMetaFromId } from "@/backend/metadata/getmeta";
import { decodeJWId } from "@/backend/metadata/justwatch";
-import { Loading } from "@/components/layout/Loading";
+import { MWMediaType, MWSeasonWithEpisodeMeta } from "@/backend/metadata/types";
import { IconPatch } from "@/components/buttons/IconPatch";
-import { useVideoPlayerDescriptor } from "@/video/state/hooks";
-import { useMeta } from "@/video/state/logic/meta";
-import { useControls } from "@/video/state/logic/controls";
-import { useWatchedContext } from "@/state/watched";
-import { useTranslation } from "react-i18next";
+import { Icon, Icons } from "@/components/Icon";
+import { Loading } from "@/components/layout/Loading";
+import { FloatingCardView } from "@/components/popout/FloatingCard";
import { FloatingView } from "@/components/popout/FloatingView";
import { useFloatingRouter } from "@/hooks/useFloatingRouter";
-import { FloatingCardView } from "@/components/popout/FloatingCard";
+import { useLoading } from "@/hooks/useLoading";
+import { useWatchedContext } from "@/state/watched";
+import { useVideoPlayerDescriptor } from "@/video/state/hooks";
+import { useControls } from "@/video/state/logic/controls";
+import { useMeta } from "@/video/state/logic/meta";
+
import { PopoutListEntry } from "./PopoutUtils";
export function EpisodeSelectionPopout() {
diff --git a/src/video/components/popouts/PlaybackSpeedPopout.tsx b/src/video/components/popouts/PlaybackSpeedPopout.tsx
index 1013647a..87c75980 100644
--- a/src/video/components/popouts/PlaybackSpeedPopout.tsx
+++ b/src/video/components/popouts/PlaybackSpeedPopout.tsx
@@ -1,12 +1,14 @@
+import { useTranslation } from "react-i18next";
+
import { Icon, Icons } from "@/components/Icon";
import { FloatingCardView } from "@/components/popout/FloatingCard";
import { FloatingView } from "@/components/popout/FloatingView";
+import { Slider } from "@/components/Slider";
import { useFloatingRouter } from "@/hooks/useFloatingRouter";
import { useVideoPlayerDescriptor } from "@/video/state/hooks";
import { useControls } from "@/video/state/logic/controls";
-import { useTranslation } from "react-i18next";
import { useMediaPlaying } from "@/video/state/logic/mediaplaying";
-import { Slider } from "@/components/Slider";
+
import { PopoutListEntry, PopoutSection } from "./PopoutUtils";
const speedSelectionOptions = [0.25, 0.5, 0.75, 1, 1.25, 1.5, 2];
diff --git a/src/video/components/popouts/PopoutAnchor.tsx b/src/video/components/popouts/PopoutAnchor.tsx
index 3439f468..c0d4eb9d 100644
--- a/src/video/components/popouts/PopoutAnchor.tsx
+++ b/src/video/components/popouts/PopoutAnchor.tsx
@@ -1,7 +1,8 @@
+import { ReactNode, useEffect, useRef } from "react";
+
import { getPlayerState } from "@/video/state/cache";
import { useVideoPlayerDescriptor } from "@/video/state/hooks";
import { updateInterface } from "@/video/state/logic/interface";
-import { ReactNode, useEffect, useRef } from "react";
interface Props {
for: string;
diff --git a/src/video/components/popouts/PopoutProviderAction.tsx b/src/video/components/popouts/PopoutProviderAction.tsx
index 3c90d46d..5882dc7f 100644
--- a/src/video/components/popouts/PopoutProviderAction.tsx
+++ b/src/video/components/popouts/PopoutProviderAction.tsx
@@ -1,12 +1,13 @@
+import { useCallback } from "react";
+
+import { PopoutFloatingCard } from "@/components/popout/FloatingCard";
+import { FloatingContainer } from "@/components/popout/FloatingContainer";
import { useSyncPopouts } from "@/video/components/hooks/useSyncPopouts";
import { EpisodeSelectionPopout } from "@/video/components/popouts/EpisodeSelectionPopout";
import { SettingsPopout } from "@/video/components/popouts/SettingsPopout";
import { useVideoPlayerDescriptor } from "@/video/state/hooks";
import { useControls } from "@/video/state/logic/controls";
import { useInterface } from "@/video/state/logic/interface";
-import { useCallback } from "react";
-import { PopoutFloatingCard } from "@/components/popout/FloatingCard";
-import { FloatingContainer } from "@/components/popout/FloatingContainer";
import "./Popouts.css";
diff --git a/src/video/components/popouts/PopoutUtils.tsx b/src/video/components/popouts/PopoutUtils.tsx
index a56d1a91..c818d086 100644
--- a/src/video/components/popouts/PopoutUtils.tsx
+++ b/src/video/components/popouts/PopoutUtils.tsx
@@ -1,8 +1,9 @@
-import { Icon, Icons } from "@/components/Icon";
-import { Spinner } from "@/components/layout/Spinner";
-import { ProgressRing } from "@/components/layout/ProgressRing";
import { createRef, useEffect, useRef } from "react";
+import { Icon, Icons } from "@/components/Icon";
+import { ProgressRing } from "@/components/layout/ProgressRing";
+import { Spinner } from "@/components/layout/Spinner";
+
interface PopoutListEntryBaseTypes {
active?: boolean;
children: React.ReactNode;
diff --git a/src/video/components/popouts/SettingsPopout.tsx b/src/video/components/popouts/SettingsPopout.tsx
index 9c92575e..03a45620 100644
--- a/src/video/components/popouts/SettingsPopout.tsx
+++ b/src/video/components/popouts/SettingsPopout.tsx
@@ -2,14 +2,15 @@ import { FloatingCardView } from "@/components/popout/FloatingCard";
import { FloatingDragHandle } from "@/components/popout/FloatingDragHandle";
import { FloatingView } from "@/components/popout/FloatingView";
import { useFloatingRouter } from "@/hooks/useFloatingRouter";
-import { DownloadAction } from "@/video/components/actions/list-entries/DownloadAction";
import { CaptionsSelectionAction } from "@/video/components/actions/list-entries/CaptionsSelectionAction";
-import { SourceSelectionAction } from "@/video/components/actions/list-entries/SourceSelectionAction";
+import { DownloadAction } from "@/video/components/actions/list-entries/DownloadAction";
import { PlaybackSpeedSelectionAction } from "@/video/components/actions/list-entries/PlaybackSpeedSelectionAction";
+import { SourceSelectionAction } from "@/video/components/actions/list-entries/SourceSelectionAction";
+
import { CaptionSelectionPopout } from "./CaptionSelectionPopout";
-import { SourceSelectionPopout } from "./SourceSelectionPopout";
import { CaptionSettingsPopout } from "./CaptionSettingsPopout";
import { PlaybackSpeedPopout } from "./PlaybackSpeedPopout";
+import { SourceSelectionPopout } from "./SourceSelectionPopout";
export function SettingsPopout() {
const floatingRouter = useFloatingRouter();
diff --git a/src/video/components/popouts/SourceSelectionPopout.tsx b/src/video/components/popouts/SourceSelectionPopout.tsx
index 62bec772..21ed0c86 100644
--- a/src/video/components/popouts/SourceSelectionPopout.tsx
+++ b/src/video/components/popouts/SourceSelectionPopout.tsx
@@ -1,24 +1,26 @@
import { useMemo, useRef, useState } from "react";
-import { Icons } from "@/components/Icon";
-import { useLoading } from "@/hooks/useLoading";
-import { Loading } from "@/components/layout/Loading";
-import { IconPatch } from "@/components/buttons/IconPatch";
-import { useVideoPlayerDescriptor } from "@/video/state/hooks";
-import { useMeta } from "@/video/state/logic/meta";
-import { useControls } from "@/video/state/logic/controls";
-import { MWStream } from "@/backend/helpers/streams";
+import { useTranslation } from "react-i18next";
+
+import { MWEmbed, MWEmbedType } from "@/backend/helpers/embed";
+import { MWProviderScrapeResult } from "@/backend/helpers/provider";
import {
getEmbedScraperByType,
getProviders,
} from "@/backend/helpers/register";
import { runEmbedScraper, runProvider } from "@/backend/helpers/run";
-import { MWProviderScrapeResult } from "@/backend/helpers/provider";
-import { useTranslation } from "react-i18next";
-import { MWEmbed, MWEmbedType } from "@/backend/helpers/embed";
+import { MWStream } from "@/backend/helpers/streams";
+import { IconPatch } from "@/components/buttons/IconPatch";
+import { Icons } from "@/components/Icon";
+import { Loading } from "@/components/layout/Loading";
import { FloatingCardView } from "@/components/popout/FloatingCard";
import { FloatingView } from "@/components/popout/FloatingView";
import { useFloatingRouter } from "@/hooks/useFloatingRouter";
+import { useLoading } from "@/hooks/useLoading";
+import { useVideoPlayerDescriptor } from "@/video/state/hooks";
+import { useControls } from "@/video/state/logic/controls";
+import { useMeta } from "@/video/state/logic/meta";
import { useSource } from "@/video/state/logic/source";
+
import { PopoutListEntry } from "./PopoutUtils";
interface EmbedEntryProps {
diff --git a/src/video/state/hooks.tsx b/src/video/state/hooks.tsx
index c320d81b..6c532bf7 100644
--- a/src/video/state/hooks.tsx
+++ b/src/video/state/hooks.tsx
@@ -1,10 +1,11 @@
import {
- createContext,
ReactNode,
+ createContext,
useContext,
useEffect,
useState,
} from "react";
+
import { registerVideoPlayer, unregisterVideoPlayer } from "./init";
const VideoPlayerContext = createContext("");
diff --git a/src/video/state/init.ts b/src/video/state/init.ts
index 559c4ca4..cf8f0137 100644
--- a/src/video/state/init.ts
+++ b/src/video/state/init.ts
@@ -1,4 +1,5 @@
import { nanoid } from "nanoid";
+
import { _players } from "./cache";
import { VideoPlayerState } from "./types";
diff --git a/src/video/state/logic/controls.ts b/src/video/state/logic/controls.ts
index e87a5255..0824b601 100644
--- a/src/video/state/logic/controls.ts
+++ b/src/video/state/logic/controls.ts
@@ -2,6 +2,7 @@ import { updateInterface } from "@/video/state/logic/interface";
import { updateMeta } from "@/video/state/logic/meta";
import { updateProgress } from "@/video/state/logic/progress";
import { VideoPlayerMeta, VideoPlayerTimeFormat } from "@/video/state/types";
+
import { getPlayerState } from "../cache";
import { VideoPlayerStateController } from "../providers/providerTypes";
diff --git a/src/video/state/logic/error.ts b/src/video/state/logic/error.ts
index d3958337..0407a65a 100644
--- a/src/video/state/logic/error.ts
+++ b/src/video/state/logic/error.ts
@@ -1,4 +1,5 @@
import { useEffect, useState } from "react";
+
import { getPlayerState } from "../cache";
import { listenEvent, sendEvent, unlistenEvent } from "../events";
import { VideoPlayerState } from "../types";
diff --git a/src/video/state/logic/interface.ts b/src/video/state/logic/interface.ts
index 35ab51c6..2b064944 100644
--- a/src/video/state/logic/interface.ts
+++ b/src/video/state/logic/interface.ts
@@ -1,4 +1,5 @@
import { useEffect, useState } from "react";
+
import { getPlayerState } from "../cache";
import { listenEvent, sendEvent, unlistenEvent } from "../events";
import { VideoPlayerState, VideoPlayerTimeFormat } from "../types";
diff --git a/src/video/state/logic/mediaplaying.ts b/src/video/state/logic/mediaplaying.ts
index ff631064..a681a0dc 100644
--- a/src/video/state/logic/mediaplaying.ts
+++ b/src/video/state/logic/mediaplaying.ts
@@ -1,4 +1,5 @@
import { useEffect, useState } from "react";
+
import { getPlayerState } from "../cache";
import { listenEvent, sendEvent, unlistenEvent } from "../events";
import { VideoPlayerState } from "../types";
diff --git a/src/video/state/logic/meta.ts b/src/video/state/logic/meta.ts
index 35e6ad79..95907774 100644
--- a/src/video/state/logic/meta.ts
+++ b/src/video/state/logic/meta.ts
@@ -1,4 +1,5 @@
import { useEffect, useState } from "react";
+
import { getPlayerState } from "../cache";
import { listenEvent, sendEvent, unlistenEvent } from "../events";
import { VideoPlayerMeta, VideoPlayerState } from "../types";
diff --git a/src/video/state/logic/misc.ts b/src/video/state/logic/misc.ts
index a30a146a..01088621 100644
--- a/src/video/state/logic/misc.ts
+++ b/src/video/state/logic/misc.ts
@@ -1,4 +1,5 @@
import { useEffect, useState } from "react";
+
import { getPlayerState } from "../cache";
import { listenEvent, sendEvent, unlistenEvent } from "../events";
import { VideoPlayerState } from "../types";
diff --git a/src/video/state/logic/progress.ts b/src/video/state/logic/progress.ts
index b2482c0e..051450e5 100644
--- a/src/video/state/logic/progress.ts
+++ b/src/video/state/logic/progress.ts
@@ -1,4 +1,5 @@
import { useEffect, useState } from "react";
+
import { getPlayerState } from "../cache";
import { listenEvent, sendEvent, unlistenEvent } from "../events";
import { VideoPlayerState } from "../types";
diff --git a/src/video/state/logic/source.ts b/src/video/state/logic/source.ts
index 6f2d0afb..5fafb60c 100644
--- a/src/video/state/logic/source.ts
+++ b/src/video/state/logic/source.ts
@@ -1,5 +1,7 @@
-import { MWStreamQuality, MWStreamType } from "@/backend/helpers/streams";
import { useEffect, useState } from "react";
+
+import { MWStreamQuality, MWStreamType } from "@/backend/helpers/streams";
+
import { getPlayerState } from "../cache";
import { listenEvent, sendEvent, unlistenEvent } from "../events";
import { VideoPlayerState } from "../types";
diff --git a/src/video/state/providers/castingStateProvider.ts b/src/video/state/providers/castingStateProvider.ts
index faf34dc5..e791c2f9 100644
--- a/src/video/state/providers/castingStateProvider.ts
+++ b/src/video/state/providers/castingStateProvider.ts
@@ -1,21 +1,23 @@
import fscreen from "fscreen";
+
+import { revokeCaptionBlob } from "@/backend/helpers/captions";
import {
canChangeVolume,
canFullscreen,
canFullscreenAnyElement,
canWebkitFullscreen,
} from "@/utils/detectFeatures";
-import { updateSource } from "@/video/state/logic/source";
import {
getStoredVolume,
setStoredVolume,
} from "@/video/components/hooks/volumeStore";
-import { resetStateForSource } from "@/video/state/providers/helpers";
import { updateInterface } from "@/video/state/logic/interface";
-import { revokeCaptionBlob } from "@/backend/helpers/captions";
+import { updateSource } from "@/video/state/logic/source";
+import { resetStateForSource } from "@/video/state/providers/helpers";
+
+import { VideoPlayerStateProvider } from "./providerTypes";
import { getPlayerState } from "../cache";
import { updateMediaPlaying } from "../logic/mediaplaying";
-import { VideoPlayerStateProvider } from "./providerTypes";
import { updateProgress } from "../logic/progress";
// TODO HLS for casting?
diff --git a/src/video/state/providers/utils.ts b/src/video/state/providers/utils.ts
index 9d5d47e5..f07e7c50 100644
--- a/src/video/state/providers/utils.ts
+++ b/src/video/state/providers/utils.ts
@@ -1,6 +1,7 @@
import { updateMisc } from "@/video/state/logic/misc";
-import { getPlayerState } from "../cache";
+
import { VideoPlayerStateProvider } from "./providerTypes";
+import { getPlayerState } from "../cache";
export function setProvider(
descriptor: string,
diff --git a/src/video/state/providers/videoStateProvider.ts b/src/video/state/providers/videoStateProvider.ts
index e527419b..2f8c5beb 100644
--- a/src/video/state/providers/videoStateProvider.ts
+++ b/src/video/state/providers/videoStateProvider.ts
@@ -1,29 +1,31 @@
-import Hls from "hls.js";
import fscreen from "fscreen";
+import Hls from "hls.js";
+
+import { revokeCaptionBlob } from "@/backend/helpers/captions";
+import { MWStreamType } from "@/backend/helpers/streams";
import {
canChangeVolume,
canFullscreen,
canFullscreenAnyElement,
- canWebkitFullscreen,
canPictureInPicture,
+ canWebkitFullscreen,
canWebkitPictureInPicture,
} from "@/utils/detectFeatures";
-import { MWStreamType } from "@/backend/helpers/streams";
-import { updateInterface } from "@/video/state/logic/interface";
-import { updateSource } from "@/video/state/logic/source";
import {
getStoredVolume,
setStoredVolume,
} from "@/video/components/hooks/volumeStore";
import { updateError } from "@/video/state/logic/error";
+import { updateInterface } from "@/video/state/logic/interface";
import { updateMisc } from "@/video/state/logic/misc";
+import { updateSource } from "@/video/state/logic/source";
import { resetStateForSource } from "@/video/state/providers/helpers";
-import { revokeCaptionBlob } from "@/backend/helpers/captions";
+
+import { VideoPlayerStateProvider } from "./providerTypes";
+import { handleBuffered } from "./utils";
import { getPlayerState } from "../cache";
import { updateMediaPlaying } from "../logic/mediaplaying";
-import { VideoPlayerStateProvider } from "./providerTypes";
import { updateProgress } from "../logic/progress";
-import { handleBuffered } from "./utils";
function errorMessage(err: MediaError) {
switch (err.code) {
diff --git a/src/video/state/types.ts b/src/video/state/types.ts
index 5782d7c1..9a6f3987 100644
--- a/src/video/state/types.ts
+++ b/src/video/state/types.ts
@@ -1,10 +1,12 @@
+import Hls from "hls.js";
+
import {
MWCaption,
MWStreamQuality,
MWStreamType,
} from "@/backend/helpers/streams";
import { DetailedMeta } from "@/backend/metadata/getmeta";
-import Hls from "hls.js";
+
import { VideoPlayerStateProvider } from "./providers/providerTypes";
export type VideoPlayerMeta = {
diff --git a/src/views/SettingsModal.tsx b/src/views/SettingsModal.tsx
index b3ba74ed..47de7888 100644
--- a/src/views/SettingsModal.tsx
+++ b/src/views/SettingsModal.tsx
@@ -1,21 +1,22 @@
+import { useMemo } from "react";
+import { useTranslation } from "react-i18next";
+
+import CaptionColorSelector, {
+ colors,
+} from "@/components/CaptionColorSelector";
import { Dropdown } from "@/components/Dropdown";
import { Icon, Icons } from "@/components/Icon";
import { Modal, ModalCard } from "@/components/layout/Modal";
-import { useSettings } from "@/state/settings";
-import { useTranslation } from "react-i18next";
-import { CaptionCue } from "@/video/components/actions/CaptionRendererAction";
+import { Slider } from "@/components/Slider";
+import { conf } from "@/setup/config";
+import { appLanguageOptions } from "@/setup/i18n";
import {
CaptionLanguageOption,
LangCode,
captionLanguages,
} from "@/setup/iso6391";
-import { useMemo } from "react";
-import { appLanguageOptions } from "@/setup/i18n";
-import CaptionColorSelector, {
- colors,
-} from "@/components/CaptionColorSelector";
-import { Slider } from "@/components/Slider";
-import { conf } from "@/setup/config";
+import { useSettings } from "@/state/settings";
+import { CaptionCue } from "@/video/components/actions/CaptionRendererAction";
export default function SettingsModal(props: {
onClose: () => void;
diff --git a/src/views/developer/EmbedTesterView.tsx b/src/views/developer/EmbedTesterView.tsx
index 9b659bb7..15315ea4 100644
--- a/src/views/developer/EmbedTesterView.tsx
+++ b/src/views/developer/EmbedTesterView.tsx
@@ -1,3 +1,5 @@
+import { ReactNode, useCallback, useEffect, useMemo, useState } from "react";
+
import { MWEmbed, MWEmbedScraper, MWEmbedType } from "@/backend/helpers/embed";
import { getEmbeds } from "@/backend/helpers/register";
import { runEmbedScraper } from "@/backend/helpers/run";
@@ -7,7 +9,6 @@ import { Navigation } from "@/components/layout/Navigation";
import { ArrowLink } from "@/components/text/ArrowLink";
import { Title } from "@/components/text/Title";
import { useLoading } from "@/hooks/useLoading";
-import { ReactNode, useCallback, useEffect, useMemo, useState } from "react";
interface MediaSelectorProps {
embedType: MWEmbedType;
diff --git a/src/views/developer/ProviderTesterView.tsx b/src/views/developer/ProviderTesterView.tsx
index 0862b2b8..45f2297b 100644
--- a/src/views/developer/ProviderTesterView.tsx
+++ b/src/views/developer/ProviderTesterView.tsx
@@ -1,3 +1,6 @@
+import { ReactNode, useEffect, useState } from "react";
+
+import { testData } from "@/__tests__/providers/testdata";
import { MWProviderScrapeResult } from "@/backend/helpers/provider";
import { getProviders } from "@/backend/helpers/register";
import { runProvider } from "@/backend/helpers/run";
@@ -6,8 +9,6 @@ import { Navigation } from "@/components/layout/Navigation";
import { ArrowLink } from "@/components/text/ArrowLink";
import { Title } from "@/components/text/Title";
import { useLoading } from "@/hooks/useLoading";
-import { testData } from "@/__tests__/providers/testdata";
-import { ReactNode, useEffect, useState } from "react";
interface MediaSelectorProps {
onSelect: (meta: DetailedMeta) => void;
diff --git a/src/views/developer/VideoTesterView.tsx b/src/views/developer/VideoTesterView.tsx
index 2291470e..4c3cb7e5 100644
--- a/src/views/developer/VideoTesterView.tsx
+++ b/src/views/developer/VideoTesterView.tsx
@@ -1,3 +1,6 @@
+import { useCallback, useState } from "react";
+import { Helmet } from "react-helmet";
+
import { MWStreamQuality, MWStreamType } from "@/backend/helpers/streams";
import { DetailedMeta } from "@/backend/metadata/getmeta";
import { MWMediaType } from "@/backend/metadata/types";
@@ -8,8 +11,6 @@ import { ThinContainer } from "@/components/layout/ThinContainer";
import { MetaController } from "@/video/components/controllers/MetaController";
import { SourceController } from "@/video/components/controllers/SourceController";
import { VideoPlayer } from "@/video/components/VideoPlayer";
-import { useCallback, useState } from "react";
-import { Helmet } from "react-helmet";
interface VideoData {
streamUrl: string;
diff --git a/src/views/media/MediaErrorView.tsx b/src/views/media/MediaErrorView.tsx
index 6e611f33..e79d3521 100644
--- a/src/views/media/MediaErrorView.tsx
+++ b/src/views/media/MediaErrorView.tsx
@@ -1,8 +1,9 @@
+import { Helmet } from "react-helmet";
+import { useTranslation } from "react-i18next";
+
import { ErrorMessage } from "@/components/layout/ErrorBoundary";
import { useGoBack } from "@/hooks/useGoBack";
import { VideoPlayerHeader } from "@/video/components/parts/VideoPlayerHeader";
-import { Helmet } from "react-helmet";
-import { useTranslation } from "react-i18next";
export function MediaFetchErrorView() {
const { t } = useTranslation();
diff --git a/src/views/media/MediaView.tsx b/src/views/media/MediaView.tsx
index c2ee94e7..132161dd 100644
--- a/src/views/media/MediaView.tsx
+++ b/src/views/media/MediaView.tsx
@@ -1,25 +1,27 @@
-import { useHistory, useParams } from "react-router-dom";
-import { Helmet } from "react-helmet";
import { useEffect, useRef, useState } from "react";
+import { Helmet } from "react-helmet";
+import { useTranslation } from "react-i18next";
+import { useHistory, useParams } from "react-router-dom";
+
import { MWStream } from "@/backend/helpers/streams";
-import { SelectedMediaData, useScrape } from "@/hooks/useScrape";
import { DetailedMeta, getMetaFromId } from "@/backend/metadata/getmeta";
import { decodeJWId } from "@/backend/metadata/justwatch";
-import { Loading } from "@/components/layout/Loading";
-import { useLoading } from "@/hooks/useLoading";
import { MWMediaType, MWSeasonWithEpisodeMeta } from "@/backend/metadata/types";
-import { useGoBack } from "@/hooks/useGoBack";
import { IconPatch } from "@/components/buttons/IconPatch";
-import { VideoPlayer } from "@/video/components/VideoPlayer";
-import { MetaController } from "@/video/components/controllers/MetaController";
-import { SourceController } from "@/video/components/controllers/SourceController";
import { Icons } from "@/components/Icon";
-import { VideoPlayerHeader } from "@/video/components/parts/VideoPlayerHeader";
-import { ProgressListenerController } from "@/video/components/controllers/ProgressListenerController";
-import { VideoPlayerMeta } from "@/video/state/types";
-import { SeriesController } from "@/video/components/controllers/SeriesController";
+import { Loading } from "@/components/layout/Loading";
+import { useGoBack } from "@/hooks/useGoBack";
+import { useLoading } from "@/hooks/useLoading";
+import { SelectedMediaData, useScrape } from "@/hooks/useScrape";
import { useWatchedItem } from "@/state/watched";
-import { useTranslation } from "react-i18next";
+import { MetaController } from "@/video/components/controllers/MetaController";
+import { ProgressListenerController } from "@/video/components/controllers/ProgressListenerController";
+import { SeriesController } from "@/video/components/controllers/SeriesController";
+import { SourceController } from "@/video/components/controllers/SourceController";
+import { VideoPlayerHeader } from "@/video/components/parts/VideoPlayerHeader";
+import { VideoPlayer } from "@/video/components/VideoPlayer";
+import { VideoPlayerMeta } from "@/video/state/types";
+
import { MediaFetchErrorView } from "./MediaErrorView";
import { MediaScrapeLog } from "./MediaScrapeLog";
import { NotFoundMedia, NotFoundWrapper } from "../notfound/NotFoundView";
diff --git a/src/views/notfound/NotFoundView.tsx b/src/views/notfound/NotFoundView.tsx
index 3d9e7032..b7dfccf1 100644
--- a/src/views/notfound/NotFoundView.tsx
+++ b/src/views/notfound/NotFoundView.tsx
@@ -1,12 +1,13 @@
import { ReactNode } from "react";
+import { Helmet } from "react-helmet";
import { useTranslation } from "react-i18next";
+
import { IconPatch } from "@/components/buttons/IconPatch";
import { Icons } from "@/components/Icon";
import { Navigation } from "@/components/layout/Navigation";
import { ArrowLink } from "@/components/text/ArrowLink";
import { Title } from "@/components/text/Title";
import { useGoBack } from "@/hooks/useGoBack";
-import { Helmet } from "react-helmet";
import { VideoPlayerHeader } from "@/video/components/parts/VideoPlayerHeader";
export function NotFoundWrapper(props: {
diff --git a/src/views/other/v2Migration.tsx b/src/views/other/v2Migration.tsx
index 0f1e0424..1334ae26 100644
--- a/src/views/other/v2Migration.tsx
+++ b/src/views/other/v2Migration.tsx
@@ -1,5 +1,6 @@
-import { useEffect, useState } from "react";
import pako from "pako";
+import { useEffect, useState } from "react";
+
import { MWMediaType } from "@/backend/metadata/types";
import { conf } from "@/setup/config";
diff --git a/src/views/search/HomeView.tsx b/src/views/search/HomeView.tsx
index 952d1ec2..bfb64695 100644
--- a/src/views/search/HomeView.tsx
+++ b/src/views/search/HomeView.tsx
@@ -1,19 +1,21 @@
+import { useAutoAnimate } from "@formkit/auto-animate/react";
+import { useCallback, useEffect, useMemo, useState } from "react";
import { Trans, useTranslation } from "react-i18next";
+import { useHistory } from "react-router-dom";
+
+import { Button } from "@/components/Button";
+import { EditButton } from "@/components/buttons/EditButton";
import { Icons } from "@/components/Icon";
+import { Modal, ModalCard } from "@/components/layout/Modal";
import { SectionHeading } from "@/components/layout/SectionHeading";
import { MediaGrid } from "@/components/media/MediaGrid";
+import { WatchedMediaCard } from "@/components/media/WatchedMediaCard";
import {
getIfBookmarkedFromPortable,
useBookmarkContext,
} from "@/state/bookmark";
import { useWatchedContext } from "@/state/watched";
-import { WatchedMediaCard } from "@/components/media/WatchedMediaCard";
-import { EditButton } from "@/components/buttons/EditButton";
-import { useCallback, useEffect, useMemo, useState } from "react";
-import { useAutoAnimate } from "@formkit/auto-animate/react";
-import { useHistory } from "react-router-dom";
-import { Modal, ModalCard } from "@/components/layout/Modal";
-import { Button } from "@/components/Button";
+
import { EmbedMigration } from "../other/v2Migration";
function Bookmarks() {
diff --git a/src/views/search/SearchLoadingView.tsx b/src/views/search/SearchLoadingView.tsx
index 307ed428..4c59d677 100644
--- a/src/views/search/SearchLoadingView.tsx
+++ b/src/views/search/SearchLoadingView.tsx
@@ -1,4 +1,5 @@
import { useTranslation } from "react-i18next";
+
import { Loading } from "@/components/layout/Loading";
import { useSearchQuery } from "@/hooks/useSearchQuery";
diff --git a/src/views/search/SearchResultsPartial.tsx b/src/views/search/SearchResultsPartial.tsx
index 63250193..5769338b 100644
--- a/src/views/search/SearchResultsPartial.tsx
+++ b/src/views/search/SearchResultsPartial.tsx
@@ -1,6 +1,8 @@
import { useEffect, useMemo, useState } from "react";
-import { useDebounce } from "@/hooks/useDebounce";
+
import { MWQuery } from "@/backend/metadata/types";
+import { useDebounce } from "@/hooks/useDebounce";
+
import { HomeView } from "./HomeView";
import { SearchLoadingView } from "./SearchLoadingView";
import { SearchResultsView } from "./SearchResultsView";
diff --git a/src/views/search/SearchResultsView.tsx b/src/views/search/SearchResultsView.tsx
index 0726ae5f..25d347c9 100644
--- a/src/views/search/SearchResultsView.tsx
+++ b/src/views/search/SearchResultsView.tsx
@@ -1,13 +1,15 @@
import { useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
+
+import { searchForMedia } from "@/backend/metadata/search";
+import { MWMediaMeta, MWQuery } from "@/backend/metadata/types";
import { IconPatch } from "@/components/buttons/IconPatch";
import { Icons } from "@/components/Icon";
import { SectionHeading } from "@/components/layout/SectionHeading";
import { MediaGrid } from "@/components/media/MediaGrid";
import { WatchedMediaCard } from "@/components/media/WatchedMediaCard";
import { useLoading } from "@/hooks/useLoading";
-import { searchForMedia } from "@/backend/metadata/search";
-import { MWMediaMeta, MWQuery } from "@/backend/metadata/types";
+
import { SearchLoadingView } from "./SearchLoadingView";
function SearchSuffix(props: { failed?: boolean; results?: number }) {
diff --git a/src/views/search/SearchView.tsx b/src/views/search/SearchView.tsx
index 9dfdee64..fce4ce08 100644
--- a/src/views/search/SearchView.tsx
+++ b/src/views/search/SearchView.tsx
@@ -1,14 +1,16 @@
import { useCallback, useState } from "react";
-import Sticky from "react-stickynode";
+import { Helmet } from "react-helmet";
import { useTranslation } from "react-i18next";
+import Sticky from "react-stickynode";
+
import { Navigation } from "@/components/layout/Navigation";
import { ThinContainer } from "@/components/layout/ThinContainer";
+import { WideContainer } from "@/components/layout/WideContainer";
import { SearchBarInput } from "@/components/SearchBar";
import { Title } from "@/components/text/Title";
-import { useSearchQuery } from "@/hooks/useSearchQuery";
-import { WideContainer } from "@/components/layout/WideContainer";
import { useBannerSize } from "@/hooks/useBanner";
-import { Helmet } from "react-helmet";
+import { useSearchQuery } from "@/hooks/useSearchQuery";
+
import { SearchResultsPartial } from "./SearchResultsPartial";
export function SearchView() {
diff --git a/yarn.lock b/yarn.lock
index 48e68bc8..867bedb7 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1301,6 +1301,80 @@
estree-walker "^2.0.2"
picomatch "^2.3.1"
+"@sentry-internal/tracing@7.49.0":
+ version "7.49.0"
+ resolved "https://registry.yarnpkg.com/@sentry-internal/tracing/-/tracing-7.49.0.tgz#f589de565370884b9a13f82c98463de9b2d25dcd"
+ integrity sha512-ESh3+ZneQk/3HESTUmIPNrW5GVPu/HrRJU+eAJJto74vm+6vP7zDn2YV2gJ1w18O/37nc7W/bVCgZJlhZ3cwew==
+ dependencies:
+ "@sentry/core" "7.49.0"
+ "@sentry/types" "7.49.0"
+ "@sentry/utils" "7.49.0"
+ tslib "^1.9.3"
+
+"@sentry/browser@7.49.0":
+ version "7.49.0"
+ resolved "https://registry.yarnpkg.com/@sentry/browser/-/browser-7.49.0.tgz#5ce1cdb8d883c129d9a4e313c08a54c5ada4661b"
+ integrity sha512-x2DekKkQoY7/dhBzE4J25mdQ978NtPBTVQb+uZqlF/t5mp4K44TAszmPqy8lC/CmVHkp7qcpRGSCIzeboUL4KA==
+ dependencies:
+ "@sentry-internal/tracing" "7.49.0"
+ "@sentry/core" "7.49.0"
+ "@sentry/replay" "7.49.0"
+ "@sentry/types" "7.49.0"
+ "@sentry/utils" "7.49.0"
+ tslib "^1.9.3"
+
+"@sentry/core@7.49.0":
+ version "7.49.0"
+ resolved "https://registry.yarnpkg.com/@sentry/core/-/core-7.49.0.tgz#340d059f5efeff1a3359fef66d0c8e34e79ac992"
+ integrity sha512-AlSnCYgfEbvK8pkNluUkmdW/cD9UpvOVCa+ERQswXNRkAv5aDGCL6Ihv6fnIajE++BYuwZh0+HwZUBVKTFzoZg==
+ dependencies:
+ "@sentry/types" "7.49.0"
+ "@sentry/utils" "7.49.0"
+ tslib "^1.9.3"
+
+"@sentry/integrations@^7.49.0":
+ version "7.49.0"
+ resolved "https://registry.yarnpkg.com/@sentry/integrations/-/integrations-7.49.0.tgz#e123f687e0abe10d3428027e3879ce231503fc2f"
+ integrity sha512-qsEVkcZjw+toFGnzsVo+Cozz+hMK9LugzkfJyOFL+CyiEx9MfkEmsvRpZe1ETEWKe/VZylYU27NQzl6UNuAUjw==
+ dependencies:
+ "@sentry/types" "7.49.0"
+ "@sentry/utils" "7.49.0"
+ localforage "^1.8.1"
+ tslib "^1.9.3"
+
+"@sentry/react@^7.49.0":
+ version "7.49.0"
+ resolved "https://registry.yarnpkg.com/@sentry/react/-/react-7.49.0.tgz#9a31808d4232d3010019e09d7c706b3d4fe54960"
+ integrity sha512-s+ROJr1tP9zVBmoOn94JM+fu2TuoJKxkSXTEUOKoQ9P6P5ROzpDqTzHRGk6u4OjZTy5tftRyEqBGM2Iaf9Y+UA==
+ dependencies:
+ "@sentry/browser" "7.49.0"
+ "@sentry/types" "7.49.0"
+ "@sentry/utils" "7.49.0"
+ hoist-non-react-statics "^3.3.2"
+ tslib "^1.9.3"
+
+"@sentry/replay@7.49.0":
+ version "7.49.0"
+ resolved "https://registry.yarnpkg.com/@sentry/replay/-/replay-7.49.0.tgz#c7f16bc3ca0c5911f641738f8894eb596c5da00d"
+ integrity sha512-UY3bHoBDPOu4Dpq3m3oxNjLrq09NiFVYUfrTN4QOq1Am2SA04XbuCj/YZ+jNVy/NrFtoz9cTovK6oQbNw53jog==
+ dependencies:
+ "@sentry/core" "7.49.0"
+ "@sentry/types" "7.49.0"
+ "@sentry/utils" "7.49.0"
+
+"@sentry/types@7.49.0":
+ version "7.49.0"
+ resolved "https://registry.yarnpkg.com/@sentry/types/-/types-7.49.0.tgz#2c217091e13dc373682f5be2e9b5baed9d2ae695"
+ integrity sha512-9yXXh7iv76+O6h2ONUVx0wsL1auqJFWez62mTjWk4350SgMmWp/zUkBxnVXhmcYqscz/CepC+Loz9vITLXtgxg==
+
+"@sentry/utils@7.49.0":
+ version "7.49.0"
+ resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-7.49.0.tgz#b1b3a2af52067dd27e660c7c3062a31cdf4b94f9"
+ integrity sha512-JdC9yGnOgev4ISJVwmIoFsk8Zx0psDZJAj2DV7x4wMZsO6QK+YjC7G3mUED/S5D5lsrkBZ/3uvQQhr8DQI4UcQ==
+ dependencies:
+ "@sentry/types" "7.49.0"
+ tslib "^1.9.3"
+
"@surma/rollup-plugin-off-main-thread@^2.2.3":
version "2.2.3"
resolved "https://registry.yarnpkg.com/@surma/rollup-plugin-off-main-thread/-/rollup-plugin-off-main-thread-2.2.3.tgz#ee34985952ca21558ab0d952f00298ad2190c053"
@@ -2599,7 +2673,7 @@ eslint-module-utils@^2.7.4:
dependencies:
debug "^3.2.7"
-eslint-plugin-import@^2.25.4:
+eslint-plugin-import@^2.27.5:
version "2.27.5"
resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.27.5.tgz#876a6d03f52608a3e5bb439c2550588e51dd6c65"
integrity sha512-LmEt3GVofgiGuiE+ORpnvP+kAm3h6MLZJ4Q5HCyHADofsb4VzXFsRiWj3c0OFiV+3DWFh0qg3v9gcPlfc3zRow==
@@ -3143,7 +3217,7 @@ hls.js@^1.0.7:
resolved "https://registry.yarnpkg.com/hls.js/-/hls.js-1.3.5.tgz#0e8b0799ecf2feb7ba199f5e95f35ba9552e04f4"
integrity sha512-uybAvKS6uDe0MnWNEPnO0krWVr+8m2R0hJ/viql8H3MVK+itq8gGQuIYoFHL3rECkIpNH98Lw8YuuWMKZxp3Ew==
-hoist-non-react-statics@^3.1.0:
+hoist-non-react-statics@^3.1.0, hoist-non-react-statics@^3.3.2:
version "3.3.2"
resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45"
integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==
@@ -3217,6 +3291,11 @@ ignore@^5.2.0:
resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.4.tgz#a291c0c6178ff1b960befe47fcdec301674a6324"
integrity sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==
+immediate@~3.0.5:
+ version "3.0.6"
+ resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.0.6.tgz#9db1dbd0faf8de6fbe0f5dd5e56bb606280de69b"
+ integrity sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==
+
import-fresh@^3.0.0, import-fresh@^3.2.1:
version "3.3.0"
resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b"
@@ -3642,6 +3721,13 @@ levn@~0.3.0:
prelude-ls "~1.1.2"
type-check "~0.3.2"
+lie@3.1.1:
+ version "3.1.1"
+ resolved "https://registry.yarnpkg.com/lie/-/lie-3.1.1.tgz#9a436b2cc7746ca59de7a41fa469b3efb76bd87e"
+ integrity sha512-RiNhHysUjhrDQntfYSfY4MU24coXXdEOgw9WGcKHNeEwffDYbF//u87M1EWaMGzuFoSbqW0C9C6lEEhDOAswfw==
+ dependencies:
+ immediate "~3.0.5"
+
lilconfig@^2.0.5, lilconfig@^2.0.6:
version "2.1.0"
resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-2.1.0.tgz#78e23ac89ebb7e1bfbf25b18043de756548e7f52"
@@ -3652,6 +3738,13 @@ local-pkg@^0.4.2:
resolved "https://registry.yarnpkg.com/local-pkg/-/local-pkg-0.4.3.tgz#0ff361ab3ae7f1c19113d9bb97b98b905dbc4963"
integrity sha512-SFppqq5p42fe2qcZQqqEOiVRXl+WCP1MdT6k7BDEW1j++sp5fIY+/fdRQitvKgB5BrBcmrs5m/L0v2FrU5MY1g==
+localforage@^1.8.1:
+ version "1.10.0"
+ resolved "https://registry.yarnpkg.com/localforage/-/localforage-1.10.0.tgz#5c465dc5f62b2807c3a84c0c6a1b1b3212781dd4"
+ integrity sha512-14/H1aX7hzBBmmh7sGPd+AOMkkIrHM3Z1PAyGgZigA1H1p5O5ANnMyWzvpAETtG68/dC4pC0ncy3+PPGzXZHPg==
+ dependencies:
+ lie "3.1.1"
+
locate-path@^6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286"
@@ -5021,7 +5114,7 @@ tsconfig-paths@^3.14.1:
minimist "^1.2.6"
strip-bom "^3.0.0"
-tslib@^1.8.1:
+tslib@^1.8.1, tslib@^1.9.3:
version "1.14.1"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00"
integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==