From 67f30474636bca8587fac1d82de01a62f6d652fe Mon Sep 17 00:00:00 2001 From: William Oldham Date: Tue, 23 Jan 2024 23:54:32 +0000 Subject: [PATCH 01/55] Use Promise.all for simultaneous calls --- src/hooks/auth/useAuth.ts | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/hooks/auth/useAuth.ts b/src/hooks/auth/useAuth.ts index 83fe7e22..f608d148 100644 --- a/src/hooks/auth/useAuth.ts +++ b/src/hooks/auth/useAuth.ts @@ -149,8 +149,10 @@ export function useAuth() { bookmarkMediaToInput(tmdbId, item), ); - await importProgress(backendUrl, account, progressInputs); - await importBookmarks(backendUrl, account, bookmarkInputs); + await Promise.all([ + importProgress(backendUrl, account, progressInputs), + importBookmarks(backendUrl, account, bookmarkInputs), + ]); }, [backendUrl], ); @@ -174,9 +176,11 @@ export function useAuth() { throw err; } - const bookmarks = await getBookmarks(backendUrl, account); - const progress = await getProgress(backendUrl, account); - const settings = await getSettings(backendUrl, account); + const [bookmarks, progress, settings] = await Promise.all([ + getBookmarks(backendUrl, account), + getProgress(backendUrl, account), + getSettings(backendUrl, account), + ]); syncData(user.user, user.session, progress, bookmarks, settings); }, From a91e2b6e2efc9964b4214e7b8bdd3f09f45b9582 Mon Sep 17 00:00:00 2001 From: mrjvs Date: Wed, 24 Jan 2024 14:02:35 +0100 Subject: [PATCH 02/55] adjust setupPart timings and extension message preload --- src/backend/extension/messaging.ts | 13 +++++++++++-- src/pages/parts/settings/SetupPart.tsx | 2 +- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/backend/extension/messaging.ts b/src/backend/extension/messaging.ts index 1f2a2b00..3204d541 100644 --- a/src/backend/extension/messaging.ts +++ b/src/backend/extension/messaging.ts @@ -6,13 +6,22 @@ import { import { isAllowedExtensionVersion } from "@/backend/extension/compatibility"; import { ExtensionMakeRequestResponse } from "@/backend/extension/plasmo"; +// for some reason, about 500 ms is needed after +// page load before the extension starts responding properly +const isExtensionReady = new Promise((resolve) => { + setTimeout(() => { + resolve(); + }, 500); +}); + let activeExtension = false; -function sendMessage( +async function sendMessage( message: MessageKey, payload: MessagesMetadata[MessageKey]["req"] | undefined = undefined, timeout: number = -1, ) { + await isExtensionReady; return new Promise((resolve) => { if (timeout >= 0) setTimeout(() => resolve(null), timeout); sendToBackgroundViaRelay< @@ -54,7 +63,7 @@ export async function sendPage( export async function extensionInfo(): Promise< MessagesMetadata["hello"]["res"] | null > { - const message = await sendMessage("hello", undefined, 300); + const message = await sendMessage("hello", undefined, 500); return message; } diff --git a/src/pages/parts/settings/SetupPart.tsx b/src/pages/parts/settings/SetupPart.tsx index d188c759..16100ca7 100644 --- a/src/pages/parts/settings/SetupPart.tsx +++ b/src/pages/parts/settings/SetupPart.tsx @@ -29,7 +29,7 @@ type SetupData = { function testProxy(url: string) { return new Promise((resolve, reject) => { - setTimeout(() => reject(new Error("Timed out!")), 1000); + setTimeout(() => reject(new Error("Timed out!")), 3000); singularProxiedFetch(url, testUrl, {}) .then((res) => { if (res.url !== testUrl) return reject(new Error("Not a proxy")); From 8abcd6c43a53187109af6cf2363fae951b6682e4 Mon Sep 17 00:00:00 2001 From: mrjvs Date: Wed, 24 Jan 2024 14:17:23 +0100 Subject: [PATCH 03/55] Mobile responsive onboarding start --- src/pages/onboarding/Onboarding.tsx | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/src/pages/onboarding/Onboarding.tsx b/src/pages/onboarding/Onboarding.tsx index 525ed7df..38d465c2 100644 --- a/src/pages/onboarding/Onboarding.tsx +++ b/src/pages/onboarding/Onboarding.tsx @@ -39,7 +39,7 @@ export function OnboardingPage() { {t("onboarding.defaultConfirm.description")} -
+
@@ -58,7 +58,7 @@ export function OnboardingPage() { {t("onboarding.start.explainer")} -
+
navigate("/onboarding/proxy")}> {t("onboarding.start.options.proxy.action")} -
+
or @@ -86,7 +86,7 @@ export function OnboardingPage() {
-

+


+ +
+ +
); From e1c09225ee67fd96b56a55673ed9928076c961d7 Mon Sep 17 00:00:00 2001 From: mrjvs Date: Wed, 24 Jan 2024 14:51:00 +0100 Subject: [PATCH 04/55] Detect browser for extension --- package.json | 1 + pnpm-lock.yaml | 7 + src/assets/locales/en.json | 6 +- src/pages/onboarding/OnboardingExtension.tsx | 160 +++++++++++++++---- src/utils/detectFeatures.ts | 24 +++ 5 files changed, 168 insertions(+), 30 deletions(-) diff --git a/package.json b/package.json index f58b8eab..b8396c0f 100644 --- a/package.json +++ b/package.json @@ -38,6 +38,7 @@ "@types/node-forge": "^1.3.10", "classnames": "^2.3.2", "core-js": "^3.34.0", + "detect-browser": "^5.3.0", "dompurify": "^3.0.6", "flag-icons": "^7.1.0", "focus-trap-react": "^10.2.3", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a0e60397..329390b5 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -48,6 +48,9 @@ dependencies: core-js: specifier: ^3.34.0 version: 3.34.0 + detect-browser: + specifier: ^5.3.0 + version: 5.3.0 dompurify: specifier: ^3.0.6 version: 3.0.6 @@ -3338,6 +3341,10 @@ packages: resolution: {integrity: sha512-M1Ob1zPSIvlARiJUkKqvAZ3VAqQY6Jcuth/pBKQ2b1dX/Qx0OnJ8Vux6J2H5PTMQeRzWrrbTu70VxBfv/OPDJA==} dev: false + /detect-browser@5.3.0: + resolution: {integrity: sha512-53rsFbGdwMwlF7qvCt0ypLM5V5/Mbl0szB7GPN8y9NCcbknYOeVVXdrXEq+90IwAfrrzt6Hd+u2E2ntakICU8w==} + dev: false + /didyoumean@1.2.2: resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==} dev: true diff --git a/src/assets/locales/en.json b/src/assets/locales/en.json index 15038d6f..84d4e08c 100644 --- a/src/assets/locales/en.json +++ b/src/assets/locales/en.json @@ -232,7 +232,7 @@ "downloadSubtitle": "Download current subtitle", "downloadPlaylist": "Download playlist", "downloadVideo": "Download video", - "hlsDisclaimer": "Downloads are taken directly from the provider. movie-web does not have control over how the downloads are provided.

