diff --git a/electron.vite.config.mjs b/electron.vite.config.mjs index 85e350d..a455137 100644 --- a/electron.vite.config.mjs +++ b/electron.vite.config.mjs @@ -17,7 +17,8 @@ export default defineConfig({ renderer: { resolve: { alias: { - '@renderer': resolve('src/renderer/src') + '@renderer': resolve('src/renderer/src'), + "@" : resolve('src/'), } }, plugins: [vue(), Jsx()] diff --git a/package-lock.json b/package-lock.json index ecbd5fc..d8f86a8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "laitool", - "version": "3.1.9", + "version": "3.2.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "laitool", - "version": "3.1.9", + "version": "3.2.0", "hasInstallScript": true, "dependencies": { "@alicloud/alimt20181012": "^1.2.0", diff --git a/package.json b/package.json index 341d2de..fb0cd97 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "laitool", - "version": "3.1.9", + "version": "3.2.0", "description": "An AI tool for image processing, video processing, and other functions.", "main": "./out/main/index.js", "author": "laitool.cn", diff --git a/resources/scripts/db/book.realm.lock b/resources/scripts/db/book.realm.lock index 8c40e99..a7ba841 100644 Binary files a/resources/scripts/db/book.realm.lock and b/resources/scripts/db/book.realm.lock differ diff --git a/resources/scripts/db/software.realm.lock b/resources/scripts/db/software.realm.lock index 4d29dfd..c2db7c0 100644 Binary files a/resources/scripts/db/software.realm.lock and b/resources/scripts/db/software.realm.lock differ diff --git a/src/define/db/service/Book/bookService.ts b/src/define/db/service/Book/bookService.ts index 4c8b240..3b72ca4 100644 --- a/src/define/db/service/Book/bookService.ts +++ b/src/define/db/service/Book/bookService.ts @@ -205,7 +205,7 @@ export class BookService extends BaseRealmService { } else if (book.type == BookType.MJ_REVERSE) { imageCategory = BookImageCategory.MJ } else if (book.type == BookType.ORIGINAL) { - imageCategory = BookImageCategory.MJ + imageCategory = global.config.defaultImageMode } else { throw new Error('未知的小说类型') } diff --git a/src/define/define_string/bookDefineString.ts b/src/define/define_string/bookDefineString.ts index 06c9eba..28d833d 100644 --- a/src/define/define_string/bookDefineString.ts +++ b/src/define/define_string/bookDefineString.ts @@ -104,6 +104,9 @@ const BOOK = { */ GET_IMAGE_URL_AND_DOWNLOAD: "GET_IMAGE_URL_AND_DOWNLOAD", + /** 添加一键生图后台任务 */ + GENERATE_ALL_TASK_IMAGE: "GENERATE_ALL_TASK_IMAGE", + //#endregion COMPUTE_STORYBOARD: 'COMPUTE_STORYBOARD', diff --git a/src/define/define_string/index.ts b/src/define/define_string/index.ts index d10a215..6d7397a 100644 --- a/src/define/define_string/index.ts +++ b/src/define/define_string/index.ts @@ -3,6 +3,7 @@ import TASK from "./taskDefineString" import TTS from "./ttsDefineString" import SETTING from "./settingDefineString" import BOOK from "./bookDefineString" +import WRITE from "./writeDefineString" export const DEFINE_STRING = { SYSTEM: SYSTEM, @@ -10,6 +11,7 @@ export const DEFINE_STRING = { TTS: TTS, BOOK: BOOK, SETTING: SETTING, + WRITE: WRITE, SHOW_GLOBAL_MESSAGE: "SHOW_GLOBAL_MESSAGE", SHOW_GLOBAL_MAIN_NOTIFICATION: 'SHOW_GLOBAL_MAIN_NOTIFICATION', OPEN_DEV_TOOLS_PASSWORD: 'OPEN_DEV_TOOLS_PASSWORD', @@ -246,14 +248,6 @@ export const DEFINE_STRING = { */ GET_SCENE_PRESET: "GET_SCENE_PRESET" }, - WRITE: { - GET_WRITE_CONFIG: 'GET_WRITE_CONFIG', - SAVE_WRITE_CONFIG: 'SAVE_WRITE_CONFIG', - 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", UPDATE_BOOK_TASK_DETAIL_DATA: "UPDATE_BOOK_TASK_DETAIL_DATA", diff --git a/src/define/define_string/taskDefineString.ts b/src/define/define_string/taskDefineString.ts index 757537d..62be6d2 100644 --- a/src/define/define_string/taskDefineString.ts +++ b/src/define/define_string/taskDefineString.ts @@ -9,6 +9,9 @@ const TASK = { /** 获取等待中的任务 */ GET_ALL_STATUS_TASK_COUNT: "GET_ALL_STATUS_TASK_COUNT", + /** 获取后台任务的集合,分页 */ + GET_BACK_TASK_COLLECTION: "GET_BACK_TASK_COLLECTION", + }; export default TASK; diff --git a/src/define/define_string/writeDefineString.ts b/src/define/define_string/writeDefineString.ts new file mode 100644 index 0000000..69a3cb0 --- /dev/null +++ b/src/define/define_string/writeDefineString.ts @@ -0,0 +1,15 @@ +const WRITE = { + GET_WRITE_CONFIG: 'GET_WRITE_CONFIG', + SAVE_WRITE_CONFIG: 'SAVE_WRITE_CONFIG', + ACTION_START: 'ACTION_START', + GET_SUBTITLE_SETTING: "GET_SUBTITLE_SETTING", + RESET_SUBTITLE_SETTING: "RESET_SUBTITLE_SETTING", + SAVE_SUBTITLE_SETTING: "SAVE_SUBTITLE_SETTING", + + /** 生成洗稿后文案 */ + GENERATE_AFTER_GPT_WORD: "GENERATE_AFTER_GPT_WORD", + /** 生成洗稿后文案返回数据,前端接收 */ + GENERATE_AFTER_GPT_WORD_RESPONSE: "GENERATE_AFTER_GPT_WORD_RESPONSE" +} + +export default WRITE \ No newline at end of file diff --git a/src/define/enum/bookEnum.ts b/src/define/enum/bookEnum.ts index 49ae8af..9725a28 100644 --- a/src/define/enum/bookEnum.ts +++ b/src/define/enum/bookEnum.ts @@ -282,3 +282,144 @@ export enum BookTagSelectType { // 标签 TAG = 'tag' } + + +/** + * 根据Key返回指定的后台任务类型的label + * @param key + * @returns + */ +export function GetBookBackTaskTypeLabel(key: string) { + switch (key) { + case BookBackTaskType.STORYBOARD: + return '分镜计算'; + case BookBackTaskType.SPLIT: + return '分割视频'; + case BookBackTaskType.AUDIO: + return '提取音频'; + case BookBackTaskType.RECOGNIZE: + return '识别字幕'; + case BookBackTaskType.FRAME: + return '抽帧'; + case BookBackTaskType.MJ_REVERSE: + return 'MJ反推'; + case BookBackTaskType.SD_REVERSE: + return 'SD反推'; + case BookBackTaskType.MJ_IMAGE: + return 'MJ生成图片'; + case BookBackTaskType.SD_IMAGE: + return 'SD生成图片'; + case BookBackTaskType.FLUX_FORGE_IMAGE: + return 'flux forge生成图片'; + case BookBackTaskType.FLUX_API_IMAGE: + return 'flux api生成图片'; + case BookBackTaskType.D3_IMAGE: + return 'D3生成图片'; + case BookBackTaskType.HD: + return '高清'; + case BookBackTaskType.COMPOSING: + return '合成视频'; + case BookBackTaskType.INFERENCE: + return '推理'; + case BookBackTaskType.TRANSLATE: + return '翻译'; + default: + return key; + } +} + +/** + * 根据Key返回指定的后台任务状态的label + * @param key + * @returns + */ +export function GetBookTaskDetailStatusLabel(key: string) { + switch (key) { + case BookTaskStatus.WAIT: + return '等待'; + case BookTaskStatus.STORYBOARD: + return '分镜计算中'; + case BookTaskStatus.STORYBOARD_FAIL: + return '分镜计算失败'; + case BookTaskStatus.STORYBOARD_DONE: + return '分镜计算完成'; + case BookTaskStatus.SPLIT: + return '分割视频中'; + case BookTaskStatus.SPLIT_FAIL: + return '分割视频失败'; + case BookTaskStatus.SPLIT_DONE: + return '分割视频完成'; + case BookTaskStatus.AUDIO: + return '提取音频中'; + case BookTaskStatus.AUDIO_FAIL: + return '提取音频失败'; + case BookTaskStatus.AUDIO_DONE: + return '提取音频完成'; + case BookTaskStatus.RECOGNIZE: + return '识别字幕中'; + case BookTaskStatus.RECOGNIZE_FAIL: + return '识别字幕失败'; + case BookTaskStatus.RECOGNIZE_DONE: + return '识别字幕完成'; + case BookTaskStatus.FRAME: + return '抽帧中'; + case BookTaskStatus.FRAME_FAIL: + return '抽帧失败'; + case BookTaskStatus.FRAME_DONE: + return '抽帧完成'; + case BookTaskStatus.REVERSE: + return '反推中'; + case BookTaskStatus.REVERSE_FAIL: + return '反推失败'; + case BookTaskStatus.REVERSE_DONE: + return '反推完成'; + case BookTaskStatus.IMAGE: + return '生成图片中'; + case BookTaskStatus.IMAGE_FAIL: + return '生成图片失败'; + case BookTaskStatus.IMAGE_DONE: + return '生成图片完成'; + case BookTaskStatus.HD: + return '高清中'; + case BookTaskStatus.HD_FAIL: + return '高清失败'; + case BookTaskStatus.HD_DONE: + return '高清完成'; + case BookTaskStatus.COMPOSING: + return '合成视频中'; + case BookTaskStatus.COMPOSING_FAIL: + return '合成视频失败'; + case BookTaskStatus.COMPOSING_DONE: + return '合成视频完成'; + case BookTaskStatus.DRAFT_DONE: + return '添加草稿完成'; + case BookTaskStatus.DRAFT_FAIL: + return '添加草稿失败'; + default: + return key; + } +} + +/** + * 根据Key返回指定的后台任务状态的label + * @param key + * @returns + */ +export function GetBookBackTaskStatusLabel(key: string) { + switch (key) { + case BookBackTaskStatus.WAIT: + return '等待'; + case BookBackTaskStatus.RUNNING: + return '运行中'; + case BookBackTaskStatus.PAUSE: + return '暂停'; + case BookBackTaskStatus.DONE: + return '完成'; + case BookBackTaskStatus.FAIL: + return '失败'; + case BookBackTaskStatus.RECONNECT: + return '重连'; + default: + return key; + } +} diff --git a/src/main/IPCEvent/bookIpc.ts b/src/main/IPCEvent/bookIpc.ts index cb3f9de..8cc9ddb 100644 --- a/src/main/IPCEvent/bookIpc.ts +++ b/src/main/IPCEvent/bookIpc.ts @@ -267,21 +267,22 @@ export function BookIpc() { async (event, id, operateBookType) => await bookImage.ResetGenerateImage(id, operateBookType) ) + // 获取图片的URL并且下载 ipcMain.handle( DEFINE_STRING.BOOK.GET_IMAGE_URL_AND_DOWNLOAD, async (event, id: string, operateBookType: OperateBookType, coverData: boolean) => await bookImage.GetImageUrlAndDownload(id, operateBookType, coverData) ) + /** 添加一键生图后台任务 */ + ipcMain.handle( + DEFINE_STRING.BOOK.GENERATE_ALL_TASK_IMAGE, + async (event, ids: string[], operateBookType: OperateBookType) => await bookImage.GenerateAllTaskImage(ids, operateBookType) + ) + //#endregion - // 一拆四,将一个任务拆分成四个任务,并且复制对应的图片 - ipcMain.handle( - DEFINE_STRING.BOOK.ONE_TO_FOUR_BOOK_TASK, - async (event, bookTaskDetailId) => await bookTask.OneToFourBookTask(bookTaskDetailId) - ) - //#region 小说相关 // 重置小说数据 @@ -294,6 +295,12 @@ export function BookIpc() { //#region 小说批次任务相关 + // 一拆四,将一个任务拆分成四个任务,并且复制对应的图片 + ipcMain.handle( + DEFINE_STRING.BOOK.ONE_TO_FOUR_BOOK_TASK, + async (event, bookTaskDetailId) => await bookTask.OneToFourBookTask(bookTaskDetailId) + ) + // 新建小说批次任务 ipcMain.handle(DEFINE_STRING.BOOK.ADD_NEW_BOOK_TASK, async (event, addBookTaskData) => await bookTask.AddNewBookTask(addBookTaskData)) diff --git a/src/main/IPCEvent/taskIpc.ts b/src/main/IPCEvent/taskIpc.ts index 647b744..dba3e57 100644 --- a/src/main/IPCEvent/taskIpc.ts +++ b/src/main/IPCEvent/taskIpc.ts @@ -6,6 +6,7 @@ import { BookBackTaskStatus, BookBackTaskType, TaskExecuteType } from "../../def let bookServiceBasic = new BookServiceBasic(); import BackTaskService from '../Service/task/backTaskService' +import { TaskModal } from "@/model/task"; const backTaskService = new BackTaskService() function TaskIpc() { @@ -38,8 +39,12 @@ function TaskIpc() { return errorMessage(`添加多个任务失败,错误信息如下:${error.toString()} `, 'TaskIpc_AddMultiBookBackTask') } }) + /** 获取指定状态的任务数量 */ ipcMain.handle(DEFINE_STRING.TASK.GET_ALL_STATUS_TASK_COUNT, async (event, value: BookBackTaskStatus[]) => await backTaskService.GetAllStatusTaskCount(value)) + + /** 获取后台任务的集合,分页 */ + ipcMain.handle(DEFINE_STRING.TASK.GET_BACK_TASK_COLLECTION, async (event, queryTaskCondition: TaskModal.QueryTaskCondition) => await backTaskService.GetBackTaskCollection(queryTaskCondition)) } export { TaskIpc } \ No newline at end of file diff --git a/src/main/IPCEvent/writingIpc.js b/src/main/IPCEvent/writingIpc.ts similarity index 87% rename from src/main/IPCEvent/writingIpc.js rename to src/main/IPCEvent/writingIpc.ts index 46c6dc5..349d013 100644 --- a/src/main/IPCEvent/writingIpc.js +++ b/src/main/IPCEvent/writingIpc.ts @@ -5,7 +5,9 @@ let writing = new Writing(global) import { WritingSetting } from '../setting/writeSetting' let writingSetting = new WritingSetting() import { SubtitleService } from '../Service/Subtitle/subtitleService' +import { BookPrompt } from '../Service/Book/bookPrompt' let subtitleService = new SubtitleService() +const bookPrompt = new BookPrompt(); function WritingIpc() { // 监听分镜时间的保存 @@ -71,5 +73,14 @@ function WritingIpc() { DEFINE_STRING.WRITE.ACTION_START, async (event, aiSetting, word) => await writing.ActionStart(aiSetting, word) ) + + //#region 文案洗稿相关 + + /** 生成洗稿后文案 */ + ipcMain.handle( + DEFINE_STRING.WRITE.GENERATE_AFTER_GPT_WORD, + async (event, ids: string[], isEmpty: boolean) => await bookPrompt.GenerateAfterGptWord(ids, isEmpty) + ) + //#endregion } export { WritingIpc } diff --git a/src/main/Service/Book/bookFrame.ts b/src/main/Service/Book/bookFrame.ts index e23eb9c..b683669 100644 --- a/src/main/Service/Book/bookFrame.ts +++ b/src/main/Service/Book/bookFrame.ts @@ -84,7 +84,7 @@ export class BookFrame { startTime: Math.ceil(element.startTime / 1000), endTime: Math.ceil(element.endTime / 1000), status: BookTaskStatus.WAIT, - word: element.word, + word: element.text, videoPath: element.videoPath, oldImage: path.relative(define.project_path, element.framePath), afterGpt: element.text, diff --git a/src/main/Service/Book/bookImage.ts b/src/main/Service/Book/bookImage.ts index 9a1d5ac..e9b87b6 100644 --- a/src/main/Service/Book/bookImage.ts +++ b/src/main/Service/Book/bookImage.ts @@ -1,4 +1,4 @@ -import { BookImageCategory, BookType, MJAction, OperateBookType } from "../../../define/enum/bookEnum"; +import { BookBackTaskType, BookImageCategory, BookType, MJAction, OperateBookType, TaskExecuteType } from "../../../define/enum/bookEnum"; import { GeneralResponse } from "../../../model/generalResponse"; import { errorMessage, successMessage } from "../../Public/generalTools"; import { Book } from "../../../model/book"; @@ -19,6 +19,13 @@ import { MJImageType } from "../../../define/enum/mjEnum"; import MJApi from '../MJ/mjApi' const execAsync = util.promisify(exec); +type TaskTemp = { + bookTaskId: string; + bookId: string; + imageCategory: BookImageCategory; + bookTaskDetail: Book.SelectBookTaskDetail; +} + /** * 小说图片执行的相关操作 */ @@ -87,8 +94,9 @@ export class BookImage { } //#endregion + //#region 开始高清图片任务 /** - * 开始高清图片,当前默认是四倍,后续可以调整 + * 开始高清图片 * @param id 要高清操作的ID * @param scale 高清的倍率 * @param operateBookType 操作的类型(支持BOOK,BOOKTASK两种) @@ -188,7 +196,9 @@ export class BookImage { } } + //#endregion + //#region 检查图片的大小 /** * 检查图片的大小(只要有一个图片的大小超过了 fileSize 的大小,就返回false) * @param id 需要操作的的ID,可以是整个小说的ID,也可以单个批次的ID @@ -251,6 +261,88 @@ export class BookImage { } } + //#endregion + + + /** + * 将指定的批次任务,添加里面的所有的分镜到出图任务中 + * @param ids 批次任务ID + * @param operateBookType 操作类型 + */ + async GenerateAllTaskImage(ids: string[], operateBookType: OperateBookType) { + try { + let bookTasks = [] as Book.SelectBookTask[]; + if (operateBookType == OperateBookType.ASSIGNBOOKTASK) { + for (let i = 0; i < ids.length; i++) { + const element = ids[i]; + let tempBookTask = await this.bookServiceBasic.GetBookTaskDataById(element); + bookTasks.push(tempBookTask) + } + } else { + throw new Error('不支持的操作类型,请检查') + } + if (bookTasks.length <= 0) { + throw new Error('没有找到需要操作的数据,请检查') + } + let taskTemp = [] as TaskTemp[] + for (let i = 0; i < bookTasks.length; i++) { + const element = bookTasks[i]; + let bookTaskDetails = await this.bookServiceBasic.GetBookTaskDetailData({ + bookTaskId: element.id + }, true) + if (bookTaskDetails.length <= 0) { + throw new Error(`批次任务 ${element.name} 没有找到分镜数据,请检查`) + } + let emptyPrompt = bookTaskDetails.filter(item => isEmpty(item.prompt)); + if (emptyPrompt.length > 0) { + throw new Error(`批次任务 ${element.name} 的分镜 ${emptyPrompt[0].name} 没有找到出图提示词,请检查`) + } + + taskTemp.push(...bookTaskDetails.map((item) => { + return { + bookTaskId: element.id, + bookId: element.bookId, + imageCategory: element.imageCategory, + bookTaskDetail: item + } + })) + } + + // 检查当前小说批次任务是不是又出图方式 + // 判断是不是所有的小说批次都有小说分镜 + // 判断每个分镜是不是都有出图提示词 + // 添加出图任务 + for (let i = 0; i < taskTemp.length; i++) { + const element = taskTemp[i]; + // 添加任务 + let taskType = BookBackTaskType.MJ_IMAGE + let responseMessageName = DEFINE_STRING.BOOK.MJ_IMAGE_GENERATE_RETURN; + + if (element.imageCategory == BookImageCategory.MJ) { + taskType = BookBackTaskType.MJ_IMAGE; + responseMessageName = DEFINE_STRING.BOOK.MJ_IMAGE_GENERATE_RETURN; + } else if (element.imageCategory == BookImageCategory.SD) { + taskType = BookBackTaskType.SD_IMAGE; + responseMessageName = DEFINE_STRING.BOOK.SD_IMAGE_GENERATE_RETURN; + } else if (element.imageCategory == BookImageCategory.D3) { + taskType = BookBackTaskType.D3_IMAGE; + responseMessageName = DEFINE_STRING.BOOK.D3_IMAGE_GENERATE_RETURN; + } else if (element.imageCategory == BookImageCategory.FLUX_FORGE) { + taskType = BookBackTaskType.FLUX_FORGE_IMAGE; + responseMessageName = DEFINE_STRING.BOOK.FLUX_FORGE_IMAGE_GENERATE_RETURN + } else if (element.imageCategory == BookImageCategory.FLUX_API) { + taskType = BookBackTaskType.FLUX_API_IMAGE; + responseMessageName = DEFINE_STRING.BOOK.FLUX_API_IMAGE_GENERATE_RETURN; + } else { + throw new Error('未知的出图类型') + } + await this.bookServiceBasic.AddBookBackTask(element.bookId, taskType, TaskExecuteType.AUTO, element.bookTaskId, element.bookTaskDetail.id, responseMessageName) + } + return successMessage(null, "添加所选的的生图任务成功", "BookImage_GenerateAllTaskImage") + } catch (error) { + return errorMessage("添加所有的生图任务失败,失败信息如下:" + error.toString(), "BookImage_GenerateAllTaskImage"); + } + } /** * 生成所有的图片,这个方法主要是分流,根据批次生成的方式,添加对应的数据 @@ -277,6 +369,7 @@ export class BookImage { } } + //#region 对图片进行锁定或者是解锁操作 /** * 对图片进行锁定或者是解锁操作 * @param id 要执行的id @@ -327,7 +420,9 @@ export class BookImage { return errorMessage("图片执行锁定或解锁失败,失败信息如下:" + error.toString(), "BookImage_ImageLockOperation") } } + //#endregion + //#region 下载指定的图片地址并且分割 /** * 下载指定的图片地址并且分割 * @param bookTaskDetailId 对应的分镜ID @@ -416,8 +511,9 @@ export class BookImage { } } } + //#endregion - + //#region 获取指定的图片链接 ,然后下载图片 /** * 获取指定的图片链接 ,然后下载图片 * @param id @@ -486,4 +582,5 @@ export class BookImage { return errorMessage('获取图片链接并且下载失败,错误信息如下:' + error.message, 'BookImage_GetImageUrlAndDownload') } } + //#endregion } diff --git a/src/main/Service/Book/bookPrompt.ts b/src/main/Service/Book/bookPrompt.ts index 2f58fec..5dcd05d 100644 --- a/src/main/Service/Book/bookPrompt.ts +++ b/src/main/Service/Book/bookPrompt.ts @@ -13,6 +13,7 @@ import path from 'path' import readline from 'readline'; import { define } from "../../../define/define"; import { ValidateJson } from "../../../define/Tools/validate"; +import { SendMessageToRenderer } from "../globalService"; export class BookPrompt { @@ -290,6 +291,67 @@ export class BookPrompt { } } + /** + * 生成洗稿后文案 + * @param ids 要洗稿的分镜的ID + * @param isEmpty 是不是只洗空行 + * 先删除,在洗稿 + */ + async GenerateAfterGptWord(ids: string[], empty: boolean) { + try { + if (ids.length <= 0) { + throw new Error('没有传入要洗稿的数据ID') + } + let bookTaskDetails = [] as Book.SelectBookTaskDetail[] + for (let i = 0; i < ids.length; i++) { + const element = ids[i]; + let bookTaskDetail = await this.bookServiceBasic.GetBookTaskDetailDataById(element) + bookTaskDetails.push(bookTaskDetail); + } + if (empty) { + bookTaskDetails = bookTaskDetails.filter(item => isEmpty(item.gptPrompt)) + } + if (bookTaskDetails.length <= 0) { + throw new Error('没有找到要洗稿的数据') + } + + // 开始删除数据 + for (let i = 0; i < bookTaskDetails.length; i++) { + const element = bookTaskDetails[i]; + await this.bookServiceBasic.UpdateBookTaskDetail(element.id, { + afterGpt: undefined + }) + + SendMessageToRenderer({ + code: 1, + message: "删除洗稿后文案成功", + id: element.id, + data: "" + }, DEFINE_STRING.WRITE.GENERATE_AFTER_GPT_WORD_RESPONSE) + } + + for (let i = 0; i < bookTaskDetails.length; i++) { + const element = bookTaskDetails[i]; + + // 开始洗稿 + let content = await this.gptService.GenerateAfterGptWordByGPT(element.word); + // 修改数据 + await this.bookServiceBasic.UpdateBookTaskDetail(element.id, { + afterGpt: content + }) + SendMessageToRenderer({ + code: 1, + message: "文案洗稿成功", + id: element.id, + data: content + }, DEFINE_STRING.WRITE.GENERATE_AFTER_GPT_WORD_RESPONSE) + } + return successMessage(null, "洗稿完成", "ReverseBook_GenerateAfterGptWord") + } catch (error) { + return errorMessage("生成洗稿后文案失败,错误信息如下:" + error.message, "ReverseBook_GenerateAfterGptWord") + } + } + //#endregion //#region 原创的提示词相关 diff --git a/src/main/Service/Book/bookTask.ts b/src/main/Service/Book/bookTask.ts index c993f0a..59753ac 100644 --- a/src/main/Service/Book/bookTask.ts +++ b/src/main/Service/Book/bookTask.ts @@ -1,5 +1,5 @@ import { CheckFileOrDirExist, CheckFolderExistsOrCreate, CopyFileOrFolder, DeleteFolderAllFile } from "../../../define/Tools/file"; -import { AddBookTaskCopyData, BookImageCategory, BookTaskStatus, CopyImageType, OperateBookType } from "../../../define/enum/bookEnum"; +import { AddBookTaskCopyData, BookImageCategory, BookTaskStatus, BookType, CopyImageType, OperateBookType } from "../../../define/enum/bookEnum"; import { errorMessage, successMessage } from "../../Public/generalTools"; import { Book } from "../../../model/book"; import path from 'path' @@ -119,9 +119,23 @@ export class BookTask { * @param no 当前的编号 * @returns */ - CopyBookTaskBaseData(bookTask: Book.SelectBookTask, addNewBookTask: Book.AddBookTask, no: number): Book.SelectBookTask { + async CopyBookTaskBaseData(bookTask: Book.SelectBookTask, addNewBookTask: Book.AddBookTask, no: number): Promise { let name = 'output_' + no.toString().padStart(5, '0'); let imageFolder = path.join(define.project_path, `${bookTask.bookId}/tmp/${name}`); + let imageCategory = global.config.defaultImageMode; + let book = await this.bookServiceBasic.GetBookDataById(bookTask.bookId) + if (!isEmpty(bookTask.imageCategory)) { + imageCategory = bookTask.imageCategory; + } else { + if (book.type == BookType.MJ_REVERSE) { + imageCategory = BookImageCategory.MJ + } else if (book.type == BookType.SD_REVERSE) { + imageCategory = BookImageCategory.SD + } else if (book.type == BookType.ORIGINAL) { + imageCategory = global.config.defaultImageMode + } + } + let newBookTask = { id: uuidv4(), bookId: bookTask.bookId, @@ -142,7 +156,7 @@ export class BookTask { videoConfig: bookTask.videoConfig ??= undefined, prefixPrompt: addNewBookTask.prefixPrompt ??= undefined, suffixPrompt: addNewBookTask.suffixPrompt ?? undefined, - imageCategory: bookTask.imageCategory ??= BookImageCategory.MJ, + imageCategory: imageCategory, subImageFolder: [], draftSrtStyle: undefined, backgroundMusic: bookTask.backgroundMusic ??= undefined, @@ -164,7 +178,7 @@ export class BookTask { * @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 newBookTask = await this.CopyBookTaskBaseData(bookTask, addNewBookTask, no) let newBookTaskDetails = await this.CopyBookTaskDetailBaseData(bookTask, newBookTask, bookTaskDetails, addNewBookTask) return { newBookTask: newBookTask, @@ -199,7 +213,7 @@ export class BookTask { bookTasks.push(newBookTask) bookTaskDetail.push(...newBookTaskDetails) } else { - let newBookTask = this.CopyBookTaskBaseData({ bookId: addNewBookTask.selectBookId }, addNewBookTask, maxNo + i) + let newBookTask = await this.CopyBookTaskBaseData({ bookId: addNewBookTask.selectBookId }, addNewBookTask, maxNo + i) bookTasks.push(newBookTask); } } diff --git a/src/main/Service/Flux/flux.ts b/src/main/Service/Flux/flux.ts index 795aa67..744fd66 100644 --- a/src/main/Service/Flux/flux.ts +++ b/src/main/Service/Flux/flux.ts @@ -160,6 +160,7 @@ export class FluxOpt { }) } catch (error) { let errorMsg = "FLUX FORGE 生成图片失败,错误信息如下:" + error.toString() + await this.bookServiceBasic.UpdateBookTaskDetailMjMessage(task.bookTaskDetailId, { mjApiUrl: sdSetting ? sdSetting.setting.webui_api_url : "", progress: 0, @@ -171,6 +172,11 @@ export class FluxOpt { status: "error", message: errorMsg }) + await this.bookServiceBasic.UpdateTaskStatus({ + id: task.id, + status: BookBackTaskStatus.FAIL, + errorMessage: errorMsg + }); global.newWindow[0].win.webContents.send(task.messageName, { code: 0, diff --git a/src/main/Service/GPT/gpt.ts b/src/main/Service/GPT/gpt.ts index 93cb623..a7bf2f0 100644 --- a/src/main/Service/GPT/gpt.ts +++ b/src/main/Service/GPT/gpt.ts @@ -318,4 +318,65 @@ export class GptService { } } //#endregion + + //#region 聚合推文 文案洗稿 + + /** + * 聚合推文的文案洗稿 + * @param word 之前的提示词 + * @returns + */ + async GenerateAfterGptWordByGPT(word: string,) { + try { + let prompt = `## - Role: 文案去重 + : 用户需要将小说的文本进行细致的分析,并将原文文本内容进行同义转换,换一种描述,不要改变文案的结构,不改变原来的意思,要求参考原文案的写作风格,不要书面词,要尽可能的口语化输出,但不要太过于运用东北话的一些词语,比如“瞅“,“爱咋咋地“,“咋地“,等类似的词语。 + + ## 要求 + 严格执行以下要求进行小说内容的段落分行: + 1.严格要求根据原文进行同义输出:不允许将原文一行内容所表达的内容分成多行来输出。 + 2.仅对原文中的话换一种描述,但不能完全脱离这句话的意思。 + 3.输出的内容每一句话与原文的字数尽量保持一致,严格遵守不能远少于原文每个句子的字数,再次强调不能少于原文每句话的字数。 + 4.输出的文案的相似度必须低于百分之五十 + + ## 示例 + 输入1:这个世界的女人每结一次婚,就会在手上戴一枚戒指,手上的戒指越多,那么就代表着她的老公越多。 + 输出1: 雌性每洞房一次,就会带上一枚戒指,手上的戒指越多,就代表被她宠幸的雄性越多。 + + 输入2:我重生到末日前的第一件事,就是从学校后山徒手抓了一条黑曼巴蛇,并丧心病狂的将之活生生吞下。 + 输出2: 我重生后的第一件事就是生吞羊辣子,然后挖出自己双眼,再割掉自己头颅,众人以为我是疯子得了精神病,但只有我知道我是重生者。 + + 输入3:我明明觉醒了SSS级异能紫霄神器,却被全校所有人嘲笑,原因我觉醒的异能在别人眼里只是一个人形电击棒。 + 输出3: 我明明觉醒了sss级暗黑系异能,却被超神学院五大院长同时除名,就连唯一的妹妹夜哭着劝我放弃修炼。 + + 输入3:我的奶奶觉醒级凶兽烈焰巨龙,我的爷爷觉醒级凶兽暴虐滔天,而我却在这个觉醒凶兽的世界觉醒了个破烂石板 + 输出3: 我爷爷觉醒了s级凶兽黄金朱厌,奶奶觉醒了ss级神兽泰坦巨猿,可我却在这个人人觉醒泰坦战士的世界 + + 输入5: 一个先天满属性的天才却被称为废物,只因我在转职时觉醒了史上最垃圾的道士职业,就连重金挖我来的学校也将我当成了耻辱 + 输出5: 一个先天满属性的天才却被称为废物,只因我在转职时觉醒了史上最垃圾的道士职业,就连重金挖我来的学校也将我当成了耻辱 + + ## + 最后再强调,你作为一位优秀的小说改写者,每一次输出都要严格遵守<要求>,一步一步慢慢思考,参考<示例>的格式,一步一步思考,按顺序执行<要求>,不需要做解释说明,只呈现最后的结果,严格要求理解全文之后再进行分组,无需要在输出用户,直接输出Ai部分。 + `; + + let message = [ + { + "role": "system", + "content": prompt, + }, { + "role": "user", + "content": word + } + ] + + // 开始请求,这个默认是使用的是LAI API的gpt-4o-mini + let content = await RetryWithBackoff(async () => { + return await this.FetchGpt(message, null, null, null); + }, 5, 2000) + return content; + + } catch (error) { + throw error + } + } + //#endregion } \ No newline at end of file diff --git a/src/main/Service/MJ/mj.ts b/src/main/Service/MJ/mj.ts index 7c95699..6d68be0 100644 --- a/src/main/Service/MJ/mj.ts +++ b/src/main/Service/MJ/mj.ts @@ -660,7 +660,7 @@ export class MJOpt { data: task_res }, task.messageName); // 当获取的图片的进度小于100的时候,等待5秒继续监听 - await new Promise(resolve => setTimeout(resolve, 5000)); + await new Promise(resolve => setTimeout(resolve, 9000)); } catch (error) { throw error; } diff --git a/src/main/Service/SD/sd.ts b/src/main/Service/SD/sd.ts index 029c98f..226cf35 100644 --- a/src/main/Service/SD/sd.ts +++ b/src/main/Service/SD/sd.ts @@ -369,7 +369,6 @@ export class SDOpt { action: MJAction.IMAGINE, status: "error", message: errorMsg, - id: task.bookTaskDetailId }) await this.bookServiceBasic.UpdateBookTaskDetail(task.bookTaskDetailId, { diff --git a/src/main/Service/ServiceBasic/bookBackTaskServiceBasic.ts b/src/main/Service/ServiceBasic/bookBackTaskServiceBasic.ts index 95ac3cb..8be1ca4 100644 --- a/src/main/Service/ServiceBasic/bookBackTaskServiceBasic.ts +++ b/src/main/Service/ServiceBasic/bookBackTaskServiceBasic.ts @@ -1,6 +1,8 @@ import { BookBackTaskStatus, BookBackTaskType, TaskExecuteType } from "../../../define/enum/bookEnum"; import { BookBackTaskListService } from "../../../define/db/service/Book/bookBackTaskListService"; import { Book } from "../../../model/book"; +import { TaskModal } from "@/model/task"; +import { cloneDeep, isEmpty } from "lodash"; export default class BookBackTaskServiceBasic { bookBackTaskListService: BookBackTaskListService @@ -57,6 +59,29 @@ export default class BookBackTaskServiceBasic { this.bookBackTaskListService.UpdateTaskStatus(bookBackTask) } + /** + * 设置指定消息名称的任务为失败,并设置错误消息 + * @param messageName + * @param errorMessage + */ + async SetMessageNameTaskToFail(messageName: string, errorMessage: string) { + console.log(messageName, errorMessage) + await this.InitService(); + let tasks = this.bookBackTaskListService.realm.objects('BookBackTaskList').filtered('messageName == $0 ', messageName) + tasks = tasks.filtered('status != $0', BookBackTaskStatus.DONE); + tasks = tasks.filtered('status != $0', BookBackTaskStatus.FAIL); + let ids = tasks.map((item) => { + return item.id + }) + this.bookBackTaskListService.transaction(() => { + for (let i = 0; i < ids.length; i++) { + let task = this.bookBackTaskListService.realm.objectForPrimaryKey('BookBackTaskList', ids[i]); + task.status = BookBackTaskStatus.FAIL + task.errorMessage = errorMessage + } + }) + } + /** * 丢弃等待中和重新连接的任务 */ @@ -75,4 +100,86 @@ export default class BookBackTaskServiceBasic { } }) } + /** + * 查询指定的条件的后台任务集合 + * @param queryTaskCondition + */ + GetBackTaskCollection(queryTaskCondition: TaskModal.QueryTaskCondition): TaskModal.TaskCollection { + let tasks = this.bookBackTaskListService.realm.objects('BookBackTaskList'); + if (!isEmpty(queryTaskCondition.bookName)) { + let book = this.bookBackTaskListService.realm.objects('Book').filtered('name CONTAINS[c] $0', queryTaskCondition.bookName); + let ids = [] as string[] + if (book.length > 0) { + ids = book.map((item) => { + return item.id as string + }) + } + tasks = tasks.filtered('bookId in $0', ids); + } + if (!isEmpty(queryTaskCondition.bookTaskName)) { + let bookTask = this.bookBackTaskListService.realm.objects('BookTask').filtered('name CONTAINS[c] $0', queryTaskCondition.bookTaskName); + let ids = [] as string[] + if (bookTask.length > 0) { + ids = bookTask.map((item) => { + return item.id as string + }) + } + tasks = tasks.filtered('bookTaskId in $0', ids); + } + if (!isEmpty(queryTaskCondition.bookTaskDetailName)) { + let bookTaskDetail = this.bookBackTaskListService.realm.objects('BookTaskDetail').filtered('name CONTAINS[c] $0', queryTaskCondition.bookTaskDetailName); + let ids = [] as string[] + if (bookTaskDetail.length > 0) { + ids = bookTaskDetail.map((item) => { + return item.id as string + }) + } + tasks = tasks.filtered('bookTaskDetailId in $0', ids); + } + if (!isEmpty(queryTaskCondition.taskName)) { + tasks = tasks.filtered('name CONTAINS[c] $0', queryTaskCondition.taskName); + } + if (!isEmpty(queryTaskCondition.taskType)) { + tasks = tasks.filtered('type == $0', queryTaskCondition.taskType); + } + if (!isEmpty(queryTaskCondition.taskStatus)) { + tasks = tasks.filtered('status == $0', queryTaskCondition.taskStatus); + } + if (!isEmpty(queryTaskCondition.taskErrorMessage)) { + tasks = tasks.filtered('errorMessage CONTAINS[c] $0', queryTaskCondition.taskErrorMessage); + } + let count = tasks.length + tasks = tasks.sorted('createTime', true) + let task = tasks.slice((queryTaskCondition.page - 1) * queryTaskCondition.pageSize, queryTaskCondition.page * queryTaskCondition.pageSize) as TaskModal.BackTaskCollection[]; + + let taskList = Array.from(task).map((item) => { + let resObj = { + ...item, + } as TaskModal.BackTaskCollection + return cloneDeep(resObj) + }) + + for (let i = 0; i < taskList.length; i++) { + const element = taskList[i]; + let book = this.bookBackTaskListService.realm.objectForPrimaryKey('Book', element.bookId) + if (book) { + element.bookName = book.name as string; + } + let bookTask = this.bookBackTaskListService.realm.objectForPrimaryKey('BookTask', element.bookTaskId); + if (bookTask) { + element.bookTaskName = bookTask.name as string; + } + let bookTaskDetail = this.bookBackTaskListService.realm.objectForPrimaryKey('BookTaskDetail', element.bookTaskDetailId); + if (bookTaskDetail) { + element.bookTaskDetailName = bookTaskDetail.name as string; + } + } + + return { + page: queryTaskCondition.page, + pageSize: queryTaskCondition.pageSize, + count: count, + data: taskList + } + } } \ No newline at end of file diff --git a/src/main/Service/ServiceBasic/bookServiceBasic.ts b/src/main/Service/ServiceBasic/bookServiceBasic.ts index 2122454..de9477a 100644 --- a/src/main/Service/ServiceBasic/bookServiceBasic.ts +++ b/src/main/Service/ServiceBasic/bookServiceBasic.ts @@ -7,6 +7,7 @@ import BookBackTaskServiceBasic from "./bookBackTaskServiceBasic"; import { BookBasic } from "./bookBasic"; import BookTaskServiceBasic from "./bookTaskServiceBasic"; import BookTaskDetailServiceBasic from "./bookTaskDetailServiceBasic"; +import { TaskModal } from "@/model/task"; /** * 该类中封装了小说的基础服务,主要是检查每次引入对应的服务类 @@ -90,6 +91,10 @@ class BookServiceBasic { UpdateTaskStatus = async (bookBackTask: Book.UpdateBookTaskListStatus) => await this.bookBackTaskServiceBasic.UpdateTaskStatus(bookBackTask); GiveUpNotStartBackTask = async () => await this.bookBackTaskServiceBasic.GiveUpNotStartBackTask(); + /** 设置指定的消息名称的任务状态为错误及错误信息 */ + SetMessageNameTaskToFail = async (massageName: string, errorMessage: string) => await this.bookBackTaskServiceBasic.SetMessageNameTaskToFail(massageName, errorMessage); + /** 查询指定的条件的后台任务 */ + GetBackTaskCollection = async (queryTaskCondition: TaskModal.QueryTaskCondition) => await this.bookBackTaskServiceBasic.GetBackTaskCollection(queryTaskCondition); //#endregion } export { BookServiceBasic }; diff --git a/src/main/Service/Subtitle/subtitleService.ts b/src/main/Service/Subtitle/subtitleService.ts index e400eaa..e7b9525 100644 --- a/src/main/Service/Subtitle/subtitleService.ts +++ b/src/main/Service/Subtitle/subtitleService.ts @@ -179,6 +179,9 @@ export class SubtitleService { default: throw new Error("未知的识别字幕模式") } + if (res.code == 0) { + throw new Error(res.message) + } if (operateBookType == OperateBookType.BOOKTASKDETAIL) { let bookTaskDetail = await this.bookServiceBasic.GetBookTaskDetailDataById(bookTaskId) return successMessage(bookTaskDetail.afterGpt, "获取文案成功", "ReverseBook_GetCopywriting") diff --git a/src/main/Service/system/electronInterface.ts b/src/main/Service/system/electronInterface.ts index 3151f5a..864ee67 100644 --- a/src/main/Service/system/electronInterface.ts +++ b/src/main/Service/system/electronInterface.ts @@ -32,7 +32,7 @@ export default class ElectronInterface { /** * 打开对应的文件夹 * @param params - * @returns + * @returns */ public async OpenFolder(params: OpenFolderParams) { try { diff --git a/src/main/Service/task/backTaskService.ts b/src/main/Service/task/backTaskService.ts index 6c354c8..a2644f0 100644 --- a/src/main/Service/task/backTaskService.ts +++ b/src/main/Service/task/backTaskService.ts @@ -1,3 +1,4 @@ +import { TaskModal } from "@/model/task"; import { BookBackTaskStatus } from "../../../define/enum/bookEnum"; import { GeneralResponse } from "../../../model/generalResponse"; import { errorMessage, successMessage } from "../../Public/generalTools"; @@ -10,6 +11,7 @@ export default class BackTaskService { this.bookServiceBasic = new BookServiceBasic() } + //#region 获取指定状态的后台任务数量 /** * 获取指定状态的后台任务数量 */ @@ -23,6 +25,9 @@ export default class BackTaskService { } } + //#endregion + + //#region 启动后台任务 /** * 启动后台任务,判断是不是丢弃,要是丢弃的话,先将等待任务全部丢弃 * @param isGiveUp @@ -44,4 +49,14 @@ export default class BackTaskService { return errorMessage('启动后台任务失败', 'BackTaskService_StartBackTask') } } + //#endregion + + public async GetBackTaskCollection(queryTaskCondition: TaskModal.QueryTaskCondition) { + try { + let res = await this.bookServiceBasic.GetBackTaskCollection(queryTaskCondition); + return successMessage(res, '获取后台任务集合成功', 'BackTaskService_GetBackTaskCollection'); + } catch (error) { + return errorMessage('获取后台任务集合失败,失败信息如下:' + error.toString(), 'BackTaskService_GetBackTaskCollection') + } + } } \ No newline at end of file diff --git a/src/main/Service/task/taskManage.ts b/src/main/Service/task/taskManage.ts index 1bfaca4..f67b71e 100644 --- a/src/main/Service/task/taskManage.ts +++ b/src/main/Service/task/taskManage.ts @@ -14,6 +14,8 @@ import { AsyncQueue } from '../../quene' import { SoftWareServiceBasic } from '../ServiceBasic/softwareServiceBasic' import { MJSetting } from '../../../model/Setting/mjSetting' import { BookVideo } from '../Book/bookVideo' +import { BookServiceBasic } from '../ServiceBasic/bookServiceBasic' +import { TaskModal } from '@/model/task' export class TaskManager { isExecuting: boolean = false; @@ -26,6 +28,7 @@ export class TaskManager { eventListeners: Record = {}; softWareServiceBasic: SoftWareServiceBasic bookVideo: BookVideo + bookServiceBasic: BookServiceBasic mjSetting: MJSetting.MjSetting spaceTime: number = 5000; @@ -48,6 +51,7 @@ export class TaskManager { this.d3Opt = new D3Opt() this.softWareServiceBasic = new SoftWareServiceBasic(); this.fluxOpt = new FluxOpt() + this.bookServiceBasic = new BookServiceBasic(); } async InitService(getMJsetting = false) { @@ -63,6 +67,16 @@ export class TaskManager { } } + /** + * 执行后台任务的时候,当批次任务出错的时候,会删除子任务, + * 这边将子任务设置为失败状态,用于管理 + * @param messageName + */ + async ErrorTaskHandle(messageName: string, ems: string, bsb: BookServiceBasic) { + debugger + bsb.SetMessageNameTaskToFail(messageName, ems); + } + // 初始化事件监听方法 InitListeners() { if (this.isListening) return; // 如果已经在监听,直接返回 @@ -201,7 +215,6 @@ export class TaskManager { global.requestQuene.enqueue(async () => { await this.basicReverse.ExtractSubtitlesData(task); }, `${batch}_${task.id}`, batch) - } /** @@ -209,10 +222,10 @@ export class TaskManager { * @param task */ AddSingleReversePrompt(task: TaskModal.Task): void { - let batch = DEFINE_STRING.BOOK.ADD_REVERSE_PROMPT; + let batch = task.messageName; global.requestQuene.enqueue(async () => { await this.reverseBook.SingleReversePrompt(task); - }, `${batch}_${task.id}`, batch) + }, `${batch}_${task.id}`, batch, `${batch}_${task.id}_${new Date().getTime()}`, this.bookServiceBasic.SetMessageNameTaskToFail); } /** @@ -221,10 +234,10 @@ export class TaskManager { */ async AddImageMJImage(task: TaskModal.Task) { // 判断是不是MJ的任务 - let batch = DEFINE_STRING.MJ.MJ_IMAGE; + let batch = task.messageName; global.mjQueue.enqueue(async () => { await this.mjOpt.MJImagine(task); - }, `${batch}_${task.id}`, batch, `${batch}_${task.id}_${new Date().getTime()}`); + }, `${batch}_${task.id}`, batch, `${batch}_${task.id}_${new Date().getTime()}`, this.bookServiceBasic.SetMessageNameTaskToFail); } @@ -233,10 +246,10 @@ export class TaskManager { * @param task */ async AddSDImage(task: TaskModal.Task) { - let batch = DEFINE_STRING.SD.TXT2IMG + let batch = task.messageName; global.requestQuene.enqueue(async () => { await this.sdOpt.SDImageGenerate(task); - }, `${batch}_${task.id}`, batch, `${batch}_${task.id}_${new Date().getTime()}`) + }, `${batch}_${task.id}`, batch, `${batch}_${task.id}_${new Date().getTime()}`, this.bookServiceBasic.SetMessageNameTaskToFail) } /** @@ -251,7 +264,7 @@ export class TaskManager { let batch = task.messageName; global.requestQuene.enqueue(async () => { await this.d3Opt.D3ImageGenerate(task); - }, `${batch}_${task.id}`, batch, `${batch}_${task.id}_${new Date().getTime()}`) + }, `${batch}_${task.id}`, batch, `${batch}_${task.id}_${new Date().getTime()}`, this.bookServiceBasic.SetMessageNameTaskToFail); } /** 添加生成视频的后台任务 */ @@ -260,7 +273,7 @@ export class TaskManager { global.requestQuene.enqueue(async () => { this.bookVideo = new BookVideo(); await this.bookVideo.GenerateVideo(task); - }, `${batch}_${task.id}`, batch, `${batch}_${task.id}_${new Date().getTime()}`) + }, `${batch}_${task.id}`, batch, `${batch}_${task.id}_${new Date().getTime()}`, this.bookServiceBasic.SetMessageNameTaskToFail) } @@ -269,10 +282,10 @@ export class TaskManager { * @param task */ async AddFluxForgeImage(task: TaskModal.Task) { - let batch = task.messageName + let batch = task.messageName; global.requestQuene.enqueue(async () => { await this.fluxOpt.FluxForgeImage(task); - }, `${batch}_${task.id}`, batch, `${batch}_${task.id}_${new Date().getTime()}`) + }, `${batch}_${task.id}`, batch, `${batch}_${task.id}_${new Date().getTime()}`, this.bookServiceBasic.SetMessageNameTaskToFail) } /** @@ -283,7 +296,7 @@ export class TaskManager { let batch = task.messageName; global.requestQuene.enqueue(async () => { await this.fluxOpt.FluxAPIImage(task); - }, `${batch}_${task.id}`, batch, `${batch}_${task.id}_${new Date().getTime()}`) + }, `${batch}_${task.id}`, batch, `${batch}_${task.id}_${new Date().getTime()}`, this.bookServiceBasic.SetMessageNameTaskToFail) } /** diff --git a/src/main/func.js b/src/main/func.js index 52a5a5e..98eaca2 100644 --- a/src/main/func.js +++ b/src/main/func.js @@ -683,6 +683,8 @@ async function SaveSDConfig(value) { sd_config.webui.width = value.width ? value.width : sd_config.webui.width sd_config.webui.height = value.height ? value.height : sd_config.webui.height sd_config.webui.cfg_scale = value.cfg_scale ? value.cfg_scale : sd_config.webui.cfg_scale + sd_config.webui.batch_size = value.batch_size ? value.batch_size : sd_config.webui.batch_size + sd_config.setting.seed = value.seed ? value.seed : sd_config.setting.seed sd_config.webui.adetailer = value.hasOwnProperty('adetailer') ? value.adetailer : sd_config.webui.adetailer @@ -695,6 +697,7 @@ async function SaveSDConfig(value) { } else { sd_config.flux.model = value.flux_model ? value.flux_model : FLxuAPIImageType.FLUX } + await fspromises.writeFile(define.sd_setting, JSON.stringify(sd_config)) return { code: 1, diff --git a/src/main/quene.js b/src/main/quene.js index 12afb7b..421fffc 100644 --- a/src/main/quene.js +++ b/src/main/quene.js @@ -19,7 +19,7 @@ export class AsyncQueue { this.taskProgress = [] } - async enqueue(task, taskId, batchId, subBatchId = 'default') { + async enqueue(task, taskId, batchId, subBatchId = 'default', errorFunc = null) { if (batchId && batchId != DEFINE_STRING.QUEUE_BATCH.IMAGE_SAVE_TO_OTHER_FOLDER) { // 判断当前的任务是否已经存在,存在则不添加 let index = this.tasks.findIndex( @@ -44,7 +44,7 @@ export class AsyncQueue { this.batchCompletion[batchId].remaining++ this.batchCompletion[batchId].subBatches[subBatchId].remaining++ - this.tasks.push({ task, taskId, batchId, subBatchId }) + this.tasks.push({ task, taskId, batchId, subBatchId, errorFunc }) if (!this.manualMode) { await this.process() } @@ -95,7 +95,7 @@ export class AsyncQueue { ? this.taskProgress.length < task_count : this.currentConcurrency < this.concurrencyLimit) ) { - const { task, taskId, batchId, subBatchId } = this.tasks.shift() + const { task, taskId, batchId, subBatchId, errorFunc } = this.tasks.shift() this.currentConcurrency++ Promise.resolve(task()) .then(() => { @@ -132,6 +132,13 @@ export class AsyncQueue { // console.error(`Task ${taskId} failed after ${maxRetryCount} retries`); this.currentConcurrency-- this.handleTaskCompletion(batchId, subBatchId, { taskId, error }) + // 执行传入的错误后执行方法,一般用于修改后台任务或者是批量提示 + if (errorFunc) { + errorFunc( + batchId, + `任务 ${taskId} 执行失败,错误信息:${error.toString()},开始清理整个批次,批次ID:${batchId}` + ) + } if (!this.manualMode) { this.process() } @@ -151,6 +158,13 @@ export class AsyncQueue { // 出现报错。直接停掉当前对应的的子批次 this.currentConcurrency-- this.handleTaskCompletion(batchId, subBatchId, { taskId, error }) + // 执行传入的错误后执行方法,一般用于修改后台任务或者是批量提示 + if (errorFunc) { + errorFunc( + batchId, + `任务 ${taskId} 执行失败,错误信息:${error.toString()},开始清理整个批次,批次ID:${batchId}` + ) + } if (!this.manualMode) { this.process() } else { diff --git a/src/model/Setting/softwareSetting.d.ts b/src/model/Setting/softwareSetting.d.ts index 3c769db..09bc433 100644 --- a/src/model/Setting/softwareSetting.d.ts +++ b/src/model/Setting/softwareSetting.d.ts @@ -40,5 +40,6 @@ declare namespace SoftwareSettingModel { gpt_key: string = undefined // GPT KEY, laiApiSelect: string = undefined // LaiAPI选择 hdScale: number = 2 // HD缩放 + defaultImageMode: BookImageCategory = 'mj' // 默认图片模式 } } \ No newline at end of file diff --git a/src/model/task.d.ts b/src/model/task.d.ts index 9d73c32..8012e87 100644 --- a/src/model/task.d.ts +++ b/src/model/task.d.ts @@ -1,6 +1,7 @@ +import { BookBackTaskStatus, BookBackTaskType, TaskExecuteType } from "@/define/enum/bookEnum" declare namespace TaskModal { - type Task = { + interface Task { id?: string bookId?: string bookTaskId?: string @@ -16,4 +17,32 @@ declare namespace TaskModal { endTime?: number, messageName?: string } + + interface TaskCondition { + bookName?: string, + bookTaskName?: string, + bookTaskDetailName?: string, + taskName?: string, + taskType?: BookBackTaskType, + taskStatus?: BookBackTaskStatus, + taskErrorMessage?: string + } + + interface QueryTaskCondition extends TaskCondition { + page: number, + pageSize: number + } + + interface BackTaskCollection extends Task { + bookName?: string + bookTaskName?: string + bookTaskDetailName?: string + } + + type TaskCollection = { + page: number, + pageSize: number, + count: number, + data: BackTaskCollection[] + } } \ No newline at end of file diff --git a/src/preload/book.ts b/src/preload/book.ts index 81a4e35..16c8ef6 100644 --- a/src/preload/book.ts +++ b/src/preload/book.ts @@ -179,6 +179,9 @@ const book = { GetImageUrlAndDownload: async (id: string, operateBookType: OperateBookType, coverData: boolean) => await ipcRenderer.invoke(DEFINE_STRING.BOOK.GET_IMAGE_URL_AND_DOWNLOAD, id, operateBookType, coverData), + /** 添加一键生图后台任务 */ + GenerateAllTaskImage: async (ids: string[], operateBookType: OperateBookType) => await ipcRenderer.invoke(DEFINE_STRING.BOOK.GENERATE_ALL_TASK_IMAGE, ids, operateBookType), + //#endregion //#region 一键反推的单个任务 diff --git a/src/preload/task.ts b/src/preload/task.ts index cfe044c..79642ca 100644 --- a/src/preload/task.ts +++ b/src/preload/task.ts @@ -1,6 +1,7 @@ import { ipcRenderer } from 'electron' import { DEFINE_STRING } from '../define/define_string' import { BookBackTaskStatus, BookBackTaskType, TaskExecuteType } from '../define/enum/bookEnum' +import { TaskModal } from '@/model/task' const task = { @@ -10,16 +11,8 @@ const task = { * @returns */ StartBackTask: async (isGiveUp: boolean) => await ipcRenderer.invoke(DEFINE_STRING.TASK.START_BACK_TASK, isGiveUp), - /** - * 添加单个任务 - * @param bookId 小说ID - * @param taskType 任务类型 - * @param executeType 任务执行类型 - * @param bookTaskId 小说任务ID - * @param bookTaskDetailId 小说分镜ID - * @param responseMessageName 响应消息名称 - * @returns - */ + + /** 添加单个任务 */ AddBookBackTask: async (bookId: string, taskType: BookBackTaskType, executeType = TaskExecuteType.AUTO, bookTaskId = null, @@ -27,14 +20,14 @@ const task = { responseMessageName: string = null) => await ipcRenderer.invoke(DEFINE_STRING.TASK.ADD_BOOK_BACK_TASK, bookId, taskType, executeType, bookTaskId, bookTaskDetailId, responseMessageName), - /** - * 同时添加多个任务 - * @param task - */ + /** 同时添加多个任务 */ AddMultiBookBackTask: async (task: TaskModal.Task[]) => await ipcRenderer.invoke(DEFINE_STRING.TASK.ADD_MULTI_BOOK_BACK_TASK, task), /** 获取等待中的任务 */ GetAllStatusTaskCount: async (value: BookBackTaskStatus[]) => await ipcRenderer.invoke(DEFINE_STRING.TASK.GET_ALL_STATUS_TASK_COUNT, value), + + /** 获取后台任务的集合,分页 */ + GetBackTaskCollection: async (queryTaskCondition: TaskModal.QueryTaskCondition) => await ipcRenderer.invoke(DEFINE_STRING.TASK.GET_BACK_TASK_COLLECTION, queryTaskCondition), } export { task } diff --git a/src/preload/write.js b/src/preload/write.ts similarity index 82% rename from src/preload/write.js rename to src/preload/write.ts index 382691d..c607478 100644 --- a/src/preload/write.js +++ b/src/preload/write.ts @@ -30,7 +30,13 @@ const write = { // 开始执行API相关的一系列任务 ActionStart(aiSetting, word) { return ipcRenderer.invoke(DEFINE_STRING.WRITE.ACTION_START, aiSetting, word) - } + }, + //#endregion + + //#region 文案洗稿相关 + + /** 生成洗稿后文案 */ + GenerateAfterGptWord: async (ids: string[], isEmpty: boolean) => await ipcRenderer.invoke(DEFINE_STRING.WRITE.GENERATE_AFTER_GPT_WORD, ids, isEmpty), //#endregion } diff --git a/src/renderer/src/components/BackTask/BackTask.vue b/src/renderer/src/components/BackTask/BackTask.vue new file mode 100644 index 0000000..7eb9e78 --- /dev/null +++ b/src/renderer/src/components/BackTask/BackTask.vue @@ -0,0 +1,19 @@ + + + diff --git a/src/renderer/src/components/BackTask/TaskDataTable.vue b/src/renderer/src/components/BackTask/TaskDataTable.vue new file mode 100644 index 0000000..5b3d3b0 --- /dev/null +++ b/src/renderer/src/components/BackTask/TaskDataTable.vue @@ -0,0 +1,247 @@ + + + diff --git a/src/renderer/src/components/Backstep/PushBackPrompt.vue b/src/renderer/src/components/Backstep/PushBackPrompt.vue index 8490444..952d015 100644 --- a/src/renderer/src/components/Backstep/PushBackPrompt.vue +++ b/src/renderer/src/components/Backstep/PushBackPrompt.vue @@ -99,7 +99,6 @@ export default defineComponent({ // 处理数据。获取当前的所有的数据 let dialogWidth = window.innerWidth * 0.7 let dialogHeight = window.innerHeight * 0.9 - // ImportWordAndSrt dialog.create({ showIcon: false, closeOnEsc: false, diff --git a/src/renderer/src/components/Backstep/ReGenerate.vue b/src/renderer/src/components/Backstep/ReGenerate.vue index 98832de..a126e5f 100644 --- a/src/renderer/src/components/Backstep/ReGenerate.vue +++ b/src/renderer/src/components/Backstep/ReGenerate.vue @@ -397,7 +397,6 @@ export default defineComponent({ // 处理数据。获取当前的所有的数据 let dialogWidth = window.innerWidth * 0.8 let dialogHeight = window.innerHeight * 0.9 - // ImportWordAndSrt dialog.create({ showIcon: false, closeOnEsc: false, @@ -479,7 +478,6 @@ export default defineComponent({ // 处理数据。获取当前的所有的数据 let dialogWidth = 800 let dialogHeight = 600 - // ImportWordAndSrt dialog.create({ showIcon: false, closeOnEsc: false, diff --git a/src/renderer/src/components/Book/Components/ImportWord/ImportWordAndSrt.vue b/src/renderer/src/components/Book/Components/ImportWord/ImportWordAndSrt.vue index 329aea2..e3506d6 100644 --- a/src/renderer/src/components/Book/Components/ImportWord/ImportWordAndSrt.vue +++ b/src/renderer/src/components/Book/Components/ImportWord/ImportWordAndSrt.vue @@ -652,7 +652,6 @@ export default defineComponent({ // 处理数据。获取当前的所有的数据 let dialogWidth = window.innerWidth * 0.7 let dialogHeight = window.innerHeight * 0.9 - // ImportWordAndSrt dialog.create({ showIcon: false, closeOnEsc: false, diff --git a/src/renderer/src/components/Book/Components/ManageBook/BookListAction.vue b/src/renderer/src/components/Book/Components/ManageBook/BookListAction.vue index 33a272b..0fd7eda 100644 --- a/src/renderer/src/components/Book/Components/ManageBook/BookListAction.vue +++ b/src/renderer/src/components/Book/Components/ManageBook/BookListAction.vue @@ -51,7 +51,6 @@ async function EditBook(e) { reverseManageStore.SetSelectBook(toRaw(book.value)) // 一个弹窗添加数据 let dialogWidth = 600 - // ImportWordAndSrt dialog.create({ showIcon: false, closeOnEsc: false, diff --git a/src/renderer/src/components/Book/Components/ManageBook/ManageBookButton.vue b/src/renderer/src/components/Book/Components/ManageBook/ManageBookButton.vue index f71882b..306bf65 100644 --- a/src/renderer/src/components/Book/Components/ManageBook/ManageBookButton.vue +++ b/src/renderer/src/components/Book/Components/ManageBook/ManageBookButton.vue @@ -83,11 +83,10 @@ export default defineComponent({ reverseManageStore.SetSelectBook({ name: null, id: null, - type: BookType.MJ_REVERSE + type: BookType.ORIGINAL, }) // 一个弹窗添加数据 let dialogWidth = 600 - // ImportWordAndSrt dialog.create({ showIcon: false, closeOnEsc: false, @@ -103,7 +102,7 @@ export default defineComponent({ */ async function DataTableStripedUpdate() { // 修改数据 - + // 直接提交修改 let res = await softwareStore.SaveSoftware() console.log(res) @@ -115,7 +114,6 @@ export default defineComponent({ // 选择下拉菜单 async function SelectDropdown(key) { - console.log(key) softwareStore.softWare.reverse_data_table_size = key // 直接提交修改 diff --git a/src/renderer/src/components/Book/Components/PresetLibrary/CharacterTags.vue b/src/renderer/src/components/Book/Components/PresetLibrary/CharacterTags.vue index 383c847..e7754ad 100644 --- a/src/renderer/src/components/Book/Components/PresetLibrary/CharacterTags.vue +++ b/src/renderer/src/components/Book/Components/PresetLibrary/CharacterTags.vue @@ -7,7 +7,7 @@ - - diff --git a/src/renderer/src/components/Components/BackTask/QueryButton.vue b/src/renderer/src/components/Components/BackTask/QueryButton.vue deleted file mode 100644 index 56de7db..0000000 --- a/src/renderer/src/components/Components/BackTask/QueryButton.vue +++ /dev/null @@ -1,57 +0,0 @@ - - - diff --git a/src/renderer/src/components/Components/BackTask/TaskDataTable.vue b/src/renderer/src/components/Components/BackTask/TaskDataTable.vue deleted file mode 100644 index bbee35c..0000000 --- a/src/renderer/src/components/Components/BackTask/TaskDataTable.vue +++ /dev/null @@ -1,7 +0,0 @@ - - - diff --git a/src/renderer/src/components/Home/Home.vue b/src/renderer/src/components/Home/Home.vue index 603c0b3..6c56540 100644 --- a/src/renderer/src/components/Home/Home.vue +++ b/src/renderer/src/components/Home/Home.vue @@ -64,7 +64,7 @@ import { MD5 } from 'crypto-js' import InputDialogContent from '../Original/Components/InputDialogContent.vue' import APIIcon from '../Icon/APIIcon.vue' import BackTaskIcon from '../Icon/BackTaskIcon.vue' -import BackTask from '../Components/BackTask/BackTask.vue' +import BackTask from '@/renderer/src/components/BackTask/BackTask.vue' import { useSystemStore } from '../../../../stores/system' import { TimeDelay } from '../../../../define/Tools/time' import { BookBackTaskStatus } from '../../../../define/enum/bookEnum' @@ -603,7 +603,8 @@ function renderIcon(icon) { } function OpneBackTask() { - let dialogWidth = window.innerWidth * 0.8 + let dialogWidth = window.innerWidth * 0.9 + if (dialogWidth < 800) dialogWidth = 800 let dialogHeight = window.innerHeight * 0.95 dialog.create({ title: '后台任务', diff --git a/src/renderer/src/components/Original/Components/ImportWordAndSrt.vue b/src/renderer/src/components/Original/Components/ImportWordAndSrt.vue index 4679418..9c8b45e 100644 --- a/src/renderer/src/components/Original/Components/ImportWordAndSrt.vue +++ b/src/renderer/src/components/Original/Components/ImportWordAndSrt.vue @@ -1,6 +1,22 @@ - diff --git a/src/renderer/src/components/Original/Components/PromptSetting.vue b/src/renderer/src/components/Original/Components/PromptSetting.vue index ed225d8..61b17d0 100644 --- a/src/renderer/src/components/Original/Components/PromptSetting.vue +++ b/src/renderer/src/components/Original/Components/PromptSetting.vue @@ -315,7 +315,8 @@ export default defineComponent({ let sdObj = { batch_size: toRaw(sd_batch_size.value), width: toRaw(sd_image_width.value), - height: toRaw(sd_image_height.value) + height: toRaw(sd_image_height.value), + seed: toRaw(sd_seed.value) } await window.api.SaveSDConfig(sdObj, (value) => { if (value.code == 0) { diff --git a/src/renderer/src/components/Original/DataTable.vue b/src/renderer/src/components/Original/DataTable.vue index 311a2a2..df16107 100644 --- a/src/renderer/src/components/Original/DataTable.vue +++ b/src/renderer/src/components/Original/DataTable.vue @@ -615,7 +615,6 @@ export default defineComponent({ // 处理数据。获取当前的所有的数据 let dialogWidth = window.innerWidth * 0.8 let dialogHeight = window.innerHeight * 0.9 - // ImportWordAndSrt dialog.create({ showIcon: false, closeOnEsc: false, diff --git a/src/renderer/src/components/Original/MenuButton.vue b/src/renderer/src/components/Original/MenuButton.vue index 3a6ab95..2eba945 100644 --- a/src/renderer/src/components/Original/MenuButton.vue +++ b/src/renderer/src/components/Original/MenuButton.vue @@ -114,7 +114,6 @@ export default defineComponent({ // 处理数据。获取当前的所有的数据 let dialogWidth = window.innerWidth * 0.95 let dialogHeight = window.innerHeight * 0.95 - // ImportWordAndSrt dialog.create({ showIcon: false, closeOnEsc: false, @@ -133,9 +132,8 @@ export default defineComponent({ async function GetCharacter() { // 判断当前数据是不是存在 // 处理数据。获取当前的所有的数据 - let dialogWidth = window.innerWidth * 0.6 + let dialogWidth = 1000 let dialogHeight = window.innerHeight * 0.9 - // ImportWordAndSrt dialog.create({ showIcon: false, closeOnEsc: false, @@ -217,7 +215,6 @@ export default defineComponent({ * 生成所有的图片 */ async function GenerateImageAll() { - let tmpData = cloneDeep(toRaw(data.value)) tmpData = tmpData.filter((item) => { return item.imageLock == null || item.imageLock == false @@ -363,11 +360,10 @@ export default defineComponent({ message.error(value.message) return } - + console.log(value.value) // 拿到提示词文件地址,然后还是获取 await window.pmpt.OpenPromptFileTxt(value.value, (res) => { - console.log(res) if (res.code == 0) { message.error(res.message) @@ -382,7 +378,6 @@ export default defineComponent({ positiveText: '继续', negativeText: '取消', onPositiveClick: async () => { - // 开始导入 for (let i = 0; i < data.value.length && i < res.data.length; i++) { const element = res.data[i] diff --git a/src/renderer/src/components/Setting/SDSetting.vue b/src/renderer/src/components/Setting/SDSetting.vue index 771ca05..491b873 100644 --- a/src/renderer/src/components/Setting/SDSetting.vue +++ b/src/renderer/src/components/Setting/SDSetting.vue @@ -36,6 +36,25 @@ placeholder="输入重绘幅度" /> + + + + + + + + + + + - diff --git a/src/stores/reverseManage.ts b/src/stores/reverseManage.ts index 0c466b9..49a572e 100644 --- a/src/stores/reverseManage.ts +++ b/src/stores/reverseManage.ts @@ -39,6 +39,7 @@ export const useReverseManageStore = defineStore('reverseManage', { imageFolder: null, styleList: null, prefix: null, + imageCategory: null, status: BookTaskStatus.WAIT, errorMsg: null } as Book.SelectBookTask// 当前选中的小说任务 diff --git a/src/stores/software.ts b/src/stores/software.ts index 402bdb5..796a37e 100644 --- a/src/stores/software.ts +++ b/src/stores/software.ts @@ -1,3 +1,4 @@ +import { TaskModal } from '@/model/task' import { defineStore } from 'pinia' // 系统相关设置 @@ -15,8 +16,8 @@ export const useSoftwareStore = defineStore('software', { taskType: undefined, taskStatus: undefined, taskErrorMessage: undefined, - }, // 查询传递的数据 - taskData: [], // 后台任务数据 + } as TaskModal.TaskCondition, // 查询传递的数据 + taskData: [] as TaskModal.Task[], // 后台任务数据 }, softWare: { theme: 'light', // 系统主题,亮或是暗 @@ -43,15 +44,6 @@ export const useSoftwareStore = defineStore('software', { } }, actions: { - // 设置一键反推界面显示数据 - SetReverseDispalayShow(value) { - this.softWare.reverse_display_show = value - }, - - // 设置反推界面时候小说信息显示斑马纹 - SetReverseBookStripedShow(value) { - this.softWare.reverse_show_book_striped = value - }, // 修改软件主题 SetSoftware(value) { @@ -75,8 +67,22 @@ export const useSoftwareStore = defineStore('software', { // 保存数据 // @ts-ignore return await window.setting.SaveSoftWareSetting(JSON.parse(JSON.stringify(this.softWare))) - } + }, //#endregion + + /** + * 重置后台任务查询数据 + */ + ResetBackTaskQueryData() { + this.backTask.queryData = { + taskName: undefined, + bookId: undefined, + bookTaskId: undefined, + taskType: undefined, + taskStatus: undefined, + taskErrorMessage: undefined + } + } } })