LaiTool/src/main/Service/Translate/TranslateService.ts
2024-09-12 14:13:09 +08:00

266 lines
10 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 { GeneralResponse } from "../../../model/generalResponse";
import { TranslateModel } from "../../../model/translate";
import { errorMessage, successMessage } from "../../Public/generalTools";
import { Translate } from "./Translate";
import { DEFINE_STRING } from "../../../define/define_string"
import { TranslateAPIType, TranslateType } from "../../../define/enum/translate";
import { Book } from "../../../model/book";
import { ResponseMessageType } from "../../../define/enum/softwareEnum";
import { isEmpty } from "lodash";
import { ValidateJson } from "../../../define/Tools/validate";
import { BookServiceBasic } from "../ServiceBasic/bookServiceBasic";
import { SoftWareServiceBasic } from "../ServiceBasic/softwareServiceBasic";
import { ExecuteConcurrently } from "../../../define/Tools/common";
/**
* 翻译实现服务
*/
export class TranslateService {
translate: Translate
bookServiceBasic: BookServiceBasic
softWareServiceBasic: SoftWareServiceBasic
constructor() {
this.bookServiceBasic = new BookServiceBasic();
this.translate = new Translate();
this.softWareServiceBasic = new SoftWareServiceBasic();
}
// 返回翻译结果。用于前端修改
private sendTranslateReturn(windowId: number, data: GeneralResponse.MessageResponse, message_name: string = DEFINE_STRING.BOOK.MAIN_DATA_RETURN): void {
let win = global.newWindow[0]
if (windowId) {
win = global.newWindow.filter(item => item.id == windowId)[0];
}
if (!message_name) {
message_name = DEFINE_STRING.BOOK.MAIN_DATA_RETURN
}
win.win.webContents.send(message_name, data)
}
//#region 翻译设置
/**
* 初始化翻译设置
* @returns
*/
InitialTranslateSetting(): TranslateModel.TranslateModel {
return {
selectModel: TranslateAPIType.BAIDU,
translation_auto: true,
translates: [
{
name: TranslateAPIType.LAITOOL,
translation_business: "https://api.laitool.cc/v1/chat/completions",
translation_app_id: "gpt-4o-mini",
translation_secret: "LAI API 令牌"
},
{
name: TranslateAPIType.BAIDU,
translation_business: "https://fanyi-api.baidu.com/api/trans/vip/translate",
translation_app_id: "百度翻译的APP ID",
translation_secret: "百度翻译的密钥"
},
{
name: TranslateAPIType.TENCENT,
translation_business: "https://tmt.tencentcloudapi.com",
translation_app_id: '腾讯云的APP ID',
translation_secret: "腾讯云的密钥"
},
{
name: TranslateAPIType.VOLCENGINE,
translation_business: "https://translate.volcengineapi.com?",
translation_app_id: '火山引擎的APP ID',
translation_secret: '火山引擎的密钥'
},
{
name: TranslateAPIType.ALI,
translation_business: "https://mt.cn-hangzhou.aliyuncs.com",
translation_app_id: '阿里云的APP ID',
translation_secret: '阿里云的密钥'
}
]
}
}
/**
* 获取翻译设置
*/
async GetTranslateSetting(): Promise<GeneralResponse.SuccessItem | GeneralResponse.ErrorItem> {
try {
let translateSetting = undefined as TranslateModel.TranslateModel
let translateSettingString = await this.softWareServiceBasic.GetSoftWarePropertyData('translationSetting');
if (isEmpty(translateSettingString)) {
// 初始化
translateSetting = this.InitialTranslateSetting();
await this.ResetTranslateSetting();
translateSettingString = await this.softWareServiceBasic.GetSoftWarePropertyData('translationSetting');
translateSetting = JSON.parse(translateSettingString);
} else {
// 解析
let tryParse = ValidateJson(translateSettingString)
if (!tryParse) {
throw new Error("翻译设置数据解析失败,请重置后重新配置")
}
translateSetting = JSON.parse(translateSettingString);
}
// v3.0.1 preview 4 版本新增laitool gpt翻译之前版本没有这边要检测下是不是有配置没有初始化
let index = translateSetting.translates.findIndex(item => item.name == TranslateAPIType.LAITOOL);
if (index < 0) {
// 初始化,将数据加在最前面
let laitool = {
name: TranslateAPIType.LAITOOL,
translation_business: "https://api.laitool.cc/v1/chat/completions",
translation_app_id: "gpt-4o-mini",
translation_secret: "LAI API 令牌"
}
translateSetting.translates.unshift(laitool)
}
return successMessage(translateSetting, "获取翻译设置成功", "TranslateService_GetTranslateSetting")
} catch (error) {
return errorMessage("获取翻译设置失败,失败信息如下:" + error.toString(), "TranslateService_GetTranslateSetting")
}
}
/**
* 重置翻译设置
*/
async ResetTranslateSetting(): Promise<GeneralResponse.SuccessItem | GeneralResponse.ErrorItem> {
try {
let translateSetting = this.InitialTranslateSetting()
let res = await this.softWareServiceBasic.SaveSoftwarePropertyData('translationSetting', JSON.stringify(translateSetting))
return successMessage(translateSetting, "重置翻译设置成功", "TranslateService_ResetTranslateSetting")
} catch (error) {
return errorMessage("重置翻译设置失败,失败信息如下:" + error.toString(), "TranslateService_ResetTranslateSetting")
}
}
/**
* 保存翻译设置
* @param value 保存的数据
* @returns
*/
async SaveTranslateSetting(value: TranslateModel.TranslateModel): Promise<GeneralResponse.SuccessItem | GeneralResponse.ErrorItem> {
try {
let res = await this.softWareServiceBasic.SaveSoftwarePropertyData('translationSetting', JSON.stringify(value))
// 这边要判断是不是用的laitool
let laitool = value.translates.filter(item => item.name == TranslateAPIType.LAITOOL)
if (laitool.length > 0) {
let laitoolData = laitool[0]
if (!laitoolData.translation_business.includes('laitool')) {
throw new Error("不允许修改laitool的地址为其他外站地址")
}
}
return successMessage(value, "保存翻译设置成功", "TranslateService_SaveTranslateSetting")
} catch (error) {
return errorMessage("保存翻译设置失败,失败信息如下:" + error.toString(), "TranslateService_SaveTranslateSetting")
}
}
//#endregion
/**
* 翻译反推提示词处理
* @param bookTaskDetailId 对应的分镜ID
* @param reversePromptId 对应的反推提示词数据ID
* @param to 目标语言
* @param dstString 翻译后的字符串
*/
private async TranslateProcessReversePrompt(bookTaskDetailId: string, reversePromptId: string, to: string, dstString: string): Promise<void> {
// 开始修改翻译后的数据
let updateData = {} as Book.ReversePrompt
if (to == "zh") {
updateData.promptCN = dstString
} else if (to == "en") {
updateData.prompt = dstString
updateData.promptCN = dstString
}
// 修改数据
await this.bookServiceBasic.UpdateBookTaskDetailReversePrompt(bookTaskDetailId, reversePromptId, updateData)
}
// 处理返回的数据
async TranslateReturnProcess(value: TranslateModel.TranslateNowIPCParams, to: string, dstString: string) {
switch (value.type) {
case TranslateType.REVERSE_PROMPT_TRANSLATE:
await this.TranslateProcessReversePrompt(value.bookTaskDetailId, value.reversePromptId, to, dstString)
break;
case TranslateType.GPT_PROMPT_TRANSLATE:
// 这个直接改就行
await this.bookServiceBasic.UpdateBookTaskDetail(value.bookTaskDetailId, {
gptPrompt: dstString
})
break;
default:
throw new Error("未知的翻译类型");
}
}
/**
* 翻译
* @param value
* @returns
*/
async TranslateNowReturn(value: TranslateModel.TranslateNowIPCParams[]): Promise<GeneralResponse.SuccessItem | GeneralResponse.ErrorItem> {
try {
// 循环所有的数据,返回翻译结果
let tasks = []
for (let i = 0; i < value.length; i++) {
const element = value[i];
tasks.push(async () => {
let res = await this.translate.TranslateReturnNow(element)
// global.logger.info("断点写出", JSON.stringify(res))
// 单个翻译,将返回的数据写入到原数据中
// 添加一个对返回信息进行处理的函数
let data = res.data
// 先将数据进行拼接
let srcString = ""
if (element.isSplit) {
let dstStrs = []
} else {
// 没有拆分的,只有一句
srcString = data.data[0].dst
}
// 写回数据库
await this.TranslateReturnProcess(element, data.to, srcString);
let responseType = ResponseMessageType.REVERSE_PROMPT_TRANSLATE;
if (element.type == TranslateType.GPT_PROMPT_TRANSLATE) {
responseType = ResponseMessageType.GPT_PROMPT_TRANSLATE
}
// 做个返回数据
this.sendTranslateReturn(element.windowId, {
code: 1,
id: element.bookTaskDetailId,
type: responseType,
data: {
progress: i + 1,
total: value.length,
from: element.from,
to: data.to,
bookTaskDetailId: element.bookTaskDetailId,
reversePromptId: element.reversePromptId,
prompt: srcString,
promptCN: srcString
}
}, element.responseMessgeName)
})
}
let res = await ExecuteConcurrently(tasks, global.config.task_number)
// 将翻译后的数据返回,前端进行修改
return successMessage(null, "全部翻译完成", "TranslateService_TranslateNowReturn")
} catch (error) {
return errorMessage("翻译失败,失败信息如下:" + error.toString(), "TranslateService_TranslateNowReturn")
}
}
}