LaiTool/src/main/Service/Book/basicReverse.ts
lq1405 6fa58e4d94 V 3.2.1 2024.11.9
1. (聚合推文)修复原创默认出图方式设置默认值
2. (聚合推文)优化出图显示
3. (聚合推文)添加图片上传功能,包括主图和选图区域
4. (聚合推文)新增图片缓存区,上传缓存图片(主图和选图区的图片),可以直接在当前小说所有的批次中的分镜中调用(下载到主图和选图区)
5. (聚合推文)添加一键修脸开关
6. (聚合推文)原创添加一键锁定
7. (聚合推文)新增小说批次任务显示当前所属小说
8. (聚合推文)小说批次任务状态显示优化
9. (聚合推文)优化后台任务状态显示
10. (聚合推文)原创,反推 一键生图 修复
2024-11-09 16:46:06 +08:00

902 lines
29 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 path from 'path'
import fs from 'fs'
const util = require('util')
const { exec } = require('child_process')
const execAsync = util.promisify(exec)
import { define } from '../../../define/define'
import { BookService } from '../../../define/db/service/Book/bookService'
import { LogScheduler } from '../task/logScheduler'
import { LoggerStatus, LoggerType, OtherData } from '../../../define/enum/softwareEnum'
import { errorMessage, successMessage } from '../../Public/generalTools'
import { CheckFileOrDirExist, CheckFolderExistsOrCreate } from '../../../define/Tools/file'
import { BookTaskDetailService } from '../../../define/db/service/Book/bookTaskDetailService'
import { BookBackTaskListService } from '../../../define/db/service/Book/bookBackTaskListService'
import { BookTaskService } from '../../../define/db/service/Book/bookTaskService'
import { isEmpty, set } from 'lodash'
import { TimeStringToMilliseconds, MillisecondsToTimeString } from '../../../define/Tools/time'
import { FfmpegOptions } from '../ffmpegOptions'
import {
BookBackTaskType,
BookTaskStatus,
BookType,
TaskExecuteType
} from '../../../define/enum/bookEnum'
import { Book } from '../../../model/book/book'
import { GeneralResponse } from '../../../model/generalResponse'
const fspromises = fs.promises
/**
* 后台执行的任务函数,直接调用改函数即可,抽帧,分镜,提取字幕等
*/
export class BasicReverse {
bookService: BookService
bookTaskService: BookTaskService
bookTaskDetailService: BookTaskDetailService
bookBackTaskListService: BookBackTaskListService
logScheduler: LogScheduler
ffmpegOptions: FfmpegOptions
constructor() {
this.logScheduler = new LogScheduler()
this.ffmpegOptions = new FfmpegOptions()
}
/**
* 初始化服务
*/
async InitService() {
if (!this.bookService) {
this.bookService = await BookService.getInstance()
}
if (!this.bookTaskService) {
this.bookTaskService = await BookTaskService.getInstance()
}
if (!this.bookTaskDetailService) {
this.bookTaskDetailService = await BookTaskDetailService.getInstance()
}
if (!this.bookBackTaskListService) {
this.bookBackTaskListService = await BookBackTaskListService.getInstance()
}
}
//#region 计算视频的分镜,操作
/**
* 添加分镜计算的任务
* @param {*} bookId
*/
async AddFrameDataTask(bookId) {
try {
await this.InitService()
// 获取对应的小说小说数据,找到对应的小说视频地址
let bookQuery = {
bookId: bookId
}
let bookData = this.bookService.GetBookData(bookQuery)
if (bookData.code == 0) {
return bookData
}
if (bookData.data.book_length <= 0 || bookData.data.res_book.length <= 0) {
throw new Error('没有找到对应的小说数据请检查bookId是否正确')
}
// 获取小说对应的批次任务数据,默认初始化为第一个
let bookTaskRes = await this.bookTaskService.GetBookTaskData({
bookId: bookId,
name: 'output_00001'
})
if (bookTaskRes.data.bookTasks.length <= 0 || bookTaskRes.data.total <= 0) {
throw new Error('没有找到对应的小说批次任务数据请检查bookId是否正确')
}
// 获取小说的视频地址
let book = bookData.data.res_book[0]
let bookTask = bookTaskRes.data.bookTasks[0]
// 开始添加任务
let taskRes = await this.bookBackTaskListService.AddBookBackTask(
bookId,
BookBackTaskType.STORYBOARD,
TaskExecuteType.AUTO,
bookTask.id,
null
)
if (taskRes.code == 0) {
throw new Error(taskRes.message)
}
this.logScheduler.AddLogToDB(
bookId,
book.type,
`添加分镜任务成功`,
OtherData.DEFAULT,
LoggerStatus.SUCCESS
)
return successMessage(null, '添加分镜任务成功', 'BasicReverse_AddFrameDataTask')
} catch (error) {
throw error
}
}
/**
* 执行分镜任务
* @param bookId 小说ID
* @param bookTaskId 小说人物ID只能是第一个
* @returns
*/
async ComputeStoryboardFunc(bookId: string, bookTaskId: string): Promise<string> {
await this.InitService()
let book = this.bookService.GetBookDataById(bookId)
if (book == null) {
throw new Error('没有找到对应的小说数据')
}
this.bookTaskService.UpdateBookTaskStatus(bookTaskId, BookTaskStatus.STORYBOARD)
// 分镜之前,删除之前的老数据
let deleteBookTaskRes = this.bookTaskDetailService.DeleteBookTaskDetail({
bookId: bookId,
bookTaskId: bookTaskId
})
let oldVideoPath = book.oldVideoPath
let frameJson = oldVideoPath + '.json'
let sensitivity = 30
// 开始之前,推送日志
let log_content = `开始进行分镜操作,视频地址:${oldVideoPath},敏感度:${sensitivity},正在调用程序进行处理`
await this.logScheduler.AddLogToDB(
bookId,
book.type,
log_content,
OtherData.DEFAULT,
LoggerStatus.DOING
)
// 小说进行分镜python进行将结果写道一个json里面
// 使用异步的方法调用一个python程序然后写入到指定的json文件中k
let command = `"${path.join(
define.scripts_path,
'Lai.exe'
)}" "-ka" "${oldVideoPath}" "${frameJson}" "${sensitivity}"`
const output = await execAsync(command, {
maxBuffer: 1024 * 1024 * 10,
encoding: 'utf-8'
})
// 有错误输出
if (output.stderr != '') {
let error_msg = `分镜成功,但有警告提示:${output.stderr}`
await this.logScheduler.AddLogToDB(
bookId,
book.type,
error_msg,
OtherData.DEFAULT,
LoggerStatus.FAIL
)
}
// 分镜成功,处理输出
let josnIsExist = await CheckFileOrDirExist(frameJson)
if (!josnIsExist) {
let error_message = `分镜失败,没有找到对应的分镜输出文件:${frameJson}`
this.bookTaskService.UpdateBookTaskStatus(
bookTaskId,
BookTaskStatus.STORYBOARD_FAIL,
error_message
)
await this.logScheduler.AddLogToDB(
bookId,
book.type,
error_message,
OtherData.DEFAULT,
LoggerStatus.FAIL
)
throw new Error(error_message)
}
let frameJsonData = JSON.parse(await fspromises.readFile(frameJson, 'utf-8'))
if (frameJsonData.length <= 0) {
let error_msg = `分镜失败,没有找到对应的分镜数据`
this.bookTaskService.UpdateBookTaskStatus(
bookTaskId,
BookTaskStatus.STORYBOARD_FAIL,
error_msg
)
await this.logScheduler.AddLogToDB(
bookId,
book.type,
error_msg,
OtherData.DEFAULT,
LoggerStatus.FAIL
)
throw new Error(error_msg)
}
// 循环写入小说人物详细数据
for (let i = 0; i < frameJsonData.length; i++) {
let dataArray = frameJsonData[i]
let bookTaskDetail = {
bookId: bookId,
bookTaskId: bookTaskId,
startTime: 0,
endTime: 0
} as Book.SelectBookTaskDetail
// 将字符串转换为number
bookTaskDetail.startTime = TimeStringToMilliseconds(dataArray[0])
bookTaskDetail.endTime = TimeStringToMilliseconds(dataArray[1])
bookTaskDetail.status = BookTaskStatus.STORYBOARD_DONE // 分镜完成
let res = this.bookTaskDetailService.AddBookTaskDetail(bookTaskDetail)
if (res.code == 0) {
throw new Error(res.message)
}
}
this.bookTaskService.UpdateBookTaskStatus(bookTaskId, BookTaskStatus.STORYBOARD_DONE)
// 分镜成功,推送日志
await this.logScheduler.AddLogToDB(
bookId,
book.type,
`分镜成功,分镜数据如下:${frameJsonData}`,
OtherData.DEFAULT,
LoggerStatus.SUCCESS
)
return `分镜成功,分镜信息在 ${frameJson}`
}
/**
* 执行分镜任务通过task获取
* @param {*} task
* @returns
*/
async GetFrameData(task) {
try {
// 分镜任务一定有对应的小说ID和对应的小说批次任务ID没有直接报错
if (isEmpty(task.bookId) || isEmpty(task.bookTaskId)) {
throw new Error('分镜任务bookId, bookTaskId不能为空')
}
await this.InitService()
let bookId = task.bookId
let bookTaskId = task.bookTaskId
let frameRes = await this.ComputeStoryboardFunc(bookId, bookTaskId)
return successMessage(frameRes)
} catch (error) {
throw error
}
}
//#endregion 计算视频的分镜
//#region 裁剪视频的相关操作
/**
* 添加裁剪视频的任务
* @param {*} bookId 小说ID
* @param {*} frameJson 存放分镜数据的json文件地址
*/
async AddCutVideoDataTask(bookId) {
try {
if (isEmpty(bookId)) {
throw new Error('bookId不能为空')
}
await this.InitService()
// 判断小说是不是存在
let book = this.bookService.GetBookDataById(bookId)
if (book == null) {
throw new Error('没有找到对应的小说数据')
}
// 找到对应的小说ID和对应的小说批次任务ID判断是不是有分镜数据
let bookTaskRes = this.bookTaskService.GetBookTaskData({
bookId: bookId,
name: 'output_00001'
})
if (bookTaskRes.data.bookTasks.length <= 0 || bookTaskRes.data.total <= 0) {
throw new Error('没有找到对应的小说批次任务数据请检查bookId是否正确')
}
let bookTask = bookTaskRes.data.bookTasks[0]
let bookTaskDetail = this.bookTaskDetailService.GetBookTaskData({
bookId: bookId,
bookTaskId: bookTask.id
})
if (bookTaskDetail.data.length <= 0) {
// 传入的分镜数据为空,需要重新获取
await this.logScheduler.AddLogToDB(
bookId,
book.type,
`没有传入分镜数据,开始调用分镜方法`,
OtherData.DEFAULT,
LoggerStatus.DOING
)
let frameRes = await this.GetFrameData(bookId)
if (frameRes.code == 0) {
throw new Error(frameRes.message)
}
}
bookTaskDetail = this.bookTaskDetailService.GetBookTaskData({
bookId: bookId,
bookTaskId: bookTask.id
})
if (bookTaskDetail.data.length <= 0) {
this.bookTaskService.UpdateBookTaskStatus(
bookTask.id,
BookTaskStatus.SPLIT_FAIL,
'重新调用分镜方法还是没有分镜数据,请检查'
)
throw new Error('重新调用分镜方法还是没有分镜数据,请检查')
}
this.bookTaskService.UpdateBookTaskStatus(bookTask.id, BookTaskStatus.SPLIT)
// 有分镜数据,开始处理
await this.logScheduler.AddLogToDB(
bookId,
book.type,
`成功获取分镜数据,开始添加裁剪视频任务`,
OtherData.DEFAULT,
LoggerStatus.SUCCESS
)
// 添加裁剪视频任务
for (let i = 0; i < bookTaskDetail.data.length; i++) {
const element = bookTaskDetail.data[i]
// 创建后台的视频分割任务
let taskRes = this.bookBackTaskListService.AddBookBackTask(
bookId,
BookBackTaskType.SPLIT,
TaskExecuteType.AUTO,
bookTask.id,
element.id
)
if (taskRes.code == 0) {
throw new Error(taskRes.message)
}
}
// 添加日志
await this.logScheduler.AddLogToDB(
bookId,
book.type,
`添加视频裁剪任务成功`,
OtherData.DEFAULT,
LoggerStatus.SUCCESS
)
return successMessage(null, '添加视频裁剪任务成功', 'BasicReverse_AddCutVideoDataTask')
} catch (error) {
throw error
}
}
/**
* 裁剪视频操作
* @param bookTaskDetailId 小说详细的分镜ID
* @returns
*/
async FrameDataToCutVideoData(bookTaskDetail: Book.SelectBookTaskDetail, frameShortClipData: Book.BookFrameShortClip): Promise<string> {
await this.InitService()
let startTime = bookTaskDetail.startTime
let endTime = bookTaskDetail.endTime
let book = this.bookService.GetBookDataById(bookTaskDetail.bookId)
if (book == null) {
throw new Error('没有找到对应的小说数据')
}
if (startTime == null || endTime == null) {
this.bookTaskService.UpdateBookTaskStatus(
bookTaskDetail.bookTaskId,
BookTaskStatus.SPLIT_FAIL,
'开始时间和结束时间不能为空'
)
throw new Error('开始时间和结束时间不能为空')
}
let outVideoFile = path.join(book.bookFolderPath, `data/frame/${bookTaskDetail.name}.mp4`)
let res = await this.ffmpegOptions.FfmpegCutVideo(
frameShortClipData.startTime,
frameShortClipData.endTime,
frameShortClipData.videoPath,
outVideoFile
)
if (res.code == 0) {
this.bookTaskService.UpdateBookTaskStatus(
bookTaskDetail.bookTaskId,
BookTaskStatus.SPLIT_FAIL,
res.message
)
throw new Error(res.message)
}
// 视频裁剪完成,要将裁剪后的视频地址写入到数据库中
this.bookTaskDetailService.UpdateBookTaskDetail(bookTaskDetail.id, {
videoPath: path.relative(define.project_path, outVideoFile)
})
// 小改小说批次的状态
this.bookTaskService.UpdateBookTaskStatus(bookTaskDetail.bookTaskId, BookTaskStatus.SPLIT_DONE)
// 结束,分镜完毕,推送日志,返回成功
await this.logScheduler.AddLogToDB(
bookTaskDetail.bookId,
book.type,
`${bookTaskDetail.name}_视频裁剪完成`,
OtherData.DEFAULT,
LoggerStatus.SUCCESS
)
return `${bookTaskDetail.name}_视频裁剪完成`;
}
/**
* 开始执行指定的任务
* @param {*} task
* @returns
*/
async CutVideoData(task: TaskModal.Task): Promise<GeneralResponse.ErrorItem | GeneralResponse.SuccessItem> {
try {
// 视频分割任务一定有对应的小说ID和对应的小说批次任务ID和对应的分镜ID没有直接报错
if (isEmpty(task.bookId) || isEmpty(task.bookTaskId) || isEmpty(task.bookTaskDetailId)) {
throw new Error('分镜任务bookId, bookTaskId, bookTaskDetailId不能为空')
}
await this.InitService()
let bookTaskDetail = this.bookTaskDetailService.GetBookTaskDetailDataById(
task.bookTaskDetailId
)
if (bookTaskDetail == null) {
throw new Error('没有找到对应的分镜数据')
}
// let cur_res = await this.FrameDataToCutVideoData(bookTaskDetail, null);
return successMessage(null, `${task.name}_视频裁剪完成`, "BasicReverse_CutVideoData");
} catch (error) {
throw error
}
}
//#endregion
//#region 分离音频的相关操作
/**
* 添加视频分离音频的任务
* @param {*} bookId 小说ID
* @param {*} bookTaskId 小说任务ID
* @returns
*/
async AddSplitAudioDataTask(bookId, bookTaskId = null) {
try {
await this.InitService()
let book = this.bookService.GetBookDataById(bookId)
if (book == null) {
throw new Error('没有找到对应的小说数据')
}
let bookTaskRes
if (bookTaskId != null) {
bookTaskRes = await this.bookTaskService.GetBookTaskData({ id: bookTaskId })
} else {
bookTaskRes = await this.bookTaskService.GetBookTaskData({
bookId: bookId,
name: 'output_00001'
})
}
if (bookTaskRes.data.bookTasks.length <= 0 || bookTaskRes.data.total <= 0) {
await this.logScheduler.AddLogToDB(
bookId,
book.type,
`没有找到对应的小说批次任务数据请检查bookId是否正确`,
OtherData.DEFAULT,
LoggerStatus.FAIL
)
throw new Error('没有找到对应的小说批次任务数据请检查bookId是否正确')
}
let bookTask = bookTaskRes.data.bookTasks[0]
// 获取对应小说批次任务的分镜信息
let bookTaskDetails = this.bookTaskDetailService.GetBookTaskData({
bookId: bookId,
bookTaskId: bookTask.id
})
if (bookTaskDetails.data.length <= 0) {
await this.logScheduler.AddLogToDB(
bookId,
book.type,
`没有找到对应的小说批次任务数据请检查bookId是否正确或者手动执行`,
bookTask.id,
LoggerStatus.FAIL
)
throw new Error('没有找到对应的小说批次任务数据请检查bookId是否正确或者手动执行')
}
// 开始添加任务
for (let i = 0; i < bookTaskDetails.data.length; i++) {
const element = bookTaskDetails.data[i]
let taskRes = await this.bookBackTaskListService.AddBookBackTask(
bookId,
BookBackTaskType.AUDIO,
TaskExecuteType.AUTO,
bookTask.id,
element.id
)
if (taskRes.code == 0) {
throw new Error(taskRes.message)
}
// 添加日志
await this.logScheduler.AddLogToDB(
bookId,
book.type,
`添加音频 ${taskRes.data.name} 分离任务成功`,
OtherData.DEFAULT,
LoggerStatus.SUCCESS
)
}
return successMessage(null, `添加所有音频分离任务成功`, 'BasicReverse_AddSplitAudioDataTask')
} catch (error) {
throw error
}
}
/**
* 执行分离音频任务
* @param {*} task 任务对象
* @returns
*/
async SplitAudioData(task) {
try {
// 执行分离音频任务一定有对应的小说ID和对应的小说批次任务ID和对应的分镜ID没有直接报错
if (isEmpty(task.bookId) || isEmpty(task.bookTaskId) || isEmpty(task.bookTaskDetailId)) {
throw new Error('分镜任务bookId, bookTaskId, bookTaskDetailId不能为空')
}
await this.InitService()
let book = this.bookService.GetBookDataById(task.bookId)
if (book == null) {
throw new Error('没有找到对应的小说数据')
}
let bookTaskDetail = this.bookTaskDetailService.GetBookTaskDetailDataById(
task.bookTaskDetailId
)
if (bookTaskDetail == null) {
throw new Error('没有找到对应的分镜数据')
}
let videoPath = bookTaskDetail.videoPath
let audioPath = path.join(book.bookFolderPath, `data/audio/${bookTaskDetail.name}.wav`)
await CheckFolderExistsOrCreate(path.dirname(audioPath))
// 开始分离音频
let audioRes = await this.ffmpegOptions.FfmpegExtractAudio(videoPath, audioPath)
if (audioRes.code == 0) {
let errorMessage = `分离音频失败,错误信息如下:${audioRes.message}`
this.bookTaskService.UpdateBookTaskStatus(
task.bookTaskId,
BookTaskStatus.AUDIO_FAIL,
errorMessage
)
throw new Error(audioRes.message)
}
this.bookTaskDetailService.UpdateBookTaskDetail(bookTaskDetail.id, {
audioPath: path.relative(define.project_path, audioPath)
})
// 推送成功消息
await this.logScheduler.AddLogToDB(
task.bookId,
book.type,
`${bookTaskDetail.name}分离音频成功,输出地址:${audioPath}`,
OtherData.DEFAULT,
LoggerStatus.SUCCESS
)
// 修改状态为分离音频成功
this.bookTaskService.UpdateBookTaskStatus(task.bookTaskId, BookTaskStatus.AUDIO_DONE)
return successMessage(
null,
`${bookTaskDetail.name}分离音频成功,输出地址:${audioPath}`,
'BasicReverse_SplitAudioData'
)
} catch (error) {
throw error
}
}
//#endregion
//#region 开始抽帧
// 添加抽帧任务
async AddGetFrameTask(bookId, bookTaskId = null) {
try {
// 开始添加任务
await this.InitService()
let book = this.bookService.GetBookDataById(bookId)
if (book == null) {
throw new Error('没有找到对应的小说数据')
}
let bookTaskRes
if (bookTaskId != null) {
bookTaskRes = await this.bookTaskService.GetBookTaskData({ id: bookTaskId })
} else {
bookTaskRes = await this.bookTaskService.GetBookTaskData({
bookId: bookId,
name: 'output_00001'
})
}
if (bookTaskRes.data.bookTasks.length <= 0 || bookTaskRes.data.total <= 0) {
throw new Error('没有找到对应的小说批次任务数据请检查bookId是否正确')
}
let bookTask = bookTaskRes.data.bookTasks[0]
let bookTaskDetails = this.bookTaskDetailService.GetBookTaskData({
bookId: bookId,
bookTaskId: bookTask.id
})
if (bookTaskDetails.data.length <= 0) {
throw new Error('没有找到对应的小说批次任务数据请检查bookId是否正确或者手动执行')
}
for (let i = 0; i < bookTaskDetails.data.length; i++) {
const element = bookTaskDetails.data[i]
let taskRes = await this.bookBackTaskListService.AddBookBackTask(
bookId,
BookBackTaskType.FRAME,
TaskExecuteType.AUTO,
bookTask.id,
element.id
)
if (taskRes.code == 0) {
throw new Error(taskRes.message)
}
await this.logScheduler.AddLogToDB(
bookId,
book.type,
`添加 ${taskRes.data.name} 抽帧任务成功`,
OtherData.DEFAULT,
LoggerStatus.SUCCESS
)
}
return successMessage(null, '添加所有抽帧任务成功', 'BasicReverse_AddGetFrameTask')
} catch (error) {
throw error
}
}
/**
* 对分镜人物进行抽帧
* @param book 小说对象
* @param bookTaskDetail 小说对应的详细信息的对象
* @returns
*/
async FrameFunc(book: Book.SelectBook, bookTaskDetail: Book.SelectBookTaskDetail): Promise<string> {
let videoPath = bookTaskDetail.videoPath
let outputFramePath = path.join(
book.bookFolderPath,
`tmp/input/${bookTaskDetail.name}.png`
)
let frameTime = (bookTaskDetail.endTime - bookTaskDetail.startTime) / 2
let res = await this.ffmpegOptions.FfmpegGetFrame(frameTime, videoPath, outputFramePath)
if (res.code == 0) {
let errorMessage = `抽帧失败,错误信息如下:${res.message}`
this.bookTaskService.UpdateBookTaskStatus(
bookTaskDetail.bookTaskId,
BookTaskStatus.FRAME_FAIL,
errorMessage
)
throw new Error(errorMessage)
}
// 抽帧成功,将抽帧的地址写入到数据库中
this.bookTaskDetailService.UpdateBookTaskDetail(bookTaskDetail.id, {
oldImage: path.relative(define.project_path, outputFramePath)
})
// 推送成功消息
await this.logScheduler.AddLogToDB(
book.id,
book.type,
`${bookTaskDetail.name}抽帧成功,输出地址:${outputFramePath}`,
OtherData.DEFAULT,
LoggerStatus.SUCCESS
)
// 修改状态为抽帧成功
this.bookTaskService.UpdateBookTaskStatus(bookTaskDetail.bookTaskId, BookTaskStatus.FRAME_DONE)
return `${bookTaskDetail.name}抽帧成功,输出地址:${outputFramePath}`
}
/**
* 开始执行抽帧任务
* @param {*} task 执行的任务
*/
async GetFrame(task) {
try {
// 抽帧任务一定有对应的小说ID和对应的小说批次任务ID和对应的分镜ID没有直接报错
if (isEmpty(task.bookId) || isEmpty(task.bookTaskId) || isEmpty(task.bookTaskDetailId)) {
throw new Error('分镜任务bookId, bookTaskId, bookTaskDetailId不能为空')
}
await this.InitService()
let book = this.bookService.GetBookDataById(task.bookId)
if (book == null) {
throw new Error('没有找到对应的小说数据')
}
let bookTaskDetail = this.bookTaskDetailService.GetBookTaskDetailDataById(
task.bookTaskDetailId
)
if (bookTaskDetail == null) {
throw new Error('没有找到对应的分镜数据')
}
let res = await this.FrameFunc(book, bookTaskDetail);
return successMessage(null, res, 'BasicReverse_GetFrame')
} catch (error) {
throw error
}
}
//#endregion
//#region 提取字幕相关操作
/**
* 添加提取字幕的任务
* @param {*} bookId 小说ID
* @param {*} bookTaskId 小说任务ID
* @returns
*/
async AddExtractSubtitlesDataTask(bookId, bookTaskId = null) {
try {
await this.InitService()
let book = this.bookService.GetBookDataById(bookId)
if (book == null) {
throw new Error('没有找到对应的小说数据')
}
let bookTask
if (bookTaskId != null) {
bookTaskId = await this.bookTaskService.GetBookTaskData({ id: bookTaskId })
} else {
bookTask = await this.bookTaskService.GetBookTaskData({
bookId: bookId,
name: 'output_00001'
})
}
if (bookTask.data.bookTasks.length <= 0 || bookTask.data.total <= 0) {
throw new Error('没有找到对应的小说批次任务数据请检查bookId是否正确')
}
bookTask = bookTask.data.bookTasks[0]
// 获取对应小说批次任务的分镜信息
let bookTaskDetails = this.bookTaskDetailService.GetBookTaskData({
bookId: bookId,
bookTaskId: bookTask.id
})
if (bookTaskDetails.data.length <= 0) {
throw new Error('没有找到对应的小说批次任务数据请检查bookId是否正确或者手动执行')
}
for (let i = 0; i < bookTaskDetails.data.length; i++) {
const element = bookTaskDetails.data[i]
let taskRes = await this.bookBackTaskListService.AddBookBackTask(
bookId,
BookBackTaskType.RECOGNIZE,
TaskExecuteType.AUTO,
bookTask.id,
element.id
)
if (taskRes.code == 0) {
throw new Error(taskRes.message)
}
}
await this.logScheduler.AddLogToDB(
bookId,
book.type,
`添加提取字幕任务成功`,
OtherData.DEFAULT,
LoggerStatus.SUCCESS
)
return successMessage(
null,
'添加提取字幕任务成功 ',
'BasicReverse_AddExtractSubtitlesDataTask '
)
} catch (error) {
throw error
}
}
async GetCopywritingFunc(book: Book.SelectBook, bookTaskDetail: Book.SelectBookTaskDetail): Promise<string> {
let txt = ''
// 开始提取,调用本地的服务识别字幕
let isWisper = true
// 判断是不是用本地的wisper服务
if (isWisper) {
// 开始调用wisper
// 使用异步的方法调用一个python程序然后写入到指定的json文件中
let out_dir = path.dirname(bookTaskDetail.videoPath)
// #TODO -t 被移除
let command = `"${path.join(define.scripts_path, 'Lai.exe')}" "-t" "${out_dir}" "${bookTaskDetail.audioPath
}" "${bookTaskDetail.name}"`
const output = await execAsync(command, {
maxBuffer: 1024 * 1024 * 10,
encoding: 'utf-8'
})
// 有错误输出
if (output.stderr != '') {
let error_msg = `提取字幕成功,但有警告提示:${output.stderr}`
await this.logScheduler.AddLogToDB(
book.id,
book.type,
error_msg,
OtherData.DEFAULT,
LoggerStatus.FAIL
)
}
// 没有的话,去读取对应的字幕文件
let outTxtPath = path.join(out_dir, `${bookTaskDetail.name}.txt`)
txt = await fspromises.readFile(outTxtPath, 'utf-8')
} else {
// 使用网络服务
}
// 修改分镜数据的字幕
this.bookTaskDetailService.UpdateBookTaskDetail(bookTaskDetail.id, {
word: txt,
afterGpt: txt
})
// 提取字幕成功,推送日志
await this.logScheduler.AddLogToDB(
book.id,
book.type,
`${bookTaskDetail.name} 提取字幕成功`,
OtherData.DEFAULT,
LoggerStatus.SUCCESS
)
return txt;
}
/**
* 执行提取字幕任务
* @param {*} task
* @returns
*/
async ExtractSubtitlesData(task) {
try {
// 执行提取字幕任务一定有对应的小说ID和对应的小说批次任务ID和对应的分镜ID没有直接报错
if (isEmpty(task.bookId) || isEmpty(task.bookTaskId) || isEmpty(task.bookTaskDetailId)) {
throw new Error('分镜任务bookId, bookTaskId, bookTaskDetailId不能为空')
}
await this.InitService()
// 获取详细的小说分镜信息
let bookTaskDetail = this.bookTaskDetailService.GetBookTaskDetailDataById(
task.bookTaskDetailId
)
if (bookTaskDetail == null) {
throw new Error('没有找到对应的分镜数据')
}
let book = this.bookService.GetBookDataById(task.bookId)
if (book == null) {
throw new Error('没有找到对应的小说数据')
}
let res = await this.GetCopywritingFunc(book, bookTaskDetail)
return successMessage(null, res, 'BasicReverse_ExtractSubtitlesData')
} catch (error) {
throw error
}
}
//#endregion
}