From e74053b5a4de3021156af586405cca389fd14b1b Mon Sep 17 00:00:00 2001 From: aidenlx <31102694+aidenlx@users.noreply.github.com> Date: Sat, 17 Feb 2024 11:43:59 +0800 Subject: [PATCH] fix(webpage): improve webpage plugin timeout notice; optimize load speed; add reload command --- apps/app/src/components/context.tsx | 5 +++ apps/app/src/components/player/menus.tsx | 3 ++ apps/app/src/components/provider.tsx | 2 +- apps/app/src/lib/remote-player/lib/plugin.ts | 32 ++++++-------- apps/app/src/lib/remote-player/provider.ts | 38 +++++++++++++++-- apps/app/src/lib/remote-player/ua.ts | 1 + apps/app/src/media-view/base.tsx | 2 + apps/app/src/media-view/file-embed.tsx | 8 +++- apps/app/src/media-view/file-view.tsx | 1 + apps/app/src/media-view/menu/index.ts | 17 ++++++++ apps/app/src/media-view/remote-view.tsx | 1 + apps/app/src/media-view/url-embed.tsx | 7 ++- apps/app/src/switcher/modal.ts | 2 +- apps/app/src/web/userscript/bilibili.ts | 18 ++++---- apps/app/src/web/userscript/youtube.ts | 45 +++++++++++--------- 15 files changed, 127 insertions(+), 55 deletions(-) diff --git a/apps/app/src/components/context.tsx b/apps/app/src/components/context.tsx index e5707ead..7265fb9c 100644 --- a/apps/app/src/components/context.tsx +++ b/apps/app/src/components/context.tsx @@ -173,6 +173,7 @@ export const MediaViewContext = createContext<{ store: MediaViewStoreApi; plugin: MediaExtended; embed: boolean; + reload: () => void; onScreenshot?: (info: ScreenshotInfo) => any; onTimestamp?: (timestamp: number) => any; }>(null as any); @@ -198,6 +199,10 @@ export function useMediaViewStoreInst() { return store; } +export function useReload() { + return useContext(MediaViewContext).reload; +} + export function useApp(): App; export function useApp(selector: (state: App) => U): U; export function useApp(selector?: (state: App) => U): U | App { diff --git a/apps/app/src/components/player/menus.tsx b/apps/app/src/components/player/menus.tsx index 7b961ca9..6199b0c2 100644 --- a/apps/app/src/components/player/menus.tsx +++ b/apps/app/src/components/player/menus.tsx @@ -16,6 +16,7 @@ import { useMediaViewStore, useMediaViewStoreInst, usePlugin, + useReload, } from "../context"; import { dataLpPassthrough } from "./buttons"; @@ -76,6 +77,7 @@ export function MoreOptions() { const workspace = useApp((app) => app.workspace); const plugin = usePlugin(); const isEmbed = useIsEmbed(); + const reload = useReload(); const source = useMediaViewStore((state) => state.source?.url); const store = useMediaViewStoreInst(); const onClick = useMenu((menu) => { @@ -93,6 +95,7 @@ export function MoreOptions() { menu, { player, + reload, source, toggleControls, controls, diff --git a/apps/app/src/components/provider.tsx b/apps/app/src/components/provider.tsx index f46e4288..2c2c25b3 100644 --- a/apps/app/src/components/provider.tsx +++ b/apps/app/src/components/provider.tsx @@ -56,7 +56,7 @@ export function MediaProviderEnhanced({ { + this.register( + this.controller.on("mx-toggle-controls", ({ payload: showWebsite }) => { + document.body.classList.toggle("mx-show-controls", showWebsite); + }), + ); + if (isNativePlayer) { this.register( this.controller.on("mx-toggle-controls", ({ payload: showWebsite }) => { - document.body.classList.toggle("mx-show-controls", showWebsite); + this.media.controls = showWebsite; }), ); - if (isNativePlayer) { - this.register( - this.controller.on( - "mx-toggle-controls", - ({ payload: showWebsite }) => { - this.media.controls = showWebsite; - }, - ), - ); - } - this.register( - this.controller.on("mx-toggle-webfs", ({ payload: enableWebFs }) => { - document.body.classList.toggle("mx-fs-enable", enableWebFs); - }), - ); - }); + } + this.register( + this.controller.on("mx-toggle-webfs", ({ payload: enableWebFs }) => { + document.body.classList.toggle("mx-fs-enable", enableWebFs); + }), + ); + document.body.classList.add("mx-play-ready"); this.controller.send("mx-play-ready", void 0); console.log("sent play ready"); } diff --git a/apps/app/src/lib/remote-player/provider.ts b/apps/app/src/lib/remote-player/provider.ts index 9d194b92..08fdd908 100644 --- a/apps/app/src/lib/remote-player/provider.ts +++ b/apps/app/src/lib/remote-player/provider.ts @@ -9,7 +9,7 @@ import type { WebviewTag } from "electron"; import init from "inline:./scripts/initialize"; import { isString } from "maverick.js/std"; -import { Notice } from "obsidian"; +import { ButtonComponent, Notice } from "obsidian"; import type { WebviewElement } from "@/components/webview"; import { GET_PORT_TIMEOUT, PORT_MESSAGE } from "@/lib/remote-player/const"; import { plugins } from "@/web/plugin"; @@ -195,14 +195,17 @@ export class WebiviewMediaProvider implements MediaProviderAdapter { }); }); + const timeoutLimit = 10e3; finishLoad - .then(() => Promise.race([playReady, timeout(10e3)])) + .then(() => { + return Promise.race([playReady, timeout(timeoutLimit)]); + }) .then(() => { this.togglePlayReady(true); }) .catch((err) => { if (err instanceof TimeoutError) { - new Notice("Webview failed to load plugin in time"); + timeoutNotice(timeoutLimit); } else if (err instanceof WebviewLoadError) { new Notice("Webview failed to load website: " + err.message); } else { @@ -314,3 +317,32 @@ function notifyLogin() { ); localStorage.setItem(label, "1"); } + +function timeoutNotice(timeout: number) { + const label = "mx:webview-timeout-ignore"; + const ignore = localStorage.getItem(label); + if (ignore) return; + const timeoutLabel = (timeout / 1e3).toFixed(1); + const notice = new Notice( + createFragment((e) => { + e.createDiv({ + text: `Webpage not fully loaded within ${timeoutLabel}s. You can still try to play.`, + }); + e.createDiv({}, (div) => { + div.style.display = "flex"; + div.style.justifyContent = "flex-end"; + div.style.gap = "1em"; + div.style.marginTop = "1em"; + new ButtonComponent(div).setButtonText("OK"); + new ButtonComponent(div) + .setButtonText("Don't show again") + .onClick(() => { + console.log("ignore webview timeout notice"); + localStorage.setItem(label, "1"); + notice.hide(); + }); + }); + }), + 5e3, + ); +} diff --git a/apps/app/src/lib/remote-player/ua.ts b/apps/app/src/lib/remote-player/ua.ts index 29703cd4..b2941446 100644 --- a/apps/app/src/lib/remote-player/ua.ts +++ b/apps/app/src/lib/remote-player/ua.ts @@ -1,3 +1,4 @@ export function getUserAgent(ua: string) { + return "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36"; return ua.replaceAll(/(?:Electron|obsidian)\/\S+ ?/g, ""); } diff --git a/apps/app/src/media-view/base.tsx b/apps/app/src/media-view/base.tsx index 2c4930d2..d61c6d10 100644 --- a/apps/app/src/media-view/base.tsx +++ b/apps/app/src/media-view/base.tsx @@ -92,6 +92,7 @@ function noticeNotetaking(action: string) { export function onPaneMenu< T extends PlayerComponent & { getViewType(): MediaViewType; + render(): void; } & View, >( view: T, @@ -122,6 +123,7 @@ export function onPaneMenu< plugin: view.plugin, disableWebFullscreen, toggleWebFullscreen, + reload: () => view.render(), }, menuSource, view.leaf, diff --git a/apps/app/src/media-view/file-embed.tsx b/apps/app/src/media-view/file-embed.tsx index f783dd37..2424c910 100644 --- a/apps/app/src/media-view/file-embed.tsx +++ b/apps/app/src/media-view/file-embed.tsx @@ -53,7 +53,8 @@ export class MediaFileEmbed }; } - onload(): void { + // eslint-disable-next-line react/require-render-return + render() { this.root?.unmount(); this.root = ReactDOM.createRoot(this.info.containerEl); this.root.render( @@ -61,6 +62,7 @@ export class MediaFileEmbed value={{ plugin: this.plugin, store: this.store, + reload: () => this.render(), embed: true, }} > @@ -68,6 +70,10 @@ export class MediaFileEmbed , ); } + onload(): void { + super.onload(); + this.render(); + } async loadFile() { await this.store diff --git a/apps/app/src/media-view/file-view.tsx b/apps/app/src/media-view/file-view.tsx index 1ced2d36..3e59e6ad 100644 --- a/apps/app/src/media-view/file-view.tsx +++ b/apps/app/src/media-view/file-view.tsx @@ -72,6 +72,7 @@ abstract class MediaFileView value={{ plugin: this.plugin, store: this.store, + reload: () => this.render(), embed: false, }} > diff --git a/apps/app/src/media-view/menu/index.ts b/apps/app/src/media-view/menu/index.ts index 2e2d9baf..15ef7eec 100644 --- a/apps/app/src/media-view/menu/index.ts +++ b/apps/app/src/media-view/menu/index.ts @@ -14,6 +14,7 @@ export interface PlayerContext { player: MediaPlayerInstance; source: MediaURL; plugin: MxPlugin; + reload: () => void; setTransform: MediaViewState["setTransform"]; transform: MediaViewState["transform"]; controls: boolean | undefined; @@ -59,6 +60,22 @@ export default function registerMediaMenu(this: MxPlugin) { handleExternalLinkMenu(this); this.registerEvent( this.app.workspace.on("mx-media-menu", (menu, ctx, source) => { + if ( + source === "more-options" || + source === "sidebar-context-menu" || + source === "tab-header" || + source === "player-menu-embed" + ) { + menu.addItem((item) => + item + .setTitle("Refresh") + .setSection("view") + .setIcon("reset") + .onClick(() => { + ctx.reload(); + }), + ); + } if (source !== "sidebar-context-menu" && source !== "tab-header") { menu.addItem((item) => speedMenu(item, ctx.player)); if (ctx.player.state.viewType === "video") { diff --git a/apps/app/src/media-view/remote-view.tsx b/apps/app/src/media-view/remote-view.tsx index aed5e3fd..8bda8d35 100644 --- a/apps/app/src/media-view/remote-view.tsx +++ b/apps/app/src/media-view/remote-view.tsx @@ -131,6 +131,7 @@ export abstract class MediaRemoteView value={{ plugin: this.plugin, store: this.store, + reload: () => this.render(), embed: false, }} > diff --git a/apps/app/src/media-view/url-embed.tsx b/apps/app/src/media-view/url-embed.tsx index 972d4b33..95e90f11 100644 --- a/apps/app/src/media-view/url-embed.tsx +++ b/apps/app/src/media-view/url-embed.tsx @@ -26,7 +26,7 @@ export class MediaRenderChild return this.store.getState().setSource; } - onload(): void { + render() { this.root?.unmount(); this.root = ReactDOM.createRoot(this.containerEl); this.root.render( @@ -34,6 +34,7 @@ export class MediaRenderChild value={{ plugin: this.plugin, store: this.store, + reload: () => this.render(), embed: true, }} > @@ -41,6 +42,10 @@ export class MediaRenderChild , ); } + onload(): void { + super.onload(); + this.render(); + } onunload() { // unmount before detach from DOM diff --git a/apps/app/src/switcher/modal.ts b/apps/app/src/switcher/modal.ts index b6a6554d..69e14f74 100644 --- a/apps/app/src/switcher/modal.ts +++ b/apps/app/src/switcher/modal.ts @@ -168,7 +168,7 @@ export class MediaSwitcherModal extends SuggestModal { } else { assertNever(item); } - if (!item.inferredType) { + if (item.isFileUrl && !item.inferredType) { new Notice("Unsupported file type: " + item.pathname); return; } diff --git a/apps/app/src/web/userscript/bilibili.ts b/apps/app/src/web/userscript/bilibili.ts index 4729ae1f..29128ca4 100644 --- a/apps/app/src/web/userscript/bilibili.ts +++ b/apps/app/src/web/userscript/bilibili.ts @@ -36,8 +36,8 @@ export default class BilibiliPlugin extends MediaPlugin { ); await super.onload(); this.revertAutoSeek(); - await this.untilMediaReady("canplay"); - await Promise.all([this.toggleDanmaku(false), this.untilWebFullscreen()]); + Promise.all([this.toggleDanmaku(false)]); + await this.untilWebFullscreen(); } get player() { @@ -91,20 +91,20 @@ export default class BilibiliPlugin extends MediaPlugin { return this.player.classList.contains("mode-webscreen"); } - async enterWebFullscreen() { + enterWebFullscreen() { if (this.isWebFullscreen()) { console.log("Already in web fullscreen"); return; } - const fsButton = await waitForSelector( - ".bpx-player-ctrl-web", - this.player, + waitForSelector(".bpx-player-ctrl-web", this.player).then( + (fsButton) => { + console.log("Clicking fullscreen button"); + fsButton.click(); + }, ); - console.log("Clicking fullscreen button"); - fsButton.click(); } - async revertAutoSeek() { + revertAutoSeek() { const plyaer = this.player; const toastContainer = plyaer.querySelector( ".bpx-player-toast-auto", diff --git a/apps/app/src/web/userscript/youtube.ts b/apps/app/src/web/userscript/youtube.ts index b1bddd5c..2bd1a1ad 100644 --- a/apps/app/src/web/userscript/youtube.ts +++ b/apps/app/src/web/userscript/youtube.ts @@ -1,6 +1,7 @@ // hugely inspired by https://greasyfork.org/zh-CN/scripts/4870-maximize-video const css = ` +body:not(.mx-player-ready) #movie_player, ytd-watch-flexy[theater] #movie_player { position: fixed !important; top: 0 !important; @@ -91,12 +92,12 @@ export default class BilibiliPlugin extends MediaPlugin { } async onload(): Promise { await super.onload(); - - waitForSelector(".video-ads.ytp-ad-module", this.app).then( - (adModule) => this.removePlayerAD(adModule), - ); - - await Promise.all([this.disableAutoPlay()]); + Promise.all([ + waitForSelector(".video-ads.ytp-ad-module", this.app).then( + (adModule) => this.removePlayerAD(adModule), + ), + this.disableAutoPlay(), + ]); } get app() { @@ -182,23 +183,25 @@ export default class BilibiliPlugin extends MediaPlugin { } } - async enterWebFullscreen() { + enterWebFullscreen() { this.assignParentClass(this.moviePlayer); - const fsButton = await waitForSelector( - "#movie_player .ytp-size-button", - ); - const isCinematicsMode = () => - !!this.app.querySelector("ytd-watch-flexy[theater]"); - if (!isCinematicsMode()) { - console.log("Entering cinema mode"); - do { - fsButton.click(); - await sleep(200); - } while (!isCinematicsMode()); - console.log("Entered cinema mode"); - } - window.dispatchEvent(new Event("resize")); + (async () => { + const fsButton = await waitForSelector( + "#movie_player .ytp-size-button", + ); + const isCinematicsMode = () => + !!this.app.querySelector("ytd-watch-flexy[theater]"); + if (!isCinematicsMode()) { + console.log("Entering cinema mode"); + do { + fsButton.click(); + await sleep(200); + } while (!isCinematicsMode()); + console.log("Entered cinema mode"); + } + window.dispatchEvent(new Event("resize")); + })(); } }