3.1.9 2024.10.28

1. (聚合推文)MJ反推、SD反推 添加剪映分镜
2. (聚合推文)完善SD反推分类(界面同MJ反推,些许不一致)
3. (聚合推文)完成一键合成视频(单个和批量)
4. 修改聚合推文进入界面小说批次任务表格样式
5. (聚合推文)完善一键重置
6. (聚合推文)完善一键删除
This commit is contained in:
2024-10-28 18:38:11 +08:00
parent 0c5988ed41
commit 22cfe65dde
78 changed files with 4982 additions and 2440 deletions
+22
View File
@@ -1,3 +1,4 @@
import { escapeRegExp } from "lodash";
/**
* 检查字符串中是不是包含中文或者标点符号
@@ -66,4 +67,25 @@ export async function ExecuteConcurrently(tasks: Array<() => Promise<any>>, conc
}
}
return Promise.all(results);
}
/**
* 替换主字符串中的子字符串
* @param mainString 主字符串
* @param substringArray 子字符串数组
* @returns 替换后的字符串
*/
export function ReplaceSubstrings(mainString: string, substringArray: string[], replacement: string = " "): string {
// 按长度降序排序子字符串数组,以确保较长的子字符串先被替换
substringArray.sort((a, b) => b.length - a.length);
// 对每个子字符串进行替换
for (const substring of substringArray) {
// 创建一个正则表达式,用于全局匹配子字符串
const regex = new RegExp(escapeRegExp(substring), 'g');
// 将匹配到的子字符串替换为等长的空格
mainString = mainString.replace(regex, replacement);
}
return mainString;
}
-1
View File
@@ -5,7 +5,6 @@
* @returns 可以解析返回true,否则返回false
*/
export function ValidateJson(str: string): boolean {
try {
JSON.parse(str);
return true
@@ -244,7 +244,7 @@ export class BookTaskDetailService extends BaseRealmService {
* 删除满足条件的对象吗,必传小说ID和小说任务ID
* @param condition bookIdbookTaskIdnameid
*/
DeleteBookTaskDetail(condition) {
DeleteBookTaskDetail(condition: Book.DeleteBookTaskDetailCondition) {
try {
if (isEmpty(condition.id) && isEmpty(condition.bookTaskId) && isEmpty(condition.bookId)) {
throw new Error('删除小说分镜信息失败,没有必要参数')
@@ -0,0 +1,164 @@
const BOOK = {
MAIN_DATA_RETURN: 'MAIN_DATA_RETURN', // 监听任务返回
REPLACE_VIDEO_CURRENT_FRAME: 'REPLACE_VIDEO_CURRENT_FRAME',
GET_BOOK_TYPE: 'GET_BOOK_TYPE',
ADD_OR_MODIFY_BOOK: 'ADD_OR_MODIFY_BOOK',
GET_BOOK_DATA: 'GET_BOOK_DATA',
GET_FRAME_DATA: 'GET_FRAME_DATA',
GET_BOOK_TASK_DATA: 'GET_BOOK_TASK_DATA',
AUTO_ACTION: 'AUTO_ACTION',
SAVE_BOOK_SUBTITLE_POSITION: 'SAVE_BOOK_SUBTITLE_POSITION',
OPEN_BOOK_SUBTITLE_POSITION_SCREENSHOT: 'OPEN_BOOK_SUBTITLE_POSITION_SCREENSHOT',
GET_CURRENT_FRAME_TEXT: 'GET_CURRENT_FRAME_TEXT',
GET_VIDEO_FRAME_TEXT: 'GET_VIDEO_FRAME_TEXT',
GET_BOOK_TASK_DETAIL: 'GET_BOOK_TASK_DETAIL',
REVERSE_PROMPT_TO_GPT_PROMPT: 'REVERSE_PROMPT_TO_GPT_PROMPT',
SINGLE_REVERSE_TO_GPT_PROMPT: 'SINGLE_REVERSE_TO_GPT_PROMPT',
SAVE_IMAGE_STYLE: 'SAVE_IMAGE_STYLE',
IMAGE_LOCK_OPERATION: "IMAGE_LOCK_OPERATION",
DOWNLOAD_IMAGE_AND_SPLIT: "DOWNLOAD_IMAGE_AND_SPLIT",
ONE_TO_FOUR_BOOK_TASK: "ONE_TO_FOUR_BOOK_TASK",
RESET_BOOK_TASK: "RESET_BOOK_TASK",
DELETE_BOOK_TASK: "DELETE_BOOK_TASK",
GENERATE_IMAGE_ALL: "GENERATE_IMAGE_ALL",
CHECK_IMAGE_FILE_SIZE: "CHECK_IMAGE_FILE_SIZE",
HD_IMAGE: "HD_IMAGE",
USE_BOOK_VIDEO_DATA_TO_BOOK_TASK: "USE_BOOK_VIDEO_DATA_TO_BOOK_TASK",
ADD_JIANYING_DRAFT: "ADD_JIANYING_DRAFT",
EXPORT_COPYWRITING: "EXPORT_COPYWRITING",
IMPORT_COPYWRITING: 'IMPORT_COPYWRITING',
MERGE_PROMPT: "MERGE_PROMPT",
RESET_BOOK_DATA: "RESET_BOOK_DATA",
DELETE_BOOK_DATA: "DELETE_BOOK_DATA",
CLEAR_IMPORT_WORD: "CLEAR_IMPORT_WORD",
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",
SAVE_COPYWRITING: 'SAVE_COPYWRITING',
//#region 通用
/** 重置小说批次详细信息的数据 */
RESET_BOOK_TASK_DETAIL_DATA: "RESET_BOOK_TASK_DETAIL_DATA",
//#endregion
//#region 分镜
/** 剪映抽帧,并判断是不是切割视频 */
JIANYING_FRAME: "JIANYING_FRAME",
//#endregion
//#region 提示词
/**
* 原创推理提示词
*/
ORIGINAL_GPT_PROMPT: "ORIGINAL_GPT_PROMPT",
/** * 原创推理提示词返回 */
ORIGINAL_GPT_PROMPT_RETURN: "ORIGINAL_GPT_PROMPT_RETURN",
/** * 导入提示词,通用 */
IMPORT_GPT_PROMPT: "IMPORT_GPT_PROMPT",
/** 移除不想要的提示词 */
REMOVE_BAD_PROMPT: "REMOVE_BAD_PROMPT",
//#endregion
//#region 生图返回相关
/**
* MJ生图返回信息
*/
MJ_IMAGE_GENERATE_RETURN: 'MJ_IMAGE_GENERATE_RETURN',
/**
* SD生图返回信息
*/
SD_IMAGE_GENERATE_RETURN: 'SD_IMAGE_GENERATE_RETURN',
/**
* D3 出图返回信息
*/
D3_IMAGE_GENERATE_RETURN: 'D3_IMAGE_GENERATE_RETURN',
/**
* flux forge 生图返回信息
*/
FLUX_FORGE_IMAGE_GENERATE_RETURN: "FLUX_FORGE_IMAGE_GENERATE_RETURN",
/**
* flux api 生图返回信息
*/
FLUX_API_IMAGE_GENERATE_RETURN: "FLUX_API_IMAGE_GENERATE_RETURN",
//#endregion
//#region 图片相关
/**
* 获取MJ的消息ID,下载图片并分割
*/
GET_IMAGE_URL_AND_DOWNLOAD: "GET_IMAGE_URL_AND_DOWNLOAD",
//#endregion
COMPUTE_STORYBOARD: 'COMPUTE_STORYBOARD',
GET_FRAME: 'GET_FRAME',
FRAMING: 'FRAMING',
//#region 字幕相关
/**
* 获取copywriting
*/
GET_COPYWRITING: 'GET_COPYWRITING',
/**
* 获取copywriting的返回信息
*/
GET_COPYWRITING_RETURN: 'GET_COPYWRITING_RETURN',
//#endregion
REMOVE_WATERMARK: 'REMOVE_WATERMARK',
REMOVE_WATERMARK_RETURN: 'REMOVE_WATERMARK_RETURN',
SPLI_TAUDIO: 'SPLI_TAUDIO',
SPLI_TAUDIO_RETURN: 'SPLI_TAUDIO_RETURN',
//#region 反推相关
/**
* 添加反推任务
*/
ADD_REVERSE_PROMPT: 'ADD_REVERSE_PROMPT',
/**
* 反推任务返回信息
*/
REVERSE_PROMPT_RETURN: 'REVERSE_PROMPT_RETURN',
//#endregion
//#region 翻译相关
/**
* 反推翻译返回信息
*/
REVERSE_PROMPT_TRANSLATE_RETURN: 'REVERSE_PROMPT_TRANSLATE_RETURN',
//#endregion
//#region
/** 合成视频相关 */
ADD_GENERATE_VIDEO_TASK: "ADD_GENERATE_VIDEO_TASK",
/** 生成视频返回 */
GENERATE_VIDEO_RETURN: "GENERATE_VIDEO_RETURN",
//#endregion
}
export default BOOK;
+2 -140
View File
@@ -2,11 +2,13 @@ import { SYSTEM } from "./systemDefineString"
import TASK from "./taskDefineString"
import TTS from "./ttsDefineString"
import SETTING from "./settingDefineString"
import BOOK from "./bookDefineString"
export const DEFINE_STRING = {
SYSTEM: SYSTEM,
TASK: TASK,
TTS: TTS,
BOOK: BOOK,
SETTING: SETTING,
SHOW_GLOBAL_MESSAGE: "SHOW_GLOBAL_MESSAGE",
SHOW_GLOBAL_MAIN_NOTIFICATION: 'SHOW_GLOBAL_MAIN_NOTIFICATION',
@@ -225,146 +227,6 @@ export const DEFINE_STRING = {
BATCH_PROCESS_IMAGE_RESULT: 'BATCH_PROCESS_IMAGE_RESULT',
PROCESS_IMAGE_WATERMASK_CHECK: "PROCESS_IMAGE_WATERMASK_CHECK"
},
BOOK: {
MAIN_DATA_RETURN: 'MAIN_DATA_RETURN', // 监听任务返回
REPLACE_VIDEO_CURRENT_FRAME: 'REPLACE_VIDEO_CURRENT_FRAME',
GET_BOOK_TYPE: 'GET_BOOK_TYPE',
ADD_OR_MODIFY_BOOK: 'ADD_OR_MODIFY_BOOK',
GET_BOOK_DATA: 'GET_BOOK_DATA',
GET_FRAME_DATA: 'GET_FRAME_DATA',
GET_BOOK_TASK_DATA: 'GET_BOOK_TASK_DATA',
AUTO_ACTION: 'AUTO_ACTION',
SAVE_BOOK_SUBTITLE_POSITION: 'SAVE_BOOK_SUBTITLE_POSITION',
OPEN_BOOK_SUBTITLE_POSITION_SCREENSHOT: 'OPEN_BOOK_SUBTITLE_POSITION_SCREENSHOT',
GET_CURRENT_FRAME_TEXT: 'GET_CURRENT_FRAME_TEXT',
GET_VIDEO_FRAME_TEXT: 'GET_VIDEO_FRAME_TEXT',
GET_BOOK_TASK_DETAIL: 'GET_BOOK_TASK_DETAIL',
REVERSE_PROMPT_TO_GPT_PROMPT: 'REVERSE_PROMPT_TO_GPT_PROMPT',
SINGLE_REVERSE_TO_GPT_PROMPT: 'SINGLE_REVERSE_TO_GPT_PROMPT',
SAVE_IMAGE_STYLE: 'SAVE_IMAGE_STYLE',
IMAGE_LOCK_OPERATION: "IMAGE_LOCK_OPERATION",
DOWNLOAD_IMAGE_AND_SPLIT: "DOWNLOAD_IMAGE_AND_SPLIT",
ONE_TO_FOUR_BOOK_TASK: "ONE_TO_FOUR_BOOK_TASK",
RESET_BOOK_TASK: "RESET_BOOK_TASK",
DELETE_BOOK_TASK: "DELETE_BOOK_TASK",
GENERATE_IMAGE_ALL: "GENERATE_IMAGE_ALL",
CHECK_IMAGE_FILE_SIZE: "CHECK_IMAGE_FILE_SIZE",
HD_IMAGE: "HD_IMAGE",
USE_BOOK_VIDEO_DATA_TO_BOOK_TASK: "USE_BOOK_VIDEO_DATA_TO_BOOK_TASK",
ADD_JIANYING_DRAFT: "ADD_JIANYING_DRAFT",
EXPORT_COPYWRITING: "EXPORT_COPYWRITING",
IMPORT_COPYWRITING: 'IMPORT_COPYWRITING',
MERGE_PROMPT: "MERGE_PROMPT",
RESET_BOOK_DATA: "RESET_BOOK_DATA",
DELETE_BOOK_DATA: "DELETE_BOOK_DATA",
CLEAR_IMPORT_WORD: "CLEAR_IMPORT_WORD",
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",
SAVE_COPYWRITING: 'SAVE_COPYWRITING',
//#region 提示词
/**
* 原创推理提示词
*/
ORIGINAL_GPT_PROMPT: "ORIGINAL_GPT_PROMPT",
/**
* 原创推理提示词返回
*/
ORIGINAL_GPT_PROMPT_RETURN: "ORIGINAL_GPT_PROMPT_RETURN",
/**
* 导入提示词,通用
*/
IMPORT_GPT_PROMPT: "IMPORT_GPT_PROMPT",
//#endregion
//#region 生图返回相关
/**
* MJ生图返回信息
*/
MJ_IMAGE_GENERATE_RETURN: 'MJ_IMAGE_GENERATE_RETURN',
/**
* SD生图返回信息
*/
SD_IMAGE_GENERATE_RETURN: 'SD_IMAGE_GENERATE_RETURN',
/**
* D3 出图返回信息
*/
D3_IMAGE_GENERATE_RETURN: 'D3_IMAGE_GENERATE_RETURN',
/**
* flux forge 生图返回信息
*/
FLUX_FORGE_IMAGE_GENERATE_RETURN: "FLUX_FORGE_IMAGE_GENERATE_RETURN",
/**
* flux api 生图返回信息
*/
FLUX_API_IMAGE_GENERATE_RETURN: "FLUX_API_IMAGE_GENERATE_RETURN",
//#endregion
//#region 图片相关
/**
* 获取MJ的消息ID,下载图片并分割
*/
GET_IMAGE_URL_AND_DOWNLOAD: "GET_IMAGE_URL_AND_DOWNLOAD",
//#endregion
COMPUTE_STORYBOARD: 'COMPUTE_STORYBOARD',
GET_FRAME: 'GET_FRAME',
FRAMING: 'FRAMING',
//#region 字幕相关
/**
* 获取copywriting
*/
GET_COPYWRITING: 'GET_COPYWRITING',
/**
* 获取copywriting的返回信息
*/
GET_COPYWRITING_RETURN: 'GET_COPYWRITING_RETURN',
//#endregion
REMOVE_WATERMARK: 'REMOVE_WATERMARK',
REMOVE_WATERMARK_RETURN: 'REMOVE_WATERMARK_RETURN',
SPLI_TAUDIO: 'SPLI_TAUDIO',
SPLI_TAUDIO_RETURN: 'SPLI_TAUDIO_RETURN',
//#region 反推相关
/**
* 添加反推任务
*/
ADD_REVERSE_PROMPT: 'ADD_REVERSE_PROMPT',
/**
* 反推任务返回信息
*/
REVERSE_PROMPT_RETURN: 'REVERSE_PROMPT_RETURN',
//#endregion
//#region 翻译相关
/**
* 反推翻译返回信息
*/
REVERSE_PROMPT_TRANSLATE_RETURN: 'REVERSE_PROMPT_TRANSLATE_RETURN',
//#endregion
},
PROMPT: {
GET_SORT_OPTIONS: 'GET_SORT_OPTIONS',
SAVE_PROMPT_SORT_DATA: 'SAVE_PROMPT_SORT_DATA',
+6 -1
View File
@@ -208,7 +208,12 @@ export enum BookTaskStatus {
COMPOSING_DONE = 'composing_done',
// 合成视频失败
COMPOSING_FAIL = 'composing_fail'
COMPOSING_FAIL = 'composing_fail',
/** 添加草稿完成 */
DRAFT_DONE = 'draft_done',
/** 添加草稿失败 */
DRAFT_FAIL = 'draft_fail',
}
export enum TagDefineType {
+1
View File
@@ -60,6 +60,7 @@ export enum ResponseMessageType {
GET_TEXT = 'getText', // 获取文案
REMOVE_WATERMARK = "REMOVE_WATERMARK",// 删除水印
MJ_REVERSE = 'MJ_REVERSE',// MJ反推,返回反推结果
SD_REVERSE = 'SD_REVERSE',// MJ反推,返回反推结果
REVERSE_PROMPT_TRANSLATE = 'REVERSE_PROMPT_TRANSLATE',// 反推提示词翻译
GPT_PROMPT_TRANSLATE = 'GPT_PROMPT_TRANSLATE', // GPT提示词翻译
MJ_IMAGE = 'MJ_IMAGE',// MJ 生成图片
+18 -3
View File
@@ -92,10 +92,11 @@ export function BookIpc() {
//#endregion
//#region 小说通用操作
//#region 小说详情通用操作
ipcMain.handle(DEFINE_STRING.BOOK.REPLACE_BOOK_DATA, async (event, bookTaskId, replaceData) => await bookGeneral.ReplaceBookData(bookTaskId, replaceData))
//#endregion
//#region 分镜相关
@@ -116,6 +117,9 @@ export function BookIpc() {
await bookFrame.ReplaceVideoCurrentFrame(bookTaskDetailId, currentTime)
)
// 剪映抽帧,并判断是不是切割视频
ipcMain.handle(DEFINE_STRING.BOOK.JIANYING_FRAME, async (event, bookTaskId, draftName) => await bookFrame.JianyingFrame(bookTaskId, draftName))
//#endregion
//#region 提示词相关
@@ -154,6 +158,12 @@ export function BookIpc() {
async (event, bookTaskId: string, txtPath: string) => await bookPrompt.ImportGPTPrompt(bookTaskId, txtPath)
)
/** 移除不想要的提示词 */
ipcMain.handle(
DEFINE_STRING.BOOK.REMOVE_BAD_PROMPT,
async (event, bookTaskId: string) => await bookPrompt.RemoveBadPrompt(bookTaskId)
)
//#endregion
//#region 文案相关
@@ -290,13 +300,13 @@ export function BookIpc() {
// 重置小说批次数据
ipcMain.handle(
DEFINE_STRING.BOOK.RESET_BOOK_TASK,
async (event, bookTaskId) => await bookTask.ReSetBookTask(bookTaskId)
async (event, bookTaskId, operateBookType) => await bookTask.ReSetBookTask(bookTaskId, operateBookType)
)
// 删除小说批次数据
ipcMain.handle(
DEFINE_STRING.BOOK.DELETE_BOOK_TASK,
async (event, bookTaskId) => await bookTask.DeleteBookTask(bookTaskId)
async (event, bookTaskId, operateBookType) => await bookTask.DeleteBookTask(bookTaskId, operateBookType)
)
@@ -312,5 +322,10 @@ export function BookIpc() {
DEFINE_STRING.BOOK.ADD_JIANYING_DRAFT,
async (event, id: string | string[], operateBookType: OperateBookType) => await bookVideo.AddJianyingDraft(id, operateBookType)
)
ipcMain.handle(
DEFINE_STRING.BOOK.ADD_GENERATE_VIDEO_TASK,
async (event, id: string | string[], operateBookType: OperateBookType) => await bookVideo.AddGenerateVideoTask(id, operateBookType)
)
//#endregion
}
+14 -4
View File
@@ -236,7 +236,7 @@ export class ReverseBook {
if (operateBookType == OperateBookType.BOOKTASK) {
bookTaskDetails = await this.bookServiceBasic.GetBookTaskDetailData({
bookTaskId: id
})
});
} else if (operateBookType == OperateBookType.BOOKTASKDETAIL) {
let bookTaskDetail = await this.bookServiceBasic.GetBookTaskDetailDataById(id);
bookTaskDetails = [bookTaskDetail]
@@ -263,7 +263,11 @@ export class ReverseBook {
if (bookTaskDetailIds.length <= 0) {
throw new Error("没有需要反推的数据,请检查")
}
await this.AddReversePromptTask(bookTaskDetailIds, type);
// 判断是不是SD反推并且是反推的全部的数据
if (type == BookType.SD_REVERSE && operateBookType == OperateBookType.BOOKTASK) {
bookTaskDetailIds = [null];
}
await this.AddReversePromptTask(bookTaskDetailIds, type, id);
return successMessage(null, "添加反推任务成功", 'ReverseBook_AddReversePrompt')
} catch (error) {
return errorMessage("添加反推任务失败,错误信息如下:" + error.message, "ReverseBook_AddReversePrompt")
@@ -276,8 +280,14 @@ export class ReverseBook {
* @param type 反推的类型
* @returns
*/
async AddReversePromptTask(bookTaskDetailIds: string[], type: BookType): Promise<void> {
async AddReversePromptTask(bookTaskDetailIds: string[], type: BookType, bookTaskId: string): Promise<void> {
try {
if (bookTaskDetailIds.length == 1 && bookTaskDetailIds[0] == null && type == BookType.SD_REVERSE) {
let bookTask = await this.bookServiceBasic.GetBookTaskDataById(bookTaskId);
await this.bookServiceBasic.AddBookBackTask(bookTask.bookId, BookBackTaskType.SD_REVERSE, TaskExecuteType.AUTO, bookTaskId, bookTaskDetailIds[0], DEFINE_STRING.BOOK.REVERSE_PROMPT_RETURN
);
return;
}
for (let index = 0; index < bookTaskDetailIds.length; index++) {
const bookTaskDetailId = bookTaskDetailIds[index];
let bookTaskDetail = await this.bookServiceBasic.GetBookTaskDetailDataById(bookTaskDetailId)
@@ -327,7 +337,7 @@ export class ReverseBook {
res = await this.mjOpt.MJImage2Text(task);
break
case BookBackTaskType.SD_REVERSE:
res = await this.SDReversePrompt(task);
res = await this.sdOpt.SDReversePrompt(task);
break
default:
throw new Error("未知的任务类型")
+78
View File
@@ -11,6 +11,10 @@ import { LogScheduler } from '../task/logScheduler';
import { BookBasic } from "./BooKBasic";
import { LoggerStatus, OtherData } from "../../../define/enum/softwareEnum";
import { BasicReverse } from "./basicReverse";
import JianyingService from "../jianying/jianyingService";
import { ValidateJson } from "../../../define/Tools/validate";
import { BookTaskStatus } from "../../../define/enum/bookEnum";
import { define } from "../../../define/define";
export class BookFrame {
bookServiceBasic: BookServiceBasic
@@ -18,14 +22,86 @@ export class BookFrame {
logScheduler: LogScheduler
basicReverse: BasicReverse
bookBasic: BookBasic
jianyingService: JianyingService
constructor() {
this.bookServiceBasic = new BookServiceBasic();
this.ffmpegOptions = new FfmpegOptions();
this.logScheduler = new LogScheduler()
this.bookBasic = new BookBasic()
this.basicReverse = new BasicReverse()
this.jianyingService = new JianyingService()
}
/**
* 剪映抽帧,并判断是不是切割视频
* @param draftName 草稿名字
* @param clipVideo 是不是切割视频
*/
async JianyingFrame(bookTaskId: string, draftName: string) {
try {
console.log(draftName);
let draftPath = path.join(global.config.draft_path, draftName);
if (!await CheckFileOrDirExist(draftPath)) {
throw new Error("未找到对应的草稿文件,请检查");
}
let bookTask = await this.bookServiceBasic.GetBookTaskDataById(bookTaskId);
let book = await this.bookServiceBasic.GetBookDataById(bookTask.bookId);
// 在执行操作之前,先删除之前的文件
await this.bookServiceBasic.DeleteBookTaskDetailData({
bookTaskId: bookTask.id
})
// 在删除对应的图片和视频文件
let input = path.resolve(book.bookFolderPath, "tmp/input");
if (await CheckFileOrDirExist(input)) {
await DeleteFolderAllFile(input, true);
}
await this.jianyingService.GetDraftFrameAndText(draftPath, book.bookFolderPath, 'input');
let draftTimeLinePath = path.resolve(book.bookFolderPath, "draftFrameData.json");
if (!await CheckFileOrDirExist(draftTimeLinePath)) {
throw new Error("没有找到对应的草稿时间轴数据,请检查");
}
let draftTimeLineString = await fs.promises.readFile(draftTimeLinePath, "utf-8");
if (!ValidateJson(draftTimeLineString)) {
throw new Error("草稿时间轴数据格式错误,请检查");
}
let draftTimeLine = JSON.parse(draftTimeLineString);
// 开始修改数据
// 获取SD设置
let sdConifg = JSON.parse(await fs.promises.readFile(define.sd_setting, 'utf-8'));
let adetailer = false;
if (sdConifg && sdConifg?.webui?.adetailer) {
adetailer = true;
}
// 写入数据
for (let i = 0; i < draftTimeLine.length; i++) {
const element = draftTimeLine[i];
await this.bookServiceBasic.AddBookTaskDetail({
bookTaskId: bookTaskId,
bookId: bookTask.bookId,
startTime: Math.ceil(element.startTime / 1000),
endTime: Math.ceil(element.endTime / 1000),
status: BookTaskStatus.WAIT,
word: element.word,
videoPath: element.videoPath,
oldImage: path.relative(define.project_path, element.framePath),
afterGpt: element.text,
subValue: undefined,
timeLimit: `${element.startTime} -- ${element.endTime}`,
// 新增修脸跟随
adetailer: adetailer
})
}
return successMessage(null, '剪映抽帧成功', 'BookFrame_JianyingFrame')
} catch (error) {
return errorMessage('剪映抽帧失败,失败信息如下:' + error.toString(), 'BookFrame_JianyingFrame')
}
}
//#region 替换指定分镜的视频当前帧
/**
* 替换指定分镜的视频当前帧
* @param bookTaskDetailId 指定的小说分镜ID
@@ -65,6 +141,8 @@ export class BookFrame {
return errorMessage('替换指定分镜的视频当前帧失败,失败信息如下:' + error.toString(), 'BookFrame_ReplaceVideoCurrentFrame');
}
}
//#endregion
//#region 进行分镜截取的一些操作
+1 -1
View File
@@ -13,7 +13,7 @@ export class BookGeneral {
constructor() {
this.bookServiceBasic = new BookServiceBasic()
}
//#region 批量替换数据的操作
/**
-8
View File
@@ -73,14 +73,6 @@ export class BookImage {
for (let i = 0; i < bookTaskDetails.length; i++) {
const element = bookTaskDetails[i];
await this.bookServiceBasic.DeleteBoookTaskDetailGenerateImage(element.id);
// if (bookTask.imageCategory == BookImageCategory.MJ) {
// } else if (bookTask.imageCategory == BookImageCategory.D3) {
// throw new Error('暂时不支持D3生成的图片删除')
// } else if (bookTask.imageCategory == BookImageCategory.SD) {
// await this.bookServiceBasic.DeleteBoookTaskDetailGenerateImage(element.id);
// } else {
// throw new Error('未知的小说类型,请检查')
// }
// 上面的信息重置完毕之后,开始删除文件信息
let outImage = element.outImagePath;
if (await CheckFileOrDirExist(outImage)) {
+55 -2
View File
@@ -5,12 +5,14 @@ import { GeneralResponse } from "../../../model/generalResponse";
import { errorMessage, SendReturnMessage, successMessage } from "../../Public/generalTools";
import { BookServiceBasic } from "../ServiceBasic/bookServiceBasic";
import { GptService } from "../GPT/gpt";
import { ExecuteConcurrently } from "../../../define/Tools/common";
import { ExecuteConcurrently, ReplaceSubstrings } from "../../../define/Tools/common";
import { DEFINE_STRING } from "../../../define/define_string";
import { CheckFileOrDirExist } from "../../../define/Tools/file";
import fs from 'fs';
import path from 'path'
import readline from 'readline';
import { define } from "../../../define/define";
import { ValidateJson } from "../../../define/Tools/validate";
export class BookPrompt {
@@ -21,6 +23,51 @@ export class BookPrompt {
this.gptService = new GptService()
}
//#region 移除不想要的提示词
/**
* 移除不想要的提示词
* @param bookTaskId 小说任务的ID
*/
async RemoveBadPrompt(bookTaskId: string) {
try {
let bookTaskDetails = await this.bookServiceBasic.GetBookTaskDetailData({
bookTaskId: bookTaskId
})
let sdConfigString = await fs.promises.readFile(define.sd_setting, 'utf-8')
if (isEmpty(sdConfigString)) {
throw new Error('读取SD配置文件失败')
}
if (!ValidateJson(sdConfigString)) {
throw new Error('SD配置文件格式错误')
}
let sdConfig = JSON.parse(sdConfigString)
let badPrompt = sdConfig.tag.badPrompt;
let badPromptList = badPrompt.split(',')
let result = []
for (let i = 0; i < bookTaskDetails.length; i++) {
const element = bookTaskDetails[i];
if (isEmpty(element.gptPrompt)) {
continue;
}
let newText = ReplaceSubstrings(element.gptPrompt, badPromptList, "");
newText.replaceAll(",,", ",");
await this.bookServiceBasic.UpdateBookTaskDetail(element.id, {
gptPrompt: newText
})
result.push({
id: element.id,
gptPrompt: newText
})
}
return successMessage(result, "移除不想要的提示词成功", "Book_RemoveBadPrompt")
} catch (error) {
return errorMessage("移除不想要的提示词失败,错误信息如下:" + error.message, "Book_RemoveBadPrompt")
}
}
//#endregion
//#region 提示词通用
/**
@@ -191,7 +238,13 @@ export class BookPrompt {
}
}
})
} else if (type == BookType.SD_REVERSE) { }
} else if (type == BookType.SD_REVERSE) {
// SD反推
for (let i = 0; i < bookTaskDetails.length; i++) {
const element = bookTaskDetails[i];
await this.bookServiceBasic.UpdateBookTaskDetail(element.id, { gptPrompt: undefined });
}
}
else {
throw new Error("SD反推删除还不支持")
}
+53 -17
View File
@@ -233,17 +233,36 @@ export class BookTask {
* 重置小说任务数据
* @param bookTaskId 小说任务ID
*/
async ReSetBookTask(bookTaskId: string): Promise<GeneralResponse.ErrorItem | GeneralResponse.SuccessItem> {
async ReSetBookTask(bookTaskId: string | string[], operateBookType = OperateBookType.BOOKTASK as OperateBookType): Promise<GeneralResponse.ErrorItem | GeneralResponse.SuccessItem> {
try {
let bookTask = await this.bookServiceBasic.GetBookTaskDataById(bookTaskId);
await this.bookServiceBasic.ResetBookTask(bookTaskId);
// 数据库删除完毕,看是删除对应的文件(主要是图片)
let imageFolder = bookTask.imageFolder;
// 整个删掉在重建
if (await CheckFileOrDirExist(imageFolder)) {
await DeleteFolderAllFile(imageFolder)
let bookTasks = [] as Book.SelectBookTask[]
if (operateBookType == OperateBookType.BOOKTASK) {
let tempBookTask = await this.bookServiceBasic.GetBookTaskDataById(bookTaskId as string);
bookTasks.push(tempBookTask)
} else if (operateBookType == OperateBookType.ASSIGNBOOKTASK) {
for (let i = 0; i < bookTaskId.length; i++) {
const element = bookTaskId[i];
let tempBookTask = await this.bookServiceBasic.GetBookTaskDataById(element);
bookTasks.push(tempBookTask);
}
}
else {
throw new Error("无效的操作类型")
}
if (bookTasks.length <= 0) {
throw new Error("没有找到对应的小说任务")
}
for (let i = 0; i < bookTasks.length; i++) {
const element = bookTasks[i];
await this.bookServiceBasic.ResetBookTask(element.id);
// 数据库删除完毕,看是删除对应的文件(主要是图片)
let imageFolder = element.imageFolder;
// 整个删掉在重建
if (await CheckFileOrDirExist(imageFolder)) {
await DeleteFolderAllFile(imageFolder)
}
await CheckFolderExistsOrCreate(imageFolder)
}
await CheckFolderExistsOrCreate(imageFolder)
return successMessage(null, "重置小说数据成功", "BookTask_ReSetBookTask")
} catch (error) {
return errorMessage('重置小说批次数据失败,错误信息如下' + error.toString(), "BookTask_ReSetBookTask");
@@ -254,15 +273,32 @@ export class BookTask {
* 删除对应的小说批次数据
* @param bookTaskId 小说批次ID
*/
async DeleteBookTask(bookTaskId: string): Promise<GeneralResponse.ErrorItem | GeneralResponse.SuccessItem> {
async DeleteBookTask(bookTaskId: string | string[], operateBookType = OperateBookType.BOOKTASK as OperateBookType): Promise<GeneralResponse.ErrorItem | GeneralResponse.SuccessItem> {
try {
let bookTask = await this.bookServiceBasic.GetBookTaskDataById(bookTaskId);
let imageFolder = bookTask.imageFolder;
// 先删除每个批次对应的数据,然后删除批次
await this.bookServiceBasic.DeleteBookTaskData(bookTaskId);
// 删除成功,直接把对应的出图文件夹删掉
if (await CheckFileOrDirExist(imageFolder)) {
await DeleteFolderAllFile(imageFolder, true)
let bookTasks = [] as Book.SelectBookTask[]
if (operateBookType == OperateBookType.BOOKTASK) {
let tempBookTask = await this.bookServiceBasic.GetBookTaskDataById(bookTaskId as string);
bookTasks.push(tempBookTask)
} else if (operateBookType == OperateBookType.ASSIGNBOOKTASK) {
for (let i = 0; i < bookTaskId.length; i++) {
const element = bookTaskId[i];
let tempBookTask = await this.bookServiceBasic.GetBookTaskDataById(element);
bookTasks.push(tempBookTask);
}
} else {
throw new Error("无效的操作类型")
}
for (let i = 0; i < bookTasks.length; i++) {
const element = bookTasks[i];
let bookTask = await this.bookServiceBasic.GetBookTaskDataById(element.id);
let imageFolder = bookTask.imageFolder;
// 先删除每个批次对应的数据,然后删除批次
await this.bookServiceBasic.DeleteBookTaskData(element.id);
// 删除成功,直接把对应的出图文件夹删掉
if (await CheckFileOrDirExist(imageFolder)) {
await DeleteFolderAllFile(imageFolder, true)
}
}
return successMessage(null, "删除小说批次数据成功", "BookTask_DeleteBookTask")
} catch (error) {
+230 -6
View File
@@ -1,26 +1,36 @@
import { OperateBookType } from "../../../define/enum/bookEnum";
import { BookBackTaskStatus, BookBackTaskType, BookTaskStatus, OperateBookType, TaskExecuteType } from "../../../define/enum/bookEnum";
import { Book } from "../../../model/book";
import { errorMessage, successMessage } from "../../Public/generalTools";
import { GeneralResponse } from "../../../model/generalResponse";
import { Setting } from '../../../main/setting/setting'
import path from 'path'
import { CheckFolderExistsOrCreate } from "../../../define/Tools/file";
import { CheckFileOrDirExist, CheckFolderExistsOrCreate, CopyFileOrFolder, GetFilesWithExtensions } from "../../../define/Tools/file";
import fs from 'fs'
import { ClipDraft } from '../../Public/clipDraft'
import { BookServiceBasic } from "../ServiceBasic/bookServiceBasic";
import { isEmpty } from "lodash";
import JianyingService from "../jianying/jianyingService";
import { DEFINE_STRING } from "../../../define/define_string";
import { define } from "../../../define/define";
import { ValidateJson } from "../../../define/Tools/validate";
import BookSetting from "@/main/setting/bookSetting";
import util from 'util';
import { spawn, exec } from 'child_process';
import { SendMessageToRenderer } from "../globalService";
const execAsync = util.promisify(exec);
export class BookVideo {
setting: Setting
bookServiceBasic: BookServiceBasic
jianyingService: JianyingService
bookSetting: BookSetting
constructor() {
this.setting = new Setting(global)
this.bookServiceBasic = new BookServiceBasic()
this.jianyingService = new JianyingService()
}
//#region 引用主小说相关数据
/**
* 将小说中的生成视频的数据,写道小说批次任务中
@@ -29,7 +39,6 @@ export class BookVideo {
*/
async UseBookVideoDataToBookTask(id: string, operateBookType: OperateBookType) {
try {
console.log(id, operateBookType)
let book = undefined as Book.SelectBook;
let bookTasks = undefined as Book.SelectBookTask[];
if (operateBookType == OperateBookType.BOOK) {
@@ -61,7 +70,7 @@ export class BookVideo {
// 将修改数据放在一个事务中
for (let i = 0; i < bookTasks.length; i++) {
const element = bookTasks[i];
this.bookServiceBasic.UpdetedBookTaskData(element.id, {
await this.bookServiceBasic.UpdetedBookTaskData(element.id, {
backgroundMusic: book.backgroundMusic,
friendlyReminder: book.friendlyReminder,
draftSrtStyle: book.draftSrtStyle,
@@ -81,6 +90,10 @@ export class BookVideo {
}
}
//#endregion
//#region 生成配置文件,用于生成草稿或者是合成视频
/**
* 生成配置文件,用于生成草稿或者是合成视频
* @param book 小说数据
@@ -98,7 +111,7 @@ export class BookVideo {
}
// 开始生成配置文件
let configPath = path.join(book.bookFolderPath, "scripts/config.json");
let configPath = path.join(book.bookFolderPath, `scripts/${bookTask.name}_config.json`);
await CheckFolderExistsOrCreate(path.dirname(configPath));
// 开始配置
@@ -145,11 +158,16 @@ export class BookVideo {
}
// 完毕,将数据写出
await fs.promises.writeFile(configPath, JSON.stringify(configData));
// 复制一个到config.json中
await CopyFileOrFolder(configPath, path.join(book.bookFolderPath, 'scripts/config.json'));
} catch (error) {
throw error
}
}
//#endregion
//#region 添加剪映草稿
/**
* 添加剪映草稿
@@ -159,7 +177,6 @@ export class BookVideo {
*/
async AddJianyingDraft(id: string | string[], operateBookType: OperateBookType): Promise<GeneralResponse.ErrorItem | GeneralResponse.SuccessItem> {
try {
console.log(id, operateBookType)
let book = undefined as Book.SelectBook
let bookTasks = [] as Book.SelectBookTask[]
if (operateBookType == OperateBookType.ASSIGNBOOKTASK) {
@@ -231,4 +248,211 @@ export class BookVideo {
return errorMessage('添加剪映草稿失败,错误信息如下:' + error.toString(), "BookTask_AddJianyingDraft");
}
}
//#endregion
//#region 添加合成视频任务
async AddGenerateVideoTask(id: string | string[], operateBookType: OperateBookType): Promise<GeneralResponse.ErrorItem | GeneralResponse.SuccessItem> {
try {
let book = undefined as Book.SelectBook
let bookTasks = [] as Book.SelectBookTask[]
if (operateBookType == OperateBookType.ASSIGNBOOKTASK) {
if (id.length <= 0) {
throw new Error("没有找到小说批次任务,请检查");
}
for (let i = 0; i < id.length; i++) {
const element = id[i];
let tempBookTask = await this.bookServiceBasic.GetBookTaskDataById(element as string);
bookTasks.push(tempBookTask)
if (book == undefined) {
book = await this.bookServiceBasic.GetBookDataById(tempBookTask.bookId);
}
// 这边要将主小说的数据写入到选中批次中
let useRes = await this.UseBookVideoDataToBookTask(element, OperateBookType.BOOKTASK);
if (useRes.code == 0) {
throw new Error(useRes.message);
}
}
} else if (operateBookType == OperateBookType.BOOKTASK) {
// 直接获取对应的数据
let tempBookTask = await this.bookServiceBasic.GetBookTaskDataById(id as string);
if (tempBookTask == null) {
throw new Error("没有找到小说批次任务,请检查");
}
bookTasks = [tempBookTask]
book = await this.bookServiceBasic.GetBookDataById(tempBookTask.bookId);
if (book == null) {
throw new Error
}
} else {
throw new Error("未知的操作类型");
}
if (bookTasks.length <= 0) {
throw new Error("没有找到小说批次任务,请检查")
}
let result = []
for (let i = 0; i < bookTasks.length; i++) {
const element = bookTasks[i];
await this.GenerateConfigFile(book, element);
// 数据处理完毕,开始添加任务
await this.bookServiceBasic.AddBookBackTask(book.id, BookBackTaskType.COMPOSING, TaskExecuteType.AUTO, element.id, null, DEFINE_STRING.BOOK.GENERATE_VIDEO_RETURN);
// 修改状态
await this.bookServiceBasic.UpdetedBookTaskData(element.id, {
status: BookTaskStatus.COMPOSING,
});
result.push(element.name);
}
// 添加完毕
return successMessage(result, `${result.join('\n')} ${'\n'} 合成视频任务添加成功`, "BookTask_AddGenerateVideoTask")
} catch (error) {
return errorMessage('添加剪映草稿失败,错误信息如下:' + error.toString(), "BookTask_AddJianyingDraft");
}
}
//#endregion
//#region 合成视频任务执行
/**
* 合成视频任务执行
* @param task
*/
async GenerateVideo(task: TaskModal.Task): Promise<GeneralResponse.ErrorItem | GeneralResponse.SuccessItem> {
try {
console.log(task);
this.bookSetting = new BookSetting();
let bookTask = await this.bookServiceBasic.GetBookTaskDataById(task.bookTaskId);
let book = await this.bookServiceBasic.GetBookDataById(bookTask.bookId);
if (isEmpty(bookTask.srtPath) || !await CheckFileOrDirExist(bookTask.srtPath)) {
throw new Error("没有找到对应的SRT文件,请检查");
}
if (isEmpty(bookTask.audioPath) || !await CheckFileOrDirExist(bookTask.audioPath)) {
throw new Error("没有找到对应的音频文件,请检查");
}
if (isEmpty(bookTask.backgroundMusic)) {
throw new Error("没有找到对应的背景音乐文件夹,请检查");
}
let videoConfigString = await fs.promises.readFile(define.video_config, 'utf-8');
if (!ValidateJson(videoConfigString)) {
throw new Error("视频配置文件格式错误,请检查");
}
// 检查图片是不是出完了
let bookTaskDetails = await this.bookServiceBasic.GetBookTaskDetailData({
bookTaskId: bookTask.id
});
for (let i = 0; i < bookTaskDetails.length; i++) {
const element = bookTaskDetails[i];
if (isEmpty(element.outImagePath) || !await CheckFileOrDirExist(element.outImagePath)) {
throw new Error("存在没有出完的图片或者图片不存在,请检查");
}
}
// 检查分镜信息和出入的图片数量是不是一致
let outImages = await GetFilesWithExtensions(bookTask.imageFolder, ['.png']);
if (outImages.length != bookTaskDetails.length) {
throw new Error("分镜信息和出图文件夹中图片数量不一致,请检查");
}
let videoConfig = JSON.parse(videoConfigString);
let assRandomIndex = Math.floor(Math.random() * videoConfig.assConfig.length)
let outMp4 = path.join(
book.bookFolderPath,
bookTask.name + '.mp4'
)
// 将所有的数据写入到配置文件中
let obj = {
srt_path: bookTask.srtPath,
// 字幕样式(需要随机)
srt_style: isEmpty(bookTask.draftSrtStyle) ? videoConfig.assConfig[assRandomIndex] : videoConfig.assConfig[assRandomIndex],
audio_path: bookTask.audioPath,
background_music_folder: await this.bookSetting.GetBackgroundMusicFolder(bookTask.backgroundMusic),
// 水印设置(需要随机)
friendly_reminder: await this.bookSetting.GetWatermarkSetting(),
video_resolution_x: videoConfig.video_resolution_x,
video_resolution_y: videoConfig.video_resolution_y,
outpue_file: outMp4,
image_folder: bookTask.imageFolder,
srt_config: path.join(book.bookFolderPath, `scripts/${bookTask.name}_config.json`),
mp4_file_txt: path.join(book.bookFolderPath, `scripts/${bookTask.name}.txt`),
status: 'no',
audio_sound_size: videoConfig.audioSoundSize,
background_music_sound_size: videoConfig.backgroundMusicSoundSize,
keyFrame: videoConfig.keyframe,
frameRate: videoConfig.frameRate,
bitRate: videoConfig.bitRate,
stdout: '',
stderr: ""
}
// 写出配置文件
let configPath = path.join(book.bookFolderPath, `scripts/${bookTask.name}_video_config.json`);
await fs.promises.writeFile(configPath, JSON.stringify(obj), 'utf-8');
let scriptPath = path.join(define.scripts_path, 'Lai.exe')
let gpu = global.gpu.type
if (videoConfig.libx264) {
gpu = 'OTHER'
}
// 执行生成图片的脚本
let script = `cd "${define.scripts_path}" && "${scriptPath}" -c "${configPath.replaceAll("\\", '/')}" "${gpu}"`
const output = await execAsync(script, { maxBuffer: 1024 * 1024 * 10, encoding: 'utf-8' })
if (output.stderr != '') {
obj.status = 'video_error'
obj.stdout = output.stdout
obj.stderr = output.stderr
throw new Error(output.stderr)
} else {
obj.status = 'video_ok'
obj.stdout = output.stdout
obj.stderr = output.stderr
}
// 检查输出文件是不是存在
if (!CheckFileOrDirExist(outMp4)) {
throw new Error("生成的视频文件不存在,请检查")
}
// 修改数据
await this.bookServiceBasic.UpdetedBookTaskData(task.bookTaskId, {
status: BookTaskStatus.COMPOSING_DONE,
generateVideoPath: path.relative(define.project_path, outMp4)
})
await this.bookServiceBasic.UpdateTaskStatus({
id: task.id,
status: BookBackTaskStatus.DONE
})
SendMessageToRenderer({
code: 1,
id: task.bookTaskDetailId,
data: BookTaskStatus.COMPOSING_DONE
}, task.messageName);
return successMessage(null, "合成视频任务执行成功", "BookTask_GenerateVideo");
} catch (error) {
let message = '合成视频任务执行失败,错误信息如下:' + error.toString()
// 失败
await this.bookServiceBasic.UpdetedBookTaskData(task.bookTaskId, {
status: BookTaskStatus.COMPOSING_FAIL,
errorMsg: error.message
})
await this.bookServiceBasic.UpdateTaskStatus({
id: task.id,
status: BookBackTaskStatus.FAIL,
errorMessage: message
})
SendMessageToRenderer({
code: 1,
id: task.bookTaskDetailId,
data: BookTaskStatus.COMPOSING_FAIL,
message: message
}, task.messageName);
return errorMessage(message, "BookTask_GenerateVideo");
}
}
//#endregion
}
+2 -4
View File
@@ -401,7 +401,7 @@ export class MJOpt {
let characterString = '' // 人物
let characterUrl = ''
let sceneString = "" // 场景
// 获取当前的自定义风格的垫图字符串
if (book.type == BookType.ORIGINAL) {
let sceneIds = element.sceneTags ? element.sceneTags : []
@@ -588,7 +588,6 @@ export class MJOpt {
} else {
if (task_res.progress == 100) {
task_res.type == MJRespoonseType.FINISHED;
console.log(task.id, "22222")
await this.bookServiceBasic.UpdateTaskStatus({
id: task.id,
status: BookBackTaskStatus.DONE
@@ -597,7 +596,6 @@ export class MJOpt {
let imagePath = path.join(book.bookFolderPath, `data\\MJOriginalImage\\${task_res.messageId}.png`);
await CheckFolderExistsOrCreate(path.dirname(imagePath))
await this.tools.downloadFileUrl(task_res.imageClick, imagePath)
debugger
// 进行图片裁剪
let imageRes = await ImageSplit(imagePath, bookTaskDetail.name, path.join(book.bookFolderPath, 'data\\MJOriginalImage'));
if (imageRes && imageRes.length < 4) {
@@ -606,7 +604,7 @@ export class MJOpt {
// 修改数据库数据,将图片保存到对应的文件夹中
let firstImage = imageRes[0];
if (book.type == BookType.ORIGINAL) {
if (book.type == BookType.ORIGINAL && bookTask.name == "output_00001") {
await CopyFileOrFolder(firstImage, path.join(book.bookFolderPath, `tmp\\input\\${bookTaskDetail.name}.png`));
}
let out_file = path.join(bookTask.imageFolder, `${bookTaskDetail.name}.png`)
+148 -3
View File
@@ -4,15 +4,20 @@ import { checkStringValueAddSuffix, errorMessage, successMessage } from "../../P
import { define } from '../../../define/define'
import fs from "fs";
import { ImageStyle } from "../Book/imageStyle";
import { BookBackTaskStatus, BookType, OperateBookType } from "../../../define/enum/bookEnum";
import { BookBackTaskStatus, BookTaskStatus, BookType, OperateBookType } from "../../../define/enum/bookEnum";
import { isEmpty } from "lodash";
import { BookServiceBasic } from "../ServiceBasic/bookServiceBasic";
import { PresetService } from "../presetService";
import path from "path";
import axios from "axios";
import { CheckFolderExistsOrCreate, CopyFileOrFolder, DeleteFileExifData } from "../../../define/Tools/file";
import { CheckFileOrDirExist, CheckFolderExistsOrCreate, CopyFileOrFolder, DeleteFileExifData } from "../../../define/Tools/file";
import { Base64ToFile } from "../../../define/Tools/image";
import { MJAction, MJImageType } from "../../../define/enum/mjEnum";
import { DEFINE_STRING } from "../../../define/define_string";
import { OtherData, ResponseMessageType } from "../../../define/enum/softwareEnum";
import util from 'util';
import { exec } from 'child_process';
const execAsync = util.promisify(exec)
const fspromise = fs.promises
export class SDOpt {
@@ -35,6 +40,14 @@ export class SDOpt {
return sdSetting
}
private sendChangeMessage(data: GeneralResponse.MessageResponse, message_name: string = DEFINE_STRING.BOOK.MAIN_DATA_RETURN) {
if (!message_name) {
message_name = DEFINE_STRING.BOOK.MAIN_DATA_RETURN
}
global.newWindow[0].win.webContents.send(message_name, data)
}
//#region 合并提示词
/**
@@ -300,7 +313,7 @@ export class SDOpt {
// 这边去图片信息
await DeleteFileExifData(path.join(define.package_path, 'exittool/exiftool.exe'), infoImgPath, imgPath);
// 写出去
if (bookTask.name == 'output_00001') {
if (bookTask.name == 'output_00001' && book.type == BookType.ORIGINAL) {
// 复制一个到input
let inputImgPath = path.join(inputFolder, `${bookTaskDetail.name}.png`)
await CopyFileOrFolder(imgPath, inputImgPath)
@@ -359,6 +372,16 @@ export class SDOpt {
id: task.bookTaskDetailId
})
await this.bookServiceBasic.UpdateBookTaskDetail(task.bookTaskDetailId, {
status: BookTaskStatus.IMAGE_FAIL,
})
await this.bookServiceBasic.UpdateTaskStatus({
id: task.id,
status: BookBackTaskStatus.FAIL,
errorMessage: errorMsg
})
global.newWindow[0].win.webContents.send(task.messageName, {
code: 0,
message: errorMsg,
@@ -371,4 +394,126 @@ export class SDOpt {
}
}
//#endregion
//#region 反推
/**
* SD 执行单个反推任务
* @param task 对应的任务
* @returns
*/
async SDReversePrompt(task: TaskModal.Task): Promise<GeneralResponse.ErrorItem | GeneralResponse.SuccessItem> {
try {
console.log(task)
if (task.bookTaskDetailId == OtherData.DEFAULT) { // 反推整个任务的提示词
let bookTask = await this.bookServiceBasic.GetBookTaskDataById(task.bookTaskId);
let book = await this.bookServiceBasic.GetBookDataById(bookTask.bookId);
let bookTaskDetail = await this.bookServiceBasic.GetBookTaskDetailData({
bookTaskId: task.bookTaskId
});
let oldImages = []
for (let i = 0; i < bookTaskDetail.length; i++) {
const element = bookTaskDetail[i];
oldImages.push({
id: element.id,
name: element.name,
imagePath: element.oldImage
})
}
// 判断是不是有为空的
let imagePaths = [] as string[]
for (let i = 0; i < oldImages.length; i++) {
const element = oldImages[i];
if (!isEmpty(element.imagePath)) {
imagePaths.push(element.imagePath)
}
}
if (imagePaths.length == 0) {
throw new Error("没有找到需要反推的图片,请先生成图片")
}
let inputTxt = path.join(book.bookFolderPath, 'tmp/input/input.txt')
await fspromise.writeFile(inputTxt, imagePaths.join('\n'), 'utf-8')
let exe = path.join(define.scripts_path, 'Lai.exe')
let scripts = `cd "${define.scripts_path}" && ${exe} -pt "${inputTxt}"`;
// 完成了反推
// 开始写出数据
const output = await execAsync(scripts, { maxBuffer: 1024 * 1024 * 10, encoding: 'utf-8' })
for (let i = 0; i < oldImages.length; i++) {
const element = oldImages[i];
let tagTxt = path.join(path.dirname(element.imagePath), `${element.name}.txt`)
if (!await CheckFileOrDirExist(tagTxt)) {
throw new Error("反推失败,没有找到反推出来的提示词文件,请检查")
}
let content = await fspromise.readFile(tagTxt, 'utf-8');
content = content.replace(/\r?\n/g, '');
// 更新数据库
await this.bookServiceBasic.UpdateBookTaskDetail(element.id, {
gptPrompt: content
});
// 主动返回任务状态
this.sendChangeMessage({
code: 1,
type: ResponseMessageType.SD_REVERSE,
id: element.id,
data: content
}, task.messageName);
}
} else { // 反推单个分镜的提示词
let bookTaskDetail = await this.bookServiceBasic.GetBookTaskDetailDataById(task.bookTaskDetailId);
let imagePath = bookTaskDetail.oldImage;
if (isEmpty(imagePath)) {
throw new Error("当前任务没有生成图片,请先生成图片")
}
if (!await CheckFileOrDirExist(imagePath)) {
throw new Error("当前任务生成图片不存在,请先生成图片")
}
// 开始执行exe进行反推
let exe = path.join(define.scripts_path, 'Lai.exe')
let scripts = `cd "${define.scripts_path}" && ${exe} -ps "${imagePath}"`;
const output = await execAsync(scripts, { maxBuffer: 1024 * 1024 * 10, encoding: 'utf-8' })
// 反推成功
let imageName = path.basename(imagePath, path.extname(imagePath));
let txt = path.join(path.dirname(imagePath), `${imageName}.txt`)
if (!await CheckFileOrDirExist(txt)) {
throw new Error("反推失败,没有找到反推出来的提示词文件,请检查")
}
let content = await fspromise.readFile(txt, 'utf-8');
content = content.replace(/\r?\n/g, '');
// 更新数据库
await this.bookServiceBasic.UpdateBookTaskDetail(task.bookTaskDetailId, {
gptPrompt: content
});
await this.bookServiceBasic.UpdateTaskStatus({
id: task.id,
status: BookBackTaskStatus.DONE
})
// 主动返回任务状态
this.sendChangeMessage({
code: 1,
type: ResponseMessageType.SD_REVERSE,
id: task.bookTaskDetailId,
data: content
}, task.messageName);
}
} catch (error) {
let errorMsg = "SD反推失败,错误信息如下:" + error.toString()
await this.bookServiceBasic.UpdateTaskStatus({
id: task.id,
status: BookBackTaskStatus.FAIL,
errorMessage: errorMsg
})
return errorMessage(errorMsg, "SDOpt_SDReversePrompt");
}
}
//#endregion
}
@@ -32,8 +32,8 @@ class BookServiceBasic {
}
//#region 事务操作
transaction = async (realm : any) => await this.bookBasic.transaction(realm);
transaction = async (realm: any) => await this.bookBasic.transaction(realm);
// (callback: (realm: any) => void) {
// this.bookService.transaction(() => {
// callback(this.bookService.realm)
@@ -64,16 +64,16 @@ class BookServiceBasic {
//#region 分镜任务
AddBookTaskDetail = async (bookTaskDetail: Book.SelectBookTaskDetail) => await this.bookTaskDetailServiceBasic.AddBookTaskDetail(bookTaskDetail);
GetBookTaskDetailDataById = async (bookTaskDetailId: string) => await this.bookTaskDetailServiceBasic.GetBookTaskDetailDataById(bookTaskDetailId);
GetBookTaskDetailData = async (condition: Book.QueryBookTaskDetailCondition, returnEmpty: boolean = false) => await this.bookTaskDetailServiceBasic.GetBookTaskDetailData(condition, returnEmpty);
UpdateBookTaskDetail = async (bookTaskDetailId: string, data: Book.SelectBookTaskDetail) => await this.bookTaskDetailServiceBasic.UpdateBookTaskDetail(bookTaskDetailId, data);
UpdateBookTaskStatus = async (bookTaskDetailId: string, status: BookTaskStatus,errorMsg? : string) => await this.bookTaskDetailServiceBasic.UpdateBookTaskStatus(bookTaskDetailId, status,errorMsg);
DeleteBookTaskDetailReversePromptById = async (bookTaskDetailId: string, reversePromptId: string) => await this.bookTaskDetailServiceBasic.DeleteBookTaskDetailReversePromptById(bookTaskDetailId);
UpdateBookTaskStatus = async (bookTaskDetailId: string, status: BookTaskStatus, errorMsg?: string) => await this.bookTaskDetailServiceBasic.UpdateBookTaskStatus(bookTaskDetailId, status, errorMsg);
DeleteBookTaskDetailReversePromptById = async (bookTaskDetailId: string) => await this.bookTaskDetailServiceBasic.DeleteBookTaskDetailReversePromptById(bookTaskDetailId);
DeleteBoookTaskDetailGenerateImage = async (bookTaskDetailId: string) => await this.bookTaskDetailServiceBasic.DeleteBoookTaskDetailGenerateImage(bookTaskDetailId);
UpdateBookTaskDetailReversePrompt = async (bookTaskDetailId: string, reversePromptId: string, data: Book.ReversePrompt) => await this.bookTaskDetailServiceBasic.UpdateBookTaskDetailReversePrompt(bookTaskDetailId, reversePromptId, data);
UpdateBookTaskDetailMjMessage = async (bookTaskDetailId: string, mjMessage: Book.MJMessage) => await this.bookTaskDetailServiceBasic.UpdateBookTaskDetailMjMessage(bookTaskDetailId, mjMessage);
DeleteBookTaskDetailData = async (condition: Book.DeleteBookTaskDetailCondition) => await this.bookTaskDetailServiceBasic.DeleteBookTaskDetail(condition);
//#endregion
@@ -89,6 +89,16 @@ export default class BookTaskDetailServiceBasic {
this.bookTaskDetailService.DeleteBookTaskDetailReversePromptById(bookTaskDetailId)
}
/**
* 删除指定的小说分镜数据
* @param condition
*/
async DeleteBookTaskDetail(condition: Book.QueryBookTaskDetailCondition): Promise<void> {
await this.InitService();
this.bookTaskDetailService.DeleteBookTaskDetail(condition)
}
/**
* 删除指定的小说分镜生成的图片
* @param bookTaskDetailId
@@ -354,7 +354,6 @@ export class SubtitleService {
bookTaskId: bookTaskId
}, true)
debugger
// 获取SD设置
let sdConifg = JSON.parse(await fs.promises.readFile(define.sd_setting, 'utf-8'));
let adetailer = false;
+14
View File
@@ -0,0 +1,14 @@
import { DEFINE_STRING } from "@/define/define_string"
import { GeneralResponse } from "@/model/generalResponse"
/**
* 主动返回前端的消息
* @param data 返回的数据
* @param message_name 消息名称
*/
export function SendMessageToRenderer(data: GeneralResponse.MessageResponse, message_name: string = DEFINE_STRING.BOOK.MAIN_DATA_RETURN) {
if (!message_name) {
message_name = DEFINE_STRING.BOOK.MAIN_DATA_RETURN
}
global.newWindow[0].win.webContents.send(message_name, data)
}
+4 -3
View File
@@ -36,9 +36,8 @@ class JianyingService {
* 获取剪映草稿的关键帧和文本
* @param draftDir 草稿目录
* @param projectDir 项目目录
* @param packagePath 包路径
*/
public async GetDraftFrameAndText(draftDir: string, projectDir: string, packagePath: string) {
public async GetDraftFrameAndText(draftDir: string, projectDir: string, inputName: string = "input_crop"): Promise<void> {
try {
// 获取草稿文件路径
let draftJsonPath = path.resolve(draftDir, "draft_content.json");
@@ -59,7 +58,7 @@ class JianyingService {
await DeleteFolderAllFile(projectTmp);
}
// 创建输出文件夹
let projectInput = path.resolve(projectTmp, "input_crop");
let projectInput = path.resolve(projectTmp, inputName);
console.log("projectInput", projectInput);
// 获取剪映的轨道,并且判断是否包含一个video轨道和一个text轨道
@@ -98,6 +97,8 @@ class JianyingService {
console.log("GetDraftFrameAndText", jsonPath);
} catch (error) {
throw error;
} finally {
this.draftTimeLine = [];
}
}
+17 -1
View File
@@ -13,6 +13,7 @@ import { FluxOpt } from '../Flux/flux'
import { AsyncQueue } from '../../quene'
import { SoftWareServiceBasic } from '../ServiceBasic/softwareServiceBasic'
import { MJSetting } from '../../../model/Setting/mjSetting'
import { BookVideo } from '../Book/bookVideo'
export class TaskManager {
isExecuting: boolean = false;
@@ -24,6 +25,7 @@ export class TaskManager {
bookBackTaskListService!: BookBackTaskListService;
eventListeners: Record<string | number, Function[]> = {};
softWareServiceBasic: SoftWareServiceBasic
bookVideo: BookVideo
mjSetting: MJSetting.MjSetting
spaceTime: number = 5000;
@@ -252,6 +254,15 @@ export class TaskManager {
}, `${batch}_${task.id}`, batch, `${batch}_${task.id}_${new Date().getTime()}`)
}
/** 添加生成视频的后台任务 */
async AddGenerateVideo(task: TaskModal.Task) {
let batch = task.messageName;
global.requestQuene.enqueue(async () => {
this.bookVideo = new BookVideo();
await this.bookVideo.GenerateVideo(task);
}, `${batch}_${task.id}`, batch, `${batch}_${task.id}_${new Date().getTime()}`)
}
/**
* 添加 flux forge 任务到内存队列中
@@ -299,7 +310,8 @@ export class TaskManager {
case BookBackTaskType.RECOGNIZE:
this.AddExtractSubtitlesData(task);
break;
case BookBackTaskType.MJ_REVERSE || BookBackTaskType.SD_REVERSE:
case BookBackTaskType.MJ_REVERSE:
case BookBackTaskType.SD_REVERSE:
this.AddSingleReversePrompt(task);
break;
case BookBackTaskType.FLUX_FORGE_IMAGE:
@@ -317,6 +329,10 @@ export class TaskManager {
case BookBackTaskType.D3_IMAGE:
this.AddD3Image(task);
break;
case BookBackTaskType.COMPOSING: // 合成视频
this.AddGenerateVideo(task);
break;
default:
throw new Error('未知的任务类型');
}
+2 -2
View File
@@ -506,7 +506,7 @@ async function getFrame(value) {
let package_path = define.package_path.replaceAll('\\', '/')
let jianying = new JianyingService()
await jianying.GetDraftFrameAndText(draft_path, out_dir, package_path)
await jianying.GetDraftFrameAndText(draft_path, out_dir)
global.newWindow[0].win.webContents.send(DEFINE_STRING.SHOW_MESSAGE_DIALOG, {
code: 1,
@@ -539,7 +539,7 @@ async function PushBackPrompt() {
let script = `cd "${define.scripts_path}" && "${py_path}" -p "${sd_config_path.replaceAll(
'\\',
'/'
)}" "input" "${global.config.project_path}"`
)}" "input_crop" "${global.config.project_path}"`
const output = await execAsync(script, { maxBuffer: 1024 * 1024 * 10, encoding: 'utf-8' })
global.newWindow[0].win.webContents.send(DEFINE_STRING.SHOW_MESSAGE_DIALOG, {
code: 1,
-5
View File
@@ -1,5 +0,0 @@
export class BookSetting {
constructor() {
}
}
+57
View File
@@ -0,0 +1,57 @@
import { define } from "@/define/define";
import { CheckFileOrDirExist } from "@/define/Tools/file";
import { ValidateJson } from "@/define/Tools/validate";
import fs from 'fs';
class BookSetting {
constructor() { }
/**
* 通过ID获取设置的背景音乐文件夹
* @param id 背景音乐文件夹ID
*/
async GetBackgroundMusicFolder(id: string): Promise<string> {
let clipSettingString = await fs.promises.readFile(define.clip_setting, 'utf-8');
if (!ValidateJson(clipSettingString)) {
throw new Error('Clip设置文件格式错误');
}
let clipSetting = JSON.parse(clipSettingString);
let backgroundMusicSetting = clipSetting.background_music_setting;
if (!backgroundMusicSetting) {
throw new Error('没有找到对应的背景音乐文件夹地址设置,请检查');
}
let clip = backgroundMusicSetting.find((clip: any) => clip.id === id);
if (clip == undefined) {
throw new Error('没有找到对应的背景音乐文件夹地址,请检查');
}
if (!await CheckFileOrDirExist(clip.folder_path)) {
throw new Error('背景音乐文件夹不存在,请检查');
}
return clip.folder_path;
}
/**
* 获取指定的水印设置,没有的话,随机返回一个
* @param id 水印设置ID
*/
async GetWatermarkSetting(id?: string) {
let videoSettingString = await fs.promises.readFile(define.video_config, 'utf-8');
if (!ValidateJson(videoSettingString)) {
throw new Error('Video设置文件格式错误');
}
let videoSetting = JSON.parse(videoSettingString);
let watermarkConfig = videoSetting.watermarkConfig;
if (!watermarkConfig) {
throw new Error('没有找到对应的水印设置,请检查');
}
if (id == undefined) {
return watermarkConfig[Math.floor(Math.random() * watermarkConfig.length)];
}
let watermark = watermarkConfig.find((watermark: any) => watermark.id === id);
if (watermark == undefined) {
throw new Error('没有找到对应的水印设置,请检查');
}
return watermark;
}
}
export default BookSetting;
+7
View File
@@ -272,6 +272,13 @@ declare namespace Book {
duration: number
}
type DeleteBookTaskDetailCondition = {
id?: string;
bookId?: string;
bookTaskId?: string;
name?: string;
}
//#region 替换小说数据
type BookReplaceData = {
+16 -6
View File
@@ -54,7 +54,7 @@ const book = {
//#endregion
//#region 小说通用操作
//#region bookTaskDetail通用操作
// 替换文案数据
ReplaceBookData: async (bookTaskId: string, replaceData: Book.BookReplaceData) =>
@@ -82,6 +82,9 @@ const book = {
bookTaskDetailId,
currentTime
),
/** 剪映抽帧,并判断是不是切割视频 */
JianyingFrame: async (bookTaskId: string, draftName: string) => await ipcRenderer.invoke(DEFINE_STRING.BOOK.JIANYING_FRAME, bookTaskId, draftName),
//#endregion
//#region 文案相关信息
@@ -160,6 +163,10 @@ const book = {
// 导入提示词
ImportGPTPrompt: async (bookTaskId: string, txtPath: string) => await ipcRenderer.invoke(DEFINE_STRING.BOOK.IMPORT_GPT_PROMPT, bookTaskId, txtPath),
/** 移除不想要的提示词 */
RemoveBadPrompt: async (bookTaskId: string) => await ipcRenderer.invoke(DEFINE_STRING.BOOK.REMOVE_BAD_PROMPT, bookTaskId),
//#endregion
//#region 图片相关
@@ -227,12 +234,12 @@ const book = {
await ipcRenderer.invoke(DEFINE_STRING.BOOK.ADD_NEW_BOOK_TASK, addBookTaskData),
// 重置小说批次数据
ReSetBookTask: async (bookTaskId) =>
await ipcRenderer.invoke(DEFINE_STRING.BOOK.RESET_BOOK_TASK, bookTaskId),
ReSetBookTask: async (bookTaskId: string | string[], operateBookType: OperateBookType) =>
await ipcRenderer.invoke(DEFINE_STRING.BOOK.RESET_BOOK_TASK, bookTaskId, operateBookType),
// 删除小说批次数据
DeleteBookTask: async (bookTaskId) =>
await ipcRenderer.invoke(DEFINE_STRING.BOOK.DELETE_BOOK_TASK, bookTaskId),
DeleteBookTask: async (bookTaskId: string | string[], operateBookType: OperateBookType) =>
await ipcRenderer.invoke(DEFINE_STRING.BOOK.DELETE_BOOK_TASK, bookTaskId, operateBookType),
// 一键生成所有图片
GenerateImageAll: async (bookTaskId, imageCategory) =>
@@ -261,8 +268,11 @@ const book = {
// 添加数据到剪映草稿
AddJianyingDraft: async (id: string | string[], operateBookType: OperateBookType) =>
await ipcRenderer.invoke(DEFINE_STRING.BOOK.ADD_JIANYING_DRAFT, id, operateBookType)
await ipcRenderer.invoke(DEFINE_STRING.BOOK.ADD_JIANYING_DRAFT, id, operateBookType),
/** 合成视频相关 */
AddGenerateVideoTask: async (id: string | string[], operateBookType: OperateBookType) =>
await ipcRenderer.invoke(DEFINE_STRING.BOOK.ADD_GENERATE_VIDEO_TASK, id, operateBookType)
//#endregion
}
export { book }
@@ -1,300 +0,0 @@
<template>
<div>
<n-dropdown trigger="hover" :options="reverseOptions" @select="ReverseSelect">
<n-button size="tiny" :color="softwareStore.SoftColor.BROWN_YELLOW" @click="AddReversePrompt">
单句反推
</n-button>
</n-dropdown>
<n-dropdown trigger="hover" :options="tranlateOptions" @select="ReverseSelect">
<n-button
style="margin-left: 5px"
size="tiny"
:color="softwareStore.SoftColor.BROWN_YELLOW"
@click="TranslateOne('translate_chinese')"
>
英文 2 中文
</n-button>
</n-dropdown>
<n-button
style="margin-left: 5px"
size="tiny"
:color="softwareStore.SoftColor.BROWN_YELLOW"
@click="SelectReversePrompt"
>
重选反推提示词
</n-button>
</div>
</template>
<script>
import { ref, onMounted, defineComponent, onUnmounted, toRaw, watch, h } from 'vue'
import { useMessage, useDialog, NDropdown, NButton } from 'naive-ui'
import { useSoftwareStore } from '../../../../../stores/software'
import { useReverseManageStore } from '../../../../../stores/reverseManage'
import { TranslateType } from '../../../../../define/enum/translate'
import { ContainsChineseOrPunctuation } from '../../../../../define/Tools/common'
import ReSelectReversePrompt from '../MJReverse/ReSelectReversePrompt.vue'
import { OperateBookType } from '../../../../../define/enum/bookEnum'
import { isEmpty } from 'lodash'
import { DEFINE_STRING } from '../../../../../define/define_string'
export default defineComponent({
components: { NDropdown, NButton },
props: ['initData', 'index'],
setup(props) {
let message = useMessage()
let dialog = useDialog()
let data = ref(props.initData)
let index = ref(props.index)
let softwareStore = useSoftwareStore()
let reverseManageStore = useReverseManageStore()
onMounted(async () => {})
/**
* 单句反推提示词
*/
async function AddReversePrompt() {
let res = await window.book.AddReversePrompt(
data.value.id,
OperateBookType.BOOKTASKDETAIL,
null
)
if (res.code == 0) {
message.error(res.message)
} else {
message.success('添加反推任务成功')
}
}
async function AddNextReversePrompt() {
let res = await window.book.AddReversePrompt(
data.value.id,
OperateBookType.UNDERBOOKTASK,
reverseManageStore.selectBook.type
)
if (res.code == 0) {
message.error(res.message)
} else {
message.success('添加反推任务成功')
}
}
/**
* 提示词菜单下拉操作
*/
async function ReverseSelect(key) {
switch (key) {
case 'remove_reverse': // 删除反推提示词
let res = await window.book.ResetGptReverseData(
data.value.id,
OperateBookType.BOOKTASKDETAIL,
reverseManageStore.selectBook.type
)
if (res.code == 0) {
message.error(res.message)
} else {
reverseManageStore.selectBookTaskDetail[props.index].gptPrompt = undefined
reverseManageStore.selectBookTaskDetail[props.index].reversePrompt = undefined
message.success('删除反推数据成功')
}
break
case 'translate_english': // 中文 2 英文
await TranslateOne('translate_english')
break
case 'down_translate_chinese': // 下翻译中文
await TranslateOne('down_translate_chinese')
break
case 'down_translate_english': // 下翻译英文
await TranslateOne('down_translate_english')
break
case 'down_reverse':
await AddNextReversePrompt()
break
default:
message.error('未知的操作类型')
break
}
}
// 处理翻译英文的数据
function TranslateOneParams(from, to) {
let translateData = []
if (!isEmpty(data.value.gptPrompt)) {
translateData.push({
text: data.value.gptPrompt,
from: from,
to: to,
type: TranslateType.GPT_PROMPT_TRANSLATE,
isSplit: false, // 该参数设置为true时,会将文本按照标点符号分割成多个句子进行翻译
bookTaskDetailId: data.value.id,
responseMessgeName: DEFINE_STRING.BOOK.REVERSE_PROMPT_TRANSLATE_RETURN
})
} else {
let reversePrompt = data.value.reversePrompt
if (reversePrompt) {
reversePrompt.forEach((item) => {
let temp_obj = undefined
if (from == 'zh' && to == 'en') {
// 判断是不是有中文
if (!ContainsChineseOrPunctuation(item.promptCN ? item.promptCN : item.prompt)) {
return
}
}
temp_obj = {
text: item.prompt,
from: from,
to: to,
type: TranslateType.REVERSE_PROMPT_TRANSLATE,
isSplit: false, // 该参数设置为true时,会将文本按照标点符号分割成多个句子进行翻译
bookTaskDetailId: data.value.id,
reversePromptId: item.id,
responseMessgeName: DEFINE_STRING.BOOK.REVERSE_PROMPT_TRANSLATE_RETURN
}
translateData.push(temp_obj)
})
}
}
return translateData
}
// 处理批次的翻译
function TranslateBatchParams(from, to) {
let translateData = []
for (let i = index.value; i < reverseManageStore.selectBookTaskDetail.length; i++) {
const element = reverseManageStore.selectBookTaskDetail[i]
// 这边判断GPT提示词是不是存在,存在的话翻译GPT提示词
if (!isEmpty(element.gptPrompt)) {
translateData.push({
text: element.gptPrompt,
from: from,
to: to,
type: TranslateType.GPT_PROMPT_TRANSLATE,
isSplit: false, // 该参数设置为true时,会将文本按照标点符号分割成多个句子进行翻译
bookTaskDetailId: element.id,
responseMessgeName: DEFINE_STRING.BOOK.REVERSE_PROMPT_TRANSLATE_RETURN
})
} else {
let reversePrompt = element.reversePrompt
if (reversePrompt) {
reversePrompt.forEach((item) => {
if (from == 'zh' && to == 'en') {
// 判断是不是有中文
if (!ContainsChineseOrPunctuation(item.promptCN ? item.promptCN : item.prompt)) {
return
}
}
let temp_obj = {
text: item.prompt,
from: from,
to: to,
type: TranslateType.REVERSE_PROMPT_TRANSLATE,
isSplit: false, // 该参数设置为true时,会将文本按照标点符号分割成多个句子进行翻译
bookTaskDetailId: element.id,
reversePromptId: item.id,
responseMessgeName: DEFINE_STRING.BOOK.REVERSE_PROMPT_TRANSLATE_RETURN
}
translateData.push(temp_obj)
})
}
}
}
return translateData
}
// 单句翻译
async function TranslateOne(key) {
let translateData = []
switch (key) {
case 'translate_english':
translateData = TranslateOneParams('zh', 'en')
break
case 'translate_chinese':
translateData = TranslateOneParams('en', 'zh')
break
case 'down_translate_english':
translateData = TranslateBatchParams('zh', 'en')
break
case 'down_translate_chinese':
translateData = TranslateBatchParams('en', 'zh')
break
default:
message.error('未知的操作类型')
break
}
if (translateData.length <= 0) {
message.error('没有需要翻译的数据')
return
}
softwareStore.spin.spinning = true
softwareStore.spin.tip = `翻译中 ... 0 / ${translateData.length}`
// 开始翻译
let res = await window.translate.TranslateNowReturn(translateData)
softwareStore.spin.spinning = false
window.api.showGlobalMessageDialog(res)
}
/**
* 重选反推提示词
*/
async function SelectReversePrompt() {
// 判断当前是不是又反推提示词
if (!data.value.reversePrompt || data.value.reversePrompt.length <= 0) {
message.error('当前没有反推提示词')
return
}
// ImportWordAndSrt
dialog.create({
showIcon: false,
title: `重选MJ反推提示词 ${data.value.name}`,
closeOnEsc: false,
content: () =>
h(ReSelectReversePrompt, {
reversePrompts: data.value.reversePrompt,
index: index.value
}),
style: `width : 750px;`,
maskClosable: false
})
}
return {
index,
data,
softwareStore,
ReverseSelect,
TranslateOne,
reverseManageStore,
SelectReversePrompt,
AddReversePrompt,
reverseOptions: [
{
label: '下推理',
key: 'down_reverse'
},
{
label: '删除反推提示词',
key: 'remove_reverse'
}
],
tranlateOptions: [
{
label: '中文 2 英文',
key: 'translate_english'
},
{
label: '下翻译英文',
key: 'down_translate_english'
},
{
label: '下翻译中文',
key: 'down_translate_chinese'
}
]
}
}
})
</script>
@@ -1,165 +0,0 @@
<template>
<div style="display: flex; justify-content: space-between; white-space: nowrap">
<div style="display: flex">
<div>反推/GPT提示词</div>
<n-button
v-if="reverseManageStore.selectBook.type == bookType.MJ_REVERSE"
type="text"
:color="softwareStore.SoftColor.ZHUYANTUO"
size="tiny"
style="margin-left: 10px"
@click="SelectPromptIndex"
>选择提示词</n-button
><n-button
v-if="reverseManageStore.selectBook.type == bookType.MJ_REVERSE"
type="text"
:color="softwareStore.SoftColor.ZHUYANTUO"
size="tiny"
style="margin-left: 5px"
@click="SelectStyle"
>风格</n-button
>
</div>
<div>
<n-popover trigger="hover">
<template #trigger>
<n-button
color="#7c461e"
@click="SelectReversePromptAndReplace"
text
style="font-size: 24px"
>
<n-icon>
<FindReplaceRound />
</n-icon>
</n-button>
</template>
<span>批量替换反推提示词</span>
</n-popover>
</div>
</div>
</template>
<script>
import { ref, onMounted, defineComponent, onUnmounted, toRaw, h, watch } from 'vue'
import { useMessage, useDialog, NButton, NPopover, NIcon } from 'naive-ui'
import { useReverseManageStore } from '../../../../../stores/reverseManage'
import { BookRepalceDataType, BookType } from '../../../../../define/enum/bookEnum'
import { useSoftwareStore } from '../../../../../stores/software'
import SelectMJReversePrompt from '../MJReverse/SelectMJReversePrompt.vue'
import SelectImageStyle from '../../Components/SelectImageStyle.vue'
import FindReplaceRound from '../../Icon/FindReplaceRound.vue'
import DatatableHeaderAfterGptSelectAndReplace from './DatatableHeaderAfterGptSelectAndReplace.vue'
export default defineComponent({
components: { NButton, NPopover, FindReplaceRound, NIcon },
setup() {
let message = useMessage()
let reverseManageStore = useReverseManageStore()
let softwareStore = useSoftwareStore()
let bookType = ref(BookType)
let dialog = useDialog()
let selectStyleRef = ref(null)
let da = undefined
/**
* 选择MJ反推提示词。用于生图
*/
async function SelectPromptIndex() {
// 判断是不是MJ反推,判断是不是所有的都有提示词
if (reverseManageStore.selectBook.type != BookType.MJ_REVERSE) {
message.error('只有MJ反推可以使用该按钮')
return
}
let hasReversePrompt = false
// 判断是不是所有的都有提示词,只要有一个有,就可以
for (let index = 0; index < reverseManageStore.selectBookTaskDetail.length; index++) {
const element = reverseManageStore.selectBookTaskDetail[index]
if (element.reversePrompt && element.reversePrompt.length > 0) {
hasReversePrompt = true
break
}
}
if (!hasReversePrompt) {
message.error('请先进行MJ反推')
return
}
dialog.create({
title: '选择反推提示词',
content: () => h(SelectMJReversePrompt),
style: 'width : 500px',
showIcon: false
})
}
// 选择风格
async function SelectStyle() {
// 先获取风格
// 判断当前数据是不是存在
// 处理数据。获取当前的所有的数据
let dialogWidth = window.innerWidth * 0.8
let dialogHeight = window.innerHeight * 0.9
// ImportWordAndSrt
dialog.create({
showIcon: false,
closeOnEsc: false,
content: () =>
h(SelectImageStyle, {
height: dialogHeight,
ref: selectStyleRef,
imageStyle: reverseManageStore.selectBookTask.imageStyle,
customizeImageStyle: reverseManageStore.selectBookTask.customizeImageStyle
}),
style: `width : ${dialogWidth}px; height : ${dialogHeight}px`,
maskClosable: false,
onClose: async () => {
console.log(selectStyleRef)
// 将数据保存到当前的数据中
// reverseManageStore.selectBookTask.styleList = toRaw(selectStyleRef.value.selectStyle)
// 保存当前的风格到数据库
let res = await window.book.SaveImageStyle(
toRaw(selectStyleRef.value.selectStyle),
reverseManageStore.selectBookTask.id
)
if (res.code == 0) {
message.error(res.message)
return
}
reverseManageStore.selectBookTask.imageStyle = res.data.imageStyle
reverseManageStore.selectBookTask.customizeImageStyle = res.data.customizeImageStyle
message.success('保存成功')
}
})
}
async function SelectReversePromptAndReplace() {
if (da) {
return
}
da = dialog.create({
title: '批量查询替换反推提示词',
showIcon: false,
maskClosable: false,
content: () =>
h(DatatableHeaderAfterGptSelectAndReplace, { type: BookRepalceDataType.GPT_PROMPT }),
onClose: () => {
da = undefined
}
})
}
return {
reverseManageStore,
bookType,
softwareStore,
SelectPromptIndex,
SelectStyle,
selectStyleRef,
SelectReversePromptAndReplace
}
}
})
</script>
@@ -1,215 +0,0 @@
<template>
<div>
<n-dropdown trigger="hover" :options="mergeOptions" @select="MergePrompt">
<n-button
size="tiny"
:color="softwareStore.SoftColor.BROWN_YELLOW"
@click="MergePrompt(undefined)"
>
单句合并
</n-button>
</n-dropdown>
<n-button
style="margin-left: 5px"
size="tiny"
:color="softwareStore.SoftColor.BROWN_YELLOW"
@click="SingleGenerateImage"
>
单句生图
</n-button>
<n-button
style="margin-left: 5px"
size="tiny"
:color="softwareStore.SoftColor.BROWN_YELLOW"
@click="UnderGenerateImage"
>
下生图
</n-button>
<n-button
style="margin-left: 5px"
size="tiny"
:color="softwareStore.SoftColor.BROWN_YELLOW"
@click="ImportMJImageUrl"
>
导入图片
</n-button>
</div>
<n-input
type="textarea"
size="tiny"
@input="InputDebounced"
v-model:value="data.prompt"
:autosize="{ minRows: 6, maxRows: 6 }"
></n-input>
</template>
<script>
import { ref, onMounted, defineComponent, onUnmounted, toRaw, watch, h } from 'vue'
import { useMessage, useDialog, NInput, NButton, NDropdown } from 'naive-ui'
import { useSoftwareStore } from '../../../../../stores/software'
import { useReverseManageStore } from '../../../../../stores/reverseManage'
import { OperateBookType } from '../../../../../define/enum/bookEnum'
import { BookImageCategory } from '../../../../../define/enum/bookEnum'
import { debounce } from 'lodash'
import InputDialogContent from '../../Original/Components/InputDialogContent.vue'
import { DEFINE_STRING } from '../../../../../define/define_string'
export default defineComponent({
components: { NInput, NButton, NDropdown },
props: ['initData', 'index'],
setup(props) {
let message = useMessage()
let softwareStore = useSoftwareStore()
let reverseManageStore = useReverseManageStore()
let data = ref(props.initData)
let image_url_ref = ref(null)
let dialog = useDialog()
onMounted(async () => {})
// 合并提示词
async function MergePrompt(key) {
let type = key
if (!key) {
if (reverseManageStore.selectBookTask.imageCategory == BookImageCategory.MJ) {
type = 'mj_merge'
} else if (reverseManageStore.selectBookTask.imageCategory == BookImageCategory.SD) {
type = 'sd_merge'
} else {
type = 'mj_merge'
}
}
// 开始进行合并,调用不同的合并模式
// 合并之前,要确认保存
softwareStore.spin.spinning = true
softwareStore.spin.tip = '合并命令中。。。'
let res = await window.book.MergePrompt(data.value.id, type, OperateBookType.BOOKTASKDETAIL)
softwareStore.spin.spinning = false
// 这边对返回的数据进行处理
if (res.code == 0) {
message.error(res.message)
return
}
for (let i = 0; i < res.data.length; i++) {
const element = res.data[i]
let selectBookDetailIndex = reverseManageStore.selectBookTaskDetail.findIndex(
(item) => item.id == element.id
)
reverseManageStore.selectBookTaskDetail[selectBookDetailIndex].prompt = element.prompt
}
message.success(res.message)
}
// 单个出图
async function SingleGenerateImage() {
let res = undefined
if (reverseManageStore.selectBookTask.imageCategory == BookImageCategory.MJ) {
res = await window.mj.AddMJGenerateImageTask(
data.value.id,
OperateBookType.BOOKTASKDETAIL,
DEFINE_STRING.BOOK.MJ_IMAGE_GENERATE_RETURN
)
} else if (reverseManageStore.selectBookTask.imageCategory == BookImageCategory.SD) {
// res = await window.sd.SingleGenerateImage(data.value.id, OperateBookType.BOOKTASKDETAIL)
message.error('暂不支该出图模式')
} else {
message.error('未知的出图模式')
return
}
if (res.code == 0) {
message.error(res.message)
return
}
message.success(res.message)
}
// 下生图
async function UnderGenerateImage() {
let res = undefined
if (reverseManageStore.selectBookTask.imageCategory == BookImageCategory.MJ) {
res = await window.mj.AddMJGenerateImageTask(
data.value.id,
OperateBookType.UNDERBOOKTASK,
DEFINE_STRING.BOOK.MJ_IMAGE_GENERATE_RETURN
)
} else if (reverseManageStore.selectBookTask.imageCategory == BookImageCategory.SD) {
// res = await window.sd.SingleGenerateImage(data.value.id, OperateBookType.UNDERBOOKTASK)
message.error('暂不支该出图模式')
} else {
message.error('未知的出图模式')
return
}
if (res.code == 0) {
message.error(res.message)
return
}
message.success(res.message)
}
let InputDebounced = debounce(handleInput, 1000)
async function handleInput() {
let res = await window.db.UpdateBookTaskDetailData(data.value.id, {
prompt: data.value.prompt
})
if (res.code == 0) {
message.error(res.message)
}
}
/**
* 写入图片的链接,将图片链接下载,并且分割
*/
async function ImportMJImageUrl() {
// 判断当前数据是不是存在
// 处理数据。获取当前的所有的数据
let dialogWidth = 400
let dialogHeight = 150
let da = dialog.create({
title: '添加图片链接/本地地址',
showIcon: false,
closeOnEsc: false,
content: () =>
h(InputDialogContent, {
ref: image_url_ref,
initData: null,
placeholder: '请输入图片链接/本地地址'
}),
style: `width : ${dialogWidth}px; min-height : ${dialogHeight}px`,
maskClosable: false,
onClose: async () => {
da?.destroy()
let row_image_url = image_url_ref.value.data
softwareStore.spin.spinning = true
softwareStore.spin.tip = '正在下载图片/分割图片中。。。'
let res = await window.book.DownloadImageUrlAndSplit(data.value.id, row_image_url)
softwareStore.spin.spinning = false
if (res.code == 0) {
message.error(res.message)
return
}
data.value.outImagePath = res.data.outImagePath
data.value.subImagePath = res.data.subImagePath
message.success(res.message)
}
})
}
return {
softwareStore,
reverseManageStore,
InputDebounced,
SingleGenerateImage,
MergePrompt,
data,
image_url_ref,
ImportMJImageUrl,
UnderGenerateImage,
mergeOptions: [
{ label: 'MJ模式合并', key: 'mj_merge' },
{ label: 'SD模式合并', key: 'sd_merge' }
]
}
}
})
</script>
@@ -33,7 +33,7 @@
</n-button>
</template>
<script>
<script setup>
import { ref, onMounted, defineComponent, h } from 'vue'
import { useMessage, NButton, useDialog } from 'naive-ui'
import { useReverseManageStore } from '../../../../../../stores/reverseManage.ts'
@@ -41,175 +41,173 @@ import { useSoftwareStore } from '../../../../../../stores/software.ts'
import { BookType, OperateBookType } from '../../../../../../define/enum/bookEnum.ts'
import ManageBookTaskGenerateInformation from './ManageBookTaskGenerateInformation.vue'
export default defineComponent({
components: {
NButton
},
let props = defineProps({
bookTask: undefined
})
props: ['bookTask'],
setup(props) {
let message = useMessage()
let dialog = useDialog()
let bookTask = ref(props.bookTask)
console.log('bookTask', bookTask.value)
let reverseManageStore = useReverseManageStore()
let softwareStore = useSoftwareStore()
let message = useMessage()
let dialog = useDialog()
let bookTask = ref(props.bookTask)
console.log('bookTask', bookTask.value)
let reverseManageStore = useReverseManageStore()
let softwareStore = useSoftwareStore()
onMounted(async () => {})
onMounted(async () => {})
/**
* 重置小说数据
* @param e
*/
async function ReSetBookTaskDetail(e) {
e.stopPropagation()
dialog.warning({
title: '重置小说任务',
positiveText: '确定',
negativeText: '取消',
content:
'确定要重置小说任务吗?该操作不可逆,会将对应的批次任务的数据清空,分镜数据全部删除,请谨慎操作!',
onPositiveClick: async () => {
let res = await window.book.ReSetBookTask(bookTask.value.id)
if (res.code == 1) {
message.success('重置小说任务成功')
} else {
message.error(res.message)
}
}
})
/**
* 重置小说数据
* @param e
*/
async function ReSetBookTaskDetail(e) {
e.stopPropagation()
dialog.warning({
title: '重置小说任务',
positiveText: '确定',
negativeText: '取消',
content:
'确定要重置小说任务吗?该操作不可逆,会将对应的批次任务的数据清空,分镜数据全部删除,请谨慎操作!',
onPositiveClick: async () => {
let res = await window.book.ReSetBookTask(bookTask.value.id)
if (res.code == 1) {
message.success('重置小说任务成功')
} else {
message.error(res.message)
}
}
})
}
/**
* 删除小说任务
*/
async function DeleteBookTask(e) {
e.stopPropagation()
dialog.warning({
title: '删除小说任务',
positiveText: '确定',
negativeText: '取消',
content:
'确定要删除小说任务吗?该操作不可逆,会将对应的批次任务数据和分镜数据全部删除,请谨慎操作!',
onPositiveClick: async () => {
let res = await window.book.DeleteBookTask(bookTask.value.id)
if (res.code == 1) {
// 加载任务列表
let bookTaskRes = await reverseManageStore.GetBookTaskDataFromDB({
bookId: reverseManageStore.selectBook.id
})
softwareStore.spin.spinning = false
if (bookTaskRes.code == 0) {
message.error(bookTaskRes.message)
return
}
// 删除成功
message.success('删除小说任务并刷新数据成功')
} else {
message.error(res.message)
}
/**
* 删除小说任务
*/
async function DeleteBookTask(e) {
e.stopPropagation()
dialog.warning({
title: '删除小说任务',
positiveText: '确定',
negativeText: '取消',
content:
'确定要删除小说任务吗?该操作不可逆,会将对应的批次任务数据和分镜数据全部删除,请谨慎操作!',
onPositiveClick: async () => {
let res = await window.book.DeleteBookTask(bookTask.value.id)
if (res.code == 1) {
// 加载任务列表
let bookTaskRes = await reverseManageStore.GetBookTaskDataFromDB({
bookId: reverseManageStore.selectBook.id
})
softwareStore.spin.spinning = false
if (bookTaskRes.code == 0) {
message.error(bookTaskRes.message)
return
}
})
// 删除成功
message.success('删除小说任务并刷新数据成功')
} else {
message.error(res.message)
}
}
})
}
async function hdImageFunc(id, operateBookType) {
let da = dialog.warning({
title: '高清提示',
content: `是否确认高清,当前的高清倍数为 ${softwareStore.globalSetting.hdScale} 倍,是否继续?`,
positiveText: '继续',
negativeText: '取消',
onPositiveClick: async () => {
da.destroy()
softwareStore.spin.spinning = true
softwareStore.spin.tip = '正在高清中。。。'
let res = await window.book.HDImage(
id,
softwareStore.globalSetting.hdScale ?? 2,
operateBookType
)
softwareStore.spin.spinning = false
if (res.code == 1) {
message.success('高清成功')
} else {
message.error(res.message)
}
}
})
}
/**
* 高清图片
*/
async function HDImage(e) {
e.stopPropagation()
async function hdImageFunc(id, operateBookType) {
let da = dialog.warning({
title: '高清提示',
content: `是否确认高清,当前的高清倍数为 ${softwareStore.globalSetting.hdScale} 倍,是否继续?`,
positiveText: '继续',
negativeText: '取消',
onPositiveClick: async () => {
da.destroy()
softwareStore.spin.spinning = true
softwareStore.spin.tip = '正在进行高清检查。。。'
let checkImage = await window.book.CheckImageFileSize(
bookTask.value.id,
10240,
OperateBookType.BOOKTASK
softwareStore.spin.tip = '正在高清中。。。'
let res = await window.book.HDImage(
id,
softwareStore.globalSetting.hdScale ?? 2,
operateBookType
)
softwareStore.spin.spinning = false
if (checkImage.code == 0) {
message.error(checkImage.message)
return
}
// 高清检查完成
if (checkImage.data && checkImage.data.length > 0) {
// 出现了图片大的,用户判断是不是需要高清
let msg = checkImage.data.map((item, index) => {
return item.outImagePath + '\n'
})
dialog.warning({
title: '高清警告',
content: '下面的图片大于10MB,是否继续高清?' + '\n' + msg,
positiveText: '确定',
negativeText: '取消',
maskClosable: false,
onPositiveClick: async () => {
alert('确认高清')
await hdImageFunc(bookTask.value.id, OperateBookType.BOOKTASK)
}
})
if (res.code == 1) {
message.success('高清成功')
} else {
// 没有就直接高清
message.error(res.message)
}
}
})
}
/**
* 高清图片
*/
async function HDImage(e) {
e.stopPropagation()
softwareStore.spin.spinning = true
softwareStore.spin.tip = '正在进行高清检查。。。'
let checkImage = await window.book.CheckImageFileSize(
bookTask.value.id,
10240,
OperateBookType.BOOKTASK
)
softwareStore.spin.spinning = false
if (checkImage.code == 0) {
message.error(checkImage.message)
return
}
// 高清检查完成
if (checkImage.data && checkImage.data.length > 0) {
// 出现了图片大的,用户判断是不是需要高清
let msg = checkImage.data.map((item, index) => {
return item.outImagePath + '\n'
})
dialog.warning({
title: '高清警告',
content: '下面的图片大于10MB,是否继续高清?' + '\n' + msg,
positiveText: '确定',
negativeText: '取消',
maskClosable: false,
onPositiveClick: async () => {
alert('确认高清')
await hdImageFunc(bookTask.value.id, OperateBookType.BOOKTASK)
}
}
/**
* 生成草稿
*/
async function ClipDraft(e) {
e.stopPropagation()
dialog.info({
closeOnEsc: false,
title: `生成草稿前检查 ${bookTask.value.name}`,
maskClosable: false,
content: () =>
h(ManageBookTaskGenerateInformation, { bookTask: bookTask.value, type: 'bookTask' })
})
}
/**
* 合成视频
*/
async function GenerateVideo(e) {
e.stopPropagation()
message.error('该功能暂不可用')
}
return {
bookTask,
reverseManageStore,
ClipDraft,
GenerateVideo,
softwareStore,
ReSetBookTaskDetail,
DeleteBookTask,
HDImage
}
})
} else {
// 没有就直接高清
await hdImageFunc(bookTask.value.id, OperateBookType.BOOKTASK)
}
})
}
/**
* 生成草稿
*/
async function ClipDraft(e) {
e.stopPropagation()
dialog.info({
closeOnEsc: false,
title: `生成草稿前检查 ${bookTask.value.name}`,
maskClosable: false,
content: () =>
h(ManageBookTaskGenerateInformation, {
bookTask: bookTask.value,
type: 'bookTask',
optionType: 'draft'
})
})
}
/**
* 合成视频
*/
async function GenerateVideo(e) {
e.stopPropagation()
dialog.info({
closeOnEsc: false,
title: `合成视频前检查 ${bookTask.value.name}`,
maskClosable: false,
content: () =>
h(ManageBookTaskGenerateInformation, {
bookTask: bookTask.value,
type: 'bookTask',
optionType: 'video'
})
})
}
</script>
@@ -46,7 +46,7 @@
clearable
/>
</n-form-item>
<n-form-item label="选择剪映草稿" path="backgroundMusic">
<n-form-item label="选择剪映草稿" path="backgroundMusic" v-if="optionType != 'video'">
<div>
<div style="color: red">
注意选择的草稿主轨道的图片数量要和当前的相同会生成新的草稿并且只会替换图片保留其余的数据
@@ -67,12 +67,23 @@
>
<n-button type="info" style="margin-left: 10px" @click="SaveVideoData">保存数据</n-button>
</n-form-item>
<div v-if="type == 'bookTask'" style="color: red">注意在生成草稿前要先保存数据</div>
<div v-if="type == 'bookTask'" style="color: red">
注意 生成草稿/合成视频 前要先保存数据
</div>
<div v-else style="color: red">
注意在生成草稿前要先保存数据当前会生成选择的草稿全部会使用上面的参数
注意 生成草稿/合成视频 前要先保存数据当前会生成选择的 草稿/视频全部会使用上面的参数
</div>
<n-form-item style="display: flex; justify-content: flex-end">
<n-button type="info" style="margin-left: 10px" @click="AddJianyingDraft">生成草稿</n-button>
<n-button
v-if="optionType == 'video'"
type="info"
style="margin-left: 10px"
@click="AddGenerateVideoTask"
>添加合成视频任务</n-button
>
<n-button v-else type="info" style="margin-left: 10px" @click="AddJianyingDraft"
>生成草稿</n-button
>
</n-form-item>
</n-form>
</template>
@@ -81,18 +92,21 @@
import { ref, onMounted } from 'vue'
import { useMessage, NForm, NFormItem, NButton, NIcon, NInput, NSelect } from 'naive-ui'
import { FolderOpen } from '@vicons/ionicons5'
import { OperateBookType } from '../../../../../../define/enum/bookEnum'
import { BookTaskStatus, OperateBookType } from '../../../../../../define/enum/bookEnum'
import { useReverseManageStore } from '../../../../../../stores/reverseManage'
let props = defineProps({
bookTask: undefined,
type: undefined,
selectBookTask: []
selectBookTask: [],
optionType: undefined
})
let bookTask = ref(props.bookTask)
let type = ref(props.type)
let optionType = ref(props.optionType)
let backgroundMusicOptions = ref([])
let message = useMessage()
let draftSelect = ref([])
@@ -217,23 +231,78 @@ async function SaveVideoData() {
}
}
/**
* 添加草稿
*/
async function AddJianyingDraft() {
async function AddGenerateVideoTask() {
let res = undefined
if (props.type == 'book') {
let res = await window.book.AddJianyingDraft(
// 单个
res = await window.book.AddGenerateVideoTask(
props.selectBookTask,
OperateBookType.ASSIGNBOOKTASK
)
window.api.showGlobalMessageDialog(res)
if (res.code == 1) {
reverseManageStore.UpdatedBookTaskData(props.selectBookTask, {
status: BookTaskStatus.COMPOSING
})
} else {
reverseManageStore.UpdatedBookTaskData(props.selectBookTask, {
status: BookTaskStatus.COMPOSING_FAIL,
errorMsg: res.message
})
}
} else if (props.type == 'bookTask') {
let res = await window.book.AddJianyingDraft(bookTask.value.id, OperateBookType.BOOKTASK)
window.api.showGlobalMessageDialog(res)
// 批次
res = await window.book.AddGenerateVideoTask(bookTask.value.id, OperateBookType.BOOKTASK)
if (res.code == 1) {
reverseManageStore.UpdatedBookTaskData(bookTask.value.id, {
status: BookTaskStatus.COMPOSING
})
} else {
reverseManageStore.UpdatedBookTaskData(bookTask.value.id, {
status: BookTaskStatus.COMPOSING_FAIL,
errorMsg: res.message
})
}
} else {
message.error('未知的操作类型,请检查')
return
}
window.api.showGlobalMessageDialog(res)
}
/**
* 添加草稿
*/
async function AddJianyingDraft() {
let res = undefined
if (props.type == 'book') {
res = await window.book.AddJianyingDraft(props.selectBookTask, OperateBookType.ASSIGNBOOKTASK)
if (res.code == 1) {
reverseManageStore.UpdatedBookTaskData(props.selectBookTask, {
status: BookTaskStatus.DRAFT_DONE
})
} else {
reverseManageStore.UpdatedBookTaskData(props.selectBookTask, {
status: BookTaskStatus.DRAFT_FAIL,
errorMsg: res.message
})
}
} else if (props.type == 'bookTask') {
res = await window.book.AddJianyingDraft(bookTask.value.id, OperateBookType.BOOKTASK)
if (res.code == 1) {
reverseManageStore.UpdatedBookTaskData(bookTask.value.id, {
status: BookTaskStatus.DRAFT_DONE
})
} else {
reverseManageStore.UpdatedBookTaskData(bookTask.value.id, {
status: BookTaskStatus.DRAFT_FAIL,
errorMsg: res.message
})
}
} else {
message.error('未知的操作类型,请检查')
return
}
window.api.showGlobalMessageDialog(res)
}
let rules = ref({
srtPath: [{ required: true, message: '请选择背景音乐', trigger: ['input', 'blur', 'change'] }],
@@ -1,960 +0,0 @@
<template>
<div style="display: flex; justify-content: space-between; align-items: center">
<div style="display: flex; align-items: center">
<n-button strong secondary @click="RetuenMain" size="small"> 返回 </n-button>
<!-- <n-dropdown trigger="hover" :options="AutoOptions" @select="SelectAutoAction">
<n-button :color="softwareStore.SoftColor.ORANGE" @click="Start" style="margin-left: 5px">
自动开始
</n-button>
</n-dropdown> -->
<n-divider vertical style="height: 30px" />
<n-dropdown trigger="hover" :options="frameOptions" @select="handleSelect">
<n-button
:color="softwareStore.SoftColor.ORANGE"
class="top-button"
size="small"
@click="ActionFrame"
>
开始分镜
</n-button>
</n-dropdown>
<n-dropdown trigger="hover" :options="copywritingOptions" @select="handleSelect">
<n-button
:color="softwareStore.SoftColor.ORANGE"
style="margin-left: 5px"
size="small"
@click="GetCopywriting('all')"
>
开始提取全部文案
</n-button>
</n-dropdown>
<n-dropdown trigger="hover" :options="watermarkOptions" @select="handleSelect">
<n-button
:color="softwareStore.SoftColor.ORANGE"
style="margin-left: 5px"
size="small"
@click="RemoveWatermark"
>
开始去除水印
</n-button>
</n-dropdown>
<n-dropdown trigger="hover" :options="importWordOptions" @select="handleSelect">
<n-button
:color="softwareStore.SoftColor.ORANGE"
style="margin-left: 5px"
size="small"
@click="ImportWordAndSrtFunc"
>
导入文案/SRT
</n-button>
</n-dropdown>
<n-dropdown trigger="hover" :options="reverseOptions" @select="handleSelect">
<n-button
:color="softwareStore.SoftColor.ORANGE"
style="margin-left: 5px"
size="small"
@click="ImageReversePrompt(undefined)"
>
一键反推
</n-button>
</n-dropdown>
<n-dropdown trigger="hover" :options="translateOptions" @select="handleSelect">
<n-button
:color="softwareStore.SoftColor.ORANGE"
style="margin-left: 5px"
size="small"
@click="Translate(undefined)"
>
一键翻译
</n-button>
</n-dropdown>
<n-dropdown trigger="hover" :options="generateOptions" @select="handleSelect">
<n-button
:color="softwareStore.SoftColor.ORANGE"
style="margin-left: 5px"
size="small"
@click="GenerateImageAll"
>
一键生图
</n-button>
</n-dropdown>
<n-dropdown trigger="hover" :options="resetOptions" @select="handleSelect">
<n-button
:color="softwareStore.SoftColor.ORANGE"
style="margin-left: 5px"
size="small"
@click="ResetAllData"
>
重置
</n-button>
</n-dropdown>
<n-button
:color="softwareStore.SoftColor.ORANGE"
style="margin-left: 5px"
size="small"
@click="OpenSettingDialog"
>
设置
</n-button>
<div style="margin-left: 10px">
<MonitorStatus />
</div>
</div>
<div class="right-button">
<n-button
text
strong
:type="softwareStore.show_logger ? 'default' : 'info'"
@click="switchLogger"
>
{{ softwareStore.show_logger ? '隐藏日志' : '显示日志' }}
</n-button>
</div>
</div>
</template>
<script>
import { ref, onMounted, defineComponent, onUnmounted, toRaw, watch, h } from 'vue'
import { useMessage, NButton, useDialog, NCheckbox, NDropdown, NDivider, NRate } from 'naive-ui'
import { useRouter } from 'vue-router'
import { useSoftwareStore } from '../../../../../stores/software'
import { useReverseManageStore } from '../../../../../stores/reverseManage.ts'
import { isEmpty } from 'lodash'
import VideoCanvas from '../../VideoSubtitle/VideoCanvas.vue'
import { SubtitleSavePositionType } from '../../../../../define/enum/waterMarkAndSubtitle'
import { DEFINE_STRING } from '../../../../../define/define_string'
import Setting from './Setting.vue'
import { BookType, OperateBookType } from '../../../../../define/enum/bookEnum'
import TranslateSetting from '../../Setting/TranslateSetting.vue'
import { TranslateType } from '../../../../../define/enum/translate'
import { ContainsChineseOrPunctuation } from '../../../../../define/Tools/common'
import ImportWordAndSrt from '../../Original/Components/ImportWordAndSrt.vue'
import GetWaterMaskRectangle from '../../Watermark/GetWaterMaskRectangle.vue'
import MonitorStatus from './MonitorStatus.vue'
export default defineComponent({
components: {
NButton,
NCheckbox,
NDropdown,
NDivider,
GetWaterMaskRectangle,
MonitorStatus
},
setup() {
let router = useRouter()
let message = useMessage()
let dialog = useDialog()
let reverseManageStore = useReverseManageStore()
let softwareStore = useSoftwareStore()
// 获取当前这个界面的数据
onMounted(async () => {})
// 返回主页
function RetuenMain() {
router.push('/book_management')
}
// 自动开始
async function AutoAction() {
// 开始自动执行。先分镜,后面在添加任务中
let res_frame = await window.book.AutoAction(reverseManageStore.selectBook.id)
}
// 选择自动开始的操作(停止或者是继续)
async function SelectAutoAction() {}
// 是不是显示日志
async function switchLogger() {
softwareStore.show_logger = !softwareStore.show_logger
}
/**
* 开始分镜
*/
async function ComputeStoryboard() {
softwareStore.spin.spinning = true
softwareStore.spin.tip = '正在计算分镜中'
if (isEmpty(reverseManageStore.selectBookTask.id)) {
window.api.showGlobalMessageDialog({
code: 0,
message: '没有找到要执行的任务'
})
return
}
let res_frame = await window.book.ComputeStoryboard(reverseManageStore.selectBook.id)
if (res_frame.code == 0) {
softwareStore.spin.spinning = false
window.api.showGlobalMessageDialog(res_frame)
return
}
// 分镜完成,重新获取一下数据
let getRes = await reverseManageStore.GetBookTaskDataFromDB({
id: reverseManageStore.selectBookTask.id
})
if (getRes.code == 0) {
softwareStore.spin.spinning = false
message.error(getRes.message)
return
}
softwareStore.spin.spinning = false
message.success('重新获取数据成功')
}
// 执行分镜的方法
async function Framing() {
softwareStore.spin.spinning = true
softwareStore.spin.tip = '正在分镜中'
if (isEmpty(reverseManageStore.selectBookTask.id)) {
window.api.showGlobalMessageDialog({
code: 0,
message: '没有找到要执行的任务'
})
return
}
let res_frame = await window.book.Framing(reverseManageStore.selectBook.id)
if (res_frame.code == 0) {
softwareStore.spin.spinning = false
window.api.showGlobalMessageDialog(res_frame)
return
}
// 分镜完成,重新获取一下数据
let getRes = await reverseManageStore.GetBookTaskDataFromDB({
id: reverseManageStore.selectBookTask.id
})
if (getRes.code == 0) {
softwareStore.spin.spinning = false
message.error(getRes.message)
return
}
softwareStore.spin.spinning = false
message.success('重新获取数据成功')
}
// 获取文案信息
async function GetCopywriting(type) {
if (isEmpty(reverseManageStore.selectBookTask.id)) {
window.api.showGlobalMessageDialog({
code: 0,
message: '没有找到要执行的任务'
})
return
}
let subtitleSettingRes = await window.write.GetSubtitleSetting()
if (subtitleSettingRes.code == 0) {
message.error(subtitleSettingRes.message)
return
}
let da = dialog.warning({
title: '开始提取文案提示',
content: `即将进行 ${type == 'blank' ? '空白' : '全部'} 文案提取${
type == 'blank' ? '' : ',会重新提取并覆盖旧的文案数据'
},当前的文案提取模式为 ${subtitleSettingRes.data.selectModel} ,是否继续?`,
positiveText: '继续',
negativeText: '取消',
onPositiveClick: async () => {
da?.destroy()
softwareStore.spin.spinning = true
softwareStore.spin.tip = `正在使用 ${subtitleSettingRes.data.selectModel} 提取文案,正在准备中。。。`
let copywriting_res = await window.book.GetCopywriting(
reverseManageStore.selectBook.id,
reverseManageStore.selectBookTask.id,
OperateBookType.BOOKTASK,
type == 'blank' ? false : true
)
softwareStore.spin.spinning = false
if (copywriting_res.code == 0) {
message.error(copywriting_res.message)
return
}
// dialog.success({})
message.success('获取全部文案成功')
}
})
}
// 开始去除水印
async function RemoveWatermark() {
if (isEmpty(reverseManageStore.selectBookTask.id)) {
window.api.showGlobalMessageDialog({
code: 0,
message: '没有找到要执行的任务'
})
return
}
let da = dialog.warning({
title: '去除水印提示',
content: '即将去除全部水印,是否继续?',
positiveText: '继续',
negativeText: '取消',
onPositiveClick: async () => {
da?.destroy()
softwareStore.spin.spinning = true
softwareStore.spin.tip = '正在去除水印中。。。'
let res_frame = await window.book.RemoveWatermark(
reverseManageStore.selectBookTask.id,
OperateBookType.BOOKTASK
)
softwareStore.spin.spinning = false
window.api.showGlobalMessageDialog(res_frame)
}
})
}
// 获取文案信息设置
async function GetCopywritingSetting() {
let dialogHeight = window.innerHeight * 0.9
dialog.create({
showIcon: false,
closeOnEsc: false,
content: () =>
h(VideoCanvas, {
videoWidth: 800,
videoSrc: reverseManageStore.selectBook.oldVideoPath,
height: dialogHeight - 50,
type: SubtitleSavePositionType.SETTING
}),
style: `width : 850px; height : ${dialogHeight}px`,
maskClosable: false,
onClose: () => {}
})
}
// 导出文案
async function ExportCopywriting() {
softwareStore.spin.spinning = true
softwareStore.spin.tip = '正在导出文案中...'
let res = await window.book.ExportCopywriting(reverseManageStore.selectBookTask.id)
if (res.code == 0) {
message.error(res.message)
} else {
dialog.success({
title: '导出文案成功',
content: '导出文案成功,是否打开导出的字幕文件',
positiveText: '打开',
negativeText: '取消',
onPositiveClick: () => {
window.system.OpenFile(res.data)
}
})
}
softwareStore.spin.spinning = false
}
// 导入文案
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)
}
})
}
/**
* 获取水印位置
*/
async function GetWatermarkPosition() {
// 开始获取水印位置
// 判断当前数据是不是存在
// 处理数据。获取当前的所有的数据
let dialogWidth = window.innerWidth * 0.7
let dialogHeight = window.innerHeight * 0.9
// ImportWordAndSrt
dialog.create({
showIcon: false,
closeOnEsc: false,
content: () => h(GetWaterMaskRectangle, { width: dialogWidth, height: dialogHeight }),
style: `width : ${dialogWidth}px; height : ${dialogHeight}px`,
maskClosable: false,
onClose: () => {}
})
}
// 一些选择方式
async function handleSelect(key) {
switch (key) {
case 'compute_frame': // 计算分镜
await ComputeStoryboard()
break
case 'framing': // 开始分镜
await Framing()
break
case 'recognizing_blank': // 提取空白文案
await GetCopywriting('blank')
break
case 'recognizing_setting': // 文案位置设置
await GetCopywritingSetting()
break
case 'export_recognizing': // 导出文案
await ExportCopywriting()
break
case 'import_recognizing': // 导入文案
await ImportCopywriting()
break
case 'watermark_position': // 水印位置设置
await GetWatermarkPosition()
break
case 'clear_import_word': // 清除导入文案
await ClearImportWord()
break
case 'mj_reverse': // MJ反推
await ImageReversePrompt(BookType.MJ_REVERSE)
break
case 'sd_reverse': // SD反推
await ImageReversePrompt(BookType.SD_REVERSE)
break
case 'translate_chinese': // 英文翻译为中文
await Translate('translate_chinese')
break
case 'translate_english': // 中文翻译为英文
await Translate('translate_english')
break
case 'translate_setting': // 翻译设置
await Translate('translate_setting')
break
case 'reset_gpt_reverse_data': // 重置反推/GPT提示词
await ResetGptReverseData()
break
case 'reset_merge_prompt': // 重置合并提示词数据
await ResetMergePromptData()
break
case 'reset_Generate_Image': // 重置生成的图片
await ResetGenerateImage()
break
default:
message.error('未知的选项')
}
}
// 重置出的图片
async function ResetGenerateImage() {
dialog.warning({
title: '重置所有图片提示',
content:
'继续操作会重置所有的生成的图片,包括生成的主图和子图,但是并不会删除本地的图片文件,是否继续?',
positiveText: '继续',
negativeText: '取消',
onPositiveClick: async () => {
let res = await window.book.ResetGenerateImage(
reverseManageStore.selectBookTask.id,
OperateBookType.BOOKTASK
)
if (res.code == 0) {
message.error(res.message)
} else {
// 前端重置数据
for (let i = 0; i < reverseManageStore.selectBookTaskDetail.length; i++) {
reverseManageStore.selectBookTaskDetail[i].mjMessage = undefined
reverseManageStore.selectBookTaskDetail[i].outImagePath = undefined
reverseManageStore.selectBookTaskDetail[i].subImagePath = undefined
}
message.success('重置所有生成图片成功')
}
}
})
}
// 重置合并提示词数据
async function ResetMergePromptData() {
dialog.warning({
title: '重置合并提示词数据提示',
content: '继续操作会重置所有的合并提示词数据,请确认是不是继续?',
positiveText: '继续',
negativeText: '取消',
onPositiveClick: async () => {
let res = await window.book.ResetMergePromptData(
reverseManageStore.selectBookTask.id,
OperateBookType.BOOKTASK
)
if (res.code == 0) {
message.error(res.message)
} else {
// 前端做响应
for (let i = 0; i < reverseManageStore.selectBookTaskDetail.length; i++) {
reverseManageStore.selectBookTaskDetail[i].prompt = undefined
}
message.success('重置所有合并提示词数据成功')
}
}
})
}
/**
* 重置反推/GPT提示词,更具不同的小说类型进行不同的重置
*/
async function ResetGptReverseData() {
dialog.warning({
title: '删除反推提示词警告提示',
content: '继续操作会移除所有的反推提示词数据,注意,是所有,请确认是不是继续?',
positiveText: '继续',
negativeText: '取消',
onPositiveClick: async () => {
let res = await window.book.ResetGptReverseData(
reverseManageStore.selectBookTask.id,
OperateBookType.BOOKTASK,
reverseManageStore.selectBook.type
)
if (res.code == 0) {
message.error(res.message)
} else {
// 这个做个前端删除
for (let i = 0; i < reverseManageStore.selectBookTaskDetail.length; i++) {
reverseManageStore.selectBookTaskDetail[i].reversePrompt = undefined
reverseManageStore.selectBookTaskDetail[i].gptPrompt = undefined
}
message.success('删除所有的反推/GPT提示词成功')
}
}
})
}
/**
* 打开设置对话框
*/
async function OpenSettingDialog() {
let dialogWidth = window.innerWidth * 0.8
let dialogHeight = window.innerHeight * 0.95
dialog.create({
showIcon: false,
title: '设置',
content: () => h(Setting, { height: dialogHeight }),
style: `width : ${dialogWidth}px; height : ${dialogHeight}px`,
maskClosable: false
})
}
/**
* MJ反推数据
* @param type 反推类型
*/
async function ImageReversePrompt(type = undefined) {
if (isEmpty(reverseManageStore.selectBookTask.id)) {
window.api.showGlobalMessageDialog({
code: 0,
message: '没有找到要执行的任务'
})
return
}
dialog.warning({
title: '反推提示',
content: `即将进行反推操作,反推方式为 ${
type ? type : reverseManageStore.selectBook.type
} ,是否继续?`,
positiveText: '继续',
negativeText: '取消',
onPositiveClick: async () => {
if (!type) {
let bookType = reverseManageStore.selectBook.type
type = bookType
}
if (type != BookType.MJ_REVERSE && type != BookType.SD_REVERSE) {
message.error(`该类型 ${bookType} 的小说不支持反推`)
return
}
// 开始添加
let res = await window.book.AddReversePrompt(
reverseManageStore.selectBookTask.id,
OperateBookType.BOOKTASK,
type
)
if (res.code == 0) {
message.error(res.message)
} else {
message.success('添加所有反推任务成功')
}
}
})
}
/**
* 翻译提示词
* @param key 操作的值,进行分类
*/
async function Translate(key) {
switch (key) {
case undefined:
case null:
message.error('请选择子菜单中的选项')
break
case 'translate_chinese': // 将英文翻译到中文
await TranslateService('en', 'zh')
break
case 'translate_english': // 将中文翻译到英文
await TranslateService('zh', 'en')
break
case 'translate_setting': // 翻译设置
await OpenTranslateSettingDialog()
break
default:
message.error('不支持的操作类型')
break
}
}
/**
* 打开翻译设置弹窗
*/
async function OpenTranslateSettingDialog() {
// 判断当前数据是不是存在
// 处理数据。获取当前的所有的数据
let dialogWidth = window.innerWidth * 0.8
let dialogHeight = window.innerHeight * 0.9
dialog.create({
showIcon: false,
title: '翻译设置',
content: () => h(TranslateSetting),
style: `width : ${dialogWidth}px; height : ${dialogHeight}px`,
maskClosable: false
})
}
/**
* 调用翻译接口
* @param from 源语言
* @param to 目标语言
*/
async function TranslateService(from, to) {
let translateData = []
for (let i = 0; i < reverseManageStore.selectBookTaskDetail.length; i++) {
const element = reverseManageStore.selectBookTaskDetail[i]
if (!isEmpty(element.gptPrompt)) {
translateData.push({
text: element.gptPrompt,
from: from,
to: to,
type: TranslateType.GPT_PROMPT_TRANSLATE,
isSplit: false, // 该参数设置为true时,会将文本按照标点符号分割成多个句子进行翻译
bookTaskDetailId: element.id,
responseMessgeName: DEFINE_STRING.BOOK.REVERSE_PROMPT_TRANSLATE_RETURN
})
} else {
let reversePrompt = element.reversePrompt
if (reversePrompt) {
reversePrompt.forEach((item) => {
if (from == 'zh' && to == 'en') {
// 判断是不是有中文
if (!ContainsChineseOrPunctuation(item.promptCN ? item.promptCN : item.prompt)) {
return
}
}
let temp_obj = {
text: item.promptCN ? item.promptCN : item.prompt,
from: from,
to: to,
type: TranslateType.REVERSE_PROMPT_TRANSLATE,
isSplit: false, // 该参数设置为true时,会将文本按照标点符号分割成多个句子进行翻译
bookTaskDetailId: element.id,
reversePromptId: item.id,
responseMessgeName: DEFINE_STRING.BOOK.REVERSE_PROMPT_TRANSLATE_RETURN
}
translateData.push(temp_obj)
})
}
}
}
if (translateData.length == 0) {
message.error('没有需要翻译的数据')
return
}
softwareStore.spin.spinning = true
softwareStore.spin.tip = `翻译中 ... 0 / ${translateData.length}`
// 开始翻译
let res = await window.translate.TranslateNowReturn(translateData)
softwareStore.spin.spinning = false
window.api.showGlobalMessageDialog(res)
}
/**
* 生成所有图片
*/
async function GenerateImageAll() {
let res = await window.book.GenerateImageAll(
reverseManageStore.selectBookTask.id,
reverseManageStore.selectBookTask.imageCategory
)
if (res.code == 0) {
message.error(res.message)
return
}
message.success(res.message)
}
/**
* 导入文案和SRT
*/
async function ImportWordAndSrtFunc() {
let dialogWidth = window.innerWidth * 0.95
let dialogHeight = window.innerHeight * 0.95
// 这边要处理下数据
if (
reverseManageStore.selectBook.type == BookType.MJ_REVERSE ||
reverseManageStore.selectBook.type == BookType.MJ_REVERSE
) {
if (reverseManageStore.selectBookTaskDetail.length <= 0) {
message.error('反推小说,没有找到对应的分镜数据')
}
}
let tempData = []
for (let i = 0; i < reverseManageStore.selectBookTaskDetail.length; i++) {
const element = reverseManageStore.selectBookTaskDetail[i]
tempData.push({
no: element.no,
id: element.id,
lastId: i == 0 ? '' : reverseManageStore.selectBookTaskDetail[i - 1].id,
word: element.word,
after_gpt: element.afterGpt,
subValue: element.subValue ? element.subValue : [],
name: element.name + '.png',
prompt: element.prompt,
timeLimit: element.timeLimit
})
}
// ImportWordAndSrt
dialog.create({
showIcon: false,
closeOnEsc: false,
content: () =>
h(ImportWordAndSrt, {
initData: tempData,
height: dialogHeight,
type: BookType.MJ_REVERSE
}),
style: `width : ${dialogWidth}px; min-height : ${dialogHeight}px`,
maskClosable: false,
onClose: () => {}
})
}
/**
* 清除导入文案
*/
async function ClearImportWord() {
let da = dialog.warning({
title: '清除导入文案提示',
content:
'继续执行该操作会将导入的对齐文案全部清除(包括文案的时间线),并且操作不可逆,是否继续?',
positiveText: '继续',
negativeText: '取消',
onPositiveClick: async () => {
da?.destroy()
softwareStore.spin.spinning = true
softwareStore.spin.tip = '正在清除导入数据。。。'
let res = await window.book.ClearImportWord(reverseManageStore.selectBookTask.id)
softwareStore.spin.spinning = false
if (res.code == 0) {
message.error(res.message)
} else {
// 这边重置下数据
for (let i = 0; i < res.data.length; i++) {
const element = res.data[i]
let index = reverseManageStore.selectBookTaskDetail.findIndex(
(item) => item.id == element.id
)
if (index != -1) {
reverseManageStore.selectBookTaskDetail[index].subValue = undefined
reverseManageStore.selectBookTaskDetail[index].startTime = element.startTime
reverseManageStore.selectBookTaskDetail[index].endTime = element.endTime
reverseManageStore.selectBookTaskDetail[index].timeLimit = element.timeLimit
}
}
message.success('清除导入文案成功')
}
}
})
}
async function ActionFrame() {
message.info('请使用下面菜单中的按钮进行对应的操作')
}
return {
RetuenMain,
AutoAction,
ImportWordAndSrtFunc,
ClearImportWord,
reverseManageStore,
softwareStore,
ActionFrame,
switchLogger,
ComputeStoryboard,
OpenSettingDialog,
GetCopywriting,
RemoveWatermark,
ImageReversePrompt,
ResetGptReverseData,
Translate,
TranslateService,
GenerateImageAll,
handleSelect,
AutoOptions: [
{
label: '停止任务',
value: 'auto_stop'
},
{
label: '继续任务',
value: 'auto_continue'
}
],
frameOptions: [
{
label: '计算分镜',
key: 'compute_frame'
},
{ label: '分镜', key: 'framing' }
],
copywritingOptions: [
{
label: '提取空白文案',
key: 'recognizing_blank'
},
{
label: '提取文案位置',
key: 'recognizing_setting'
},
{
label: '导出文案',
key: 'export_recognizing'
},
{
label: '导入文案',
key: 'import_recognizing'
},
{
label: '停止提取',
key: 'stop_recognizing'
}
],
watermarkOptions: [
{
label: '选择水印位置',
key: 'watermark_position'
},
{
label: '停止去除',
key: 'stop_watermark'
}
],
reverseOptions: [
{
label: 'MJ反推',
key: 'mj_reverse'
},
{
label: 'SD反推',
key: 'sd_reverse',
disabled: reverseManageStore.selectBook.type != BookType.SD_REVERSE
},
{
label: '停止反推',
key: 'stop_reverse'
}
],
translateOptions: [
{
label: '英文 -> 中文',
key: 'translate_chinese'
},
{
label: '中文 -> 英文',
key: 'translate_english'
},
{
label: '翻译设置',
key: 'translate_setting'
},
{
label: '停止翻译',
key: 'translate_stop'
}
],
generateOptions: [
{
label: '停止生图',
key: 'stop_image_generate'
}
],
importWordOptions: [
{
label: '清除导入文案',
key: 'clear_import_word'
}
],
resetOptions: [
{
label: '重置反推/GPT提示词',
key: 'reset_gpt_reverse_data'
},
{
label: '重置合并提示词',
key: 'reset_merge_prompt'
},
{
label: '重置图片',
key: 'reset_Generate_Image'
}
]
}
}
})
</script>
<style lang="css">
.right-button {
display: flex;
justify-content: flex-end;
}
.top-button {
margin-left: 5px;
}
</style>
@@ -35,7 +35,6 @@ export default defineComponent({
props: ['initData', 'index'],
setup(props) {
let data = ref(props.initData)
console.log('data', props.initData, props.index)
onMounted(async () => {})
let InputDebounced = debounce(handleInput, 1000)
@@ -0,0 +1,284 @@
<template>
<div>
<n-dropdown trigger="hover" :options="reverseOptions" @select="ReverseSelect">
<n-button size="tiny" :color="softwareStore.SoftColor.BROWN_YELLOW" @click="AddReversePrompt">
单句反推
</n-button>
</n-dropdown>
<n-dropdown trigger="hover" :options="tranlateOptions" @select="ReverseSelect">
<n-button
style="margin-left: 5px"
size="tiny"
:color="softwareStore.SoftColor.BROWN_YELLOW"
@click="TranslateOne('translate_chinese')"
>
英文 2 中文
</n-button>
</n-dropdown>
<n-button
v-if="reverseManageStore.selectBook.type == bookType.MJ_REVERSE"
style="margin-left: 5px"
size="tiny"
:color="softwareStore.SoftColor.BROWN_YELLOW"
@click="SelectReversePrompt"
>
重选反推提示词
</n-button>
</div>
</template>
<script setup>
import { ref, onMounted, defineComponent, onUnmounted, toRaw, watch, h } from 'vue'
import { useMessage, useDialog, NDropdown, NButton } from 'naive-ui'
import { useSoftwareStore } from '../../../../../stores/software'
import { useReverseManageStore } from '../../../../../stores/reverseManage'
import { TranslateType } from '../../../../../define/enum/translate'
import { ContainsChineseOrPunctuation } from '../../../../../define/Tools/common'
import ReSelectReversePrompt from '../MJReverse/ReSelectReversePrompt.vue'
import { BookType, OperateBookType } from '../../../../../define/enum/bookEnum'
import { isEmpty } from 'lodash'
import { DEFINE_STRING } from '../../../../../define/define_string'
let props = defineProps({
initData: undefined,
index: undefined
})
let message = useMessage()
let dialog = useDialog()
let data = ref(props.initData)
let index = ref(props.index)
let softwareStore = useSoftwareStore()
let reverseManageStore = useReverseManageStore()
let bookType = ref(BookType)
onMounted(async () => {})
/**
* 单句反推提示词
*/
async function AddReversePrompt() {
let res = await window.book.AddReversePrompt(data.value.id, OperateBookType.BOOKTASKDETAIL, null)
if (res.code == 0) {
message.error(res.message)
} else {
message.success('添加反推任务成功')
}
}
async function AddNextReversePrompt() {
let res = await window.book.AddReversePrompt(
data.value.id,
OperateBookType.UNDERBOOKTASK,
reverseManageStore.selectBook.type
)
if (res.code == 0) {
message.error(res.message)
} else {
message.success('添加反推任务成功')
}
}
/**
* 提示词菜单下拉操作
*/
async function ReverseSelect(key) {
switch (key) {
case 'remove_reverse': // 删除反推提示词
let res = await window.book.ResetGptReverseData(
data.value.id,
OperateBookType.BOOKTASKDETAIL,
reverseManageStore.selectBook.type
)
if (res.code == 0) {
message.error(res.message)
} else {
reverseManageStore.selectBookTaskDetail[props.index].gptPrompt = undefined
reverseManageStore.selectBookTaskDetail[props.index].reversePrompt = undefined
message.success('删除反推数据成功')
}
break
case 'translate_english': // 中文 2 英文
await TranslateOne('translate_english')
break
case 'down_translate_chinese': // 下翻译中文
await TranslateOne('down_translate_chinese')
break
case 'down_translate_english': // 下翻译英文
await TranslateOne('down_translate_english')
break
case 'down_reverse':
await AddNextReversePrompt()
break
default:
message.error('未知的操作类型')
break
}
}
// 处理翻译英文的数据
function TranslateOneParams(from, to) {
let translateData = []
if (!isEmpty(data.value.gptPrompt)) {
translateData.push({
text: data.value.gptPrompt,
from: from,
to: to,
type: TranslateType.GPT_PROMPT_TRANSLATE,
isSplit: false, // 该参数设置为true时,会将文本按照标点符号分割成多个句子进行翻译
bookTaskDetailId: data.value.id,
responseMessgeName: DEFINE_STRING.BOOK.REVERSE_PROMPT_TRANSLATE_RETURN
})
} else {
let reversePrompt = data.value.reversePrompt
if (reversePrompt) {
reversePrompt.forEach((item) => {
let temp_obj = undefined
if (from == 'zh' && to == 'en') {
// 判断是不是有中文
if (!ContainsChineseOrPunctuation(item.promptCN ? item.promptCN : item.prompt)) {
return
}
}
temp_obj = {
text: item.prompt,
from: from,
to: to,
type: TranslateType.REVERSE_PROMPT_TRANSLATE,
isSplit: false, // 该参数设置为true时,会将文本按照标点符号分割成多个句子进行翻译
bookTaskDetailId: data.value.id,
reversePromptId: item.id,
responseMessgeName: DEFINE_STRING.BOOK.REVERSE_PROMPT_TRANSLATE_RETURN
}
translateData.push(temp_obj)
})
}
}
return translateData
}
// 处理批次的翻译
function TranslateBatchParams(from, to) {
let translateData = []
for (let i = index.value; i < reverseManageStore.selectBookTaskDetail.length; i++) {
const element = reverseManageStore.selectBookTaskDetail[i]
// 这边判断GPT提示词是不是存在,存在的话翻译GPT提示词
if (!isEmpty(element.gptPrompt)) {
translateData.push({
text: element.gptPrompt,
from: from,
to: to,
type: TranslateType.GPT_PROMPT_TRANSLATE,
isSplit: false, // 该参数设置为true时,会将文本按照标点符号分割成多个句子进行翻译
bookTaskDetailId: element.id,
responseMessgeName: DEFINE_STRING.BOOK.REVERSE_PROMPT_TRANSLATE_RETURN
})
} else {
let reversePrompt = element.reversePrompt
if (reversePrompt) {
reversePrompt.forEach((item) => {
if (from == 'zh' && to == 'en') {
// 判断是不是有中文
if (!ContainsChineseOrPunctuation(item.promptCN ? item.promptCN : item.prompt)) {
return
}
}
let temp_obj = {
text: item.prompt,
from: from,
to: to,
type: TranslateType.REVERSE_PROMPT_TRANSLATE,
isSplit: false, // 该参数设置为true时,会将文本按照标点符号分割成多个句子进行翻译
bookTaskDetailId: element.id,
reversePromptId: item.id,
responseMessgeName: DEFINE_STRING.BOOK.REVERSE_PROMPT_TRANSLATE_RETURN
}
translateData.push(temp_obj)
})
}
}
}
return translateData
}
// 单句翻译
async function TranslateOne(key) {
let translateData = []
switch (key) {
case 'translate_english':
translateData = TranslateOneParams('zh', 'en')
break
case 'translate_chinese':
translateData = TranslateOneParams('en', 'zh')
break
case 'down_translate_english':
translateData = TranslateBatchParams('zh', 'en')
break
case 'down_translate_chinese':
translateData = TranslateBatchParams('en', 'zh')
break
default:
message.error('未知的操作类型')
break
}
if (translateData.length <= 0) {
message.error('没有需要翻译的数据')
return
}
softwareStore.spin.spinning = true
softwareStore.spin.tip = `翻译中 ... 0 / ${translateData.length}`
// 开始翻译
let res = await window.translate.TranslateNowReturn(translateData)
softwareStore.spin.spinning = false
window.api.showGlobalMessageDialog(res)
}
/**
* 重选反推提示词
*/
async function SelectReversePrompt() {
// 判断当前是不是又反推提示词
if (!data.value.reversePrompt || data.value.reversePrompt.length <= 0) {
message.error('当前没有反推提示词')
return
}
// ImportWordAndSrt
dialog.create({
showIcon: false,
title: `重选MJ反推提示词 ${data.value.name}`,
closeOnEsc: false,
content: () =>
h(ReSelectReversePrompt, {
reversePrompts: data.value.reversePrompt,
index: index.value
}),
style: `width : 750px;`,
maskClosable: false
})
}
let reverseOptions = [
{
label: '下推理',
key: 'down_reverse'
},
{
label: '删除反推提示词',
key: 'remove_reverse'
}
]
let tranlateOptions = [
{
label: '中文 2 英文',
key: 'translate_english'
},
{
label: '下翻译英文',
key: 'down_translate_english'
},
{
label: '下翻译中文',
key: 'down_translate_chinese'
}
]</script>
@@ -0,0 +1,235 @@
<template>
<div style="display: flex; justify-content: space-between; white-space: nowrap">
<div style="display: flex">
<div>反推/GPT提示词</div>
<n-button
v-if="reverseManageStore.selectBook.type == bookType.MJ_REVERSE"
type="text"
:color="softwareStore.SoftColor.ZHUYANTUO"
size="tiny"
style="margin-left: 10px"
@click="SelectPromptIndex"
>选择提示词</n-button
><n-button
type="text"
:color="softwareStore.SoftColor.ZHUYANTUO"
size="tiny"
style="margin-left: 5px"
@click="SelectStyle"
>风格</n-button
>
<n-button
type="text"
:color="softwareStore.SoftColor.ZHUYANTUO"
size="tiny"
style="margin-left: 5px"
@click="RemoveBadPrompt"
>去除提示词</n-button
>
<n-button
type="text"
:color="softwareStore.SoftColor.ZHUYANTUO"
size="tiny"
style="margin-left: 5px"
@click="ManagentBadPrompt"
>管理</n-button
>
</div>
<div>
<n-popover trigger="hover">
<template #trigger>
<n-button
color="#7c461e"
@click="SelectReversePromptAndReplace"
text
style="font-size: 24px"
>
<n-icon>
<FindReplaceRound />
</n-icon>
</n-button>
</template>
<span>批量替换反推提示词</span>
</n-popover>
</div>
</div>
</template>
<script setup>
import { ref, toRaw, h } from 'vue'
import { useMessage, useDialog, NButton, NPopover, NIcon } from 'naive-ui'
import { useReverseManageStore } from '../../../../../stores/reverseManage'
import { BookRepalceDataType, BookType } from '../../../../../define/enum/bookEnum'
import { useSoftwareStore } from '../../../../../stores/software'
import SelectMJReversePrompt from '../MJReverse/SelectMJReversePrompt.vue'
import SelectImageStyle from '../../Components/SelectImageStyle.vue'
import FindReplaceRound from '../../Icon/FindReplaceRound.vue'
import DatatableHeaderAfterGptSelectAndReplace from './DatatableHeaderAfterGptSelectAndReplace.vue'
import ManageBadPrompt from '../../Components/ManageBadPrompt.vue'
let message = useMessage()
let reverseManageStore = useReverseManageStore()
let softwareStore = useSoftwareStore()
let bookType = ref(BookType)
let dialog = useDialog()
let selectStyleRef = ref(null)
let da = undefined
let promprRef = ref(null)
/**
* 管理不想要的值
*/
async function ManagentBadPrompt() {
// 弹窗,管理不想要的值
dialog.create({
showIcon: false,
title: '管理不想要的值',
content: () => h(ManageBadPrompt, { ref: promprRef }),
style: `width : 650px;`,
maskClosable: false,
positiveText: '确定',
negativeText: '取消',
onPositiveClick: async () => {
console.log(toRaw(promprRef.value.tags))
// 将数据写入
await window.api.SaveBadPrompt(toRaw(promprRef.value.tags), (value) => {
console.log(value)
if (value.code == 0) {
message.error(value.message)
return false
}
message.success('添加成功')
})
return true
}
})
}
/** 移除不想要的值 */
async function RemoveBadPrompt() {
softwareStore.spin.spinning = true
softwareStore.spin.tip = '正在移除不想要的值...'
let dia = dialog.warning({
title: '移除不想要的值提示',
content: '是否移除反推/GPT提示词里面不想要的值,移除后不可恢复,是否继续?',
positiveText: '继续',
negativeText: '取消',
onPositiveClick: async () => {
try {
dia.destroy()
let res = await window.book.RemoveBadPrompt(reverseManageStore.selectBookTask.id)
if (res.code == 0) {
message.error(res.message)
return
}
console.log('RemoveBadPrompt', res)
if (res.data && res.data.length > 0) {
for (let i = 0; i < res.data.length; i++) {
const element = res.data[i]
let findIndex = reverseManageStore.selectBookTaskDetail.findIndex(
(item) => item.id == element.id
)
if (findIndex != -1) {
reverseManageStore.selectBookTaskDetail[findIndex].gptPrompt = element.gptPrompt
}
}
} else {
throw new Error('没有移除任何值')
}
message.success(res.message)
} catch (error) {
message.error(error.message)
} finally {
softwareStore.spin.spinning = false
}
}
})
}
/**
* 选择MJ反推提示词。用于生图
*/
async function SelectPromptIndex() {
// 判断是不是MJ反推,判断是不是所有的都有提示词
if (reverseManageStore.selectBook.type != BookType.MJ_REVERSE) {
message.error('只有MJ反推可以使用该按钮')
return
}
let hasReversePrompt = false
// 判断是不是所有的都有提示词,只要有一个有,就可以
for (let index = 0; index < reverseManageStore.selectBookTaskDetail.length; index++) {
const element = reverseManageStore.selectBookTaskDetail[index]
if (element.reversePrompt && element.reversePrompt.length > 0) {
hasReversePrompt = true
break
}
}
if (!hasReversePrompt) {
message.error('请先进行MJ反推')
return
}
dialog.create({
title: '选择反推提示词',
content: () => h(SelectMJReversePrompt),
style: 'width : 500px',
showIcon: false
})
}
// 选择风格
async function SelectStyle() {
// 先获取风格
// 判断当前数据是不是存在
// 处理数据。获取当前的所有的数据
let dialogWidth = window.innerWidth * 0.8
let dialogHeight = window.innerHeight * 0.9
// ImportWordAndSrt
dialog.create({
showIcon: false,
closeOnEsc: false,
content: () =>
h(SelectImageStyle, {
height: dialogHeight,
ref: selectStyleRef,
imageStyle: reverseManageStore.selectBookTask.imageStyle,
customizeImageStyle: reverseManageStore.selectBookTask.customizeImageStyle
}),
style: `width : ${dialogWidth}px; height : ${dialogHeight}px`,
maskClosable: false,
onClose: async () => {
console.log(selectStyleRef)
// 将数据保存到当前的数据中
// reverseManageStore.selectBookTask.styleList = toRaw(selectStyleRef.value.selectStyle)
// 保存当前的风格到数据库
let res = await window.book.SaveImageStyle(
toRaw(selectStyleRef.value.selectStyle),
reverseManageStore.selectBookTask.id
)
if (res.code == 0) {
message.error(res.message)
return
}
reverseManageStore.selectBookTask.imageStyle = res.data.imageStyle
reverseManageStore.selectBookTask.customizeImageStyle = res.data.customizeImageStyle
message.success('保存成功')
}
})
}
async function SelectReversePromptAndReplace() {
if (da) {
return
}
da = dialog.create({
title: '批量查询替换反推提示词',
showIcon: false,
maskClosable: false,
content: () =>
h(DatatableHeaderAfterGptSelectAndReplace, { type: BookRepalceDataType.GPT_PROMPT }),
onClose: () => {
da = undefined
}
})
}
</script>
@@ -2,7 +2,7 @@
<div style="display: flex; align-items: center">
<div>出图</div>
<n-select
style="width: 70px; margin-left: 5px"
style="width: 100px; margin-left: 5px"
size="tiny"
v-model:value="select"
@update:value="handleUpdateValue"
@@ -47,13 +47,20 @@ export default defineComponent({
? reverseManageStore.selectBookTask.imageCategory
: BookImageCategory.MJ
)
onMounted(async () => {})
let options = ref([])
onMounted(async () => {
//
await window.api.GetImageGenerateCategory((value) => {
if (value.code == 0) {
message.error(value.message)
return
}
options.value = value.data
})
})
async function handleUpdateValue(value) {
if (value == BookImageCategory.D3) {
message.error('暂不支持该出图模式')
return
}
//
let res = await window.db.UpdateBookTaskData(reverseManageStore.selectBookTask.id, {
imageCategory: value
@@ -124,20 +131,7 @@ export default defineComponent({
key: 'unlock'
}
],
options: [
{
label: 'MJ',
value: BookImageCategory.MJ
},
{
label: 'SD',
value: BookImageCategory.SD
},
{
label: 'D3',
value: BookImageCategory.D3
}
]
options
}
}
})
@@ -0,0 +1,305 @@
<template>
<div>
<n-dropdown trigger="hover" :options="mergeOptions" @select="MergePrompt">
<n-button
size="tiny"
:color="softwareStore.SoftColor.BROWN_YELLOW"
@click="MergePrompt(undefined)"
>
单句合并
</n-button>
</n-dropdown>
<n-button
style="margin-left: 5px"
size="tiny"
:color="softwareStore.SoftColor.BROWN_YELLOW"
@click="SingleGenerateImage"
>
单句生图
</n-button>
<n-button
style="margin-left: 5px"
size="tiny"
:color="softwareStore.SoftColor.BROWN_YELLOW"
@click="UnderGenerateImage"
>
下生图
</n-button>
<n-button
style="margin-left: 5px"
size="tiny"
:color="softwareStore.SoftColor.BROWN_YELLOW"
@click="ImportMJImageUrl"
>
导入图片
</n-button>
</div>
<n-input
type="textarea"
size="tiny"
@input="InputDebounced"
v-model:value="data.prompt"
:autosize="{ minRows: 6, maxRows: 6 }"
></n-input>
</template>
<script setup>
import { ref, onMounted, defineComponent, onUnmounted, toRaw, watch, h } from 'vue'
import { useMessage, useDialog, NInput, NButton, NDropdown } from 'naive-ui'
import { useSoftwareStore } from '../../../../../stores/software'
import { useReverseManageStore } from '../../../../../stores/reverseManage'
import {
BookBackTaskType,
OperateBookType,
TaskExecuteType
} from '../../../../../define/enum/bookEnum'
import { BookImageCategory } from '../../../../../define/enum/bookEnum'
import { debounce } from 'lodash'
import InputDialogContent from '../../Original/Components/InputDialogContent.vue'
import { DEFINE_STRING } from '../../../../../define/define_string'
let props = defineProps({
initData: undefined,
index: undefined
})
let message = useMessage()
let softwareStore = useSoftwareStore()
let reverseManageStore = useReverseManageStore()
let data = ref(props.initData)
let image_url_ref = ref(null)
let dialog = useDialog()
onMounted(async () => {})
// 合并提示词
async function MergePrompt(key) {
let type = key
if (!key) {
if (reverseManageStore.selectBookTask.imageCategory == BookImageCategory.MJ) {
type = 'mj_merge'
} else if (reverseManageStore.selectBookTask.imageCategory == BookImageCategory.SD) {
type = 'sd_merge'
} else {
type = 'mj_merge'
}
}
// 开始进行合并,调用不同的合并模式
// 合并之前,要确认保存
softwareStore.spin.spinning = true
softwareStore.spin.tip = '合并命令中。。。'
let res = await window.book.MergePrompt(data.value.id, type, OperateBookType.BOOKTASKDETAIL)
softwareStore.spin.spinning = false
// 这边对返回的数据进行处理
if (res.code == 0) {
message.error(res.message)
return
}
for (let i = 0; i < res.data.length; i++) {
const element = res.data[i]
let selectBookDetailIndex = reverseManageStore.selectBookTaskDetail.findIndex(
(item) => item.id == element.id
)
reverseManageStore.selectBookTaskDetail[selectBookDetailIndex].prompt = element.prompt
}
message.success(res.message)
}
// 单个出图
async function SingleGenerateImage() {
let res = undefined
if (reverseManageStore.selectBookTask.imageCategory == BookImageCategory.MJ) {
res = await window.mj.AddMJGenerateImageTask(
data.value.id,
OperateBookType.BOOKTASKDETAIL,
DEFINE_STRING.BOOK.MJ_IMAGE_GENERATE_RETURN
)
} else if (reverseManageStore.selectBookTask.imageCategory == BookImageCategory.SD) {
// 直接添加SD出图任务
res = await window.task.AddBookBackTask(
reverseManageStore.selectBook.id,
BookBackTaskType.SD_IMAGE,
TaskExecuteType.AUTO,
reverseManageStore.selectBookTask.id,
data.value.id,
DEFINE_STRING.BOOK.SD_IMAGE_GENERATE_RETURN
)
} else if (reverseManageStore.selectBookTask.imageCategory == BookImageCategory.D3) {
res = await window.task.AddBookBackTask(
reverseManageStore.selectBook.id,
BookBackTaskType.D3_IMAGE,
TaskExecuteType.AUTO,
reverseManageStore.selectBookTask.id,
data.value.id,
DEFINE_STRING.BOOK.D3_IMAGE_GENERATE_RETURN
)
} else if (reverseManageStore.selectBookTask.imageCategory == BookImageCategory.FLUX_API) {
res = await window.task.AddBookBackTask(
reverseManageStore.selectBook.id,
BookBackTaskType.FLUX_API_IMAGE,
TaskExecuteType.AUTO,
reverseManageStore.selectBookTask.id,
data.value.id,
DEFINE_STRING.BOOK.FLUX_API_IMAGE_GENERATE_RETURN
)
} else if (reverseManageStore.selectBookTask.imageCategory == BookImageCategory.FLUX_FORGE) {
res = await window.task.AddBookBackTask(
reverseManageStore.selectBook.id,
BookBackTaskType.FLUX_FORGE_IMAGE,
TaskExecuteType.AUTO,
reverseManageStore.selectBookTask.id,
data.value.id,
DEFINE_STRING.BOOK.FLUX_FORGE_IMAGE_GENERATE_RETURN
)
} else {
message.error('未知的出图模式')
return
}
if (res.code == 0) {
message.error(res.message)
return
}
message.success(res.message)
}
// 下生图
async function UnderGenerateImage() {
let res = undefined
let bookTaskDetails = reverseManageStore.selectBookTaskDetail.filter(
(item, index) => index >= props.index && !item.imageLock
)
if (bookTaskDetails.length <= 0) {
message.error('未找到可生成图片的分镜,或者分镜都被锁定')
return
}
if (reverseManageStore.selectBookTask.imageCategory == BookImageCategory.MJ) {
res = await window.mj.AddMJGenerateImageTask(
data.value.id,
OperateBookType.UNDERBOOKTASK,
DEFINE_STRING.BOOK.MJ_IMAGE_GENERATE_RETURN
)
} else if (reverseManageStore.selectBookTask.imageCategory == BookImageCategory.SD) {
// 添加SD出图任务
let tasksList = []
for (let i = 0; i < bookTaskDetails.length; i++) {
const element = bookTaskDetails[i]
tasksList.push({
bookId: reverseManageStore.selectBook.id,
type: BookBackTaskType.SD_IMAGE,
executeType: TaskExecuteType.AUTO,
bookTaskId: reverseManageStore.selectBookTask.id,
bookTaskDetailId: element.id,
messageName: DEFINE_STRING.BOOK.SD_IMAGE_GENERATE_RETURN
})
}
res = await window.task.AddMultiBookBackTask(tasksList)
} else if (reverseManageStore.selectBookTask.imageCategory == BookImageCategory.FLUX_FORGE) {
// 本地的forge flux
let tasksList = []
for (let i = 0; i < bookTaskDetails.length; i++) {
const element = bookTaskDetails[i]
tasksList.push({
bookId: reverseManageStore.selectBook.id,
type: BookBackTaskType.FLUX_FORGE_IMAGE,
executeType: TaskExecuteType.AUTO,
bookTaskId: reverseManageStore.selectBookTask.id,
bookTaskDetailId: element.id,
messageName: DEFINE_STRING.BOOK.FLUX_FORGE_IMAGE_GENERATE_RETURN
})
}
res = await window.task.AddMultiBookBackTask(tasksList)
} else if (reverseManageStore.selectBookTask.imageCategory == BookImageCategory.FLUX_API) {
// 本地的forge flux
let tasksList = []
for (let i = 0; i < bookTaskDetails.length; i++) {
const element = bookTaskDetails[i]
tasksList.push({
bookId: reverseManageStore.selectBook.id,
type: BookBackTaskType.FLUX_API_IMAGE,
executeType: TaskExecuteType.AUTO,
bookTaskId: reverseManageStore.selectBookTask.id,
bookTaskDetailId: element.id,
messageName: DEFINE_STRING.BOOK.FLUX_API_IMAGE_GENERATE_RETURN
})
}
res = await window.task.AddMultiBookBackTask(tasksList)
} else if (reverseManageStore.selectBookTask.imageCategory == BookImageCategory.D3) {
let tasksList = []
for (let i = 0; i < bookTaskDetails.length; i++) {
const element = bookTaskDetails[i]
tasksList.push({
bookId: reverseManageStore.selectBook.id,
type: BookBackTaskType.D3_IMAGE,
executeType: TaskExecuteType.AUTO,
bookTaskId: reverseManageStore.selectBookTask.id,
bookTaskDetailId: element.id,
messageName: DEFINE_STRING.BOOK.D3_IMAGE_GENERATE_RETURN
})
}
res = await window.task.AddMultiBookBackTask(tasksList)
} else {
message.error('未知的出图模式')
return
}
if (res.code == 0) {
message.error(res.message)
return
}
message.success(res.message)
}
let InputDebounced = debounce(handleInput, 1000)
async function handleInput() {
let res = await window.db.UpdateBookTaskDetailData(data.value.id, {
prompt: data.value.prompt
})
if (res.code == 0) {
message.error(res.message)
}
}
/**
* 写入图片的链接,将图片链接下载,并且分割
*/
async function ImportMJImageUrl() {
// 判断当前数据是不是存在
// 处理数据。获取当前的所有的数据
let dialogWidth = 400
let dialogHeight = 150
let da = dialog.create({
title: '添加图片链接/本地地址',
showIcon: false,
closeOnEsc: false,
content: () =>
h(InputDialogContent, {
ref: image_url_ref,
initData: null,
placeholder: '请输入图片链接/本地地址'
}),
style: `width : ${dialogWidth}px; min-height : ${dialogHeight}px`,
maskClosable: false,
onClose: async () => {
da?.destroy()
let row_image_url = image_url_ref.value.data
softwareStore.spin.spinning = true
softwareStore.spin.tip = '正在下载图片/分割图片中。。。'
let res = await window.book.DownloadImageUrlAndSplit(data.value.id, row_image_url)
softwareStore.spin.spinning = false
if (res.code == 0) {
message.error(res.message)
return
}
data.value.outImagePath = res.data.outImagePath
data.value.subImagePath = res.data.subImagePath
message.success(res.message)
}
})
}
let mergeOptions = [
{ label: 'MJ模式合并', key: 'mj_merge' },
{ label: 'SD模式合并', key: 'sd_merge' }
]
</script>
@@ -0,0 +1,110 @@
<template>
<n-spin :show="show">
<n-form :label-width="80" :model="formValue">
<n-form-item label="选择剪映草稿" path="draftName">
<n-select
v-model:value="formValue.draftName"
filterable
placeholder="选择草稿"
:options="draftSelect"
clearable
/>
</n-form-item>
<n-form-item path="writeTimeLine">
<n-button type="primary" @click="GetJianyingFrame">开始抽帧</n-button>
</n-form-item>
</n-form>
<template #description> 正在剪映抽帧中... </template>
</n-spin>
</template>
<script setup>
import { onMounted, ref } from 'vue'
import {
NForm,
NFormItem,
NInput,
NSpin,
NButton,
NSelect,
NCheckbox,
useMessage,
useDialog
} from 'naive-ui'
import { isEmpty } from 'lodash'
import { useReverseManageStore } from '../../../../../stores/reverseManage.ts'
let draftSelect = ref([])
let formValue = ref({
draftName: undefined
})
let show = ref(false)
let message = useMessage()
let dialog = useDialog()
let reverseManageStore = useReverseManageStore()
onMounted(() => {
// 获取草稿文件列表
window.api.getDraftFileList((value) => {
value.forEach((element) => {
let obj = {
label: element,
value: element
}
draftSelect.value.push(obj)
})
})
})
async function GetJianyingFrame() {
if (
reverseManageStore.selectBookTaskDetail &&
reverseManageStore.selectBookTaskDetail.length > 0
) {
let da = dialog.warning({
title: '剪映抽帧提示',
content:
'检测到到当前已经存在分镜数据,继续抽帧将会首先删除当前数据,哪怕是失败也不能恢复,是否继续?',
positiveText: '继续',
negativeText: '取消',
onPositiveClick: async () => {
da.destroy()
await JianyingFrame()
}
})
} else {
await JianyingFrame()
}
}
async function JianyingFrame() {
if (isEmpty(formValue.value.draftName)) {
message.error('请选择草稿文件')
return
}
show.value = true
try {
let res = await window.book.JianyingFrame(
reverseManageStore.selectBookTask.id,
formValue.value.draftName
)
if (res.code == 0) {
message.error(res.message)
return
}
message.success('剪映抽帧成功')
// 重新加载数据
// 分镜完成,重新获取一下数据
let getRes = await reverseManageStore.GetBookTaskDetail(reverseManageStore.selectBookTask.id)
if (getRes.code == 0) {
message.error(getRes.message)
return
}
} catch (error) {
message.error(error.toString())
} finally {
show.value = false
}
}
</script>
@@ -37,7 +37,7 @@
import { ref, onMounted, defineComponent, onUnmounted, toRaw, computed } from 'vue'
import { useMessage, NButton, NInput, NSpin, NDropdown } from 'naive-ui'
import { useReverseManageStore } from '../../../../../stores/reverseManage'
import DatatableGptPromptButton from '../Components/DatatableGptPromptButton.vue'
import DatatableGptPromptButton from './DatatableGptPromptButton.vue'
import { debounce } from 'lodash'
export default defineComponent({
File diff suppressed because it is too large Load Diff
@@ -10,164 +10,162 @@
</div>
</template>
<script>
import { ref, onMounted, defineComponent, watch, h } from 'vue'
<script setup>
import { ref, onMounted, defineComponent, computed, watch, h } from 'vue'
import { useMessage, NDataTable, NImage } from 'naive-ui'
import { useSoftwareStore } from '../../../../../stores/software'
import { useReverseManageStore } from '../../../../../stores/reverseManage'
import DataTableShowGenerateImage from '../../Original/Components/DataTableShowGenerateImage.vue'
import ManageBookOldImage from '../Components/ManageBookOldImage.vue'
import ManageBookOldImage from './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'
import DatatableAfterGpt from '../Components/DatatableAfterGpt.vue'
import DatatableGenerateImage from '../Components/DatatableGenerateImage.vue'
import DatatableHeaderGptPrompt from './DatatableHeaderGptPrompt.vue'
import DatatableHeaderAfterGpt from './DatatableHeaderAfterGpt.vue'
import DatatableHeaderPrompt from './DatatableHeaderPrompt.vue'
import DatatablePrompt from './DatatablePrompt.vue'
import DatatableHeaderImage from './DatatableHeaderImage.vue'
import DatatableAfterGpt from './DatatableAfterGpt.vue'
import DatatableGenerateImage from './DatatableGenerateImage.vue'
import ODataTableAction from '../Original/ODataTableAction.vue'
import SDReversePrompt from './SDReversePrompt.vue'
export default defineComponent({
components: { NDataTable, NImage },
let softwareStore = useSoftwareStore()
let reverseManageStore = useReverseManageStore()
let message = useMessage()
let maxHeight = ref(0)
let datatable = ref(null)
setup() {
let softwareStore = useSoftwareStore()
let reverseManageStore = useReverseManageStore()
let message = useMessage()
let maxHeight = ref(0)
let datatable = ref(null)
watch(
() => softwareStore.skip,
(newValue) => {
let doc = document.querySelectorAll('.n-data-table-tr')
let sk = doc[newValue.skipRowIndex]
datatable.value.scrollTo({ el: sk })
},
{ deep: true }
)
watch(
() => softwareStore.skip,
(newValue) => {
let doc = document.querySelectorAll('.n-data-table-tr')
let sk = doc[newValue.skipRowIndex]
datatable.value.scrollTo({ el: sk })
},
{ deep: true }
)
const createColumns = () => [
{
title: 'No.',
key: 'name',
width: 50,
fixed: 'left'
},
{
title(row) {
return h(DatatableHeaderAfterGpt)
},
key: 'afterGpt',
width: 200,
fixed: 'left',
render(row, index) {
return h(DatatableAfterGpt, { initData: row, index: index })
}
},
{
title: '图片/视频',
key: 'oldImage',
className: 'space-row',
width: 130,
render(row, index) {
return h(ManageBookOldImage, {
initData: row,
index: index
})
}
},
{
title(row) {
return h(DatatableHeaderGptPrompt)
},
key: 'gptPrompt',
className: 'space-row',
resizable: true,
width: 400,
minWidth: 400,
maxWidth: 600,
render(row, index) {
if (reverseManageStore.selectBook.type == BookType.MJ_REVERSE) {
return h(MJReversePrompt, {
initData: row,
index: index
})
} else if (reverseManageStore.selectBook.type == BookType.SD_REVERSE) {
return h('span', ['SD 反推这边不能用', index])
} else if (reverseManageStore.selectBook.type == BookType.ORIGINAL) {
return h('span', ['原创 这边暂时不能用', index])
}
return h('span', ['不支持的类型', index])
}
},
{
title(row) {
return h(DatatableHeaderPrompt)
},
className: 'space-row',
key: 'row1',
render(row, index) {
return h(DatatablePrompt, {
initData: row,
index: index
})
}
},
{
title() {
return h(DatatableHeaderImage)
},
width: 300,
className: 'space-row',
render(row, index) {
return h(DatatableGenerateImage, {
image_generate_category: 'mj',
initData: row,
index: index,
type: 'data'
})
},
fixed: 'right'
}
]
onMounted(async () => {
calcHeight()
setTimeout(() => {
let div = document.getElementsByClassName('space-row')
for (let i = 0; i < div.length; i++) {
div[i].style.padding = '0px 5px'
}
}, 100)
// 数据加载完毕
let detailRes = await window.book.GetBookTaskDetail(reverseManageStore.selectBookTask.id)
if (detailRes.code == 0) {
message.error(detailRes.message)
softwareStore.spin.spinning = false
return
}
softwareStore.spin.spinning = false
reverseManageStore.selectBookTaskDetail = detailRes.data
})
function calcHeight() {
// 视口的高度
let height = window.innerHeight
maxHeight.value = height - 120
const columns = [
{
title: 'No.',
key: 'no',
width: 50,
fixed: 'left'
},
{
title(row) {
return h(DatatableHeaderAfterGpt)
},
key: 'afterGpt',
width: 200,
fixed: 'left',
className: 'empty-margin',
render(row, index) {
return h(DatatableAfterGpt, { initData: row, index: index })
}
return {
softwareStore,
reverseManageStore,
maxHeight,
columns: createColumns(),
datatable
},
{
title: '图片/视频',
key: 'oldImage',
className: 'empty-margin',
width: 130,
render(row, index) {
return h(ManageBookOldImage, {
initData: row,
index: index
})
}
},
{
title(row) {
return h(DatatableHeaderGptPrompt)
},
key: 'gptPrompt',
className: 'empty-margin',
resizable: true,
width: 400,
minWidth: 400,
maxWidth: 600,
render(row, index) {
if (reverseManageStore.selectBook.type == BookType.MJ_REVERSE) {
return h(MJReversePrompt, {
initData: row,
index: index
})
} else if (reverseManageStore.selectBook.type == BookType.SD_REVERSE) {
return h(SDReversePrompt, {
initData: row,
index: index
})
} else if (reverseManageStore.selectBook.type == BookType.ORIGINAL) {
return h('span', ['原创 这边暂时不能用', index])
}
return h('span', ['不支持的类型', index])
}
},
{
title(row) {
return h(DatatableHeaderPrompt)
},
className: 'empty-margin',
key: 'row1',
render(row, index) {
return h(DatatablePrompt, {
initData: row,
index: index
})
}
},
{
// 参数
title: '参数',
key: 'parameter',
width: '110',
render(row, index) {
return h(ODataTableAction, { initData: row, index: index })
}
},
{
title() {
return h(DatatableHeaderImage)
},
width: 300,
className: 'empty-margin',
render(row, index) {
return h(DatatableGenerateImage, {
image_generate_category: 'mj',
initData: row,
index: index,
type: 'data'
})
},
fixed: 'right'
}
]
onMounted(async () => {
setTimeout(() => {
let div = document.getElementsByClassName('empty-margin')
for (let i = 0; i < div.length; i++) {
div[i].style.padding = '0px 5px'
}
calcHeight()
// getMaxHeight()
}, 100)
// 数据加载完毕
let detailRes = await window.book.GetBookTaskDetail(reverseManageStore.selectBookTask.id)
if (detailRes.code == 0) {
message.error(detailRes.message)
softwareStore.spin.spinning = false
return
}
softwareStore.spin.spinning = false
reverseManageStore.selectBookTaskDetail = detailRes.data
})
function calcHeight() {
// 视口的高度
let height = window.innerHeight
maxHeight.value = height - 120
}
</script>
@@ -0,0 +1,44 @@
<template>
<div>
<DatatableGptPromptButton :initData="data" :index="index" />
</div>
<n-input
type="textarea"
size="tiny"
v-model:value="data.gptPrompt"
:autosize="{
minRows: 6,
maxRows: 6
}"
@input="InputDebounced"
>
</n-input>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import { useMessage, NButton, NInput, NSpin, NDropdown } from 'naive-ui'
import { useReverseManageStore } from '../../../../../stores/reverseManage'
import DatatableGptPromptButton from './DatatableGptPromptButton.vue'
import { debounce } from 'lodash'
let props = defineProps({
initData: undefined,
index: undefined
})
let data = ref(props.initData)
let index = ref(props.index)
let message = useMessage()
let reverseManageStore = useReverseManageStore()
onMounted(async () => {})
let InputDebounced = debounce(handleInput, 1000)
async function handleInput(value) {
let res = await window.db.UpdateBookTaskDetailData(data.value.id, {
gptPrompt: value
})
if (res.code == 0) {
message.error(res.message)
}
}
</script>
@@ -1,7 +1,12 @@
<template>
<div style="height: 100%; display: flex; width: 100%; overflow: auto; padding: 0; margin: 0">
<div>
<div v-if="reverseManageStore.selectBook.type == bookType.MJ_REVERSE">
<div
v-if="
reverseManageStore.selectBook.type == bookType.MJ_REVERSE ||
reverseManageStore.selectBook.type == bookType.SD_REVERSE
"
>
<div v-if="true" class="left_container" style="flex: 10; min-width: 500px">
<ManageBookDetailButton />
<ManageBookReverseTable style="margin-top: 5px" />
@@ -20,10 +25,10 @@
</div>
</template>
<script>
<script setup>
import { ref, onMounted, defineComponent, onUnmounted, toRaw, watch } from 'vue'
import { useMessage, NDivider } from 'naive-ui'
import ManageBookDetailButton from './Components/ManageBookDetailButton.vue'
import ManageBookDetailButton from './MJReverse/ManageBookDetailButton.vue'
import ManageBookReverseTable from './MJReverse/ManageBookReverseTable.vue'
import ManageBookShowLogger from './Components/ManageBook/ManageBookShowLogger.vue'
import { useSoftwareStore } from '../../../../stores/software'
@@ -33,202 +38,250 @@ import { ResponseMessageType } from '../../../../define/enum/softwareEnum'
import { useReverseManageStore } from '../../../../stores/reverseManage'
import OriginalMainPage from './Original/OriginalMainPage.vue'
export default defineComponent({
components: {
ManageBookDetailButton,
ManageBookReverseTable,
ManageBookShowLogger,
NDivider,
OriginalMainPage
},
let message = useMessage()
let softwareStore = useSoftwareStore()
let reverseManageStore = useReverseManageStore()
let bookType = ref(BookType)
setup() {
let message = useMessage()
let softwareStore = useSoftwareStore()
let reverseManageStore = useReverseManageStore()
let bookType = ref(BookType)
onMounted(async () => {
// 监听提取文案的返回数据
window.api.setEventListen([DEFINE_STRING.BOOK.GET_COPYWRITING_RETURN], (value) => {
if (value.code == 0) {
message.error(value.message)
code.value = code.value + '\n' + value.data
return
}
// 修改进度
softwareStore.spin.tip = `正在提取文案,当前进度 ${value.data.progress.current} / ${value.data.progress.total} 。。。`
let index = reverseManageStore.selectBookTaskDetail.findIndex((item) => item.id == value.id)
if (index >= 0) {
reverseManageStore.selectBookTaskDetail[index].word = value.data.content
reverseManageStore.selectBookTaskDetail[index].afterGpt = value.data.content
}
})
// 监听删除水印的返回的图片数据
window.api.setEventListen([DEFINE_STRING.BOOK.REMOVE_WATERMARK_RETURN], (value) => {
if (value.code == 0) {
message.error(value.message)
return
}
let index = reverseManageStore.selectBookTaskDetail.findIndex((item) => item.id == value.id)
if (index >= 0) {
reverseManageStore.selectBookTaskDetail[index].oldImage =
value.data + '?time=' + new Date().getTime()
}
})
// 监听反推提示词的返回数据
window.api.setEventListen([DEFINE_STRING.BOOK.REVERSE_PROMPT_RETURN], (value) => {
if (value.code == 0) {
errorResponse(value)
} else {
MJReverseResponse(value)
}
})
// 监听MJ出图的返回信息
window.api.setEventListen([DEFINE_STRING.BOOK.MJ_IMAGE_GENERATE_RETURN], (value) => {
if (value.code == 0) {
errorResponse(value)
}
MJImageResponse(value)
})
// 监听反推的翻译数据返回
window.api.setEventListen([DEFINE_STRING.BOOK.REVERSE_PROMPT_TRANSLATE_RETURN], (value) => {
if (value.code == 0) {
errorResponse(value)
} else {
if (value.type == ResponseMessageType.REVERSE_PROMPT_TRANSLATE) {
ReversePromptTranslateResponse(value)
} else if (value.type == ResponseMessageType.GPT_PROMPT_TRANSLATE) {
GptPromptTranslateResponse(value)
} else {
message.error('错误的返回数据类型')
return
}
}
})
})
function errorResponse(value) {
switch (value.dialogType) {
case DialogType.DIALOG:
window.api.showGlobalMessageDialog(value)
break
case DialogType.MESSAGE:
message.error(value.message)
break
case DialogType.NOTIFICATION:
window.api.showGlobalNotificationDialog(value)
break
default:
window.api.showGlobalNotificationDialog(value)
break
}
// 原创SD生图返回的数据进行修改
function OriginalSDImageResponseReturn(value) {
console.log('OriginalSDImageResponseReturn', value)
// 开始修改
if (value.data) {
let dataIndex = reverseManageStore.selectBookTaskDetail.findIndex(
(item) => item.id == value.data.id
)
if (dataIndex >= 0) {
reverseManageStore.selectBookTaskDetail[dataIndex].mjMessage = value.data
reverseManageStore.selectBookTaskDetail[dataIndex].outImagePath = value.data.outImagePath
reverseManageStore.selectBookTaskDetail[dataIndex].subImagePath = value.data.subImagePath
}
onUnmounted(() => {
window.api.removeEventListen(DEFINE_STRING.BOOK.GET_COPYWRITING_RETURN)
window.api.removeEventListen(DEFINE_STRING.BOOK.REMOVE_WATERMARK_RETURN)
window.api.removeEventListen(DEFINE_STRING.BOOK.REVERSE_PROMPT_RETURN)
window.api.removeEventListen(DEFINE_STRING.BOOK.MJ_IMAGE_GENERATE_RETURN)
window.api.removeEventListen(DEFINE_STRING.BOOK.REVERSE_PROMPT_TRANSLATE_RETURN)
})
function MJImageResponse(value) {
//开始修改
if (value.data) {
let dataIndex = reverseManageStore.selectBookTaskDetail.findIndex(
(item) => item.id == value.data.id
)
if (dataIndex >= 0) {
reverseManageStore.selectBookTaskDetail[dataIndex].mjMessage = value.data
reverseManageStore.selectBookTaskDetail[dataIndex].outImagePath = value.data.outImagePath
reverseManageStore.selectBookTaskDetail[dataIndex].subImagePath = value.data.subImagePath
}
}
if (value.data.status == 'error') {
message.error(value.data.message)
}
/**
* 处理翻译返回的数据,主要是将翻译的数据放到对应的位置
* @param value 返回的数据,数据结构如下
* bookTaskDetailId: string // 分镜ID
* reversePromptId: string // 反推提示词ID
* from: string // 源语言
* to: string // 目标语言 to 是 zh 值重新保存到 promptCN 中,to 是 en ,需要重新覆盖prompt和prom
* prompt: string // 英文数据
* promptCN: string // 中文翻译数据(但是也可以存储英文)
*/
function ReversePromptTranslateResponse(value) {
if (!value.data) {
return
}
// 修改进度
softwareStore.spin.tip = `翻译中 ... ${value.data.progress} / ${value.data.total}`
let detailIndex = reverseManageStore.selectBookTaskDetail.findIndex(
(item) => item.id == value.data.bookTaskDetailId
)
if (detailIndex < 0) {
return
}
let dataObj = reverseManageStore.selectBookTaskDetail[detailIndex]
let reverseIndex = dataObj.reversePrompt.findIndex(
(item) => item.id == value.data.reversePromptId
)
if (reverseIndex < 0) {
return
}
if (value.data.to == 'en') {
reverseManageStore.selectBookTaskDetail[detailIndex].reversePrompt[reverseIndex].prompt =
value.data.prompt
reverseManageStore.selectBookTaskDetail[detailIndex].reversePrompt[reverseIndex].promptCN =
value.data.promptCN
} else if (value.data.to == 'zh') {
reverseManageStore.selectBookTaskDetail[detailIndex].reversePrompt[reverseIndex].promptCN =
value.data.promptCN
} else {
message.error('未知的翻译目标语言')
}
}
function GptPromptTranslateResponse(value) {
console.log('GptPromptTranslateResponse', value)
if (!value.data) {
return
}
let index = reverseManageStore.selectBookTaskDetail.findIndex(
(item) => item.id == value.data.bookTaskDetailId
)
if (index < 0) {
return
}
reverseManageStore.selectBookTaskDetail[index].gptPrompt = value.data.prompt
// 这边也要修改进度
softwareStore.spin.tip = `翻译中 ... ${value.data.progress} / ${value.data.total}`
}
/**
* MJ反推返回数据
*/
function MJReverseResponse(value) {
if (value.data && value.data.progress == 100) {
let dataIndex = reverseManageStore.selectBookTaskDetail.findIndex(
(item) => item.id == value.data.id
)
if (dataIndex >= 0) {
reverseManageStore.selectBookTaskDetail[dataIndex].gptPrompt = undefined
reverseManageStore.selectBookTaskDetail[dataIndex].reversePrompt = JSON.parse(
value.data.prompt
)
}
}
}
return { softwareStore, reverseManageStore, bookType }
}
}
onMounted(async () => {
// 监听SD出图返回的数据
window.api.setEventListen([DEFINE_STRING.BOOK.SD_IMAGE_GENERATE_RETURN], (value) =>
OriginalSDImageResponseReturn(value)
)
// 监听D3出图返回的数据
window.api.setEventListen([DEFINE_STRING.BOOK.D3_IMAGE_GENERATE_RETURN], (value) => {
OriginalSDImageResponseReturn(value)
})
// 监听FLUX API 出图返回的数据
window.api.setEventListen([DEFINE_STRING.BOOK.FLUX_API_IMAGE_GENERATE_RETURN], (value) =>
OriginalSDImageResponseReturn(value)
)
// 监听FLUX FORGE 出图返回的数据
window.api.setEventListen([DEFINE_STRING.BOOK.FLUX_FORGE_IMAGE_GENERATE_RETURN], (value) =>
OriginalSDImageResponseReturn(value)
)
// 监听提取文案的返回数据
window.api.setEventListen([DEFINE_STRING.BOOK.GET_COPYWRITING_RETURN], (value) => {
if (value.code == 0) {
message.error(value.message)
code.value = code.value + '\n' + value.data
return
}
// 修改进度
softwareStore.spin.tip = `正在提取文案,当前进度 ${value.data.progress.current} / ${value.data.progress.total} 。。。`
let index = reverseManageStore.selectBookTaskDetail.findIndex((item) => item.id == value.id)
if (index >= 0) {
reverseManageStore.selectBookTaskDetail[index].word = value.data.content
reverseManageStore.selectBookTaskDetail[index].afterGpt = value.data.content
}
})
// 监听删除水印的返回的图片数据
window.api.setEventListen([DEFINE_STRING.BOOK.REMOVE_WATERMARK_RETURN], (value) => {
if (value.code == 0) {
message.error(value.message)
return
}
let index = reverseManageStore.selectBookTaskDetail.findIndex((item) => item.id == value.id)
if (index >= 0) {
reverseManageStore.selectBookTaskDetail[index].oldImage =
value.data + '?time=' + new Date().getTime()
}
})
// 监听反推提示词的返回数据
window.api.setEventListen([DEFINE_STRING.BOOK.REVERSE_PROMPT_RETURN], (value) => {
if (value.code == 0) {
errorResponse(value)
return
}
if (value.type == ResponseMessageType.MJ_REVERSE) {
MJReverseResponse(value)
} else if (value.type == ResponseMessageType.SD_REVERSE) {
SDReverseResponse(value)
} else {
console.error('错误的返回数据类型')
return
}
})
// 监听MJ出图的返回信息
window.api.setEventListen([DEFINE_STRING.BOOK.MJ_IMAGE_GENERATE_RETURN], (value) => {
if (value.code == 0) {
errorResponse(value)
}
MJImageResponse(value)
})
// 监听反推的翻译数据返回
window.api.setEventListen([DEFINE_STRING.BOOK.REVERSE_PROMPT_TRANSLATE_RETURN], (value) => {
if (value.code == 0) {
errorResponse(value)
} else {
if (value.type == ResponseMessageType.REVERSE_PROMPT_TRANSLATE) {
ReversePromptTranslateResponse(value)
} else if (value.type == ResponseMessageType.GPT_PROMPT_TRANSLATE) {
GptPromptTranslateResponse(value)
} else {
message.error('错误的返回数据类型')
return
}
}
})
})
function errorResponse(value) {
switch (value.dialogType) {
case DialogType.DIALOG:
window.api.showGlobalMessageDialog(value)
break
case DialogType.MESSAGE:
message.error(value.message)
break
case DialogType.NOTIFICATION:
window.api.showGlobalNotificationDialog(value)
break
default:
window.api.showGlobalNotificationDialog(value)
break
}
}
onUnmounted(() => {
window.api.removeEventListen(DEFINE_STRING.BOOK.GET_COPYWRITING_RETURN)
window.api.removeEventListen(DEFINE_STRING.BOOK.REMOVE_WATERMARK_RETURN)
window.api.removeEventListen(DEFINE_STRING.BOOK.REVERSE_PROMPT_RETURN)
window.api.removeEventListen(DEFINE_STRING.BOOK.MJ_IMAGE_GENERATE_RETURN)
window.api.removeEventListen(DEFINE_STRING.BOOK.REVERSE_PROMPT_TRANSLATE_RETURN)
window.api.removeEventListen(DEFINE_STRING.BOOK.SD_IMAGE_GENERATE_RETURN)
window.api.removeEventListen(DEFINE_STRING.BOOK.D3_IMAGE_GENERATE_RETURN)
window.api.removeEventListen(DEFINE_STRING.BOOK.FLUX_API_IMAGE_GENERATE_RETURN)
window.api.removeEventListen(DEFINE_STRING.BOOK.FLUX_FORGE_IMAGE_GENERATE_RETURN)
})
function MJImageResponse(value) {
//开始修改
if (value.data) {
let dataIndex = reverseManageStore.selectBookTaskDetail.findIndex(
(item) => item.id == value.data.id
)
if (dataIndex >= 0) {
reverseManageStore.selectBookTaskDetail[dataIndex].mjMessage = value.data
reverseManageStore.selectBookTaskDetail[dataIndex].outImagePath = value.data.outImagePath
reverseManageStore.selectBookTaskDetail[dataIndex].subImagePath = value.data.subImagePath
}
}
}
/**
* 处理翻译返回的数据,主要是将翻译的数据放到对应的位置
* @param value 返回的数据,数据结构如下
* bookTaskDetailId: string // 分镜ID
* reversePromptId: string // 反推提示词ID
* from: string // 源语言
* to: string // 目标语言 to 是 zh 值重新保存到 promptCN 中,to 是 en ,需要重新覆盖prompt和prom
* prompt: string // 英文数据
* promptCN: string // 中文翻译数据(但是也可以存储英文)
*/
function ReversePromptTranslateResponse(value) {
if (!value.data) {
return
}
// 修改进度
softwareStore.spin.tip = `翻译中 ... ${value.data.progress} / ${value.data.total}`
let detailIndex = reverseManageStore.selectBookTaskDetail.findIndex(
(item) => item.id == value.data.bookTaskDetailId
)
if (detailIndex < 0) {
return
}
let dataObj = reverseManageStore.selectBookTaskDetail[detailIndex]
let reverseIndex = dataObj.reversePrompt.findIndex(
(item) => item.id == value.data.reversePromptId
)
if (reverseIndex < 0) {
return
}
if (value.data.to == 'en') {
reverseManageStore.selectBookTaskDetail[detailIndex].reversePrompt[reverseIndex].prompt =
value.data.prompt
reverseManageStore.selectBookTaskDetail[detailIndex].reversePrompt[reverseIndex].promptCN =
value.data.promptCN
} else if (value.data.to == 'zh') {
reverseManageStore.selectBookTaskDetail[detailIndex].reversePrompt[reverseIndex].promptCN =
value.data.promptCN
} else {
message.error('未知的翻译目标语言')
}
}
function GptPromptTranslateResponse(value) {
console.log('GptPromptTranslateResponse', value)
if (!value.data) {
return
}
let index = reverseManageStore.selectBookTaskDetail.findIndex(
(item) => item.id == value.data.bookTaskDetailId
)
if (index < 0) {
return
}
reverseManageStore.selectBookTaskDetail[index].gptPrompt = value.data.prompt
// 这边也要修改进度
softwareStore.spin.tip = `翻译中 ... ${value.data.progress} / ${value.data.total}`
}
/**
* MJ反推返回数据
*/
function MJReverseResponse(value) {
if (value.data && value.data.progress == 100) {
let dataIndex = reverseManageStore.selectBookTaskDetail.findIndex(
(item) => item.id == value.data.id
)
if (dataIndex >= 0) {
reverseManageStore.selectBookTaskDetail[dataIndex].gptPrompt = undefined
reverseManageStore.selectBookTaskDetail[dataIndex].reversePrompt = JSON.parse(
value.data.prompt
)
}
}
}
/**
* SD反推返回数据
*/
function SDReverseResponse(value) {
if (value.data) {
let dataIndex = reverseManageStore.selectBookTaskDetail.findIndex((item) => item.id == value.id)
if (dataIndex >= 0) {
reverseManageStore.selectBookTaskDetail[dataIndex].gptPrompt = value.data
}
}
}
</script>
<style lang="css">
@@ -30,11 +30,29 @@
@update:checked-row-keys="handleCheck"
/>
</div>
<n-dropdown
placement="bottom-start"
trigger="manual"
:x="x"
:y="y"
:options="options"
:show="showDropdown"
:on-clickoutside="onClickoutside"
@select="handleSelect"
/>
</template>
<script setup>
import { ref, onMounted, defineComponent, onUnmounted, toRaw, watch, h } from 'vue'
import { useMessage, useDialog, NButton, NDataTable, NIcon, NWatermark } from 'naive-ui'
import { ref, onMounted, defineComponent, onUnmounted, toRaw, nextTick, h } from 'vue'
import {
useMessage,
useDialog,
NButton,
NDataTable,
NIcon,
NDropdown,
NPerformantEllipsis
} from 'naive-ui'
import { useReverseManageStore } from '../../../../stores/reverseManage'
import { useSoftwareStore } from '../../../../stores/software'
import { AddSharp } from '@vicons/ionicons5'
@@ -53,6 +71,12 @@ const router = useRouter()
let message = useMessage()
let columns = createColumns()
const checkedRowKeysRef = ref([])
let showDropdown = ref(false)
let x = ref(0)
let y = ref(0)
let selectRow = ref(null)
let options = []
onMounted(async () => {
window.api.setEventListen([DEFINE_STRING.BOOK.MAIN_DATA_RETURN], (value) => {
if (value.code == 0) {
@@ -107,39 +131,238 @@ function createColumns() {
width: 100
},
{
title: '操作',
key: 'action',
width: 215,
fixed: 'right',
title: '失败原因',
key: 'errorMsg',
width: 210,
render(row, index) {
return h(BookTaskListAction, {
bookTask: row
})
return h(
NPerformantEllipsis,
{
style: 'max-width: 200px'
},
row.errorMsg
)
}
}
]
}
/**
* 高清数据
*/
async function hdImageFunc(id, operateBookType) {
function createOptions(row) {
return [
{
label: `高清 ${row.name}`,
key: 'hd'
},
{
label: `生成草稿 ${row.name}`,
key: 'draft'
},
{
label: `合成视频 ${row.name}`,
key: 'generateVideo'
},
{
label: () => h('span', { style: { color: '#f4a534' } }, `重置 ${row.name}`),
key: 'reset'
},
{
label: () => h('span', { style: { color: 'red' } }, `删除 ${row.name}`),
key: 'delete'
}
]
}
async function HDImage(value) {
softwareStore.spin.spinning = true
softwareStore.spin.tip = '正在高清中。。。'
let res = await window.book.HDImage(id, 4, operateBookType)
softwareStore.spin.tip = '正在进行高清检查。。。'
let checkImage = await window.book.CheckImageFileSize(value.id, 10240, OperateBookType.BOOKTASK)
softwareStore.spin.spinning = false
if (res.code == 1) {
message.success('高清成功')
if (checkImage.code == 0) {
message.error(checkImage.message)
return
}
// 高清检查完成
if (checkImage.data && checkImage.data.length > 0) {
// 出现了图片大的,用户判断是不是需要高清
let msg = checkImage.data.map((item, index) => {
return item.outImagePath + '\n'
})
dialog.warning({
title: '高清警告',
content: '下面的图片大于10MB,是否继续高清?' + '\n' + msg,
positiveText: '确定',
negativeText: '取消',
maskClosable: false,
onPositiveClick: async () => {
alert('确认高清')
await hdImageFunc(value.id, OperateBookType.BOOKTASK)
}
})
} else {
message.error(res.message)
// 没有就直接高清
await hdImageFunc(value.id, OperateBookType.BOOKTASK)
}
}
async function hdImageFunc(id, operateBookType) {
let da = dialog.warning({
title: '高清提示',
content: `是否确认高清,当前的高清倍数为 ${softwareStore.globalSetting.hdScale} 倍,是否继续?`,
positiveText: '继续',
negativeText: '取消',
onPositiveClick: async () => {
da.destroy()
softwareStore.spin.spinning = true
softwareStore.spin.tip = '正在高清中。。。'
let res = await window.book.HDImage(
id,
softwareStore.globalSetting.hdScale ?? 2,
operateBookType
)
softwareStore.spin.spinning = false
if (res.code == 1) {
message.success('高清成功')
} else {
message.error(res.message)
}
}
})
}
/**
* 生成草稿
*/
async function ClipDraft(value) {
dialog.info({
closeOnEsc: false,
title: `生成草稿前检查 ${value.name}`,
maskClosable: false,
content: () =>
h(ManageBookTaskGenerateInformation, {
bookTask: value,
type: 'bookTask',
optionType: 'draft'
})
})
}
/**
* 合成视频
*/
async function GenerateVideo(value) {
dialog.info({
closeOnEsc: false,
title: `合成视频前检查 ${value.name}`,
maskClosable: false,
content: () =>
h(ManageBookTaskGenerateInformation, {
bookTask: value,
type: 'bookTask',
optionType: 'video'
})
})
}
/**
* 重置小说数据
* @param e
*/
async function ReSetBookTaskDetail(value) {
dialog.warning({
title: '重置小说任务',
positiveText: '确定',
negativeText: '取消',
content:
'确定要重置小说任务吗?该操作不可逆,会将对应的批次任务的数据清空,分镜数据全部删除,请谨慎操作!',
onPositiveClick: async () => {
let res = await window.book.ReSetBookTask(value.id, OperateBookType.BOOKTASK)
if (res.code == 1) {
message.success('重置小说任务成功')
} else {
message.error(res.message)
}
}
})
}
/**
* 删除小说任务
*/
async function DeleteBookTask(value) {
dialog.warning({
title: '删除小说任务',
positiveText: '确定',
negativeText: '取消',
content:
'确定要删除小说任务吗?该操作不可逆,会将对应的批次任务数据和分镜数据全部删除,请谨慎操作!',
onPositiveClick: async () => {
let res = await window.book.DeleteBookTask(value.id, OperateBookType.BOOKTASK)
if (res.code == 1) {
// 加载任务列表
let bookTaskRes = await reverseManageStore.GetBookTaskDataFromDB({
bookId: reverseManageStore.selectBook.id
})
softwareStore.spin.spinning = false
if (bookTaskRes.code == 0) {
message.error(bookTaskRes.message)
return
}
// 删除成功
message.success('删除小说任务并刷新数据成功')
} else {
message.error(res.message)
}
}
})
}
async function handleSelect(key) {
showDropdown.value = false
switch (key) {
case 'hd':
await HDImage(selectRow.value)
break
case 'draft':
await ClipDraft(selectRow.value)
break
case 'generateVideo':
await GenerateVideo(selectRow.value)
break
case 'reset':
await ReSetBookTaskDetail(selectRow.value)
break
case 'delete':
await DeleteBookTask(selectRow.value)
break
default:
message.error('未知的下拉值')
break
}
}
function onClickoutside() {
showDropdown.value = false
}
// /**
// * 高清数据
// */
// async function hdImageFunc2(id, operateBookType) {
// softwareStore.spin.spinning = true
// softwareStore.spin.tip = '正在高清中。。。'
// let res = await window.book.HDImage(id, 4, operateBookType)
// softwareStore.spin.spinning = false
// if (res.code == 1) {
// message.success('高清成功')
// } else {
// message.error(res.message)
// }
// }
/**
* 一键高清全部
*/
async function HDImageAll() {
console.log('一键高清', checkedRowKeysRef.value)
if (checkedRowKeysRef.value.length == 0) {
message.error('请选择要高清的数据')
return
@@ -214,21 +437,99 @@ async function DraftAll() {
* 一键生成视频
*/
async function VideoAll() {
alert('视频全部')
if (checkedRowKeysRef.value.length == 0) {
message.error('请选择要生成草稿的任务')
return
}
// 草稿弹窗
dialog.info({
closeOnEsc: false,
title: `生成草稿前检查 ${reverseManageStore.selectBook.name}`,
maskClosable: false,
content: () =>
h(ManageBookTaskGenerateInformation, {
bookTask: reverseManageStore.selectBook,
type: 'book',
selectBookTask: [...checkedRowKeysRef.value],
optionType: 'video'
})
})
}
/**
* 一键重置
*/
async function ResetAll() {
alert('重置全部')
if (checkedRowKeysRef.value.length == 0) {
message.error('请选择要高清的数据')
return
}
dialog.warning({
title: '重置选中小说任务',
positiveText: '确定',
negativeText: '取消',
content:
'确定要重置选中的小说任务吗?该操作不可逆,会将对应的批次任务的数据清空,分镜数据全部删除,请谨慎操作!',
onPositiveClick: async () => {
let res = await window.book.ReSetBookTask(
[...checkedRowKeysRef.value],
OperateBookType.ASSIGNBOOKTASK
)
if (res.code == 1) {
// 加载任务列表
let bookTaskRes = await reverseManageStore.GetBookTaskDataFromDB({
bookId: reverseManageStore.selectBook.id
})
softwareStore.spin.spinning = false
if (bookTaskRes.code == 0) {
message.error(bookTaskRes.message)
return
}
message.success('重置小说任务成功')
} else {
message.error(res.message)
}
}
})
}
/**
* 一键删除
*/
async function DeleteAll() {
alert('删除全部')
if (checkedRowKeysRef.value.length == 0) {
message.error('请选择要高清的数据')
return
}
dialog.warning({
title: '删除所选小说任务',
positiveText: '确定',
negativeText: '取消',
content:
'确定要删除选中的小说任务吗?该操作不可逆,会将对应的批次任务数据和分镜数据全部删除,请谨慎操作!',
onPositiveClick: async () => {
let res = await window.book.DeleteBookTask(
[...checkedRowKeysRef.value],
OperateBookType.ASSIGNBOOKTASK
)
if (res.code == 1) {
// 加载任务列表
let bookTaskRes = await reverseManageStore.GetBookTaskDataFromDB({
bookId: reverseManageStore.selectBook.id
})
softwareStore.spin.spinning = false
if (bookTaskRes.code == 0) {
message.error(bookTaskRes.message)
return
}
// 删除成功
message.success('删除小说任务并刷新数据成功')
} else {
message.error(res.message)
}
}
})
}
const renderIcon = () => {
return h(NIcon, null, {
@@ -245,6 +546,17 @@ const rowProps = (row) => {
softwareStore.spin.tip = '正在加载数据'
// 跳转
router.push({ name: 'manage_book', params: { id: row.id } })
},
onContextmenu: (e) => {
e.preventDefault()
selectRow.value = row
showDropdown.value = false
options = createOptions(row)
nextTick().then(() => {
showDropdown.value = true
x.value = e.clientX
y.value = e.clientY
})
}
}
}
@@ -87,7 +87,7 @@ import { isEmpty } from 'lodash'
import { TranslateType } from '../../../../../define/enum/translate'
import { DEFINE_STRING } from '../../../../../define/define_string'
import FindReplaceRound from '../../Icon/FindReplaceRound.vue'
import DatatableHeaderAfterGptSelectAndReplace from '../Components/DatatableHeaderAfterGptSelectAndReplace.vue'
import DatatableHeaderAfterGptSelectAndReplace from '../MJReverse/DatatableHeaderAfterGptSelectAndReplace.vue'
import { BookRepalceDataType } from '../../../../../define/enum/bookEnum'
let softwareStore = useSoftwareStore()
@@ -60,7 +60,7 @@ import { useSoftwareStore } from '../../../../../stores/software'
import { useReverseManageStore } from '../../../../../stores/reverseManage'
import { BookRepalceDataType, OperateBookType } from '../../../../../define/enum/bookEnum'
import FindReplaceRound from '../../Icon/FindReplaceRound.vue'
import DatatableHeaderAfterGptSelectAndReplace from '../Components/DatatableHeaderAfterGptSelectAndReplace.vue'
import DatatableHeaderAfterGptSelectAndReplace from '../MJReverse/DatatableHeaderAfterGptSelectAndReplace.vue'
let message = useMessage()
let dialog = useDialog()
let softwareStore = useSoftwareStore()
@@ -73,7 +73,7 @@ import { useReverseManageStore } from '../../../../../stores/reverseManage'
import ImportWordAndSrt from '../Components/ImportWord/ImportWordAndSrt.vue'
import CharacterAnalyze from '../Components/PresetLibrary/CharacterAnalyze.vue'
import PromptSetting from '../../Original/Components/PromptSetting.vue'
import MonitorStatus from '../Components/MonitorStatus.vue'
import MonitorStatus from '../MJReverse/MonitorStatus.vue'
import {
BookBackTaskType,
BookImageCategory,
@@ -20,8 +20,8 @@
<script setup>
import { ref, h, onMounted, watch } from 'vue'
import { NDataTable, useMessage } from 'naive-ui'
import DatatableHeaderAfterGpt from '../Components/DatatableHeaderAfterGpt.vue'
import DatatableAfterGpt from '../Components/DatatableAfterGpt.vue'
import DatatableHeaderAfterGpt from '../MJReverse/DatatableHeaderAfterGpt.vue'
import DatatableAfterGpt from '../MJReverse/DatatableAfterGpt.vue'
import ODatatableHeaderCharacterAndScene from './ODataTableHeaderCharacterAndScene.vue'
import ODatatableCharacterAndScene from './ODataTableCharacterAndScene.vue'
import ODataTableGptPrompt from './ODataTableGptPrompt.vue'
@@ -29,8 +29,8 @@ import ODataTableHeaderGptPrompt from './ODataTableHeaderGptPrompt.vue'
import ODataTableHeaderPrompt from './ODatatableHeaderPrompt.vue'
import ODatatablePrompt from './ODatatablePrompt.vue'
import ODataTableAction from './ODataTableAction.vue'
import DatatableHeaderGenerateImage from '../Components/DatatableHeaderGenerateImage.vue'
import DatatableGenerateImage from '../Components/DatatableGenerateImage.vue'
import DatatableHeaderGenerateImage from '../MJReverse/DatatableHeaderGenerateImage.vue'
import DatatableGenerateImage from '../MJReverse/DatatableGenerateImage.vue'
import { useReverseManageStore } from '../../../../../stores/reverseManage'
import { useSoftwareStore } from '../../../../../stores/software'
let reverseManageStore = useReverseManageStore()
@@ -171,7 +171,6 @@ async function ManageAccount(remote = null) {
settingStore.actionRemoteMJ = remote
}
// 弹窗,管理不想要的值
let dW = 800
let dH = 600
dialog.create({
@@ -509,7 +509,6 @@ async function openGptBuyUrl() {
* 添加多个账号,这样可以同时跑多个账号
*/
async function AddMultiMjAccount() {
// 弹窗,管理不想要的值
let dW = window.innerWidth * 0.9
let dH = window.innerHeight * 0.9
dialog.create({
@@ -468,7 +468,6 @@ export default defineComponent({
}
// 开始处理
// 弹窗,管理不想要的值
dp = dialog.create({
name: 'progressDialog',
showIcon: false,
+30 -4
View File
@@ -59,13 +59,38 @@ export const useReverseManageStore = defineStore('reverseManage', {
}
},
actions: {
//#region 更新小说批次任务数据
/** 更新小说批次任务数据 */
UpdatedBookTaskData(bookTaskId: string | string[], data: Book.SelectBookTask) {
if (Array.isArray(bookTaskId)) {
bookTaskId.forEach(item => {
let findIndex = this.bookTaskData.findIndex((t: Book.SelectBookTask) => t.id === item)
if (findIndex != -1) {
for (let key in data) {
this.bookTaskData[findIndex][key] = data[key]
}
}
})
} else {
let findIndex = this.bookTaskData.findIndex((t: Book.SelectBookTask) => t.id === bookTaskId)
if (findIndex != -1) {
for (let key in data) {
this.bookTaskData[findIndex][key] = data[key]
}
}
}
},
//#endregion
/**
* 获取小说的数据
* @param {*} condition
*/
async GetBookDataFromDB(condition) {
try {
//@ts-ignore
let res = await window.book.GetBookData(condition)
if (res.code == 0) {
@@ -85,7 +110,7 @@ export const useReverseManageStore = defineStore('reverseManage', {
// 获取小说任务数据
async GetBookTaskDataFromDB(condition) {
try {
this.bookTaskData = []
//@ts-ignore
let res = await window.book.GetBookTaskData(condition)
@@ -203,7 +228,7 @@ export const useReverseManageStore = defineStore('reverseManage', {
//@ts-ignore
save_res = await window.book.AddOrModifyBook({ ...book })
}
if (save_res.code == 0) {
throw new Error(save_res.message)
}
@@ -238,7 +263,8 @@ export const useReverseManageStore = defineStore('reverseManage', {
return {
...item,
outImagePath: item.outImagePath ? item.outImagePath + '?t=' + new Date().getTime() : undefined,
subImagePath: item.subImagePath ? item.subImagePath.map(item => item + '?t=' + new Date().getTime()) : []
subImagePath: item.subImagePath ? item.subImagePath.map(item => item + '?t=' + new Date().getTime()) : [],
oldImage: item.oldImage ? item.oldImage + '?t=' + new Date().getTime() : undefined
}
})
// 这边开始修改数据,