From 38baf571b76ba9d2f13823bed194bc81fd2d1890 Mon Sep 17 00:00:00 2001 From: IndieKKY Date: Fri, 4 Oct 2024 21:45:47 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E6=98=AF=E5=90=A6=E8=87=AA?= =?UTF-8?q?=E5=8A=A8=E6=8F=92=E5=85=A5=E5=AD=97=E5=B9=95=E5=88=97=E8=A1=A8?= =?UTF-8?q?=E9=80=89=E9=A1=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- manifest.config.ts | 2 +- src/chrome/background.ts | 7 +- src/const.ts | 1 + src/inject/inject.ts | 106 ++++++++++++++++++++---------- src/messaging/ExtensionMessage.ts | 10 +++ src/pages/OptionsPage.tsx | 8 ++- src/typings.d.ts | 1 + 7 files changed, 97 insertions(+), 38 deletions(-) diff --git a/manifest.config.ts b/manifest.config.ts index 2c53280..31f2f6a 100644 --- a/manifest.config.ts +++ b/manifest.config.ts @@ -41,7 +41,7 @@ export default defineManifest(async (env) => ({ "128": "favicon-128x128.png" }, "action": { - "default_popup": "popup.html", + // "default_popup": "popup.html", "default_icon": { "16": "favicon-16x16.png", "32": "favicon-32x32.png", diff --git a/src/chrome/background.ts b/src/chrome/background.ts index 736df8a..3530eb4 100644 --- a/src/chrome/background.ts +++ b/src/chrome/background.ts @@ -1,6 +1,6 @@ import {v4} from 'uuid' import {handleTask, initTaskService, tasksMap} from './taskService' -import {MESSAGE_TO_EXTENSION_ADD_TASK, MESSAGE_TO_EXTENSION_GET_TASK} from '@/const' +import {MESSAGE_TARGET_INJECT, MESSAGE_TO_EXTENSION_ADD_TASK, MESSAGE_TO_EXTENSION_GET_TASK, MESSAGE_TO_INJECT_TOGGLE_DISPLAY} from '@/const' import ExtensionMessage from '@/messaging/ExtensionMessage' const methods: { @@ -65,4 +65,9 @@ chrome.runtime.onMessage.addListener((event: MessageData, sender: chrome.runtime } }) +//点击扩展图标 +chrome.action.onClicked.addListener(async (tab) => { + extensionMessage.broadcastMessageExact([tab.id!], MESSAGE_TARGET_INJECT, MESSAGE_TO_INJECT_TOGGLE_DISPLAY).catch(console.error) +}) + initTaskService() diff --git a/src/const.ts b/src/const.ts index 3b60553..f16dab9 100644 --- a/src/const.ts +++ b/src/const.ts @@ -5,6 +5,7 @@ export const MESSAGE_TARGET_APP = 'BilibiliAPP' export const MESSAGE_TO_EXTENSION_ADD_TASK = 'addTask' export const MESSAGE_TO_EXTENSION_GET_TASK = 'getTask' +export const MESSAGE_TO_INJECT_TOGGLE_DISPLAY = 'toggleDisplay' export const MESSAGE_TO_INJECT_FOLD = 'fold' export const MESSAGE_TO_INJECT_MOVE = 'move' export const MESSAGE_TO_INJECT_PLAY = 'play' diff --git a/src/inject/inject.ts b/src/inject/inject.ts index 95cb3a9..c749afa 100644 --- a/src/inject/inject.ts +++ b/src/inject/inject.ts @@ -1,18 +1,32 @@ -import { TOTAL_HEIGHT_DEF, HEADER_HEIGHT, TOTAL_HEIGHT_MIN, TOTAL_HEIGHT_MAX, IFRAME_ID, MESSAGE_TO_INJECT_DOWNLOAD_AUDIO, MESSAGE_TARGET_INJECT, MESSAGE_TO_APP_SET_INFOS } from '@/const' -import { MESSAGE_TO_INJECT_FOLD, MESSAGE_TO_INJECT_MOVE, MESSAGE_TO_APP_SET_VIDEO_INFO, MESSAGE_TO_INJECT_GET_SUBTITLE, MESSAGE_TO_INJECT_GET_VIDEO_STATUS, MESSAGE_TO_INJECT_GET_VIDEO_ELEMENT_INFO, MESSAGE_TO_INJECT_UPDATETRANSRESULT, MESSAGE_TO_INJECT_PLAY, MESSAGE_TO_INJECT_HIDE_TRANS, MESSAGE_TO_INJECT_REFRESH_VIDEO_INFO} from '@/const' +import { TOTAL_HEIGHT_DEF, HEADER_HEIGHT, TOTAL_HEIGHT_MIN, TOTAL_HEIGHT_MAX, IFRAME_ID, MESSAGE_TO_INJECT_DOWNLOAD_AUDIO, MESSAGE_TARGET_INJECT, MESSAGE_TO_APP_SET_INFOS, MESSAGE_TO_INJECT_TOGGLE_DISPLAY, STORAGE_ENV } from '@/const' +import { MESSAGE_TO_INJECT_FOLD, MESSAGE_TO_INJECT_MOVE, MESSAGE_TO_APP_SET_VIDEO_INFO, MESSAGE_TO_INJECT_GET_SUBTITLE, MESSAGE_TO_INJECT_GET_VIDEO_STATUS, MESSAGE_TO_INJECT_GET_VIDEO_ELEMENT_INFO, MESSAGE_TO_INJECT_UPDATETRANSRESULT, MESSAGE_TO_INJECT_PLAY, MESSAGE_TO_INJECT_HIDE_TRANS, MESSAGE_TO_INJECT_REFRESH_VIDEO_INFO } from '@/const' import InjectMessage from '@/messaging/InjectMessage' const debug = (...args: any[]) => { console.debug('[Inject]', ...args) } -(function () { +(async function () { // 如果路径不是/video或/list,则不注入 if (!location.pathname.startsWith('/video') && !location.pathname.startsWith('/list')) { debug('Not inject') return } + //读取envData + const envDataStr = (await chrome.storage.sync.get(STORAGE_ENV))[STORAGE_ENV] + let manualInsert: boolean | null = null + if (envDataStr) { + try { + const envData = JSON.parse(envDataStr) + debug('envData: ', envData) + + manualInsert = envData.manualInsert + } catch (error) { + console.error('Error parsing envData:', error) + } + } + const runtime: { injectMessage: InjectMessage // lastV?: string | null @@ -42,7 +56,7 @@ const debug = (...args: any[]) => { */ const refreshVideoElement = () => { const newVideoElement = getVideoElement() - const newVideoElementHeight = (newVideoElement != null)?(Math.min(Math.max(newVideoElement.offsetHeight, TOTAL_HEIGHT_MIN), TOTAL_HEIGHT_MAX)):TOTAL_HEIGHT_DEF + const newVideoElementHeight = (newVideoElement != null) ? (Math.min(Math.max(newVideoElement.offsetHeight, TOTAL_HEIGHT_MIN), TOTAL_HEIGHT_MAX)) : TOTAL_HEIGHT_DEF if (newVideoElement === runtime.videoElement && Math.abs(newVideoElementHeight - runtime.videoElementHeight) < 1) { return false } else { @@ -54,39 +68,50 @@ const debug = (...args: any[]) => { } } - const timerIframe = setInterval(function () { + const createIframe = () => { var danmukuBox = document.getElementById('danmukuBox') if (danmukuBox) { - clearInterval(timerIframe) - - //延迟插入iframe(插入太快,网络较差时容易出现b站网页刷新,原因暂时未知,可能b站的某种机制?) - setTimeout(() => { - var vKey = '' - for (const key in danmukuBox?.dataset) { - if (key.startsWith('v-')) { - vKey = key - break - } + var vKey = '' + for (const key in danmukuBox?.dataset) { + if (key.startsWith('v-')) { + vKey = key + break } + } - const iframe = document.createElement('iframe') - iframe.id = IFRAME_ID - iframe.src = chrome.runtime.getURL('index.html') - iframe.style.border = 'none' - iframe.style.width = '100%' - iframe.style.height = '44px' - iframe.style.marginBottom = '3px' - iframe.allow = 'clipboard-read; clipboard-write;' - if (vKey) { - iframe.dataset[vKey] = danmukuBox?.dataset[vKey] - } - //insert before first child - danmukuBox?.insertBefore(iframe, danmukuBox?.firstChild) + const iframe = document.createElement('iframe') + iframe.id = IFRAME_ID + iframe.src = chrome.runtime.getURL('index.html') + iframe.style.border = 'none' + iframe.style.width = '100%' + iframe.style.height = '44px' + iframe.style.marginBottom = '3px' + iframe.allow = 'clipboard-read; clipboard-write;' - debug('iframe inserted') - }, 1500) + if (vKey) { + iframe.dataset[vKey] = danmukuBox?.dataset[vKey] + } + + //insert before first child + danmukuBox?.insertBefore(iframe, danmukuBox?.firstChild) + + debug('iframe inserted') + + return iframe } - }, 1000) + } + + if (!manualInsert) { + const timerIframe = setInterval(function () { + var danmukuBox = document.getElementById('danmukuBox') + if (danmukuBox) { + clearInterval(timerIframe) + + //延迟插入iframe(插入太快,网络较差时容易出现b站网页刷新,原因暂时未知,可能b站的某种机制?) + setTimeout(createIframe, 1500) + } + }, 1000) + } let aid: number | null = null let title = '' @@ -204,6 +229,14 @@ const debug = (...args: any[]) => { const methods: { [key: string]: (params: any, context: MethodContext) => Promise } = { + [MESSAGE_TO_INJECT_TOGGLE_DISPLAY]: async (params) => { + const iframe = document.getElementById(IFRAME_ID) as HTMLIFrameElement | undefined + if (iframe != null) { + iframe.style.display = iframe.style.display === 'none' ? 'block' : 'none' + } else { + createIframe() + } + }, [MESSAGE_TO_INJECT_FOLD]: async (params) => { runtime.fold = params.fold updateIframeHeight() @@ -249,7 +282,7 @@ const debug = (...args: any[]) => { let text = document.getElementById('trans-result-text') if (text) { - text.innerHTML = runtime.curTrans??'' + text.innerHTML = runtime.curTrans ?? '' } else { const container = document.getElementsByClassName('bpx-player-subtitle-panel-wrap')?.[0] if (container) { @@ -259,7 +292,7 @@ const debug = (...args: any[]) => { div.style.margin = '2px' text = document.createElement('text') text.id = 'trans-result-text' - text.innerHTML = runtime.curTrans??'' + text.innerHTML = runtime.curTrans ?? '' text.style.fontSize = '1rem' text.style.padding = '5px' text.style.color = 'white' @@ -309,7 +342,10 @@ const debug = (...args: any[]) => { runtime.injectMessage.init(methods) setInterval(() => { - refreshVideoInfo().catch(console.error) - refreshSubtitles() + const iframe = document.getElementById(IFRAME_ID) as HTMLIFrameElement | undefined + if (iframe != null && iframe.style.display !== 'none') { + refreshVideoInfo().catch(console.error) + refreshSubtitles() + } }, 1000) })() diff --git a/src/messaging/ExtensionMessage.ts b/src/messaging/ExtensionMessage.ts index 622e28c..bb68379 100644 --- a/src/messaging/ExtensionMessage.ts +++ b/src/messaging/ExtensionMessage.ts @@ -61,6 +61,16 @@ class ExtensionMessage { }) } + broadcastMessageExact = async (tabIds: number[], target: string, method: string, params?: any) => { + for (const tabId of tabIds) { + try { + await chrome.tabs.sendMessage(tabId, {target, method, params}) + } catch (e) { + console.error('send message to tab error', tabId, e) + } + } + } + broadcastMessage = async (ignoreTabIds: number[] | undefined | null, target: string, method: string, params?: any) => { const tabs = await chrome.tabs.query({ discarded: false, diff --git a/src/pages/OptionsPage.tsx b/src/pages/OptionsPage.tsx index 14f8c09..5bc09c8 100644 --- a/src/pages/OptionsPage.tsx +++ b/src/pages/OptionsPage.tsx @@ -59,6 +59,7 @@ const FormItem = (props: { const OptionsPage = () => { const dispatch = useAppDispatch() const envData = useAppSelector(state => state.env.envData) + const {value: autoInsertValue, onChange: setAutoInsertValue} = useEventChecked(!envData.manualInsert) const {value: autoExpandValue, onChange: setAutoExpandValue} = useEventChecked(envData.autoExpand) // const {value: autoScrollValue, onChange: setAutoScrollValue} = useEventChecked(envData.autoScroll) const {value: translateEnableValue, onChange: setTranslateEnableValue} = useEventChecked(envData.translateEnable) @@ -111,6 +112,7 @@ const OptionsPage = () => { const onSave = useCallback(() => { dispatch(setEnvData({ + manualInsert: !autoInsertValue, autoExpand: autoExpandValue, aiType: aiTypeValue, apiKey: apiKeyValue, @@ -140,7 +142,7 @@ const OptionsPage = () => { setTimeout(() => { window.close() }, 3000) - }, [dispatch, autoExpandValue, aiTypeValue, apiKeyValue, serverUrlValue, modelValue, customModelValue, customModelTokensValue, geminiApiKeyValue, translateEnableValue, languageValue, hideOnDisableAutoTranslateValue, themeValue, transDisplayValue, summarizeEnableValue, summarizeFloatValue, summarizeLanguageValue, wordsValue, fetchAmountValue, fontSizeValue, promptsValue, searchEnabledValue, cnSearchEnabledValue, askEnabledValue]) + }, [dispatch, autoInsertValue, autoExpandValue, aiTypeValue, apiKeyValue, serverUrlValue, modelValue, customModelValue, customModelTokensValue, geminiApiKeyValue, translateEnableValue, languageValue, hideOnDisableAutoTranslateValue, themeValue, transDisplayValue, summarizeEnableValue, summarizeFloatValue, summarizeLanguageValue, wordsValue, fetchAmountValue, fontSizeValue, promptsValue, searchEnabledValue, cnSearchEnabledValue, askEnabledValue]) const onCancel = useCallback(() => { window.close() @@ -197,6 +199,10 @@ const OptionsPage = () => { return
+ + + diff --git a/src/typings.d.ts b/src/typings.d.ts index 25183ab..7e2dbb4 100644 --- a/src/typings.d.ts +++ b/src/typings.d.ts @@ -22,6 +22,7 @@ interface MethodContext { } interface EnvData { + manualInsert?: boolean //是否手动插入字幕列表 autoExpand?: boolean flagDot?: boolean