You've already forked bilibili-subtitle
增加是否自动插入字幕列表选项
This commit is contained in:
@@ -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",
|
||||
|
@@ -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()
|
||||
|
@@ -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'
|
||||
|
@@ -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,13 +68,9 @@ 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-')) {
|
||||
@@ -77,16 +87,31 @@ const debug = (...args: any[]) => {
|
||||
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)
|
||||
|
||||
debug('iframe inserted')
|
||||
}, 1500)
|
||||
|
||||
return iframe
|
||||
}
|
||||
}
|
||||
|
||||
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<any>
|
||||
} = {
|
||||
[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(() => {
|
||||
const iframe = document.getElementById(IFRAME_ID) as HTMLIFrameElement | undefined
|
||||
if (iframe != null && iframe.style.display !== 'none') {
|
||||
refreshVideoInfo().catch(console.error)
|
||||
refreshSubtitles()
|
||||
}
|
||||
}, 1000)
|
||||
})()
|
||||
|
@@ -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,
|
||||
|
@@ -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 <div className='flex justify-center'>
|
||||
<div className="w-2/3 max-w-[600px] flex flex-col gap-3 p-2">
|
||||
<Section title='通用配置'>
|
||||
<FormItem title='自动插入' htmlFor='autoInsert' tip='是否自动插入字幕列表(可以手动点击扩展图标插入)'>
|
||||
<input id='autoInsert' type='checkbox' className='toggle toggle-primary' checked={autoInsertValue}
|
||||
onChange={setAutoInsertValue}/>
|
||||
</FormItem>
|
||||
<FormItem title='自动展开' htmlFor='autoExpand' tip='是否视频有字幕时自动展开字幕列表'>
|
||||
<input id='autoExpand' type='checkbox' className='toggle toggle-primary' checked={autoExpandValue}
|
||||
onChange={setAutoExpandValue}/>
|
||||
|
1
src/typings.d.ts
vendored
1
src/typings.d.ts
vendored
@@ -22,6 +22,7 @@ interface MethodContext {
|
||||
}
|
||||
|
||||
interface EnvData {
|
||||
manualInsert?: boolean //是否手动插入字幕列表
|
||||
autoExpand?: boolean
|
||||
flagDot?: boolean
|
||||
|
||||
|
Reference in New Issue
Block a user