You've already forked bilibili-subtitle
消息通信优化
This commit is contained in:
29
src/App.tsx
29
src/App.tsx
@@ -1,12 +1,12 @@
|
|||||||
import React, {useCallback, useContext, useEffect, useMemo} from 'react'
|
import React, {useCallback, useContext, useEffect, useMemo} from 'react'
|
||||||
import 'tippy.js/dist/tippy.css'
|
import 'tippy.js/dist/tippy.css'
|
||||||
import {useAppDispatch, useAppSelector} from './hooks/redux'
|
import {useAppDispatch, useAppSelector} from './hooks/redux'
|
||||||
import {setEnvData, setEnvReady, setFold, setPage, setTempData, setTempReady} from './redux/envReducer'
|
import {setCurFetched, setCurInfo, setData, setEnvData, setEnvReady, setFold, setInfos, setPage, setTempData, setTempReady, setTitle, setUrl} from './redux/envReducer'
|
||||||
import Header from './biz/Header'
|
import Header from './biz/Header'
|
||||||
import Body from './biz/Body'
|
import Body from './biz/Body'
|
||||||
import useSubtitleService from './hooks/useSubtitleService'
|
import useSubtitleService from './hooks/useSubtitleService'
|
||||||
import {cloneDeep} from 'lodash-es'
|
import {cloneDeep} from 'lodash-es'
|
||||||
import {EVENT_EXPAND, MESSAGE_TO_INJECT_FOLD, PAGE_MAIN, PAGE_SETTINGS, STORAGE_ENV, STORAGE_TEMP} from './const'
|
import {EVENT_EXPAND, MESSAGE_TO_APP_SET_INFOS, MESSAGE_TO_APP_SET_VIDEO_INFO, MESSAGE_TO_INJECT_FOLD, PAGE_MAIN, PAGE_SETTINGS, STORAGE_ENV, STORAGE_TEMP} from './const'
|
||||||
import {EventBusContext} from './Router'
|
import {EventBusContext} from './Router'
|
||||||
import useTranslateService from './hooks/useTranslateService'
|
import useTranslateService from './hooks/useTranslateService'
|
||||||
import Settings from './biz/Settings'
|
import Settings from './biz/Settings'
|
||||||
@@ -14,9 +14,9 @@ import {handleJson} from '@kky002/kky-util'
|
|||||||
import {useLocalStorage} from '@kky002/kky-hooks'
|
import {useLocalStorage} from '@kky002/kky-hooks'
|
||||||
import {Toaster} from 'react-hot-toast'
|
import {Toaster} from 'react-hot-toast'
|
||||||
import {setTheme} from './util/biz_util'
|
import {setTheme} from './util/biz_util'
|
||||||
import {sendInject} from './util/biz_util'
|
|
||||||
import useSearchService from './hooks/useSearchService'
|
import useSearchService from './hooks/useSearchService'
|
||||||
import useMessageService from './hooks/useMessageService'
|
import useMessageService from './messaging/useMessageService'
|
||||||
|
import useMessage from './messaging/useMessage'
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
const dispatch = useAppDispatch()
|
const dispatch = useAppDispatch()
|
||||||
@@ -26,6 +26,7 @@ function App() {
|
|||||||
const eventBus = useContext(EventBusContext)
|
const eventBus = useContext(EventBusContext)
|
||||||
const page = useAppSelector(state => state.env.page)
|
const page = useAppSelector(state => state.env.page)
|
||||||
const totalHeight = useAppSelector(state => state.env.totalHeight)
|
const totalHeight = useAppSelector(state => state.env.totalHeight)
|
||||||
|
const {sendInject} = useMessage()
|
||||||
|
|
||||||
const foldCallback = useCallback(() => {
|
const foldCallback = useCallback(() => {
|
||||||
dispatch(setFold(!fold))
|
dispatch(setFold(!fold))
|
||||||
@@ -71,11 +72,29 @@ function App() {
|
|||||||
setTheme(envData.theme)
|
setTheme(envData.theme)
|
||||||
}, [envData.theme])
|
}, [envData.theme])
|
||||||
|
|
||||||
|
//methods
|
||||||
|
const methods = useMemo(() => ({
|
||||||
|
[MESSAGE_TO_APP_SET_INFOS]: (params: any, from: string, context: MethodContext) => {
|
||||||
|
dispatch(setInfos(params.infos))
|
||||||
|
dispatch(setCurInfo(undefined))
|
||||||
|
dispatch(setCurFetched(false))
|
||||||
|
dispatch(setData(undefined))
|
||||||
|
return true
|
||||||
|
},
|
||||||
|
[MESSAGE_TO_APP_SET_VIDEO_INFO]: (params: any, from: string, context: MethodContext) => {
|
||||||
|
dispatch(setInfos(params.infos))
|
||||||
|
dispatch(setUrl(params.url))
|
||||||
|
dispatch(setTitle(params.title))
|
||||||
|
console.debug('video title: ', params.title)
|
||||||
|
return true
|
||||||
|
},
|
||||||
|
}), [dispatch])
|
||||||
|
|
||||||
// services
|
// services
|
||||||
useSubtitleService()
|
useSubtitleService()
|
||||||
useTranslateService()
|
useTranslateService()
|
||||||
useSearchService()
|
useSearchService()
|
||||||
useMessageService()
|
useMessageService(methods)
|
||||||
|
|
||||||
return <div className='select-none w-full' style={{
|
return <div className='select-none w-full' style={{
|
||||||
height: fold?undefined:`${totalHeight}px`,
|
height: fold?undefined:`${totalHeight}px`,
|
||||||
|
@@ -17,8 +17,8 @@ import {EVENT_EXPAND, MESSAGE_TO_INJECT_DOWNLOAD_AUDIO, PAGE_SETTINGS} from '../
|
|||||||
import {formatSrtTime, formatTime, formatVttTime} from '../util/util'
|
import {formatSrtTime, formatTime, formatVttTime} from '../util/util'
|
||||||
import {downloadText, openUrl} from '@kky002/kky-util'
|
import {downloadText, openUrl} from '@kky002/kky-util'
|
||||||
import toast from 'react-hot-toast'
|
import toast from 'react-hot-toast'
|
||||||
import {getSummarize, sendInject} from '../util/biz_util'
|
import {getSummarize} from '../util/biz_util'
|
||||||
|
import useMessage from '../messaging/useMessage'
|
||||||
interface Props {
|
interface Props {
|
||||||
placement: Placement
|
placement: Placement
|
||||||
}
|
}
|
||||||
@@ -70,6 +70,8 @@ const MoreBtn = (props: Props) => {
|
|||||||
const title = useAppSelector(state => state.env.title)
|
const title = useAppSelector(state => state.env.title)
|
||||||
const curSummaryType = useAppSelector(state => state.env.tempData.curSummaryType)
|
const curSummaryType = useAppSelector(state => state.env.tempData.curSummaryType)
|
||||||
|
|
||||||
|
const {sendInject} = useMessage()
|
||||||
|
|
||||||
const downloadCallback = useCallback((download: boolean) => {
|
const downloadCallback = useCallback((download: boolean) => {
|
||||||
if (data == null) {
|
if (data == null) {
|
||||||
return
|
return
|
||||||
|
@@ -1,10 +1,7 @@
|
|||||||
import {v4} from 'uuid'
|
import {v4} from 'uuid'
|
||||||
import {handleTask, initTaskService, tasksMap} from './taskService'
|
import {handleTask, initTaskService, tasksMap} from './taskService'
|
||||||
import {MESSAGE_TARGET_EXTENSION, MESSAGE_TO_EXTENSION_ADD_TASK, MESSAGE_TO_EXTENSION_GET_TASK} from '@/const'
|
import {MESSAGE_TO_EXTENSION_ADD_TASK, MESSAGE_TO_EXTENSION_GET_TASK} from '@/const'
|
||||||
|
import ExtensionMessage from '@/messaging/ExtensionMessage'
|
||||||
const debug = (...args: any[]) => {
|
|
||||||
console.debug('[Extension]', ...args)
|
|
||||||
}
|
|
||||||
|
|
||||||
const methods: {
|
const methods: {
|
||||||
[key: string]: (params: any, context: MethodContext) => Promise<any>
|
[key: string]: (params: any, context: MethodContext) => Promise<any>
|
||||||
@@ -46,12 +43,12 @@ const methods: {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
// 初始化backgroundMessage
|
||||||
|
const extensionMessage = new ExtensionMessage()
|
||||||
|
extensionMessage.init(methods)
|
||||||
|
|
||||||
/**
|
|
||||||
* Note: Return true when sending a response asynchronously.
|
|
||||||
*/
|
|
||||||
chrome.runtime.onMessage.addListener((event: MessageData, sender: chrome.runtime.MessageSender, sendResponse: (result: any) => void) => {
|
chrome.runtime.onMessage.addListener((event: MessageData, sender: chrome.runtime.MessageSender, sendResponse: (result: any) => void) => {
|
||||||
debug((sender.tab != null) ? `tab ${sender.tab.url ?? ''} => ` : 'extension => ', event)
|
// debug((sender.tab != null) ? `tab ${sender.tab.url ?? ''} => ` : 'extension => ', event)
|
||||||
|
|
||||||
// legacy
|
// legacy
|
||||||
if (event.type === 'syncGet') { // sync.get
|
if (event.type === 'syncGet') { // sync.get
|
||||||
@@ -66,44 +63,6 @@ chrome.runtime.onMessage.addListener((event: MessageData, sender: chrome.runtime
|
|||||||
chrome.storage.sync.remove(event.keys).catch(console.error)
|
chrome.storage.sync.remove(event.keys).catch(console.error)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// check event target
|
|
||||||
if (event.target !== MESSAGE_TARGET_EXTENSION) return
|
|
||||||
|
|
||||||
const method = methods[event.method]
|
|
||||||
if (method != null) {
|
|
||||||
method(event.params, {
|
|
||||||
event,
|
|
||||||
sender,
|
|
||||||
}).then(data => sendResponse({
|
|
||||||
success: true,
|
|
||||||
code: 200,
|
|
||||||
data,
|
|
||||||
})).catch(err => {
|
|
||||||
console.error(err)
|
|
||||||
let message
|
|
||||||
if (err instanceof Error) {
|
|
||||||
message = err.message
|
|
||||||
} else if (typeof err === 'string') {
|
|
||||||
message = err
|
|
||||||
} else {
|
|
||||||
message = 'error: ' + JSON.stringify(err)
|
|
||||||
}
|
|
||||||
sendResponse({
|
|
||||||
success: false,
|
|
||||||
code: 500,
|
|
||||||
message,
|
|
||||||
})
|
|
||||||
})
|
|
||||||
return true
|
|
||||||
} else {
|
|
||||||
console.error('Unknown method:', event.method)
|
|
||||||
sendResponse({
|
|
||||||
success: false,
|
|
||||||
code: 501,
|
|
||||||
message: 'Unknown method: ' + event.method,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
initTaskService()
|
initTaskService()
|
||||||
|
@@ -1,14 +0,0 @@
|
|||||||
export const broadcastMessage = async (ignoreTabIds: number[] | undefined | null, target: string, method: string, params?: any) => {
|
|
||||||
const tabs = await chrome.tabs.query({
|
|
||||||
discarded: false,
|
|
||||||
})
|
|
||||||
for (const tab of tabs) {
|
|
||||||
try {
|
|
||||||
if (tab.id && ((ignoreTabIds == null) || !ignoreTabIds.includes(tab.id))) {
|
|
||||||
await chrome.tabs.sendMessage(tab.id, {target, method, params})
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
console.error('send message to tab error', tab.id, e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,14 +1,14 @@
|
|||||||
import {useAppDispatch, useAppSelector} from './redux'
|
import {useAppDispatch, useAppSelector} from './redux'
|
||||||
import React, {useCallback} from 'react'
|
import React, {useCallback} from 'react'
|
||||||
import {setNeedScroll, setReviewAction, setTempData} from '../redux/envReducer'
|
import {setNeedScroll, setReviewAction, setTempData} from '../redux/envReducer'
|
||||||
import {sendInject} from '../util/biz_util'
|
|
||||||
import {MESSAGE_TO_INJECT_MOVE} from '../const'
|
import {MESSAGE_TO_INJECT_MOVE} from '../const'
|
||||||
|
import useMessage from '../messaging/useMessage'
|
||||||
const useSubtitle = () => {
|
const useSubtitle = () => {
|
||||||
const dispatch = useAppDispatch()
|
const dispatch = useAppDispatch()
|
||||||
const reviewed = useAppSelector(state => state.env.tempData.reviewed)
|
const reviewed = useAppSelector(state => state.env.tempData.reviewed)
|
||||||
const reviewAction = useAppSelector(state => state.env.reviewAction)
|
const reviewAction = useAppSelector(state => state.env.reviewAction)
|
||||||
const reviewActions = useAppSelector(state => state.env.tempData.reviewActions)
|
const reviewActions = useAppSelector(state => state.env.tempData.reviewActions)
|
||||||
|
const {sendInject} = useMessage()
|
||||||
|
|
||||||
const move = useCallback((time: number, togglePause: boolean) => {
|
const move = useCallback((time: number, togglePause: boolean) => {
|
||||||
sendInject(MESSAGE_TO_INJECT_MOVE, {time, togglePause})
|
sendInject(MESSAGE_TO_INJECT_MOVE, {time, togglePause})
|
||||||
|
@@ -19,8 +19,8 @@ import {EventBusContext} from '../Router'
|
|||||||
import {EVENT_EXPAND, GEMINI_TOKENS, TOTAL_HEIGHT_MAX, TOTAL_HEIGHT_MIN, WORDS_MIN, WORDS_RATE, MESSAGE_TO_INJECT_GET_VIDEO_STATUS, MESSAGE_TO_INJECT_GET_VIDEO_ELEMENT_INFO, MESSAGE_TO_INJECT_REFRESH_VIDEO_INFO, MESSAGE_TO_INJECT_HIDE_TRANS, MESSAGE_TO_INJECT_UPDATETRANSRESULT} from '../const'
|
import {EVENT_EXPAND, GEMINI_TOKENS, TOTAL_HEIGHT_MAX, TOTAL_HEIGHT_MIN, WORDS_MIN, WORDS_RATE, MESSAGE_TO_INJECT_GET_VIDEO_STATUS, MESSAGE_TO_INJECT_GET_VIDEO_ELEMENT_INFO, MESSAGE_TO_INJECT_REFRESH_VIDEO_INFO, MESSAGE_TO_INJECT_HIDE_TRANS, MESSAGE_TO_INJECT_UPDATETRANSRESULT} from '../const'
|
||||||
import {useInterval} from 'ahooks'
|
import {useInterval} from 'ahooks'
|
||||||
import {getModelMaxTokens, getWholeText} from '../util/biz_util'
|
import {getModelMaxTokens, getWholeText} from '../util/biz_util'
|
||||||
import {sendInject} from '../util/biz_util'
|
|
||||||
import {MESSAGE_TO_INJECT_GET_SUBTITLE} from '../const'
|
import {MESSAGE_TO_INJECT_GET_SUBTITLE} from '../const'
|
||||||
|
import useMessage from '../messaging/useMessage'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Service是单例,类似后端的服务概念
|
* Service是单例,类似后端的服务概念
|
||||||
@@ -44,6 +44,7 @@ const useSubtitleService = () => {
|
|||||||
const autoTranslate = useAppSelector(state => state.env.autoTranslate)
|
const autoTranslate = useAppSelector(state => state.env.autoTranslate)
|
||||||
const reviewed = useAppSelector(state => state.env.tempData.reviewed)
|
const reviewed = useAppSelector(state => state.env.tempData.reviewed)
|
||||||
const reviewActions = useAppSelector(state => state.env.tempData.reviewActions)
|
const reviewActions = useAppSelector(state => state.env.tempData.reviewActions)
|
||||||
|
const {sendInject} = useMessage()
|
||||||
|
|
||||||
//如果reviewActions达到15次,则设置reviewed为false
|
//如果reviewActions达到15次,则设置reviewed为false
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
@@ -29,9 +29,9 @@ import {
|
|||||||
} from '../const'
|
} from '../const'
|
||||||
import toast from 'react-hot-toast'
|
import toast from 'react-hot-toast'
|
||||||
import {useMemoizedFn} from 'ahooks/es'
|
import {useMemoizedFn} from 'ahooks/es'
|
||||||
import {extractJsonArray, extractJsonObject, getModel, sendExtension} from '../util/biz_util'
|
import {extractJsonArray, extractJsonObject, getModel} from '../util/biz_util'
|
||||||
import {formatTime} from '../util/util'
|
import {formatTime} from '../util/util'
|
||||||
|
import useMessage from '@/messaging/useMessage'
|
||||||
const useTranslate = () => {
|
const useTranslate = () => {
|
||||||
const dispatch = useAppDispatch()
|
const dispatch = useAppDispatch()
|
||||||
const data = useAppSelector(state => state.env.data)
|
const data = useAppSelector(state => state.env.data)
|
||||||
@@ -45,7 +45,7 @@ const useTranslate = () => {
|
|||||||
const reviewed = useAppSelector(state => state.env.tempData.reviewed)
|
const reviewed = useAppSelector(state => state.env.tempData.reviewed)
|
||||||
const reviewAction = useAppSelector(state => state.env.reviewAction)
|
const reviewAction = useAppSelector(state => state.env.reviewAction)
|
||||||
const reviewActions = useAppSelector(state => state.env.tempData.reviewActions)
|
const reviewActions = useAppSelector(state => state.env.tempData.reviewActions)
|
||||||
|
const {sendExtension} = useMessage()
|
||||||
/**
|
/**
|
||||||
* 获取下一个需要翻译的行
|
* 获取下一个需要翻译的行
|
||||||
* 会检测冷却
|
* 会检测冷却
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
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 } from '@/const'
|
||||||
import { PostMessagePayload, PostMessageResponse, startListening } from 'postmessage-promise'
|
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_TARGET_EXTENSION, 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[]) => {
|
const debug = (...args: any[]) => {
|
||||||
console.debug('[Inject]', ...args)
|
console.debug('[Inject]', ...args)
|
||||||
@@ -14,7 +14,7 @@ const debug = (...args: any[]) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const runtime: {
|
const runtime: {
|
||||||
postMessageToApp?: (method: string, payload: PostMessagePayload) => Promise<PostMessageResponse>
|
injectMessage: InjectMessage
|
||||||
// lastV?: string | null
|
// lastV?: string | null
|
||||||
// lastVideoInfo?: VideoInfo
|
// lastVideoInfo?: VideoInfo
|
||||||
|
|
||||||
@@ -26,40 +26,12 @@ const debug = (...args: any[]) => {
|
|||||||
showTrans: boolean
|
showTrans: boolean
|
||||||
curTrans?: string
|
curTrans?: string
|
||||||
} = {
|
} = {
|
||||||
|
injectMessage: new InjectMessage(),
|
||||||
fold: true,
|
fold: true,
|
||||||
videoElementHeight: TOTAL_HEIGHT_DEF,
|
videoElementHeight: TOTAL_HEIGHT_DEF,
|
||||||
showTrans: false,
|
showTrans: false,
|
||||||
}
|
}
|
||||||
|
|
||||||
const sendExtension = async <T = any>(method: string, params?: any) => {
|
|
||||||
return await chrome.runtime.sendMessage<MessageData, MessageResult>({
|
|
||||||
target: MESSAGE_TARGET_EXTENSION,
|
|
||||||
method,
|
|
||||||
params: params??{},
|
|
||||||
}).then((messageResult) => {
|
|
||||||
if (messageResult.success) {
|
|
||||||
return messageResult.data as T
|
|
||||||
} else {
|
|
||||||
throw new Error(messageResult.message)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const sendApp = async <T>(method: string, params: any) => {
|
|
||||||
if (runtime.postMessageToApp != null) {
|
|
||||||
const messageResult = await runtime.postMessageToApp(method, params) as MessageResult | undefined
|
|
||||||
if (messageResult != null) {
|
|
||||||
if (messageResult.success) {
|
|
||||||
return messageResult.data as T
|
|
||||||
} else {
|
|
||||||
throw new Error(messageResult.message)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
throw new Error('no response')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const getVideoElement = () => {
|
const getVideoElement = () => {
|
||||||
const videoWrapper = document.getElementById('bilibili-player')
|
const videoWrapper = document.getElementById('bilibili-player')
|
||||||
return videoWrapper?.querySelector('video') as HTMLVideoElement | undefined
|
return videoWrapper?.querySelector('video') as HTMLVideoElement | undefined
|
||||||
@@ -179,7 +151,7 @@ const debug = (...args: any[]) => {
|
|||||||
debug('refreshVideoInfo: ', aid, cid, pages, subtitles)
|
debug('refreshVideoInfo: ', aid, cid, pages, subtitles)
|
||||||
|
|
||||||
//send setVideoInfo
|
//send setVideoInfo
|
||||||
sendApp(MESSAGE_TO_APP_SET_VIDEO_INFO, {
|
runtime.injectMessage.sendApp(MESSAGE_TO_APP_SET_VIDEO_INFO, {
|
||||||
url: location.origin + location.pathname,
|
url: location.origin + location.pathname,
|
||||||
title,
|
title,
|
||||||
aid,
|
aid,
|
||||||
@@ -214,7 +186,7 @@ const debug = (...args: any[]) => {
|
|||||||
.then(res => res.json())
|
.then(res => res.json())
|
||||||
.then(res => {
|
.then(res => {
|
||||||
// console.log('refreshSubtitles: ', aid, cid, res)
|
// console.log('refreshSubtitles: ', aid, cid, res)
|
||||||
sendApp(MESSAGE_TO_APP_SET_INFOS, {
|
runtime.injectMessage.sendApp(MESSAGE_TO_APP_SET_INFOS, {
|
||||||
infos: res.data.subtitle.subtitles
|
infos: res.data.subtitle.subtitles
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@@ -333,73 +305,8 @@ const debug = (...args: any[]) => {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// 初始化injectMessage
|
||||||
* @param sendResponse No matter what is returned, this method will definitely be called.
|
runtime.injectMessage.init(methods)
|
||||||
*/
|
|
||||||
const messageHandler = (event: MessageData, sender: chrome.runtime.MessageSender | null, sendResponse: (response?: MessageResult) => void) => {
|
|
||||||
const source = sender != null?((sender.tab != null) ? `tab ${sender.tab.url ?? ''}` : 'extension'):'app'
|
|
||||||
debug(`${source} => `, JSON.stringify(event))
|
|
||||||
|
|
||||||
// check event target
|
|
||||||
if (event.target !== MESSAGE_TARGET_INJECT) return
|
|
||||||
|
|
||||||
const method = methods[event.method]
|
|
||||||
if (method != null) {
|
|
||||||
method(event.params, {
|
|
||||||
event,
|
|
||||||
sender,
|
|
||||||
}).then(data => {
|
|
||||||
// debug(`${source} <= `, event.method, JSON.stringify(data))
|
|
||||||
return data
|
|
||||||
}).then(data => sendResponse({
|
|
||||||
success: true,
|
|
||||||
code: 200,
|
|
||||||
data,
|
|
||||||
})).catch(err => {
|
|
||||||
console.error(err)
|
|
||||||
let message
|
|
||||||
if (err instanceof Error) {
|
|
||||||
message = err.message
|
|
||||||
} else if (typeof err === 'string') {
|
|
||||||
message = err
|
|
||||||
} else {
|
|
||||||
message = 'error: ' + JSON.stringify(err)
|
|
||||||
}
|
|
||||||
sendResponse({
|
|
||||||
success: false,
|
|
||||||
code: 500,
|
|
||||||
message,
|
|
||||||
})
|
|
||||||
})
|
|
||||||
return true
|
|
||||||
} else {
|
|
||||||
console.error('Unknown method:', event.method)
|
|
||||||
sendResponse({
|
|
||||||
success: false,
|
|
||||||
code: 501,
|
|
||||||
message: 'Unknown method: ' + event.method,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// listen message from app
|
|
||||||
startListening({}).then(e => {
|
|
||||||
const { postMessage, listenMessage, destroy } = e
|
|
||||||
runtime.postMessageToApp = postMessage
|
|
||||||
listenMessage((method, params, sendResponse) => {
|
|
||||||
messageHandler({
|
|
||||||
target: MESSAGE_TARGET_INJECT,
|
|
||||||
method,
|
|
||||||
params,
|
|
||||||
}, null, sendResponse)
|
|
||||||
})
|
|
||||||
}).catch(console.error)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* listen message from extension
|
|
||||||
* Attention: return true if you need to sendResponse asynchronously
|
|
||||||
*/
|
|
||||||
chrome.runtime.onMessage.addListener(messageHandler)
|
|
||||||
|
|
||||||
setInterval(() => {
|
setInterval(() => {
|
||||||
refreshVideoInfo().catch(console.error)
|
refreshVideoInfo().catch(console.error)
|
||||||
|
79
src/messaging/ExtensionMessage.ts
Normal file
79
src/messaging/ExtensionMessage.ts
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
import { MESSAGE_TARGET_EXTENSION } from '@/const'
|
||||||
|
|
||||||
|
class ExtensionMessage {
|
||||||
|
methods?: {
|
||||||
|
[key: string]: (params: any, context: MethodContext) => Promise<any>
|
||||||
|
}
|
||||||
|
|
||||||
|
debug = (...args: any[]) => {
|
||||||
|
console.debug('[Extension Messaging]', ...args)
|
||||||
|
}
|
||||||
|
|
||||||
|
init = (methods: {
|
||||||
|
[key: string]: (params: any, context: MethodContext) => Promise<any>
|
||||||
|
}) => {
|
||||||
|
this.methods = methods
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Note: Return true when sending a response asynchronously.
|
||||||
|
*/
|
||||||
|
chrome.runtime.onMessage.addListener((event: MessageData, sender: chrome.runtime.MessageSender, sendResponse: (result: any) => void) => {
|
||||||
|
this.debug((sender.tab != null) ? `tab ${sender.tab.url ?? ''} => ` : 'extension => ', event)
|
||||||
|
|
||||||
|
// check event target
|
||||||
|
if (event.target !== MESSAGE_TARGET_EXTENSION) return
|
||||||
|
|
||||||
|
const method = this.methods?.[event.method]
|
||||||
|
if (method != null) {
|
||||||
|
method(event.params, {
|
||||||
|
event,
|
||||||
|
sender,
|
||||||
|
}).then(data => sendResponse({
|
||||||
|
success: true,
|
||||||
|
code: 200,
|
||||||
|
data,
|
||||||
|
})).catch(err => {
|
||||||
|
console.error(err)
|
||||||
|
let message
|
||||||
|
if (err instanceof Error) {
|
||||||
|
message = err.message
|
||||||
|
} else if (typeof err === 'string') {
|
||||||
|
message = err
|
||||||
|
} else {
|
||||||
|
message = 'error: ' + JSON.stringify(err)
|
||||||
|
}
|
||||||
|
sendResponse({
|
||||||
|
success: false,
|
||||||
|
code: 500,
|
||||||
|
message,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
console.error('Unknown method:', event.method)
|
||||||
|
sendResponse({
|
||||||
|
success: false,
|
||||||
|
code: 501,
|
||||||
|
message: 'Unknown method: ' + event.method,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
broadcastMessage = async (ignoreTabIds: number[] | undefined | null, target: string, method: string, params?: any) => {
|
||||||
|
const tabs = await chrome.tabs.query({
|
||||||
|
discarded: false,
|
||||||
|
})
|
||||||
|
for (const tab of tabs) {
|
||||||
|
try {
|
||||||
|
if (tab.id && ((ignoreTabIds == null) || !ignoreTabIds.includes(tab.id))) {
|
||||||
|
await chrome.tabs.sendMessage(tab.id, {target, method, params})
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error('send message to tab error', tab.id, e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ExtensionMessage
|
121
src/messaging/InjectMessage.ts
Normal file
121
src/messaging/InjectMessage.ts
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
import { MESSAGE_TARGET_EXTENSION, MESSAGE_TARGET_INJECT } from '@/const'
|
||||||
|
import { PostMessagePayload, PostMessageResponse, startListening } from 'postmessage-promise'
|
||||||
|
|
||||||
|
class InjectMessage {
|
||||||
|
//类实例
|
||||||
|
methods?: {
|
||||||
|
[key: string]: (params: any, context: MethodContext) => Promise<any>
|
||||||
|
}
|
||||||
|
postMessageToApp?: (method: string, payload: PostMessagePayload) => Promise<PostMessageResponse>
|
||||||
|
|
||||||
|
debug = (...args: any[]) => {
|
||||||
|
console.debug('[Inject Messaging]', ...args)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param sendResponse No matter what is returned, this method will definitely be called.
|
||||||
|
*/
|
||||||
|
messageHandler = (event: MessageData, sender: chrome.runtime.MessageSender | null, sendResponse: (response?: MessageResult) => void) => {
|
||||||
|
const source = sender != null ? ((sender.tab != null) ? `tab ${sender.tab.url ?? ''}` : 'extension') : 'app'
|
||||||
|
this.debug(`${source} => `, JSON.stringify(event))
|
||||||
|
|
||||||
|
// check event target
|
||||||
|
if (event.target !== MESSAGE_TARGET_INJECT) return
|
||||||
|
|
||||||
|
const method = this.methods?.[event.method]
|
||||||
|
if (method != null) {
|
||||||
|
method(event.params, {
|
||||||
|
event,
|
||||||
|
sender,
|
||||||
|
}).then(data => {
|
||||||
|
// debug(`${source} <= `, event.method, JSON.stringify(data))
|
||||||
|
return data
|
||||||
|
}).then(data => sendResponse({
|
||||||
|
success: true,
|
||||||
|
code: 200,
|
||||||
|
data,
|
||||||
|
})).catch(err => {
|
||||||
|
console.error(err)
|
||||||
|
let message
|
||||||
|
if (err instanceof Error) {
|
||||||
|
message = err.message
|
||||||
|
} else if (typeof err === 'string') {
|
||||||
|
message = err
|
||||||
|
} else {
|
||||||
|
message = 'error: ' + JSON.stringify(err)
|
||||||
|
}
|
||||||
|
sendResponse({
|
||||||
|
success: false,
|
||||||
|
code: 500,
|
||||||
|
message,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
console.error('Unknown method:', event.method)
|
||||||
|
sendResponse({
|
||||||
|
success: false,
|
||||||
|
code: 501,
|
||||||
|
message: 'Unknown method: ' + event.method,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
init(methods: {
|
||||||
|
[key: string]: (params: any, context: MethodContext) => Promise<any>
|
||||||
|
}) {
|
||||||
|
this.methods = methods
|
||||||
|
// listen message from app
|
||||||
|
startListening({}).then(e => {
|
||||||
|
const { postMessage, listenMessage, destroy } = e
|
||||||
|
this.postMessageToApp = postMessage
|
||||||
|
listenMessage((method, params, sendResponse) => {
|
||||||
|
this.messageHandler({
|
||||||
|
target: MESSAGE_TARGET_INJECT,
|
||||||
|
method,
|
||||||
|
params,
|
||||||
|
}, null, sendResponse)
|
||||||
|
})
|
||||||
|
}).catch(console.error)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* listen message from extension
|
||||||
|
* Attention: return true if you need to sendResponse asynchronously
|
||||||
|
*/
|
||||||
|
chrome.runtime.onMessage.addListener(this.messageHandler)
|
||||||
|
}
|
||||||
|
|
||||||
|
sendExtension = async <T = any>(method: string, params?: any): Promise<T> => {
|
||||||
|
return await chrome.runtime.sendMessage<MessageData, MessageResult>({
|
||||||
|
target: MESSAGE_TARGET_EXTENSION,
|
||||||
|
method,
|
||||||
|
params: params ?? {},
|
||||||
|
}).then((messageResult) => {
|
||||||
|
if (messageResult.success) {
|
||||||
|
return messageResult.data as T
|
||||||
|
} else {
|
||||||
|
throw new Error(messageResult.message)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
sendApp = async <T>(method: string, params: any): Promise<T> => {
|
||||||
|
if (this.postMessageToApp != null) {
|
||||||
|
const messageResult = await this.postMessageToApp(method, params) as MessageResult | undefined
|
||||||
|
if (messageResult != null) {
|
||||||
|
if (messageResult.success) {
|
||||||
|
return messageResult.data as T
|
||||||
|
} else {
|
||||||
|
throw new Error(messageResult.message)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new Error('no response')
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new Error('error: postMessageToApp is not initialized')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export default InjectMessage
|
42
src/messaging/useMessage.ts
Normal file
42
src/messaging/useMessage.ts
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
import { MESSAGE_TARGET_EXTENSION } from '@/const'
|
||||||
|
import { injectWaiter } from './useMessageService'
|
||||||
|
import { useCallback } from 'react'
|
||||||
|
|
||||||
|
const useMessage = () => {
|
||||||
|
const sendExtension = useCallback(async <T = any>(method: string, params?: any) => {
|
||||||
|
return await chrome.runtime.sendMessage<MessageData, MessageResult>({
|
||||||
|
target: MESSAGE_TARGET_EXTENSION,
|
||||||
|
method,
|
||||||
|
params: params ?? {},
|
||||||
|
}).then((messageResult) => {
|
||||||
|
if (messageResult.success) {
|
||||||
|
return messageResult.data as T
|
||||||
|
} else {
|
||||||
|
throw new Error(messageResult.message)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
const sendInject = useCallback(async <T = any>(method: string, params?: any) => {
|
||||||
|
// wait
|
||||||
|
const postInjectMessage = await injectWaiter.wait()
|
||||||
|
// send message
|
||||||
|
const messageResult = await postInjectMessage(method, params) as MessageResult | undefined
|
||||||
|
if (messageResult != null) {
|
||||||
|
if (messageResult.success) {
|
||||||
|
return messageResult.data as T
|
||||||
|
} else {
|
||||||
|
throw new Error(messageResult.message)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new Error('no response')
|
||||||
|
}
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
return {
|
||||||
|
sendExtension,
|
||||||
|
sendInject
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default useMessage
|
@@ -1,16 +1,15 @@
|
|||||||
import {useCallback, useContext, useEffect} from 'react'
|
import {useCallback, useEffect} from 'react'
|
||||||
import {
|
import {
|
||||||
MESSAGE_TARGET_APP,
|
MESSAGE_TARGET_APP,
|
||||||
MESSAGE_TARGET_EXTENSION,
|
MESSAGE_TARGET_EXTENSION,
|
||||||
MESSAGE_TARGET_INJECT,
|
MESSAGE_TARGET_INJECT,
|
||||||
MESSAGE_TO_APP_SET_INFOS,
|
|
||||||
MESSAGE_TO_APP_SET_VIDEO_INFO,
|
|
||||||
} from '@/const'
|
} from '@/const'
|
||||||
import {debug} from '@/util/biz_util'
|
|
||||||
import {callServer, PostMessagePayload, PostMessageResponse} from 'postmessage-promise'
|
import {callServer, PostMessagePayload, PostMessageResponse} from 'postmessage-promise'
|
||||||
import {useAppDispatch} from '../hooks/redux'
|
|
||||||
import {Waiter} from '@kky002/kky-util'
|
import {Waiter} from '@kky002/kky-util'
|
||||||
import {setInfos, setTitle, setUrl, setCurInfo, setCurFetched, setData} from '@/redux/envReducer'
|
|
||||||
|
const debug = (...args: any[]) => {
|
||||||
|
console.debug('[App Messaging]', ...args)
|
||||||
|
}
|
||||||
|
|
||||||
let postInjectMessage: (method: string, params: PostMessagePayload) => Promise<PostMessageResponse> | undefined
|
let postInjectMessage: (method: string, params: PostMessagePayload) => Promise<PostMessageResponse> | undefined
|
||||||
|
|
||||||
@@ -19,35 +18,21 @@ export const injectWaiter = new Waiter<typeof postInjectMessage>(() => ({
|
|||||||
data: postInjectMessage
|
data: postInjectMessage
|
||||||
}), 100, 15000)
|
}), 100, 15000)
|
||||||
|
|
||||||
const useMessageService = () => {
|
const useMessageService = (methods?: {
|
||||||
const dispatch = useAppDispatch()
|
[key: string]: (params: any, from: string, context: MethodContext) => boolean
|
||||||
const path = 'app' //useAppSelector(state => state.env.path)
|
}) => {
|
||||||
|
|
||||||
const messageHandler = useCallback((method: string, params: any, from: string, context: any): boolean => {
|
const messageHandler = useCallback((method: string, params: any, from: string, context: any): boolean => {
|
||||||
switch (method) {
|
const handler = methods?.[method]
|
||||||
case MESSAGE_TO_APP_SET_INFOS:
|
if (handler != null) {
|
||||||
dispatch(setInfos(params.infos))
|
return handler(params, from, context)
|
||||||
dispatch(setCurInfo(undefined))
|
}else {
|
||||||
dispatch(setCurFetched(false))
|
|
||||||
dispatch(setData(undefined))
|
|
||||||
break
|
|
||||||
case MESSAGE_TO_APP_SET_VIDEO_INFO:
|
|
||||||
dispatch(setInfos(params.infos))
|
|
||||||
dispatch(setUrl(params.url))
|
|
||||||
dispatch(setTitle(params.title))
|
|
||||||
console.debug('video title: ', params.title)
|
|
||||||
break
|
|
||||||
default:
|
|
||||||
debug('unknown message method: ', method)
|
debug('unknown message method: ', method)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return true
|
}, [methods])
|
||||||
}, [dispatch])
|
|
||||||
|
|
||||||
// connect to inject
|
// connect to inject
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (path !== 'app') return
|
|
||||||
|
|
||||||
let destroyFunc: (() => void) | undefined
|
let destroyFunc: (() => void) | undefined
|
||||||
|
|
||||||
const serverObject = {
|
const serverObject = {
|
||||||
@@ -76,7 +61,7 @@ const useMessageService = () => {
|
|||||||
return () => {
|
return () => {
|
||||||
destroyFunc?.()
|
destroyFunc?.()
|
||||||
}
|
}
|
||||||
}, [messageHandler, path])
|
}, [messageHandler])
|
||||||
|
|
||||||
const extensionMessageCallback = useCallback((event: MessageData, sender: chrome.runtime.MessageSender, sendResponse: (response?: any) => void) => {
|
const extensionMessageCallback = useCallback((event: MessageData, sender: chrome.runtime.MessageSender, sendResponse: (response?: any) => void) => {
|
||||||
debug((sender.tab != null) ? `tab ${sender.tab.url??''} => ` : 'extension => ', JSON.stringify(event))
|
debug((sender.tab != null) ? `tab ${sender.tab.url??''} => ` : 'extension => ', JSON.stringify(event))
|
@@ -2,43 +2,11 @@ import {APP_DOM_ID, CUSTOM_MODEL_TOKENS, MODEL_DEFAULT, MODEL_MAP, SUMMARIZE_TYP
|
|||||||
import {isDarkMode} from '@kky002/kky-util'
|
import {isDarkMode} from '@kky002/kky-util'
|
||||||
import toast from 'react-hot-toast'
|
import toast from 'react-hot-toast'
|
||||||
import {findIndex} from 'lodash-es'
|
import {findIndex} from 'lodash-es'
|
||||||
import {MESSAGE_TARGET_EXTENSION} from '../const'
|
|
||||||
import {injectWaiter} from '../hooks/useMessageService'
|
|
||||||
|
|
||||||
export const debug = (...args: any[]) => {
|
export const debug = (...args: any[]) => {
|
||||||
console.debug('[APP]', ...args)
|
console.debug('[APP]', ...args)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const sendExtension = async <T = any>(method: string, params?: any) => {
|
|
||||||
return await chrome.runtime.sendMessage<MessageData, MessageResult>({
|
|
||||||
target: MESSAGE_TARGET_EXTENSION,
|
|
||||||
method,
|
|
||||||
params: params??{},
|
|
||||||
}).then((messageResult) => {
|
|
||||||
if (messageResult.success) {
|
|
||||||
return messageResult.data as T
|
|
||||||
} else {
|
|
||||||
throw new Error(messageResult.message)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
export const sendInject = async <T = any>(method: string, params?: any) => {
|
|
||||||
// wait
|
|
||||||
const postInjectMessage = await injectWaiter.wait()
|
|
||||||
// send message
|
|
||||||
const messageResult = await postInjectMessage(method, params) as MessageResult | undefined
|
|
||||||
if (messageResult != null) {
|
|
||||||
if (messageResult.success) {
|
|
||||||
return messageResult.data as T
|
|
||||||
} else {
|
|
||||||
throw new Error(messageResult.message)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
throw new Error('no response')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取译文
|
* 获取译文
|
||||||
*/
|
*/
|
||||||
|
Reference in New Issue
Block a user