From 5ee4f013ff9776790e3a7f4e7a76708e45ba966a Mon Sep 17 00:00:00 2001
From: mrjvs
Date: Fri, 21 Apr 2023 20:49:47 +0200
Subject: [PATCH 01/14] Sentry integration
Co-authored-by: Jip Frijlink
---
package.json | 2 +
src/index.tsx | 1 +
src/setup/constants.ts | 2 +
src/setup/sentry.tsx | 15 +++++++
yarn.lock | 97 +++++++++++++++++++++++++++++++++++++++++-
5 files changed, 115 insertions(+), 2 deletions(-)
create mode 100644 src/setup/sentry.tsx
diff --git a/package.json b/package.json
index 2223761f..31c7f631 100644
--- a/package.json
+++ b/package.json
@@ -7,6 +7,8 @@
"@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",
diff --git a/src/index.tsx b/src/index.tsx
index 1ba12d5e..d47668e7 100644
--- a/src/index.tsx
+++ b/src/index.tsx
@@ -9,6 +9,7 @@ import { registerSW } from "virtual:pwa-register";
import App from "@/setup/App";
import "@/setup/ga";
+import "@/setup/sentry";
import "@/setup/i18n";
import "@/setup/index.css";
import "@/backend";
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/sentry.tsx b/src/setup/sentry.tsx
new file mode 100644
index 00000000..417a0d4c
--- /dev/null
+++ b/src/setup/sentry.tsx
@@ -0,0 +1,15 @@
+import * as Sentry from "@sentry/react";
+import { CaptureConsole, HttpClient } from "@sentry/integrations";
+import { SENTRY_DSN } from "@/setup/constants";
+import { conf } from "@/setup/config";
+
+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/yarn.lock b/yarn.lock
index 48e68bc8..25213135 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"
@@ -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==
From 8b8cbc8cc902b52daaceb37d267ada09c72a094d Mon Sep 17 00:00:00 2001
From: mrjvs
Date: Fri, 21 Apr 2023 21:08:01 +0200
Subject: [PATCH 02/14] Dutch language translations
Co-authored-by: Jip Frijlink
---
src/components/popout/FloatingCard.tsx | 5 +-
src/setup/i18n.ts | 4 +
src/setup/iso6391.ts | 2 +-
src/setup/locales/en/translation.json | 3 +-
src/setup/locales/nl/translation.json | 128 +++++++++++++++++++++++++
5 files changed, 139 insertions(+), 3 deletions(-)
create mode 100644 src/setup/locales/nl/translation.json
diff --git a/src/components/popout/FloatingCard.tsx b/src/components/popout/FloatingCard.tsx
index 8d894f16..c132c99c 100644
--- a/src/components/popout/FloatingCard.tsx
+++ b/src/components/popout/FloatingCard.tsx
@@ -1,3 +1,4 @@
+import { useTranslation } from "react-i18next";
import { FloatingCardAnchorPosition } from "@/components/popout/positions/FloatingCardAnchorPosition";
import { FloatingCardMobilePosition } from "@/components/popout/positions/FloatingCardMobilePosition";
import { useIsMobile } from "@/hooks/useIsMobile";
@@ -133,13 +134,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/setup/i18n.ts b/src/setup/i18n.ts
index 3b18e3f5..4e9869d3 100644
--- a/src/setup/i18n.ts
+++ b/src/setup/i18n.ts
@@ -4,12 +4,16 @@ import LanguageDetector from "i18next-browser-languagedetector";
// Languages
import en from "./locales/en/translation.json";
+import nl from "./locales/nl/translation.json";
import { captionLanguages } from "./iso6391";
const locales = {
en: {
translation: en,
},
+ nl: {
+ translation: nl,
+ },
};
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/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"
+ }
+}
From a34d245e2b3452039fbf19e1826fe820630cc811 Mon Sep 17 00:00:00 2001
From: mrjvs
Date: Fri, 21 Apr 2023 21:09:56 +0200
Subject: [PATCH 03/14] version bump
---
package.json | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/package.json b/package.json
index 31c7f631..1e769dbd 100644
--- a/package.json
+++ b/package.json
@@ -1,8 +1,8 @@
{
"name": "movie-web",
- "version": "3.0.11",
+ "version": "3.0.12",
"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",
From a1e3d98538f98290215ec99682c0b35885813c0c Mon Sep 17 00:00:00 2001
From: JdM
Date: Sat, 22 Apr 2023 13:32:34 +0200
Subject: [PATCH 04/14] Add French in the settings languages selector
---
src/setup/i18n.ts | 5 +++
src/setup/locales/fr/translation.json | 45 +++++++++++++++++++++------
2 files changed, 41 insertions(+), 9 deletions(-)
diff --git a/src/setup/i18n.ts b/src/setup/i18n.ts
index 4e9869d3..80b15622 100644
--- a/src/setup/i18n.ts
+++ b/src/setup/i18n.ts
@@ -5,6 +5,8 @@ import LanguageDetector from "i18next-browser-languagedetector";
// Languages
import en from "./locales/en/translation.json";
import nl from "./locales/nl/translation.json";
+import fr from "./locales/fr/translation.json";
+
import { captionLanguages } from "./iso6391";
const locales = {
@@ -14,6 +16,9 @@ const locales = {
nl: {
translation: nl,
},
+ fr: {
+ translation: fr,
+ },
};
i18n
// detect user language
diff --git a/src/setup/locales/fr/translation.json b/src/setup/locales/fr/translation.json
index fe9d73eb..d68c26d1 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": "Not found",
+ "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": "Eevenir en arrière",
"sources": "Sources",
"seasons": "Saisons",
"captions": "Sous-titres",
+ "playbackSpeed": "Vitesse de lecture",
+ "customPlaybackSpeed": "Custom playback speed",
"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",
From 3a7b05264dfcede220d9a8d9ca434ba5bca9d6d9 Mon Sep 17 00:00:00 2001
From: JdM
Date: Sat, 22 Apr 2023 16:49:15 +0200
Subject: [PATCH 05/14] Update src/setup/locales/fr/translation.json
Co-authored-by: BrightDV <92821484+BrightDV@users.noreply.github.com>
---
src/setup/locales/fr/translation.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/setup/locales/fr/translation.json b/src/setup/locales/fr/translation.json
index d68c26d1..ac3ecf34 100644
--- a/src/setup/locales/fr/translation.json
+++ b/src/setup/locales/fr/translation.json
@@ -69,7 +69,7 @@
"playbackSpeed": "Vitesse"
},
"popouts": {
- "back": "Eevenir en arrière",
+ "back": "Revenir en arrière",
"sources": "Sources",
"seasons": "Saisons",
"captions": "Sous-titres",
From 7b641c61cd18413419eb7bda010b5f8a3e3165cd Mon Sep 17 00:00:00 2001
From: JdM
Date: Sat, 22 Apr 2023 16:49:24 +0200
Subject: [PATCH 06/14] Update src/setup/locales/fr/translation.json
Co-authored-by: BrightDV <92821484+BrightDV@users.noreply.github.com>
---
src/setup/locales/fr/translation.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/setup/locales/fr/translation.json b/src/setup/locales/fr/translation.json
index ac3ecf34..5ff00b50 100644
--- a/src/setup/locales/fr/translation.json
+++ b/src/setup/locales/fr/translation.json
@@ -74,7 +74,7 @@
"seasons": "Saisons",
"captions": "Sous-titres",
"playbackSpeed": "Vitesse de lecture",
- "customPlaybackSpeed": "Custom playback speed",
+ "customPlaybackSpeed": "Vitesse de lecture personnalisée",
"captionPreferences": {
"title": "Personnaliser",
"delay": "Délai",
From 2a4bc7349c49c15ffe6989e80a7a394df44db6fa Mon Sep 17 00:00:00 2001
From: JdM
Date: Sat, 22 Apr 2023 16:49:34 +0200
Subject: [PATCH 07/14] Update src/setup/locales/fr/translation.json
Co-authored-by: BrightDV <92821484+BrightDV@users.noreply.github.com>
---
src/setup/locales/fr/translation.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/setup/locales/fr/translation.json b/src/setup/locales/fr/translation.json
index 5ff00b50..477851c6 100644
--- a/src/setup/locales/fr/translation.json
+++ b/src/setup/locales/fr/translation.json
@@ -30,7 +30,7 @@
"seasonAndEpisode": "S{{saison}} E{{épisode}}"
},
"notFound": {
- "genericTitle": "Not found",
+ "genericTitle": "Introuvable",
"backArrow": "Retour à l'accueil",
"media": {
"title": "Impossible de trouver ce média",
From d54921900bdddfbe150800e1c0584d155bea1b00 Mon Sep 17 00:00:00 2001
From: mrjvs
Date: Sun, 23 Apr 2023 14:11:07 +0200
Subject: [PATCH 08/14] Update src/setup/locales/fr/translation.json
Co-authored-by: James Hawkins
---
src/setup/locales/fr/translation.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/setup/locales/fr/translation.json b/src/setup/locales/fr/translation.json
index 477851c6..44c575bc 100644
--- a/src/setup/locales/fr/translation.json
+++ b/src/setup/locales/fr/translation.json
@@ -69,7 +69,7 @@
"playbackSpeed": "Vitesse"
},
"popouts": {
- "back": "Revenir en arrière",
+ "back": "Retourner",
"sources": "Sources",
"seasons": "Saisons",
"captions": "Sous-titres",
From cccc84624a08b8896ae812808eb7509e8450e9b0 Mon Sep 17 00:00:00 2001
From: James Hawkins
Date: Sun, 23 Apr 2023 13:11:50 +0100
Subject: [PATCH 09/14] Update README.md
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
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.
From 9b62b55fbbe18c01eb221c0742401004cd57569e Mon Sep 17 00:00:00 2001
From: frost768
Date: Sun, 23 Apr 2023 20:03:01 +0300
Subject: [PATCH 10/14] Turkish translation
---
src/setup/i18n.ts | 4 +
src/setup/locales/tr/translation.json | 128 ++++++++++++++++++++++++++
2 files changed, 132 insertions(+)
create mode 100644 src/setup/locales/tr/translation.json
diff --git a/src/setup/i18n.ts b/src/setup/i18n.ts
index 4e9869d3..a90c3d7c 100644
--- a/src/setup/i18n.ts
+++ b/src/setup/i18n.ts
@@ -5,6 +5,7 @@ import LanguageDetector from "i18next-browser-languagedetector";
// Languages
import en from "./locales/en/translation.json";
import nl from "./locales/nl/translation.json";
+import tr from "./locales/tr/translation.json";
import { captionLanguages } from "./iso6391";
const locales = {
@@ -14,6 +15,9 @@ const locales = {
nl: {
translation: nl,
},
+ tr: {
+ translation: tr,
+ },
};
i18n
// detect user language
diff --git a/src/setup/locales/tr/translation.json b/src/setup/locales/tr/translation.json
new file mode 100644
index 00000000..1a6bc549
--- /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, relativetime}} 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"
+ }
+}
From 433d618096070272a3ebbd76edfd4da00207a4ce Mon Sep 17 00:00:00 2001
From: frost768
Date: Sun, 23 Apr 2023 20:09:50 +0300
Subject: [PATCH 11/14] remove relativeTime formatting
---
src/setup/locales/tr/translation.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/setup/locales/tr/translation.json b/src/setup/locales/tr/translation.json
index 1a6bc549..326cc35d 100644
--- a/src/setup/locales/tr/translation.json
+++ b/src/setup/locales/tr/translation.json
@@ -57,7 +57,7 @@
"backToHome": "Ana sayfaya dön",
"backToHomeShort": "Geri",
"seasonAndEpisode": "S{{season}} B{{episode}}",
- "timeLeft": "{{timeLeft, relativetime}} kaldı",
+ "timeLeft": "{{timeLeft}} kaldı",
"finishAt": "{{timeFinished, datetime}}'de/da bitiyor",
"buttons": {
"episodes": "Bölümler",
From a718abdcdda8bb4e8fae5a4594d525115d003922 Mon Sep 17 00:00:00 2001
From: Jip Fr
Date: Mon, 24 Apr 2023 00:00:53 +0200
Subject: [PATCH 12/14] feat(player): add soundbar visibility thingie for M
keyboard shortcut
---
src/hooks/useVolumeToggle.ts | 6 +++---
src/video/components/actions/KeyboardShortcutsAction.tsx | 2 +-
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/src/hooks/useVolumeToggle.ts b/src/hooks/useVolumeToggle.ts
index 636b787b..c6b8388f 100644
--- a/src/hooks/useVolumeToggle.ts
+++ b/src/hooks/useVolumeToggle.ts
@@ -7,12 +7,12 @@ export function useVolumeControl(descriptor: string) {
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/video/components/actions/KeyboardShortcutsAction.tsx b/src/video/components/actions/KeyboardShortcutsAction.tsx
index ba5ffc32..c87b6f68 100644
--- a/src/video/components/actions/KeyboardShortcutsAction.tsx
+++ b/src/video/components/actions/KeyboardShortcutsAction.tsx
@@ -60,7 +60,7 @@ export function KeyboardShortcutsAction() {
// Mute
case "m":
- toggleVolume();
+ toggleVolume(true);
break;
// Decrease volume
From 99e47f16ea174d18ae0e3185303ebf1be5912829 Mon Sep 17 00:00:00 2001
From: Jip Frijlink
Date: Mon, 24 Apr 2023 00:11:37 +0200
Subject: [PATCH 13/14] Bump version
---
package.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/package.json b/package.json
index 1e769dbd..817408a2 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "movie-web",
- "version": "3.0.12",
+ "version": "3.0.13",
"private": true,
"homepage": "https://movie-web.app",
"dependencies": {
From 4880d46dc41ca9bdbd632709c0cf22ef1e7005ba Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Y=C4=B1lmaz=20=C3=87ABUK?=
Date: Mon, 24 Apr 2023 18:41:54 +0300
Subject: [PATCH 14/14] style: sort imports according to ESLint rules
This commit updates the import statements in the codebase to comply with ESLint rules for import ordering. All imports have been sorted alphabetically and grouped according to the specified import groups in the ESLint configuration. This improves the codebase's consistency and maintainability.
---
.eslintrc.js | 53 ++++++++++++++-----
package.json | 2 +-
src/__tests__/providers/providers.test.ts | 7 +--
src/backend/embeds/streamm4u.ts | 4 +-
src/backend/helpers/captions.ts | 7 +--
src/backend/helpers/fetch.ts | 3 +-
src/backend/helpers/provider.ts | 4 +-
src/backend/metadata/getmeta.ts | 5 +-
src/backend/metadata/search.ts | 5 +-
src/backend/providers/flixhq.ts | 1 +
src/backend/providers/gdriveplayer.ts | 5 +-
src/backend/providers/m4ufree.ts | 1 +
src/backend/providers/superstream/index.ts | 8 +--
src/components/Button.tsx | 3 +-
src/components/CaptionColorSelector.tsx | 1 +
src/components/Dropdown.tsx | 2 +-
src/components/Overlay.tsx | 3 +-
src/components/SearchBar.tsx | 4 +-
src/components/Transition.tsx | 2 +-
src/components/buttons/DropdownButton.tsx | 5 +-
src/components/buttons/EditButton.tsx | 4 +-
src/components/buttons/IconButton.tsx | 3 +-
src/components/layout/Backdrop.tsx | 1 +
src/components/layout/BrandPill.tsx | 1 +
src/components/layout/ErrorBoundary.tsx | 3 +-
src/components/layout/Modal.tsx | 5 +-
src/components/layout/Navigation.tsx | 4 +-
src/components/layout/SectionHeading.tsx | 1 +
src/components/media/MediaCard.tsx | 10 ++--
src/components/media/WatchedMediaCard.tsx | 4 +-
src/components/popout/FloatingCard.tsx | 8 +--
src/components/popout/FloatingContainer.tsx | 3 +-
src/components/popout/FloatingView.tsx | 3 +-
.../positions/FloatingCardAnchorPosition.tsx | 3 +-
.../positions/FloatingCardMobilePosition.tsx | 2 +-
src/components/text/ArrowLink.tsx | 1 +
src/hooks/useBanner.tsx | 10 ++--
src/hooks/useChromecastAvailable.ts | 3 +-
src/hooks/useScrape.ts | 3 +-
src/hooks/useSearchQuery.ts | 3 +-
src/hooks/useVolumeToggle.ts | 3 +-
src/index.tsx | 5 +-
src/setup/App.tsx | 14 ++---
src/setup/Layout.tsx | 5 +-
src/setup/config.ts | 2 +-
src/setup/ga.ts | 1 +
src/setup/i18n.ts | 7 ++-
src/setup/sentry.tsx | 5 +-
src/state/bookmark/context.tsx | 4 +-
src/state/bookmark/store.ts | 3 +-
src/state/settings/context.tsx | 6 ++-
src/state/settings/store.ts | 1 +
src/state/watched/context.tsx | 12 +++--
src/state/watched/migrations/v2.ts | 1 +
src/state/watched/store.ts | 3 +-
src/video/components/VideoPlayer.tsx | 16 +++---
src/video/components/VideoPlayerBase.tsx | 12 +++--
.../components/actions/AirplayAction.tsx | 4 +-
.../components/actions/BackdropAction.tsx | 3 +-
.../actions/CaptionRendererAction.tsx | 10 ++--
.../components/actions/CastingTextAction.tsx | 3 +-
.../components/actions/ChromecastAction.tsx | 3 +-
.../components/actions/DividerAction.tsx | 2 +-
.../components/actions/FullscreenAction.tsx | 4 +-
.../actions/KeyboardShortcutsAction.tsx | 5 +-
src/video/components/actions/MetaAction.tsx | 3 +-
.../components/actions/MiddlePauseAction.tsx | 3 +-
.../components/actions/PageTitleAction.tsx | 4 +-
src/video/components/actions/PauseAction.tsx | 4 +-
.../actions/PictureInPictureAction.tsx | 10 ++--
.../components/actions/ProgressAction.tsx | 3 +-
.../actions/SeriesSelectionAction.tsx | 11 ++--
.../components/actions/SettingsAction.tsx | 9 ++--
.../components/actions/ShowTitleAction.tsx | 1 +
.../components/actions/SkipTimeAction.tsx | 1 +
src/video/components/actions/TimeAction.tsx | 9 ++--
src/video/components/actions/VolumeAction.tsx | 3 +-
.../list-entries/CaptionsSelectionAction.tsx | 4 +-
.../actions/list-entries/DownloadAction.tsx | 12 +++--
.../PlaybackSpeedSelectionAction.tsx | 4 +-
.../list-entries/SourceSelectionAction.tsx | 6 ++-
.../components/controllers/MetaController.tsx | 3 +-
.../ProgressListenerController.tsx | 7 +--
.../controllers/SeriesController.tsx | 5 +-
.../controllers/SourceController.tsx | 3 +-
.../hooks/useCurrentSeriesEpisodeInfo.ts | 5 +-
src/video/components/hooks/useInitialized.ts | 3 +-
src/video/components/hooks/useSyncPopouts.ts | 5 +-
.../components/internal/CastingInternal.tsx | 3 +-
.../internal/VideoElementInternal.tsx | 3 +-
.../internal/WrapperRegisterInternal.tsx | 3 +-
.../components/parts/VideoErrorBoundary.tsx | 8 +--
.../components/parts/VideoPlayerError.tsx | 4 +-
.../components/parts/VideoPlayerHeader.tsx | 7 +--
.../parts/VideoPlayerIconButton.tsx | 3 +-
src/video/components/parts/VideoPopout.tsx | 3 +-
.../popouts/CaptionSelectionPopout.tsx | 6 ++-
.../popouts/CaptionSettingsPopout.tsx | 10 ++--
.../popouts/EpisodeSelectionPopout.tsx | 22 ++++----
.../popouts/PlaybackSpeedPopout.tsx | 6 ++-
src/video/components/popouts/PopoutAnchor.tsx | 3 +-
.../popouts/PopoutProviderAction.tsx | 7 +--
src/video/components/popouts/PopoutUtils.tsx | 7 +--
.../components/popouts/SettingsPopout.tsx | 7 +--
.../popouts/SourceSelectionPopout.tsx | 24 +++++----
src/video/state/hooks.tsx | 3 +-
src/video/state/init.ts | 1 +
src/video/state/logic/controls.ts | 1 +
src/video/state/logic/error.ts | 1 +
src/video/state/logic/interface.ts | 1 +
src/video/state/logic/mediaplaying.ts | 1 +
src/video/state/logic/meta.ts | 1 +
src/video/state/logic/misc.ts | 1 +
src/video/state/logic/progress.ts | 1 +
src/video/state/logic/source.ts | 4 +-
.../state/providers/castingStateProvider.ts | 10 ++--
src/video/state/providers/utils.ts | 3 +-
.../state/providers/videoStateProvider.ts | 18 ++++---
src/video/state/types.ts | 4 +-
src/views/SettingsModal.tsx | 21 ++++----
src/views/developer/EmbedTesterView.tsx | 3 +-
src/views/developer/ProviderTesterView.tsx | 5 +-
src/views/developer/VideoTesterView.tsx | 5 +-
src/views/media/MediaErrorView.tsx | 5 +-
src/views/media/MediaView.tsx | 30 ++++++-----
src/views/notfound/NotFoundView.tsx | 3 +-
src/views/other/v2Migration.tsx | 3 +-
src/views/search/HomeView.tsx | 16 +++---
src/views/search/SearchLoadingView.tsx | 1 +
src/views/search/SearchResultsPartial.tsx | 4 +-
src/views/search/SearchResultsView.tsx | 6 ++-
src/views/search/SearchView.tsx | 10 ++--
yarn.lock | 2 +-
133 files changed, 462 insertions(+), 280 deletions(-)
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/package.json b/package.json
index 817408a2..ce56f79e 100644
--- a/package.json
+++ b/package.json
@@ -82,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 c132c99c..5a837d01 100644
--- a/src/components/popout/FloatingCard.tsx
+++ b/src/components/popout/FloatingCard.tsx
@@ -1,12 +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;
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 c6b8388f..766ae7e6 100644
--- a/src/hooks/useVolumeToggle.ts
+++ b/src/hooks/useVolumeToggle.ts
@@ -1,6 +1,7 @@
+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);
diff --git a/src/index.tsx b/src/index.tsx
index d47668e7..1bf99f70 100644
--- a/src/index.tsx
+++ b/src/index.tsx
@@ -3,11 +3,12 @@ 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";
diff --git a/src/setup/App.tsx b/src/setup/App.tsx
index 6aef8905..83b8c731 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/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 43dbb1ef..56ac9b0f 100644
--- a/src/setup/i18n.ts
+++ b/src/setup/i18n.ts
@@ -1,14 +1,13 @@
import i18n from "i18next";
-import { initReactI18next } from "react-i18next";
import LanguageDetector from "i18next-browser-languagedetector";
+import { initReactI18next } from "react-i18next";
// Languages
+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";
-import fr from "./locales/fr/translation.json";
-
-import { captionLanguages } from "./iso6391";
const locales = {
en: {
diff --git a/src/setup/sentry.tsx b/src/setup/sentry.tsx
index 417a0d4c..268b31d7 100644
--- a/src/setup/sentry.tsx
+++ b/src/setup/sentry.tsx
@@ -1,7 +1,8 @@
-import * as Sentry from "@sentry/react";
import { CaptureConsole, HttpClient } from "@sentry/integrations";
-import { SENTRY_DSN } from "@/setup/constants";
+import * as Sentry from "@sentry/react";
+
import { conf } from "@/setup/config";
+import { SENTRY_DSN } from "@/setup/constants";
Sentry.init({
dsn: SENTRY_DSN,
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 c87b6f68..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();
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 25213135..867bedb7 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2673,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==