You've already forked bilibili-subtitle
增加是否自动插入字幕列表选项
This commit is contained in:
@@ -41,7 +41,7 @@ export default defineManifest(async (env) => ({
|
|||||||
"128": "favicon-128x128.png"
|
"128": "favicon-128x128.png"
|
||||||
},
|
},
|
||||||
"action": {
|
"action": {
|
||||||
"default_popup": "popup.html",
|
// "default_popup": "popup.html",
|
||||||
"default_icon": {
|
"default_icon": {
|
||||||
"16": "favicon-16x16.png",
|
"16": "favicon-16x16.png",
|
||||||
"32": "favicon-32x32.png",
|
"32": "favicon-32x32.png",
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
import {v4} from 'uuid'
|
import {v4} from 'uuid'
|
||||||
import {handleTask, initTaskService, tasksMap} from './taskService'
|
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'
|
import ExtensionMessage from '@/messaging/ExtensionMessage'
|
||||||
|
|
||||||
const methods: {
|
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()
|
initTaskService()
|
||||||
|
@@ -5,6 +5,7 @@ export const MESSAGE_TARGET_APP = 'BilibiliAPP'
|
|||||||
export const MESSAGE_TO_EXTENSION_ADD_TASK = 'addTask'
|
export const MESSAGE_TO_EXTENSION_ADD_TASK = 'addTask'
|
||||||
export const MESSAGE_TO_EXTENSION_GET_TASK = 'getTask'
|
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_FOLD = 'fold'
|
||||||
export const MESSAGE_TO_INJECT_MOVE = 'move'
|
export const MESSAGE_TO_INJECT_MOVE = 'move'
|
||||||
export const MESSAGE_TO_INJECT_PLAY = 'play'
|
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 { 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 { 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'
|
import InjectMessage from '@/messaging/InjectMessage'
|
||||||
|
|
||||||
const debug = (...args: any[]) => {
|
const debug = (...args: any[]) => {
|
||||||
console.debug('[Inject]', ...args)
|
console.debug('[Inject]', ...args)
|
||||||
}
|
}
|
||||||
|
|
||||||
(function () {
|
(async function () {
|
||||||
// 如果路径不是/video或/list,则不注入
|
// 如果路径不是/video或/list,则不注入
|
||||||
if (!location.pathname.startsWith('/video') && !location.pathname.startsWith('/list')) {
|
if (!location.pathname.startsWith('/video') && !location.pathname.startsWith('/list')) {
|
||||||
debug('Not inject')
|
debug('Not inject')
|
||||||
return
|
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: {
|
const runtime: {
|
||||||
injectMessage: InjectMessage
|
injectMessage: InjectMessage
|
||||||
// lastV?: string | null
|
// lastV?: string | null
|
||||||
@@ -42,7 +56,7 @@ const debug = (...args: any[]) => {
|
|||||||
*/
|
*/
|
||||||
const refreshVideoElement = () => {
|
const refreshVideoElement = () => {
|
||||||
const newVideoElement = getVideoElement()
|
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) {
|
if (newVideoElement === runtime.videoElement && Math.abs(newVideoElementHeight - runtime.videoElementHeight) < 1) {
|
||||||
return false
|
return false
|
||||||
} else {
|
} else {
|
||||||
@@ -54,39 +68,50 @@ const debug = (...args: any[]) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const timerIframe = setInterval(function () {
|
const createIframe = () => {
|
||||||
var danmukuBox = document.getElementById('danmukuBox')
|
var danmukuBox = document.getElementById('danmukuBox')
|
||||||
if (danmukuBox) {
|
if (danmukuBox) {
|
||||||
clearInterval(timerIframe)
|
var vKey = ''
|
||||||
|
for (const key in danmukuBox?.dataset) {
|
||||||
//延迟插入iframe(插入太快,网络较差时容易出现b站网页刷新,原因暂时未知,可能b站的某种机制?)
|
if (key.startsWith('v-')) {
|
||||||
setTimeout(() => {
|
vKey = key
|
||||||
var vKey = ''
|
break
|
||||||
for (const key in danmukuBox?.dataset) {
|
|
||||||
if (key.startsWith('v-')) {
|
|
||||||
vKey = key
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const iframe = document.createElement('iframe')
|
const iframe = document.createElement('iframe')
|
||||||
iframe.id = IFRAME_ID
|
iframe.id = IFRAME_ID
|
||||||
iframe.src = chrome.runtime.getURL('index.html')
|
iframe.src = chrome.runtime.getURL('index.html')
|
||||||
iframe.style.border = 'none'
|
iframe.style.border = 'none'
|
||||||
iframe.style.width = '100%'
|
iframe.style.width = '100%'
|
||||||
iframe.style.height = '44px'
|
iframe.style.height = '44px'
|
||||||
iframe.style.marginBottom = '3px'
|
iframe.style.marginBottom = '3px'
|
||||||
iframe.allow = 'clipboard-read; clipboard-write;'
|
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')
|
if (vKey) {
|
||||||
}, 1500)
|
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 aid: number | null = null
|
||||||
let title = ''
|
let title = ''
|
||||||
@@ -204,6 +229,14 @@ const debug = (...args: any[]) => {
|
|||||||
const methods: {
|
const methods: {
|
||||||
[key: string]: (params: any, context: MethodContext) => Promise<any>
|
[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) => {
|
[MESSAGE_TO_INJECT_FOLD]: async (params) => {
|
||||||
runtime.fold = params.fold
|
runtime.fold = params.fold
|
||||||
updateIframeHeight()
|
updateIframeHeight()
|
||||||
@@ -249,7 +282,7 @@ const debug = (...args: any[]) => {
|
|||||||
|
|
||||||
let text = document.getElementById('trans-result-text')
|
let text = document.getElementById('trans-result-text')
|
||||||
if (text) {
|
if (text) {
|
||||||
text.innerHTML = runtime.curTrans??''
|
text.innerHTML = runtime.curTrans ?? ''
|
||||||
} else {
|
} else {
|
||||||
const container = document.getElementsByClassName('bpx-player-subtitle-panel-wrap')?.[0]
|
const container = document.getElementsByClassName('bpx-player-subtitle-panel-wrap')?.[0]
|
||||||
if (container) {
|
if (container) {
|
||||||
@@ -259,7 +292,7 @@ const debug = (...args: any[]) => {
|
|||||||
div.style.margin = '2px'
|
div.style.margin = '2px'
|
||||||
text = document.createElement('text')
|
text = document.createElement('text')
|
||||||
text.id = 'trans-result-text'
|
text.id = 'trans-result-text'
|
||||||
text.innerHTML = runtime.curTrans??''
|
text.innerHTML = runtime.curTrans ?? ''
|
||||||
text.style.fontSize = '1rem'
|
text.style.fontSize = '1rem'
|
||||||
text.style.padding = '5px'
|
text.style.padding = '5px'
|
||||||
text.style.color = 'white'
|
text.style.color = 'white'
|
||||||
@@ -309,7 +342,10 @@ const debug = (...args: any[]) => {
|
|||||||
runtime.injectMessage.init(methods)
|
runtime.injectMessage.init(methods)
|
||||||
|
|
||||||
setInterval(() => {
|
setInterval(() => {
|
||||||
refreshVideoInfo().catch(console.error)
|
const iframe = document.getElementById(IFRAME_ID) as HTMLIFrameElement | undefined
|
||||||
refreshSubtitles()
|
if (iframe != null && iframe.style.display !== 'none') {
|
||||||
|
refreshVideoInfo().catch(console.error)
|
||||||
|
refreshSubtitles()
|
||||||
|
}
|
||||||
}, 1000)
|
}, 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) => {
|
broadcastMessage = async (ignoreTabIds: number[] | undefined | null, target: string, method: string, params?: any) => {
|
||||||
const tabs = await chrome.tabs.query({
|
const tabs = await chrome.tabs.query({
|
||||||
discarded: false,
|
discarded: false,
|
||||||
|
@@ -59,6 +59,7 @@ const FormItem = (props: {
|
|||||||
const OptionsPage = () => {
|
const OptionsPage = () => {
|
||||||
const dispatch = useAppDispatch()
|
const dispatch = useAppDispatch()
|
||||||
const envData = useAppSelector(state => state.env.envData)
|
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: autoExpandValue, onChange: setAutoExpandValue} = useEventChecked(envData.autoExpand)
|
||||||
// const {value: autoScrollValue, onChange: setAutoScrollValue} = useEventChecked(envData.autoScroll)
|
// const {value: autoScrollValue, onChange: setAutoScrollValue} = useEventChecked(envData.autoScroll)
|
||||||
const {value: translateEnableValue, onChange: setTranslateEnableValue} = useEventChecked(envData.translateEnable)
|
const {value: translateEnableValue, onChange: setTranslateEnableValue} = useEventChecked(envData.translateEnable)
|
||||||
@@ -111,6 +112,7 @@ const OptionsPage = () => {
|
|||||||
|
|
||||||
const onSave = useCallback(() => {
|
const onSave = useCallback(() => {
|
||||||
dispatch(setEnvData({
|
dispatch(setEnvData({
|
||||||
|
manualInsert: !autoInsertValue,
|
||||||
autoExpand: autoExpandValue,
|
autoExpand: autoExpandValue,
|
||||||
aiType: aiTypeValue,
|
aiType: aiTypeValue,
|
||||||
apiKey: apiKeyValue,
|
apiKey: apiKeyValue,
|
||||||
@@ -140,7 +142,7 @@ const OptionsPage = () => {
|
|||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
window.close()
|
window.close()
|
||||||
}, 3000)
|
}, 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(() => {
|
const onCancel = useCallback(() => {
|
||||||
window.close()
|
window.close()
|
||||||
@@ -197,6 +199,10 @@ const OptionsPage = () => {
|
|||||||
return <div className='flex justify-center'>
|
return <div className='flex justify-center'>
|
||||||
<div className="w-2/3 max-w-[600px] flex flex-col gap-3 p-2">
|
<div className="w-2/3 max-w-[600px] flex flex-col gap-3 p-2">
|
||||||
<Section title='通用配置'>
|
<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='是否视频有字幕时自动展开字幕列表'>
|
<FormItem title='自动展开' htmlFor='autoExpand' tip='是否视频有字幕时自动展开字幕列表'>
|
||||||
<input id='autoExpand' type='checkbox' className='toggle toggle-primary' checked={autoExpandValue}
|
<input id='autoExpand' type='checkbox' className='toggle toggle-primary' checked={autoExpandValue}
|
||||||
onChange={setAutoExpandValue}/>
|
onChange={setAutoExpandValue}/>
|
||||||
|
1
src/typings.d.ts
vendored
1
src/typings.d.ts
vendored
@@ -22,6 +22,7 @@ interface MethodContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface EnvData {
|
interface EnvData {
|
||||||
|
manualInsert?: boolean //是否手动插入字幕列表
|
||||||
autoExpand?: boolean
|
autoExpand?: boolean
|
||||||
flagDot?: boolean
|
flagDot?: boolean
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user