1. (聚合推文)修复原创默认出图方式设置默认值 2. (聚合推文)优化出图显示 3. (聚合推文)添加图片上传功能,包括主图和选图区域 4. (聚合推文)新增图片缓存区,上传缓存图片(主图和选图区的图片),可以直接在当前小说所有的批次中的分镜中调用(下载到主图和选图区) 5. (聚合推文)添加一键修脸开关 6. (聚合推文)原创添加一键锁定 7. (聚合推文)新增小说批次任务显示当前所属小说 8. (聚合推文)小说批次任务状态显示优化 9. (聚合推文)优化后台任务状态显示 10. (聚合推文)原创,反推 一键生图 修复
902 lines
29 KiB
TypeScript
902 lines
29 KiB
TypeScript
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
|
||
}
|