Files
bilibili-subtitle/src/biz/MoreBtn.tsx
2024-06-18 10:57:20 +08:00

306 lines
9.9 KiB
TypeScript

import React, {MouseEvent, useCallback, useContext, useRef, useState} from 'react'
import {useClickAway} from 'ahooks'
import {
AiFillWechat,
BsFillChatDotsFill,
FiMoreVertical,
ImDownload3,
IoMdSettings,
RiFileCopy2Line
} from 'react-icons/all'
import Popover from '../components/Popover'
import {Placement} from '@popperjs/core/lib/enums'
import {useAppDispatch, useAppSelector} from '../hooks/redux'
import {setEnvData, setPage, setTempData} from '../redux/envReducer'
import {EventBusContext} from '../Router'
import {EVENT_EXPAND, PAGE_SETTINGS} from '../const'
import {formatSrtTime, formatTime, formatVttTime} from '../util/util'
import {downloadText, openUrl} from '@kky002/kky-util'
import toast from 'react-hot-toast'
import {getSummarize} from '../util/biz_util'
interface Props {
placement: Placement
}
const DownloadTypes = [
{
type: 'text',
name: '列表',
},
{
type: 'textWithTime',
name: '列表(带时间)',
},
{
type: 'article',
name: '文章',
},
{
type: 'srt',
name: 'srt',
},
{
type: 'vtt',
name: 'vtt',
},
{
type: 'json',
name: '原始json',
},
{
type: 'summarize',
name: '总结',
},
]
const MoreBtn = (props: Props) => {
const {placement} = props
const dispatch = useAppDispatch()
const moreRef = useRef(null)
const data = useAppSelector(state => state.env.data)
const envReady = useAppSelector(state => state.env.envReady)
const envData = useAppSelector(state => state.env.envData)
const downloadType = useAppSelector(state => state.env.tempData.downloadType)
const [moreVisible, setMoreVisible] = useState(false)
const eventBus = useContext(EventBusContext)
const segments = useAppSelector(state => state.env.segments)
const url = useAppSelector(state => state.env.url)
const title = useAppSelector(state => state.env.title)
const curSummaryType = useAppSelector(state => state.env.tempData.curSummaryType)
const downloadCallback = useCallback((download: boolean) => {
if (data == null) {
return
}
let fileName = title
let s, suffix
if (!downloadType || downloadType === 'text') {
s = `${title??'无标题'}\n${url??'无链接'}\n\n`
for (const item of data.body) {
s += item.content + '\n'
}
suffix = 'txt'
} else if (downloadType === 'textWithTime') {
s = `${title??'无标题'}\n${url??'无链接'}\n\n`
for (const item of data.body) {
s += formatTime(item.from) + ' ' + item.content + '\n'
}
suffix = 'txt'
} else if (downloadType === 'article') {
s = `${title??'无标题'}\n${url??'无链接'}\n\n`
for (const item of data.body) {
s += item.content + ', '
}
s = s.substring(0, s.length - 1) // remove last ','
suffix = 'txt'
} else if (downloadType === 'srt') {
/**
* 1
* 00:05:00,400 --> 00:05:15,300
* This is an example of
* a subtitle.
*
* 2
* 00:05:16,400 --> 00:05:25,300
* This is an example of
* a subtitle - 2nd subtitle.
*/
s = ''
for (const item of data.body) {
const ss = (item.idx + 1) + '\n' + formatSrtTime(item.from) + ' --> ' + formatSrtTime(item.to) + '\n' + ((item.content?.trim()) ?? '') + '\n\n'
s += ss
}
s = s.substring(0, s.length - 1)// remove last '\n'
suffix = 'srt'
} else if (downloadType === 'vtt') {
/**
* WEBVTT title
*
* 1
* 00:05:00.400 --> 00:05:15.300
* This is an example of
* a subtitle.
*
* 2
* 00:05:16.400 --> 00:05:25.300
* This is an example of
* a subtitle - 2nd subtitle.
*/
s = `WEBVTT ${title ?? ''}\n\n`
for (const item of data.body) {
const ss = (item.idx + 1) + '\n' + formatVttTime(item.from) + ' --> ' + formatVttTime(item.to) + '\n' + ((item.content?.trim()) ?? '') + '\n\n'
s += ss
}
s = s.substring(0, s.length - 1)// remove last '\n'
suffix = 'vtt'
} else if (downloadType === 'json') {
s = JSON.stringify(data)
suffix = 'json'
} else if (downloadType === 'summarize') {
s = `${title??'无标题'}\n${url??'无链接'}\n\n`
const [success, content] = getSummarize(title, segments, curSummaryType)
if (!success) return
s += content
fileName += ' - 总结'
suffix = 'txt'
} else {
return
}
if (download) {
downloadText(s, fileName+'.'+suffix)
} else {
navigator.clipboard.writeText(s).then(() => {
toast.success('复制成功')
}).catch(console.error)
}
setMoreVisible(false)
}, [curSummaryType, data, downloadType, segments, title, url])
const downloadAudioCallback = useCallback(() => {
window.parent.postMessage({
type: 'downloadAudio',
}, '*')
}, [])
const selectCallback = useCallback((e: any) => {
dispatch(setTempData({
downloadType: e.target.value,
}))
}, [dispatch])
const preventCallback = useCallback((e: any) => {
e.stopPropagation()
}, [])
const moreCallback = useCallback((e: MouseEvent) => {
e.stopPropagation()
if (!envData.flagDot) {
dispatch(setEnvData({
...envData,
flagDot: true,
}))
}
setMoreVisible(!moreVisible)
// 显示菜单时自动展开,防止菜单显示不全
if (!moreVisible) {
eventBus.emit({
type: EVENT_EXPAND
})
}
}, [dispatch, envData, eventBus, moreVisible])
useClickAway(() => {
setMoreVisible(false)
}, moreRef)
return <>
<div ref={moreRef} onClick={moreCallback}>
<div className='indicator flex items-center'>
{envReady && !envData.flagDot && <span className="indicator-item bg-secondary w-1.5 h-1.5 rounded-full"></span>}
<FiMoreVertical className='desc transform ease-in duration-300 hover:text-primary' title='更多'/>
</div>
</div>
{moreVisible &&
<Popover refElement={moreRef.current} className='bg-neutral text-neutral-content py-1 z-[1000]' options={{
placement
}}>
<ul className='menu menu-compact'>
<li className='hover:bg-accent'>
<a className='flex items-center' onClick={(e) => {
e.preventDefault()
e.stopPropagation()
downloadCallback(false)
}}>
<RiFileCopy2Line className='w-[20px] h-[20px] text-primary/75 bg-white rounded-sm p-0.5'/>
<select className='select select-ghost select-xs' value={downloadType} onChange={selectCallback}
onClick={preventCallback}>
{DownloadTypes?.map((item: any) => <option key={item.type} value={item.type}>{item.name}</option>)}
</select>
</a>
</li>
<li className='hover:bg-accent'>
<a className='flex items-center' onClick={(e) => {
e.preventDefault()
e.stopPropagation()
downloadCallback(true)
}}>
<ImDownload3 className='w-[20px] h-[20px] text-primary/75 bg-white rounded-sm p-0.5'/>
<select className='select select-ghost select-xs' value={downloadType} onChange={selectCallback}
onClick={preventCallback}>
{DownloadTypes?.map((item: any) => <option key={item.type} value={item.type}>{item.name}</option>)}
</select>
</a>
</li>
<li className='hover:bg-accent'>
<a className='flex items-center' onClick={(e) => {
e.preventDefault()
e.stopPropagation()
downloadAudioCallback()
}}>
<ImDownload3 className='w-[20px] h-[20px] text-primary/75 bg-white rounded-sm p-0.5'/>
(m4s)
</a>
</li>
<li className='hover:bg-accent'>
<a className='flex items-center' onClick={(e) => {
e.preventDefault()
e.stopPropagation()
openUrl('https://jq.qq.com/?_wv=1027&k=RJyFABPF')
}}>
<BsFillChatDotsFill className='w-[20px] h-[20px] text-primary/75 bg-white rounded-sm p-0.5'/>
QQ交流群(194536885)
</a>
</li>
<li className='hover:bg-accent'>
<a className='flex items-center' onClick={(e) => {
e.preventDefault()
e.stopPropagation()
openUrl('https://static.ssstab.com/images/indiekky_public.png')
}}>
<AiFillWechat className='w-[20px] h-[20px] text-primary/75 bg-white rounded-sm p-0.5'/>
(IndieKKY)
</a>
</li>
{/* <li className='hover:bg-accent'> */}
{/* <a className='flex items-center' onClick={(e) => { */}
{/* e.preventDefault() */}
{/* e.stopPropagation() */}
{/* openUrl('https://bibigpt.co/r/bilibili') */}
{/* }}> */}
{/* <img alt='BibiGPT' src='/bibigpt.png' className='w-[20px] h-[20px] bg-white rounded-sm p-0.5'/> */}
{/* BibiGPT */}
{/* </a> */}
{/* </li> */}
{/* <li className='hover:bg-accent'> */}
{/* <a className='flex items-center' onClick={(e) => { */}
{/* e.preventDefault() */}
{/* e.stopPropagation() */}
{/* openUrl('https://chromewebstore.google.com/detail/fiaeclpicddpifeflpmlgmbjgaedladf') */}
{/* }}> */}
{/* <img alt='youtube subtitle' src='/youtube-caption.png' */}
{/* className='w-[20px] h-[20px] bg-white rounded-sm p-0.5'/> */}
{/* Youtube Caption */}
{/* </a> */}
{/* </li> */}
<li className='hover:bg-accent'>
<a className='flex items-center' onClick={(e) => {
dispatch(setPage(PAGE_SETTINGS))
setMoreVisible(false)
e.preventDefault()
e.stopPropagation()
}}>
<IoMdSettings className='w-[20px] h-[20px] text-primary/75 bg-white rounded-sm p-0.5'/>
</a>
</li>
</ul>
</Popover>}
</>
}
export default MoreBtn