You've already forked bilibili-subtitle
支持gemini
This commit is contained in:
@@ -45,7 +45,6 @@ const Body = () => {
|
|||||||
const foldAll = useAppSelector(state => state.env.foldAll)
|
const foldAll = useAppSelector(state => state.env.foldAll)
|
||||||
const envData = useAppSelector(state => state.env.envData)
|
const envData = useAppSelector(state => state.env.envData)
|
||||||
const compact = useAppSelector(state => state.env.tempData.compact)
|
const compact = useAppSelector(state => state.env.tempData.compact)
|
||||||
const apiKey = useAppSelector(state => state.env.envData.apiKey)
|
|
||||||
const floatKeyPointsSegIdx = useAppSelector(state => state.env.floatKeyPointsSegIdx)
|
const floatKeyPointsSegIdx = useAppSelector(state => state.env.floatKeyPointsSegIdx)
|
||||||
const translateEnable = useAppSelector(state => state.env.envData.translateEnable)
|
const translateEnable = useAppSelector(state => state.env.envData.translateEnable)
|
||||||
const summarizeEnable = useAppSelector(state => state.env.envData.summarizeEnable)
|
const summarizeEnable = useAppSelector(state => state.env.envData.summarizeEnable)
|
||||||
@@ -76,6 +75,7 @@ const Body = () => {
|
|||||||
}, [dispatch])
|
}, [dispatch])
|
||||||
|
|
||||||
const onSummarizeAll = useCallback(() => {
|
const onSummarizeAll = useCallback(() => {
|
||||||
|
const apiKey = envData.aiType === 'gemini'?envData.geminiApiKey:envData.apiKey
|
||||||
if (!apiKey) {
|
if (!apiKey) {
|
||||||
dispatch(setPage(PAGE_SETTINGS))
|
dispatch(setPage(PAGE_SETTINGS))
|
||||||
toast.error('需要先设置ApiKey!')
|
toast.error('需要先设置ApiKey!')
|
||||||
@@ -98,7 +98,7 @@ const Body = () => {
|
|||||||
}
|
}
|
||||||
toast.success(`已添加${segments_.length}个总结任务!`)
|
toast.success(`已添加${segments_.length}个总结任务!`)
|
||||||
}
|
}
|
||||||
}, [addSummarizeTask, apiKey, curSummaryType, dispatch, segments])
|
}, [addSummarizeTask, curSummaryType, dispatch, envData.aiType, envData.apiKey, envData.geminiApiKey, segments])
|
||||||
|
|
||||||
const onFoldAll = useCallback(() => {
|
const onFoldAll = useCallback(() => {
|
||||||
dispatch(setFoldAll(!foldAll))
|
dispatch(setFoldAll(!foldAll))
|
||||||
@@ -111,13 +111,14 @@ const Body = () => {
|
|||||||
}, [dispatch, foldAll, segments])
|
}, [dispatch, foldAll, segments])
|
||||||
|
|
||||||
const toggleAutoTranslateCallback = useCallback(() => {
|
const toggleAutoTranslateCallback = useCallback(() => {
|
||||||
if (envData.apiKey) {
|
const apiKey = envData.aiType === 'gemini'?envData.geminiApiKey:envData.apiKey
|
||||||
|
if (apiKey) {
|
||||||
dispatch(setAutoTranslate(!autoTranslate))
|
dispatch(setAutoTranslate(!autoTranslate))
|
||||||
} else {
|
} else {
|
||||||
dispatch(setPage(PAGE_SETTINGS))
|
dispatch(setPage(PAGE_SETTINGS))
|
||||||
toast.error('需要先设置ApiKey!')
|
toast.error('需要先设置ApiKey!')
|
||||||
}
|
}
|
||||||
}, [autoTranslate, dispatch, envData.apiKey])
|
}, [autoTranslate, dispatch, envData.aiType, envData.apiKey, envData.geminiApiKey])
|
||||||
|
|
||||||
const onEnableAutoScroll = useCallback(() => {
|
const onEnableAutoScroll = useCallback(() => {
|
||||||
dispatch(setAutoScroll(true))
|
dispatch(setAutoScroll(true))
|
||||||
|
@@ -66,19 +66,20 @@ const Summarize = (props: {
|
|||||||
const {segment, segmentIdx, summary, float} = props
|
const {segment, segmentIdx, summary, float} = props
|
||||||
|
|
||||||
const dispatch = useAppDispatch()
|
const dispatch = useAppDispatch()
|
||||||
const apiKey = useAppSelector(state => state.env.envData.apiKey)
|
const envData = useAppSelector(state => state.env.envData)
|
||||||
const fontSize = useAppSelector(state => state.env.envData.fontSize)
|
const fontSize = useAppSelector(state => state.env.envData.fontSize)
|
||||||
const curSummaryType = useAppSelector(state => state.env.tempData.curSummaryType)
|
const curSummaryType = useAppSelector(state => state.env.tempData.curSummaryType)
|
||||||
const {addSummarizeTask} = useTranslate()
|
const {addSummarizeTask} = useTranslate()
|
||||||
|
|
||||||
const onGenerate = useCallback(() => {
|
const onGenerate = useCallback(() => {
|
||||||
|
const apiKey = envData.aiType === 'gemini'?envData.geminiApiKey:envData.apiKey
|
||||||
if (apiKey) {
|
if (apiKey) {
|
||||||
addSummarizeTask(curSummaryType, segment).catch(console.error)
|
addSummarizeTask(curSummaryType, segment).catch(console.error)
|
||||||
} else {
|
} else {
|
||||||
dispatch(setPage(PAGE_SETTINGS))
|
dispatch(setPage(PAGE_SETTINGS))
|
||||||
toast.error('需要先设置ApiKey!')
|
toast.error('需要先设置ApiKey!')
|
||||||
}
|
}
|
||||||
}, [addSummarizeTask, apiKey, curSummaryType, dispatch, segment])
|
}, [addSummarizeTask, curSummaryType, dispatch, envData.aiType, envData.apiKey, envData.geminiApiKey, segment])
|
||||||
|
|
||||||
const onCopy = useCallback(() => {
|
const onCopy = useCallback(() => {
|
||||||
if (summary != null) {
|
if (summary != null) {
|
||||||
|
@@ -262,18 +262,21 @@ const Settings = () => {
|
|||||||
|
|
||||||
{aiTypeValue === 'gemini' && <Section title='gemini配置'>
|
{aiTypeValue === 'gemini' && <Section title='gemini配置'>
|
||||||
<FormItem title='ApiKey' htmlFor='geminiApiKey'>
|
<FormItem title='ApiKey' htmlFor='geminiApiKey'>
|
||||||
<input id='geminiApiKey' type='text' className='input input-sm input-bordered w-full' placeholder='xxx' value={geminiApiKeyValue} onChange={onChangeGeminiApiKeyValue}/>
|
<input id='geminiApiKey' type='text' className='input input-sm input-bordered w-full' placeholder='xxx'
|
||||||
|
value={geminiApiKeyValue} onChange={onChangeGeminiApiKeyValue}/>
|
||||||
</FormItem>
|
</FormItem>
|
||||||
<div className='flex justify-center'>
|
<div className='flex justify-center'>
|
||||||
<a className='link text-xs' onClick={toggleMoreFold}>{moreFold ? '点击查看说明' : '点击折叠说明'}</a>
|
<a className='link text-xs' onClick={toggleMoreFold}>{moreFold ? '点击查看说明' : '点击折叠说明'}</a>
|
||||||
</div>
|
</div>
|
||||||
{!moreFold && <div>
|
{!moreFold && <div>
|
||||||
<ul className='pl-3 list-decimal desc text-xs'>
|
<ul className='pl-3 list-decimal desc text-xs'>
|
||||||
<li>官方网址:<a className='link' href='https://makersuite.google.com/app/apikey' target='_blank' rel="noreferrer">Google AI Studio</a></li>
|
<li>官方网址:<a className='link' href='https://makersuite.google.com/app/apikey' target='_blank'
|
||||||
|
rel="noreferrer">Google AI Studio</a> (目前免费)</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>}
|
</div>}
|
||||||
<div className='flex justify-center'>
|
<div className='flex justify-center'>
|
||||||
<a className='link text-xs' onClick={togglePromptsFold}>{promptsFold?'点击查看提示词':'点击折叠提示词'}</a>
|
<a className='link text-xs'
|
||||||
|
onClick={togglePromptsFold}>{promptsFold ? '点击查看提示词' : '点击折叠提示词'}</a>
|
||||||
</div>
|
</div>
|
||||||
{!promptsFold && <div>
|
{!promptsFold && <div>
|
||||||
{PROMPT_TYPES.map((item, idx) => <FormItem key={item.type} title={<div>
|
{PROMPT_TYPES.map((item, idx) => <FormItem key={item.type} title={<div>
|
||||||
@@ -284,9 +287,11 @@ const Settings = () => {
|
|||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
[item.type]: PROMPT_DEFAULTS[item.type] ?? ''
|
[item.type]: PROMPT_DEFAULTS[item.type] ?? ''
|
||||||
})
|
})
|
||||||
}}>点击填充默认</div>
|
}}>点击填充默认
|
||||||
|
</div>
|
||||||
</div>} htmlFor={`prompt-${item.type}`}>
|
</div>} htmlFor={`prompt-${item.type}`}>
|
||||||
<textarea id={`prompt-${item.type}`} className='mt-2 textarea input-bordered w-full' placeholder='留空使用默认提示词' value={promptsValue[item.type]??''} onChange={(e) => {
|
<textarea id={`prompt-${item.type}`} className='mt-2 textarea input-bordered w-full'
|
||||||
|
placeholder='留空使用默认提示词' value={promptsValue[item.type] ?? ''} onChange={(e) => {
|
||||||
setPromptsValue({
|
setPromptsValue({
|
||||||
...promptsValue,
|
...promptsValue,
|
||||||
[item.type]: e.target.value
|
[item.type]: e.target.value
|
||||||
|
@@ -18,3 +18,16 @@ export const handleChatCompleteTask = async (task: Task) => {
|
|||||||
throw new Error(`${task.resp.error.code as string??''} ${task.resp.error.message as string ??''}`)
|
throw new Error(`${task.resp.error.code as string??''} ${task.resp.error.message as string ??''}`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const handleGeminiChatCompleteTask = async (task: Task) => {
|
||||||
|
const data = task.def.data
|
||||||
|
const resp = await fetch('https://generativelanguage.googleapis.com/v1/models/gemini-pro:generateContent', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'x-goog-api-key': task.def.extra.geminiApiKey,
|
||||||
|
},
|
||||||
|
body: JSON.stringify(data),
|
||||||
|
})
|
||||||
|
task.resp = await resp.json()
|
||||||
|
}
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import {TASK_EXPIRE_TIME} from '../const'
|
import {TASK_EXPIRE_TIME} from '../const'
|
||||||
import {handleChatCompleteTask} from './openaiService'
|
import {handleChatCompleteTask, handleGeminiChatCompleteTask} from './openaiService'
|
||||||
|
|
||||||
export const tasksMap = new Map<string, Task>()
|
export const tasksMap = new Map<string, Task>()
|
||||||
|
|
||||||
@@ -11,6 +11,9 @@ export const handleTask = async (task: Task) => {
|
|||||||
case 'chatComplete':
|
case 'chatComplete':
|
||||||
await handleChatCompleteTask(task)
|
await handleChatCompleteTask(task)
|
||||||
break
|
break
|
||||||
|
case 'geminiChatComplete':
|
||||||
|
await handleGeminiChatCompleteTask(task)
|
||||||
|
break
|
||||||
default:
|
default:
|
||||||
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
|
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
|
||||||
throw new Error(`任务类型不支持: ${task.def.type}`)
|
throw new Error(`任务类型不支持: ${task.def.type}`)
|
||||||
|
@@ -82,9 +82,24 @@ const useTranslate = () => {
|
|||||||
prompt = prompt.replaceAll('{{subtitles}}', lineStr)
|
prompt = prompt.replaceAll('{{subtitles}}', lineStr)
|
||||||
|
|
||||||
const taskDef: TaskDef = {
|
const taskDef: TaskDef = {
|
||||||
type: 'chatComplete',
|
type: envData.aiType === 'gemini'?'geminiChatComplete':'chatComplete',
|
||||||
serverUrl: envData.serverUrl,
|
serverUrl: envData.serverUrl,
|
||||||
data: {
|
data: envData.aiType === 'gemini'
|
||||||
|
?{
|
||||||
|
contents: [
|
||||||
|
{
|
||||||
|
parts: [
|
||||||
|
{
|
||||||
|
text: prompt
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
generationConfig: {
|
||||||
|
maxOutputTokens: 2048
|
||||||
|
}
|
||||||
|
}
|
||||||
|
:{
|
||||||
model: envData.model??MODEL_DEFAULT,
|
model: envData.model??MODEL_DEFAULT,
|
||||||
messages: [
|
messages: [
|
||||||
{
|
{
|
||||||
@@ -99,6 +114,7 @@ const useTranslate = () => {
|
|||||||
extra: {
|
extra: {
|
||||||
type: 'translate',
|
type: 'translate',
|
||||||
apiKey: envData.apiKey,
|
apiKey: envData.apiKey,
|
||||||
|
geminiApiKey: envData.geminiApiKey,
|
||||||
startIdx,
|
startIdx,
|
||||||
size: lines.length,
|
size: lines.length,
|
||||||
}
|
}
|
||||||
@@ -117,10 +133,10 @@ const useTranslate = () => {
|
|||||||
dispatch(addTaskId(task.id))
|
dispatch(addTaskId(task.id))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [data?.body, dispatch, envData.apiKey, envData.fetchAmount, envData.serverUrl, envData.prompts, title, language.name])
|
}, [data?.body, envData.fetchAmount, envData.prompts, envData.aiType, envData.serverUrl, envData.model, envData.apiKey, envData.geminiApiKey, language.name, title, dispatch])
|
||||||
|
|
||||||
const addSummarizeTask = useCallback(async (type: SummaryType, segment: Segment) => {
|
const addSummarizeTask = useCallback(async (type: SummaryType, segment: Segment) => {
|
||||||
if (segment.text.length >= SUMMARIZE_THRESHOLD && envData.apiKey) {
|
if (segment.text.length >= SUMMARIZE_THRESHOLD) {
|
||||||
let subtitles = ''
|
let subtitles = ''
|
||||||
for (const item of segment.items) {
|
for (const item of segment.items) {
|
||||||
subtitles += formatTime(item.from) + ' ' + item.content + '\n'
|
subtitles += formatTime(item.from) + ' ' + item.content + '\n'
|
||||||
@@ -135,9 +151,24 @@ const useTranslate = () => {
|
|||||||
prompt = prompt.replaceAll('{{segment}}', segment.text)
|
prompt = prompt.replaceAll('{{segment}}', segment.text)
|
||||||
|
|
||||||
const taskDef: TaskDef = {
|
const taskDef: TaskDef = {
|
||||||
type: 'chatComplete',
|
type: envData.aiType === 'gemini'?'geminiChatComplete':'chatComplete',
|
||||||
serverUrl: envData.serverUrl,
|
serverUrl: envData.serverUrl,
|
||||||
data: {
|
data: envData.aiType === 'gemini'
|
||||||
|
?{
|
||||||
|
contents: [
|
||||||
|
{
|
||||||
|
parts: [
|
||||||
|
{
|
||||||
|
text: prompt
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
generationConfig: {
|
||||||
|
maxOutputTokens: 2048
|
||||||
|
}
|
||||||
|
}
|
||||||
|
:{
|
||||||
model: envData.model??MODEL_DEFAULT,
|
model: envData.model??MODEL_DEFAULT,
|
||||||
messages: [
|
messages: [
|
||||||
{
|
{
|
||||||
@@ -154,6 +185,7 @@ const useTranslate = () => {
|
|||||||
summaryType: type,
|
summaryType: type,
|
||||||
startIdx: segment.startIdx,
|
startIdx: segment.startIdx,
|
||||||
apiKey: envData.apiKey,
|
apiKey: envData.apiKey,
|
||||||
|
geminiApiKey: envData.geminiApiKey,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
console.debug('addSummarizeTask', taskDef)
|
console.debug('addSummarizeTask', taskDef)
|
||||||
@@ -162,7 +194,7 @@ const useTranslate = () => {
|
|||||||
const task = await chrome.runtime.sendMessage({type: 'addTask', taskDef})
|
const task = await chrome.runtime.sendMessage({type: 'addTask', taskDef})
|
||||||
dispatch(addTaskId(task.id))
|
dispatch(addTaskId(task.id))
|
||||||
}
|
}
|
||||||
}, [dispatch, envData.apiKey, envData.prompts, envData.serverUrl, summarizeLanguage.name, title])
|
}, [dispatch, envData.aiType, envData.apiKey, envData.geminiApiKey, envData.model, envData.prompts, envData.serverUrl, summarizeLanguage.name, title])
|
||||||
|
|
||||||
const handleTranslate = useMemoizedFn((task: Task, content: string) => {
|
const handleTranslate = useMemoizedFn((task: Task, content: string) => {
|
||||||
let map: {[key: string]: string} = {}
|
let map: {[key: string]: string} = {}
|
||||||
@@ -221,7 +253,7 @@ const useTranslate = () => {
|
|||||||
console.debug('getTask', taskResp.task)
|
console.debug('getTask', taskResp.task)
|
||||||
const task: Task = taskResp.task
|
const task: Task = taskResp.task
|
||||||
const taskType: string | undefined = task.def.extra?.type
|
const taskType: string | undefined = task.def.extra?.type
|
||||||
const content = task.resp?.choices?.[0]?.message?.content?.trim()
|
const content = envData.aiType === 'gemini'?task.resp?.candidates[0]?.content?.parts[0]?.text?.trim():task.resp?.choices?.[0]?.message?.content?.trim()
|
||||||
if (task.status === 'done') {
|
if (task.status === 'done') {
|
||||||
// 异常提示
|
// 异常提示
|
||||||
if (task.error) {
|
if (task.error) {
|
||||||
@@ -239,7 +271,7 @@ const useTranslate = () => {
|
|||||||
} else {
|
} else {
|
||||||
dispatch(delTaskId(taskId))
|
dispatch(delTaskId(taskId))
|
||||||
}
|
}
|
||||||
}, [dispatch, handleSummarize, handleTranslate])
|
}, [dispatch, envData.aiType, handleSummarize, handleTranslate])
|
||||||
|
|
||||||
return {getFetch, getTask, addTask, addSummarizeTask}
|
return {getFetch, getTask, addTask, addSummarizeTask}
|
||||||
}
|
}
|
||||||
|
2
src/typings.d.ts
vendored
2
src/typings.d.ts
vendored
@@ -38,7 +38,7 @@ interface TempData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface TaskDef {
|
interface TaskDef {
|
||||||
type: 'chatComplete'
|
type: 'chatComplete' | 'geminiChatComplete'
|
||||||
serverUrl?: string
|
serverUrl?: string
|
||||||
data: any
|
data: any
|
||||||
extra?: any
|
extra?: any
|
||||||
|
Reference in New Issue
Block a user