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 { TaskScheduler } from "../taskScheduler"; 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 { 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 { 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 { 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 { 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 语音转文案或者是字幕识别 /** * 反推提取文案的入口方法 * @param bookId 小说ID * @param bookTaskId 小说批次任务ID * @param operateBookType 操作的小说类型 * @param coverData 是不是要覆盖旧的数据 * @returns */ async GetCopywriting(bookId: string, bookTaskId: string, operateBookType: OperateBookType, coverData: boolean): Promise { 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 (!coverData) { // 不覆盖数据,将已经有的数据过滤掉 bookTaskDetails = bookTaskDetails.filter(item => isEmpty(item.afterGpt) && isEmpty(item.word)) } 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 { 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 }