Please note that you are downloading an HLS playlist, it is not recommended to download if you are not familiar with advanced streaming formats. Try different sources for different formats.", + "hlsDisclaimer": "Downloads are taken directly from the provider. movie-web does not have control over how the downloads are provided.

Please note that you are downloading an HLS playlist, it is not recommended to download if you are not familiar with advanced streaming formats. Try different sources for different formats.", "onAndroid": { "1": "To download on Android, click the download button then, on the new page, tap and hold on the video, then select save.", "shortTitle": "Download / Android", @@ -506,8 +506,10 @@ "extension": { "title": "Let's start with an extension", "explainer": "Using the browser extension, you can get the best streams we have to offer. With just a simple install.", + "explainerIos": "Unfortunately, the browser extension is not supported on IOS, Press Go back to choose another option.", "extensionHelp": "If you've installed the extension but it's not detected. Open the extension through your browsers extension menu and follow the steps on screen.", - "link": "Install extension", + "linkChrome": "Install Chrome extension", + "linkFirefox": "Install Firefox extension", "back": "Go back", "status": { "loading": "Waiting for you to install the extension", diff --git a/src/pages/onboarding/OnboardingExtension.tsx b/src/pages/onboarding/OnboardingExtension.tsx index 3c17e3b8..5dbbecad 100644 --- a/src/pages/onboarding/OnboardingExtension.tsx +++ b/src/pages/onboarding/OnboardingExtension.tsx @@ -1,4 +1,4 @@ -import { ReactNode } from "react"; +import { ReactNode, useMemo } from "react"; import { Trans, useTranslation } from "react-i18next"; import { useAsyncFn, useInterval } from "react-use"; @@ -18,6 +18,10 @@ import { import { Card, Link } from "@/pages/onboarding/utils"; import { PageTitle } from "@/pages/parts/util/PageTitle"; import { conf } from "@/setup/config"; +import { + ExtensionDetectionResult, + detectExtensionInstall, +} from "@/utils/detectFeatures"; type ExtensionStatus = | "unknown" @@ -40,6 +44,7 @@ async function getExtensionState(): Promise { export function ExtensionStatus(props: { status: ExtensionStatus; loading: boolean; + showHelp?: boolean; }) { const { t } = useTranslation(); @@ -88,19 +93,119 @@ export function ExtensionStatus(props: { {content}
- -
- -

- , - }} - /> -

-
-
+ {props.showHelp ? ( + +
+ +

+ , + }} + /> +

