From 5dc75f018ad36457ed4bcd6245d5bc4fae102f8e Mon Sep 17 00:00:00 2001 From: lq1405 <2769838458@qq.com> Date: Wed, 4 Sep 2024 19:49:20 +0800 Subject: [PATCH] LaiTool V3.0.4 --- package-lock.json | 30 +- package.json | 3 +- resources/scripts/db/book.realm.lock | Bin 1416 -> 1416 bytes resources/scripts/db/software.realm | Bin 196608 -> 393216 bytes resources/scripts/db/software.realm.lock | Bin 1416 -> 1416 bytes src/define/Tools/image.ts | 49 ++-- src/define/api/apiUrlDefine.js | 2 +- src/define/db/service/Book/bookService.ts | 3 - src/define/define_string.ts | 3 + src/define/enum/bookEnum.ts | 22 ++ src/define/gptDefine.js | 65 ++++- src/main/IPCEvent/bookIpc.ts | 20 +- src/main/Public/GPT.js | 6 +- src/main/Service/Book/BooKBasic.ts | 29 +- src/main/Service/Book/ReverseBook.ts | 81 +----- src/main/Service/Book/basicReverse.ts | 14 +- src/main/Service/Book/bookFrame.ts | 137 +++++++++ src/main/Service/Book/bookGeneral.ts | 198 +++++++++++++ src/main/Service/Book/bookTask.ts | 270 +++++++++++++++--- src/main/Service/MJ/mj.ts | 16 +- src/main/Service/SD/sd.ts | 45 ++- .../Service/ServiceBasic/bookServiceBasic.ts | 33 +++ src/main/Service/Subtitle/subtitleService.ts | 59 +++- src/main/Service/ffmpegOptions.ts | 2 +- src/main/index.js | 4 +- src/model/book.d.ts | 41 ++- src/preload/{book.js => book.ts} | 21 ++ src/preload/index.js | 2 +- .../src/components/APIService/ApiService.vue | 31 +- .../src/components/Backstep/CopyWriting.vue | 8 +- .../Book/Components/DatatableAfterGpt.vue | 1 - .../Components/DatatableHeaderAfterGpt.vue | 45 +++ ...atatableHeaderAfterGptSelectAndReplace.vue | 116 ++++++++ .../Components/DatatableHeaderGptPrompt.vue | 86 ++++-- .../Book/Components/DatatableHeaderPrompt.vue | 56 +++- .../Components/ManageBook/AddBookTask.vue | 173 +++++++++++ .../Components/ManageBookDetailButton.vue | 38 ++- .../Book/MJReverse/ManageBookReverseTable.vue | 10 +- .../src/components/Book/ManageBookTask.vue | 12 + .../src/components/Book/ReverseManage.vue | 8 - .../Components/BackTask/BackTask.vue | 19 ++ .../Components/BackTask/QueryButton.vue | 57 ++++ .../Components/BackTask/TaskDataTable.vue | 7 + src/renderer/src/components/Home/Home.vue | 56 +++- .../{APIService => Icon}/APIIcon.vue | 0 .../src/components/Icon/BackTaskIcon.vue | 16 ++ .../src/components/Icon/FindReplaceRound.vue | 12 + .../Original/Components/CharacterAnalyze.vue | 2 +- .../Original/Components/ImportWordAndSrt.vue | 2 +- .../src/components/Setting/SDSetting.vue | 4 +- .../Setting/VideoGenerateSetting.vue | 18 +- src/stores/setting.ts | 1 + src/stores/software.ts | 11 + 53 files changed, 1632 insertions(+), 312 deletions(-) create mode 100644 src/main/Service/Book/bookGeneral.ts rename src/preload/{book.js => book.ts} (91%) create mode 100644 src/renderer/src/components/Book/Components/DatatableHeaderAfterGpt.vue create mode 100644 src/renderer/src/components/Book/Components/DatatableHeaderAfterGptSelectAndReplace.vue create mode 100644 src/renderer/src/components/Book/Components/ManageBook/AddBookTask.vue create mode 100644 src/renderer/src/components/Components/BackTask/BackTask.vue create mode 100644 src/renderer/src/components/Components/BackTask/QueryButton.vue create mode 100644 src/renderer/src/components/Components/BackTask/TaskDataTable.vue rename src/renderer/src/components/{APIService => Icon}/APIIcon.vue (100%) create mode 100644 src/renderer/src/components/Icon/BackTaskIcon.vue create mode 100644 src/renderer/src/components/Icon/FindReplaceRound.vue diff --git a/package-lock.json b/package-lock.json index b0a5bf7..26b2b6b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "laitool", - "version": "3.0.3", + "version": "3.0.4", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "laitool", - "version": "3.0.3", + "version": "3.0.4", "hasInstallScript": true, "dependencies": { "@alicloud/alimt20181012": "^1.2.0", @@ -21,7 +21,6 @@ "axios": "^1.6.5", "blob-to-buffer": "^1.2.9", "compressing": "^1.10.0", - "compressorjs": "^1.2.1", "crypto-js": "^4.2.0", "electron-store": "^9.0.0", "electron-updater": "^6.1.7", @@ -3048,11 +3047,6 @@ "bluebird": "^3.5.5" } }, - "node_modules/blueimp-canvas-to-blob": { - "version": "3.29.0", - "resolved": "https://registry.npmmirror.com/blueimp-canvas-to-blob/-/blueimp-canvas-to-blob-3.29.0.tgz", - "integrity": "sha512-0pcSSGxC0QxT+yVkivxIqW0Y4VlO2XSDPofBAqoJ1qJxgH9eiUDLv50Rixij2cDuEfx4M6DpD9UGZpRhT5Q8qg==" - }, "node_modules/boolbase": { "version": "1.0.0", "dev": true, @@ -3669,15 +3663,6 @@ "mkdirp": "bin/cmd.js" } }, - "node_modules/compressorjs": { - "version": "1.2.1", - "resolved": "https://registry.npmmirror.com/compressorjs/-/compressorjs-1.2.1.tgz", - "integrity": "sha512-+geIjeRnPhQ+LLvvA7wxBQE5ddeLU7pJ3FsKFWirDw6veY3s9iLxAQEw7lXGHnhCJvBujEQWuNnGzZcvCvdkLQ==", - "dependencies": { - "blueimp-canvas-to-blob": "^3.29.0", - "is-blob": "^2.1.0" - } - }, "node_modules/concat-map": { "version": "0.0.1", "license": "MIT" @@ -5899,17 +5884,6 @@ "version": "0.3.2", "license": "MIT" }, - "node_modules/is-blob": { - "version": "2.1.0", - "resolved": "https://registry.npmmirror.com/is-blob/-/is-blob-2.1.0.tgz", - "integrity": "sha512-SZ/fTft5eUhQM6oF/ZaASFDEdbFVe89Imltn9uZr03wdKMcWNVYSMjQPFtg05QuNkt5l5c135ElvXEQG0rk4tw==", - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/is-ci": { "version": "3.0.1", "dev": true, diff --git a/package.json b/package.json index 1f1929d..f6280de 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "laitool", - "version": "3.0.3", + "version": "3.0.4", "description": "An AI tool for image processing, video processing, and other functions.", "main": "./out/main/index.js", "author": "laitool.cn", @@ -29,7 +29,6 @@ "axios": "^1.6.5", "blob-to-buffer": "^1.2.9", "compressing": "^1.10.0", - "compressorjs": "^1.2.1", "crypto-js": "^4.2.0", "electron-store": "^9.0.0", "electron-updater": "^6.1.7", diff --git a/resources/scripts/db/book.realm.lock b/resources/scripts/db/book.realm.lock index fdf514abb2f87dfffbb760031acbda6e942d442a..2be42e7b166044669567ca6b5d625ded1360edca 100644 GIT binary patch literal 1416 zcmZQ%qpk3fXo515%yMtWgbN8qp5&!XTseF YQ75oW2dlbK!O;*H4S~@R7%CwE08$DDr~m)} literal 1416 zcmZQ%Ksy0 z?G5;7fmPjZH1i~O7=zU@!L&o^4zzILkhVcn0pYGfvv%r?Arut9giY a010P>GKjtC=3&)5DmWSfqai?22mk;sRvvZ$ diff --git a/resources/scripts/db/software.realm b/resources/scripts/db/software.realm index 4702f0f817f7a27d6e53479ba3a5a845279baf1a..5ec3fa8c09ca14db7754b12028c4c3599a6c3f71 100644 GIT binary patch literal 393216 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%0DuHUC78n7H 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

=h5aG>VBEFzM|B??+|OdMX8~;5@)*; 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

