227 lines
9.3 KiB
TypeScript
227 lines
9.3 KiB
TypeScript
|
|
import { isEmpty } from "lodash";
|
|||
|
|
import { GetSubtitleType, SubtitleSavePositionType } from "../../../define/enum/waterMarkAndSubtitle"
|
|||
|
|
import { errorMessage, successMessage } from "../../Public/generalTools"
|
|||
|
|
import { SoftWareServiceBasic } from "../ServiceBasic/softwareServiceBasic"
|
|||
|
|
import { ValidateJson } from "../../../define/Tools/validate";
|
|||
|
|
import { GeneralResponse } from "../../../model/generalResponse";
|
|||
|
|
import { SubtitleModel } from "../../../model/subtitle";
|
|||
|
|
import { define } from '../../../define/define'
|
|||
|
|
import path from 'path'
|
|||
|
|
import fs from 'fs'
|
|||
|
|
import { CheckFileOrDirExist } from "../../../define/Tools/file";
|
|||
|
|
import { BookServiceBasic } from "../ServiceBasic/bookServiceBasic";
|
|||
|
|
import { Subtitle } from "./subtitle";
|
|||
|
|
import { LoggerStatus, ResponseMessageType } from "../../../define/enum/softwareEnum";
|
|||
|
|
import { TaskScheduler } from "../taskScheduler";
|
|||
|
|
import { OperationType } from "realm/dist/public-types/internal";
|
|||
|
|
import { OperateBookType } from "../../../define/enum/bookEnum";
|
|||
|
|
import { Book } from "../../../model/book";
|
|||
|
|
|
|||
|
|
export class SubtitleService {
|
|||
|
|
softWareServiceBasic: SoftWareServiceBasic
|
|||
|
|
bookServiceBasic: BookServiceBasic
|
|||
|
|
subtitle: Subtitle
|
|||
|
|
taskScheduler: TaskScheduler
|
|||
|
|
constructor() {
|
|||
|
|
this.softWareServiceBasic = new SoftWareServiceBasic();
|
|||
|
|
this.bookServiceBasic = new BookServiceBasic();
|
|||
|
|
this.subtitle = new Subtitle();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
//#region 设置相关的方法
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 初始化字幕设置
|
|||
|
|
*/
|
|||
|
|
async InitSubtitleSetting(): Promise<SubtitleModel.subtitleSettingModel> {
|
|||
|
|
let defauleSetting = {
|
|||
|
|
selectModel: GetSubtitleType.LAI_WHISPER,
|
|||
|
|
laiWhisper: {
|
|||
|
|
url: 'https://api.laitool.cc/',
|
|||
|
|
apiKey: '你的LAI API KEY',
|
|||
|
|
syncGPTAPIKey: false,
|
|||
|
|
prompt: undefined
|
|||
|
|
}
|
|||
|
|
} as SubtitleModel.subtitleSettingModel
|
|||
|
|
await this.softWareServiceBasic.SaveSoftwarePropertyData("subtitleSetting", JSON.stringify(defauleSetting));
|
|||
|
|
return defauleSetting
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 获取提起字幕的设置
|
|||
|
|
*/
|
|||
|
|
async GetSubtitleSetting(): Promise<GeneralResponse.ErrorItem | GeneralResponse.SuccessItem> {
|
|||
|
|
try {
|
|||
|
|
let subtitleSetting = undefined as SubtitleModel.subtitleSettingModel
|
|||
|
|
let subtitleSettingString = await this.softWareServiceBasic.GetSoftWarePropertyData('subtitleSetting');
|
|||
|
|
if (isEmpty(subtitleSettingString)) {
|
|||
|
|
// 初始化
|
|||
|
|
subtitleSetting = await this.InitSubtitleSetting();
|
|||
|
|
} else {
|
|||
|
|
if (ValidateJson(subtitleSettingString)) {
|
|||
|
|
subtitleSetting = JSON.parse(subtitleSettingString)
|
|||
|
|
} else {
|
|||
|
|
throw new Error("提起字幕设置解析失败,请重置后重新配置")
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
return successMessage(subtitleSetting, '获取提取字幕设置成功', "SubtitleService_GetSubtitleSetting")
|
|||
|
|
} catch (error) {
|
|||
|
|
return errorMessage("获取字幕设置失败,失败信息如下:" + error.message, "SubtitleService_GetSubtitleSetting")
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 重置识别字幕设置
|
|||
|
|
*/
|
|||
|
|
async ResetSubtitleSetting(): Promise<GeneralResponse.ErrorItem | GeneralResponse.SuccessItem> {
|
|||
|
|
try {
|
|||
|
|
let subtitleSetting = await this.InitSubtitleSetting();
|
|||
|
|
return successMessage(subtitleSetting, "重置字幕设置成功", "SubtitleService_ResetSubtitleSetting")
|
|||
|
|
} catch (error) {
|
|||
|
|
return errorMessage("重置字幕设置失败,失败信息如下:" + error.message, "SubtitleService_ResetSubtitleSetting")
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 保存提取字幕设置,并作相应的一些简单的检查
|
|||
|
|
* @param subtitleSetting 要保存的数据结构体
|
|||
|
|
*/
|
|||
|
|
async SaveSubtitleSetting(subtitleSetting: SubtitleModel.subtitleSettingModel): Promise<GeneralResponse.ErrorItem | GeneralResponse.SuccessItem> {
|
|||
|
|
try {
|
|||
|
|
// 判断模式,通过不同的模式判断是不是又必要检查
|
|||
|
|
if (subtitleSetting.selectModel == GetSubtitleType.LOCAL_OCR) {
|
|||
|
|
let localOcrPath = path.join(define.scripts_path, 'LaiOcr/LaiOcr.exe');
|
|||
|
|
let fileIsExists = await CheckFileOrDirExist(localOcrPath);
|
|||
|
|
if (!fileIsExists) {
|
|||
|
|
throw new Error("当前模式未本地OCR,但是没有检查到对应的执行文件,请查看教程,安装对应的拓展");
|
|||
|
|
}
|
|||
|
|
} else if (subtitleSetting.selectModel == GetSubtitleType.LOCAL_WHISPER) {
|
|||
|
|
// let localWhisper = path.join(define.scripts_path,'')
|
|||
|
|
// 这个好像没有什么可以检查的
|
|||
|
|
} else if (subtitleSetting.selectModel == GetSubtitleType.LAI_WHISPER) {
|
|||
|
|
// 判断是不是laitool的,不是的话报错
|
|||
|
|
if (!subtitleSetting.laiWhisper.url.includes('laitool')) {
|
|||
|
|
throw new Error('该模式只能试用LAI API的接口请求');
|
|||
|
|
}
|
|||
|
|
if (isEmpty(subtitleSetting.laiWhisper.apiKey)) {
|
|||
|
|
throw new Error("当前模式为LAI API的接口请求,请输入LAI API KEY")
|
|||
|
|
}
|
|||
|
|
if (isEmpty(subtitleSetting.laiWhisper.url)) {
|
|||
|
|
throw new Error("当前模式为LAI API的接口请求,请输入LAI API URL")
|
|||
|
|
}
|
|||
|
|
} else {
|
|||
|
|
throw new Error("未知的识别字幕模式")
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 检查做完,开始保存数据
|
|||
|
|
await this.softWareServiceBasic.SaveSoftwarePropertyData('subtitleSetting', JSON.stringify(subtitleSetting))
|
|||
|
|
return successMessage(null, "保存提取文案设置成功", "SubtitleService_SaveSubtitleSetting");
|
|||
|
|
} catch (error) {
|
|||
|
|
return errorMessage("保存提取文案设置失败,失败信息如下:" + error.message, "SubtitleService_SaveSubtitleSetting")
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
//#endregion
|
|||
|
|
|
|||
|
|
|
|||
|
|
|
|||
|
|
//#region 语音转文案或者是字幕识别
|
|||
|
|
/**
|
|||
|
|
* 反推提取文案
|
|||
|
|
*/
|
|||
|
|
async GetCopywriting(bookId: string, bookTaskId: string, operateBookType: OperateBookType): Promise<GeneralResponse.ErrorItem | GeneralResponse.SuccessItem> {
|
|||
|
|
try {
|
|||
|
|
let subtitleSettingRes = await this.GetSubtitleSetting();
|
|||
|
|
if (subtitleSettingRes.code == 0) {
|
|||
|
|
throw new Error(subtitleSettingRes.message)
|
|||
|
|
}
|
|||
|
|
let subtitleSetting = subtitleSettingRes.data as SubtitleModel.subtitleSettingModel;
|
|||
|
|
let res = undefined as GeneralResponse.ErrorItem | GeneralResponse.SuccessItem
|
|||
|
|
|
|||
|
|
let bookTaskDetails = undefined as Book.SelectBookTaskDetail[]
|
|||
|
|
|
|||
|
|
let tempBookTaskId = bookTaskId
|
|||
|
|
if (operateBookType == OperateBookType.BOOKTASK) {
|
|||
|
|
bookTaskDetails = await this.bookServiceBasic.GetBookTaskDetailData({
|
|||
|
|
bookId: bookId,
|
|||
|
|
bookTaskId: bookTaskId
|
|||
|
|
})
|
|||
|
|
} else if (operateBookType == OperateBookType.BOOKTASKDETAIL) {
|
|||
|
|
let tempBookTaskDetail = await this.bookServiceBasic.GetBookTaskDetailDataById(bookTaskId)
|
|||
|
|
tempBookTaskId = tempBookTaskDetail.bookTaskId
|
|||
|
|
bookTaskDetails = [tempBookTaskDetail]
|
|||
|
|
} else {
|
|||
|
|
throw new Error("未知的操作类型")
|
|||
|
|
}
|
|||
|
|
if (bookTaskDetails.length <= 0) {
|
|||
|
|
throw new Error("分镜信息不存在");
|
|||
|
|
}
|
|||
|
|
let { book, bookTask } = await this.bookServiceBasic.GetBookAndTask(bookId, tempBookTaskId)
|
|||
|
|
|
|||
|
|
switch (subtitleSetting.selectModel) {
|
|||
|
|
case GetSubtitleType.LOCAL_OCR:
|
|||
|
|
res = await this.subtitle.GetCopywritingByLocalOcr(book, bookTask, bookTaskDetails)
|
|||
|
|
break;
|
|||
|
|
case GetSubtitleType.LOCAL_WHISPER:
|
|||
|
|
throw new Error("本地Whisper暂时不支持")
|
|||
|
|
break;
|
|||
|
|
case GetSubtitleType.LAI_WHISPER:
|
|||
|
|
res = await this.subtitle.GetCopywritingByLaiWhisper(book, bookTask, bookTaskDetails, subtitleSetting)
|
|||
|
|
break;
|
|||
|
|
default:
|
|||
|
|
throw new Error("未知的识别字幕模式")
|
|||
|
|
}
|
|||
|
|
if (operateBookType == OperateBookType.BOOKTASKDETAIL) {
|
|||
|
|
let bookTaskDetail = await this.bookServiceBasic.GetBookTaskDetailDataById(bookTaskId)
|
|||
|
|
return successMessage(bookTaskDetail.afterGpt, "获取文案成功", "ReverseBook_GetCopywriting")
|
|||
|
|
} else {
|
|||
|
|
return res
|
|||
|
|
}
|
|||
|
|
} catch (error) {
|
|||
|
|
return errorMessage("获取分镜数据失败,失败信息如下:" + error.message, 'ReverseBook_GetCopywriting')
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 导出指定导出指定小说的文案
|
|||
|
|
* @param bookTaskId 小说批次任务ID
|
|||
|
|
* @returns
|
|||
|
|
*/
|
|||
|
|
async ExportCopywriting(bookTaskId: string): Promise<GeneralResponse.ErrorItem | GeneralResponse.SuccessItem> {
|
|||
|
|
try {
|
|||
|
|
let bookTask = await this.bookServiceBasic.GetBookTaskDataId(bookTaskId)
|
|||
|
|
let book = await this.bookServiceBasic.GetBookDataById(bookTask.bookId)
|
|||
|
|
let bookTaskDetails = await this.bookServiceBasic.GetBookTaskDetailData({
|
|||
|
|
bookId: book.id,
|
|||
|
|
bookTaskId: bookTaskId
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
let emptyList = []
|
|||
|
|
let content = []
|
|||
|
|
|
|||
|
|
// 检查是不是所有的里面都有文案
|
|||
|
|
for (let i = 0; i < bookTaskDetails.length; i++) {
|
|||
|
|
const element = bookTaskDetails[i];
|
|||
|
|
if (isEmpty(element.afterGpt)) {
|
|||
|
|
emptyList.push(element.name)
|
|||
|
|
} else {
|
|||
|
|
content.push(element.afterGpt)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
if (emptyList.length > 0) {
|
|||
|
|
throw new Error(`以下分镜没有文案:${emptyList.join("\n")}`);
|
|||
|
|
}
|
|||
|
|
// 写出文案
|
|||
|
|
let contentStr = content.join("。\n");
|
|||
|
|
contentStr = contentStr + '。'
|
|||
|
|
let wordPath = path.join(book.bookFolderPath, "文案.txt")
|
|||
|
|
await fs.promises.writeFile(wordPath, contentStr, 'utf-8')
|
|||
|
|
return successMessage(wordPath, "导出文案成功", "ReverseBook_ExportCopywriting")
|
|||
|
|
} catch (error) {
|
|||
|
|
return errorMessage("导出文案失败,失败信息如下:" + error.message, 'ReverseBook_ExportCopywriting')
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
//#endregion
|
|||
|
|
}
|