+
+
+ ) : null} + + ); +} + +interface ExtensionPageProps { + status: ExtensionStatus; + loading: boolean; +} + +function ChromeExtensionPage(props: ExtensionPageProps) { + const { t } = useTranslation(); + const installLink = conf().ONBOARDING_EXTENSION_INSTALL_LINK; + return ( + <> + + {t("onboarding.extension.title")} + + + {t("onboarding.extension.explainer")} + + {installLink ? ( + + {t("onboarding.extension.linkChrome")} + + ) : null} + + + + ); +} + +function FirefoxExtensionPage(props: ExtensionPageProps) { + const { t } = useTranslation(); + const installLink = conf().ONBOARDING_EXTENSION_INSTALL_LINK; + return ( + <> + + {t("onboarding.extension.title")} + + + {t("onboarding.extension.explainer")} + + {installLink ? ( + + {t("onboarding.extension.linkFirefox")} + + ) : null} + + + + ); +} + +function IosExtensionPage(_props: ExtensionPageProps) { + const { t } = useTranslation(); + return ( + <> + + {t("onboarding.extension.title")} + + + }} + /> + + + ); +} + +function UnknownExtensionPage(props: ExtensionPageProps) { + const { t } = useTranslation(); + const installChromeLink = conf().ONBOARDING_EXTENSION_INSTALL_LINK; + const installFirefoxLink = conf().ONBOARDING_EXTENSION_INSTALL_LINK; + return ( + <> + + {t("onboarding.extension.title")} + + + {t("onboarding.extension.explainer")} + +
+ {installChromeLink ? ( + + {t("onboarding.extension.linkChrome")} + + ) : null} +
+
+ {installFirefoxLink ? ( + + {t("onboarding.extension.linkFirefox")} + + ) : null} +
+ + ); } @@ -109,7 +214,7 @@ export function OnboardingExtensionPage() { const { t } = useTranslation(); const navigate = useNavigateOnboarding(); const { completeAndRedirect } = useRedirectBack(); - const installLink = conf().ONBOARDING_EXTENSION_INSTALL_LINK; + const extensionSupport = useMemo(() => detectExtensionInstall(), []); const [{ loading, value }, exec] = useAsyncFn( async (triggeredManually: boolean = false) => { @@ -121,24 +226,23 @@ export function OnboardingExtensionPage() { ); useInterval(exec, 1000); + const componentMap: Record< + ExtensionDetectionResult, + typeof UnknownExtensionPage + > = { + chrome: ChromeExtensionPage, + firefox: FirefoxExtensionPage, + ios: IosExtensionPage, + unknown: UnknownExtensionPage, + }; + const PageContent = componentMap[extensionSupport]; + return ( - - {t("onboarding.extension.title")} - - - {t("onboarding.extension.explainer")} - - {installLink ? ( - - {t("onboarding.extension.link")} - - ) : null} - - +
+
+ + ); +} + export function ExtensionStatus(props: { status: ExtensionStatus; loading: boolean; showHelp?: boolean; }) { const { t } = useTranslation(); + const [lastKnownStatus, setLastKnownStatus] = useState(props.status); + useEffect(() => { + if (!props.loading) setLastKnownStatus(props.status); + }, [props.status, props.loading]); let content: ReactNode = null; if (props.loading || props.status === "unknown") @@ -93,6 +114,7 @@ export function ExtensionStatus(props: { {content}
+ {lastKnownStatus === "unknown" ? : null} {props.showHelp ? (
From a87f4cf22266c28b1743cb4311af1dd942876fb7 Mon Sep 17 00:00:00 2001 From: mrjvs Date: Wed, 24 Jan 2024 15:13:59 +0100 Subject: [PATCH 07/55] Make download subtitle not automatically crash the tab but only after clicking (if ram is too low) --- .../player/atoms/settings/Downloads.tsx | 25 +++++++++---------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/src/components/player/atoms/settings/Downloads.tsx b/src/components/player/atoms/settings/Downloads.tsx index bba9cf20..45c97997 100644 --- a/src/components/player/atoms/settings/Downloads.tsx +++ b/src/components/player/atoms/settings/Downloads.tsx @@ -1,4 +1,4 @@ -import { useMemo } from "react"; +import { useCallback, useMemo } from "react"; import { Trans, useTranslation } from "react-i18next"; import { Button } from "@/components/buttons/Button"; @@ -46,13 +46,13 @@ export function DownloadView({ id }: { id: string }) { const sourceType = usePlayerStore((s) => s.source?.type); const selectedCaption = usePlayerStore((s) => s.caption?.selected); - const subtitleUrl = useMemo( - () => - selectedCaption - ? convertSubtitlesToSrtDataurl(selectedCaption?.srtData) - : null, - [selectedCaption], - ); + const openSubtitleDownload = useCallback(() => { + const dataUrl = selectedCaption + ? convertSubtitlesToSrtDataurl(selectedCaption?.srtData) + : null; + if (!dataUrl) return; + window.open(dataUrl); + }, [selectedCaption]); if (!downloadUrl) return null; @@ -74,10 +74,9 @@ export function DownloadView({ id }: { id: string }) { @@ -109,8 +108,8 @@ export function DownloadView({ id }: { id: string }) {