B}f8sr1o9Osb5gpi5u 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 delta 35574 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 O;h?5<-S(j#)&DPIOMICC diff --git a/resources/scripts/db/software.realm.lock b/resources/scripts/db/software.realm.lock index 52188d94b1356099035b151b8b2503d902d3fa6c..cd65c2d855fc5a38afb7286e5e832f4788e4c5d1 100644 GIT binary patch literal 1416 zcmZQ%Rxn!)RfkA`r#~6o7&tt6Z>qKF0NbQBs9&JYb8 WJ`i>2{zcI=${7uT(GVDxApij62ni7Y literal 1416 zcmZQ% { /** * 压缩图片到指定的大小 - * @param file 图片的Blob对象 + * @param base64 图片的Blob对象 * @param maxSizeInBytes 最大文件大小,单位字节 * @returns 返回一个Promise,解析为压缩后的Blob对象 */ -export function CompressImageToSize(base64: string, maxSizeInBytes: number): Promise { - let mimeType = this.getMimeType(base64); - let byteCharacters = atob(base64.split(',')[1]); - let byteNumbers = new Array(byteCharacters.length); - for (let i = 0; i < byteCharacters.length; i++) { - byteNumbers[i] = byteCharacters.charCodeAt(i); - } - let byteArray = new Uint8Array(byteNumbers); - let imageBlob = new Blob([byteArray], { type: mimeType }); +export async function CompressImageToSize(filePath: string, maxSizeInBytes: number): Promise { + let quality = 100; // 初始质量设置 + let outputBuffer; - return new Promise((resolve, reject) => { - const compress = (quality: number) => { - new Compressor(imageBlob as Blob, { - quality, - success(result) { - if (result.size <= maxSizeInBytes || quality <= 0.1) { - resolve(result); - } else { - // 递归降低质量 - compress(quality - 0.1); - } - }, - error(err) { - reject(err); - }, - }); - }; - // 从较高的质量开始 - compress(0.9); - }); + const image = sharp(filePath); + // 输出图片的大小 + const metadata = await image.metadata(); + + // 迭代压缩过程 + while (true) { + outputBuffer = await image.jpeg({ quality }).toBuffer(); + if (outputBuffer.length <= maxSizeInBytes || quality === 20) { + break; + } + quality -= 5; // 每次迭代降低质量 + } + + return outputBuffer; } /** diff --git a/src/define/api/apiUrlDefine.js b/src/define/api/apiUrlDefine.js index f83d6bb..6e8b88b 100644 --- a/src/define/api/apiUrlDefine.js +++ b/src/define/api/apiUrlDefine.js @@ -1,6 +1,6 @@ let apiUrl = [ { - label: 'LAI API', + label: 'LAI API - 香港', value: 'b44c6f24-59e4-4a71-b2c7-3df0c4e35e65', gpt_url: 'https://api.laitool.cc/v1/chat/completions', mj_url: { diff --git a/src/define/db/service/Book/bookService.ts b/src/define/db/service/Book/bookService.ts index 5ab9172..34f016c 100644 --- a/src/define/db/service/Book/bookService.ts +++ b/src/define/db/service/Book/bookService.ts @@ -189,9 +189,6 @@ export class BookService extends BaseRealmService { await CopyFileOrFolder(book.oldVideoPath, oldVideoPath) } - let ffmpegOptions = new FfmpegOptions(); - let res = await ffmpegOptions.FfmpegCompressVideo(oldVideoPath, 800, "2000k") - // 创建对应的文件夹 await CheckFolderExistsOrCreate(bookFolderPath) await CheckFolderExistsOrCreate(imageFolder) diff --git a/src/define/define_string.ts b/src/define/define_string.ts index 440448f..fa27741 100644 --- a/src/define/define_string.ts +++ b/src/define/define_string.ts @@ -233,6 +233,7 @@ export const DEFINE_STRING = { USE_BOOK_VIDEO_DATA_TO_BOOK_TASK: "USE_BOOK_VIDEO_DATA_TO_BOOK_TASK", ADD_JIANYING_DRAFT: "ADD_JIANYING_DRAFT", EXPORT_COPYWRITING: "EXPORT_COPYWRITING", + IMPORT_COPYWRITING: 'IMPORT_COPYWRITING', MERGE_PROMPT: "MERGE_PROMPT", RESET_BOOK_DATA: "RESET_BOOK_DATA", DELETE_BOOK_DATA: "DELETE_BOOK_DATA", @@ -240,6 +241,8 @@ export const DEFINE_STRING = { RESET_GPT_REVERSE_DATA: "RESET_GPT_REVERSE_DATA", REMOVE_MERGE_PROMPT_DATA: "REMOVE_MERGE_PROMPT_DATA", REMOVE_GENERATE_IMAGE: 'REMOVE_GENERATE_IMAGE', + ADD_NEW_BOOK_TASK: "ADD_NEW_BOOK_TASK", + REPLACE_BOOK_DATA: "REPLACE_BOOK_DATA", COMPUTE_STORYBOARD: 'COMPUTE_STORYBOARD', diff --git a/src/define/enum/bookEnum.ts b/src/define/enum/bookEnum.ts index 9b7bf88..c3dba87 100644 --- a/src/define/enum/bookEnum.ts +++ b/src/define/enum/bookEnum.ts @@ -17,6 +17,16 @@ export enum BookImageCategory { D3 = 'd3' } +export enum AddBookTaskCopyData { + AFTER_GPT = 'after_gpt', // 文案 + OLD_IMAGE = 'old_image', // 抽帧/视频 + GPT_PROMPT = 'gpt_prompt', // 反推/GPT提示词 + CHARACTER = 'character', // 角色 + IMAGE_STYLE = 'image_style', // 风格 + PROMPT = 'prompt', // 生图提示词 + IMAGE = 'image', // 生图 +} + export enum MJCategroy { @@ -234,3 +244,15 @@ export enum PromptMergeType { // D3 合并 D3_MERGE = 'd3_merge' } + +/** + * 小说数据替换类型 + */ +export enum BookRepalceDataType { + // 文案 + AFTER_GPT = 'after_gpt', + // GPT提示词 + GPT_PROMPT = 'gpt_prompt', + // 提示词 + PROMPT = 'prompt', +} diff --git a/src/define/gptDefine.js b/src/define/gptDefine.js index 988122f..ab6efe0 100644 --- a/src/define/gptDefine.js +++ b/src/define/gptDefine.js @@ -1,5 +1,5 @@ let fspromises = require('fs').promises -import { cloneDeep, get } from 'lodash' +import { cloneDeep, get, values } from 'lodash' import { define } from './define' const { v4: uuidv4 } = require('uuid') import { apiUrl } from './api/apiUrlDefine' @@ -69,6 +69,61 @@ export const gptDefine = { id: 'a93b693e-bb3f-406d-9730-cba43a6585e4' }, + superSinglePromptChineseSystemContent: { + prompt_name: '超级无敌单帧-中文版', + prompt_roles: `# Role: 小说转漫画提示词大师 + ## Profile + + *Author*: laolu + *Version*: 0.1 + *Language*: 中文 + *Description*: 这个角色会将用户输入的小说文本转化为一个生动的画面描写,最后生成对应的SD提示词。 + + ## Features + + 1. 文本转化为画面描写:创作引人入胜、生动有趣的画面描写,善于创意想象并使用各种形容词,以第三人称视角转化文本为画面描写。 + + ## Rules + + 1. 一个文本就是一副画面,不跳过任何一个句子,不能编造 + 2. 【画面描写】删除人物姓名 + 3. 【画面描写】删除人物对话 + 4. 【画面描写】每一句都要有人物的外形和动作的描写,场景的具体描写,多使用形容词 + + ## Examples + + 用户: + 在那个梦里,我整整学了七年炒饭。 + AI: + 一个身材高大的帅气男人站在梦境之门前,手中紧握炒饭的铲子。身后是一排代表七年的日历页,梦境之门的另一边,隐约可见一个厨房的轮廓。 + + ## Workflow + + 根据文本生成对应的画面描写,直接使用中文数据,不要又过多的说明。 + + ## Initialization + + 作为角色 ,每一次输出都要严格遵守,一步一步思考,按顺序执行 ,使用默认 ,下面是小说文本:`, + prompt_example: [ + { + user_content: '上研究生后。发现导师竟然是曾经网恋的前男友。', + assistant_content: + '一位女士走进一间宽敞、光线充足的研究生实验室,她的目光定格在前方工作台前一位正在辛勤工作的男士身上--他是她的新导师;他身着深色衬衫和熨烫整齐的长裤,身材高大,散发着专业精神和魅力; 温暖的环境光柔和地照亮了他们过去网恋时熟悉的轮廓,皱起的眉头和专注的目光透露出学者坚定不移的奉献精神;熙熙攘攘的研究生和精密的设备融为一体,构成了一幅现代学术画卷,她的内心涌动着甜蜜的怀旧和尴尬的现实这两种复杂的情绪' + }, + { + user_content: '我欲哭无泪', + assistant_content: + '一位女士坐在昏暗的房间角落,双手紧握,眼神空洞,脸上流露出无法言说的悲伤;她的周围是散乱的家具和昏黄的灯光,营造出一种压抑和孤寂的氛围;她的身体微微前倾,仿佛承受着巨大的心理压力,却无法找到宣泄的出口;整个场景透露出一种深深的无奈和心碎,她的表情和姿态都在无声地诉说着“我欲哭无泪”的绝望。' + }, + { + user_content: '被窝里他搂着我的腰亲我,再丢掉我,就把你锁起来。', + assistant_content: + '在昏暗的卧室中,一对情侣亲密地依偎在温暖的被窝里,男子温柔地搂着女子的腰,轻吻她的脸颊,气氛温馨而浪漫;然而,随着话语的转变,男子的动作突然变得愤怒,他用力地推开女子,眼神中闪过一丝威胁;女子的表情由幸福转为惊恐,她紧紧抓住被单,试图保护自己;整个场景充满了紧张和不安,昏暗的灯光和凌乱的床铺加剧了这种氛围,仿佛预示着即将发生的冲突和束缚' + } + ], + id: 'a93b693e-bb3f-406d-9730-cba43a6585e7' + }, + onlyPromptMJSystemContent: { prompt_name: '小说提示词-仅出词', prompt_roles: `# Pico: 小说分镜 @@ -208,6 +263,8 @@ export const gptDefine = { return this.CustomizeGptPrompt(this.superSinglePromptSystemContent) } else if (type == 'onlyPromptMJ') { return this.CustomizeGptPrompt(this.onlyPromptMJSystemContent) + } else if (type == 'superSinglePromptChinese') { + return this.CustomizeGptPrompt(this.superSinglePromptChineseSystemContent) } else { return [] } @@ -233,6 +290,8 @@ export const gptDefine = { return this.replace(this.cartoonFirstPromptSystemContent, replacements) case 'superSinglePrompt': return this.replace(this.superSinglePromptSystemContent, replacements) + case 'superSinglePromptChinese': + return this.replace(this.superSinglePromptChineseSystemContent, replacements) default: throw new Error(`不存在的类型 : ${type}`) } @@ -307,6 +366,10 @@ export const gptDefine = { value: 'superSinglePrompt', label: '超级无敌单帧' }, + { + value: 'superSinglePromptChinese', + label: '超级无敌单帧-中文版' + }, { value: 'onlyPromptMJ', label: '仅出词(不出人物场景-MJ)' diff --git a/src/main/IPCEvent/bookIpc.ts b/src/main/IPCEvent/bookIpc.ts index 5af27bd..9f947be 100644 --- a/src/main/IPCEvent/bookIpc.ts +++ b/src/main/IPCEvent/bookIpc.ts @@ -13,6 +13,7 @@ import { Watermark } from '../Service/watermark' import { SubtitleService } from '../Service/Subtitle/subtitleService' import { BookFrame } from '../Service/Book/bookFrame' import { BookPrompt } from '../Service/Book/bookPrompt' +import { BookGeneral } from '../Service/Book/bookGeneral' let reverseBook = new ReverseBook() let basicReverse = new BasicReverse() let subtitle = new Subtitle() @@ -26,6 +27,7 @@ let watermark = new Watermark() let subtitleService = new SubtitleService() let bookFrame = new BookFrame() let bookPrompt = new BookPrompt(); +let bookGeneral = new BookGeneral() export function BookIpc() { // 获取样式图片的子列表 @@ -89,6 +91,12 @@ export function BookIpc() { //#endregion + //#region 小说通用操作 + + ipcMain.handle(DEFINE_STRING.BOOK.REPLACE_BOOK_DATA, async (event, bookTaskId, replaceData) => await bookGeneral.ReplaceBookData(bookTaskId, replaceData)) + + //#endregion + //#region 分镜相关 // 开始计算分镜 ipcMain.handle( @@ -99,7 +107,7 @@ export function BookIpc() { // 开始执行分镜,切分视频 ipcMain.handle( DEFINE_STRING.BOOK.FRAMING, - async (event, bookId) => await reverseBook.Framing(bookId) + async (event, bookId) => await bookFrame.Framing(bookId) ) // 替换分镜视频的当前帧 @@ -149,6 +157,13 @@ export function BookIpc() { async (event, bookTaskId) => await subtitleService.ExportCopywriting(bookTaskId) ) + // 将文案文件导入到小说中 + ipcMain.handle( + DEFINE_STRING.BOOK.IMPORT_COPYWRITING, + async (event, bookId, bookTaskId, txtPath) => await subtitleService.ImportCopywriting(bookId, bookTaskId, txtPath) + + ) + // 清除导入对齐的文案 ipcMain.handle(DEFINE_STRING.BOOK.CLEAR_IMPORT_WORD, async (event, bookTaskId) => await subtitleService.ClearImportWord(bookTaskId)) @@ -243,6 +258,9 @@ export function BookIpc() { //#region 小说批次任务相关 + // 新建小说批次任务 + ipcMain.handle(DEFINE_STRING.BOOK.ADD_NEW_BOOK_TASK, async (event, addBookTaskData) => await bookTask.AddNewBookTask(addBookTaskData)) + // 重置小说批次数据 ipcMain.handle( DEFINE_STRING.BOOK.RESET_BOOK_TASK, diff --git a/src/main/Public/GPT.js b/src/main/Public/GPT.js index 5717075..812c2ed 100644 --- a/src/main/Public/GPT.js +++ b/src/main/Public/GPT.js @@ -106,7 +106,11 @@ export class GPT { let single_word = element.after_gpt // 判断当前的格式 - if (['superSinglePrompt', 'onlyPromptMJ'].includes(this.global.config.gpt_auto_inference)) { + if ( + ['superSinglePrompt', 'onlyPromptMJ', 'superSinglePromptChinese'].includes( + this.global.config.gpt_auto_inference + ) + ) { // 有返回案例的 message = gptDefine.GetExamplePromptMessage(this.global.config.gpt_auto_inference) // 加当前提问的 diff --git a/src/main/Service/Book/BooKBasic.ts b/src/main/Service/Book/BooKBasic.ts index 8535708..d6034c6 100644 --- a/src/main/Service/Book/BooKBasic.ts +++ b/src/main/Service/Book/BooKBasic.ts @@ -7,6 +7,7 @@ import { GeneralResponse } from '../../../model/generalResponse' import { BookServiceBasic } from '../ServiceBasic/bookServiceBasic' import { BookTask } from './bookTask' import fs from 'fs' +import { Book } from '../../../model/book' export class BookBasic { bookServiceBasic: BookServiceBasic @@ -34,7 +35,7 @@ export class BookBasic { return res } catch (error) { return errorMessage( - '修改数据错误,错误信息如下:' + error.message, + '修改数据错误,错误信息如下:' + error.toString(), 'BookBasic_AddOrModifyBook' ) } @@ -63,6 +64,32 @@ export class BookBasic { //#endregion + //#region 小说批次基础操作 + + /** + * 通过小说ID和小说批次任务ID获取小说和小说批次任务数据 + * @param bookId 小说ID + * @param bookTaskName 小说批次的名字,或者是小说批次任务的ID + * @returns + */ + async GetBookAndTask(bookId: string, bookTaskName: string) { + let book = await this.bookServiceBasic.GetBookDataById(bookId) + // 获取小说对应的批次任务数据,默认初始化为第一个 + let condition = { + bookId: bookId + } as Book.QueryBookBackTaskCondition + if (bookTaskName == "output_00001") { + condition["name"] = bookTaskName + } else { + condition["id"] = bookTaskName + } + let bookTaskRes = await this.bookServiceBasic.GetBookTaskData(condition) + return { book: book as Book.SelectBook, bookTask: bookTaskRes.bookTasks[0] as Book.SelectBookTask } + } + + //#endregion + + //#region 小说相关操作 /** diff --git a/src/main/Service/Book/ReverseBook.ts b/src/main/Service/Book/ReverseBook.ts index b7172a8..a93ce8b 100644 --- a/src/main/Service/Book/ReverseBook.ts +++ b/src/main/Service/Book/ReverseBook.ts @@ -21,6 +21,7 @@ import { BookServiceBasic } from '../ServiceBasic/bookServiceBasic' import { SDOpt } from '../SD/sd' + /** * 一键反推的相关操作 */ @@ -33,6 +34,7 @@ export class ReverseBook { subtitle: Subtitle watermark: Watermark bookServiceBasic: BookServiceBasic + bookBasic: BookBasic constructor() { this.basicReverse = new BasicReverse() @@ -41,6 +43,7 @@ export class ReverseBook { this.watermark = new Watermark() this.taskScheduler = new TaskScheduler() this.bookServiceBasic = new BookServiceBasic() + this.bookBasic = new BookBasic() } // 主动返回前端的消息 sendReturnMessage(data: GeneralResponse.MessageResponse, message_name = DEFINE_STRING.BOOK.GET_COPYWRITING_RETURN) { @@ -189,32 +192,12 @@ export class ReverseBook { } } - /** - * 通过小说ID和小说批次任务ID获取小说和小说批次任务数据 - * @param bookId 小说ID - * @param bookTaskName 小说批次的名字,或者是小说批次任务的ID - * @returns - */ - async GetBookAndTask(bookId: string, bookTaskName: string) { - let book = await this.bookServiceBasic.GetBookDataById(bookId) - // 获取小说对应的批次任务数据,默认初始化为第一个 - let condition = { - bookId: bookId - } as Book.QueryBookBackTaskCondition - if (bookTaskName == "output_00001") { - condition["name"] = bookTaskName - } else { - condition["id"] = bookTaskName - } - 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 { try { - let { book, bookTask } = await this.GetBookAndTask(bookId, 'output_00001') + let { book, bookTask } = await this.bookBasic.GetBookAndTask(bookId, 'output_00001') let res = await this.basicReverse.ComputeStoryboardFunc(bookId, bookTask.id); // 分镜成功直接返回 return successMessage(null, res, "ReverseBook_ComputeStoryboard") @@ -224,62 +207,6 @@ export class ReverseBook { } } - /** - * 开始进行分镜,切割视频并抽帧 - * @param bookId - */ - async Framing(bookId: string): Promise { - try { - let { book, bookTask } = await this.GetBookAndTask(bookId, 'output_00001') - let bookTaskDetail = undefined as Book.SelectBookTaskDetail[] - try { - // 获取所有的分镜数据 - bookTaskDetail = await this.bookServiceBasic.GetBookTaskDetailData({ - bookId: bookId, - bookTaskId: bookTask.id - }) - } catch (error) { - // 传入的分镜数据为空,需要重新获取 - await this.taskScheduler.AddLogToDB( - bookId, - book.type, - `没有传入分镜数据,请先进行分镜计算`, - OtherData.DEFAULT, - LoggerStatus.DOING - ) - throw new Error("没有传入分镜数据,请先进行分镜计算"); - } - let bookTaskDetails = bookTaskDetail; - - for (let i = 0; i < bookTaskDetails.length; i++) { - const item = bookTaskDetails[i]; - let res = await this.basicReverse.FrameDataToCutVideoData(item); - } - await this.taskScheduler.AddLogToDB( - bookId, - book.type, - "所有的视频裁剪完成,开始抽帧", - bookTask.id, - LoggerStatus.SUCCESS - ) - - bookTaskDetail = await this.bookServiceBasic.GetBookTaskDetailData({ - bookId: bookId, - bookTaskId: bookTask.id - }) - - 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); - } - // 分镜成功直接返回 - return successMessage(null, "所有的视频裁剪,抽帧完成", "ReverseBook_Framing") - - } catch (error) { - return errorMessage("开始切割视频并抽帧失败,失败信息如下:" + error.message, 'ReverseBook_Framing') - } - } //#endregion //#region 反推相关任务 diff --git a/src/main/Service/Book/basicReverse.ts b/src/main/Service/Book/basicReverse.ts index 44df9b1..8c30db1 100644 --- a/src/main/Service/Book/basicReverse.ts +++ b/src/main/Service/Book/basicReverse.ts @@ -294,7 +294,7 @@ export class BasicReverse { } // 找到对应的小说ID和对应的小说批次任务ID,判断是不是有分镜数据 - let bookTaskRes = await this.bookTaskService.GetBookTaskData({ + let bookTaskRes = this.bookTaskService.GetBookTaskData({ bookId: bookId, name: 'output_00001' }) @@ -351,7 +351,7 @@ export class BasicReverse { for (let i = 0; i < bookTaskDetail.data.length; i++) { const element = bookTaskDetail.data[i] // 创建后台的视频分割任务 - let taskRes = await this.bookBackTaskListService.AddBookBackTask( + let taskRes = this.bookBackTaskListService.AddBookBackTask( bookId, BookBackTaskType.SPLIT, TaskExecuteType.AUTO, @@ -381,7 +381,7 @@ export class BasicReverse { * @param bookTaskDetailId 小说详细的分镜ID * @returns */ - async FrameDataToCutVideoData(bookTaskDetail: Book.SelectBookTaskDetail): Promise { + async FrameDataToCutVideoData(bookTaskDetail: Book.SelectBookTaskDetail, frameShortClipData: Book.BookFrameShortClip): Promise { await this.InitService() let startTime = bookTaskDetail.startTime let endTime = bookTaskDetail.endTime @@ -402,9 +402,9 @@ export class BasicReverse { let outVideoFile = path.join(book.bookFolderPath, `data/frame/${bookTaskDetail.name}.mp4`) let res = await this.ffmpegOptions.FfmpegCutVideo( - startTime, - endTime, - book.oldVideoPath, + frameShortClipData.startTime, + frameShortClipData.endTime, + frameShortClipData.videoPath, outVideoFile ) if (res.code == 0) { @@ -454,7 +454,7 @@ export class BasicReverse { throw new Error('没有找到对应的分镜数据') } - let cur_res = await this.FrameDataToCutVideoData(bookTaskDetail); + // let cur_res = await this.FrameDataToCutVideoData(bookTaskDetail, null); return successMessage(null, `${task.name}_视频裁剪完成`, "BasicReverse_CutVideoData"); } catch (error) { diff --git a/src/main/Service/Book/bookFrame.ts b/src/main/Service/Book/bookFrame.ts index bd88638..27ce5a3 100644 --- a/src/main/Service/Book/bookFrame.ts +++ b/src/main/Service/Book/bookFrame.ts @@ -6,13 +6,24 @@ import path from 'path'; import { FfmpegOptions } from "../ffmpegOptions"; import { CheckFileOrDirExist, CopyFileOrFolder, DeleteFolderAllFile } from "../../../define/Tools/file"; import fs from 'fs'; +import { Book } from "../../../model/book"; +import { TaskScheduler } from '../taskScheduler'; +import { BookBasic } from "./BooKBasic"; +import { LoggerStatus, OtherData } from "../../../define/enum/softwareEnum"; +import { BasicReverse } from "./basicReverse"; export class BookFrame { bookServiceBasic: BookServiceBasic ffmpegOptions: FfmpegOptions + taskScheduler: TaskScheduler + basicReverse: BasicReverse + bookBasic: BookBasic constructor() { this.bookServiceBasic = new BookServiceBasic(); this.ffmpegOptions = new FfmpegOptions(); + this.taskScheduler = new TaskScheduler() + this.bookBasic = new BookBasic() + this.basicReverse = new BasicReverse() } @@ -56,4 +67,130 @@ export class BookFrame { } } + //#region 进行分镜截取的一些操作 + + /** + * 预处理视频,将视频切割成小段,减少计算时间 + * @param book 小说数据 + * @param bookTaskDetails 分镜数据 + */ + async FrameShortClip(book: Book.SelectBook, bookTaskDetails: Book.SelectBookTaskDetail[], duration: number): Promise { + let result = [] as Book.BookFrameShortClip[] // 返回的数据 + let durationTime = 0; // 小视频片段的持续时间 + let tempCount = 0; + let videoPath = book.oldVideoPath + `_${tempCount}.mp4`; // 新的视频路径 + let startTime = 0; // 开始时间 + let endTime = 0; // 结束时间 + let lastEndTime = 0; // 上一个结束时间 + for (let i = 0; i < bookTaskDetails.length; i++) { + const item = bookTaskDetails[i]; + let temRes = { + startTime: item.startTime - lastEndTime, + endTime: item.endTime - lastEndTime, + videoPath: videoPath, + duration: item.endTime - item.startTime + } + endTime = item.endTime; + durationTime += item.endTime - item.startTime; + if (durationTime > duration) { // 判断条件切割视频 + // 开始切割视频 + await this.ffmpegOptions.FfmpegCutVideo( + startTime, + endTime, + book.oldVideoPath, + videoPath + ) + lastEndTime = item.endTime; + tempCount++; + durationTime = 0; + startTime = endTime; + endTime = 0; + videoPath = book.oldVideoPath + `_${tempCount}.mp4`; + } + result.push(temRes) + } + // 最后一个也要切割 + if (durationTime > 0) { + await this.ffmpegOptions.FfmpegCutVideo( + startTime, + endTime, + book.oldVideoPath, + videoPath + ) + } + return result; + } + + + /** + * 开始进行分镜,切割视频并抽帧 + * @param bookId + */ + async Framing(bookId: string): Promise { + try { + let { book, bookTask } = await this.bookBasic.GetBookAndTask(bookId, 'output_00001') + let bookTaskDetail = undefined as Book.SelectBookTaskDetail[] + try { + // 获取所有的分镜数据 + bookTaskDetail = await this.bookServiceBasic.GetBookTaskDetailData({ + bookId: bookId, + bookTaskId: bookTask.id + }) + } catch (error) { + // 传入的分镜数据为空,需要重新获取 + await this.taskScheduler.AddLogToDB( + bookId, + book.type, + `没有传入分镜数据,请先进行分镜计算`, + OtherData.DEFAULT, + LoggerStatus.DOING + ) + throw new Error("没有传入分镜数据,请先进行分镜计算"); + } + let bookTaskDetails = bookTaskDetail; + + // 这边重新开始计算,再次切割视频(预切割,减少长视频的计算时间) + let shortClipData = await this.FrameShortClip(book, bookTaskDetails, 180000); + console.log(shortClipData) + + if (shortClipData.length != bookTaskDetails.length) { + throw new Error('切割短视频和分镜数据不一致,请检查') + } + // 开始切割视频 + for (let i = 0; i < bookTaskDetails.length; i++) { + const item = bookTaskDetails[i]; + if (!shortClipData[i]) { + throw new Error('切割短视频和分镜数据不一致,请检查') + } + let res = await this.basicReverse.FrameDataToCutVideoData(item, shortClipData[i]); + } + await this.taskScheduler.AddLogToDB( + bookId, + book.type, + "所有的视频裁剪完成,开始抽帧", + bookTask.id, + LoggerStatus.SUCCESS + ) + + bookTaskDetail = await this.bookServiceBasic.GetBookTaskDetailData({ + bookId: bookId, + bookTaskId: bookTask.id + }) + + 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); + } + // 分镜成功直接返回 + return successMessage(null, "所有的视频裁剪,抽帧完成", "ReverseBook_Framing") + + } catch (error) { + return errorMessage("开始切割视频并抽帧失败,失败信息如下:" + error.message, 'ReverseBook_Framing') + } + } + + + //#endregion + } \ No newline at end of file diff --git a/src/main/Service/Book/bookGeneral.ts b/src/main/Service/Book/bookGeneral.ts new file mode 100644 index 0000000..8671a27 --- /dev/null +++ b/src/main/Service/Book/bookGeneral.ts @@ -0,0 +1,198 @@ +import { isEmpty } from "lodash"; +import { BookRepalceDataType } from "../../../define/enum/bookEnum"; +import { Book } from "../../../model/book"; +import { GeneralResponse } from "../../../model/generalResponse"; +import { errorMessage, successMessage } from "../../Public/generalTools"; +import { BookServiceBasic } from "../ServiceBasic/bookServiceBasic"; + +/** + * 小说通用操作的类 + */ +export class BookGeneral { + bookServiceBasic: BookServiceBasic + constructor() { + this.bookServiceBasic = new BookServiceBasic() + } + + //#region 批量替换数据的操作 + + /** + * 替换after_gpt 和 word 中的数据 + * @param bookTask + * @param bookTaskDetail + * @param replaceData + */ + ReplaceAfterGpt(bookTask: Book.SelectBook, bookTaskDetail: Book.SelectBookTaskDetail[], replaceData: Book.BookReplaceData): Book.ReplaceDataRes[] { + let result = []; + // 修改放在一个事务中 + this.bookServiceBasic.transaction((realm) => { + for (let i = 0; i < bookTaskDetail.length; i++) { + let element = bookTaskDetail[i]; + let afterGpt = element.afterGpt + // 判断是否存在before的数据 + if (!afterGpt.includes(replaceData.before)) { + continue + } + let newAfterGpt = afterGpt.replaceAll(replaceData.before, replaceData.after); + let btd = realm.objectForPrimaryKey('BookTaskDetail', element.id) + btd.afterGpt = newAfterGpt + result.push({ + bookTaskDetailId: element.id, + newData: newAfterGpt + } as Book.ReplaceDataRes) + } + }) + return result + } + + /** + * 替换反推提示词中的数据 + * @param realm realm对象 + * @param bookTaskDetail 小说分镜数据 + * @param replaceData 替换的数据 + * @returns + */ + ReplaceRversePrompt(realm: any, bookTaskDetail: Book.SelectBookTaskDetail, replaceData: Book.BookReplaceData): Book.ReplaceDataRes[] { + let result = []; + if (bookTaskDetail.reversePrompt && bookTaskDetail.reversePrompt.length > 0) { + for (let k = 0; k < bookTaskDetail.reversePrompt.length; k++) { + const item = bookTaskDetail.reversePrompt[k]; + if (!isEmpty(item.prompt) && item.prompt.includes(replaceData.before)) { + let newPrompt = item.prompt.replaceAll(replaceData.before, replaceData.after) + let rsp = realm.objectForPrimaryKey("ReversePrompt", item.id) + rsp.prompt = newPrompt + result.push({ + bookTaskDetailId: bookTaskDetail.id, + newData: newPrompt, + type: 'reversePrompt', + reversePromptType: 'prompt', + reversePromptId: item.id + } as Book.ReplaceDataRes) + } + if (!isEmpty(item.promptCN) && item.promptCN.includes(replaceData.before)) { + let newPrompt = item.promptCN.replaceAll(replaceData.before, replaceData.after) + let rsp = realm.objectForPrimaryKey("ReversePrompt", item.id) + rsp.promptCN = newPrompt + result.push({ + bookTaskDetailId: bookTaskDetail.id, + newData: newPrompt, + type: 'reversePrompt', + reversePromptType: 'promptCN', + reversePromptId: item.id + } as Book.ReplaceDataRes) + } + } + } + return result; + } + + /** + * 替换反推提示词词中的数据 + * @param bookTask 小说任务 + * @param bookTaskDetail 小说详细任务 + * @param replaceData 替换的数据 + * @returns + */ + ReplaceGPTPrompt(bookTask: Book.SelectBookTask, bookTaskDetail: Book.SelectBookTaskDetail[], replaceData: Book.BookReplaceData): Book.ReplaceDataRes[] { + let result = []; + // 修改放在一个事务中 + this.bookServiceBasic.transaction((realm) => { + for (let i = 0; i < bookTaskDetail.length; i++) { + let element = bookTaskDetail[i]; + // 替换GPT提示词 + if (!isEmpty(element.gptPrompt) && element.gptPrompt.includes(replaceData.before)) { + let newGptPrompt = element.gptPrompt.replaceAll(replaceData.before, replaceData.after) + let btd = realm.objectForPrimaryKey('BookTaskDetail', element.id) + btd.gptPrompt = newGptPrompt + result.push({ + bookTaskDetailId: element.id, + newData: newGptPrompt, + type: 'gptPrompt' + } as Book.ReplaceDataRes) + } + + if (replaceData.replaceAll) { + // 替换反推提示词,有的话 + let res = this.ReplaceRversePrompt(realm, element, replaceData); + result.push(...res) + } else { + if (!isEmpty(element.reversePrompt)) { + continue; + } + // 这边处理反推提示词,有的话 + let res = this.ReplaceRversePrompt(realm, element, replaceData); + result.push(...res) + } + + } + }) + return result + } + + /** + * 替换生图提示词里面的数据 + * @param bookTask 小说批次任务 + * @param bookTaskDetail 小说分镜任务信息 + * @param replaceData 替换的数据信息 + */ + ReplacePromptData(bookTask: Book.SelectBookTask, bookTaskDetail: Book.SelectBookTaskDetail[], replaceData: Book.BookReplaceData): Book.ReplaceDataRes[] { + let res = [] + this.bookServiceBasic.transaction((realm) => { + for (let i = 0; i < bookTaskDetail.length; i++) { + const element = bookTaskDetail[i]; + let prompt = element.prompt + if (isEmpty(prompt) || !prompt.includes(replaceData.before)) { + continue + } + let newPrompt = prompt.replaceAll(replaceData.before, replaceData.after); + let btd = realm.objectForPrimaryKey('BookTaskDetail', element.id) + btd.prompt = newPrompt + res.push({ + bookTaskDetailId: element.id, + newData: newPrompt, + type: 'prompt' + } as Book.ReplaceDataRes) + } + }) + return res + } + + /** + * 批量替换小说中的一些数据,这个方法会根据传递的数据进行替换 + * @param bookTaskId 小说任务ID + * @param replaceData 替换的数据 + */ + async ReplaceBookData(bookTaskId: string, replaceData: Book.BookReplaceData): Promise { + try { + console.log(bookTaskId, replaceData) + let bookTask = await this.bookServiceBasic.GetBookTaskDataById(bookTaskId) + let bookTaskDetails = await this.bookServiceBasic.GetBookTaskDetailData({ + bookTaskId: bookTaskId + }) + if (bookTaskDetails.length <= 0) { + throw new Error("未找到对应的小说任务批次任务数据"); + } + let res = [] + // 根据type调用对应的方法 + switch (replaceData.type) { + case BookRepalceDataType.AFTER_GPT: + res = this.ReplaceAfterGpt(bookTask, bookTaskDetails, replaceData) + break; + case BookRepalceDataType.GPT_PROMPT: + res = this.ReplaceGPTPrompt(bookTask, bookTaskDetails, replaceData) + break; + case BookRepalceDataType.PROMPT: + res = this.ReplacePromptData(bookTask, bookTaskDetails, replaceData) + break; + default: + throw new Error("未找到对应的替换类型"); + + } + return successMessage(res, '替换所有的提示词数据成功', 'BookGeneral_ReplaceBookData') + } catch (error) { + return errorMessage('替换失败,错误信息如下:' + error.toString(), 'BookGeneral_ReplaceBookData') + } + } + + //#endregion +} \ No newline at end of file diff --git a/src/main/Service/Book/bookTask.ts b/src/main/Service/Book/bookTask.ts index 78ac24c..8a81ab6 100644 --- a/src/main/Service/Book/bookTask.ts +++ b/src/main/Service/Book/bookTask.ts @@ -1,38 +1,232 @@ -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, CopyImageType, OperateBookType } from "../../../define/enum/bookEnum"; +import { CheckFileOrDirExist, CheckFolderExistsOrCreate, CopyFileOrFolder, DeleteFolderAllFile } from "../../../define/Tools/file"; +import { AddBookTaskCopyData, BookImageCategory, 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 { add, cloneDeep, isEmpty } from "lodash"; import { define } from '../../../define/define' import { v4 as uuidv4 } from 'uuid' import { GeneralResponse } from "../../../model/generalResponse"; +import { BookServiceBasic } from "../ServiceBasic/bookServiceBasic"; +import { ValidateJson } from "../../../define/Tools/validate"; +import fs from 'fs' +import { TimeStringToMilliseconds } from "../../../define/Tools/time"; /** * 小说批次相关的操作 */ export class BookTask { - bookTaskService: BookTaskService - bookTaskDetailService: BookTaskDetailService - bookService: BookService + bookServiceBasic: BookServiceBasic constructor() { + this.bookServiceBasic = new BookServiceBasic() + } + + //#region 添加小说批次任务 + + /** + * 复制小说批次任务的基础数据 + * @param oldBookTaskDetail 需要需要复制的小说批次任务 + */ + async CopyBookTaskDetailBaseData(bookTask: Book.SelectBookTask, newBookTask: Book.SelectBookTask, oldBookTaskDetail: Book.SelectBookTaskDetail[], addNewBookTask: Book.AddBookTask): Promise { + let bookTaskDetail = [] as Book.SelectBookTaskDetail[] + let book = await this.bookServiceBasic.GetBookDataById(bookTask.bookId) + let originalTimePath = path.join(book.bookFolderPath, `data/${book.id}.mp4.json`); + let originalTime = [] as any[]; + if (await CheckFileOrDirExist(originalTimePath)) { + let originalTimeString = await fs.promises.readFile(originalTimePath, 'utf-8'); + if (ValidateJson(originalTimeString)) { + originalTime = JSON.parse(originalTimeString) + } + } + // 判断分镜数据和批次数据是不是相同的 + if (originalTime.length != oldBookTaskDetail.length) { + originalTime = [] + } + for (let i = 0; i < oldBookTaskDetail.length; i++) { + const element = oldBookTaskDetail[i]; + let addOneBookTaskDetail = { + id: uuidv4(), + no: element.no, + name: element.name, + bookId: element.bookId, + bookTaskId: newBookTask.id, + videoPath: path.relative(define.project_path, element.videoPath), + oldImage: path.relative(define.project_path, element.oldImage), + adetailer: element.adetailer, + sdConifg: element.sdConifg, + createTime: new Date(), + updateTime: new Date(), + audioPath: element.audioPath, + subtitlePosition: element.subtitlePosition, + status: BookTaskStatus.WAIT, + imageLock: false, + } as Book.SelectBookTaskDetail + + // 开始添加数据 + if (bookTask && addNewBookTask.selectTaskDataCategory && addNewBookTask.selectTaskDataCategory.length > 0) { + // 有选择的数据,这边要调用各种方法 + if (addNewBookTask.selectTaskDataCategory.includes(AddBookTaskCopyData.AFTER_GPT)) { + // 是不是要复制文案 + addOneBookTaskDetail.word = element.word + addOneBookTaskDetail.afterGpt = element.afterGpt + addOneBookTaskDetail.subValue = element.subValue ? JSON.stringify(element.subValue) : undefined + addOneBookTaskDetail.oldImage = element.oldImage + addOneBookTaskDetail.startTime = element.startTime + addOneBookTaskDetail.endTime = element.endTime + addOneBookTaskDetail.timeLimit = element.timeLimit; + } else { + // 初始化时间信息 + if (originalTime[i]) { + addOneBookTaskDetail.startTime = TimeStringToMilliseconds(originalTime[i][0]) + addOneBookTaskDetail.endTime = TimeStringToMilliseconds(originalTime[i][1]) + addOneBookTaskDetail.timeLimit = undefined; + } + } + if (addNewBookTask.selectTaskDataCategory.includes(AddBookTaskCopyData.GPT_PROMPT)) { + // 是不是要复制GPT提示词 + addOneBookTaskDetail.gptPrompt = element.gptPrompt + // 复制反推提示词 + // 处理反推数据 + 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 = element.id + } + } + addOneBookTaskDetail.reversePrompt = reverseMessage + } + if (addNewBookTask.selectTaskDataCategory.includes(AddBookTaskCopyData.CHARACTER)) { + // 是不是要复制角色 + addOneBookTaskDetail.characterTags = element.characterTags + } + if (addNewBookTask.selectTaskDataCategory.includes(AddBookTaskCopyData.PROMPT)) { + // 是不是要复制提示 + addOneBookTaskDetail.prompt = element.prompt + } + + } + bookTaskDetail.push(addOneBookTaskDetail) + } + return bookTaskDetail + } + + /** + * 复制一份小说任务的基础数据 + * @param bookTask 小说任务 + * @param addNewBookTask 新增的小说数据 + * @param no 当前的编号 + * @returns + */ + CopyBookTaskBaseData(bookTask: Book.SelectBookTask, addNewBookTask: Book.AddBookTask, no: number): Book.SelectBookTask { + let name = 'output_' + no.toString().padStart(5, '0'); + let imageFolder = path.join(define.project_path, `${bookTask.bookId}/tmp/${name}`); + let newBookTask = { + id: uuidv4(), + bookId: bookTask.bookId, + no: no, + name: name, + generateVideoPath: undefined, + srtPath: bookTask.srtPath, + audioPath: bookTask.audioPath, + imageFolder: path.relative(define.project_path, imageFolder), + status: BookTaskStatus.WAIT, + errorMsg: undefined, + updateTime: new Date(), + createTime: new Date(), + isAuto: false, + autoAnalyzeCharacter: undefined, + imageStyle: [], + customizeImageStyle: [], + videoConfig: bookTask.videoConfig ??= undefined, + prefixPrompt: addNewBookTask.prefixPrompt ??= undefined, + suffixPrompt: addNewBookTask.suffixPrompt ?? undefined, + imageCategory: bookTask.imageCategory ??= BookImageCategory.MJ, + subImageFolder: [], + draftSrtStyle: undefined, + backgroundMusic: bookTask.backgroundMusic ??= undefined, + friendlyReminder: bookTask.friendlyReminder ??= undefined, + } as Book.SelectBookTask + if (addNewBookTask.selectTaskDataCategory && addNewBookTask.selectTaskDataCategory.includes(AddBookTaskCopyData.IMAGE_STYLE)) { + newBookTask.imageStyle = bookTask.imageStyle ??= [] + newBookTask.customizeImageStyle = bookTask.customizeImageStyle ??= [] + } + return newBookTask + } + + CopyCopywrittingData() { + + } + + /** + * 添加一个小说批次任务 + * @param bookTask 小说任务 + * @param bookTaskDetails 小说任务分镜信息 + * @param addNewBookTask 添加数据的基础数据信息 + * @returns + */ + async AddOneBookTask(bookTask: Book.SelectBookTask, bookTaskDetails: Book.SelectBookTaskDetail[], addNewBookTask: Book.AddBookTask, no: number): Promise<{ newBookTask: Book.SelectBookTask, newBookTaskDetails: Book.SelectBookTaskDetail[] }> { + let newBookTask = this.CopyBookTaskBaseData(bookTask, addNewBookTask, no) + let newBookTaskDetails = await this.CopyBookTaskDetailBaseData(bookTask, newBookTask, bookTaskDetails, addNewBookTask) + return { + newBookTask: newBookTask, + newBookTaskDetails: newBookTaskDetails + } } - async InitService() { - if (!this.bookTaskService) { - this.bookTaskService = await BookTaskService.getInstance() - } - if (!this.bookTaskDetailService) { - this.bookTaskDetailService = await BookTaskDetailService.getInstance() - } if (!this.bookService) { - this.bookService = await BookService.getInstance() + + /** + * 添加个新的小说批次任务 + * @param AddNewBookTask 添加的小说任务数据 + */ + async AddNewBookTask(addNewBookTask: Book.AddBookTask): Promise { + try { + console.log(addNewBookTask) + let bookTask = await this.bookServiceBasic.GetBookTaskDataById(addNewBookTask.selectBookTask) + let oldBookTaskDetail = await this.bookServiceBasic.GetBookTaskDetailData({ + bookTaskId: addNewBookTask.selectBookTask + }) + let bookTasks = [] as Book.SelectBookTask[] + let bookTaskDetail = [] as Book.SelectBookTaskDetail[] + + let maxNo = await this.bookServiceBasic.GetMaxBookTaskNo(bookTask.bookId) + for (let i = 0; i < addNewBookTask.count; i++) { + if (addNewBookTask.copyBookTask) { + let { newBookTask, newBookTaskDetails } = await this.AddOneBookTask(bookTask, oldBookTaskDetail, addNewBookTask, maxNo + i) + bookTasks.push(newBookTask) + bookTaskDetail.push(...newBookTaskDetails) + } else { + let newBookTask = this.CopyBookTaskBaseData({ bookId: bookTask.bookId }, addNewBookTask, maxNo + i) + bookTasks.push(newBookTask); + } + } + // 先要创建文件夹 + for (let i = 0; i < bookTasks.length; i++) { + let imageFolder = path.join(define.project_path, bookTasks[i].imageFolder) + await CheckFolderExistsOrCreate(imageFolder) + } + + // 这边开始添加数据 + this.bookServiceBasic.transaction((realm) => { + for (let i = 0; i < bookTasks.length; i++) { + const element = bookTasks[i]; + realm.create('BookTask', element) + } + for (let i = 0; i < bookTaskDetail.length; i++) { + const element = bookTaskDetail[i]; + realm.create('BookTaskDetail', element) + } + }) + return successMessage(null, "添加小说批次任务成功", "BookTask_AddNewBookTask") + } catch (error) { + return errorMessage('添加小说批次任务失败,错误信息如下' + error.toString(), "BookTask_AddNewBookTask"); } } + //#endregion + /** * 重置小说任务数据 @@ -40,13 +234,8 @@ export class BookTask { */ async ReSetBookTask(bookTaskId: string): Promise { try { - console.log(bookTaskId) - await this.InitService() - let bookTask = this.bookTaskService.GetBookTaskDataById(bookTaskId); - if (bookTask == null) { - throw new Error('未找到对应的小说任务') - } - this.bookTaskService.ResetBookTask(bookTaskId); + let bookTask = await this.bookServiceBasic.GetBookTaskDataById(bookTaskId); + await this.bookServiceBasic.ResetBookTask(bookTaskId); // 数据库删除完毕,看是删除对应的文件(主要是图片) let imageFolder = bookTask.imageFolder; // 整个删掉在重建 @@ -64,14 +253,10 @@ export class BookTask { */ async DeleteBookTask(bookTaskId: string): Promise { try { - await this.InitService(); - let bookTask = this.bookTaskService.GetBookTaskDataById(bookTaskId); - if (bookTask == null) { - throw new Error('未找到对应的小说任务,无法执行删除操作') - } + let bookTask = await this.bookServiceBasic.GetBookTaskDataById(bookTaskId); let imageFolder = bookTask.imageFolder; // 先删除每个批次对应的数据,然后删除批次 - this.bookTaskService.DeleteBookTask(bookTaskId); + await this.bookServiceBasic.DeleteBookTaskData(bookTaskId); // 删除成功,直接把对应的出图文件夹删掉 await DeleteFolderAllFile(imageFolder, true) return successMessage(null, "删除小说批次数据成功", "BookTask_DeleteBookTask") @@ -87,17 +272,15 @@ export class BookTask { */ async OneToFourBookTask(bookTaskId: string) { try { - console.log(bookTaskId) - await this.InitService(); let copyCount = 100 - let bookTask = this.bookTaskService.GetBookTaskDataById(bookTaskId) + let bookTask = await this.bookServiceBasic.GetBookTaskDataById(bookTaskId) if (bookTask == null) { throw new Error("没有找到对应的数小说任务,请检查数据") } // 获取所有的出图中最少的 - let bookTaskDetail = this.bookTaskDetailService.GetBookTaskData({ + let bookTaskDetail = (await this.bookServiceBasic.GetBookTaskData({ bookTaskId: bookTaskId - }).data as Book.SelectBookTaskDetail[] + })).bookTasks as Book.SelectBookTaskDetail[] if (bookTaskDetail == null || bookTaskDetail.length <= 0) { throw new Error("没有对应的小说分镜任务,请先添加分镜任务") } @@ -136,17 +319,12 @@ export class BookTask { */ 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 no = await this.bookServiceBasic.GetMaxBookTaskNo(sourceBookTask.bookId) let name = 'output_0000' + no let imageFolder = path.join(define.project_path, `${sourceBookTask.bookId}/tmp/${name}`) await CheckFolderExistsOrCreate(imageFolder) @@ -265,21 +443,21 @@ export class BookTask { // 数据处理完毕,开始新增数据 // 将所有的复制才做,全部放在一个事务中 - this.bookTaskService.transaction(() => { + this.bookServiceBasic.transaction((realm) => { for (let i = 0; i < addBookTask.length; i++) { const element = addBookTask[i]; - this.bookTaskService.realm.create('BookTask', element) + realm.create('BookTask', element) } for (let i = 0; i < addBookTaskDetail.length; i++) { const element = addBookTaskDetail[i]; - this.bookTaskDetailService.realm.create('BookTaskDetail', element) + realm.create('BookTaskDetail', element) } }) // 全部创建完成 // 查找到数据,然后全部返回 - let returnBookTask = this.bookTaskService.GetBookTaskData({ + let returnBookTask = (await this.bookServiceBasic.GetBookTaskData({ bookId: sourceBookTask.bookId - }).data as Book.SelectBookTask[] + })).bookTasks as Book.SelectBookTask[] return successMessage(returnBookTask, "复制小说任务成功", "BookBasic_CopyNewBookTask") } catch (error) { diff --git a/src/main/Service/MJ/mj.ts b/src/main/Service/MJ/mj.ts index be626d5..8bd4f9a 100644 --- a/src/main/Service/MJ/mj.ts +++ b/src/main/Service/MJ/mj.ts @@ -5,7 +5,7 @@ import { BookBackTaskListService } from "../../../define/db/service/Book/bookBac import { BookTaskDetailService } from "../../../define/db/service/Book/bookTaskDetailService"; import { CheckFolderExistsOrCreate, CopyFileOrFolder, JoinPath } from "../../../define/Tools/file"; import { define } from "../../../define/define" -import { GetImageBase64, ImageSplit } from "../../../define/Tools/image"; +import { CompressImageToSize, GetImageBase64, ImageSplit } from "../../../define/Tools/image"; import MJApi from "./mjApi" import { BookBackTaskStatus, BookBackTaskType, BookTaskStatus, BookType, DialogType, MJAction, OperateBookType, TaskExecuteType } from "../../../define/enum/bookEnum"; import { DEFINE_STRING } from "../../../define/define_string"; @@ -214,10 +214,16 @@ export class MJOpt { throw new Error(`${bookTaskDetail.name} 没有需要反推的图片`); } oldImagePath = JoinPath(define.project_path, oldImagePath) - let imageBase64 = await GetImageBase64(oldImagePath) + // let imageBase64 = await GetImageBase64(oldImagePath) + + // 每次执行前压缩图片 + let newImageBase64 = await CompressImageToSize(oldImagePath, 500000); + // 将buffer转换为base64 + let imageBase64 = newImageBase64.toString('base64'); + // 这个就是任务ID let reqRes = await this.mjApi.SubmitMJDescribe({ - image: imageBase64, + image: 'data:image/png;base64,' + imageBase64, taskId: task.id }) if (reqRes == '23') { @@ -394,8 +400,8 @@ export class MJOpt { if (bookTask.prefixPrompt) { promptStr = checkStringValueAddSuffix(bookTask.prefixPrompt, ',') + promptStr } - if (bookTask.prefixPrompt) { - promptStr = checkStringValueAddSuffix(promptStr, ',') + bookTask.prefixPrompt + if (bookTask.suffixPrompt) { + promptStr = checkStringValueAddSuffix(promptStr, ',') + bookTask.suffixPrompt } promptStr = ' ' + promptStr; promptStr += ` ${cref_url} ${style_url}${suffixParam}` diff --git a/src/main/Service/SD/sd.ts b/src/main/Service/SD/sd.ts index 848fe60..8687b81 100644 --- a/src/main/Service/SD/sd.ts +++ b/src/main/Service/SD/sd.ts @@ -1,11 +1,8 @@ import { Book } from "../../../model/book"; import { GeneralResponse } from "../../../model/generalResponse"; import { checkStringValueAddSuffix, errorMessage, successMessage } from "../../Public/generalTools"; -import { BookTaskDetailService } from "../../../define/db/service/Book/bookTaskDetailService"; -import { BookTaskService } from "../../../define/db/service/Book/bookTaskService"; 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"; @@ -13,26 +10,22 @@ import { BookServiceBasic } from "../ServiceBasic/bookServiceBasic"; const fspromise = fs.promises export class SDOpt { - bookTaskDetailService: BookTaskDetailService - bookTaskService: BookTaskService imageStyle: ImageStyle bookServiceBasic: BookServiceBasic constructor() { this.bookServiceBasic = new BookServiceBasic() + this.imageStyle = new ImageStyle() } - async InitService() { - if (!this.bookTaskDetailService) { - this.bookTaskDetailService = await BookTaskDetailService.getInstance() - } - if (!this.bookTaskService) { - this.bookTaskService = await BookTaskService.getInstance() - } - if (!this.imageStyle) { - this.imageStyle = new ImageStyle() - } + // TODO 这边的设置应该改为数据库 + /** + * 获取SD的设置,之后要删掉,改为数据库 + */ + private async GetSDSetting() { + let sdSetting = JSON.parse(await fspromise.readFile(define.sd_setting, 'utf-8')) + return sdSetting } /** @@ -43,15 +36,16 @@ export class SDOpt { */ async MergePrompt(id: string, operateBookType: OperateBookType): Promise { try { - await this.InitService() let bookTaskDetail = undefined as Book.SelectBookTaskDetail[]; let bookTask = undefined as Book.SelectBookTask; + let sd_setting = await this.GetSDSetting(); + let style_weight = sd_setting.setting.style_weight ? sd_setting.setting.style_weight : 1 if (operateBookType == OperateBookType.BOOKTASK) { - bookTaskDetail = this.bookTaskDetailService.GetBookTaskData({ + bookTaskDetail = (await this.bookServiceBasic.GetBookTaskData({ bookTaskId: id - }).data - bookTask = this.bookTaskService.GetBookTaskDataById(id); + })).bookTasks; + bookTask = await this.bookServiceBasic.GetBookTaskDataById(id); // 判断是不是有为空的 let emptyName = [] as string[] for (let i = 0; i < bookTaskDetail.length; i++) { @@ -69,7 +63,7 @@ export class SDOpt { throw new Error("当前分镜没有推理提示词,请先生成") } bookTaskDetail = [tempBookTaskDetail]; - bookTask = this.bookTaskService.GetBookTaskDataById(bookTaskDetail[0].bookTaskId); + bookTask = await this.bookServiceBasic.GetBookTaskDataById(bookTaskDetail[0].bookTaskId); } else { throw new Error("未知的合并类型") } @@ -86,12 +80,12 @@ export class SDOpt { for (let i = 0; i < styleArr.length; i++) { const element = styleArr[i] if (element.type == 'style_main') { - styleString += element.prompt + ', ' + styleString += `(${element.prompt}:${style_weight})` + ', ' if (element.lora && element.lora != '无' && element.lora_weight) { styleString += `, ` } } else { - styleString += element.english_style + ',' + styleString += `(${element.english_style}:${style_weight})` + ',' } } @@ -126,8 +120,8 @@ export class SDOpt { if (bookTask.prefixPrompt) { promptStr = checkStringValueAddSuffix(bookTask.prefixPrompt, ',') + promptStr } - if (bookTask.prefixPrompt) { - promptStr = checkStringValueAddSuffix(promptStr, ',') + bookTask.prefixPrompt + if (bookTask.suffixPrompt) { + promptStr = checkStringValueAddSuffix(promptStr, ',') + bookTask.suffixPrompt } if (sdGlobalPrompt) { promptStr = checkStringValueAddSuffix(sdGlobalPrompt, ',') + promptStr @@ -136,7 +130,7 @@ export class SDOpt { promptStr = ' ' + promptStr; console.log(promptStr) // 修改数据库数据 - this.bookTaskDetailService.UpdateBookTaskDetail(element.id, { + await this.bookServiceBasic.UpdateBookTaskDetail(element.id, { prompt: promptStr }) // 写回数据 @@ -147,7 +141,6 @@ export class SDOpt { } return successMessage(result, "SD和并提示词数据成功", "SDOpt_MergePrompt") - } catch (error) { return errorMessage("SD合并提示词,错误信息如下:" + error.toString(), "SDOpt_MergePrompt") } diff --git a/src/main/Service/ServiceBasic/bookServiceBasic.ts b/src/main/Service/ServiceBasic/bookServiceBasic.ts index 82230f1..0e28bbc 100644 --- a/src/main/Service/ServiceBasic/bookServiceBasic.ts +++ b/src/main/Service/ServiceBasic/bookServiceBasic.ts @@ -42,6 +42,16 @@ export class BookServiceBasic { global.newWindow[0].win.webContents.send(message_name, data) } + + //#region 事务操作 + transaction(callback: (realm: any) => void) { + this.bookService.transaction(() => { + callback(this.bookService.realm) + }) + } + + //#endregion + //#region 小说相关的基础服务 /** @@ -111,6 +121,20 @@ export class BookServiceBasic { return bookTasks.data } + /** + * 获取最大的小说批次任务的编号 + * @param bookId 小说ID + */ + async GetMaxBookTaskNo(bookId: string): Promise { + await this.InitService(); + let maxNo = this.bookTaskService.realm + .objects('BookTask') + .filtered('bookId = $0', bookId) + .max('no') + let no = maxNo == null ? 1 : Number(maxNo) + 1 + return no + } + /** * 更新小说批次任务的数据 * @param bookTaskId 小说批次任务ID @@ -121,6 +145,15 @@ export class BookServiceBasic { this.bookTaskService.UpdetedBookTaskData(bookTaskId, data) } + /** + * 重置小说批次任务的数据 + * @param bookTaskId 指定的重置的小说批次任务的ID + */ + async ResetBookTask(bookTaskId: string): Promise { + await this.InitService(); + this.bookTaskService.ResetBookTask(bookTaskId) + } + /** * 删除指定的小说批次任务的数据 * @param bookTaskId 需要删除的指定的小说批次任务ID diff --git a/src/main/Service/Subtitle/subtitleService.ts b/src/main/Service/Subtitle/subtitleService.ts index 8023c9e..6f3cd91 100644 --- a/src/main/Service/Subtitle/subtitleService.ts +++ b/src/main/Service/Subtitle/subtitleService.ts @@ -12,7 +12,7 @@ import { CheckFileOrDirExist } from "../../../define/Tools/file"; import { BookServiceBasic } from "../ServiceBasic/bookServiceBasic"; import { Subtitle } from "./subtitle"; import { TaskScheduler } from "../taskScheduler"; -import { OperateBookType } from "../../../define/enum/bookEnum"; +import { BookType, OperateBookType } from "../../../define/enum/bookEnum"; import { Book } from "../../../model/book"; import { TimeStringToMilliseconds } from "../../../define/Tools/time"; @@ -189,8 +189,9 @@ export class SubtitleService { return errorMessage("获取分镜数据失败,失败信息如下:" + error.message, 'ReverseBook_GetCopywriting') } } + //#endregion - + //#region 文案相关的操作 /** * 导出指定导出指定小说的文案 * @param bookTaskId 小说批次任务ID @@ -231,7 +232,57 @@ export class SubtitleService { } } - //#region 文案相关的操作 + /** + * 将 + * @param bookId + * @param bookTaskId + * @param txtPath + * @returns + */ + async ImportCopywriting(bookId: string, bookTaskId: string, txtPath: string): Promise { + try { + let word = await fs.promises.readFile(txtPath, 'utf-8'); + let wordLines = word.split('\n').map(line => line.trim()).filter(line => line.length > 0); + // 判断小说类型,是反推的话,文案和分镜数据要对应 + let book = await this.bookServiceBasic.GetBookDataById(bookId) + let bookTaskDetail = await this.bookServiceBasic.GetBookTaskDetailData({ + bookId: bookId, + bookTaskId: bookTaskId + }) + + if (book.type == BookType.MJ_REVERSE || book.type == BookType.SD_REVERSE) { + if (wordLines.length != bookTaskDetail.length) { + throw new Error("文案行数和分镜数据不对应,请检查") + } + } else if (book.type == BookType.ORIGINAL) { + // 原创这边也要做些判断,待定 + throw new Error("原创小说暂时不支持导入文案"); + } else { + throw new Error("未知的小说类型,请检查") + } + + let result = [] + // 这边开始导入文案,这边使用事务 + this.bookServiceBasic.transaction((realm) => { + for (let i = 0; i < bookTaskDetail.length; i++) { + const element = bookTaskDetail[i]; + let btd = realm.objectForPrimaryKey("BookTaskDetail", element.id); + let ag = wordLines[i] ? wordLines[i] : '' + + btd.afterGpt = ag + result.push({ + bookTaskDetailId: element.id, + afterGpt: ag + }); + } + }) + + return successMessage(result, "导入文案成功", "ReverseBook_ImportCopywriting") + } catch (error) { + return errorMessage("导入文案失败,失败信息如下:" + error.message, 'ReverseBook_ImportCopywriting') + } + } + /** * 清除导入的对齐后的文案 @@ -287,6 +338,4 @@ export class SubtitleService { } //#endregion - - //#endregion } \ No newline at end of file diff --git a/src/main/Service/ffmpegOptions.ts b/src/main/Service/ffmpegOptions.ts index 1959af6..c7645f9 100644 --- a/src/main/Service/ffmpegOptions.ts +++ b/src/main/Service/ffmpegOptions.ts @@ -80,7 +80,7 @@ export class FfmpegOptions { return new Promise((resolve, reject) => { Ffmpeg(tempVideo) .outputOptions([ - `-vf scale=${width}:${height}`, // 调整视频尺寸到 1280x720 + `-vf scale=${Math.floor(width)}:${Math.floor(height)}`, // 调整视频尺寸到 1280x720 `-b:v ${crf}` // 设置视频比特率为 1000kbps ]) .on('end', async () => { diff --git a/src/main/index.js b/src/main/index.js index dd9fd39..4c581df 100644 --- a/src/main/index.js +++ b/src/main/index.js @@ -66,12 +66,12 @@ async function createWindow(hash = 'ShowMessage', data, url = null) { webPreferences: { preload: join(__dirname, '../preload/index.js'), sandbox: false, - nodeIntegration: hash == 'discord' ? false : true, // 在网页中集成Node + nodeIntegration: true, // 在网页中集成Node nodeIntegrationInWorker: true, webSecurity: false, partition: 'persist:my-partition', session: ses, - webviewTag : true, + webviewTag: true } }) diff --git a/src/model/book.d.ts b/src/model/book.d.ts index 2a578cd..092bc50 100644 --- a/src/model/book.d.ts +++ b/src/model/book.d.ts @@ -1,4 +1,4 @@ -import { BookBackTaskStatus, BookBackTaskType, BookTaskStatus, BookType, TaskExecuteType } from "../define/enum/bookEnum" +import { BookBackTaskStatus, BookBackTaskType, BookTaskStatus, BookType, TaskExecuteType, BookRepalceDataType } from "../define/enum/bookEnum" import { MJAction } from "../define/enum/bookEnum" import { MJImageType } from "../define/enum/mjEnum" @@ -76,6 +76,16 @@ declare namespace Book { subImageFolder?: string[] | null // 子图片文件夹地址,多个 } + // 添加批次任务 + type AddBookTask = { + copyBookTask: boolean // 是否复制旧的小说任务 + count: number // 批次数量 + prefixPrompt?: string // 前缀提示 + suffixPrompt?: string // 后缀提示词 + selectBookTask?: string // 选择的旧的小说任务 + selectTaskDataCategory?: string[] // 选择复制的数据类型 + } + // 字幕相关 type Subtitle = { startTime: number @@ -246,4 +256,33 @@ declare namespace Book { id?: string image?: string } + + /** + * 裁剪小视频的返回数据类型 + */ + type BookFrameShortClip = { + startTime: number + endTime: number + videoPath: string + duration: number + } + + //#region 替换小说数据 + + type BookReplaceData = { + before: string, + after: string, + replaceAll?: boolean, + type: BookRepalceDataType + } + + type ReplaceDataRes = { + bookTaskDetailId: string, + newData: string, + type?: 'reversePrompt' | 'gptPrompt' | 'afterGpt' | 'prompt', + reversePromptType?: 'prompt' | 'promptCN' + reversePromptId?: string + } + + //#endregion } diff --git a/src/preload/book.js b/src/preload/book.ts similarity index 91% rename from src/preload/book.js rename to src/preload/book.ts index e9a31e6..9fa4a15 100644 --- a/src/preload/book.js +++ b/src/preload/book.ts @@ -1,5 +1,6 @@ import { ipcRenderer } from 'electron' import { DEFINE_STRING } from '../define/define_string' +import { Book } from '../model/book' const book = { // 获取小说操作类型(原创/SD反推/MJ反推) @@ -51,6 +52,18 @@ const book = { //#endregion + //#region 小说通用操作 + + // 替换文案数据 + ReplaceBookData: async (bookTaskId: string, replaceData: Book.BookReplaceData) => + await ipcRenderer.invoke( + DEFINE_STRING.BOOK.REPLACE_BOOK_DATA, + bookTaskId, + replaceData + ), + + //#endregion + //#region 分镜 // 开始计算分镜数据 @@ -85,6 +98,10 @@ const book = { ExportCopywriting: async (bookTaskId) => await ipcRenderer.invoke(DEFINE_STRING.BOOK.EXPORT_COPYWRITING, bookTaskId), + // 导入文案,修改过后的 + ImportCopywriting: async (bookId: string, bookTaskId: string, txtPath: string) => + await ipcRenderer.invoke(DEFINE_STRING.BOOK.IMPORT_COPYWRITING, bookId, bookTaskId, txtPath), + // 清除导入对齐的文案 ClearImportWord: async (bookTaskId) => await ipcRenderer.invoke(DEFINE_STRING.BOOK.CLEAR_IMPORT_WORD, bookTaskId), @@ -183,6 +200,10 @@ const book = { //#region 小说批次任务相关 + // 添加新的小说批次任务 + AddNewBookTask: async (addBookTaskData) => + await ipcRenderer.invoke(DEFINE_STRING.BOOK.ADD_NEW_BOOK_TASK, addBookTaskData), + // 重置小说批次数据 ReSetBookTask: async (bookTaskId) => await ipcRenderer.invoke(DEFINE_STRING.BOOK.RESET_BOOK_TASK, bookTaskId), diff --git a/src/preload/index.js b/src/preload/index.js index d3773a8..bd0db60 100644 --- a/src/preload/index.js +++ b/src/preload/index.js @@ -8,7 +8,7 @@ import { img } from './img.js' import { system } from './system.js' import { setting } from './setting.js' import { prompt } from './prompt.js' -import { book } from './book.js' +import { book } from './book' import { tts } from './tts.js' import { write } from './write.js' import { gpt } from './gpt.js' diff --git a/src/renderer/src/components/APIService/ApiService.vue b/src/renderer/src/components/APIService/ApiService.vue index 1386fb6..be8da6c 100644 --- a/src/renderer/src/components/APIService/ApiService.vue +++ b/src/renderer/src/components/APIService/ApiService.vue @@ -1,15 +1,42 @@ diff --git a/src/renderer/src/components/Book/Components/DatatableHeaderAfterGptSelectAndReplace.vue b/src/renderer/src/components/Book/Components/DatatableHeaderAfterGptSelectAndReplace.vue new file mode 100644 index 0000000..80f2963 --- /dev/null +++ b/src/renderer/src/components/Book/Components/DatatableHeaderAfterGptSelectAndReplace.vue @@ -0,0 +1,116 @@ + + + diff --git a/src/renderer/src/components/Book/Components/DatatableHeaderGptPrompt.vue b/src/renderer/src/components/Book/Components/DatatableHeaderGptPrompt.vue index 18db8e6..c7d98a2 100644 --- a/src/renderer/src/components/Book/Components/DatatableHeaderGptPrompt.vue +++ b/src/renderer/src/components/Book/Components/DatatableHeaderGptPrompt.vue @@ -1,37 +1,58 @@ diff --git a/src/renderer/src/components/Book/Components/ManageBookDetailButton.vue b/src/renderer/src/components/Book/Components/ManageBookDetailButton.vue index a64067a..02885f6 100644 --- a/src/renderer/src/components/Book/Components/ManageBookDetailButton.vue +++ b/src/renderer/src/components/Book/Components/ManageBookDetailButton.vue @@ -362,7 +362,6 @@ export default defineComponent({ // 导出文案 async function ExportCopywriting() { - debugger softwareStore.spin.spinning = true softwareStore.spin.tip = '正在导出文案中...' let res = await window.book.ExportCopywriting(reverseManageStore.selectBookTask.id) @@ -382,6 +381,36 @@ export default defineComponent({ softwareStore.spin.spinning = false } + // 导入文案 + async function ImportCopywriting() { + window.api.SelectFile(['txt'], async (value) => { + if (value.code == 0) { + message.error(value.message) + return + } + let txtPath = value.value + // 这边开始导入 + let res = await window.book.ImportCopywriting( + reverseManageStore.selectBook.id, + reverseManageStore.selectBookTask.id, + txtPath + ) + if (res.code == 1) { + for (let i = 0; i < res.data.length; i++) { + const element = res.data[i] + let index = reverseManageStore.selectBookTaskDetail.findIndex( + (x) => x.id == element.bookTaskDetailId + ) + if (index != -1) { + reverseManageStore.selectBookTaskDetail[index].afterGpt = element.afterGpt + } + } + } else { + window.api.showGlobalMessageDialog(res) + } + }) + } + /** * 获取水印位置 */ @@ -420,6 +449,9 @@ export default defineComponent({ case 'export_recognizing': // 导出文案 await ExportCopywriting() break + case 'import_recognizing': // 导入文案 + await ImportCopywriting() + break case 'watermark_position': // 水印位置设置 await GetWatermarkPosition() break @@ -853,6 +885,10 @@ export default defineComponent({ label: '导出文案', key: 'export_recognizing' }, + { + label: '导入文案', + key: 'import_recognizing' + }, { label: '停止提取', key: 'stop_recognizing' diff --git a/src/renderer/src/components/Book/MJReverse/ManageBookReverseTable.vue b/src/renderer/src/components/Book/MJReverse/ManageBookReverseTable.vue index d936644..a77f7a5 100644 --- a/src/renderer/src/components/Book/MJReverse/ManageBookReverseTable.vue +++ b/src/renderer/src/components/Book/MJReverse/ManageBookReverseTable.vue @@ -19,6 +19,7 @@ import ManageBookOldImage from '../Components/ManageBookOldImage.vue' import { BookType } from '../../../../../define/enum/bookEnum' import MJReversePrompt from './MJReversePrompt.vue' import DatatableHeaderGptPrompt from '../Components/DatatableHeaderGptPrompt.vue' +import DatatableHeaderAfterGpt from '../Components/DatatableHeaderAfterGpt.vue' import DatatableHeaderPrompt from '../Components/DatatableHeaderPrompt.vue' import DatatablePrompt from '../Components/DatatablePrompt.vue' import DatatableHeaderImage from '../Components/DatatableHeaderImage.vue' @@ -41,7 +42,9 @@ export default defineComponent({ fixed: 'left' }, { - title: '文案', + title(row) { + return h(DatatableHeaderAfterGpt) + }, key: 'afterGpt', width: 200, fixed: 'left', @@ -87,10 +90,7 @@ export default defineComponent({ }, { title(row) { - return h('div', { style: 'display: flex; align-items: center' }, [ - h('span', { size: 'tiny', style: 'margin-left: 5px' }, '生图提示词'), - h(DatatableHeaderPrompt) - ]) + return h(DatatableHeaderPrompt) }, className: 'space-row', key: 'row1', diff --git a/src/renderer/src/components/Book/ManageBookTask.vue b/src/renderer/src/components/Book/ManageBookTask.vue index b94f232..46906e1 100644 --- a/src/renderer/src/components/Book/ManageBookTask.vue +++ b/src/renderer/src/components/Book/ManageBookTask.vue @@ -41,6 +41,7 @@ import { useRouter } from 'vue-router' import { OperateBookType } from '../../../../define/enum/bookEnum' import { DEFINE_STRING } from '../../../../define/define_string' import { ResponseMessageType } from '../../../../define/enum/softwareEnum' +import AddBookTask from './Components/ManageBook/AddBookTask.vue' export default defineComponent({ components: { @@ -167,6 +168,16 @@ export default defineComponent({ } } + async function AddBookDialog() { + message.info('新增' + reverseManageStore.selectBook.id) + dialog.create({ + title: '新增小说批次任务', + showIcon: false, + content: () => h(AddBookTask), + style: { width: '600px' } + }) + } + /** * 一键生成草稿 */ @@ -197,6 +208,7 @@ export default defineComponent({ return { reverseManageStore, + AddBookDialog, HDImageAll, DraftAll, VideoAll, diff --git a/src/renderer/src/components/Book/ReverseManage.vue b/src/renderer/src/components/Book/ReverseManage.vue index 2565ce7..90f080e 100644 --- a/src/renderer/src/components/Book/ReverseManage.vue +++ b/src/renderer/src/components/Book/ReverseManage.vue @@ -7,14 +7,6 @@ - - - - - - - - diff --git a/src/renderer/src/components/Components/BackTask/BackTask.vue b/src/renderer/src/components/Components/BackTask/BackTask.vue new file mode 100644 index 0000000..d5d27f2 --- /dev/null +++ b/src/renderer/src/components/Components/BackTask/BackTask.vue @@ -0,0 +1,19 @@ + + + diff --git a/src/renderer/src/components/Components/BackTask/QueryButton.vue b/src/renderer/src/components/Components/BackTask/QueryButton.vue new file mode 100644 index 0000000..56de7db --- /dev/null +++ b/src/renderer/src/components/Components/BackTask/QueryButton.vue @@ -0,0 +1,57 @@ + + + diff --git a/src/renderer/src/components/Components/BackTask/TaskDataTable.vue b/src/renderer/src/components/Components/BackTask/TaskDataTable.vue new file mode 100644 index 0000000..bbee35c --- /dev/null +++ b/src/renderer/src/components/Components/BackTask/TaskDataTable.vue @@ -0,0 +1,7 @@ + + + diff --git a/src/renderer/src/components/Home/Home.vue b/src/renderer/src/components/Home/Home.vue index 6089213..f770a30 100644 --- a/src/renderer/src/components/Home/Home.vue +++ b/src/renderer/src/components/Home/Home.vue @@ -33,18 +33,21 @@