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 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