Files
bilibili-subtitle/src/hooks/useSubtitleService.ts
IndieKKY 6928b5fa00 fix
2024-10-06 19:54:40 +08:00

219 lines
7.0 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import {useAppDispatch, useAppSelector} from './redux'
import {useContext, useEffect} from 'react'
import {
setCurFetched,
setCurIdx,
setCurInfo,
setCurrentTime,
setData,
setInfos,
setNoVideo,
setSegmentFold,
setSegments,
setTitle,
setTotalHeight,
setUrl,
setTempData,
} from '../redux/envReducer'
import {EventBusContext} from '../Router'
import {EVENT_EXPAND, GEMINI_TOKENS, TOTAL_HEIGHT_MAX, TOTAL_HEIGHT_MIN, WORDS_MIN, WORDS_RATE} from '../consts/const'
import {useAsyncEffect, useInterval} from 'ahooks'
import {getModelMaxTokens, getWholeText} from '../utils/bizUtil'
import { useMessage } from './message'
/**
* Service是单例类似后端的服务概念
*/
const useSubtitleService = () => {
const dispatch = useAppDispatch()
const infos = useAppSelector(state => state.env.infos)
const curInfo = useAppSelector(state => state.env.curInfo)
const curFetched = useAppSelector(state => state.env.curFetched)
const fold = useAppSelector(state => state.env.fold)
const envReady = useAppSelector(state => state.env.envReady)
const envData = useAppSelector(state => state.env.envData)
const data = useAppSelector(state => state.env.data)
const currentTime = useAppSelector(state => state.env.currentTime)
const curIdx = useAppSelector(state => state.env.curIdx)
const eventBus = useContext(EventBusContext)
const needScroll = useAppSelector(state => state.env.needScroll)
const segments = useAppSelector(state => state.env.segments)
const transResults = useAppSelector(state => state.env.transResults)
const hideOnDisableAutoTranslate = useAppSelector(state => state.env.envData.hideOnDisableAutoTranslate)
const autoTranslate = useAppSelector(state => state.env.autoTranslate)
const reviewed = useAppSelector(state => state.env.tempData.reviewed)
const reviewActions = useAppSelector(state => state.env.tempData.reviewActions)
const {sendInject} = useMessage()
//如果reviewActions达到15次则设置reviewed为false
useEffect(() => {
if (reviewed === undefined && reviewActions && reviewActions >= 15) {
dispatch(setTempData({
reviewed: false
}))
}
}, [reviewActions, dispatch, reviewed])
// 有数据时自动展开
useEffect(() => {
if ((data != null) && data.body.length > 0) {
eventBus.emit({
type: EVENT_EXPAND
})
}
}, [data, eventBus, infos])
// 当前未展示 & (未折叠 | 自动展开) & 有列表 => 展示第一个
useEffect(() => {
let autoExpand = envData.autoExpand
// 如果显示在侧边栏,则自动展开
if (envData.sidePanel) {
autoExpand = true
}
if (!curInfo && (!fold || (envReady && autoExpand)) && (infos != null) && infos.length > 0) {
dispatch(setCurInfo(infos[0]))
dispatch(setCurFetched(false))
}
}, [curInfo, dispatch, envData.autoExpand, envReady, fold, infos])
// 获取
useEffect(() => {
if (curInfo && !curFetched) {
sendInject('GET_SUBTITLE', {info: curInfo}).then(data => {
data?.body?.forEach((item: TranscriptItem, idx: number) => {
item.idx = idx
})
// dispatch(setCurInfo(data.data.info))
dispatch(setCurFetched(true))
dispatch(setData(data))
console.log('subtitle', data)
})
}
}, [curFetched, curInfo])
useAsyncEffect(async () => {
// 初始获取列表
sendInject('REFRESH_VIDEO_INFO', {force: true})
}, [])
useAsyncEffect(async () => {
// 更新设置信息
sendInject('GET_VIDEO_ELEMENT_INFO', {}).then(info => {
dispatch(setNoVideo(info.noVideo))
if (envData.sidePanel) {
// get screen height
dispatch(setTotalHeight(window.innerHeight))
}else {
dispatch(setTotalHeight(Math.min(Math.max(info.totalHeight, TOTAL_HEIGHT_MIN), TOTAL_HEIGHT_MAX)))
}
})
}, [envData.sidePanel, infos])
// 更新当前位置
useEffect(() => {
let curIdx
if (((data?.body) != null) && currentTime) {
for (let i=0; i<data.body.length; i++) {
const item = data.body[i]
if (item.from && currentTime < item.from) {
break
} else {
curIdx = i
}
}
}
dispatch(setCurIdx(curIdx))
}, [currentTime, data?.body, dispatch])
// 需要滚动 => segment自动展开
useEffect(() => {
if (needScroll && curIdx != null) { // 需要滚动
for (const segment of segments??[]) { // 检测segments
if (segment.startIdx <= curIdx && curIdx <= segment.endIdx) { // 找到对应的segment
if (segment.fold) { // 需要展开
dispatch(setSegmentFold({
segmentStartIdx: segment.startIdx,
fold: false
}))
}
break
}
}
}
}, [curIdx, dispatch, needScroll, segments])
// data等变化时自动刷新segments
useEffect(() => {
let segments: Segment[] | undefined
const items = data?.body
if (items != null) {
if (envData.summarizeEnable) { // 分段
let size = envData.words
if (!size) { // 默认
if (envData.aiType === 'gemini') {
size = GEMINI_TOKENS*WORDS_RATE
} else {
size = getModelMaxTokens(envData)*WORDS_RATE
}
}
size = Math.max(size, WORDS_MIN)
segments = []
let transcriptItems: TranscriptItem[] = []
let totalLength = 0
for (let i = 0; i < items.length; i++) {
const item = items[i]
transcriptItems.push(item)
totalLength += item.content.length
if (totalLength >= size || i === items.length-1) { // new segment or last
// add
segments.push({
items: transcriptItems,
startIdx: transcriptItems[0].idx,
endIdx: transcriptItems[transcriptItems.length - 1].idx,
text: getWholeText(transcriptItems.map(item => item.content)),
summaries: {},
})
// reset
transcriptItems = []
totalLength = 0
}
}
} else { // 都放一个分段
segments = [{
items,
startIdx: 0,
endIdx: items.length-1,
text: getWholeText(items.map(item => item.content)),
summaries: {},
}]
}
}
dispatch(setSegments(segments))
}, [data?.body, dispatch, envData])
// 每0.5秒更新当前视频时间
useInterval(() => {
sendInject('GET_VIDEO_STATUS', {}).then(status => {
dispatch(setCurrentTime(status.currentTime))
})
}, 500)
// show translated text in the video
useEffect(() => {
if (hideOnDisableAutoTranslate && !autoTranslate) {
sendInject('HIDE_TRANS', {})
return
}
const transResult = curIdx?transResults[curIdx]:undefined
if (transResult?.code === '200' && transResult.data) {
sendInject('UPDATE_TRANS_RESULT', {result: transResult.data})
} else {
sendInject('HIDE_TRANS', {})
}
}, [autoTranslate, curIdx, hideOnDisableAutoTranslate, transResults])
}
export default useSubtitleService