LaiTool V3.0.1-preview.7
This commit is contained in:
parent
07f99708da
commit
223678f761
2
.gitignore
vendored
2
.gitignore
vendored
@ -28,3 +28,5 @@ resources/config*
|
||||
*Lai_1.exe*
|
||||
.DS_Store
|
||||
*.log*
|
||||
resources/scripts/db/book.realm.lock
|
||||
resources/scripts/db/software.realm.lock
|
||||
|
||||
4
package-lock.json
generated
4
package-lock.json
generated
@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "laitool",
|
||||
"version": "3.0.1-preview.6",
|
||||
"version": "3.0.1-preview.7",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "laitool",
|
||||
"version": "3.0.1-preview.6",
|
||||
"version": "3.0.1-preview.7",
|
||||
"hasInstallScript": true,
|
||||
"dependencies": {
|
||||
"@alicloud/alimt20181012": "^1.2.0",
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "laitool",
|
||||
"version": "3.0.1-preview.6",
|
||||
"version": "3.0.1-preview.7",
|
||||
"description": "An AI tool for image processing, video processing, and other functions.",
|
||||
"main": "./out/main/index.js",
|
||||
"author": "laitool.cn",
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -236,3 +236,20 @@ export async function GetFileSize(filePath: string): Promise<number> {
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文件夹下的所有子文件夹的完整路径
|
||||
* @param folderPath 文件夹的路径
|
||||
* @returns 返回子文件夹的完整路径数组
|
||||
*/
|
||||
export async function GetSubdirectories(folderPath: string): Promise<string[]> {
|
||||
try {
|
||||
const files = await fs.promises.readdir(folderPath, { withFileTypes: true });
|
||||
const directories = files
|
||||
.filter((fileStat) => fileStat.isDirectory())
|
||||
.map((fileStat) => path.join(folderPath, fileStat.name));
|
||||
return directories;
|
||||
} catch (error) {
|
||||
throw new Error(error);
|
||||
}
|
||||
}
|
||||
|
||||
@ -13,6 +13,7 @@ export class SoftwareModel extends Realm.Object<SoftwareModel> {
|
||||
aiSetting: string | null // AI相关的配置的json字符串
|
||||
watermarkSetting: string | null // 水印相关的配置的json字符串
|
||||
translationSetting: string | null // 翻译相关的配置的json字符串
|
||||
subtitleSetting: string | null // 字幕相关的配置的json字符串
|
||||
|
||||
static schema: ObjectSchema = {
|
||||
name: 'Software',
|
||||
@ -27,7 +28,8 @@ export class SoftwareModel extends Realm.Object<SoftwareModel> {
|
||||
writeSetting: 'string?',
|
||||
aiSetting: 'string?',
|
||||
watermarkSetting: 'string?',
|
||||
translationSetting: 'string?'
|
||||
translationSetting: 'string?',
|
||||
subtitleSetting: "string?"
|
||||
},
|
||||
// 主键为_id
|
||||
primaryKey: 'id'
|
||||
|
||||
@ -135,13 +135,13 @@ export class BookBackTaskListService extends BaseRealmService {
|
||||
* 新增一个小说相关的后台任务队列
|
||||
* @param bookBackTask 要添加的小说数据
|
||||
*/
|
||||
async AddBookBackTask(
|
||||
AddBookBackTask(
|
||||
bookId: string,
|
||||
taskType: BookBackTaskType,
|
||||
executeType = TaskExecuteType.AUTO,
|
||||
bookTaskId = null,
|
||||
bookTaskDetailId = null
|
||||
) {
|
||||
): GeneralResponse.SuccessItem | GeneralResponse.ErrorItem {
|
||||
try {
|
||||
// 通过bookid获取book信息
|
||||
let book = this.realm.objectForPrimaryKey('Book', bookId)
|
||||
|
||||
@ -11,6 +11,7 @@ import { isEmpty } from 'lodash'
|
||||
import { FfmpegOptions } from '../../../../main/Service/ffmpegOptions.js'
|
||||
import { version } from '../../../../../package.json'
|
||||
import { Book } from '../../../../model/book.js'
|
||||
import { GeneralResponse } from '../../../../model/generalResponse.js'
|
||||
|
||||
export class BookService extends BaseRealmService {
|
||||
static instance: BookService | null = null
|
||||
@ -37,7 +38,7 @@ export class BookService extends BaseRealmService {
|
||||
* 获取小说信息,没有找到返回null
|
||||
* @param bookId
|
||||
*/
|
||||
GetBookDataById(bookId): Book.SelectBook | null {
|
||||
GetBookDataById(bookId: string): Book.SelectBook | null {
|
||||
try {
|
||||
if (isEmpty(bookId)) {
|
||||
throw new Error('获取小说信息失败,缺少小说ID')
|
||||
@ -275,7 +276,7 @@ export class BookService extends BaseRealmService {
|
||||
* @param bookId 小说的ID
|
||||
* @param bookData 要修改的小说数据
|
||||
*/
|
||||
async UpdateBookData(bookId: string, bookData: Book.SelectBook) {
|
||||
UpdateBookData(bookId: string, bookData: Book.SelectBook): Book.SelectBook {
|
||||
try {
|
||||
if (bookId == null) {
|
||||
throw new Error('修改小说数据失败,缺少小说ID')
|
||||
@ -303,8 +304,27 @@ export class BookService extends BaseRealmService {
|
||||
if (bookRes == null) {
|
||||
throw new Error('获取修改后的小说数据失败,小说ID对应的数据不存在')
|
||||
}
|
||||
// return successMessage(bookRes, '修改小说数据成功', 'ReverseBook_UpdateBookData')
|
||||
return bookRes;
|
||||
} catch (error) {
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
return successMessage(bookRes, '修改小说数据成功', 'ReverseBook_UpdateBookData')
|
||||
/**
|
||||
* 删除指定的小说任务
|
||||
* @param bookId 需要删除的小说的ID
|
||||
*/
|
||||
DeleteBookData(bookId: string): void {
|
||||
try {
|
||||
this.transaction(() => {
|
||||
let book = this.realm.objectForPrimaryKey('Book', bookId);
|
||||
if (book == null) {
|
||||
throw new Error('未找到对应的小说')
|
||||
}
|
||||
// 删除对应的小说
|
||||
this.realm.delete(book)
|
||||
})
|
||||
} catch (error) {
|
||||
throw error
|
||||
}
|
||||
|
||||
@ -72,7 +72,7 @@ export class BookTaskDetailService extends BaseRealmService {
|
||||
return JoinPath(define.project_path, subImage)
|
||||
}),
|
||||
characterTags: item.characterTags ? item.characterTags.map((tag) => tag) : null,
|
||||
subValue: item.subValue,
|
||||
subValue: isEmpty(item.subValue) ? null : JSON.parse(item.subValue),
|
||||
reversePrompt: item.reversePrompt.map((reversePrompt) => {
|
||||
return {
|
||||
...reversePrompt
|
||||
@ -101,7 +101,6 @@ export class BookTaskDetailService extends BaseRealmService {
|
||||
if (bookTaskDetailId == null) {
|
||||
throw new Error('获取小说任务详细信息失败,缺少ID')
|
||||
}
|
||||
|
||||
let bookTaskDetails = this.GetBookTaskData({ id: bookTaskDetailId })
|
||||
if (bookTaskDetails.data.length <= 0) {
|
||||
return null;
|
||||
@ -165,7 +164,7 @@ export class BookTaskDetailService extends BaseRealmService {
|
||||
* @param bookTaskDetailId
|
||||
* @param updateData
|
||||
*/
|
||||
UpdateBookTaskDetail(bookTaskDetailId: string, updateData: Book.SelectBookTaskDetail) {
|
||||
UpdateBookTaskDetail(bookTaskDetailId: string, updateData: Book.SelectBookTaskDetail): Book.SelectBookTaskDetail {
|
||||
try {
|
||||
this.transaction(() => {
|
||||
let bookTaskDetail = this.realm.objectForPrimaryKey('BookTaskDetail', bookTaskDetailId)
|
||||
@ -178,18 +177,21 @@ export class BookTaskDetailService extends BaseRealmService {
|
||||
}
|
||||
bookTaskDetail.updateTime = new Date()
|
||||
})
|
||||
return successMessage(
|
||||
null,
|
||||
'修改小说任务详细信息成功',
|
||||
'BookTaskDetailService_UpdateBookTaskDetail'
|
||||
)
|
||||
let res = this.GetBookTaskDetailDataById(bookTaskDetailId)
|
||||
return res;
|
||||
} catch (error) {
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新指定ID的反推提示词数据
|
||||
* @param bookTaskDetailId
|
||||
* @param mjMessage
|
||||
*/
|
||||
UpdateBookTaskDetailMjMessage(bookTaskDetailId: string, mjMessage: Book.MJMessage): void {
|
||||
try {
|
||||
console.log('UpdateBookTaskDetailMjMessage', bookTaskDetailId, mjMessage)
|
||||
this.transaction(() => {
|
||||
let mjMessageRes = this.realm.objectForPrimaryKey('MJMessage', bookTaskDetailId)
|
||||
let bookTaskDetail = this.realm.objectForPrimaryKey('BookTaskDetail', bookTaskDetailId)
|
||||
@ -276,12 +278,20 @@ export class BookTaskDetailService extends BaseRealmService {
|
||||
* @param bookTaskDetailId 小说分镜的ID
|
||||
*/
|
||||
DeleteBookTaskDetailReversePromptById(bookTaskDetailId: string): void {
|
||||
let bookTaskDetail = this.realm.objectForPrimaryKey('BookTaskDetail', bookTaskDetailId);
|
||||
if (bookTaskDetail == null) {
|
||||
this.transaction(() => {
|
||||
let bookTaskDetails = this.realm.objects<BookTaskDetailModel>('BookTaskDetail').filtered('id = $0', bookTaskDetailId);
|
||||
if (bookTaskDetails.length <= 0) {
|
||||
throw new Error('删除小说任务详细信息的反推提示词失败,未找到对应的分镜信息')
|
||||
}
|
||||
this.transaction(() => {
|
||||
this.realm.delete(bookTaskDetail.reversePrompt)
|
||||
let bookTaskDetail = bookTaskDetails[0];
|
||||
|
||||
bookTaskDetail.gptPrompt = undefined;
|
||||
// 删除所有的反推提示词
|
||||
if (bookTaskDetail.reversePrompt) {
|
||||
bookTaskDetail.reversePrompt.forEach(item => {
|
||||
this.realm.delete(item)
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -132,7 +132,7 @@ export class BookTaskService extends BaseRealmService {
|
||||
* @param bookTaskId 小说批次任务Id
|
||||
* @param status 目标状态
|
||||
*/
|
||||
UpdateBookTaskStatus(bookTaskId: string, status: BookTaskStatus, errorMsg: string | null = null) {
|
||||
UpdateBookTaskStatus(bookTaskId: string, status: BookTaskStatus, errorMsg: string | null = null): GeneralResponse.SuccessItem {
|
||||
try {
|
||||
this.transaction(() => {
|
||||
// 修改对应小说批次任务的状态
|
||||
|
||||
@ -147,6 +147,14 @@ const migration = (oldRealm: Realm, newRealm: Realm) => {
|
||||
}
|
||||
})
|
||||
}
|
||||
if (oldRealm.schemaVersion < 20) {
|
||||
newRealm.write(() => {
|
||||
const newSoftwares = newRealm.objects('Software')
|
||||
for (let software of newSoftwares) {
|
||||
software.subtitleSetting = null // 字幕的默认设置
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export class BaseSoftWareService extends BaseService {
|
||||
@ -185,7 +193,7 @@ export class BaseSoftWareService extends BaseService {
|
||||
MjSettingModel
|
||||
],
|
||||
path: dbPath,
|
||||
schemaVersion: 19, // 当前版本号
|
||||
schemaVersion: 21, // 当前版本号
|
||||
migration: migration
|
||||
}
|
||||
// 判断当前全局是不是又当前这个
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import Realm, { UpdateMode } from 'realm'
|
||||
import { successMessage } from '../../../../main/Public/generalTools'
|
||||
import { BaseSoftWareService } from './softwareBasic.js'
|
||||
import { GeneralResponse } from '../../../../model/generalResponse'
|
||||
const { v4: uuidv4 } = require('uuid')
|
||||
|
||||
export class SoftwareService extends BaseSoftWareService {
|
||||
@ -24,7 +25,7 @@ export class SoftwareService extends BaseSoftWareService {
|
||||
}
|
||||
|
||||
// 修改数据库中行中的某个属性数据
|
||||
UpdateSoftware(software) {
|
||||
UpdateSoftware(software: SoftwareSettingModel.SoftwareSetting): GeneralResponse.SuccessItem {
|
||||
try {
|
||||
this.realm.write(() => {
|
||||
this.realm.create('Software', software, UpdateMode.Modified)
|
||||
@ -41,7 +42,7 @@ export class SoftwareService extends BaseSoftWareService {
|
||||
* @param software 软件配置信息
|
||||
* @returns
|
||||
*/
|
||||
AddSfotware(software) {
|
||||
AddSfotware(software: SoftwareSettingModel.SoftwareSetting): GeneralResponse.SuccessItem {
|
||||
try {
|
||||
software.id = uuidv4()
|
||||
this.realm.write(() => {
|
||||
@ -56,7 +57,7 @@ export class SoftwareService extends BaseSoftWareService {
|
||||
/**
|
||||
* 或软件基础配置信息
|
||||
*/
|
||||
GetSoftwareData() {
|
||||
GetSoftwareData(): GeneralResponse.SuccessItem {
|
||||
try {
|
||||
let softwares = this.realm.objects('Software')
|
||||
|
||||
|
||||
@ -123,7 +123,8 @@ export const DEFINE_STRING = {
|
||||
GPT: {
|
||||
INIT_SERVER_GPT_OPTIONS: 'INIT_SERVER_GPT_OPTIONS',
|
||||
GET_AI_SETTING: 'GET_AI_SETTING',
|
||||
SAVE_AI_SETTING: 'SAVE_AI_SETTING'
|
||||
SAVE_AI_SETTING: 'SAVE_AI_SETTING',
|
||||
SYNC_GPT_KEY: "SYNC_GPT_KEY"
|
||||
},
|
||||
|
||||
QUEUE_BATCH: {
|
||||
@ -153,7 +154,6 @@ export const DEFINE_STRING = {
|
||||
SD: {
|
||||
LOAD_SD_SERVICE_DATA: 'LOAD_SD_SERVICE_DATA',
|
||||
TXT2IMG: 'TXT2IMG',
|
||||
SD_MERGE_PROMPT: "SD_MERGE_PROMPT"
|
||||
},
|
||||
MJ: {
|
||||
SAVE_WORD_SRT: 'SAVE_WORD_SRT',
|
||||
@ -175,7 +175,6 @@ export const DEFINE_STRING = {
|
||||
GET_MJ_IMAGE_ROBOT_MODEL: 'GET_MJ_IMAGE_ROBOT_MODEL',
|
||||
MACTH_USER_RETURN: 'MACTH_USER_RETURN',
|
||||
AUTO_MATCH_USER: 'AUTO_MATCH_USER',
|
||||
MJ_MERGE_PROMPT: "MJ_MERGE_PROMPT",
|
||||
ADD_MJ_GENADD_MJ_GENERATE_IMAGE_TASK: "ADD_MJ_GENADD_MJ_GENERATE_IMAGE_TASK",
|
||||
MJ_IMAGE: "MJ_IMAGE"
|
||||
},
|
||||
@ -208,6 +207,7 @@ export const DEFINE_STRING = {
|
||||
BOOK: {
|
||||
MAIN_DATA_RETURN: 'MAIN_DATA_RETURN', // 监听任务返回
|
||||
|
||||
REPLACE_VIDEO_CURRENT_FRAME: 'REPLACE_VIDEO_CURRENT_FRAME',
|
||||
GET_BOOK_TYPE: 'GET_BOOK_TYPE',
|
||||
ADD_OR_MODIFY_BOOK: 'ADD_OR_MODIFY_BOOK',
|
||||
GET_BOOK_DATA: 'GET_BOOK_DATA',
|
||||
@ -232,6 +232,10 @@ export const DEFINE_STRING = {
|
||||
HD_IMAGE: "HD_IMAGE",
|
||||
USE_BOOK_VIDEO_DATA_TO_BOOK_TASK: "USE_BOOK_VIDEO_DATA_TO_BOOK_TASK",
|
||||
ADD_JIANYING_DRAFT: "ADD_JIANYING_DRAFT",
|
||||
EXPORT_COPYWRITING: "EXPORT_COPYWRITING",
|
||||
MERGE_PROMPT: "MERGE_PROMPT",
|
||||
RESET_BOOK_DATA: "RESET_BOOK_DATA",
|
||||
DELETE_BOOK_DATA: "DELETE_BOOK_DATA",
|
||||
|
||||
COMPUTE_STORYBOARD: 'COMPUTE_STORYBOARD',
|
||||
|
||||
@ -291,7 +295,10 @@ export const DEFINE_STRING = {
|
||||
WRITE: {
|
||||
GET_WRITE_CONFIG: 'GET_WRITE_CONFIG',
|
||||
SAVE_WRITE_CONFIG: 'SAVE_WRITE_CONFIG',
|
||||
ACTION_START: 'ACTION_START'
|
||||
ACTION_START: 'ACTION_START',
|
||||
GET_SUBTITLE_SETTING: "GET_SUBTITLE_SETTING",
|
||||
RESET_SUBTITLE_SETTING: "RESET_SUBTITLE_SETTING",
|
||||
SAVE_SUBTITLE_SETTING: "SAVE_SUBTITLE_SETTING",
|
||||
},
|
||||
DB: {
|
||||
UPDATE_BOOK_TASK_DATA: "UPDATE_BOOK_TASK_DATA",
|
||||
|
||||
@ -207,10 +207,6 @@ export enum TagDefineType {
|
||||
SCENE_MAIN = "scene_main",
|
||||
}
|
||||
|
||||
export enum MergeType {
|
||||
BOOKTASK = 'bookTask', // 整个小说批次分镜合并
|
||||
BOOKTASKDETAIL = 'bookTaskDetail' // 单个分镜合并
|
||||
}
|
||||
|
||||
export enum OperateBookType {
|
||||
BOOK = 'book', // 这个小说的所有批次
|
||||
@ -227,3 +223,14 @@ export enum CopyImageType {
|
||||
// 不包含图
|
||||
NONE = 'none'
|
||||
}
|
||||
|
||||
export enum PromptMergeType {
|
||||
// mj 合并
|
||||
MJ_MERGE = 'mj_merge',
|
||||
|
||||
// SD 合并
|
||||
SD_MERGE = 'sd_merge',
|
||||
|
||||
// D3 合并
|
||||
D3_MERGE = 'd3_merge'
|
||||
}
|
||||
|
||||
@ -60,7 +60,8 @@ export enum ResponseMessageType {
|
||||
GET_TEXT = 'getText', // 获取文案
|
||||
REMOVE_WATERMARK = "REMOVE_WATERMARK",// 删除水印
|
||||
MJ_REVERSE = 'MJ_REVERSE',// MJ反推,返回反推结果
|
||||
PROMPT_TRANSLATE = 'PROMPT_TRANSLATE',// 提示词翻译
|
||||
REVERSE_PROMPT_TRANSLATE = 'REVERSE_PROMPT_TRANSLATE',// 反推提示词翻译
|
||||
GPT_PROMPT_TRANSLATE = 'GPT_PROMPT_TRANSLATE', // GPT提示词翻译
|
||||
MJ_IMAGE = 'MJ_IMAGE',// MJ 生成图片
|
||||
HD_IMAGE = 'HD_IMAGE',// HD 生成图片
|
||||
}
|
||||
@ -73,3 +74,10 @@ export enum LaiAPIType {
|
||||
// 备用站点
|
||||
BAK_MAIN = 'bak-main'
|
||||
}
|
||||
|
||||
// 同步GPTkey的分类
|
||||
export enum SyncGptKeyType {
|
||||
// 字幕设置
|
||||
SUBTITLE_SETTING = 'subtitle_setting',
|
||||
|
||||
}
|
||||
@ -2,7 +2,6 @@
|
||||
export enum TranslateType {
|
||||
// 反推提示词翻译
|
||||
REVERSE_PROMPT_TRANSLATE = 'reverse_prompt_translate',
|
||||
|
||||
// GPT提示词翻译
|
||||
GPT_PROMPT_TRANSLATE = 'gpt_prompt_translate',
|
||||
}
|
||||
|
||||
@ -28,3 +28,13 @@ export enum RemoveWatermarkType {
|
||||
LOCAL_LAMA = 'local_lama',
|
||||
IOPAINT = 'iopaint'
|
||||
}
|
||||
|
||||
// 获取字幕的方法类型
|
||||
export enum GetSubtitleType {
|
||||
// 本地OCR
|
||||
LOCAL_OCR = 'local_ocr',
|
||||
// 本地Whisper
|
||||
LOCAL_WHISPER = 'local_whisper',
|
||||
// LAI WHISPER
|
||||
LAI_WHISPER = 'lai_whisper',
|
||||
}
|
||||
@ -2,7 +2,7 @@ import { ipcMain } from 'electron'
|
||||
import { DEFINE_STRING } from '../../define/define_string'
|
||||
import { ReverseBook } from '../Service/Book/ReverseBook'
|
||||
import { BasicReverse } from '../Service/Book/basicReverse'
|
||||
import { Subtitle } from '../Service/subtitle'
|
||||
import { Subtitle } from '../Service/Subtitle/subtitle'
|
||||
import { BookBasic } from '../Service/Book/BooKBasic'
|
||||
import { MJOpt } from '../Service/MJ/mj'
|
||||
import { BookImage } from '../Service/Book/bookImage'
|
||||
@ -10,6 +10,8 @@ import { ImageStyle } from '../Service/Book/imageStyle'
|
||||
import { BookTask } from '../Service/Book/bookTask'
|
||||
import { BookVideo } from '../Service/Book/bookVideo'
|
||||
import { Watermark } from '../Service/watermark'
|
||||
import { SubtitleService } from '../Service/Subtitle/subtitleService'
|
||||
import { BookFrame } from '../Service/Book/bookFrame'
|
||||
let reverseBook = new ReverseBook()
|
||||
let basicReverse = new BasicReverse()
|
||||
let subtitle = new Subtitle()
|
||||
@ -20,14 +22,16 @@ let imageStyle = new ImageStyle()
|
||||
let bookTask = new BookTask()
|
||||
let bookVideo = new BookVideo()
|
||||
let watermark = new Watermark()
|
||||
let subtitleService = new SubtitleService()
|
||||
let bookFrame = new BookFrame()
|
||||
|
||||
export function BookIpc() {
|
||||
// 获取样式图片的子列表
|
||||
ipcMain.handle(DEFINE_STRING.BOOK.GET_BOOK_TYPE, async (event) => reverseBook.GetBookType())
|
||||
ipcMain.handle(DEFINE_STRING.BOOK.GET_BOOK_TYPE, async (event) => bookBasic.GetBookType())
|
||||
|
||||
// 新增或者是修改小说数据
|
||||
ipcMain.handle(DEFINE_STRING.BOOK.ADD_OR_MODIFY_BOOK, async (event, book) =>
|
||||
reverseBook.AddOrModifyBook(book)
|
||||
bookBasic.AddOrModifyBook(book)
|
||||
)
|
||||
|
||||
// 获取小说数据(通过传递的参数进行筛选)
|
||||
@ -83,8 +87,7 @@ export function BookIpc() {
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region 一键反推的单个任务
|
||||
|
||||
//#region 分镜相关
|
||||
// 开始计算分镜
|
||||
ipcMain.handle(
|
||||
DEFINE_STRING.BOOK.COMPUTE_STORYBOARD,
|
||||
@ -97,10 +100,33 @@ export function BookIpc() {
|
||||
async (event, bookId) => await reverseBook.Framing(bookId)
|
||||
)
|
||||
|
||||
// 开始执行分镜任务
|
||||
// 替换分镜视频的当前帧
|
||||
ipcMain.handle(DEFINE_STRING.BOOK.REPLACE_VIDEO_CURRENT_FRAME, async (event, bookTaskDetailId, currentTime) =>
|
||||
await bookFrame.ReplaceVideoCurrentFrame(bookTaskDetailId, currentTime)
|
||||
)
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region 提示词相关
|
||||
|
||||
// 合并提示词
|
||||
ipcMain.handle(DEFINE_STRING.BOOK.MERGE_PROMPT, async (event, id, type, operateBookType) => await reverseBook.MergePrompt(id, type, operateBookType))
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region 一键反推的单个任务
|
||||
|
||||
|
||||
// 开始执行获取小说文案的方法
|
||||
ipcMain.handle(
|
||||
DEFINE_STRING.BOOK.GET_COPYWRITING,
|
||||
async (event, bookId, bookTaskId) => await reverseBook.GetCopywriting(bookId, bookTaskId)
|
||||
async (event, bookId, bookTaskId, operateBookType) => await subtitleService.GetCopywriting(bookId, bookTaskId, operateBookType)
|
||||
)
|
||||
|
||||
// 获取小说的文案数据,然后保存到对应的文件中
|
||||
ipcMain.handle(
|
||||
DEFINE_STRING.BOOK.EXPORT_COPYWRITING,
|
||||
async (event, bookTaskId) => await subtitleService.ExportCopywriting(bookTaskId)
|
||||
)
|
||||
|
||||
// 执行去除水印
|
||||
@ -112,8 +138,8 @@ export function BookIpc() {
|
||||
// 添加反推任务到任务列表
|
||||
ipcMain.handle(
|
||||
DEFINE_STRING.BOOK.ADD_REVERSE_PROMPT,
|
||||
async (event, bookTaskDetailIds, type) =>
|
||||
await reverseBook.AddReversePromptTask(bookTaskDetailIds, type)
|
||||
async (event, bookTaskDetailIds, operateBookType, type) =>
|
||||
await reverseBook.AddReversePrompt(bookTaskDetailIds, operateBookType, type)
|
||||
)
|
||||
|
||||
// 将反推出来的提示词写入到GPT提示词里面
|
||||
@ -154,9 +180,19 @@ export function BookIpc() {
|
||||
// 一拆四,将一个任务拆分成四个任务,并且复制对应的图片
|
||||
ipcMain.handle(
|
||||
DEFINE_STRING.BOOK.ONE_TO_FOUR_BOOK_TASK,
|
||||
async (event, bookTaskDetailId) => await bookBasic.OneToFourBookTask(bookTaskDetailId)
|
||||
async (event, bookTaskDetailId) => await bookTask.OneToFourBookTask(bookTaskDetailId)
|
||||
)
|
||||
|
||||
//#region 小说相关
|
||||
|
||||
// 重置小说数据
|
||||
ipcMain.handle(DEFINE_STRING.BOOK.RESET_BOOK_DATA, async (event, bookId) => await bookBasic.ResetBookData(bookId))
|
||||
|
||||
// 删除小说数据
|
||||
ipcMain.handle(DEFINE_STRING.BOOK.DELETE_BOOK_DATA, async (event, bookId) => await bookBasic.DeleteBookData(bookId))
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region 小说批次任务相关
|
||||
|
||||
// 重置小说批次数据
|
||||
@ -83,6 +83,12 @@ function GptIpc() {
|
||||
DEFINE_STRING.GPT.SAVE_AI_SETTING,
|
||||
async (event, value) => await gptSetting.SaveAISetting(value)
|
||||
)
|
||||
|
||||
// 同步GPT Key 到指定的设置
|
||||
ipcMain.handle(
|
||||
DEFINE_STRING.GPT.SYNC_GPT_KEY,
|
||||
async (event, syncType) => await gptSetting.SyncGptKey(syncType)
|
||||
)
|
||||
}
|
||||
|
||||
export { GptIpc }
|
||||
|
||||
@ -12,7 +12,7 @@ import { MainIpc } from './mainIpc.js'
|
||||
import { GlobalIpc } from './globalIpc.js'
|
||||
import { ImageIpc } from './imageIpc.js'
|
||||
import { SystemIpc } from './systemIpc.js'
|
||||
import { BookIpc } from './bookIpc.js'
|
||||
import { BookIpc } from './bookIpc'
|
||||
import { TTSIpc } from './ttsIpc.js'
|
||||
import { DBIpc } from './dbIpc'
|
||||
|
||||
|
||||
@ -138,12 +138,6 @@ function MjIpc() {
|
||||
async (event, value) => await discordSimple.DiscordDeleteMessage(value)
|
||||
)
|
||||
|
||||
// MJ合并提示词命令
|
||||
ipcMain.handle(
|
||||
DEFINE_STRING.MJ.MJ_MERGE_PROMPT,
|
||||
async (event, id, mergeType) => await mjOpt.MergePrompt(id, mergeType)
|
||||
)
|
||||
|
||||
// MJ出单张图
|
||||
ipcMain.handle(
|
||||
DEFINE_STRING.MJ.ADD_MJ_GENADD_MJ_GENERATE_IMAGE_TASK,
|
||||
|
||||
@ -29,11 +29,5 @@ function SdIpc() {
|
||||
|
||||
// 文生图,单张
|
||||
ipcMain.handle(DEFINE_STRING.SD.TXT2IMG, async (event, value) => await sd.txt2img(value))
|
||||
|
||||
// SD合并提示词
|
||||
ipcMain.handle(
|
||||
DEFINE_STRING.SD.SD_MERGE_PROMPT,
|
||||
async (event, id, mergeType) => await sdOpt.MergePrompt(id, mergeType)
|
||||
)
|
||||
}
|
||||
export { SdIpc }
|
||||
|
||||
@ -4,6 +4,8 @@ import { Writing } from '../Service/writing'
|
||||
let writing = new Writing(global)
|
||||
import { WritingSetting } from '../setting/writeSetting'
|
||||
let writingSetting = new WritingSetting()
|
||||
import { SubtitleService } from '../Service/Subtitle/subtitleService'
|
||||
let subtitleService = new SubtitleService()
|
||||
|
||||
function WritingIpc() {
|
||||
// 监听分镜时间的保存
|
||||
@ -36,7 +38,7 @@ function WritingIpc() {
|
||||
async (event, value) => await writing.ImportSrtAndGetTime(value)
|
||||
)
|
||||
|
||||
// 获取文案相关的配置(数据库)
|
||||
// 获取文案格式化相关的配置(数据库)
|
||||
ipcMain.handle(
|
||||
DEFINE_STRING.WRITE.GET_WRITE_CONFIG,
|
||||
async (event) => await writingSetting.GetWritingConfig()
|
||||
@ -48,6 +50,23 @@ function WritingIpc() {
|
||||
async (event, value) => await writingSetting.SaveWriteConfig(value)
|
||||
)
|
||||
|
||||
// 获取提取文案相关的配置(数据库)
|
||||
ipcMain.handle(
|
||||
DEFINE_STRING.WRITE.GET_SUBTITLE_SETTING,
|
||||
async (event) => await subtitleService.GetSubtitleSetting()
|
||||
)
|
||||
|
||||
// 重置提取文案相关的配置(数据库)
|
||||
ipcMain.handle(
|
||||
DEFINE_STRING.WRITE.RESET_SUBTITLE_SETTING,
|
||||
async (event) => await subtitleService.ResetSubtitleSetting()
|
||||
)
|
||||
|
||||
ipcMain.handle(
|
||||
DEFINE_STRING.WRITE.SAVE_SUBTITLE_SETTING,
|
||||
async (event, subtitleSetting) => await subtitleService.SaveSubtitleSetting(subtitleSetting)
|
||||
)
|
||||
|
||||
ipcMain.handle(
|
||||
DEFINE_STRING.WRITE.ACTION_START,
|
||||
async (event, aiSetting, word) => await writing.ActionStart(aiSetting, word)
|
||||
|
||||
@ -1,29 +1,19 @@
|
||||
import { BookType, OperateBookType, TagDefineType } from '../../../define/enum/bookEnum'
|
||||
import { errorMessage, successMessage } from '../../Public/generalTools'
|
||||
import { BookService } from '../../../define/db/service/Book/bookService'
|
||||
import { BookTaskService } from '../../../define/db/service/Book/bookTaskService'
|
||||
import { BookTaskDetailService } from '../../../define/db/service/Book/bookTaskDetailService'
|
||||
import { CopyImageType } from '../../../define/enum/bookEnum'
|
||||
const { v4: uuidv4 } = require('uuid')
|
||||
import { define } from '../../../define/define'
|
||||
import path from 'path'
|
||||
import { CheckFileOrDirExist, CheckFolderExistsOrCreate, CopyFileOrFolder } from '../../../define/Tools/file'
|
||||
import { Book } from '../../../model/book'
|
||||
import { CheckFileOrDirExist, CheckFolderExistsOrCreate, CopyFileOrFolder, DeleteFolderAllFile, GetSubdirectories } from '../../../define/Tools/file'
|
||||
import { GeneralResponse } from '../../../model/generalResponse'
|
||||
import { cloneDeep, isEmpty } from 'lodash'
|
||||
import { BookServiceBasic } from '../ServiceBasic/bookServiceBasic'
|
||||
import { BookTask } from './bookTask'
|
||||
import fs from 'fs'
|
||||
|
||||
export class BookBasic {
|
||||
constructor() { }
|
||||
bookTaskService: BookTaskService
|
||||
bookTaskDetailService: BookTaskDetailService
|
||||
|
||||
async InitService() {
|
||||
if (!this.bookTaskService) {
|
||||
this.bookTaskService = await BookTaskService.getInstance()
|
||||
}
|
||||
if (!this.bookTaskDetailService) {
|
||||
this.bookTaskDetailService = await BookTaskDetailService.getInstance()
|
||||
}
|
||||
bookServiceBasic: BookServiceBasic
|
||||
bookTask: BookTask
|
||||
constructor() {
|
||||
this.bookServiceBasic = new BookServiceBasic();
|
||||
this.bookTask = new BookTask()
|
||||
}
|
||||
|
||||
//#region 小说相关操作
|
||||
@ -73,209 +63,112 @@ export class BookBasic {
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region 小说批次任务相关操作
|
||||
//#region 小说相关操作
|
||||
|
||||
|
||||
async OneToFourBookTask(bookTaskId: string) {
|
||||
/**
|
||||
* 重置小说数据
|
||||
* @param bookId 小说ID
|
||||
* @returns
|
||||
*/
|
||||
async ResetBookData(bookId: string): Promise<GeneralResponse.ErrorItem | GeneralResponse.SuccessItem> {
|
||||
try {
|
||||
console.log(bookTaskId)
|
||||
await this.InitService();
|
||||
let copyCount = 100
|
||||
let bookTask = this.bookTaskService.GetBookTaskDataById(bookTaskId)
|
||||
if (bookTask == null) {
|
||||
throw new Error("没有找到对应的数小说任务,请检查数据")
|
||||
let book = await this.bookServiceBasic.GetBookDataById(bookId)
|
||||
// 获取所有的小说批次
|
||||
let bookTasks = (await this.bookServiceBasic.GetBookTaskData({
|
||||
bookId: bookId
|
||||
})).bookTasks;
|
||||
// 重置批次任务
|
||||
for (let i = 0; i < bookTasks.length; i++) {
|
||||
const element = bookTasks[i];
|
||||
// 第一个重置,后面的删除
|
||||
if (i == 0) {
|
||||
let resetBookTaskData = await this.bookTask.ReSetBookTask(element.id);
|
||||
if (resetBookTaskData.code == 0) {
|
||||
throw new Error(resetBookTaskData.message)
|
||||
}
|
||||
} else {
|
||||
let deleteBookTaskData = await this.bookTask.DeleteBookTask(element.id);
|
||||
if (deleteBookTaskData.code == 0) {
|
||||
throw new Error(deleteBookTaskData.message)
|
||||
}
|
||||
}
|
||||
// 获取所有的出图中最少的
|
||||
let bookTaskDetail = this.bookTaskDetailService.GetBookTaskData({
|
||||
bookTaskId: bookTaskId
|
||||
}).data as Book.SelectBookTaskDetail[]
|
||||
if (bookTaskDetail == null || bookTaskDetail.length <= 0) {
|
||||
throw new Error("没有对应的小说分镜任务,请先添加分镜任务")
|
||||
}
|
||||
|
||||
for (let i = 0; i < bookTaskDetail.length; i++) {
|
||||
const element = bookTaskDetail[i];
|
||||
if (isEmpty(element.subImagePath)) {
|
||||
throw new Error("检测到图片没有出完,请先检查出图")
|
||||
// 开始重置小说数据
|
||||
await this.bookServiceBasic.UpdateBookData(bookId, {
|
||||
srtPath: undefined,
|
||||
audioPath: undefined,
|
||||
subtitlePosition: undefined,
|
||||
imageStyle: undefined,
|
||||
autoAnalyzeCharacter: undefined,
|
||||
customizeImageStyle: undefined,
|
||||
videoConfig: undefined,
|
||||
prefixPrompt: undefined,
|
||||
suffixPrompt: undefined,
|
||||
draftSrtStyle: undefined,
|
||||
backgroundMusic: undefined,
|
||||
friendlyReminder: undefined,
|
||||
watermarkPosition: undefined,
|
||||
})
|
||||
// 文件重置,获取data下面的所有的子文件夹,删除所有的文件夹
|
||||
let dirs = await GetSubdirectories(path.join(book.bookFolderPath, 'data'))
|
||||
for (let i = 0; i < dirs.length; i++) {
|
||||
const element = dirs[i];
|
||||
await DeleteFolderAllFile(element, true)
|
||||
}
|
||||
if (element.subImagePath == null || element.subImagePath.length <= 0) {
|
||||
throw new Error("检测到图片没有出完,请先检查出图")
|
||||
let scriptPath = path.join(book.bookFolderPath, 'script')
|
||||
if (await CheckFileOrDirExist(scriptPath)) {
|
||||
await DeleteFolderAllFile(scriptPath, true)
|
||||
}
|
||||
if (element.subImagePath.length < copyCount) {
|
||||
copyCount = element.subImagePath.length
|
||||
// 删掉输入的备份文件和input文件
|
||||
let bakPath = path.join(book.bookFolderPath, 'tmp/bak');
|
||||
if (await CheckFileOrDirExist(bakPath)) {
|
||||
await DeleteFolderAllFile(bakPath, true)
|
||||
}
|
||||
let inputPath = path.join(book.bookFolderPath, 'tmp/input');
|
||||
if (await CheckFileOrDirExist(inputPath)) {
|
||||
await DeleteFolderAllFile(inputPath, true)
|
||||
}
|
||||
if (copyCount <= 0) {
|
||||
throw new Error("批次设置错误,无法进行一拆四")
|
||||
}
|
||||
// 开始复制
|
||||
let res = await this.CopyNewBookTask(bookTask, bookTaskDetail, copyCount - 1, CopyImageType.ONE)
|
||||
if (res.code == 0) {
|
||||
throw new Error(res.message)
|
||||
}
|
||||
return successMessage(res.data, "一拆四成功", "BookBasic_OneToFourBookTask")
|
||||
|
||||
// 重置完毕,开始返回
|
||||
return successMessage('重置小说数据成功', 'BookBasic_ResetBookData');
|
||||
} catch (error) {
|
||||
return errorMessage("一拆四失败,失败信息如下:" + error.message, "BookBasic_OneToFourBookTask")
|
||||
return errorMessage('重置小说数据失败,失败信息如下:' + error.message, 'BookBasic_ResetBookData');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 复制一个小说批次任务,创建新的小说批次任务
|
||||
* @param oldBookTaskId
|
||||
* @param copyCount 复制的数量
|
||||
* @param isCopyImage 是否复制图片
|
||||
* 删除指定小说数据
|
||||
* @param bookId 要删除的小说ID
|
||||
*/
|
||||
async CopyNewBookTask(sourceBookTask: Book.SelectBookTask, sourceBookTaskDetail: Book.SelectBookTaskDetail[], copyCount: number, copyImageType: CopyImageType) {
|
||||
async DeleteBookData(bookId: string): Promise<GeneralResponse.ErrorItem | GeneralResponse.SuccessItem> {
|
||||
try {
|
||||
await this.InitService();
|
||||
let addBookTask = [] as Book.SelectBookTask[]
|
||||
let addBookTaskDetail = [] as Book.SelectBookTaskDetail[]
|
||||
|
||||
// 先处理文件夹的创建,包括小说任务的和小说任务分镜的
|
||||
for (let i = 0; i < copyCount; i++) {
|
||||
let maxNo = this.bookTaskService.realm
|
||||
.objects('BookTask')
|
||||
.filtered('bookId = $0', sourceBookTask.bookId)
|
||||
.max('no')
|
||||
let no = maxNo == null ? 1 : Number(maxNo) + 1 + i
|
||||
let name = 'output_0000' + no
|
||||
let imageFolder = path.join(define.project_path, `${sourceBookTask.bookId}/tmp/${name}`)
|
||||
await CheckFolderExistsOrCreate(imageFolder)
|
||||
// 创建对应的文件夹
|
||||
let addOneBookTask = {
|
||||
id: uuidv4(),
|
||||
bookId: sourceBookTask.bookId,
|
||||
no: no,
|
||||
name: name,
|
||||
generateVideoPath: sourceBookTask.generateVideoPath,
|
||||
srtPath: sourceBookTask.srtPath,
|
||||
audioPath: sourceBookTask.audioPath,
|
||||
draftSrtStyle: sourceBookTask.draftSrtStyle,
|
||||
backgroundMusic: sourceBookTask.backgroundMusic,
|
||||
friendlyReminder: sourceBookTask.friendlyReminder,
|
||||
imageFolder: path.relative(define.project_path, imageFolder),
|
||||
status: sourceBookTask.status,
|
||||
errorMsg: sourceBookTask.errorMsg,
|
||||
updateTime: new Date(),
|
||||
createTime: new Date(),
|
||||
isAuto: sourceBookTask.isAuto,
|
||||
imageStyle: sourceBookTask.imageStyle,
|
||||
autoAnalyzeCharacter: sourceBookTask.autoAnalyzeCharacter,
|
||||
customizeImageStyle: sourceBookTask.customizeImageStyle,
|
||||
videoConfig: sourceBookTask.videoConfig,
|
||||
prefixPrompt: sourceBookTask.prefixPrompt,
|
||||
suffixPrompt: sourceBookTask.suffixPrompt,
|
||||
version: sourceBookTask.version,
|
||||
imageCategory: sourceBookTask.imageCategory,
|
||||
} as Book.SelectBookTask
|
||||
|
||||
addBookTask.push(addOneBookTask)
|
||||
|
||||
for (let j = 0; j < sourceBookTaskDetail.length; j++) {
|
||||
const element = sourceBookTaskDetail[j];
|
||||
|
||||
let outImagePath = undefined as string
|
||||
let subImagePath = [] as string[]
|
||||
|
||||
if (copyImageType == CopyImageType.ALL) { // 直接全部复制
|
||||
outImagePath = element.outImagePath
|
||||
subImagePath = element.subImagePath
|
||||
} else if (copyImageType == CopyImageType.ONE) { // 只复制对应的
|
||||
let oldImage = element.subImagePath[i + 1]
|
||||
outImagePath = path.join(imageFolder, path.basename(element.outImagePath))
|
||||
await CopyFileOrFolder(oldImage, outImagePath)
|
||||
|
||||
subImagePath = []
|
||||
}
|
||||
else if (copyImageType == CopyImageType.NONE) {
|
||||
outImagePath = undefined
|
||||
subImagePath = []
|
||||
} else {
|
||||
throw new Error("无效的图片复制类型")
|
||||
}
|
||||
if (outImagePath) {
|
||||
// 单独处理一下显示的图片
|
||||
let imageBaseName = path.basename(element.outImagePath);
|
||||
let newImageBaseName = path.join(define.project_path, `${sourceBookTask.bookId}/tmp/${name}/${imageBaseName}`)
|
||||
await CopyFileOrFolder(outImagePath, newImageBaseName)
|
||||
}
|
||||
// 处理SD设置
|
||||
let sdConifg = undefined
|
||||
if (element.sdConifg) {
|
||||
let sdConifg = cloneDeep(element.sdConifg)
|
||||
if (sdConifg.webuiConfig) {
|
||||
let tempSdConfig = cloneDeep(sdConifg.webuiConfig);
|
||||
tempSdConfig.id = uuidv4()
|
||||
sdConifg.webuiConfig = tempSdConfig
|
||||
let book = await this.bookServiceBasic.GetBookDataById(bookId);
|
||||
// 先将所有的数据重置
|
||||
let resetRes = await this.ResetBookData(bookId);
|
||||
if (resetRes.code == 0) {
|
||||
throw new Error(resetRes.message)
|
||||
}
|
||||
let bookTasks = (await this.bookServiceBasic.GetBookTaskData({
|
||||
bookId: bookId
|
||||
})).bookTasks;
|
||||
// 删除遗留重置的小说批次任务
|
||||
for (let i = 0; i < bookTasks.length; i++) {
|
||||
const element = bookTasks[i];
|
||||
await this.bookServiceBasic.DeleteBookTaskData(element.id);
|
||||
}
|
||||
|
||||
let reverseId = uuidv4()
|
||||
// 处理反推数据
|
||||
let reverseMessage = [] as Book.ReversePrompt[]
|
||||
if (element.reversePrompt && element.reversePrompt.length > 0) {
|
||||
reverseMessage = cloneDeep(element.reversePrompt)
|
||||
for (let k = 0; k < reverseMessage.length; k++) {
|
||||
reverseMessage[k].id = uuidv4()
|
||||
reverseMessage[k].bookTaskDetailId = reverseId
|
||||
}
|
||||
}
|
||||
// 开始删除数据
|
||||
await this.bookServiceBasic.DeleteBookData(bookId);
|
||||
|
||||
let addOneBookTaskDetail = {
|
||||
id: reverseId,
|
||||
no: element.no,
|
||||
name: element.name,
|
||||
bookId: sourceBookTask.bookId,
|
||||
bookTaskId: addOneBookTask.id,
|
||||
videoPath: path.relative(define.project_path, element.videoPath),
|
||||
word: element.word,
|
||||
oldImage: path.relative(define.project_path, element.oldImage),
|
||||
afterGpt: element.afterGpt,
|
||||
startTime: element.startTime,
|
||||
endTime: element.endTime,
|
||||
timeLimit: element.timeLimit,
|
||||
subValue: element.subValue,
|
||||
characterTags: element.characterTags && element.characterTags.length > 0 ? cloneDeep(element.characterTags) : [],
|
||||
gptPrompt: element.gptPrompt,
|
||||
outImagePath: path.relative(define.project_path, outImagePath),
|
||||
subImagePath: subImagePath || [],
|
||||
prompt: element.prompt,
|
||||
adetailer: element.adetailer,
|
||||
sdConifg: sdConifg,
|
||||
createTime: new Date(),
|
||||
updateTime: new Date(),
|
||||
audioPath: element.audioPath,
|
||||
subtitlePosition: element.subtitlePosition,
|
||||
status: element.status,
|
||||
reversePrompt: reverseMessage,
|
||||
imageLock: element.imageLock
|
||||
} as Book.SelectBookTaskDetail
|
||||
addBookTaskDetail.push(addOneBookTaskDetail)
|
||||
// 开始删除文件
|
||||
let bookPath = book.bookFolderPath;
|
||||
if (await CheckFileOrDirExist(bookPath)) {
|
||||
await DeleteFolderAllFile(bookPath, true)
|
||||
}
|
||||
}
|
||||
|
||||
// 数据处理完毕,开始新增数据
|
||||
// 将所有的复制才做,全部放在一个事务中
|
||||
this.bookTaskService.transaction(() => {
|
||||
for (let i = 0; i < addBookTask.length; i++) {
|
||||
const element = addBookTask[i];
|
||||
this.bookTaskService.realm.create('BookTask', element)
|
||||
}
|
||||
for (let i = 0; i < addBookTaskDetail.length; i++) {
|
||||
const element = addBookTaskDetail[i];
|
||||
this.bookTaskDetailService.realm.create('BookTaskDetail', element)
|
||||
}
|
||||
})
|
||||
// 全部创建完成
|
||||
// 查找到数据,然后全部返回
|
||||
let returnBookTask = this.bookTaskService.GetBookTaskData({
|
||||
bookId: sourceBookTask.bookId
|
||||
}).data as Book.SelectBookTask[]
|
||||
|
||||
return successMessage(returnBookTask, "复制小说任务成功", "BookBasic_CopyNewBookTask")
|
||||
return successMessage(null, '删除小说数据成功', 'BookBasic_DeleteBookData');
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
throw error
|
||||
return errorMessage('删除小说数据失败,失败信息如下:' + error.message, 'BookBasic_DeleteBookData');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -12,65 +12,36 @@ import { TaskScheduler } from "../../Service/taskScheduler"
|
||||
import { Book } from '../../../model/book'
|
||||
import { LoggerStatus, OtherData, ResponseMessageType } from '../../../define/enum/softwareEnum'
|
||||
import { GeneralResponse } from '../../../model/generalResponse'
|
||||
import { cloneDeep, isEmpty } from 'lodash'
|
||||
import { Subtitle } from '../../Service/subtitle'
|
||||
import { Subtitle } from '../Subtitle/subtitle'
|
||||
import { Watermark } from '../watermark'
|
||||
import { SubtitleSavePositionType } from '../../../define/enum/waterMarkAndSubtitle'
|
||||
import { CheckFileOrDirExist, CheckFolderExistsOrCreate, CopyFileOrFolder, GetFilesWithExtensions } from '../../../define/Tools/file'
|
||||
import { ValidateJson } from '../../../define/Tools/validate'
|
||||
import { GetImageBase64, ProcessImage } from '../../../define/Tools/image'
|
||||
import { BookBackTaskType, BookType, TagDefineType, TaskExecuteType } from '../../../define/enum/bookEnum'
|
||||
import { BookBackTaskListService } from '../../../define/db/service/Book/bookBackTaskListService'
|
||||
import { BookBackTaskType, BookType, OperateBookType, PromptMergeType, TagDefineType, TaskExecuteType } from '../../../define/enum/bookEnum'
|
||||
import { MJOpt } from '../MJ/mj'
|
||||
import { TagDefine } from '../../../define/tagDefine'
|
||||
import { ImageStyleDefine } from '../../../define/iamgeStyleDefine'
|
||||
import { BookServiceBasic } from '../ServiceBasic/bookServiceBasic'
|
||||
import { SDOpt } from '../SD/sd'
|
||||
|
||||
|
||||
/**
|
||||
* 一键反推的相关操作
|
||||
*/
|
||||
export class ReverseBook extends BookBasic {
|
||||
export class ReverseBook {
|
||||
basicReverse: BasicReverse
|
||||
bookTaskService: BookTaskService
|
||||
taskScheduler: TaskScheduler
|
||||
bookService: BookService
|
||||
bookTaskDetailService: BookTaskDetailService
|
||||
bookBackTaskListService: BookBackTaskListService
|
||||
mjOpt: MJOpt = new MJOpt()
|
||||
sdOpt: SDOpt = new SDOpt()
|
||||
tagDefine: TagDefine
|
||||
subtitle: Subtitle
|
||||
watermark: Watermark
|
||||
bookServiceBasic: BookServiceBasic
|
||||
|
||||
constructor() {
|
||||
super()
|
||||
this.basicReverse = new BasicReverse()
|
||||
this.tagDefine = new TagDefine()
|
||||
}
|
||||
|
||||
async InitService() {
|
||||
if (!this.bookTaskService) {
|
||||
this.bookTaskService = await BookTaskService.getInstance()
|
||||
}
|
||||
if (!this.taskScheduler) {
|
||||
this.taskScheduler = new TaskScheduler()
|
||||
}
|
||||
if (!this.bookService) {
|
||||
this.bookService = await BookService.getInstance()
|
||||
}
|
||||
if (!this.bookTaskDetailService) {
|
||||
this.bookTaskDetailService = await BookTaskDetailService.getInstance()
|
||||
}
|
||||
if (!this.subtitle) {
|
||||
this.subtitle = new Subtitle()
|
||||
}
|
||||
if (!this.watermark) {
|
||||
this.watermark = new Watermark()
|
||||
this.taskScheduler = new TaskScheduler()
|
||||
this.bookServiceBasic = new BookServiceBasic()
|
||||
}
|
||||
if (!this.bookBackTaskListService) {
|
||||
this.bookBackTaskListService = await BookBackTaskListService.getInstance()
|
||||
}
|
||||
}
|
||||
|
||||
// 主动返回前端的消息
|
||||
sendReturnMessage(data: GeneralResponse.MessageResponse, message_name = DEFINE_STRING.BOOK.GET_COPYWRITING_RETURN) {
|
||||
global.newWindow[0].win.webContents.send(message_name, data)
|
||||
@ -104,15 +75,9 @@ export class ReverseBook extends BookBasic {
|
||||
* 获取小说的任务列表
|
||||
* @param {*} bookTaskCondition 查询任务列表的条件
|
||||
*/
|
||||
async GetBookTaskData(bookTaskCondition): Promise<GeneralResponse.ErrorItem | GeneralResponse.SuccessItem> {
|
||||
async GetBookTaskData(bookTaskCondition: Book.QueryBookTaskCondition): Promise<GeneralResponse.ErrorItem | GeneralResponse.SuccessItem> {
|
||||
try {
|
||||
await this.InitService()
|
||||
let _bookTaskService = await BookTaskService.getInstance()
|
||||
let res = _bookTaskService.GetBookTaskData(bookTaskCondition)
|
||||
if (res.code == 0) {
|
||||
throw new Error(res.message)
|
||||
}
|
||||
|
||||
let res = await this.bookServiceBasic.GetBookTaskData(bookTaskCondition)
|
||||
// //TODO 这个后面是不是将所有的数据都是用数据库
|
||||
// // 这边加载自定义风格
|
||||
// let styleLists = await this.tagDefine.getTagDataByTypeAndProperty('dynamic', "style_tags");
|
||||
@ -142,10 +107,10 @@ export class ReverseBook extends BookBasic {
|
||||
// }
|
||||
// res.data.bookTasks[index].styleList = styleList;
|
||||
// }
|
||||
return successMessage(res.data, '获取小说任务成功', 'ReverseBook_GetBookTaskData')
|
||||
return successMessage(res, '获取小说任务成功', 'ReverseBook_GetBookTaskData')
|
||||
} catch (error) {
|
||||
return errorMessage(
|
||||
'获取小说对应批次错误,错误信息入校:' + error.message,
|
||||
'获取小说对应批次错误,错误信息入下:' + error.message,
|
||||
'ReverseBook_GetBookTaskData'
|
||||
)
|
||||
}
|
||||
@ -153,20 +118,15 @@ export class ReverseBook extends BookBasic {
|
||||
|
||||
/**
|
||||
* 获取小说的所有的任务详情
|
||||
* @param bookTaskId
|
||||
* @param bookTaskId 小说任务的ID
|
||||
*/
|
||||
async GetBookTaskDetail(bookTaskId: string) {
|
||||
async GetBookTaskDetail(bookTaskId: string): Promise<GeneralResponse.ErrorItem | GeneralResponse.SuccessItem> {
|
||||
try {
|
||||
await this.InitService()
|
||||
let _bookTaskDetailService = await BookTaskDetailService.getInstance()
|
||||
let res = _bookTaskDetailService.GetBookTaskData({ bookTaskId: bookTaskId })
|
||||
if (res.code == 0) {
|
||||
throw new Error(res.message)
|
||||
}
|
||||
return res
|
||||
let res = await this.bookServiceBasic.GetBookTaskDetailData({ bookTaskId: bookTaskId })
|
||||
return successMessage(res, '获取小说任务详情成功', 'ReverseBook_GetBookTaskDetail')
|
||||
} catch (error) {
|
||||
return errorMessage(
|
||||
'获取小说对应批次错误,错误信息入校:' + error.message,
|
||||
'获取小说对应批次错误,错误信息入下:' + error.message,
|
||||
'ReverseBook_GetBookTaskData'
|
||||
)
|
||||
}
|
||||
@ -236,10 +196,7 @@ export class ReverseBook extends BookBasic {
|
||||
* @returns
|
||||
*/
|
||||
async GetBookAndTask(bookId: string, bookTaskName: string) {
|
||||
let book = this.bookService.GetBookDataById(bookId)
|
||||
if (book == null) {
|
||||
throw new Error("查找小说数据失败");
|
||||
}
|
||||
let book = await this.bookServiceBasic.GetBookDataById(bookId)
|
||||
// 获取小说对应的批次任务数据,默认初始化为第一个
|
||||
let condition = {
|
||||
bookId: bookId
|
||||
@ -249,20 +206,14 @@ export class ReverseBook extends BookBasic {
|
||||
} else {
|
||||
condition["id"] = bookTaskName
|
||||
}
|
||||
let bookTaskRes = await this.bookTaskService.GetBookTaskData(condition)
|
||||
if (bookTaskRes.data.bookTasks.length <= 0 || bookTaskRes.data.total <= 0) {
|
||||
let msg = "没有找到对应的小说批次任务数据"
|
||||
this.taskScheduler.AddLogToDB(bookId, book.type, msg, OtherData.DEFAULT, LoggerStatus.FAIL)
|
||||
throw new Error(msg)
|
||||
}
|
||||
return { book: book as Book.SelectBook, bookTask: bookTaskRes.data.bookTasks[0] as Book.SelectBookTask }
|
||||
let bookTaskRes = await this.bookServiceBasic.GetBookTaskData(condition)
|
||||
return { book: book as Book.SelectBook, bookTask: bookTaskRes.bookTasks[0] as Book.SelectBookTask }
|
||||
}
|
||||
/**
|
||||
* 开始分镜任务
|
||||
*/
|
||||
async ComputeStoryboard(bookId: string): Promise<any> {
|
||||
try {
|
||||
await this.InitService()
|
||||
let { book, bookTask } = await this.GetBookAndTask(bookId, 'output_00001')
|
||||
let res = await this.basicReverse.ComputeStoryboardFunc(bookId, bookTask.id);
|
||||
// 分镜成功直接返回
|
||||
@ -279,15 +230,15 @@ export class ReverseBook extends BookBasic {
|
||||
*/
|
||||
async Framing(bookId: string): Promise<GeneralResponse.ErrorItem | GeneralResponse.SuccessItem> {
|
||||
try {
|
||||
await this.InitService()
|
||||
let { book, bookTask } = await this.GetBookAndTask(bookId, 'output_00001')
|
||||
|
||||
let bookTaskDetail = undefined as Book.SelectBookTaskDetail[]
|
||||
try {
|
||||
// 获取所有的分镜数据
|
||||
let bookTaskDetail = this.bookTaskDetailService.GetBookTaskData({
|
||||
bookTaskDetail = await this.bookServiceBasic.GetBookTaskDetailData({
|
||||
bookId: bookId,
|
||||
bookTaskId: bookTask.id
|
||||
})
|
||||
if (bookTaskDetail.data.length <= 0) {
|
||||
} catch (error) {
|
||||
// 传入的分镜数据为空,需要重新获取
|
||||
await this.taskScheduler.AddLogToDB(
|
||||
bookId,
|
||||
@ -298,9 +249,7 @@ export class ReverseBook extends BookBasic {
|
||||
)
|
||||
throw new Error("没有传入分镜数据,请先进行分镜计算");
|
||||
}
|
||||
|
||||
let bookTaskDetails = bookTaskDetail.data as Book.SelectBookTaskDetail[]
|
||||
|
||||
let bookTaskDetails = bookTaskDetail;
|
||||
|
||||
for (let i = 0; i < bookTaskDetails.length; i++) {
|
||||
const item = bookTaskDetails[i];
|
||||
@ -314,12 +263,12 @@ export class ReverseBook extends BookBasic {
|
||||
LoggerStatus.SUCCESS
|
||||
)
|
||||
|
||||
bookTaskDetail = this.bookTaskDetailService.GetBookTaskData({
|
||||
bookTaskDetail = await this.bookServiceBasic.GetBookTaskDetailData({
|
||||
bookId: bookId,
|
||||
bookTaskId: bookTask.id
|
||||
})
|
||||
|
||||
bookTaskDetails = bookTaskDetail.data as Book.SelectBookTaskDetail[]
|
||||
bookTaskDetails = bookTaskDetail as Book.SelectBookTaskDetail[]
|
||||
for (let i = 0; i < bookTaskDetails.length; i++) {
|
||||
const item = bookTaskDetails[i];
|
||||
let res = await this.basicReverse.FrameFunc(book, item);
|
||||
@ -331,96 +280,70 @@ export class ReverseBook extends BookBasic {
|
||||
return errorMessage("开始切割视频并抽帧失败,失败信息如下:" + error.message, 'ReverseBook_Framing')
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 提起文案
|
||||
*/
|
||||
async GetCopywriting(bookId: string, bookTaskId: string = null): Promise<GeneralResponse.ErrorItem | GeneralResponse.SuccessItem> {
|
||||
try {
|
||||
await this.InitService()
|
||||
let { book, bookTask } = await this.GetBookAndTask(bookId, bookTaskId ? bookTaskId : 'output_00001')
|
||||
if (isEmpty(book.subtitlePosition)) {
|
||||
throw new Error("请先设置小说的字幕位置")
|
||||
}
|
||||
// 获取所有的分镜数据
|
||||
let bookTaskDetail = this.bookTaskDetailService.GetBookTaskData({
|
||||
bookId: bookId,
|
||||
bookTaskId: bookTask.id
|
||||
})
|
||||
if (bookTaskDetail.data.length <= 0) {
|
||||
// 传入的分镜数据为空,需要重新获取
|
||||
await this.taskScheduler.AddLogToDB(
|
||||
bookId,
|
||||
book.type,
|
||||
`没有传入分镜数据,请先进行分镜计算`,
|
||||
OtherData.DEFAULT,
|
||||
LoggerStatus.DOING
|
||||
)
|
||||
throw new Error("没有传入分镜数据,请先进行分镜计算");
|
||||
}
|
||||
|
||||
let bookTaskDetails = bookTaskDetail.data as Book.SelectBookTaskDetail[]
|
||||
for (let i = 0; i < bookTaskDetails.length; i++) {
|
||||
const item = bookTaskDetails[i];
|
||||
|
||||
let res = await this.subtitle.GetVideoFrameText({
|
||||
id: item.id,
|
||||
videoPath: item.videoPath,
|
||||
type: SubtitleSavePositionType.STORYBOARD_VIDEO,
|
||||
subtitlePosition: book.subtitlePosition
|
||||
})
|
||||
if (res.code == 0) {
|
||||
throw new Error(res.message)
|
||||
}
|
||||
// 修改数据
|
||||
this.bookTaskDetailService.UpdateBookTaskDetail(item.id, {
|
||||
word: res.data,
|
||||
afterGpt: res.data
|
||||
});
|
||||
|
||||
// let res = await this.basicReverse.GetCopywritingFunc(book, item);
|
||||
// 将当前的数据实时返回,前端进行修改
|
||||
this.sendReturnMessage({
|
||||
code: 1,
|
||||
id: item.id,
|
||||
type: ResponseMessageType.GET_TEXT,
|
||||
data: res.data // 返回识别到的文案
|
||||
})
|
||||
|
||||
// 添加日志
|
||||
await this.taskScheduler.AddLogToDB(
|
||||
bookId,
|
||||
book.type,
|
||||
`${item.name} 识别文案成功`,
|
||||
bookTask.id,
|
||||
LoggerStatus.SUCCESS
|
||||
)
|
||||
}
|
||||
return successMessage(null, "识别是所有文案成功", "ReverseBook_GetCopywriting")
|
||||
} catch (error) {
|
||||
return errorMessage("获取分镜数据失败,失败信息如下:" + error.message, 'ReverseBook_GetCopywriting')
|
||||
}
|
||||
}
|
||||
//#endregion
|
||||
|
||||
//#region 反推相关任务
|
||||
|
||||
/**
|
||||
* 所有的反推任务的入口
|
||||
* @param id 需要反推的ID,可以是小说任务ID,也可以是小说分镜ID
|
||||
* @param operateBookType 操作的分类
|
||||
* @param type 反推的类型
|
||||
* @returns
|
||||
*/
|
||||
async AddReversePrompt(id: string, operateBookType: OperateBookType, type: BookType): Promise<GeneralResponse.ErrorItem | GeneralResponse.SuccessItem> {
|
||||
try {
|
||||
let bookTaskDetailIds: string[] = []
|
||||
let bookTaskDetails = [] as Book.SelectBookTaskDetail[]
|
||||
if (operateBookType == OperateBookType.BOOKTASK) {
|
||||
bookTaskDetails = await this.bookServiceBasic.GetBookTaskDetailData({
|
||||
bookTaskId: id
|
||||
})
|
||||
} else if (operateBookType == OperateBookType.BOOKTASKDETAIL) {
|
||||
let bookTaskDetail = await this.bookServiceBasic.GetBookTaskDetailDataById(id);
|
||||
bookTaskDetails = [bookTaskDetail]
|
||||
} else if (operateBookType == OperateBookType.UNDERBOOKTASK) {
|
||||
// 下生图
|
||||
let tempBooktaskDetail = await this.bookServiceBasic.GetBookTaskDetailDataById(id);
|
||||
let tempBooktaskDetails = await this.bookServiceBasic.GetBookTaskDetailData({
|
||||
bookTaskId: tempBooktaskDetail.bookTaskId
|
||||
});
|
||||
for (let i = 0; i < tempBooktaskDetails.length; i++) {
|
||||
const element = tempBooktaskDetails[i];
|
||||
if (tempBooktaskDetail.no <= element.no) {
|
||||
bookTaskDetails.push(element)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
throw new Error("未知的操作模式,请检查");
|
||||
}
|
||||
|
||||
for (let i = 0; i < bookTaskDetails.length; i++) {
|
||||
const element = bookTaskDetails[i];
|
||||
bookTaskDetailIds.push(element.id)
|
||||
}
|
||||
if (bookTaskDetailIds.length <= 0) {
|
||||
throw new Error("没有需要反推的数据,请检查")
|
||||
}
|
||||
await this.AddReversePromptTask(bookTaskDetailIds, type);
|
||||
return successMessage(null, "添加反推任务成功", 'ReverseBook_AddReversePrompt')
|
||||
} catch (error) {
|
||||
return errorMessage("添加反推任务失败,错误信息如下:" + error.message, "ReverseBook_AddReversePrompt")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加单句生图任务
|
||||
* @param bookTaskDetailId 小说单个分镜的ID
|
||||
* @param type 反推的类型
|
||||
* @returns
|
||||
*/
|
||||
async AddReversePromptTask(bookTaskDetailIds: string[], type: BookType): Promise<GeneralResponse.ErrorItem | GeneralResponse.SuccessItem> {
|
||||
async AddReversePromptTask(bookTaskDetailIds: string[], type: BookType): Promise<void> {
|
||||
try {
|
||||
await this.InitService()
|
||||
for (let index = 0; index < bookTaskDetailIds.length; index++) {
|
||||
const bookTaskDetailId = bookTaskDetailIds[index];
|
||||
let bookTaskDetail = this.bookTaskDetailService.GetBookTaskDetailDataById(bookTaskDetailId)
|
||||
if (bookTaskDetail == null) {
|
||||
throw new Error("没有找到对应的分镜数据")
|
||||
}
|
||||
let book = this.bookService.GetBookDataById(bookTaskDetail.bookId)
|
||||
let bookTaskDetail = await this.bookServiceBasic.GetBookTaskDetailDataById(bookTaskDetailId)
|
||||
let book = await this.bookServiceBasic.GetBookDataById(bookTaskDetail.bookId)
|
||||
// 是不是又额外的类型,没有的话试用小说的类型
|
||||
let task_type = undefined as BookBackTaskType
|
||||
if (!type) {
|
||||
@ -436,17 +359,14 @@ export class ReverseBook extends BookBasic {
|
||||
default:
|
||||
throw new Error("暂不支持的推理类型")
|
||||
}
|
||||
let taskRes = await this.bookBackTaskListService.AddBookBackTask(book.id, task_type, TaskExecuteType.AUTO, bookTaskDetail.bookTaskId, bookTaskDetail.id
|
||||
// 添加后台任务
|
||||
await this.bookServiceBasic.AddBookBackTask(book.id, task_type, TaskExecuteType.AUTO, bookTaskDetail.bookTaskId, bookTaskDetail.id
|
||||
);
|
||||
if (taskRes.code == 0) {
|
||||
throw new Error(taskRes.message)
|
||||
}
|
||||
// 添加返回日志
|
||||
await this.taskScheduler.AddLogToDB(book.id, book.type, `添加 ${task_type} 反推任务成功`, bookTaskDetail.bookTaskId, LoggerStatus.SUCCESS)
|
||||
}
|
||||
return successMessage(null, "添加反推任务成功", "ReverseBook_AddReversePromptTask")
|
||||
} catch (error) {
|
||||
return errorMessage("添加单个反推失败,错误信息如下:" + error.message, "ReverseBook_SingleReversePrompt")
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
@ -481,14 +401,12 @@ export class ReverseBook extends BookBasic {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 删除指定的提示词数据
|
||||
* @param bookTaskDetailIds 要删除的提示词ID
|
||||
*/
|
||||
async RemoveReverseData(bookTaskDetailIds: string[]) {
|
||||
try {
|
||||
await this.InitService()
|
||||
// 开始删除
|
||||
if (bookTaskDetailIds.length <= 0) {
|
||||
throw new Error("没有传入要删除的数据")
|
||||
@ -496,7 +414,7 @@ export class ReverseBook extends BookBasic {
|
||||
|
||||
for (let i = 0; i < bookTaskDetailIds.length; i++) {
|
||||
const element = bookTaskDetailIds[i];
|
||||
this.bookTaskDetailService.DeleteBookTaskDetailReversePromptById(element)
|
||||
await this.bookServiceBasic.DeleteBookTaskDetailReversePromptById(element);
|
||||
}
|
||||
|
||||
// 全部删除完毕
|
||||
@ -505,6 +423,30 @@ export class ReverseBook extends BookBasic {
|
||||
return errorMessage("删除反推数据失败,错误信息如下:" + error.message, "ReverseBook_RemoveReverseData")
|
||||
}
|
||||
}
|
||||
//#endregion
|
||||
|
||||
//#region 提示词相关操作
|
||||
|
||||
/**
|
||||
* 合并提示词的入口函数
|
||||
* @param id 合并的ID,可以是小说任务ID,也可以是小说分镜ID
|
||||
* @param operateBookType 操作的类型
|
||||
* @param type 合并的类型(MJ。SD 。D3)
|
||||
* @returns
|
||||
*/
|
||||
async MergePrompt(id: string, type: PromptMergeType, operateBookType: OperateBookType): Promise<GeneralResponse.ErrorItem | GeneralResponse.SuccessItem> {
|
||||
try {
|
||||
if (type == PromptMergeType.MJ_MERGE) {
|
||||
return await this.mjOpt.MergePrompt(id, operateBookType);
|
||||
} else if (type == PromptMergeType.SD_MERGE) {
|
||||
return await this.sdOpt.MergePrompt(id, operateBookType)
|
||||
} else {
|
||||
throw new Error("未知的合并模式,请检查")
|
||||
}
|
||||
} catch (error) {
|
||||
return errorMessage("合并提示词失败,错误信息如下:" + error.message, "ReverseBook_MergePrompt")
|
||||
}
|
||||
}
|
||||
|
||||
//#endregion
|
||||
}
|
||||
|
||||
59
src/main/Service/Book/bookFrame.ts
Normal file
59
src/main/Service/Book/bookFrame.ts
Normal file
@ -0,0 +1,59 @@
|
||||
import { isEmpty } from "lodash";
|
||||
import { GeneralResponse } from "../../../model/generalResponse";
|
||||
import { errorMessage, successMessage } from "../../Public/generalTools";
|
||||
import { BookServiceBasic } from "../ServiceBasic/bookServiceBasic";
|
||||
import path from 'path';
|
||||
import { FfmpegOptions } from "../ffmpegOptions";
|
||||
import { CheckFileOrDirExist, CopyFileOrFolder, DeleteFolderAllFile } from "../../../define/Tools/file";
|
||||
import fs from 'fs';
|
||||
|
||||
export class BookFrame {
|
||||
bookServiceBasic: BookServiceBasic
|
||||
ffmpegOptions: FfmpegOptions
|
||||
constructor() {
|
||||
this.bookServiceBasic = new BookServiceBasic();
|
||||
this.ffmpegOptions = new FfmpegOptions();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 替换指定分镜的视频当前帧
|
||||
* @param bookTaskDetailId 指定的小说分镜ID
|
||||
* @param current 要替换的帧
|
||||
*/
|
||||
async ReplaceVideoCurrentFrame(bookTaskDetailId: string, current: number): Promise<GeneralResponse.ErrorItem | GeneralResponse.SuccessItem> {
|
||||
try {
|
||||
let bookTaskDetail = await this.bookServiceBasic.GetBookTaskDetailDataById(bookTaskDetailId);
|
||||
let videoPath = bookTaskDetail.videoPath;
|
||||
if (isEmpty(videoPath)) {
|
||||
throw new Error('未找到对应的视频路径,请检查');
|
||||
}
|
||||
let oldImagePath = bookTaskDetail.oldImage;
|
||||
let tempOldImagePath = path.join(path.dirname(oldImagePath), `temp_${bookTaskDetail.name}.png`);
|
||||
// 删除之前的图片
|
||||
if (await CheckFileOrDirExist(tempOldImagePath)) {
|
||||
await fs.promises.unlink(tempOldImagePath);
|
||||
}
|
||||
// 开始裁剪
|
||||
let res = await this.ffmpegOptions.FfmpegGetFrame(current, videoPath, tempOldImagePath);
|
||||
if (res.code == 0) {
|
||||
// 抽帧失败
|
||||
global.logger.error('BookFrame_ReplaceVideoCurrentFrame', '抽取视频指定帧失败,请检查');
|
||||
throw new Error('抽取视频指定帧失败,请检查');
|
||||
}
|
||||
// 成功。开始替换
|
||||
let fileExist = await CheckFileOrDirExist(tempOldImagePath);
|
||||
if (!fileExist) {
|
||||
throw new Error('抽帧出来的图片不存在,请检查');
|
||||
}
|
||||
await fs.promises.unlink(oldImagePath);
|
||||
// 重命名
|
||||
await CopyFileOrFolder(tempOldImagePath, oldImagePath);
|
||||
await fs.promises.unlink(tempOldImagePath);
|
||||
return successMessage(oldImagePath + '?t=' + new Date().getTime(), '视频指定的视频帧成功', 'BookFrame_ReplaceVideoCurrentFrame');
|
||||
} catch (error) {
|
||||
return errorMessage('替换指定分镜的视频当前帧失败,失败信息如下:' + error.toString(), 'BookFrame_ReplaceVideoCurrentFrame');
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,10 +1,15 @@
|
||||
import { CheckFolderExistsOrCreate, DeleteFolderAllFile } from "../../../define/Tools/file";
|
||||
import { CheckFolderExistsOrCreate, CopyFileOrFolder, DeleteFolderAllFile } from "../../../define/Tools/file";
|
||||
import { BookTaskService } from "../../../define/db/service/Book/bookTaskService";
|
||||
import { BookTaskDetailService } from "../../../define/db/service/Book/bookTaskDetailService";
|
||||
import { BookService } from "../../../define/db/service/Book/bookService";
|
||||
import { BookTaskStatus, OperateBookType } from "../../../define/enum/bookEnum";
|
||||
import { BookTaskStatus, CopyImageType, OperateBookType } from "../../../define/enum/bookEnum";
|
||||
import { errorMessage, successMessage } from "../../Public/generalTools";
|
||||
import { Book } from "../../../model/book";
|
||||
import path from 'path'
|
||||
import { cloneDeep, isEmpty } from "lodash";
|
||||
import { define } from '../../../define/define'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
import { GeneralResponse } from "../../../model/generalResponse";
|
||||
|
||||
/**
|
||||
* 小说批次相关的操作
|
||||
@ -33,7 +38,7 @@ export class BookTask {
|
||||
* 重置小说任务数据
|
||||
* @param bookTaskId 小说任务ID
|
||||
*/
|
||||
async ReSetBookTask(bookTaskId: string) {
|
||||
async ReSetBookTask(bookTaskId: string): Promise<GeneralResponse.ErrorItem | GeneralResponse.SuccessItem> {
|
||||
try {
|
||||
console.log(bookTaskId)
|
||||
await this.InitService()
|
||||
@ -57,7 +62,7 @@ export class BookTask {
|
||||
* 删除对应的小说批次数据
|
||||
* @param bookTaskId 小说批次ID
|
||||
*/
|
||||
async DeleteBookTask(bookTaskId: string) {
|
||||
async DeleteBookTask(bookTaskId: string): Promise<GeneralResponse.ErrorItem | GeneralResponse.SuccessItem> {
|
||||
try {
|
||||
await this.InitService();
|
||||
let bookTask = this.bookTaskService.GetBookTaskDataById(bookTaskId);
|
||||
@ -75,4 +80,212 @@ export class BookTask {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将小说批次任务出图进行一拆四
|
||||
* @param bookTaskId 操作的小说批次任务ID
|
||||
* @returns
|
||||
*/
|
||||
async OneToFourBookTask(bookTaskId: string) {
|
||||
try {
|
||||
console.log(bookTaskId)
|
||||
await this.InitService();
|
||||
let copyCount = 100
|
||||
let bookTask = this.bookTaskService.GetBookTaskDataById(bookTaskId)
|
||||
if (bookTask == null) {
|
||||
throw new Error("没有找到对应的数小说任务,请检查数据")
|
||||
}
|
||||
// 获取所有的出图中最少的
|
||||
let bookTaskDetail = this.bookTaskDetailService.GetBookTaskData({
|
||||
bookTaskId: bookTaskId
|
||||
}).data as Book.SelectBookTaskDetail[]
|
||||
if (bookTaskDetail == null || bookTaskDetail.length <= 0) {
|
||||
throw new Error("没有对应的小说分镜任务,请先添加分镜任务")
|
||||
}
|
||||
|
||||
for (let i = 0; i < bookTaskDetail.length; i++) {
|
||||
const element = bookTaskDetail[i];
|
||||
if (isEmpty(element.subImagePath)) {
|
||||
throw new Error("检测到图片没有出完,请先检查出图")
|
||||
}
|
||||
if (element.subImagePath == null || element.subImagePath.length <= 0) {
|
||||
throw new Error("检测到图片没有出完,请先检查出图")
|
||||
}
|
||||
if (element.subImagePath.length < copyCount) {
|
||||
copyCount = element.subImagePath.length
|
||||
}
|
||||
}
|
||||
if (copyCount <= 0) {
|
||||
throw new Error("批次设置错误,无法进行一拆四")
|
||||
}
|
||||
// 开始复制
|
||||
let res = await this.CopyNewBookTask(bookTask, bookTaskDetail, copyCount - 1, CopyImageType.ONE)
|
||||
if (res.code == 0) {
|
||||
throw new Error(res.message)
|
||||
}
|
||||
return successMessage(res.data, "一拆四成功", "BookBasic_OneToFourBookTask")
|
||||
} catch (error) {
|
||||
return errorMessage("一拆四失败,失败信息如下:" + error.message, "BookBasic_OneToFourBookTask")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 复制一个小说批次任务,创建新的小说批次任务
|
||||
* @param oldBookTaskId
|
||||
* @param copyCount 复制的数量
|
||||
* @param isCopyImage 是否复制图片
|
||||
*/
|
||||
async CopyNewBookTask(sourceBookTask: Book.SelectBookTask, sourceBookTaskDetail: Book.SelectBookTaskDetail[], copyCount: number, copyImageType: CopyImageType) {
|
||||
try {
|
||||
await this.InitService();
|
||||
let addBookTask = [] as Book.SelectBookTask[]
|
||||
let addBookTaskDetail = [] as Book.SelectBookTaskDetail[]
|
||||
|
||||
// 先处理文件夹的创建,包括小说任务的和小说任务分镜的
|
||||
for (let i = 0; i < copyCount; i++) {
|
||||
let maxNo = this.bookTaskService.realm
|
||||
.objects('BookTask')
|
||||
.filtered('bookId = $0', sourceBookTask.bookId)
|
||||
.max('no')
|
||||
let no = maxNo == null ? 1 : Number(maxNo) + 1 + i
|
||||
let name = 'output_0000' + no
|
||||
let imageFolder = path.join(define.project_path, `${sourceBookTask.bookId}/tmp/${name}`)
|
||||
await CheckFolderExistsOrCreate(imageFolder)
|
||||
// 创建对应的文件夹
|
||||
let addOneBookTask = {
|
||||
id: uuidv4(),
|
||||
bookId: sourceBookTask.bookId,
|
||||
no: no,
|
||||
name: name,
|
||||
generateVideoPath: sourceBookTask.generateVideoPath,
|
||||
srtPath: sourceBookTask.srtPath,
|
||||
audioPath: sourceBookTask.audioPath,
|
||||
draftSrtStyle: sourceBookTask.draftSrtStyle,
|
||||
backgroundMusic: sourceBookTask.backgroundMusic,
|
||||
friendlyReminder: sourceBookTask.friendlyReminder,
|
||||
imageFolder: path.relative(define.project_path, imageFolder),
|
||||
status: sourceBookTask.status,
|
||||
errorMsg: sourceBookTask.errorMsg,
|
||||
updateTime: new Date(),
|
||||
createTime: new Date(),
|
||||
isAuto: sourceBookTask.isAuto,
|
||||
imageStyle: sourceBookTask.imageStyle,
|
||||
autoAnalyzeCharacter: sourceBookTask.autoAnalyzeCharacter,
|
||||
customizeImageStyle: sourceBookTask.customizeImageStyle,
|
||||
videoConfig: sourceBookTask.videoConfig,
|
||||
prefixPrompt: sourceBookTask.prefixPrompt,
|
||||
suffixPrompt: sourceBookTask.suffixPrompt,
|
||||
version: sourceBookTask.version,
|
||||
imageCategory: sourceBookTask.imageCategory,
|
||||
} as Book.SelectBookTask
|
||||
|
||||
addBookTask.push(addOneBookTask)
|
||||
|
||||
for (let j = 0; j < sourceBookTaskDetail.length; j++) {
|
||||
const element = sourceBookTaskDetail[j];
|
||||
|
||||
let outImagePath = undefined as string
|
||||
let subImagePath = [] as string[]
|
||||
|
||||
if (copyImageType == CopyImageType.ALL) { // 直接全部复制
|
||||
outImagePath = element.outImagePath
|
||||
subImagePath = element.subImagePath
|
||||
} else if (copyImageType == CopyImageType.ONE) { // 只复制对应的
|
||||
let oldImage = element.subImagePath[i + 1]
|
||||
outImagePath = path.join(imageFolder, path.basename(element.outImagePath))
|
||||
await CopyFileOrFolder(oldImage, outImagePath)
|
||||
|
||||
subImagePath = []
|
||||
}
|
||||
else if (copyImageType == CopyImageType.NONE) {
|
||||
outImagePath = undefined
|
||||
subImagePath = []
|
||||
} else {
|
||||
throw new Error("无效的图片复制类型")
|
||||
}
|
||||
if (outImagePath) {
|
||||
// 单独处理一下显示的图片
|
||||
let imageBaseName = path.basename(element.outImagePath);
|
||||
let newImageBaseName = path.join(define.project_path, `${sourceBookTask.bookId}/tmp/${name}/${imageBaseName}`)
|
||||
await CopyFileOrFolder(outImagePath, newImageBaseName)
|
||||
}
|
||||
// 处理SD设置
|
||||
let sdConifg = undefined
|
||||
if (element.sdConifg) {
|
||||
let sdConifg = cloneDeep(element.sdConifg)
|
||||
if (sdConifg.webuiConfig) {
|
||||
let tempSdConfig = cloneDeep(sdConifg.webuiConfig);
|
||||
tempSdConfig.id = uuidv4()
|
||||
sdConifg.webuiConfig = tempSdConfig
|
||||
}
|
||||
}
|
||||
|
||||
let reverseId = uuidv4()
|
||||
// 处理反推数据
|
||||
let reverseMessage = [] as Book.ReversePrompt[]
|
||||
if (element.reversePrompt && element.reversePrompt.length > 0) {
|
||||
reverseMessage = cloneDeep(element.reversePrompt)
|
||||
for (let k = 0; k < reverseMessage.length; k++) {
|
||||
reverseMessage[k].id = uuidv4()
|
||||
reverseMessage[k].bookTaskDetailId = reverseId
|
||||
}
|
||||
}
|
||||
|
||||
let addOneBookTaskDetail = {
|
||||
id: reverseId,
|
||||
no: element.no,
|
||||
name: element.name,
|
||||
bookId: sourceBookTask.bookId,
|
||||
bookTaskId: addOneBookTask.id,
|
||||
videoPath: path.relative(define.project_path, element.videoPath),
|
||||
word: element.word,
|
||||
oldImage: path.relative(define.project_path, element.oldImage),
|
||||
afterGpt: element.afterGpt,
|
||||
startTime: element.startTime,
|
||||
endTime: element.endTime,
|
||||
timeLimit: element.timeLimit,
|
||||
subValue: element.subValue,
|
||||
characterTags: element.characterTags && element.characterTags.length > 0 ? cloneDeep(element.characterTags) : [],
|
||||
gptPrompt: element.gptPrompt,
|
||||
outImagePath: path.relative(define.project_path, outImagePath),
|
||||
subImagePath: subImagePath || [],
|
||||
prompt: element.prompt,
|
||||
adetailer: element.adetailer,
|
||||
sdConifg: sdConifg,
|
||||
createTime: new Date(),
|
||||
updateTime: new Date(),
|
||||
audioPath: element.audioPath,
|
||||
subtitlePosition: element.subtitlePosition,
|
||||
status: element.status,
|
||||
reversePrompt: reverseMessage,
|
||||
imageLock: element.imageLock
|
||||
} as Book.SelectBookTaskDetail
|
||||
addBookTaskDetail.push(addOneBookTaskDetail)
|
||||
}
|
||||
}
|
||||
|
||||
// 数据处理完毕,开始新增数据
|
||||
// 将所有的复制才做,全部放在一个事务中
|
||||
this.bookTaskService.transaction(() => {
|
||||
for (let i = 0; i < addBookTask.length; i++) {
|
||||
const element = addBookTask[i];
|
||||
this.bookTaskService.realm.create('BookTask', element)
|
||||
}
|
||||
for (let i = 0; i < addBookTaskDetail.length; i++) {
|
||||
const element = addBookTaskDetail[i];
|
||||
this.bookTaskDetailService.realm.create('BookTaskDetail', element)
|
||||
}
|
||||
})
|
||||
// 全部创建完成
|
||||
// 查找到数据,然后全部返回
|
||||
let returnBookTask = this.bookTaskService.GetBookTaskData({
|
||||
bookId: sourceBookTask.bookId
|
||||
}).data as Book.SelectBookTask[]
|
||||
|
||||
return successMessage(returnBookTask, "复制小说任务成功", "BookBasic_CopyNewBookTask")
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -10,14 +10,17 @@ import { CheckFolderExistsOrCreate } from "../../../define/Tools/file";
|
||||
import { BookTaskDetailService } from "../../../define/db/service/Book/bookTaskDetailService";
|
||||
import fs from 'fs'
|
||||
import { ClipDraft } from '../../Public/clipDraft'
|
||||
import { BookServiceBasic } from "../ServiceBasic/bookServiceBasic";
|
||||
|
||||
export class BookVideo {
|
||||
bookService: BookService
|
||||
bookTaskService: BookTaskService
|
||||
setting: Setting
|
||||
bookTaskDetailService: BookTaskDetailService
|
||||
bookServiceBasic: BookServiceBasic
|
||||
constructor() {
|
||||
this.setting = new Setting(global)
|
||||
this.bookServiceBasic = new BookServiceBasic()
|
||||
}
|
||||
|
||||
async InitService() {
|
||||
@ -71,17 +74,16 @@ export class BookVideo {
|
||||
}
|
||||
|
||||
// 将修改数据放在一个事务中
|
||||
this.bookService.transaction(() => {
|
||||
for (let i = 0; i < bookTasks.length; i++) {
|
||||
const element = bookTasks[i];
|
||||
let modifyBookTask = this.bookService.realm.objectForPrimaryKey('BookTask', element.id);
|
||||
modifyBookTask.backgroundMusic = book.backgroundMusic;
|
||||
modifyBookTask.friendlyReminder = book.friendlyReminder;
|
||||
modifyBookTask.draftSrtStyle = book.draftSrtStyle;
|
||||
modifyBookTask.srtPath = book.srtPath;
|
||||
modifyBookTask.audioPath = book.audioPath;
|
||||
}
|
||||
this.bookServiceBasic.UpdetedBookTaskData(element.id, {
|
||||
backgroundMusic: book.backgroundMusic,
|
||||
friendlyReminder: book.friendlyReminder,
|
||||
draftSrtStyle: book.draftSrtStyle,
|
||||
srtPath: book.srtPath,
|
||||
audioPath: book.audioPath,
|
||||
})
|
||||
}
|
||||
return successMessage({
|
||||
backgroundMusic: book.backgroundMusic,
|
||||
friendlyReminder: book.friendlyReminder,
|
||||
|
||||
178
src/main/Service/GPT/gpt.ts
Normal file
178
src/main/Service/GPT/gpt.ts
Normal file
@ -0,0 +1,178 @@
|
||||
import { isEmpty } from "lodash";
|
||||
import { gptDefine } from "../../../define/gptDefine";
|
||||
import axios from "axios";
|
||||
|
||||
/**
|
||||
* 一些GPT相关的服务都在这边
|
||||
*/
|
||||
export class GptService {
|
||||
gptUrl: string = undefined
|
||||
gptModel: string = undefined
|
||||
gptApiKey: string = undefined
|
||||
|
||||
|
||||
//#region GPT 设置
|
||||
|
||||
/**
|
||||
* 获取GPT的所有的服务商
|
||||
* @param type 获取的类型,就是all
|
||||
* @param callback 这个是个回调函数,干嘛的不知道
|
||||
* @returns
|
||||
*/
|
||||
private async GetGPTBusinessOption(type: string, callback: Function = null): Promise<any> {
|
||||
let res = await gptDefine.getGptDataByTypeAndProperty(type, "gpt_options", []);
|
||||
if (res.code == 0) {
|
||||
throw new Error(res.message)
|
||||
} else {
|
||||
if (callback) {
|
||||
callback(res.data)
|
||||
}
|
||||
return res.data
|
||||
}
|
||||
}
|
||||
|
||||
async RefreshGptSetting() {
|
||||
let all_options = await this.GetGPTBusinessOption("all", (value) => value.gpt_url);
|
||||
let index = all_options.findIndex(item => item.value == global.config.gpt_business && item.gpt_url)
|
||||
if (index < 0) {
|
||||
throw new Error("没有找到指定的GPT服务商的配置,请检查")
|
||||
}
|
||||
this.gptUrl = all_options[index].gpt_url;
|
||||
this.gptApiKey = global.config.gpt_key;
|
||||
this.gptModel = global.config.gpt_model;
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化GPT的设置
|
||||
*/
|
||||
async InitGptSetting(refresh = false) {
|
||||
if (refresh) {
|
||||
await this.RefreshGptSetting()
|
||||
} else {
|
||||
// 判断是不是存在必要信息
|
||||
if (isEmpty(this.gptUrl) || isEmpty(this.gptModel) || isEmpty(this.gptApiKey)) {
|
||||
await this.RefreshGptSetting();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 适配一些请求体中的参数
|
||||
* @param data
|
||||
* @param gpt_url
|
||||
* @returns
|
||||
*/
|
||||
ModifyData(data: any, gpt_url: string = null) {
|
||||
let res = data;
|
||||
if (!gpt_url) {
|
||||
gpt_url = this.gptUrl
|
||||
}
|
||||
if (gpt_url.includes("dashscope.aliyuncs.com")) {
|
||||
res = {
|
||||
"model": data.model,
|
||||
"input": {
|
||||
"messages": data.messages,
|
||||
},
|
||||
"parameters": {
|
||||
"result_format": "message"
|
||||
}
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* 适配返回来的数据
|
||||
* @param res 返回的数据
|
||||
* @param gpt_url 请求的URL
|
||||
* @returns
|
||||
*/
|
||||
GetResponseContent(res: any, gpt_url: string = null) {
|
||||
let content = "";
|
||||
if (!gpt_url) {
|
||||
gpt_url = this.gptUrl
|
||||
}
|
||||
if (gpt_url.includes("dashscope.aliyuncs.com")) {
|
||||
content = res.data.output.choices[0].message.content;
|
||||
} else {
|
||||
content = res.data.choices[0].message.content;
|
||||
}
|
||||
return content;
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
/**
|
||||
* 发送GPT请求
|
||||
* @param {*} message 请求的信息
|
||||
* @param {*} gpt_url gpt的url,默认在global中取
|
||||
* @param {*} gpt_key gpt的key,默认在global中取
|
||||
* @param {*} gpt_model gpt的model,默认在global中取
|
||||
* @returns
|
||||
*/
|
||||
async FetchGpt(message: any, gpt_model: string = null, gpt_key: string = null, gpt_url: string = null): Promise<string> {
|
||||
try {
|
||||
await this.InitGptSetting();
|
||||
let data = {
|
||||
"model": gpt_model ? gpt_model : this.gptModel,
|
||||
"messages": message
|
||||
};
|
||||
|
||||
data = this.ModifyData(data, gpt_url);
|
||||
let config = {
|
||||
method: 'post',
|
||||
maxBodyLength: Infinity,
|
||||
url: gpt_url ? gpt_url : this.gptUrl,
|
||||
headers: {
|
||||
'Authorization': `Bearer ${gpt_key ? gpt_key : this.gptApiKey}`,
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
data: JSON.stringify(data)
|
||||
};
|
||||
|
||||
let res = await axios.request(config);
|
||||
let content = this.GetResponseContent(res, this.gptUrl);
|
||||
return content;
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
//#region 繁体中文 -> 简体中文
|
||||
|
||||
/**
|
||||
* 将繁体中文转换为简体中文
|
||||
* @param traditionalText 繁体中文文本
|
||||
* @param apiKey Lai API的 Key
|
||||
* @param baseUrl 请求的baseurl
|
||||
* @returns
|
||||
*/
|
||||
async ChineseTraditionalToSimplified(traditionalText: string, apiKey: string, baseUrl: string = null): Promise<string> {
|
||||
try {
|
||||
let message = [
|
||||
{
|
||||
"role": "system",
|
||||
"content": '我想让你充当中文繁体转简体专家,用简体中文100%还原繁体中文,不要加其他的联想,只把原有的繁体中文转换为简体中文,请检查所有信息是否准确,并在回答时保持简活,不需要任何其他反馈。'
|
||||
}, {
|
||||
"role": "user",
|
||||
"content": '上研究生後,發現導師竟然是曾經網戀的前男友。'
|
||||
}, {
|
||||
"role": "assistant",
|
||||
"content": '上研究生后,发现导师竟然是曾经网恋的前男友。'
|
||||
}, {
|
||||
"role": "user",
|
||||
"content": traditionalText
|
||||
}
|
||||
]
|
||||
|
||||
let baseSubUrl = baseUrl ? (baseUrl.endsWith('/') ? baseUrl + 'v1/chat/completions' : baseUrl + '/v1/chat/completions') : null;
|
||||
let url = baseSubUrl ? baseSubUrl : "https://api.laitool.cc/v1/chat/completions"
|
||||
// 开始请求,这个默认是使用的是LAI API的gpt-4o-mini
|
||||
let content = await this.FetchGpt(message, 'gpt-4o-mini', apiKey, url)
|
||||
return content
|
||||
} catch (error) {
|
||||
throw error
|
||||
}
|
||||
}
|
||||
//#endregion
|
||||
}
|
||||
@ -7,7 +7,7 @@ import { CheckFolderExistsOrCreate, CopyFileOrFolder, JoinPath } from "../../../
|
||||
import { define } from "../../../define/define"
|
||||
import { GetImageBase64, ImageSplit } from "../../../define/Tools/image";
|
||||
import MJApi from "./mjApi"
|
||||
import { BookBackTaskStatus, BookBackTaskType, BookTaskStatus, BookType, DialogType, MJAction, MergeType, OperateBookType, TaskExecuteType } from "../../../define/enum/bookEnum";
|
||||
import { BookBackTaskStatus, BookBackTaskType, BookTaskStatus, BookType, DialogType, MJAction, OperateBookType, TaskExecuteType } from "../../../define/enum/bookEnum";
|
||||
import { DEFINE_STRING } from "../../../define/define_string";
|
||||
import { MJ } from "../../../model/mj";
|
||||
import { MJRespoonseType } from "../../../define/enum/mjEnum";
|
||||
@ -21,6 +21,7 @@ import { TaskScheduler } from "../taskScheduler";
|
||||
import { BookService } from "../../../define/db/service/Book/bookService";
|
||||
import { Tools } from "../../../main/tools"
|
||||
import { MJSettingService } from "../../../define/db/service/SoftWare/mjSettingService";
|
||||
import { BookServiceBasic } from "../ServiceBasic/bookServiceBasic";
|
||||
|
||||
import path from "path"
|
||||
const { v4: uuidv4 } = require('uuid')
|
||||
@ -39,10 +40,12 @@ export class MJOpt {
|
||||
bookService: BookService
|
||||
tools: Tools;
|
||||
mjSettingService: MJSettingService;
|
||||
bookServiceBasic: BookServiceBasic
|
||||
constructor() {
|
||||
this.imageStyle = new ImageStyle()
|
||||
this.taskScheduler = new TaskScheduler()
|
||||
this.tools = new Tools()
|
||||
this.bookServiceBasic = new BookServiceBasic();
|
||||
}
|
||||
async InitService() {
|
||||
if (!this.reverseBook) {
|
||||
@ -208,7 +211,8 @@ export class MJOpt {
|
||||
|
||||
this.bookTaskDetail.UpdateBookTaskDetail(task.bookTaskDetailId, {
|
||||
status: BookTaskStatus.REVERSE_DONE,
|
||||
reversePrompt: reversePrompt
|
||||
reversePrompt: reversePrompt,
|
||||
gptPrompt: undefined
|
||||
});
|
||||
|
||||
task_res.prompt = JSON.stringify(reversePrompt);
|
||||
@ -331,20 +335,35 @@ export class MJOpt {
|
||||
* @param id 合并的ID
|
||||
* @param mergeType 合并的类型
|
||||
*/
|
||||
async MergePrompt(id: string, mergeType: MergeType): Promise<GeneralResponse.ErrorItem | GeneralResponse.SuccessItem> {
|
||||
async MergePrompt(id: string, operateBookType: OperateBookType): Promise<GeneralResponse.ErrorItem | GeneralResponse.SuccessItem> {
|
||||
try {
|
||||
await this.InitService()
|
||||
|
||||
let bookTaskDetail = undefined as Book.SelectBookTaskDetail[];
|
||||
let bookTask = undefined as Book.SelectBookTask;
|
||||
|
||||
if (mergeType == MergeType.BOOKTASK) {
|
||||
if (operateBookType == OperateBookType.BOOKTASK) {
|
||||
bookTaskDetail = this.bookTaskDetail.GetBookTaskData({
|
||||
bookTaskId: id
|
||||
}).data
|
||||
bookTask = this.bookTaskService.GetBookTaskDataById(id);
|
||||
} else if (mergeType == MergeType.BOOKTASKDETAIL) {
|
||||
bookTaskDetail = [this.bookTaskDetail.GetBookTaskDetailDataById(id)]
|
||||
// 判断是不是有为空的
|
||||
let emptyName = [] as string[]
|
||||
for (let i = 0; i < bookTaskDetail.length; i++) {
|
||||
const element = bookTaskDetail[i];
|
||||
if (isEmpty(element.gptPrompt)) {
|
||||
emptyName.push(element.name)
|
||||
}
|
||||
}
|
||||
if (emptyName.length > 0) {
|
||||
throw new Error("有空的提示词,请先推理")
|
||||
}
|
||||
} else if (operateBookType == OperateBookType.BOOKTASKDETAIL) {
|
||||
let tempBookTaskDetail = await this.bookServiceBasic.GetBookTaskDetailDataById(id);
|
||||
if (isEmpty(tempBookTaskDetail.gptPrompt)) {
|
||||
throw new Error("当前分镜没有推理提示词,请先生成")
|
||||
}
|
||||
bookTaskDetail = [tempBookTaskDetail]
|
||||
bookTask = this.bookTaskService.GetBookTaskDataById(bookTaskDetail[0].bookTaskId);
|
||||
} else {
|
||||
throw new Error("未知的合并类型")
|
||||
@ -371,7 +390,6 @@ export class MJOpt {
|
||||
|
||||
for (let i = 0; i < bookTaskDetail.length; i++) {
|
||||
const element = bookTaskDetail[i];
|
||||
|
||||
let promptStr = '';
|
||||
for (let i = 0; i < promptSort.length; i++) {
|
||||
const element = promptSort[i];
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
import { MergeType } from "../../../define/enum/bookEnum";
|
||||
import { Book } from "../../../model/book";
|
||||
import { GeneralResponse } from "../../../model/generalResponse";
|
||||
import { checkStringValueAddSuffix, errorMessage, successMessage } from "../../Public/generalTools";
|
||||
@ -8,14 +7,19 @@ import { define } from '../../../define/define'
|
||||
import fs from "fs";
|
||||
import { ImageStyleDefine } from "../../../define/iamgeStyleDefine";
|
||||
import { ImageStyle } from "../Book/imageStyle";
|
||||
import { OperateBookType } from "../../../define/enum/bookEnum";
|
||||
import { isEmpty } from "lodash";
|
||||
import { BookServiceBasic } from "../ServiceBasic/bookServiceBasic";
|
||||
const fspromise = fs.promises
|
||||
|
||||
export class SDOpt {
|
||||
bookTaskDetailService: BookTaskDetailService
|
||||
bookTaskService: BookTaskService
|
||||
imageStyle: ImageStyle
|
||||
constructor() {
|
||||
|
||||
bookServiceBasic: BookServiceBasic
|
||||
constructor() {
|
||||
this.bookServiceBasic = new BookServiceBasic()
|
||||
}
|
||||
|
||||
|
||||
@ -34,22 +38,37 @@ export class SDOpt {
|
||||
/**
|
||||
* SD的提示词合并
|
||||
* @param id 要处理的ID
|
||||
* @param mergeType 合并的类型(用于判断是单个还是批量)
|
||||
* @param operateBookType 合并的类型(用于判断是单个还是批量)
|
||||
* @returns
|
||||
*/
|
||||
async MergePrompt(id: string, mergeType: MergeType): Promise<GeneralResponse.ErrorItem | GeneralResponse.SuccessItem> {
|
||||
async MergePrompt(id: string, operateBookType: OperateBookType): Promise<GeneralResponse.ErrorItem | GeneralResponse.SuccessItem> {
|
||||
try {
|
||||
await this.InitService()
|
||||
let bookTaskDetail = undefined as Book.SelectBookTaskDetail[];
|
||||
let bookTask = undefined as Book.SelectBookTask;
|
||||
|
||||
if (mergeType == MergeType.BOOKTASK) {
|
||||
if (operateBookType == OperateBookType.BOOKTASK) {
|
||||
bookTaskDetail = this.bookTaskDetailService.GetBookTaskData({
|
||||
bookTaskId: id
|
||||
}).data
|
||||
bookTask = this.bookTaskService.GetBookTaskDataById(id);
|
||||
} else if (mergeType == MergeType.BOOKTASKDETAIL) {
|
||||
bookTaskDetail = [this.bookTaskDetailService.GetBookTaskDetailDataById(id)];
|
||||
// 判断是不是有为空的
|
||||
let emptyName = [] as string[]
|
||||
for (let i = 0; i < bookTaskDetail.length; i++) {
|
||||
const element = bookTaskDetail[i];
|
||||
if (isEmpty(element.gptPrompt)) {
|
||||
emptyName.push(element.name)
|
||||
}
|
||||
}
|
||||
if (emptyName.length > 0) {
|
||||
throw new Error("有空的提示词,请先推理")
|
||||
}
|
||||
} else if (operateBookType == OperateBookType.BOOKTASKDETAIL) {
|
||||
let tempBookTaskDetail = await this.bookServiceBasic.GetBookTaskDetailDataById(id);
|
||||
if (isEmpty(tempBookTaskDetail.gptPrompt)) {
|
||||
throw new Error("当前分镜没有推理提示词,请先生成")
|
||||
}
|
||||
bookTaskDetail = [tempBookTaskDetail];
|
||||
bookTask = this.bookTaskService.GetBookTaskDataById(bookTaskDetail[0].bookTaskId);
|
||||
} else {
|
||||
throw new Error("未知的合并类型")
|
||||
|
||||
256
src/main/Service/ServiceBasic/bookServiceBasic.ts
Normal file
256
src/main/Service/ServiceBasic/bookServiceBasic.ts
Normal file
@ -0,0 +1,256 @@
|
||||
import { DEFINE_STRING } from "../../../define/define_string";
|
||||
import { GeneralResponse } from "../../../model/generalResponse";
|
||||
import { BookService } from "../../../define/db/service/Book/bookService";
|
||||
import { Book } from "../../../model/book";
|
||||
import { BookTaskService } from "../../../define/db/service/Book/bookTaskService";
|
||||
import { TaskScheduler } from "../taskScheduler";
|
||||
import { LoggerStatus, OtherData } from "../../../define/enum/softwareEnum";
|
||||
import { BookTaskDetailService } from "../../../define/db/service/Book/bookTaskDetailService";
|
||||
import { BookBackTaskListService } from "../../../define/db/service/Book/bookBackTaskListService";
|
||||
import { BookBackTaskType, BookTaskStatus, TaskExecuteType } from "../../../define/enum/bookEnum";
|
||||
|
||||
/**
|
||||
* 该类中封装了小说的基础服务,主要是检查每次引入对应的服务类
|
||||
* 这边进行一个统一的调用,方便后续的维护
|
||||
*/
|
||||
export class BookServiceBasic {
|
||||
bookService: BookService
|
||||
bookTaskService: BookTaskService;
|
||||
taskScheduler: TaskScheduler
|
||||
bookTaskDetailService: BookTaskDetailService
|
||||
bookBackTaskListService: BookBackTaskListService
|
||||
constructor() {
|
||||
this.taskScheduler = new TaskScheduler()
|
||||
}
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
||||
// 主动返回前端的消息
|
||||
sendReturnMessage(data: GeneralResponse.MessageResponse, message_name = DEFINE_STRING.BOOK.GET_COPYWRITING_RETURN) {
|
||||
global.newWindow[0].win.webContents.send(message_name, data)
|
||||
}
|
||||
|
||||
//#region 小说相关的基础服务
|
||||
|
||||
/**
|
||||
* 通过小说ID获取小说数据
|
||||
* @param bookId 小说ID
|
||||
* @returns
|
||||
*/
|
||||
async GetBookDataById(bookId: string): Promise<Book.SelectBook> {
|
||||
await this.InitService();
|
||||
let book = this.bookService.GetBookDataById(bookId);
|
||||
if (book == null) {
|
||||
let msg = '未找到对应的小说数据,请检查'
|
||||
throw new Error(msg);
|
||||
}
|
||||
return book
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新小说指定ID的数据
|
||||
* @param bookId 小说ID
|
||||
* @param data 小说要更新的数据
|
||||
*/
|
||||
async UpdateBookData(bookId: string, data: Book.SelectBook): Promise<Book.SelectBook> {
|
||||
await this.InitService();
|
||||
let res = this.bookService.UpdateBookData(bookId, data)
|
||||
return res
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除指定的小说数据
|
||||
* @param bookId 需要删除的小说ID
|
||||
*/
|
||||
async DeleteBookData(bookId: string): Promise<void> {
|
||||
await this.InitService();
|
||||
this.bookService.DeleteBookData(bookId)
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region 小说批次任务相关的基础服务
|
||||
|
||||
/**
|
||||
* 通过小说ID获取小说批次任务数据
|
||||
* @param bookTaskId 小说批次任务ID
|
||||
* @returns
|
||||
*/
|
||||
async GetBookTaskDataId(bookTaskId: string): Promise<Book.SelectBookTask> {
|
||||
await this.InitService();
|
||||
let bookTask = this.bookTaskService.GetBookTaskDataById(bookTaskId);
|
||||
if (bookTask == null) {
|
||||
let msg = '未找到对应的小说批次任务数据,请检查';
|
||||
throw new Error(msg);
|
||||
}
|
||||
return bookTask
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过查询条件获取小说批次任务数据
|
||||
* @param bookTaskCondition 小说批次的查询条件
|
||||
*/
|
||||
async GetBookTaskData(bookTaskCondition: Book.QueryBookTaskCondition): Promise<{ bookTasks: Book.SelectBookTask[], total: number }> {
|
||||
await this.InitService();
|
||||
let bookTasks = this.bookTaskService.GetBookTaskData(bookTaskCondition)
|
||||
if (bookTasks.data.bookTasks.length <= 0 || bookTasks.data.total <= 0) {
|
||||
throw new Error("未找到对应的小说批次任务数据,请检查")
|
||||
}
|
||||
return bookTasks.data
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新小说批次任务的数据
|
||||
* @param bookTaskId 小说批次任务ID
|
||||
* @param data
|
||||
*/
|
||||
async UpdetedBookTaskData(bookTaskId: string, data: Book.SelectBookTask): Promise<void> {
|
||||
await this.InitService();
|
||||
this.bookTaskService.UpdetedBookTaskData(bookTaskId, data)
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除指定的小说批次任务的数据
|
||||
* @param bookTaskId 需要删除的指定的小说批次任务ID
|
||||
*/
|
||||
async DeleteBookTaskData(bookTaskId: string): Promise<void> {
|
||||
await this.InitService();
|
||||
this.bookTaskService.DeleteBookTask(bookTaskId)
|
||||
}
|
||||
//#endregion
|
||||
|
||||
//#region 小说批次任务对应的分镜的相关的基础服务
|
||||
|
||||
/**
|
||||
* 获取指定的小说批次任务分镜数据,通过分镜ID
|
||||
* @param bookTaskDetailId 小说批次任务分镜ID
|
||||
* @returns
|
||||
*/
|
||||
async GetBookTaskDetailDataById(bookTaskDetailId: string): Promise<Book.SelectBookTaskDetail> {
|
||||
await this.InitService();
|
||||
let bookTaskDetail = this.bookTaskDetailService.GetBookTaskDetailDataById(bookTaskDetailId)
|
||||
if (bookTaskDetail == null) {
|
||||
let msg = "未找到对应的小说批次任务分镜数据,请检查"
|
||||
global.logger.error('BookServiceBasic_GetBookTaskDetailDataById', msg);
|
||||
throw new Error("未找到对应的小说批次任务分镜数据,请检查")
|
||||
}
|
||||
return bookTaskDetail
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定的小说批次任务分镜数据,通过查询条件
|
||||
* @param condition
|
||||
*/
|
||||
async GetBookTaskDetailData(condition: Book.QueryBookTaskDetailCondition): Promise<Book.SelectBookTaskDetail[]> {
|
||||
await this.InitService();
|
||||
let bookTaskDetails = this.bookTaskDetailService.GetBookTaskData(condition)
|
||||
if (bookTaskDetails.data.length <= 0) {
|
||||
let msg = "未找到对应的小说批次任务分镜数据,请检查";
|
||||
throw new Error(msg)
|
||||
}
|
||||
return bookTaskDetails.data
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改小说的分镜详细数据,通过ID
|
||||
* @param bookTaskDetailId 小说分镜的ID
|
||||
* @param data 要修改的数据,是个对象,会修改全部
|
||||
*/
|
||||
async UpdateBookTaskDetail(bookTaskDetailId: string, data: Book.SelectBookTaskDetail): Promise<Book.SelectBookTaskDetail> {
|
||||
await this.InitService();
|
||||
let res = this.bookTaskDetailService.UpdateBookTaskDetail(bookTaskDetailId, data)
|
||||
return res
|
||||
}
|
||||
|
||||
/**
|
||||
* 更行小说批次的状态
|
||||
* @param bookTaskId 小说批次的ID
|
||||
* @param status 修改后的状态
|
||||
* @param errorMsg 错误消息
|
||||
*/
|
||||
async UpdateBookTaskStatus(bookTaskId: string, status: BookTaskStatus, errorMsg: string | null = null): Promise<void> {
|
||||
await this.InitService();
|
||||
this.bookTaskService.UpdateBookTaskStatus(bookTaskId, status, errorMsg);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 删除指定的小说分镜的反推提示词
|
||||
* @param bookTaskDetail
|
||||
*/
|
||||
async DeleteBookTaskDetailReversePromptById(bookTaskDetailId: string): Promise<void> {
|
||||
await this.InitService();
|
||||
this.bookTaskDetailService.DeleteBookTaskDetailReversePromptById(bookTaskDetailId)
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region 小说后台任务相关操作
|
||||
|
||||
/**
|
||||
* 添加一个后台任务
|
||||
* @param bookId 小说ID
|
||||
* @param taskType 后台任务类型
|
||||
* @param executeType 执行的类型,是不是自动执行
|
||||
* @param bookTaskId 小说批次任务ID
|
||||
* @param bookTaskDetailId 小说批次任务分镜ID
|
||||
*/
|
||||
async AddBookBackTask(bookId: string,
|
||||
taskType: BookBackTaskType,
|
||||
executeType = TaskExecuteType.AUTO,
|
||||
bookTaskId = null,
|
||||
bookTaskDetailId = null): Promise<TaskModal.Task> {
|
||||
|
||||
await this.InitService();
|
||||
let res = this.bookBackTaskListService.AddBookBackTask(bookId, taskType, executeType, bookTaskId, bookTaskDetailId)
|
||||
if (res.code == 0) {
|
||||
throw new Error(res.message)
|
||||
}
|
||||
return res.data as TaskModal.Task
|
||||
}
|
||||
//#endregion
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 通过小说ID和小说批次任务ID获取小说和小说批次任务数据
|
||||
* @param bookId 小说ID
|
||||
* @param bookTaskName 小说批次的名字,或者是小说批次任务的ID
|
||||
* @returns
|
||||
*/
|
||||
async GetBookAndTask(bookId: string, bookTaskName: string) {
|
||||
await this.InitService();
|
||||
let book = this.bookService.GetBookDataById(bookId)
|
||||
if (book == null) {
|
||||
throw new Error("查找小说数据失败");
|
||||
}
|
||||
// 获取小说对应的批次任务数据,默认初始化为第一个
|
||||
let condition = {
|
||||
bookId: bookId
|
||||
} as Book.QueryBookBackTaskCondition
|
||||
if (bookTaskName == "output_00001") {
|
||||
condition["name"] = bookTaskName
|
||||
} else {
|
||||
condition["id"] = bookTaskName
|
||||
}
|
||||
let bookTaskRes = this.bookTaskService.GetBookTaskData(condition)
|
||||
if (bookTaskRes.data.bookTasks.length <= 0 || bookTaskRes.data.total <= 0) {
|
||||
let msg = "没有找到对应的小说批次任务数据"
|
||||
this.taskScheduler.AddLogToDB(bookId, book.type, msg, OtherData.DEFAULT, LoggerStatus.FAIL)
|
||||
throw new Error(msg)
|
||||
}
|
||||
return { book: book as Book.SelectBook, bookTask: bookTaskRes.data.bookTasks[0] as Book.SelectBookTask }
|
||||
}
|
||||
}
|
||||
76
src/main/Service/ServiceBasic/softwareServiceBasic.ts
Normal file
76
src/main/Service/ServiceBasic/softwareServiceBasic.ts
Normal file
@ -0,0 +1,76 @@
|
||||
import { SoftwareService } from '../../../define/db/service/SoftWare/softwareService';
|
||||
|
||||
|
||||
export class SoftWareServiceBasic {
|
||||
softwareService: SoftwareService
|
||||
constructor() { }
|
||||
|
||||
async InitService() {
|
||||
if (!this.softwareService) {
|
||||
this.softwareService = await SoftwareService.getInstance()
|
||||
}
|
||||
}
|
||||
|
||||
//#region software相关的基础服务
|
||||
|
||||
/**
|
||||
* 更新软件配置信息
|
||||
* @param software
|
||||
*/
|
||||
async UpdateSoftware(software: SoftwareSettingModel.SoftwareSetting): Promise<void> {
|
||||
await this.InitService();
|
||||
this.softwareService.UpdateSoftware(software)
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加新的软件配置信息
|
||||
* @param software
|
||||
*/
|
||||
async AddSfotware(software: SoftwareSettingModel.SoftwareSetting): Promise<void> {
|
||||
await this.InitService();
|
||||
this.softwareService.AddSfotware(software);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取软件配置信息
|
||||
* 软件配置信息只有一个,所以直接返回第一个
|
||||
*/
|
||||
async GetSoftwareData(): Promise<SoftwareSettingModel.SoftwareSetting> {
|
||||
await this.InitService();
|
||||
let softwares = this.softwareService.GetSoftwareData();
|
||||
if (softwares.data.length <= 0) {
|
||||
let msg = "未找到软件配置信息,请检查";
|
||||
global.logger.error(
|
||||
'SoftWareServiceBasic_GetSoftwareData',
|
||||
'获取软件的基础设置失败 ,错误信息如下:' + msg
|
||||
)
|
||||
throw new Error(msg)
|
||||
}
|
||||
return softwares.data[0]
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取软件配置指定的属性数据
|
||||
* @param property 属性的名称
|
||||
* @returns
|
||||
*/
|
||||
async GetSoftWarePropertyData(property: string): Promise<string> {
|
||||
await this.InitService();
|
||||
let res = this.softwareService.GetSoftWarePropertyData(property)
|
||||
return res
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 保存软件的指定属性的设置信息,数据一般是字符串
|
||||
* @param property 要保存的属性的名称
|
||||
* @param data 要保存的数据信息
|
||||
*/
|
||||
async SaveSoftwarePropertyData(property: string, data: string): Promise<void> {
|
||||
await this.InitService();
|
||||
this.softwareService.SaveSoftwarePropertyData(property, data)
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
}
|
||||
@ -1,45 +1,47 @@
|
||||
import { isEmpty } from 'lodash'
|
||||
import { BookService } from '../../define/db/service/Book/bookService'
|
||||
import { errorMessage, successMessage } from '../Public/generalTools'
|
||||
import { FfmpegOptions } from './ffmpegOptions'
|
||||
import { SubtitleSavePositionType } from '../../define/enum/waterMarkAndSubtitle'
|
||||
import { BookTaskDetailService } from '../../define/db/service/Book/bookTaskDetailService'
|
||||
import { define } from '../../define/define'
|
||||
import { errorMessage, successMessage } from '../../Public/generalTools'
|
||||
import { FfmpegOptions } from '../ffmpegOptions'
|
||||
import { SubtitleSavePositionType } from '../../../define/enum/waterMarkAndSubtitle'
|
||||
import { define } from '../../../define/define'
|
||||
import path from 'path'
|
||||
import {
|
||||
CheckFileOrDirExist,
|
||||
CheckFolderExistsOrCreate,
|
||||
DeleteFolderAllFile,
|
||||
GetFilesWithExtensions
|
||||
} from '../../define/Tools/file'
|
||||
} from '../../../define/Tools/file'
|
||||
import { shell } from 'electron'
|
||||
import { Book } from '../../model/book'
|
||||
import { Book } from '../../../model/book'
|
||||
import fs from 'fs'
|
||||
import { GeneralResponse } from '../../../model/generalResponse'
|
||||
import { BookServiceBasic } from '../ServiceBasic/bookServiceBasic'
|
||||
import { LoggerStatus, OtherData, ResponseMessageType } from '../../../define/enum/softwareEnum'
|
||||
import { TaskScheduler } from '../taskScheduler'
|
||||
import { SubtitleModel } from '../../../model/subtitle'
|
||||
import { BookTaskStatus, OperateBookType } from '../../../define/enum/bookEnum'
|
||||
import axios from 'axios'
|
||||
import { GptService } from '../GPT/gpt'
|
||||
import FormData from 'form-data'
|
||||
const util = require('util')
|
||||
const { exec } = require('child_process')
|
||||
const execAsync = util.promisify(exec)
|
||||
const fspromises = fs.promises
|
||||
|
||||
|
||||
/**
|
||||
* 去除水印和获取字幕相关操作
|
||||
*/
|
||||
export class Subtitle {
|
||||
bookService: BookService
|
||||
bookTaskDetailService: BookTaskDetailService
|
||||
ffmpegOptions: FfmpegOptions
|
||||
bookServiceBasic: BookServiceBasic
|
||||
taskScheduler: TaskScheduler
|
||||
gptService: GptService
|
||||
|
||||
constructor() { }
|
||||
|
||||
async InitService() {
|
||||
if (!this.bookService) {
|
||||
this.bookService = await BookService.getInstance()
|
||||
}
|
||||
if (!this.bookTaskDetailService) {
|
||||
this.bookTaskDetailService = await BookTaskDetailService.getInstance()
|
||||
}
|
||||
if (!this.ffmpegOptions) {
|
||||
constructor() {
|
||||
this.bookServiceBasic = new BookServiceBasic()
|
||||
this.taskScheduler = new TaskScheduler()
|
||||
this.ffmpegOptions = new FfmpegOptions()
|
||||
}
|
||||
this.gptService = new GptService()
|
||||
}
|
||||
|
||||
//#region 通用方法
|
||||
@ -50,7 +52,7 @@ export class Subtitle {
|
||||
* @param {*} framesPerSecond 每秒截取多少帧
|
||||
* @returns
|
||||
*/
|
||||
GenerateFrameTimes(videoDurationMs, framesPerSecond) {
|
||||
GenerateFrameTimes(videoDurationMs: number, framesPerSecond: number): number[] {
|
||||
// 直接使用视频总时长(毫秒),不进行向下取整
|
||||
const videoDurationSec = videoDurationMs / 1000
|
||||
|
||||
@ -67,117 +69,70 @@ export class Subtitle {
|
||||
let timePoint = Math.min(Math.round(interval * i), videoDurationMs)
|
||||
frameTimes.push(timePoint)
|
||||
}
|
||||
|
||||
return frameTimes
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载指定的的小说相关的所有的数据
|
||||
* @param bookId 小说ID
|
||||
* @param bookTaskId 小说任务ID
|
||||
* @returns
|
||||
*/
|
||||
async GetBookAllData(bookId: string, bookTaskId: string = null): Promise<{ book: Book.SelectBook, bookTask: Book.SelectBookTask, bookTaskDetails: Book.SelectBookTaskDetail[] }> {
|
||||
let { book, bookTask } = await this.bookServiceBasic.GetBookAndTask(bookId, bookTaskId ? bookTaskId : 'output_00001')
|
||||
if (isEmpty(book.subtitlePosition)) {
|
||||
throw new Error("请先设置小说的字幕位置")
|
||||
}
|
||||
// 获取所有的分镜数据
|
||||
let bookTaskDetails = await this.bookServiceBasic.GetBookTaskDetailData({
|
||||
bookId: bookId,
|
||||
bookTaskId: bookTask.id
|
||||
})
|
||||
if (bookTaskDetails.length <= 0) {
|
||||
throw new Error("没有找到对应的分镜数据,请先执行对应的操作")
|
||||
}
|
||||
return { book, bookTask, bookTaskDetails }
|
||||
}
|
||||
|
||||
/**
|
||||
* 通用的小说获取分案的返回方法
|
||||
* @param content 获取的文案内容
|
||||
* @param book 小说实体类
|
||||
* @param bookTask 小说任务实体类
|
||||
* @param bookTaskDetail 小说任务分镜实体类
|
||||
*/
|
||||
async GetSubtitleLoggerAndResponse(content: string, progress: GeneralResponse.ProgressResponse, book: Book.SelectBook, bookTask: Book.SelectBookTask, bookTaskDetail: Book.SelectBookTaskDetail): Promise<void> {
|
||||
// 修改数据
|
||||
await this.bookServiceBasic.UpdateBookTaskDetail(bookTaskDetail.id, {
|
||||
word: content,
|
||||
afterGpt: content
|
||||
});
|
||||
|
||||
// let res = await this.basicReverse.GetCopywritingFunc(book, item);
|
||||
// 将当前的数据实时返回,前端进行修改
|
||||
this.bookServiceBasic.sendReturnMessage({
|
||||
code: 1,
|
||||
id: bookTaskDetail.id,
|
||||
type: ResponseMessageType.GET_TEXT,
|
||||
data: {
|
||||
content: content,
|
||||
progress: progress
|
||||
} as GeneralResponse.SubtitleProgressResponse // 返回识别到的文案
|
||||
})
|
||||
|
||||
// 添加日志
|
||||
await this.taskScheduler.AddLogToDB(
|
||||
book.id,
|
||||
book.type,
|
||||
`${bookTaskDetail.name} 识别文案成功`,
|
||||
bookTask.id,
|
||||
LoggerStatus.SUCCESS
|
||||
)
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
/**
|
||||
* 获取当前视频中所有的字幕信息
|
||||
* @param {*} value 需要的参数的对象,包含下面的参数
|
||||
* @param {*} value.id 小说ID/小说分镜详细信息ID/null
|
||||
* @param {*} value.type 保存的类型(主视频/分镜视频/后续会添加外部单独的视频提取)
|
||||
* @param {*} value.videoPath 视频路径
|
||||
* @param {*} value.subtitlePosition 字幕位置信息
|
||||
*/
|
||||
async GetVideoFrameText(value: Book.GetVideoFrameTextParams) {
|
||||
try {
|
||||
await this.InitService()
|
||||
let videoPath
|
||||
let tempImageFolder
|
||||
let position
|
||||
if (value.type == SubtitleSavePositionType.MAIN_VIDEO) {
|
||||
let bookRes = this.bookService.GetBookDataById(value.id)
|
||||
if (bookRes == null) {
|
||||
throw new Error('没有找到小说对应的的视频地址')
|
||||
}
|
||||
let book = bookRes
|
||||
tempImageFolder = path.join(define.project_path, `${book.id}/data/subtitle/${book.id}/temp`)
|
||||
if (isEmpty(book.subtitlePosition)) {
|
||||
throw new Error('请先保存位置信息')
|
||||
}
|
||||
position = JSON.parse(book.subtitlePosition)
|
||||
videoPath = book.oldVideoPath
|
||||
} else if (value.type == SubtitleSavePositionType.STORYBOARD_VIDEO) {
|
||||
let bookTaskDetail = this.bookTaskDetailService.GetBookTaskDetailDataById(value.id);
|
||||
if (bookTaskDetail == null) {
|
||||
throw new Error("没有找到小说分镜详细信息")
|
||||
}
|
||||
|
||||
tempImageFolder = path.join(define.project_path, `${bookTaskDetail.bookId}/data/subtitle/${bookTaskDetail.name}_${bookTaskDetail.id}/temp`)
|
||||
if (isEmpty(value.subtitlePosition)) {
|
||||
throw new Error('请先保存位置信息')
|
||||
}
|
||||
position = JSON.parse(value.subtitlePosition)
|
||||
videoPath = bookTaskDetail.videoPath
|
||||
} else {
|
||||
throw new Error("不支持的操作");
|
||||
}
|
||||
|
||||
await CheckFolderExistsOrCreate(tempImageFolder)
|
||||
// 判断文件夹是不是存在,存在的话,将里面的所有文件删除
|
||||
await DeleteFolderAllFile(tempImageFolder)
|
||||
|
||||
// 将视频进行抽帧,(目前是每秒1帧,时间小于一秒,抽一帧)
|
||||
let getDurationRes = await this.ffmpegOptions.FfmpegGetVideoDuration(videoPath)
|
||||
if (getDurationRes.code == 0) {
|
||||
throw new Error(getDurationRes.message)
|
||||
}
|
||||
let videoDuration = getDurationRes.data
|
||||
let frameTime = this.GenerateFrameTimes(videoDuration, 1)
|
||||
for (let i = 0; i < frameTime.length; i++) {
|
||||
const item = frameTime[i];
|
||||
let name = i.toString().padStart(6, '0')
|
||||
let imagePath = path.join(tempImageFolder, `frame_${name}.png`)
|
||||
// 开始抽帧
|
||||
let res = await this.ffmpegOptions.FfmpegGetVideoFramdAndClip(
|
||||
videoPath,
|
||||
item,
|
||||
imagePath,
|
||||
position
|
||||
)
|
||||
if (res.code == 0) {
|
||||
throw new Error(res.message)
|
||||
}
|
||||
}
|
||||
|
||||
// 开始识别
|
||||
let textRes = await this.GetCurrentFrameText({
|
||||
id: value.id,
|
||||
type: value.type,
|
||||
imageFolder: tempImageFolder
|
||||
})
|
||||
|
||||
let allTextData = [] as string[]
|
||||
// 开始获取所有的数据
|
||||
let jsonPaths = await GetFilesWithExtensions(tempImageFolder, ['.json'])
|
||||
for (let i = 0; i < jsonPaths.length; i++) {
|
||||
const element = jsonPaths[i]
|
||||
// 开始拼接
|
||||
let texts = JSON.parse(await fspromises.readFile(element, 'utf-8'))
|
||||
for (let j = 0; j < texts.length; j++) {
|
||||
const text = texts[j][1][0]
|
||||
allTextData.includes(text) ? null : allTextData.push(text)
|
||||
}
|
||||
}
|
||||
|
||||
console.log(allTextData.join(','))
|
||||
// 这边计算相似度,返回过于相似的数据
|
||||
// let res = await RemoveSimilarTexts(allTextData)
|
||||
|
||||
return successMessage(
|
||||
allTextData.join(','),
|
||||
'获取视频的的文案信息成功',
|
||||
'WatermarkAndSubtitle_GetVideoFrameText'
|
||||
)
|
||||
} catch (error) {
|
||||
return errorMessage(
|
||||
'提取视频的的文案信息失败,错误消息如下:' + error.toString(),
|
||||
'WatermarkAndSubtitle_GetCurrentFrameText'
|
||||
)
|
||||
}
|
||||
}
|
||||
//#region 获取字幕位置信息相关操作
|
||||
|
||||
/**
|
||||
* 获取当前帧的文字信息
|
||||
@ -185,9 +140,8 @@ export class Subtitle {
|
||||
* @param {*} value.id 小说ID/小说分镜详细信息ID/null
|
||||
* @param {*} value.type 保存的类型(主视频/分镜视频/后续会添加外部单独的视频提取)
|
||||
*/
|
||||
async GetCurrentFrameText(value) {
|
||||
async GetCurrentFrameText(value: { id: any; type?: SubtitleSavePositionType; imageFolder: any }): Promise<GeneralResponse.ErrorItem | GeneralResponse.SuccessItem> {
|
||||
try {
|
||||
await this.InitService()
|
||||
let iamgePaths = []
|
||||
let imageFolder = value.imageFolder
|
||||
? value.imageFolder
|
||||
@ -249,7 +203,7 @@ export class Subtitle {
|
||||
* @param {*} value.id 小说ID/小说分镜详细信息ID/null
|
||||
* @param {*} value.type 保存的类型(主视频/分镜视频/后续会添加外部单独的视频提取)
|
||||
*/
|
||||
async OpenBookSubtitlePositionScreenshot(value) {
|
||||
async OpenBookSubtitlePositionScreenshot(value: { type: SubtitleSavePositionType; id: any }) {
|
||||
try {
|
||||
let folder
|
||||
if (
|
||||
@ -291,9 +245,8 @@ export class Subtitle {
|
||||
* @param {*} value.type 保存的类型(主视频/分镜视频/后续会添加外部单独的视频提取)
|
||||
* @returns
|
||||
*/
|
||||
async SaveBookSubtitlePosition(value) {
|
||||
async SaveBookSubtitlePosition(value: { type: SubtitleSavePositionType; id: string; bookSubtitlePosition: string | any[]; currentTime: number }) {
|
||||
try {
|
||||
await this.InitService()
|
||||
let saveData = []
|
||||
let videoPath
|
||||
let outImagePath
|
||||
@ -307,7 +260,7 @@ export class Subtitle {
|
||||
throw new Error('小说ID不能为空')
|
||||
}
|
||||
// 获取指定的小说
|
||||
let bookRes = this.bookService.GetBookDataById(value.id)
|
||||
let bookRes = await this.bookServiceBasic.GetBookDataById(value.id)
|
||||
if (bookRes == null) {
|
||||
throw new Error('没有找到小说信息')
|
||||
}
|
||||
@ -353,19 +306,16 @@ export class Subtitle {
|
||||
}
|
||||
|
||||
// 数据保存
|
||||
let saveRes = await this.bookService.UpdateBookData(value.id, {
|
||||
let saveRes = await this.bookServiceBasic.UpdateBookData(value.id, {
|
||||
subtitlePosition: JSON.stringify(saveData)
|
||||
})
|
||||
if (saveRes.code == 0) {
|
||||
throw new Error(saveRes.message)
|
||||
}
|
||||
} else if (value.type == SubtitleSavePositionType.STORYBOARD_VIDEO) {
|
||||
// 小说分镜详细信息保存
|
||||
if (value.id == null) {
|
||||
throw new Error('小说分镜详细信息ID不能为空')
|
||||
}
|
||||
// 获取指定的小说分镜详细信息
|
||||
let bookStoryboard = this.bookTaskDetailService.GetBookTaskDetailDataById(value.id)
|
||||
let bookStoryboard = await this.bookServiceBasic.GetBookTaskDetailDataById(value.id)
|
||||
if (bookStoryboard == null) {
|
||||
throw new Error('没有找到小说分镜信息')
|
||||
}
|
||||
@ -414,12 +364,9 @@ export class Subtitle {
|
||||
})
|
||||
}
|
||||
// 数据保存
|
||||
let saveRes = this.bookTaskDetailService.UpdateBookTaskDetail(bookStoryboard.id, {
|
||||
let saveRes = this.bookServiceBasic.UpdateBookTaskDetail(bookStoryboard.id, {
|
||||
subtitlePosition: JSON.stringify(saveData)
|
||||
})
|
||||
if (saveRes.code == 0) {
|
||||
throw new Error(saveRes.message)
|
||||
}
|
||||
}
|
||||
|
||||
// 开始设置裁剪出来的图片位置
|
||||
@ -445,4 +392,272 @@ export class Subtitle {
|
||||
)
|
||||
}
|
||||
}
|
||||
//#endregion
|
||||
|
||||
//#region 本地OCR识别字幕相关操作
|
||||
|
||||
/**
|
||||
* 获取当前视频中所有的字幕信息
|
||||
* @param {*} value 需要的参数的对象,包含下面的参数
|
||||
* @param {*} value.id 小说ID/小说分镜详细信息ID/null
|
||||
* @param {*} value.type 保存的类型(主视频/分镜视频/后续会添加外部单独的视频提取)
|
||||
* @param {*} value.videoPath 视频路径
|
||||
* @param {*} value.subtitlePosition 字幕位置信息
|
||||
*/
|
||||
async GetVideoFrameText(value: Book.GetVideoFrameTextParams): Promise<GeneralResponse.ErrorItem | GeneralResponse.SuccessItem> {
|
||||
try {
|
||||
let videoPath = undefined
|
||||
let tempImageFolder = undefined
|
||||
let position = undefined
|
||||
if (value.type == SubtitleSavePositionType.MAIN_VIDEO) {
|
||||
let bookRes = await this.bookServiceBasic.GetBookDataById(value.id)
|
||||
if (bookRes == null) {
|
||||
throw new Error('没有找到小说对应的的视频地址')
|
||||
}
|
||||
let book = bookRes
|
||||
tempImageFolder = path.join(define.project_path, `${book.id}/data/subtitle/${book.id}/temp`)
|
||||
if (isEmpty(book.subtitlePosition)) {
|
||||
throw new Error('请先保存位置信息')
|
||||
}
|
||||
position = JSON.parse(book.subtitlePosition)
|
||||
videoPath = book.oldVideoPath
|
||||
} else if (value.type == SubtitleSavePositionType.STORYBOARD_VIDEO) {
|
||||
let bookTaskDetail = await this.bookServiceBasic.GetBookTaskDetailDataById(value.id);
|
||||
if (bookTaskDetail == null) {
|
||||
throw new Error("没有找到小说分镜详细信息")
|
||||
}
|
||||
|
||||
tempImageFolder = path.join(define.project_path, `${bookTaskDetail.bookId}/data/subtitle/${bookTaskDetail.name}_${bookTaskDetail.id}/temp`)
|
||||
if (isEmpty(value.subtitlePosition)) {
|
||||
throw new Error('请先保存位置信息')
|
||||
}
|
||||
position = JSON.parse(value.subtitlePosition)
|
||||
videoPath = bookTaskDetail.videoPath
|
||||
} else {
|
||||
throw new Error("不支持的操作");
|
||||
}
|
||||
|
||||
await CheckFolderExistsOrCreate(tempImageFolder)
|
||||
// 判断文件夹是不是存在,存在的话,将里面的所有文件删除
|
||||
await DeleteFolderAllFile(tempImageFolder)
|
||||
|
||||
// 将视频进行抽帧,(目前是每秒1帧,时间小于一秒,抽一帧)
|
||||
let getDurationRes = await this.ffmpegOptions.FfmpegGetVideoDuration(videoPath)
|
||||
if (getDurationRes.code == 0) {
|
||||
throw new Error(getDurationRes.message)
|
||||
}
|
||||
let videoDuration = getDurationRes.data
|
||||
let frameTime = this.GenerateFrameTimes(videoDuration, 1)
|
||||
for (let i = 0; i < frameTime.length; i++) {
|
||||
const item = frameTime[i];
|
||||
let name = i.toString().padStart(6, '0')
|
||||
let imagePath = path.join(tempImageFolder, `frame_${name}.png`)
|
||||
// 开始抽帧
|
||||
let res = await this.ffmpegOptions.FfmpegGetVideoFramdAndClip(
|
||||
videoPath,
|
||||
item,
|
||||
imagePath,
|
||||
position
|
||||
)
|
||||
if (res.code == 0) {
|
||||
throw new Error(res.message)
|
||||
}
|
||||
}
|
||||
|
||||
// 开始识别
|
||||
let textRes = await this.GetCurrentFrameText({
|
||||
id: value.id,
|
||||
type: value.type,
|
||||
imageFolder: tempImageFolder
|
||||
})
|
||||
|
||||
let allTextData = [] as string[]
|
||||
// 开始获取所有的数据
|
||||
let jsonPaths = await GetFilesWithExtensions(tempImageFolder, ['.json'])
|
||||
for (let i = 0; i < jsonPaths.length; i++) {
|
||||
const element = jsonPaths[i]
|
||||
// 开始拼接
|
||||
let texts = JSON.parse(await fspromises.readFile(element, 'utf-8'))
|
||||
for (let j = 0; j < texts.length; j++) {
|
||||
const text = texts[j][1][0]
|
||||
allTextData.includes(text) ? null : allTextData.push(text)
|
||||
}
|
||||
}
|
||||
// 这边计算相似度,返回过于相似的数据
|
||||
// let res = await RemoveSimilarTexts(allTextData)
|
||||
|
||||
return successMessage(
|
||||
allTextData.join(','),
|
||||
'获取视频的的文案信息成功',
|
||||
'WatermarkAndSubtitle_GetVideoFrameText'
|
||||
)
|
||||
} catch (error) {
|
||||
return errorMessage(
|
||||
'提取视频的的文案信息失败,错误消息如下:' + error.toString(),
|
||||
'WatermarkAndSubtitle_GetCurrentFrameText'
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用本地OCR识别字幕文案
|
||||
* @param bookId 小说ID
|
||||
* @param bookTaskId 小说任务ID
|
||||
* @returns
|
||||
*/
|
||||
async GetCopywritingByLocalOcr(book: Book.SelectBook, bookTask: Book.SelectBookTask, bookTaskDetails: Book.SelectBookTaskDetail[]): Promise<GeneralResponse.ErrorItem | GeneralResponse.SuccessItem> {
|
||||
try {
|
||||
for (let i = 0; i < bookTaskDetails.length; i++) {
|
||||
const item = bookTaskDetails[i];
|
||||
let res = await this.GetVideoFrameText({
|
||||
id: item.id,
|
||||
videoPath: item.videoPath,
|
||||
type: SubtitleSavePositionType.STORYBOARD_VIDEO,
|
||||
subtitlePosition: book.subtitlePosition
|
||||
})
|
||||
if (res.code == 0) {
|
||||
throw new Error(res.message)
|
||||
}
|
||||
// 修改数据,并返回
|
||||
await this.GetSubtitleLoggerAndResponse(res.data, {
|
||||
total: bookTaskDetails.length,
|
||||
current: i + 1
|
||||
}, book, bookTask, item)
|
||||
}
|
||||
return successMessage(null, "识别是所有文案成功", "Subtitle_GetCopywriting")
|
||||
} catch (error) {
|
||||
return errorMessage("获取分镜数据失败,失败信息如下:" + error.message, 'Subtitle_GetCopywriting')
|
||||
}
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region Lai_WHISPER识别字幕相关操作
|
||||
|
||||
/**
|
||||
* 单个分离音频的方法
|
||||
* @param book 小说数据
|
||||
* @param bookTask 小说任务数据
|
||||
* @param bookTaskDetail 小说任务详细信息数据
|
||||
* @returns
|
||||
*/
|
||||
async SplitAudio(book: Book.SelectBook, bookTask: Book.SelectBookTask, bookTaskDetail: Book.SelectBookTaskDetail) {
|
||||
// 开始分离音频
|
||||
let videoPath = bookTaskDetail.videoPath
|
||||
let audioPath = path.join(path.dirname(videoPath), bookTaskDetail.name + '.mp3');
|
||||
let audioRes = await this.ffmpegOptions.FfmpegExtractAudio(bookTaskDetail.videoPath, audioPath)
|
||||
if (audioRes.code == 0) {
|
||||
let errorMessage = `分离音频失败,错误信息如下:${audioRes.message}`
|
||||
await this.bookServiceBasic.UpdateBookTaskStatus(
|
||||
bookTask.id,
|
||||
BookTaskStatus.AUDIO_FAIL,
|
||||
errorMessage
|
||||
)
|
||||
throw new Error(audioRes.message)
|
||||
}
|
||||
this.bookServiceBasic.UpdateBookTaskDetail(bookTaskDetail.id, {
|
||||
audioPath: path.relative(define.project_path, audioPath)
|
||||
})
|
||||
|
||||
// 推送成功消息
|
||||
await this.taskScheduler.AddLogToDB(
|
||||
book.id,
|
||||
book.type,
|
||||
`${bookTaskDetail.name}分离音频成功,输出地址:${audioPath}`,
|
||||
OtherData.DEFAULT,
|
||||
LoggerStatus.SUCCESS
|
||||
)
|
||||
// 修改状态为分离音频成功
|
||||
this.bookServiceBasic.UpdateBookTaskStatus(bookTask.id, BookTaskStatus.AUDIO_DONE)
|
||||
return audioPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用LAI Whisper 进行文本识别,然后将繁体转换为简体
|
||||
* @param audioPath 要识别的音频地址
|
||||
* @param subtitleSetting 识别字幕设置
|
||||
*/
|
||||
async LaiWhisperApi(audioPath: string, subtitleSetting: SubtitleModel.subtitleSettingModel): Promise<string> {
|
||||
// 开始调用LAI API识别
|
||||
let formdata = new FormData()
|
||||
formdata.append("file", fs.createReadStream(audioPath)); // 如果是Node.js环境,可以使用fs.createReadStream方法
|
||||
formdata.append("model", "whisper-1");
|
||||
formdata.append("response_format", "srt");
|
||||
formdata.append("temperature", "0");
|
||||
formdata.append("language", "zh");
|
||||
formdata.append("prompt", isEmpty(subtitleSetting.laiWhisper.prompt) ? "eiusmod nulla" : subtitleSetting.laiWhisper.prompt);
|
||||
let url = subtitleSetting.laiWhisper.url
|
||||
if (!url.endsWith('/')) {
|
||||
url = url + '/'
|
||||
}
|
||||
const config = {
|
||||
method: 'post',
|
||||
url: url + 'v1/audio/transcriptions',
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
'Authorization': subtitleSetting.laiWhisper.apiKey,
|
||||
'User-Agent': 'Apifox/1.0.0 (https://apifox.com)',
|
||||
'Content-Type': 'multipart/form-data',
|
||||
...formdata.getHeaders() // 在Node.js环境中需要添加这一行
|
||||
},
|
||||
data: formdata
|
||||
};
|
||||
|
||||
let res = await axios(config)
|
||||
let text = res.data.text;
|
||||
// 但是这边是繁体,需要转化为简体
|
||||
let simpleText = await this.gptService.ChineseTraditionalToSimplified(text, subtitleSetting.laiWhisper.apiKey, url);
|
||||
|
||||
console.log(res.data)
|
||||
return simpleText;
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用LAI Whisper识别字幕
|
||||
* @param bookId 小说ID
|
||||
* @param bookTaskId 小说任务ID
|
||||
* @param subtitleSetting 提取文案相关设置
|
||||
* @returns
|
||||
*/
|
||||
async GetCopywritingByLaiWhisper(book: Book.SelectBook, bookTask: Book.SelectBookTask, bookTaskDetails: Book.SelectBookTaskDetail[], subtitleSetting: SubtitleModel.subtitleSettingModel): Promise<GeneralResponse.ErrorItem | GeneralResponse.SuccessItem> {
|
||||
try {
|
||||
|
||||
let emptyVideoPaths = [] as string[]
|
||||
for (let i = 0; i < bookTaskDetails.length; i++) {
|
||||
const element = bookTaskDetails[i];
|
||||
// 将所有的分镜视频音频分开
|
||||
if (isEmpty(element.videoPath)) {
|
||||
emptyVideoPaths.push(element.name)
|
||||
}
|
||||
}
|
||||
if (emptyVideoPaths.length > 0) {
|
||||
throw new Error(`以下分镜视频没有找到对应的视频路径:${emptyVideoPaths.join(",")} \n 请先计算分镜`)
|
||||
}
|
||||
// 拆分音频和视频
|
||||
for (let i = 0; i < bookTaskDetails.length; i++) {
|
||||
const bookTaskDetail = bookTaskDetails[i];
|
||||
// 开始分离音频
|
||||
let audioPath = await this.SplitAudio(book, bookTask, bookTaskDetail)
|
||||
let fileExist = await CheckFileOrDirExist(audioPath)
|
||||
if (!fileExist) {
|
||||
throw new Error('没有找到对应的音频文件');
|
||||
}
|
||||
// 开始调用LAI API识别
|
||||
let content = await this.LaiWhisperApi(audioPath, subtitleSetting);
|
||||
// 向前端发送数据
|
||||
await this.GetSubtitleLoggerAndResponse(content, {
|
||||
total: bookTaskDetails.length,
|
||||
current: i + 1
|
||||
}, book, bookTask, bookTaskDetail)
|
||||
}
|
||||
return successMessage(
|
||||
null,
|
||||
`所有音频识别成功`,
|
||||
'Subtitle_GetCopywritingByLaiWhisper'
|
||||
)
|
||||
} catch (error) {
|
||||
return errorMessage("获取分镜数据失败,失败信息如下:" + error.message, 'Subtitle_GetCopywritingByLaiWhisper')
|
||||
}
|
||||
}
|
||||
//#endregion
|
||||
}
|
||||
227
src/main/Service/Subtitle/subtitleService.ts
Normal file
227
src/main/Service/Subtitle/subtitleService.ts
Normal file
@ -0,0 +1,227 @@
|
||||
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
|
||||
}
|
||||
@ -10,6 +10,7 @@ import { ResponseMessageType } from "../../../define/enum/softwareEnum";
|
||||
import { SoftwareService } from '../../../define/db/service/SoftWare/softwareService'
|
||||
import { isEmpty } from "lodash";
|
||||
import { ValidateJson } from "../../../define/Tools/validate";
|
||||
import { BookServiceBasic } from "../ServiceBasic/bookServiceBasic";
|
||||
|
||||
/**
|
||||
* 翻译实现服务
|
||||
@ -18,8 +19,10 @@ export class TranslateService {
|
||||
translate: Translate
|
||||
bookTaskDetail: BookTaskDetailService;
|
||||
softwareService: SoftwareService
|
||||
bookServiceBasic: BookServiceBasic
|
||||
|
||||
constructor() {
|
||||
this.bookServiceBasic = new BookServiceBasic();
|
||||
}
|
||||
|
||||
async InitService() {
|
||||
@ -107,7 +110,7 @@ export class TranslateService {
|
||||
// 解析
|
||||
let tryParse = ValidateJson(translateSettingString)
|
||||
if (!tryParse) {
|
||||
throw new Error("翻译设置数据解析失败")
|
||||
throw new Error("翻译设置数据解析失败,请重置后重新配置")
|
||||
}
|
||||
translateSetting = JSON.parse(translateSettingString);
|
||||
}
|
||||
@ -175,7 +178,7 @@ export class TranslateService {
|
||||
|
||||
|
||||
/**
|
||||
* 反推翻译提示词处理
|
||||
* 翻译反推提示词处理
|
||||
* @param bookTaskDetailId 对应的分镜ID
|
||||
* @param reversePromptId 对应的反推提示词数据ID
|
||||
* @param to 目标语言
|
||||
@ -200,6 +203,12 @@ export class TranslateService {
|
||||
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("未知的翻译类型");
|
||||
}
|
||||
@ -228,11 +237,15 @@ export class TranslateService {
|
||||
// 写回数据库
|
||||
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: ResponseMessageType.PROMPT_TRANSLATE,
|
||||
type: responseType,
|
||||
data: {
|
||||
progress: i + 1,
|
||||
total: value.length,
|
||||
|
||||
15
src/main/Service/d3.ts
Normal file
15
src/main/Service/d3.ts
Normal file
@ -0,0 +1,15 @@
|
||||
|
||||
export class D3 {
|
||||
constructor() { }
|
||||
|
||||
//#region D3进行画图的基础方法
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region 软件相关的方法
|
||||
|
||||
|
||||
|
||||
//endregion
|
||||
|
||||
}
|
||||
@ -172,7 +172,7 @@ export class FfmpegOptions {
|
||||
* @param {*} outAudioPath 输出音频地址
|
||||
* @returns
|
||||
*/
|
||||
async FfmpegExtractAudio(videoPath, outAudioPath) {
|
||||
async FfmpegExtractAudio(videoPath: string, outAudioPath: string): Promise<GeneralResponse.ErrorItem | GeneralResponse.SuccessItem> {
|
||||
try {
|
||||
// 判断视频地址是不是存在
|
||||
let videoIsExist = await CheckFileOrDirExist(videoPath)
|
||||
@ -205,13 +205,12 @@ export class FfmpegOptions {
|
||||
}
|
||||
|
||||
/**
|
||||
* Ffmpeg提取视频帧(只提取一帧)
|
||||
* 根据point判断提取什么位置的帧
|
||||
* Ffmpeg提取视频指定时间的帧(只提取一帧)
|
||||
* @param {*} frameTime 视频的时间点
|
||||
* @param {*} videoPath 视频地址
|
||||
* @param {*} outFramePath 输出帧地址
|
||||
*/
|
||||
async FfmpegGetFrame(frameTime, videoPath, outFramePath) {
|
||||
async FfmpegGetFrame(frameTime: number, videoPath: string, outFramePath: string): Promise<GeneralResponse.ErrorItem | GeneralResponse.SuccessItem> {
|
||||
try {
|
||||
let videoIsExist = await CheckFileOrDirExist(videoPath)
|
||||
if (videoIsExist == false) {
|
||||
@ -238,7 +237,7 @@ export class FfmpegOptions {
|
||||
.run()
|
||||
})
|
||||
let res_msg = `视频抽帧完成,输出地址:${res}`
|
||||
return successMessage(res, '视频抽帧成功', 'BasicReverse_FfmpegGetFrame')
|
||||
return successMessage(res, res_msg, 'BasicReverse_FfmpegGetFrame')
|
||||
} catch (error) {
|
||||
return errorMessage(error.message, 'BasicReverse_FfmpegGetFrame')
|
||||
}
|
||||
|
||||
@ -124,6 +124,12 @@ export class TaskManager {
|
||||
for (let index = 0; index < tasks.data.length; index++) {
|
||||
const element = tasks.data[index];
|
||||
if (element.type == BookBackTaskType.MJ_IMAGE || element.type == BookBackTaskType.MJ_REVERSE) {
|
||||
// 判断任务数量是不是又修改
|
||||
let taskNumber = global.mjQueue.getConcurrencyLimit();
|
||||
if (taskNumber != this.mjOpt.mjSetting.taskCount) {
|
||||
global.mjQueue.concurrencyLimit = this.mjOpt.mjSetting.taskCount // 重置并发执行的数量
|
||||
}
|
||||
|
||||
if (global.mjQueue.getWaitingQueue() > 10) {
|
||||
console.log('MJ等待中的任务太多,等待中的任务数量:', global.mjQueue.getWaitingQueue());
|
||||
this.spaceTime = 20000;
|
||||
@ -275,11 +281,6 @@ export class TaskManager {
|
||||
* @param task
|
||||
*/
|
||||
async AddImageMJImage(task: TaskModal.Task) {
|
||||
// 判断任务数量是不是又修改
|
||||
let taskNumber = global.mjQueue.getConcurrencyLimit();
|
||||
if (taskNumber != this.mjOpt.mjSetting.taskCount) {
|
||||
global.mjQueue.concurrencyLimit = this.mjOpt.mjSetting.taskCount // 重置并发执行的数量
|
||||
}
|
||||
// 判断是不是MJ的任务
|
||||
let batch = DEFINE_STRING.MJ.MJ_IMAGE;
|
||||
global.mjQueue.enqueue(async () => {
|
||||
|
||||
@ -352,7 +352,11 @@ export class Watermark {
|
||||
bookId: bookTask.bookId
|
||||
}).data as Book.SelectBookTaskDetail[]
|
||||
} else if (operateBookType == OperateBookType.BOOKTASKDETAIL) {
|
||||
bookTask = this.bookTaskService.GetBookTaskDataById(id);
|
||||
let bookTaskDetail = this.bookTaskDetailService.GetBookTaskDetailDataById(id)
|
||||
if (bookTaskDetail == null) {
|
||||
throw new Error("指定的小说任务分镜信息不存在,请检查")
|
||||
}
|
||||
bookTask = this.bookTaskService.GetBookTaskDataById(bookTaskDetail.bookTaskId);
|
||||
if (bookTask == null) {
|
||||
throw new Error('指定的小说任务数据不存在')
|
||||
}
|
||||
@ -360,10 +364,7 @@ export class Watermark {
|
||||
if (book == null) {
|
||||
throw new Error("小说数据不存在,请检查")
|
||||
}
|
||||
let bookTaskDetail = this.bookTaskDetailService.GetBookTaskDetailDataById(id)
|
||||
if (bookTaskDetail == null) {
|
||||
throw new Error("指定的小说任务分镜信息不存在,请检查")
|
||||
}
|
||||
|
||||
bookTaskDetails = [bookTaskDetail];
|
||||
} else {
|
||||
throw new Error("未知的操作类型")
|
||||
@ -451,7 +452,11 @@ export class Watermark {
|
||||
this.taskScheduler.AddLogToDB(book.id, book.type, `${element.name} 去除水印完成`, element.bookTaskId, LoggerStatus.SUCCESS)
|
||||
}
|
||||
// 全部完毕
|
||||
if (operateBookType == OperateBookType.BOOKTASKDETAIL) {
|
||||
return successMessage(bookTaskDetails[0].oldImage + '?t=' + new Date().getTime(), "去除水印完成", "ReverseBook_RemoveWatermark")
|
||||
} else {
|
||||
return successMessage(null, "全部图片去除水印完成", "ReverseBook_RemoveWatermark")
|
||||
}
|
||||
} catch (error) {
|
||||
return errorMessage("去除水印失败,错误信息如下:" + error.message, "ReverseBook_RemoveWatermark")
|
||||
}
|
||||
|
||||
@ -6,11 +6,20 @@ import axios from 'axios'
|
||||
import { ServiceBase } from '../../define/db/service/serviceBase'
|
||||
import { isEmpty } from 'lodash'
|
||||
import { ValidateJson } from '../../define/Tools/validate'
|
||||
import { SyncGptKeyType } from '../../define/enum/softwareEnum'
|
||||
import { GeneralResponse } from '../../model/generalResponse'
|
||||
import { SoftWareServiceBasic } from '../Service/ServiceBasic/softwareServiceBasic'
|
||||
import { SubtitleService } from '../Service/Subtitle/subtitleService'
|
||||
import { SubtitleModel } from '../../model/subtitle'
|
||||
|
||||
export class GptSetting extends ServiceBase {
|
||||
softWareServiceBasic: SoftWareServiceBasic
|
||||
subtitleService: SubtitleService
|
||||
constructor() {
|
||||
super()
|
||||
axios.defaults.baseURL = define.serverUrl
|
||||
this.softWareServiceBasic = new SoftWareServiceBasic();
|
||||
this.subtitleService = new SubtitleService()
|
||||
}
|
||||
|
||||
/**
|
||||
@ -105,4 +114,19 @@ export class GptSetting extends ServiceBase {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 同步GPT的Key到其他的设置中
|
||||
* @param syncTpye
|
||||
*/
|
||||
async SyncGptKey(syncTpye: SyncGptKeyType): Promise<GeneralResponse.SuccessItem | GeneralResponse.ErrorItem> {
|
||||
try {
|
||||
let globalGptKey = global.config.gpt_key;
|
||||
console.log(globalGptKey);
|
||||
return successMessage(globalGptKey, '获取全局配置成功', 'GptSetting_SyncGptKey')
|
||||
} catch (error) {
|
||||
return errorMessage("同步GPT的Key到其他的设置中失败,失败信息如下:" + error.toString(), "GptSetting_SyncGptKey")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
17
src/model/Setting/softwareSetting.d.ts
vendored
Normal file
17
src/model/Setting/softwareSetting.d.ts
vendored
Normal file
@ -0,0 +1,17 @@
|
||||
|
||||
declare namespace SoftwareSettingModel {
|
||||
type SoftwareSetting = {
|
||||
id?: string
|
||||
theme?: SoftwareThemeType
|
||||
reverse_display_show?: boolean
|
||||
reverse_show_book_striped?: boolean
|
||||
reverse_data_table_size?: ComponentSize
|
||||
globalSetting?: string
|
||||
ttsSetting?: string
|
||||
writeSetting?: string
|
||||
aiSetting?: string
|
||||
watermarkSetting?: string
|
||||
translationSetting?: string
|
||||
subtitleSetting?: string
|
||||
}
|
||||
}
|
||||
16
src/model/book.d.ts
vendored
16
src/model/book.d.ts
vendored
@ -30,6 +30,22 @@ declare namespace Book {
|
||||
suffixPrompt?: string | null // 后缀
|
||||
}
|
||||
|
||||
type BookBackTaskList = {
|
||||
id?: string
|
||||
bookId?: string
|
||||
bookTaskId?: string
|
||||
bookTaskDetailId?: string
|
||||
name?: string // 任务名称,小说名+批次名+分镜名
|
||||
type?: BookBackTaskType
|
||||
status?: BookBackTaskStatus
|
||||
errorMessage?: string
|
||||
executeType?: TaskExecuteType // 任务执行类型,手动还是自动
|
||||
createTime?: Date
|
||||
updateTime?: Date
|
||||
startTime?: number
|
||||
endTime?: number
|
||||
}
|
||||
|
||||
type SelectBookTask = {
|
||||
no?: number,
|
||||
id?: string,
|
||||
|
||||
7
src/model/generalResponse.d.ts
vendored
7
src/model/generalResponse.d.ts
vendored
@ -21,6 +21,11 @@ declare namespace GeneralResponse {
|
||||
current: number, // 当前进度
|
||||
}
|
||||
|
||||
type SubtitleProgressResponse = {
|
||||
content: string, // 文案内容
|
||||
progress: ProgressResponse
|
||||
}
|
||||
|
||||
// 主线程主动返回前端的消息类
|
||||
type MessageResponse = {
|
||||
code: number,
|
||||
@ -28,6 +33,6 @@ declare namespace GeneralResponse {
|
||||
type: ResponseMessageType,
|
||||
dialogType?: DialogType = DialogType.MESSAGE,
|
||||
message?: string,
|
||||
data?: MJ.MJResponseToFront | Buffer | string | TranslateModel.TranslateResponseMessageModel | ProgressResponse
|
||||
data?: MJ.MJResponseToFront | Buffer | string | TranslateModel.TranslateResponseMessageModel | ProgressResponse | SubtitleProgressResponse
|
||||
}
|
||||
}
|
||||
|
||||
13
src/model/subtitle.d.ts
vendored
Normal file
13
src/model/subtitle.d.ts
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
import { GetSubtitleType } from "../define/enum/waterMarkAndSubtitle"
|
||||
|
||||
declare namespace SubtitleModel {
|
||||
type subtitleSettingModel = {
|
||||
selectModel: GetSubtitleType,
|
||||
laiWhisper: {
|
||||
url: string,
|
||||
apiKey: string,
|
||||
syncGPTAPIKey: boolean,
|
||||
prompt: string
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -51,7 +51,7 @@ const book = {
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region 一键反推的单个任务
|
||||
//#region 分镜
|
||||
|
||||
// 开始计算分镜数据
|
||||
ComputeStoryboard: async (bookId) =>
|
||||
@ -60,17 +60,57 @@ const book = {
|
||||
// 开始执行分镜,切分视频
|
||||
Framing: async (bookId) => await ipcRenderer.invoke(DEFINE_STRING.BOOK.FRAMING, bookId),
|
||||
|
||||
// 替换指定的分镜的视频的当前帧
|
||||
ReplaceVideoCurrentFrame: async (bookTaskDetailId, currentTime) =>
|
||||
await ipcRenderer.invoke(
|
||||
DEFINE_STRING.BOOK.REPLACE_VIDEO_CURRENT_FRAME,
|
||||
bookTaskDetailId,
|
||||
currentTime
|
||||
),
|
||||
//#endregion
|
||||
|
||||
//#region 文案相关信息
|
||||
|
||||
// 获取文案信息
|
||||
GetCopywriting: async (bookId, bookTaskId) =>
|
||||
await ipcRenderer.invoke(DEFINE_STRING.BOOK.GET_COPYWRITING, bookId, bookTaskId),
|
||||
GetCopywriting: async (bookId, bookTaskId, operateBookType) =>
|
||||
await ipcRenderer.invoke(
|
||||
DEFINE_STRING.BOOK.GET_COPYWRITING,
|
||||
bookId,
|
||||
bookTaskId,
|
||||
operateBookType
|
||||
),
|
||||
|
||||
// 将文案信息导出,方便修改
|
||||
ExportCopywriting: async (bookTaskId) =>
|
||||
await ipcRenderer.invoke(DEFINE_STRING.BOOK.EXPORT_COPYWRITING, bookTaskId),
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region 水印
|
||||
|
||||
// 去除所有水印
|
||||
RemoveWatermark: async (id, operateBookType) =>
|
||||
await ipcRenderer.invoke(DEFINE_STRING.BOOK.REMOVE_WATERMARK, id, operateBookType),
|
||||
|
||||
// 添加单句推理的
|
||||
AddReversePrompt: async (bookTaskDetailIds, type) =>
|
||||
await ipcRenderer.invoke(DEFINE_STRING.BOOK.ADD_REVERSE_PROMPT, bookTaskDetailIds, type),
|
||||
//#endregion
|
||||
|
||||
//#region 提示词
|
||||
|
||||
MergePrompt: async (id, type, operateBookType) =>
|
||||
await ipcRenderer.invoke(DEFINE_STRING.BOOK.MERGE_PROMPT, id, type, operateBookType),
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region 一键反推的单个任务
|
||||
|
||||
// 添加单句反推的
|
||||
AddReversePrompt: async (bookTaskDetailIds, operateBookType, type) =>
|
||||
await ipcRenderer.invoke(
|
||||
DEFINE_STRING.BOOK.ADD_REVERSE_PROMPT,
|
||||
bookTaskDetailIds,
|
||||
operateBookType,
|
||||
type
|
||||
),
|
||||
|
||||
// 将反推的数据指定的位置的提示词写入到GPT提示词中
|
||||
ReversePromptToGptPrompt: async (bookId, bookTaskId, index) =>
|
||||
@ -115,6 +155,18 @@ const book = {
|
||||
OneToFourBookTask: async (bookTaskId) =>
|
||||
await ipcRenderer.invoke(DEFINE_STRING.BOOK.ONE_TO_FOUR_BOOK_TASK, bookTaskId),
|
||||
|
||||
//#region 小说相关
|
||||
|
||||
// 重置小说数据
|
||||
ResetBookData: async (bookId) =>
|
||||
await ipcRenderer.invoke(DEFINE_STRING.BOOK.RESET_BOOK_DATA, bookId),
|
||||
|
||||
// 删除小说数据
|
||||
DeleteBookData: async (bookId) =>
|
||||
await ipcRenderer.invoke(DEFINE_STRING.BOOK.DELETE_BOOK_DATA, bookId),
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region 小说批次任务相关
|
||||
|
||||
// 重置小说批次数据
|
||||
@ -142,7 +194,7 @@ const book = {
|
||||
HDImage: async (id, scale, operateBookType) =>
|
||||
await ipcRenderer.invoke(DEFINE_STRING.BOOK.HD_IMAGE, id, scale, operateBookType),
|
||||
|
||||
// 讲小说视频相关的设置添加到小说任务批次
|
||||
// 将小说视频相关的设置添加到小说任务批次
|
||||
UseBookVideoDataToBookTask: async (id, operateBookType) =>
|
||||
await ipcRenderer.invoke(
|
||||
DEFINE_STRING.BOOK.USE_BOOK_VIDEO_DATA_TO_BOOK_TASK,
|
||||
|
||||
@ -7,7 +7,6 @@ const gpt = {
|
||||
return await ipcRenderer.invoke(DEFINE_STRING.GPT.INIT_SERVER_GPT_OPTIONS)
|
||||
},
|
||||
|
||||
|
||||
//#region GPT 设置相关
|
||||
// 获取软件设置里面的GPT设置
|
||||
GetAISetting: async () => {
|
||||
@ -17,6 +16,11 @@ const gpt = {
|
||||
// 保存软件设置里面的GPT设置
|
||||
SaveAISetting: async (data) => {
|
||||
return await ipcRenderer.invoke(DEFINE_STRING.GPT.SAVE_AI_SETTING, data)
|
||||
},
|
||||
|
||||
// 同步GPT Key 到指定的设置
|
||||
SyncGptKey: async (syncType) => {
|
||||
return await ipcRenderer.invoke(DEFINE_STRING.GPT.SYNC_GPT_KEY, syncType)
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
@ -69,10 +69,6 @@ const mj = {
|
||||
AutoMatchUser: async (value, callback) =>
|
||||
callback(await ipcRenderer.invoke(DEFINE_STRING.MJ.AUTO_MATCH_USER, value)),
|
||||
|
||||
// 合并提示词
|
||||
MergePrompt: async (id, mergeType) =>
|
||||
await ipcRenderer.invoke(DEFINE_STRING.MJ.MJ_MERGE_PROMPT, id, mergeType),
|
||||
|
||||
// 单个出图
|
||||
AddMJGenerateImageTask: async (id, operateBookType) =>
|
||||
await ipcRenderer.invoke(DEFINE_STRING.MJ.ADD_MJ_GENADD_MJ_GENERATE_IMAGE_TASK, id, operateBookType)
|
||||
|
||||
@ -9,9 +9,5 @@ const sd = {
|
||||
// 文生图,单张
|
||||
txt2img: async (value, callback) =>
|
||||
callback(await ipcRenderer.invoke(DEFINE_STRING.SD.TXT2IMG, value)),
|
||||
|
||||
// 合并提示词
|
||||
MergePrompt: async (id, mergeType) =>
|
||||
await ipcRenderer.invoke(DEFINE_STRING.SD.SD_MERGE_PROMPT, id, mergeType)
|
||||
}
|
||||
export { sd }
|
||||
|
||||
@ -11,6 +11,18 @@ const write = {
|
||||
SaveWriteConfig: async (data) =>
|
||||
await ipcRenderer.invoke(DEFINE_STRING.WRITE.SAVE_WRITE_CONFIG, data),
|
||||
|
||||
// 获取当前的识别字幕设置的数据
|
||||
GetSubtitleSetting: async () =>
|
||||
await ipcRenderer.invoke(DEFINE_STRING.WRITE.GET_SUBTITLE_SETTING),
|
||||
|
||||
// 重置识别字幕设置的数据
|
||||
ResetSubtitleSetting: async () =>
|
||||
await ipcRenderer.invoke(DEFINE_STRING.WRITE.RESET_SUBTITLE_SETTING),
|
||||
|
||||
// 保存识别字幕设置的数据
|
||||
SaveSubtitleSetting: async (subtitleSetting) =>
|
||||
await ipcRenderer.invoke(DEFINE_STRING.WRITE.SAVE_SUBTITLE_SETTING, subtitleSetting),
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region AI相关的任务
|
||||
|
||||
@ -1,4 +1,6 @@
|
||||
<template>
|
||||
<n-spin style="z-index: 1000 !important;" :show="softwareStore.spin.spinning">
|
||||
<template #description> {{ softwareStore.spin.tip }} </template>
|
||||
<n-config-provider
|
||||
:hljs="hljs"
|
||||
:theme="softwareStore.softWare.theme == 'dark' ? darkTheme : null"
|
||||
@ -6,14 +8,12 @@
|
||||
<n-message-provider>
|
||||
<n-dialog-provider>
|
||||
<n-notification-provider>
|
||||
<n-spin style="z-index: 1000;" :show="softwareStore.spin.spinning">
|
||||
<RouterView></RouterView>
|
||||
<template #description> {{ softwareStore.spin.tip }} </template>
|
||||
</n-spin>
|
||||
</n-notification-provider>
|
||||
</n-dialog-provider>
|
||||
</n-message-provider>
|
||||
</n-config-provider>
|
||||
</n-spin>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
@ -7,21 +7,13 @@
|
||||
@click="EditBook"
|
||||
>编辑</n-button
|
||||
>
|
||||
<n-button
|
||||
:size="softwareStore.softWare.reverse_data_table_size"
|
||||
strong
|
||||
secondary
|
||||
style="margin-left: 5px"
|
||||
type="info"
|
||||
>任务</n-button
|
||||
>
|
||||
<n-button
|
||||
:size="softwareStore.softWare.reverse_data_table_size"
|
||||
strong
|
||||
secondary
|
||||
style="margin-left: 5px"
|
||||
type="warning"
|
||||
@click="EditBook"
|
||||
@click="ResetBookData"
|
||||
>重置</n-button
|
||||
>
|
||||
<n-button
|
||||
@ -30,33 +22,26 @@
|
||||
strong
|
||||
secondary
|
||||
type="error"
|
||||
@click="DeleteBookData"
|
||||
>删除
|
||||
</n-button>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { ref, onMounted, defineComponent, h, onUnmounted, toRaw, watch } from 'vue'
|
||||
import { useMessage, NButton, useDialog } from 'naive-ui'
|
||||
<script setup>
|
||||
import { ref, h, toRaw } from 'vue'
|
||||
import { useMessage, useDialog, NButton } from 'naive-ui'
|
||||
import { useReverseManageStore } from '../../../../../stores/reverseManage.ts'
|
||||
import { useSoftwareStore } from '../../../../../stores/software'
|
||||
import { BookType } from '../../../../../define/enum/bookEnum'
|
||||
import AddBook from '../Components/AddBook.vue'
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
NButton
|
||||
},
|
||||
|
||||
props: ['book'],
|
||||
setup(props) {
|
||||
let props = defineProps({
|
||||
book: undefined
|
||||
})
|
||||
let message = useMessage()
|
||||
let dialog = useDialog()
|
||||
let book = ref(props.book)
|
||||
let reverseManageStore = useReverseManageStore()
|
||||
let softwareStore = useSoftwareStore()
|
||||
|
||||
onMounted(async () => {})
|
||||
|
||||
/**
|
||||
* 编辑小说数据
|
||||
*/
|
||||
@ -76,12 +61,69 @@ export default defineComponent({
|
||||
maskClosable: false
|
||||
})
|
||||
}
|
||||
return {
|
||||
book,
|
||||
reverseManageStore,
|
||||
softwareStore,
|
||||
EditBook
|
||||
|
||||
async function ResetBookData(e) {
|
||||
e.stopPropagation()
|
||||
message.info('重置小说数据 ' + book.value.id)
|
||||
let da = dialog.warning({
|
||||
title: '重置小说数据警告',
|
||||
content:
|
||||
'是否重置小说数据,重置后数据将恢复至新建状态,所有的批次数据和文件数据都会被删除,请确认是否继续!',
|
||||
positiveText: '确认',
|
||||
negativeText: '取消',
|
||||
onPositiveClick: async () => {
|
||||
da?.destroy()
|
||||
softwareStore.spin.spinning = true
|
||||
softwareStore.spin.tip = '正在重置小说数据。。。'
|
||||
let res = await window.book.ResetBookData(book.value.id)
|
||||
softwareStore.spin.spinning = false
|
||||
if (res.code == 0) {
|
||||
message.error(res.message)
|
||||
return
|
||||
}
|
||||
softwareStore.spin.spinning = true
|
||||
softwareStore.spin.tip = '正在重新加载小说批次任务数据。。。'
|
||||
// 这边修改还是在哪里修改啊// 加载任务列表
|
||||
let bookTaskRes = await reverseManageStore.GetBookTaskDataFromDB({
|
||||
bookId: book.value.id
|
||||
})
|
||||
softwareStore.spin.spinning = false
|
||||
if (bookTaskRes.code == 0) {
|
||||
message.error(bookTaskRes.message)
|
||||
return
|
||||
}
|
||||
message.success('重置小说数据成功')
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
async function DeleteBookData(e) {
|
||||
e.stopPropagation()
|
||||
message.info('删除小说数据 ' + book.value.id)
|
||||
let da = dialog.warning({
|
||||
title: '删除小说数据警告',
|
||||
content:
|
||||
'是否删除小说数据,删除后数据将被永久删除,所有的批次数据和文件数据都会被删除,请确认是否继续!',
|
||||
positiveText: '确认',
|
||||
negativeText: '取消',
|
||||
onPositiveClick: async () => {
|
||||
da?.destroy()
|
||||
debugger
|
||||
softwareStore.spin.spinning = true
|
||||
softwareStore.spin.tip = '正在删除小说数据。。。'
|
||||
let res = await window.book.DeleteBookData(book.value.id)
|
||||
softwareStore.spin.spinning = false
|
||||
if (res.code == 0) {
|
||||
message.error(res.message)
|
||||
return
|
||||
}
|
||||
debugger
|
||||
// 这边直接把数据删除了就行
|
||||
reverseManageStore.bookData = reverseManageStore.bookData.filter(
|
||||
(item) => item.id != book.value.id
|
||||
)
|
||||
message.success('删除小说数据成功')
|
||||
}
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
@ -34,14 +34,11 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { ref, onMounted, defineComponent, h, onUnmounted, toRaw, watch } from 'vue'
|
||||
import { ref, onMounted, defineComponent, h } from 'vue'
|
||||
import { useMessage, NButton, useDialog } from 'naive-ui'
|
||||
import { useReverseManageStore } from '../../../../../stores/reverseManage.ts'
|
||||
import { useSoftwareStore } from '../../../../../stores/software'
|
||||
import { BookType, OperateBookType } from '../../../../../define/enum/bookEnum'
|
||||
import AddBook from '../Components/AddBook.vue'
|
||||
import { DEFINE_STRING } from '../../../../../define/define_string'
|
||||
import { ResponseMessageType } from '../../../../../define/enum/softwareEnum'
|
||||
import ManageBookTaskGenerateInformation from './ManageBookTaskGenerateInformation.vue'
|
||||
|
||||
export default defineComponent({
|
||||
@ -51,9 +48,11 @@ export default defineComponent({
|
||||
|
||||
props: ['bookTask'],
|
||||
setup(props) {
|
||||
debugger
|
||||
let message = useMessage()
|
||||
let dialog = useDialog()
|
||||
let bookTask = ref(props.bookTask)
|
||||
console.log('bookTask', bookTask.value)
|
||||
let reverseManageStore = useReverseManageStore()
|
||||
let softwareStore = useSoftwareStore()
|
||||
|
||||
@ -172,6 +171,7 @@ export default defineComponent({
|
||||
*/
|
||||
async function ClipDraft(e) {
|
||||
e.stopPropagation()
|
||||
debugger
|
||||
dialog.info({
|
||||
closeOnEsc: false,
|
||||
title: '生成草稿前检查',
|
||||
@ -186,7 +186,7 @@ export default defineComponent({
|
||||
*/
|
||||
async function GenerateVideo(e) {
|
||||
e.stopPropagation()
|
||||
alert('合成视频')
|
||||
message.error('该功能暂不可用')
|
||||
}
|
||||
|
||||
return {
|
||||
|
||||
@ -32,6 +32,7 @@ export default defineComponent({
|
||||
props: ['initData', 'index'],
|
||||
setup(props) {
|
||||
let data = ref(props.initData)
|
||||
console.log('subValue', data.value)
|
||||
onMounted(async () => {})
|
||||
let show = computed(() => {
|
||||
debugger
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div style="position: absolute; top: 0">
|
||||
<div>
|
||||
<n-dropdown trigger="hover" :options="reverseOptions" @select="ReverseSelect">
|
||||
<n-button size="tiny" :color="softwareStore.SoftColor.BROWN_YELLOW" @click="AddReversePrompt">
|
||||
单句反推
|
||||
@ -15,7 +15,6 @@
|
||||
英文 2 中文
|
||||
</n-button>
|
||||
</n-dropdown>
|
||||
|
||||
<n-button
|
||||
style="margin-left: 5px"
|
||||
size="tiny"
|
||||
@ -35,6 +34,7 @@ import { useReverseManageStore } from '../../../../../stores/reverseManage'
|
||||
import { TranslateType } from '../../../../../define/enum/translate'
|
||||
import { ContainsChineseOrPunctuation } from '../../../../../define/Tools/common'
|
||||
import ReSelectReversePrompt from '../MJReverse/ReSelectReversePrompt.vue'
|
||||
import { OperateBookType } from '../../../../../define/enum/bookEnum'
|
||||
import { isEmpty } from 'lodash'
|
||||
|
||||
export default defineComponent({
|
||||
@ -53,7 +53,11 @@ export default defineComponent({
|
||||
* 单句反推提示词
|
||||
*/
|
||||
async function AddReversePrompt() {
|
||||
let res = await window.book.AddReversePrompt([data.value.id], null)
|
||||
let res = await window.book.AddReversePrompt(
|
||||
data.value.id,
|
||||
OperateBookType.BOOKTASKDETAIL,
|
||||
null
|
||||
)
|
||||
if (res.code == 0) {
|
||||
message.error(res.message)
|
||||
} else {
|
||||
@ -63,7 +67,8 @@ export default defineComponent({
|
||||
|
||||
async function AddNextReversePrompt() {
|
||||
let res = await window.book.AddReversePrompt(
|
||||
[data.value.id],
|
||||
data.value.id,
|
||||
OperateBookType.UNDERBOOKTASK,
|
||||
reverseManageStore.selectBook.type
|
||||
)
|
||||
if (res.code == 0) {
|
||||
@ -84,6 +89,7 @@ export default defineComponent({
|
||||
if (res.code == 0) {
|
||||
message.error(res.message)
|
||||
} else {
|
||||
reverseManageStore.selectBookTaskDetail[props.index].gptPrompt = undefined
|
||||
reverseManageStore.selectBookTaskDetail[props.index].reversePrompt = undefined
|
||||
message.success('删除反推数据成功')
|
||||
}
|
||||
@ -110,16 +116,16 @@ export default defineComponent({
|
||||
// 处理翻译英文的数据
|
||||
function TranslateOneParams(from, to) {
|
||||
let translateData = []
|
||||
// if (!isEmpty(data.value.prompt)) {
|
||||
// translateData.push({
|
||||
// text: item.prompt,
|
||||
// from: from,
|
||||
// to: to,
|
||||
// type: TranslateType.PROMPT_TRANSLATE,
|
||||
// isSplit: false, // 该参数设置为true时,会将文本按照标点符号分割成多个句子进行翻译
|
||||
// bookTaskDetailId: data.value.id
|
||||
// })
|
||||
// } else {
|
||||
if (!isEmpty(data.value.gptPrompt)) {
|
||||
translateData.push({
|
||||
text: data.value.gptPrompt,
|
||||
from: from,
|
||||
to: to,
|
||||
type: TranslateType.GPT_PROMPT_TRANSLATE,
|
||||
isSplit: false, // 该参数设置为true时,会将文本按照标点符号分割成多个句子进行翻译
|
||||
bookTaskDetailId: data.value.id
|
||||
})
|
||||
} else {
|
||||
let reversePrompt = data.value.reversePrompt
|
||||
if (reversePrompt) {
|
||||
reversePrompt.forEach((item) => {
|
||||
@ -143,7 +149,7 @@ export default defineComponent({
|
||||
translateData.push(temp_obj)
|
||||
})
|
||||
}
|
||||
// }
|
||||
}
|
||||
return translateData
|
||||
}
|
||||
|
||||
@ -152,6 +158,17 @@ export default defineComponent({
|
||||
let translateData = []
|
||||
for (let i = index.value; i < reverseManageStore.selectBookTaskDetail.length; i++) {
|
||||
const element = reverseManageStore.selectBookTaskDetail[i]
|
||||
// 这边判断GPT提示词是不是存在,存在的话翻译GPT提示词
|
||||
if (!isEmpty(data.value.gptPrompt)) {
|
||||
translateData.push({
|
||||
text: element.gptPrompt,
|
||||
from: from,
|
||||
to: to,
|
||||
type: TranslateType.GPT_PROMPT_TRANSLATE,
|
||||
isSplit: false, // 该参数设置为true时,会将文本按照标点符号分割成多个句子进行翻译
|
||||
bookTaskDetailId: element.id
|
||||
})
|
||||
} else {
|
||||
let reversePrompt = element.reversePrompt
|
||||
if (reversePrompt) {
|
||||
reversePrompt.forEach((item) => {
|
||||
@ -175,6 +192,7 @@ export default defineComponent({
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
return translateData
|
||||
}
|
||||
|
||||
|
||||
@ -30,7 +30,7 @@ import { useSoftwareStore } from '../../../../../stores/software'
|
||||
import DynamicTagsSelect from '../../Components/DynamicTagsSelect.vue'
|
||||
import { usePromptStore } from '../../../../../stores/prompt'
|
||||
import { useReverseManageStore } from '../../../../../stores/reverseManage'
|
||||
import { BookImageCategory, BookType, MergeType } from '../../../../../define/enum/bookEnum'
|
||||
import { BookImageCategory, OperateBookType } from '../../../../../define/enum/bookEnum'
|
||||
|
||||
export default defineComponent({
|
||||
components: { NDropdown, NPopover, NIcon, NButton, Construct },
|
||||
@ -110,20 +110,15 @@ export default defineComponent({
|
||||
}
|
||||
}
|
||||
// 开始进行合并,调用不同的合并模式
|
||||
let res = undefined
|
||||
// 合并之前,要确认保存
|
||||
softwareStore.spin.spinning = true
|
||||
softwareStore.spin.tip = '合并命令中。。。'
|
||||
|
||||
if (type == 'mj_merge') {
|
||||
res = await window.mj.MergePrompt(reverseManageStore.selectBookTask.id, MergeType.BOOKTASK)
|
||||
} else if (type == 'sd_merge') {
|
||||
res = await window.sd.MergePrompt(reverseManageStore.selectBookTask.id, MergeType.BOOKTASK)
|
||||
} else {
|
||||
message.error('未知的合并模式')
|
||||
softwareStore.spin.spinning = false
|
||||
return
|
||||
}
|
||||
let res = await window.book.MergePrompt(
|
||||
reverseManageStore.selectBookTask.id,
|
||||
type,
|
||||
OperateBookType.BOOKTASK
|
||||
)
|
||||
|
||||
// 这边对返回的数据进行处理
|
||||
if (res.code == 0) {
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div style="position: absolute; top: 0">
|
||||
<div>
|
||||
<n-dropdown trigger="hover" :options="mergeOptions" @select="MergePrompt">
|
||||
<n-button
|
||||
size="tiny"
|
||||
@ -37,7 +37,6 @@
|
||||
</n-button>
|
||||
</div>
|
||||
<n-input
|
||||
style="margin-top: 5px"
|
||||
type="textarea"
|
||||
size="tiny"
|
||||
@input="InputDebounced"
|
||||
@ -51,7 +50,7 @@ import { ref, onMounted, defineComponent, onUnmounted, toRaw, watch, h } from 'v
|
||||
import { useMessage, useDialog, NInput, NButton, NDropdown } from 'naive-ui'
|
||||
import { useSoftwareStore } from '../../../../../stores/software'
|
||||
import { useReverseManageStore } from '../../../../../stores/reverseManage'
|
||||
import { MergeType, OperateBookType } from '../../../../../define/enum/bookEnum'
|
||||
import { OperateBookType } from '../../../../../define/enum/bookEnum'
|
||||
import { BookImageCategory } from '../../../../../define/enum/bookEnum'
|
||||
import { debounce } from 'lodash'
|
||||
import InputDialogContent from '../../Original/Components/InputDialogContent.vue'
|
||||
@ -81,25 +80,14 @@ export default defineComponent({
|
||||
}
|
||||
}
|
||||
// 开始进行合并,调用不同的合并模式
|
||||
let res = undefined
|
||||
// 合并之前,要确认保存
|
||||
softwareStore.spin.spinning = true
|
||||
softwareStore.spin.tip = '合并命令中。。。'
|
||||
|
||||
if (type == 'mj_merge') {
|
||||
res = await window.mj.MergePrompt(data.value.id, MergeType.BOOKTASKDETAIL)
|
||||
} else if (type == 'sd_merge') {
|
||||
res = await window.sd.MergePrompt(data.value.id, MergeType.BOOKTASKDETAIL)
|
||||
} else {
|
||||
message.error('未知的合并模式')
|
||||
let res = await window.book.MergePrompt(data.value.id, type, OperateBookType.BOOKTASKDETAIL)
|
||||
softwareStore.spin.spinning = false
|
||||
return
|
||||
}
|
||||
|
||||
// 这边对返回的数据进行处理
|
||||
if (res.code == 0) {
|
||||
message.error(res.message)
|
||||
softwareStore.spin.spinning = false
|
||||
return
|
||||
}
|
||||
for (let i = 0; i < res.data.length; i++) {
|
||||
@ -110,7 +98,6 @@ export default defineComponent({
|
||||
reverseManageStore.selectBookTaskDetail[selectBookDetailIndex].prompt = element.prompt
|
||||
}
|
||||
message.success(res.message)
|
||||
softwareStore.spin.spinning = false
|
||||
}
|
||||
|
||||
// 单个出图
|
||||
@ -169,7 +156,7 @@ export default defineComponent({
|
||||
// 处理数据。获取当前的所有的数据
|
||||
let dialogWidth = 400
|
||||
let dialogHeight = 150
|
||||
dialog.create({
|
||||
let da = dialog.create({
|
||||
title: '添加图片链接/本地地址',
|
||||
showIcon: false,
|
||||
closeOnEsc: false,
|
||||
@ -182,9 +169,12 @@ export default defineComponent({
|
||||
style: `width : ${dialogWidth}px; min-height : ${dialogHeight}px`,
|
||||
maskClosable: false,
|
||||
onClose: async () => {
|
||||
da?.destroy()
|
||||
let row_image_url = image_url_ref.value.data
|
||||
alert(row_image_url)
|
||||
softwareStore.spin.spinning = true
|
||||
softwareStore.spin.tip = '正在下载图片/分割图片中。。。'
|
||||
let res = await window.book.DownloadImageUrlAndSplit(data.value.id, row_image_url)
|
||||
softwareStore.spin.spinning = false
|
||||
if (res.code == 0) {
|
||||
message.error(res.message)
|
||||
return
|
||||
@ -192,24 +182,6 @@ export default defineComponent({
|
||||
data.value.outImagePath = res.data.outImagePath
|
||||
data.value.subImagePath = res.data.subImagePath
|
||||
message.success(res.message)
|
||||
|
||||
// 下载指定的图片地址并且分割
|
||||
// await window.mj.DownloadImageUrlAndSplit(
|
||||
// JSON.stringify([toRaw(row.value), row_image_url]),
|
||||
// (value) => {
|
||||
// if (value.code == 0) {
|
||||
// message.error(value.message)
|
||||
// return
|
||||
// }
|
||||
// // 更新当前的数据
|
||||
// row.value.outImagePath = value.data.outImagePath
|
||||
// row.value.subImagePath = value.data.subImagePath
|
||||
// row.value.mj_message = value.data.mj_message ? value.data.mj_message : {}
|
||||
// row.value.mj_message.image_click = value.data.image_click
|
||||
// row.value.mj_message.image_path = value.data.image_path
|
||||
// row.value.mj_message.progress = 100
|
||||
// }
|
||||
// )
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@ -0,0 +1,70 @@
|
||||
<template>
|
||||
<div class="artplayer-app" style="height: 100%">
|
||||
<Artplayer ref="art" id="art-player" @get-instance="getInstance" :option="option" :style="palyarStyle" />
|
||||
<div style="margin-top: 20px; display: flex; justify-content: center">
|
||||
<n-button :loading="loading" type="info" @click="ReplaceFrame"> 替换为当前帧 </n-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
import { useMessage, useDialog, NButton } from 'naive-ui'
|
||||
import Artplayer from '../../Components/Artplayer.vue'
|
||||
import { useReverseManageStore } from '../../../../../stores/reverseManage'
|
||||
import { useSoftwareStore } from '../../../../../stores/software'
|
||||
let message = useMessage()
|
||||
let reverseManageStore = useReverseManageStore()
|
||||
let softwareStore = useSoftwareStore()
|
||||
|
||||
let props = defineProps({
|
||||
videoPath: undefined,
|
||||
type: undefined,
|
||||
id: undefined
|
||||
})
|
||||
let videoPath = ref(props.videoPath)
|
||||
let id = ref(props.id)
|
||||
let art = ref(null)
|
||||
let loading = ref(false)
|
||||
|
||||
function getInstance(art) {}
|
||||
|
||||
async function ReplaceFrame() {
|
||||
let artInstance = art.value.instance
|
||||
message.success(artInstance.currentTime)
|
||||
|
||||
let currentTime = artInstance.currentTime
|
||||
loading.value = true
|
||||
let res = await window.book.ReplaceVideoCurrentFrame(id.value, currentTime * 1000)
|
||||
loading.value = false
|
||||
if (res.code == 0) {
|
||||
message.error(res.message)
|
||||
} else {
|
||||
// 将返回的数据写回
|
||||
let index = reverseManageStore.selectBookTaskDetail.findIndex((item) => item.id == id.value)
|
||||
if (index < 0) {
|
||||
return
|
||||
}
|
||||
reverseManageStore.selectBookTaskDetail[index].oldImage = res.data
|
||||
message.success('替换视频帧成功')
|
||||
}
|
||||
}
|
||||
|
||||
function test() {}
|
||||
let palyarStyle = {
|
||||
width: '600px',
|
||||
height: '600px',
|
||||
margin: '0 auto'
|
||||
}
|
||||
let option = {
|
||||
container: '.artplayer-app',
|
||||
id: id.value,
|
||||
url: videoPath.value,
|
||||
playbackRate: true,
|
||||
aspectRatio: true,
|
||||
mutex: true,
|
||||
setting: true,
|
||||
hotkey: true,
|
||||
autoSize: true,
|
||||
fullscreen: true
|
||||
}
|
||||
</script>
|
||||
@ -134,10 +134,12 @@ export default defineComponent({
|
||||
code.value = code.value + '\n' + value.data
|
||||
return
|
||||
}
|
||||
// 修改进度
|
||||
softwareStore.spin.tip = `正在提取文案,当前进度 ${value.data.progress.current} / ${value.data.progress.total} 。。。`
|
||||
let index = reverseManageStore.selectBookTaskDetail.findIndex((item) => item.id == value.id)
|
||||
if (index >= 0) {
|
||||
reverseManageStore.selectBookTaskDetail[index].word = value.data
|
||||
reverseManageStore.selectBookTaskDetail[index].afterGpt = value.data
|
||||
reverseManageStore.selectBookTaskDetail[index].word = value.data.content
|
||||
reverseManageStore.selectBookTaskDetail[index].afterGpt = value.data.content
|
||||
}
|
||||
})
|
||||
|
||||
@ -251,18 +253,39 @@ export default defineComponent({
|
||||
return
|
||||
}
|
||||
|
||||
let copywriting_res = await window.book.GetCopywriting(reverseManageStore.selectBook.id)
|
||||
let subtitleSettingRes = await window.write.GetSubtitleSetting()
|
||||
if (subtitleSettingRes.code == 0) {
|
||||
message.error(subtitleSettingRes.message)
|
||||
return
|
||||
}
|
||||
|
||||
let da = dialog.warning({
|
||||
title: '开始提取文案提示',
|
||||
content: `即将进行文案提取,当前的文案提取模式为 ${subtitleSettingRes.data.selectModel} ,是否继续?`,
|
||||
positiveText: '继续',
|
||||
negativeText: '取消',
|
||||
onPositiveClick: async () => {
|
||||
da?.destroy()
|
||||
softwareStore.spin.spinning = true
|
||||
softwareStore.spin.tip = `正在使用 ${subtitleSettingRes.data.selectModel} 提取文案,正在准备中。。。`
|
||||
let copywriting_res = await window.book.GetCopywriting(
|
||||
reverseManageStore.selectBook.id,
|
||||
reverseManageStore.selectBookTask.id,
|
||||
OperateBookType.BOOKTASK
|
||||
)
|
||||
softwareStore.spin.spinning = false
|
||||
if (copywriting_res.code == 0) {
|
||||
message.error(copywriting_res.message)
|
||||
return
|
||||
}
|
||||
message.success('获取文案成功')
|
||||
// dialog.success({})
|
||||
message.success('获取全部文案成功')
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 开始去除水印
|
||||
async function RemoveWatermark() {
|
||||
// softwareStore.spin.spinning = true
|
||||
// softwareStore.spin.tip = '正在去除水印中'
|
||||
if (isEmpty(reverseManageStore.selectBookTask.id)) {
|
||||
window.api.showGlobalMessageDialog({
|
||||
code: 0,
|
||||
@ -270,6 +293,15 @@ export default defineComponent({
|
||||
})
|
||||
return
|
||||
}
|
||||
let da = dialog.warning({
|
||||
title: '去除水印提示',
|
||||
content: '即将去除全部水印,是否继续?',
|
||||
positiveText: '继续',
|
||||
negativeText: '取消',
|
||||
onPositiveClick: async () => {
|
||||
da?.destroy()
|
||||
softwareStore.spin.spinning = true
|
||||
softwareStore.spin.tip = '正在去除水印中。。。'
|
||||
let res_frame = await window.book.RemoveWatermark(
|
||||
reverseManageStore.selectBookTask.id,
|
||||
OperateBookType.BOOKTASK
|
||||
@ -277,6 +309,8 @@ export default defineComponent({
|
||||
softwareStore.spin.spinning = false
|
||||
window.api.showGlobalMessageDialog(res_frame)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 获取文案信息设置
|
||||
async function GetCopywritingSetting() {
|
||||
@ -297,6 +331,28 @@ export default defineComponent({
|
||||
})
|
||||
}
|
||||
|
||||
// 导出文案
|
||||
async function ExportCopywriting() {
|
||||
debugger
|
||||
softwareStore.spin.spinning = true
|
||||
softwareStore.spin.tip = '正在导出文案中...'
|
||||
let res = await window.book.ExportCopywriting(reverseManageStore.selectBookTask.id)
|
||||
if (res.code == 0) {
|
||||
message.error(res.message)
|
||||
} else {
|
||||
dialog.success({
|
||||
title: '导出文案成功',
|
||||
content: '导出文案成功,是否打开导出的字幕文件',
|
||||
positiveText: '打开',
|
||||
negativeText: '取消',
|
||||
onPositiveClick: () => {
|
||||
window.system.OpenFile(res.data)
|
||||
}
|
||||
})
|
||||
}
|
||||
softwareStore.spin.spinning = false
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取水印位置
|
||||
*/
|
||||
@ -329,6 +385,9 @@ export default defineComponent({
|
||||
case 'recognizing_setting': // 文案位置设置
|
||||
await GetCopywritingSetting()
|
||||
break
|
||||
case 'export_recognizing': // 导出文案
|
||||
await ExportCopywriting()
|
||||
break
|
||||
case 'watermark_position': // 水印位置设置
|
||||
await GetWatermarkPosition()
|
||||
break
|
||||
@ -376,7 +435,6 @@ export default defineComponent({
|
||||
* @param type 反推类型
|
||||
*/
|
||||
async function ImageReversePrompt(type = undefined) {
|
||||
debugger
|
||||
if (isEmpty(reverseManageStore.selectBookTask.id)) {
|
||||
window.api.showGlobalMessageDialog({
|
||||
code: 0,
|
||||
@ -384,7 +442,14 @@ export default defineComponent({
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
dialog.warning({
|
||||
title: '反推提示',
|
||||
content: `即将进行反推操作,反推方式为 ${
|
||||
type ? type : reverseManageStore.selectBook.type
|
||||
} ,是否继续?`,
|
||||
positiveText: '继续',
|
||||
negativeText: '取消',
|
||||
onPositiveClick: async () => {
|
||||
if (!type) {
|
||||
let bookType = reverseManageStore.selectBook.type
|
||||
type = bookType
|
||||
@ -394,30 +459,27 @@ export default defineComponent({
|
||||
message.error(`该类型 ${bookType} 的小说不支持反推`)
|
||||
return
|
||||
}
|
||||
let reverseIds = []
|
||||
// 开始获取需要反推的数据
|
||||
for (let i = 0; i < reverseManageStore.selectBookTaskDetail.length; i++) {
|
||||
const element = reverseManageStore.selectBookTaskDetail[i]
|
||||
if (!element.reversePrompt || element.reversePrompt.length <= 0) {
|
||||
reverseIds.push(element.id)
|
||||
}
|
||||
}
|
||||
|
||||
// 开始添加
|
||||
let res = await window.book.AddReversePrompt(reverseIds, type)
|
||||
let res = await window.book.AddReversePrompt(
|
||||
reverseManageStore.selectBookTask.id,
|
||||
OperateBookType.BOOKTASK,
|
||||
type
|
||||
)
|
||||
if (res.code == 0) {
|
||||
message.error(res.message)
|
||||
} else {
|
||||
message.success('添加所有反推任务成功')
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除反推的数据
|
||||
*/
|
||||
async function RemoveReverseData(id) {
|
||||
let deleteIds = []
|
||||
|
||||
if (id == undefined) {
|
||||
// 删除全部的
|
||||
for (let i = 0; i < reverseManageStore.selectBookTaskDetail.length; i++) {
|
||||
const element = reverseManageStore.selectBookTaskDetail[i]
|
||||
@ -425,6 +487,9 @@ export default defineComponent({
|
||||
deleteIds.push(element.id)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
deleteIds.push(id)
|
||||
}
|
||||
|
||||
let res = await window.book.RemoveReverseData(deleteIds)
|
||||
if (res.code == 0) {
|
||||
@ -643,6 +708,10 @@ export default defineComponent({
|
||||
label: '提取文案位置',
|
||||
key: 'recognizing_setting'
|
||||
},
|
||||
{
|
||||
label: '导出文案',
|
||||
key: 'export_recognizing'
|
||||
},
|
||||
{
|
||||
label: '停止提取',
|
||||
key: 'stop_recognizing'
|
||||
@ -665,7 +734,8 @@ export default defineComponent({
|
||||
},
|
||||
{
|
||||
label: 'SD反推',
|
||||
key: 'sd_reverse'
|
||||
key: 'sd_reverse',
|
||||
disabled: reverseManageStore.selectBook.type != BookType.SD_REVERSE
|
||||
},
|
||||
{
|
||||
label: '清除反推数据',
|
||||
|
||||
@ -1,28 +1,107 @@
|
||||
<template>
|
||||
<div style="position: absolute; top: 0">
|
||||
<!-- <n-button size="tiny">123</n-button> -->
|
||||
<div style="margin-bottom: 2px">
|
||||
<n-button :color="softwareStore.SoftColor.BROWN_YELLOW" size="tiny" @click="OpenGetVideoFrame"
|
||||
>抽帧</n-button
|
||||
>
|
||||
<n-button
|
||||
style="margin-left: 2px"
|
||||
:color="softwareStore.SoftColor.BROWN_YELLOW"
|
||||
size="tiny"
|
||||
@click="GetVideoSubtitle"
|
||||
>文案</n-button
|
||||
>
|
||||
<n-button
|
||||
style="margin-left: 2px"
|
||||
:color="softwareStore.SoftColor.BROWN_YELLOW"
|
||||
size="tiny"
|
||||
@click="RemoveImageWatermark"
|
||||
>水印</n-button
|
||||
>
|
||||
</div>
|
||||
<n-image v-if="data.oldImage" width="110" :src="data.oldImage"> </n-image>
|
||||
<video v-else-if="data.data.videoPath" width="110" controls>
|
||||
<n-image v-if="data.oldImage" width="112" :src="data.oldImage"> </n-image>
|
||||
<video v-else-if="data.videoPath" width="110" controls>
|
||||
<source :src="data.videoPath" type="video/mp4" />
|
||||
</video>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { ref, onMounted, defineComponent, onUnmounted, toRaw, watch } from 'vue'
|
||||
import { useMessage, NImage, NButton } from 'naive-ui'
|
||||
|
||||
export default defineComponent({
|
||||
components: { NImage, NButton },
|
||||
props: ['initData', 'index'],
|
||||
setup(props) {
|
||||
<script setup>
|
||||
import { ref, onMounted, h } from 'vue'
|
||||
import { useMessage, useDialog, NImage, NButton } from 'naive-ui'
|
||||
import { useSoftwareStore } from '../../../../../stores/software'
|
||||
import { useReverseManageStore } from '../../../../../stores/reverseManage'
|
||||
import { OperateBookType } from '../../../../../define/enum/bookEnum'
|
||||
import GetVideoFrame from './GetVideoFrame.vue'
|
||||
let message = useMessage()
|
||||
let dialog = useDialog()
|
||||
let softwareStore = useSoftwareStore()
|
||||
let reverseManageStore = useReverseManageStore()
|
||||
|
||||
let props = defineProps({
|
||||
initData: undefined,
|
||||
index: undefined
|
||||
})
|
||||
let data = ref(props.initData)
|
||||
let index = ref(props.index)
|
||||
onMounted(async () => {})
|
||||
|
||||
return {
|
||||
data
|
||||
}
|
||||
// 重新对当前视频进行抽帧
|
||||
async function OpenGetVideoFrame() {
|
||||
// 判断的分镜中的视频是否存在
|
||||
if (!data.value.videoPath) {
|
||||
message.error('当前分镜没有视频')
|
||||
return
|
||||
}
|
||||
dialog.create({
|
||||
title: '获取视频的帧',
|
||||
style: 'width : 800px;',
|
||||
content: () =>
|
||||
h(GetVideoFrame, {
|
||||
videoPath: data.value.videoPath,
|
||||
id: data.value.id,
|
||||
type: OperateBookType.BOOKTASKDETAIL
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// 获取当前视频的片段的文案
|
||||
async function GetVideoSubtitle() {
|
||||
// alert(data.value.id)
|
||||
softwareStore.spin.spinning = true
|
||||
softwareStore.spin.tip = `获取分镜 ${data.value.name} 文案中。。。`
|
||||
let res = await window.book.GetCopywriting(
|
||||
reverseManageStore.selectBook.id,
|
||||
data.value.id,
|
||||
OperateBookType.BOOKTASKDETAIL
|
||||
)
|
||||
softwareStore.spin.spinning = false
|
||||
if (res.code == 0) {
|
||||
message.error(res.message)
|
||||
} else {
|
||||
// 修改指定的数据
|
||||
reverseManageStore.selectBookTaskDetail[index.value].afterGpt = res.data
|
||||
reverseManageStore.selectBookTaskDetail[index.value].word = res.data
|
||||
message.success('获取视频文案成功')
|
||||
}
|
||||
}
|
||||
|
||||
// 去除当前分镜文案的水印
|
||||
async function RemoveImageWatermark() {
|
||||
softwareStore.spin.spinning = true
|
||||
softwareStore.spin.tip = `去除分镜 ${data.value.name} 水印中。。。`
|
||||
let res = await window.book.RemoveWatermark(data.value.id, OperateBookType.BOOKTASKDETAIL)
|
||||
softwareStore.spin.spinning = false
|
||||
if (res.code == 0) {
|
||||
message.error(res.message)
|
||||
} else {
|
||||
// 修改指定的数据
|
||||
let index = reverseManageStore.selectBookTaskDetail.findIndex(
|
||||
(item) => item.id == data.value.id
|
||||
)
|
||||
if (index < 0) {
|
||||
return
|
||||
}
|
||||
reverseManageStore.selectBookTaskDetail[index].oldImage = res.data
|
||||
message.success(`${data.value.name} 去除水印成功`)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@ -48,7 +48,7 @@
|
||||
</n-form-item>
|
||||
<n-form-item path="backgroundMusic">
|
||||
<n-button :disabled="type == 'book'" type="info" @click="UseBookVideoDataToBookTask">{{
|
||||
type == 'bookTsk' ? '应用主小说相关数据' : '当前就是用的主小说的数据'
|
||||
type == bookTask ? '应用主小说相关数据' : '当前就是用的主小说的数据'
|
||||
}}</n-button>
|
||||
<n-button type="info" style="margin-left: 10px" @click="SaveVideoData">保存数据</n-button>
|
||||
</n-form-item>
|
||||
@ -73,6 +73,7 @@ export default defineComponent({
|
||||
props: ['bookTask', 'type'],
|
||||
setup(props) {
|
||||
let bookTask = ref(props.bookTask)
|
||||
debugger
|
||||
let type = ref(props.type)
|
||||
let backgroundMusicOptions = ref([])
|
||||
let message = useMessage()
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div style="margin-bottom: 7px">
|
||||
<div>
|
||||
<DatatableGptPromptButton :initData="data" :index="index" />
|
||||
</div>
|
||||
<div v-if="data.gptPrompt">
|
||||
@ -15,11 +15,22 @@
|
||||
>
|
||||
</n-input>
|
||||
</div>
|
||||
<div v-else class="show-gpt-prompt">
|
||||
<div v-else-if="data.reversePrompt && data.reversePrompt.length > 0" class="show-gpt-prompt">
|
||||
<div v-for="(item, index) in showReversePrompt" :key="index" style="margin-bottom: 5px">
|
||||
<n-input v-model:value="item.prompt" type="text" size="tiny"> </n-input>
|
||||
</div>
|
||||
</div>
|
||||
<n-input
|
||||
v-else
|
||||
type="textarea"
|
||||
size="tiny"
|
||||
v-model:value="data.gptPrompt"
|
||||
:autosize="{
|
||||
minRows: 6,
|
||||
maxRows: 6
|
||||
}"
|
||||
@input="InputDebounced"
|
||||
></n-input>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
@ -42,7 +53,7 @@ export default defineComponent({
|
||||
let showReversePrompt = computed(() => {
|
||||
debugger
|
||||
let res = []
|
||||
for (let i = 0; i < data.value.reversePrompt.length; i++) {
|
||||
for (let i = 0; i < data.value.reversePrompt?.length; i++) {
|
||||
let item = data.value.reversePrompt[i]
|
||||
let res_obj = {
|
||||
...item
|
||||
|
||||
@ -106,6 +106,7 @@ export default defineComponent({
|
||||
return h(DatatableHeaderImage)
|
||||
},
|
||||
width: 300,
|
||||
className: 'space-row',
|
||||
render(row, index) {
|
||||
return h(DataTableShowGenerateImage, {
|
||||
image_generate_category: 'mj',
|
||||
@ -126,7 +127,6 @@ export default defineComponent({
|
||||
let div = document.getElementsByClassName('space-row')
|
||||
for (let i = 0; i < div.length; i++) {
|
||||
div[i].style.padding = '0px 5px'
|
||||
div[i].style.position = 'relative'
|
||||
}
|
||||
}, 100)
|
||||
|
||||
|
||||
@ -6,7 +6,7 @@
|
||||
<n-data-table
|
||||
:columns="columns"
|
||||
:size="softwareStore.softWare.reverse_data_table_size"
|
||||
:data="data"
|
||||
:data="reverseManageStore.bookData"
|
||||
:pagination="pagination"
|
||||
:bordered="false"
|
||||
:remote="true"
|
||||
@ -39,7 +39,6 @@ export default defineComponent({
|
||||
let message = useMessage()
|
||||
let softwareStore = useSoftwareStore()
|
||||
let reverseManageStore = useReverseManageStore()
|
||||
let data = computed(() => reverseManageStore.GetBookData())
|
||||
let maxHeight = ref(0)
|
||||
// 分页设置
|
||||
const paginationReactive = (() => {
|
||||
@ -161,7 +160,7 @@ export default defineComponent({
|
||||
{
|
||||
title: '操作',
|
||||
key: 'action',
|
||||
width: 310,
|
||||
width: 240,
|
||||
fixed: 'right',
|
||||
render: (row) => {
|
||||
return h(BookListAction, { book: row })
|
||||
@ -185,7 +184,6 @@ export default defineComponent({
|
||||
return {
|
||||
softwareStore,
|
||||
reverseManageStore,
|
||||
data,
|
||||
pagination,
|
||||
maxHeight,
|
||||
columns: createColumns(),
|
||||
|
||||
@ -66,11 +66,14 @@ export default defineComponent({
|
||||
|
||||
// 执行对应的方法,修改数据
|
||||
switch (value.type) {
|
||||
case ResponseMessageType.MJ_REVERSE:
|
||||
case ResponseMessageType.MJ_REVERSE: // MJ反推的数据返回处理
|
||||
MJReverseResponse(value)
|
||||
break
|
||||
case ResponseMessageType.PROMPT_TRANSLATE:
|
||||
PromptTranslateResponse(value)
|
||||
case ResponseMessageType.REVERSE_PROMPT_TRANSLATE: // 翻译反推提示词的数据处理
|
||||
ReversePromptTranslateResponse(value)
|
||||
break
|
||||
case ResponseMessageType.GPT_PROMPT_TRANSLATE: // GPT提示词翻译返回
|
||||
GptPromptTranslateResponse(value)
|
||||
break
|
||||
case ResponseMessageType.MJ_IMAGE:
|
||||
MJImageResponse(value)
|
||||
@ -111,7 +114,7 @@ export default defineComponent({
|
||||
* prompt: string // 英文数据
|
||||
* promptCN: string // 中文翻译数据(但是也可以存储英文)
|
||||
*/
|
||||
function PromptTranslateResponse(value) {
|
||||
function ReversePromptTranslateResponse(value) {
|
||||
if (!value.data) {
|
||||
return
|
||||
}
|
||||
@ -143,15 +146,32 @@ export default defineComponent({
|
||||
}
|
||||
}
|
||||
|
||||
function GptPromptTranslateResponse(value) {
|
||||
console.log('GptPromptTranslateResponse', value)
|
||||
if (!value.data) {
|
||||
return
|
||||
}
|
||||
let index = reverseManageStore.selectBookTaskDetail.findIndex(
|
||||
(item) => item.id == value.data.bookTaskDetailId
|
||||
)
|
||||
if (index < 0) {
|
||||
return
|
||||
}
|
||||
reverseManageStore.selectBookTaskDetail[index].gptPrompt = value.data.prompt
|
||||
}
|
||||
|
||||
/**
|
||||
* MJ反推返回数据
|
||||
*/
|
||||
function MJReverseResponse(value) {
|
||||
console.log('MJReverseResponse', value)
|
||||
if (value.data && value.data.progress == 100) {
|
||||
debugger
|
||||
let dataIndex = reverseManageStore.selectBookTaskDetail.findIndex(
|
||||
(item) => item.id == value.data.id
|
||||
)
|
||||
if (dataIndex >= 0) {
|
||||
reverseManageStore.selectBookTaskDetail[dataIndex].gptPrompt = undefined
|
||||
reverseManageStore.selectBookTaskDetail[dataIndex].reversePrompt = JSON.parse(
|
||||
value.data.prompt
|
||||
)
|
||||
|
||||
@ -32,7 +32,7 @@
|
||||
|
||||
<script>
|
||||
import { ref, onMounted, defineComponent, onUnmounted, toRaw, watch, h } from 'vue'
|
||||
import { useMessage, useDialog, NButton, NDataTable, NIcon } from 'naive-ui'
|
||||
import { useMessage, useDialog, NButton, NDataTable, NIcon, NWatermark } from 'naive-ui'
|
||||
import { useReverseManageStore } from '../../../../stores/reverseManage'
|
||||
import { useSoftwareStore } from '../../../../stores/software'
|
||||
import { AddSharp } from '@vicons/ionicons5'
|
||||
@ -46,7 +46,8 @@ export default defineComponent({
|
||||
components: {
|
||||
NButton,
|
||||
NDataTable,
|
||||
BookTaskListAction
|
||||
BookTaskListAction,
|
||||
NWatermark
|
||||
},
|
||||
|
||||
setup() {
|
||||
@ -103,6 +104,7 @@ export default defineComponent({
|
||||
width: 215,
|
||||
fixed: 'right',
|
||||
render(row, index) {
|
||||
debugger
|
||||
return h(BookTaskListAction, {
|
||||
bookTask: row
|
||||
})
|
||||
|
||||
@ -89,7 +89,11 @@ export default defineComponent({
|
||||
let message = useMessage()
|
||||
let type = ref(props.type)
|
||||
let row = ref(props.row)
|
||||
let translate_options = ref([{ label: '翻译中文', key: 'chinese' }])
|
||||
let translate_options = ref([
|
||||
{ label: '翻译中文', key: 'chinese' },
|
||||
{ label: '下翻译 英文 2 中文', key: 'down_english_chinese' },
|
||||
{ label: '下翻译 中文 2 英文', key: 'down_chinese_english' }
|
||||
])
|
||||
|
||||
// 监听数据变化
|
||||
watch(
|
||||
@ -142,7 +146,6 @@ export default defineComponent({
|
||||
message.error('当前翻译的数据里面没有中文')
|
||||
}
|
||||
} else if (key == 'chinese') {
|
||||
|
||||
let t = [[toRaw(row.value)], 'en', 'zh', false, false]
|
||||
await window.mj.TranslateReturnNowTask(JSON.stringify(t), (value) => {
|
||||
if (value.code == 0) {
|
||||
@ -151,6 +154,14 @@ export default defineComponent({
|
||||
}
|
||||
message.success('添加翻译任务成功')
|
||||
})
|
||||
} else if (key == 'down_english_chinese') {
|
||||
// 下翻译 英文 到 中文
|
||||
props.func.translateAll(key, props.index)
|
||||
} else if (key == 'down_chinese_english') {
|
||||
// 下翻译 中文 到 英文
|
||||
props.func.translateAll(key, props.index)
|
||||
} else {
|
||||
message.error('未知的翻译类型')
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -81,8 +81,8 @@
|
||||
class="g-image-class generate-image-show"
|
||||
:src="outImagePath ? outImagePath : space_image"
|
||||
fit="cover"
|
||||
width="120"
|
||||
height="120"
|
||||
:width="120"
|
||||
:height="120"
|
||||
lazy
|
||||
/>
|
||||
<n-button
|
||||
@ -107,7 +107,7 @@
|
||||
subImage="true"
|
||||
class="g-image-class"
|
||||
draggable="true"
|
||||
:width="width"
|
||||
:width="56"
|
||||
style="margin: 1px"
|
||||
v-for="(image, index) in images"
|
||||
:key="image.id"
|
||||
|
||||
@ -250,6 +250,7 @@ export default defineComponent({
|
||||
row: row,
|
||||
index: index,
|
||||
func: {
|
||||
translateAll: TranslateAll,
|
||||
nextGptPrompt: NextGptPrompt,
|
||||
singlePrompt: SinglePrompt
|
||||
}
|
||||
@ -848,8 +849,9 @@ export default defineComponent({
|
||||
* 翻译传入的数据
|
||||
* @param {*} key 翻译的类型
|
||||
*/
|
||||
async function TranslateAll(key) {
|
||||
async function TranslateAll(key, index = -1) {
|
||||
// 将中文翻译成英文
|
||||
debugger
|
||||
if (key == 'english') {
|
||||
let tmp_d = []
|
||||
for (let i = 0; i < data.value.length; i++) {
|
||||
@ -877,6 +879,45 @@ export default defineComponent({
|
||||
}
|
||||
message.success('添加翻译任务成功')
|
||||
})
|
||||
} else if (key == 'down_english_chinese') {
|
||||
// 下翻译 英文 到 中文
|
||||
// 获取当前下面的所有的数据,
|
||||
let tempData = []
|
||||
for (let i = 0; i < data.value.length; i++) {
|
||||
const element = cloneDeep(data.value[i])
|
||||
if (index <= i) {
|
||||
tempData.push(element)
|
||||
}
|
||||
}
|
||||
|
||||
let t = [tempData, 'en', 'zh', false, false]
|
||||
await window.mj.TranslateReturnNowTask(JSON.stringify(t), (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message)
|
||||
return
|
||||
}
|
||||
message.success('添加翻译任务成功')
|
||||
})
|
||||
} else if (key == 'down_chinese_english') {
|
||||
// 下翻译 中文 到 英文
|
||||
// 获取当前下面的所有的数据
|
||||
let tempData = []
|
||||
for (let i = 0; i < data.value.length; i++) {
|
||||
const element = cloneDeep(data.value[i])
|
||||
if (index <= i) {
|
||||
tempData.push(element)
|
||||
}
|
||||
}
|
||||
let t = [tempData, 'zh', 'en', false, false]
|
||||
await window.mj.TranslateReturnNowTask(JSON.stringify(t), (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message)
|
||||
return
|
||||
}
|
||||
message.success('添加翻译任务成功')
|
||||
})
|
||||
} else {
|
||||
message.error('未知的翻译类型')
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -74,7 +74,6 @@ export default defineComponent({
|
||||
for (let i = 0; i < data.value.length; i++) {
|
||||
const item = data.value[i]
|
||||
await window.api.GetPromptJson(item.name, (value) => {
|
||||
console.log(value)
|
||||
if (value.code == 0 && promptError) {
|
||||
message.error(value.message)
|
||||
promptError = false
|
||||
|
||||
@ -35,8 +35,8 @@
|
||||
v-model:value="mjSetting.imageScale"
|
||||
></n-select>
|
||||
</n-form-item>
|
||||
<n-form-item label="命令后缀" style="width: 160px; margin-left: 10px" path="image_suffix">
|
||||
<n-input v-model:value="image_suffix" placeholder="请输入后缀命令"></n-input>
|
||||
<n-form-item label="命令后缀" style="width: 160px; margin-left: 10px" path="imageSuffix">
|
||||
<n-input v-model:value="mjSetting.imageSuffix" placeholder="请输入后缀命令"></n-input>
|
||||
</n-form-item>
|
||||
<n-form-item label="生图任务量" style="width: 100px; margin-left: 10px" path="taskCount">
|
||||
<n-input-number
|
||||
@ -272,7 +272,7 @@ export default defineComponent({
|
||||
let request_model_options = ref([])
|
||||
let mj_api_options = ref([])
|
||||
|
||||
let image_suffix = computed(() => {
|
||||
let computedSuffix = () => {
|
||||
let text = image_model_options.value.findIndex((item) => {
|
||||
return item.value == mjSetting.value.imageModel
|
||||
})
|
||||
@ -285,7 +285,7 @@ export default defineComponent({
|
||||
let dd = ` --${image_model_options.value[text].text} --ar ${image_scale_options.value[sc].text}`
|
||||
mjSetting.value.imageSuffix = dd
|
||||
return dd
|
||||
})
|
||||
}
|
||||
|
||||
// 初始化现在设置的数据
|
||||
async function InitData() {
|
||||
@ -419,7 +419,7 @@ export default defineComponent({
|
||||
message.error('请检查必填字段')
|
||||
return
|
||||
}
|
||||
mjSetting.value.imageSuffix = image_suffix.value
|
||||
// mjSetting.value.imageSuffix = image_suffix.value
|
||||
|
||||
// 通过选择的请求模式判断哪些是要校验的
|
||||
let request_model = mjSetting.value.requestModel
|
||||
@ -500,6 +500,9 @@ export default defineComponent({
|
||||
mjSetting.value.imageModel =
|
||||
image_model_options.value.length > 0 ? image_model_options.value[0].value : null
|
||||
}
|
||||
|
||||
// 刷新命令后缀
|
||||
mjSetting.value.imageSuffix = computedSuffix()
|
||||
}
|
||||
|
||||
/**
|
||||
@ -546,7 +549,6 @@ export default defineComponent({
|
||||
image_scale_options,
|
||||
image_model_options,
|
||||
UpdateSelectRobot,
|
||||
image_suffix,
|
||||
request_model_options,
|
||||
mj_api_options,
|
||||
mj_speed_options,
|
||||
|
||||
@ -1,18 +1,182 @@
|
||||
<template>
|
||||
<div>识别文案设置</div>
|
||||
<div style="display: flex; align-items: center; margin-bottom: 10px; margin-top: 10px">
|
||||
<div>选择识别字幕的方式</div>
|
||||
<n-select
|
||||
style="margin-left: 10px; width: 200px"
|
||||
:options="subtitleOptions"
|
||||
v-model:value="subtitleSetting.selectModel"
|
||||
placeholder="选择识别文案方式"
|
||||
/>
|
||||
<n-button style="margin-left: 10px" :type="urlDisabled ? 'warning' : 'info'" @click="UnBlock">{{
|
||||
urlDisabled ? '解锁' : '上锁'
|
||||
}}</n-button>
|
||||
<n-button style="margin-left: 10px" type="info" @click="ResetSubtitleSetting"
|
||||
>重置数据</n-button
|
||||
>
|
||||
<n-button style="margin-left: 10px" type="info" @click="SaveSubtitleSetting">保存</n-button>
|
||||
</div>
|
||||
<div>
|
||||
<n-card title="本地OCR | 本地Whisper">
|
||||
<span class="url_class" @click="OpenTeach('local_ocr')" style="margin-left: 5px"
|
||||
>本地OCR安装教程</span
|
||||
>
|
||||
<span class="url_class" @click="OpenTeach('local_whisper')" style="margin-left: 25px"
|
||||
>本地Whisper安装教程</span
|
||||
>
|
||||
</n-card>
|
||||
<n-card title="LAI Whisper" style="margin-top: 5px">
|
||||
<n-form
|
||||
ref="formRef"
|
||||
label-placement="left"
|
||||
inline
|
||||
:label-width="auto"
|
||||
:model="formValue"
|
||||
:rules="rules"
|
||||
:size="size"
|
||||
>
|
||||
<n-form-item label="API URL" path="subtitleSetting.laiWhisper.url">
|
||||
<n-input
|
||||
:disabled="urlDisabled"
|
||||
style="width: 220px"
|
||||
v-model:value="subtitleSetting.laiWhisper.url"
|
||||
></n-input>
|
||||
</n-form-item>
|
||||
<n-form-item label="API KEY" path="subtitleSetting.laiWhisper.apiKey">
|
||||
<n-input
|
||||
type="password"
|
||||
style="width: 220px"
|
||||
show-password-on="mousedown"
|
||||
v-model:value="subtitleSetting.laiWhisper.apiKey"
|
||||
></n-input>
|
||||
</n-form-item>
|
||||
<n-form-item label="prompt" path="subtitleSetting.laiWhisper.prompt">
|
||||
<n-input
|
||||
type="text"
|
||||
style="width: 220px"
|
||||
placeholder="请输入识别文案的提示"
|
||||
v-model:value="subtitleSetting.laiWhisper.prompt"
|
||||
></n-input>
|
||||
</n-form-item>
|
||||
<n-form-item path="subtitleSetting.laiWhisper.syncGPTAPIKey">
|
||||
<n-button type="info" @click="SyncGptSetting">同步GPT设置</n-button>
|
||||
</n-form-item>
|
||||
</n-form>
|
||||
</n-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { ref, onMounted, defineComponent, onUnmounted, toRaw, watch } from 'vue'
|
||||
import { useMessage } from 'naive-ui'
|
||||
|
||||
export default defineComponent({
|
||||
components: {},
|
||||
|
||||
setup() {
|
||||
onMounted(async () => {})
|
||||
|
||||
return {}
|
||||
<script setup>
|
||||
import { onMounted, ref, toRaw } from 'vue'
|
||||
import { useMessage, useDialog, NCard, NSelect, NForm, NFormItem, NInput, NButton } from 'naive-ui'
|
||||
import { GetSubtitleType } from '../../../../define/enum/waterMarkAndSubtitle'
|
||||
import { SyncGptKeyType } from '../../../../define/enum/softwareEnum'
|
||||
let message = useMessage()
|
||||
let dialog = useDialog()
|
||||
let urlDisabled = ref(true)
|
||||
let subtitleSetting = ref({
|
||||
selectModel: GetSubtitleType.LAI_WHISPER,
|
||||
laiWhisper: {
|
||||
url: 'https://api.laitool.cc/',
|
||||
apiKey: '你的LAI API KEY',
|
||||
syncGPTAPIKey: false
|
||||
}
|
||||
})
|
||||
|
||||
onMounted(async () => {
|
||||
let res = await window.write.GetSubtitleSetting()
|
||||
if (res.code == 0) {
|
||||
message.error(res.message)
|
||||
} else {
|
||||
subtitleSetting.value = res.data
|
||||
}
|
||||
})
|
||||
|
||||
// 同步GPT设置中的API KEY
|
||||
async function SyncGptSetting() {
|
||||
dialog.warning({
|
||||
title: '同步提示',
|
||||
content:
|
||||
'该功能会将通用设置中的GPT Key直接复制到这边,请确定在GPT中用的是LAI API,不然后续对应的操作可能会出现报错,是否继续操作?',
|
||||
positiveText: '确定',
|
||||
negativeText: '取消',
|
||||
onPositiveClick: async () => {
|
||||
// 确定同步
|
||||
let res = await window.gpt.SyncGptKey(SyncGptKeyType.SUBTITLE_SETTING)
|
||||
if (res.code == 0) {
|
||||
message.error(res.message)
|
||||
} else {
|
||||
subtitleSetting.value.laiWhisper.apiKey = res.data
|
||||
message.success('获取全局GPT Key成功')
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 解锁和上锁
|
||||
function UnBlock() {
|
||||
urlDisabled.value = !urlDisabled.value
|
||||
}
|
||||
|
||||
// 重置识别文案设置
|
||||
async function ResetSubtitleSetting() {
|
||||
dialog.warning({
|
||||
title: '重置识别文案设置',
|
||||
content: '注意,该操作不可逆,是否重置识别文案设置?',
|
||||
positiveText: '确定',
|
||||
negativeText: '取消',
|
||||
onPositiveClick: async () => {
|
||||
let res = await window.write.ResetSubtitleSetting()
|
||||
if (res.code == 0) {
|
||||
message.error(res.message)
|
||||
} else {
|
||||
subtitleSetting.value = res.data
|
||||
message.success('重置字幕设置成功')
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 保存识别文案设置
|
||||
async function SaveSubtitleSetting() {
|
||||
// 这边要检查是不是存在对应的环境,在后进行保存
|
||||
let res = await window.write.SaveSubtitleSetting(toRaw(subtitleSetting.value))
|
||||
if (res.code == 0) {
|
||||
message.error(res.message)
|
||||
} else {
|
||||
message.success('保存提取文案设置成功')
|
||||
}
|
||||
}
|
||||
|
||||
// 打开对应的教程文档
|
||||
function OpenTeach(type) {
|
||||
switch (type) {
|
||||
case 'local_ocr':
|
||||
window.api.OpenUrl(
|
||||
'https://rvgyir5wk1c.feishu.cn/docx/RSj1dIJvpooKCGxHxhgcDhsqnSh?from=from_copylink'
|
||||
)
|
||||
break
|
||||
case 'local_whisper':
|
||||
window.api.OpenUrl(
|
||||
'https://pvwu1oahp5m.feishu.cn/docx/VrBVd2KUDosmNfxat3OceWuInjd?from=from_copylink'
|
||||
)
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
let subtitleOptions = ref([
|
||||
{ label: '本地OCR', value: GetSubtitleType.LOCAL_OCR },
|
||||
{ label: '本地Whisper', value: GetSubtitleType.LOCAL_WHISPER },
|
||||
{ label: 'LAI Whisper', value: GetSubtitleType.LAI_WHISPER }
|
||||
])
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.url_class {
|
||||
color: blue;
|
||||
}
|
||||
|
||||
.url_class:hover {
|
||||
color: brown;
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -39,8 +39,14 @@
|
||||
</n-form-item>
|
||||
<n-form-item label="APP ID/应用ID:">
|
||||
<n-input
|
||||
:type="item.name == 'laitool' ? 'text' : 'password'"
|
||||
:show-password-on="mousedown"
|
||||
v-if="item.name == 'laitool'"
|
||||
type="text"
|
||||
v-model:value="item.translation_app_id"
|
||||
/>
|
||||
<n-input
|
||||
v-else
|
||||
type="password"
|
||||
show-password-on="mousedown"
|
||||
v-model:value="item.translation_app_id"
|
||||
/>
|
||||
</n-form-item>
|
||||
@ -93,6 +99,7 @@ export default defineComponent({
|
||||
return
|
||||
}
|
||||
translateSetting.value = translateSettingRes.data
|
||||
console.log(translateSetting.value)
|
||||
})
|
||||
|
||||
function GetTranslationName(name) {
|
||||
|
||||
@ -448,7 +448,7 @@ export default defineComponent({
|
||||
async function SaveMask() {
|
||||
debugger
|
||||
// 判断当前是不是又蒙板
|
||||
if (canvasHistory.length <= 1) {
|
||||
if (canvasHistory.length <= 0) {
|
||||
message.error('请先选择去水印的区域')
|
||||
return
|
||||
}
|
||||
|
||||
@ -81,6 +81,8 @@ export const useReverseManageStore = defineStore('reverseManage', {
|
||||
// 获取小说任务数据
|
||||
async GetBookTaskDataFromDB(condition) {
|
||||
try {
|
||||
debugger
|
||||
this.bookTaskData = []
|
||||
//@ts-ignore
|
||||
let res = await window.book.GetBookTaskData(condition)
|
||||
if (res.code == 0) {
|
||||
@ -90,7 +92,6 @@ export const useReverseManageStore = defineStore('reverseManage', {
|
||||
this.bookTaskData = res.data.bookTasks
|
||||
this.selectBookTask = res.data.bookTasks[0]
|
||||
} else {
|
||||
this.bookTaskData = []
|
||||
this.selectBookTask = {
|
||||
no: null,
|
||||
id: null,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user