LaiTool V3.1.2

This commit is contained in:
lq1405 2024-09-14 09:56:10 +08:00
parent 918d06e990
commit 8500fd3446
27 changed files with 711 additions and 252 deletions

4
package-lock.json generated
View File

@ -1,12 +1,12 @@
{ {
"name": "laitool", "name": "laitool",
"version": "3.1.1", "version": "3.1.2",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "laitool", "name": "laitool",
"version": "3.1.1", "version": "3.1.2",
"hasInstallScript": true, "hasInstallScript": true,
"dependencies": { "dependencies": {
"@alicloud/alimt20181012": "^1.2.0", "@alicloud/alimt20181012": "^1.2.0",

View File

@ -1,6 +1,6 @@
{ {
"name": "laitool", "name": "laitool",
"version": "3.1.1", "version": "3.1.2",
"description": "An AI tool for image processing, video processing, and other functions.", "description": "An AI tool for image processing, video processing, and other functions.",
"main": "./out/main/index.js", "main": "./out/main/index.js",
"author": "laitool.cn", "author": "laitool.cn",

Binary file not shown.

View File

@ -67,7 +67,7 @@ export class BookTaskDetailService extends BaseRealmService {
videoPath: JoinPath(define.project_path, item.videoPath), videoPath: JoinPath(define.project_path, item.videoPath),
audioPath: JoinPath(define.project_path, item.audioPath), audioPath: JoinPath(define.project_path, item.audioPath),
oldImage: JoinPath(define.project_path, item.oldImage), oldImage: JoinPath(define.project_path, item.oldImage),
outImagePath: JoinPath(define.project_path, item.outImagePath), outImagePath: JoinPath(define.project_path, item.outImagePath) ,
subImagePath: (item.subImagePath as string[])?.map((subImage) => { subImagePath: (item.subImagePath as string[])?.map((subImage) => {
return JoinPath(define.project_path, subImage) return JoinPath(define.project_path, subImage)
}), }),

View File

@ -253,9 +253,20 @@ export const DEFINE_STRING = {
REPLACE_BOOK_DATA: "REPLACE_BOOK_DATA", REPLACE_BOOK_DATA: "REPLACE_BOOK_DATA",
SAVE_COPYWRITING: 'SAVE_COPYWRITING', SAVE_COPYWRITING: 'SAVE_COPYWRITING',
//#region 原创推理提示词 //#region 提示词
/**
*
*/
ORIGINAL_GPT_PROMPT: "ORIGINAL_GPT_PROMPT", ORIGINAL_GPT_PROMPT: "ORIGINAL_GPT_PROMPT",
/**
*
*/
ORIGINAL_GPT_PROMPT_RETURN: "ORIGINAL_GPT_PROMPT_RETURN", ORIGINAL_GPT_PROMPT_RETURN: "ORIGINAL_GPT_PROMPT_RETURN",
/**
*
*/
IMPORT_GPT_PROMPT: "IMPORT_GPT_PROMPT",
//#endregion //#endregion
//#region 生图返回相关 //#region 生图返回相关
@ -287,6 +298,15 @@ export const DEFINE_STRING = {
//#endregion //#endregion
//#region 图片相关
/**
* MJ的消息ID
*/
GET_IMAGE_URL_AND_DOWNLOAD : "GET_IMAGE_URL_AND_DOWNLOAD",
//#endregion
COMPUTE_STORYBOARD: 'COMPUTE_STORYBOARD', COMPUTE_STORYBOARD: 'COMPUTE_STORYBOARD',
GET_FRAME: 'GET_FRAME', GET_FRAME: 'GET_FRAME',

View File

@ -18,7 +18,11 @@ export enum MJImageType {
FLUX_API = 'flux-api', FLUX_API = 'flux-api',
// flxu-forge // flxu-forge
FLUX_FORGE = 'flux-forge' FLUX_FORGE = 'flux-forge',
// 导入
IMPORT = 'import',
} }
export enum MJRobotType { export enum MJRobotType {

View File

@ -149,6 +149,11 @@ export function BookIpc() {
await bookPrompt.OriginalGetPrompt(id, operateBookType, coverData) await bookPrompt.OriginalGetPrompt(id, operateBookType, coverData)
) )
ipcMain.handle(
DEFINE_STRING.BOOK.IMPORT_GPT_PROMPT,
async (event, bookTaskId: string, txtPath: string) => await bookPrompt.ImportGPTPrompt(bookTaskId, txtPath)
)
//#endregion //#endregion
//#region 文案相关 //#region 文案相关
@ -252,6 +257,12 @@ export function BookIpc() {
async (event, id, operateBookType) => await bookImage.ResetGenerateImage(id, operateBookType) async (event, id, operateBookType) => await bookImage.ResetGenerateImage(id, operateBookType)
) )
ipcMain.handle(
DEFINE_STRING.BOOK.GET_IMAGE_URL_AND_DOWNLOAD,
async (event, id: string, operateBookType: OperateBookType, coverData: boolean) =>
await bookImage.GetImageUrlAndDownload(id, operateBookType, coverData)
)
//#endregion //#endregion

View File

@ -28,18 +28,37 @@ export class BookGeneral {
this.bookServiceBasic.transaction((realm) => { this.bookServiceBasic.transaction((realm) => {
for (let i = 0; i < bookTaskDetail.length; i++) { for (let i = 0; i < bookTaskDetail.length; i++) {
let element = bookTaskDetail[i]; let element = bookTaskDetail[i];
// 这边新增判断是不是有subValue有点话替换subValue中的数据
let afterGpt = element.afterGpt let afterGpt = element.afterGpt
// 判断是否存在before的数据 let subValue = element.subValue
if (!afterGpt.includes(replaceData.before)) { if (subValue && subValue.length > 0) {
continue subValue = subValue.map(item => {
return {
...item,
srt_value: item.srt_value.replaceAll(replaceData.before, replaceData.after)
}
})
let btd = realm.objectForPrimaryKey('BookTaskDetail', element.id)
btd.subValue = JSON.stringify(subValue);
result.push({
bookTaskDetailId: element.id,
newData: subValue,
type: 'subValue'
} as Book.ReplaceDataRes)
// 保存数据
} else {
// 判断是否存在before的数据
if (!afterGpt.includes(replaceData.before)) {
continue
}
let newAfterGpt = afterGpt.replaceAll(replaceData.before, replaceData.after);
let btd = realm.objectForPrimaryKey('BookTaskDetail', element.id)
btd.afterGpt = newAfterGpt
result.push({
bookTaskDetailId: element.id,
newData: newAfterGpt
} as Book.ReplaceDataRes)
} }
let newAfterGpt = afterGpt.replaceAll(replaceData.before, replaceData.after);
let btd = realm.objectForPrimaryKey('BookTaskDetail', element.id)
btd.afterGpt = newAfterGpt
result.push({
bookTaskDetailId: element.id,
newData: newAfterGpt
} as Book.ReplaceDataRes)
} }
}) })
return result return result

View File

@ -1,4 +1,4 @@
import { BookImageCategory, BookType, OperateBookType } from "../../../define/enum/bookEnum"; import { BookImageCategory, BookType, MJAction, OperateBookType } from "../../../define/enum/bookEnum";
import { GeneralResponse } from "../../../model/generalResponse"; import { GeneralResponse } from "../../../model/generalResponse";
import { errorMessage, successMessage } from "../../Public/generalTools"; import { errorMessage, successMessage } from "../../Public/generalTools";
import { Book } from "../../../model/book"; import { Book } from "../../../model/book";
@ -15,6 +15,8 @@ import { BookServiceBasic } from "../ServiceBasic/bookServiceBasic";
import fs from 'fs' import fs from 'fs'
import util from 'util' import util from 'util'
import { exec } from 'child_process' import { exec } from 'child_process'
import { MJImageType } from "../../../define/enum/mjEnum";
import MJApi from '../MJ/mjApi'
const execAsync = util.promisify(exec); const execAsync = util.promisify(exec);
/** /**
@ -24,9 +26,11 @@ export class BookImage {
bookServiceBasic: BookServiceBasic bookServiceBasic: BookServiceBasic
tools: Tools; tools: Tools;
mjOpt: MJOpt; mjOpt: MJOpt;
mjApi: MJApi
constructor() { constructor() {
this.tools = new Tools() this.tools = new Tools()
this.mjOpt = new MJOpt() this.mjOpt = new MJOpt()
this.mjApi = new MJApi()
this.bookServiceBasic = new BookServiceBasic() this.bookServiceBasic = new BookServiceBasic()
} }
@ -45,7 +49,7 @@ export class BookImage {
* @param id * @param id
* @param operateBookType * @param operateBookType
*/ */
async ResetGenerateImage(id: string, operateBookType: OperateBookType, coverData: boolean): Promise<GeneralResponse.SuccessItem | GeneralResponse.ErrorItem> { async ResetGenerateImage(id: string, operateBookType: OperateBookType): Promise<GeneralResponse.SuccessItem | GeneralResponse.ErrorItem> {
try { try {
let bookTaskDetails = undefined as Book.SelectBookTaskDetail[] let bookTaskDetails = undefined as Book.SelectBookTaskDetail[]
let bookTask = undefined as Book.SelectBookTask let bookTask = undefined as Book.SelectBookTask
@ -58,9 +62,8 @@ export class BookImage {
throw new Error('不支持的操作类型,请检查') throw new Error('不支持的操作类型,请检查')
} }
//这边过滤被锁定的数据 //这边过滤被锁定的数据
if (!coverData) { bookTaskDetails = bookTaskDetails.filter(item => !item.imageLock)
bookTaskDetails = bookTaskDetails.filter(item => !item.imageLock)
}
if (bookTaskDetails.length <= 0) { if (bookTaskDetails.length <= 0) {
throw new Error('没有要删除的分镜数据,请检查') throw new Error('没有要删除的分镜数据,请检查')
@ -69,15 +72,15 @@ export class BookImage {
// 开始删除数据,要删除图片数据和出图的信息 // 开始删除数据,要删除图片数据和出图的信息
for (let i = 0; i < bookTaskDetails.length; i++) { for (let i = 0; i < bookTaskDetails.length; i++) {
const element = bookTaskDetails[i]; const element = bookTaskDetails[i];
if (bookTask.imageCategory == BookImageCategory.MJ) { await this.bookServiceBasic.DeleteBoookTaskDetailGenerateImage(element.id);
await this.bookServiceBasic.DeleteBoookTaskDetailGenerateImage(element.id); // if (bookTask.imageCategory == BookImageCategory.MJ) {
} else if (bookTask.imageCategory == BookImageCategory.D3) { // } else if (bookTask.imageCategory == BookImageCategory.D3) {
throw new Error('暂时不支持D3生成的图片删除') // throw new Error('暂时不支持D3生成的图片删除')
} else if (bookTask.imageCategory == BookImageCategory.SD) { // } else if (bookTask.imageCategory == BookImageCategory.SD) {
await this.bookServiceBasic.DeleteBoookTaskDetailGenerateImage(element.id); // await this.bookServiceBasic.DeleteBoookTaskDetailGenerateImage(element.id);
} else { // } else {
throw new Error('未知的小说类型,请检查') // throw new Error('未知的小说类型,请检查')
} // }
// 上面的信息重置完毕之后,开始删除文件信息 // 上面的信息重置完毕之后,开始删除文件信息
let outImage = element.outImagePath; let outImage = element.outImagePath;
if (await CheckFileOrDirExist(outImage)) { if (await CheckFileOrDirExist(outImage)) {
@ -322,7 +325,7 @@ export class BookImage {
* @param imageUrl * @param imageUrl
* @returns * @returns
*/ */
async DownloadImageUrlAndSplit(bookTaskDetailId: string, imageUrl: string,) { async DownloadImageUrlAndSplit(bookTaskDetailId: string, imageUrl: string,): Promise<GeneralResponse.ErrorItem | GeneralResponse.SuccessItem> {
try { try {
let bookTaskDetail = await this.bookServiceBasic.GetBookTaskDetailDataById(bookTaskDetailId) let bookTaskDetail = await this.bookServiceBasic.GetBookTaskDetailDataById(bookTaskDetailId)
if (bookTaskDetail == null) { if (bookTaskDetail == null) {
@ -348,19 +351,33 @@ export class BookImage {
await this.tools.downloadFileUrl(imageUrl, imagePath) await this.tools.downloadFileUrl(imageUrl, imagePath)
} }
// 进行图片裁剪 let imageCategory = bookTask.imageCategory;
let imageRes = await ImageSplit(imagePath, bookTaskDetail.name, path.join(book.bookFolderPath, 'data\\MJOriginalImage')); let out_file = undefined
if (imageRes && imageRes.length < 4) { let imageRes = []
throw new Error("图片裁剪失败") if (imageCategory == BookImageCategory.MJ) {
// 只有MJ需要裁剪其他的不需要
// 进行图片裁剪
imageRes = await ImageSplit(imagePath, bookTaskDetail.name, path.join(book.bookFolderPath, 'data\\MJOriginalImage'));
if (imageRes && imageRes.length < 4) {
throw new Error("图片裁剪失败")
}
// 修改数据
// 修改数据库数据,将图片保存到对应的文件夹中
let firstImage = imageRes[0];
if (book.type == BookType.ORIGINAL) {
await CopyFileOrFolder(firstImage, path.join(book.bookFolderPath, `tmp\\input\\${bookTaskDetail.name}.png`));
}
out_file = path.join(bookTask.imageFolder, `${bookTaskDetail.name}.png`)
await CopyFileOrFolder(firstImage, out_file);
} else {
// 其他的导入,每次只能导入一张图
out_file = path.join(bookTask.imageFolder, `${bookTaskDetail.name}.png`);
if (book.type == BookType.ORIGINAL) {
await CopyFileOrFolder(imagePath, path.join(book.bookFolderPath, `tmp\\input\\${bookTaskDetail.name}.png`));
}
await CopyFileOrFolder(imagePath, out_file);
imageRes = [out_file]
} }
// 修改数据
// 修改数据库数据,将图片保存到对应的文件夹中
let firstImage = imageRes[0];
if (book.type == BookType.ORIGINAL) {
await CopyFileOrFolder(firstImage, path.join(book.bookFolderPath, `tmp\\input\\${bookTaskDetail.name}.png`));
}
let out_file = path.join(bookTask.imageFolder, `${bookTaskDetail.name}.png`)
await CopyFileOrFolder(firstImage, out_file);
// 修改分镜的数据 // 修改分镜的数据
await this.bookServiceBasic.UpdateBookTaskDetail(bookTaskDetailId, { await this.bookServiceBasic.UpdateBookTaskDetail(bookTaskDetailId, {
@ -368,9 +385,20 @@ export class BookImage {
subImagePath: imageRes.map((item) => path.relative(define.project_path, item)) subImagePath: imageRes.map((item) => path.relative(define.project_path, item))
}) })
let mjMessage = {
status: 'success',
progress: 100,
category: MJImageType.IMPORT,
messageId: '',
action: MJAction.IMAGINE,
};
await this.bookServiceBasic.UpdateBookTaskDetailMjMessage(bookTaskDetailId, mjMessage)
return successMessage({ return successMessage({
outImagePath: out_file + '?time=' + new Date().getTime(), outImagePath: out_file + '?time=' + new Date().getTime(),
subImagePath: imageRes.map((item) => item + '?time=' + new Date().getTime()) subImagePath: imageRes.map((item) => item + '?time=' + new Date().getTime()),
mjMessage: mjMessage
}, "下载指定的图片地址并且分割成功", "BookImage_DownloadImageUrlAndSplit") }, "下载指定的图片地址并且分割成功", "BookImage_DownloadImageUrlAndSplit")
} catch (error) { } catch (error) {
return { return {
@ -379,4 +407,74 @@ export class BookImage {
} }
} }
} }
/**
*
* @param id
* @param operateBookType
* @param coverData
*/
async GetImageUrlAndDownload(id: string, operateBookType: OperateBookType, coverData: boolean): Promise<GeneralResponse.SuccessItem | GeneralResponse.ErrorItem> {
try {
console.log("GetImageUrlAndDownload", id, operateBookType, coverData)
let bookTaskDetail = undefined as Book.SelectBookTaskDetail[]
let bookTask = undefined as Book.SelectBookTask
if (operateBookType == OperateBookType.BOOKTASK) {
bookTask = await this.bookServiceBasic.GetBookTaskDataById(id);
bookTaskDetail = await this.bookServiceBasic.GetBookTaskDetailData({
bookTaskId: bookTask.id
})
// 这边过滤出图成功的数据
if (!coverData) {
bookTaskDetail = bookTaskDetail.filter((item) => !item.outImagePath)
}
} else if (operateBookType == OperateBookType.BOOKTASKDETAIL) {
let currentBookTaskDetail = await this.bookServiceBasic.GetBookTaskDetailDataById(id);
bookTask = await this.bookServiceBasic.GetBookTaskDataById(currentBookTaskDetail.bookTaskId);
bookTaskDetail = [currentBookTaskDetail]
} else {
throw new Error('不支持的操作类型')
}
// 这边再做个详细的筛选
if (bookTaskDetail.length < 0) {
throw new Error("没有找到需要采集的数据")
}
if (bookTask.imageCategory != BookImageCategory.MJ) {
throw new Error("只有MJ模式下才能使用这个功能")
}
let result = []
for (let i = 0; i < bookTaskDetail.length; i++) {
const element = bookTaskDetail[i];
if (!element.mjMessage) continue;
if (element.mjMessage.status == 'error') continue;
if (isEmpty(element.mjMessage.messageId)) continue;
// 这边开始采集
let res = await this.mjApi.GetMJAPITaskById(element.mjMessage.messageId, undefined);
if (isEmpty(res.imagePath)) {
throw new Error("获取图片地址链接为空")
}
// 开始下载
let dr = await this.DownloadImageUrlAndSplit(element.id, res.imagePath);
if (dr.code == 0) {
throw new Error(dr.message)
}
result.push({
id: element.id,
data: dr.data
})
}
if (result.length <= 0) {
throw new Error("没有找到需要采集的数据")
}
return successMessage(result, "获取图片链接并且下载成功", "BookImage_GetImageUrlAndDownload")
} catch (error) {
return errorMessage('获取图片链接并且下载失败,错误信息如下:' + error.message, 'BookImage_GetImageUrlAndDownload')
}
}
} }

View File

@ -7,6 +7,11 @@ import { BookServiceBasic } from "../ServiceBasic/bookServiceBasic";
import { GptService } from "../GPT/gpt"; import { GptService } from "../GPT/gpt";
import { ExecuteConcurrently } from "../../../define/Tools/common"; import { ExecuteConcurrently } from "../../../define/Tools/common";
import { DEFINE_STRING } from "../../../define/define_string"; 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';
export class BookPrompt { export class BookPrompt {
bookServiceBasic: BookServiceBasic bookServiceBasic: BookServiceBasic
@ -16,6 +21,74 @@ export class BookPrompt {
this.gptService = new GptService() this.gptService = new GptService()
} }
//#region 提示词通用
/**
*
* ID和文本路径来处理一些导入逻辑
*
* @param bookTaskId
* @param txtPath
*/
async ImportGPTPrompt(bookTaskId: string, txtPath: string): Promise<GeneralResponse.SuccessItem | GeneralResponse.ErrorItem> {
try {
console.log(bookTaskId, txtPath)
let bookTaskDetail = await this.bookServiceBasic.GetBookTaskDetailData({
bookTaskId: bookTaskId
})
if (isEmpty(txtPath)) {
throw new Error("传入的文件地址不能为空")
}
// 检查文件是不是存在
if (!await CheckFileOrDirExist(txtPath)) {
throw new Error("传入的文件地址对应的文件不存在");
}
async function processLineByLine() {
const fileStream = fs.createReadStream(path.normalize(txtPath));
const rl = readline.createInterface({
input: fileStream,
crlfDelay: Infinity
});
const lines = [];
for await (const line of rl) {
lines.push(line);
}
rl.close(); // 确保关闭 readline 流
return lines;
}
let result = []
// 这个就是txt文件里面所有的数据
let lines = await processLineByLine()
// 这边开始写入
for (let i = 0; i < bookTaskDetail.length; i++) {
const element = bookTaskDetail[i];
if (i >= lines.length) {
break
}
// 修改
this.bookServiceBasic.transaction((realm) => {
let btd = realm.objectForPrimaryKey("BookTaskDetail", element.id)
if (btd == null) {
throw new Error('未找到对应的分镜数据')
}
btd.gptPrompt = lines[i]
})
result.push({
id: element.id,
gptPrompt: lines[i]
})
}
return successMessage(result, "导入提示词数据成功", 'Book_ImportGPTPrompt');
} catch (error) {
return errorMessage("导入提示词失败,错误信息如下:" + error.message, "Book_ImportGPTPrompt")
}
}
//#endregion
//#region 反推的提示词相关 //#region 反推的提示词相关
/** /**
* MJ反推出来的数据GPT提示词中 * MJ反推出来的数据GPT提示词中

View File

@ -50,8 +50,8 @@ export class BookTask {
name: element.name, name: element.name,
bookId: element.bookId, bookId: element.bookId,
bookTaskId: newBookTask.id, bookTaskId: newBookTask.id,
videoPath: path.relative(define.project_path, element.videoPath), videoPath: element.videoPath ? path.relative(define.project_path, element.videoPath) : undefined,
oldImage: path.relative(define.project_path, element.oldImage), oldImage: element.oldImage ? path.relative(define.project_path, element.oldImage) : undefined,
adetailer: element.adetailer, adetailer: element.adetailer,
sdConifg: element.sdConifg, sdConifg: element.sdConifg,
createTime: new Date(), createTime: new Date(),
@ -130,7 +130,7 @@ export class BookTask {
generateVideoPath: undefined, generateVideoPath: undefined,
srtPath: bookTask.srtPath, srtPath: bookTask.srtPath,
audioPath: bookTask.audioPath, audioPath: bookTask.audioPath,
imageFolder: path.relative(define.project_path, imageFolder), imageFolder: imageFolder ? path.relative(define.project_path, imageFolder) : undefined,
status: BookTaskStatus.WAIT, status: BookTaskStatus.WAIT,
errorMsg: undefined, errorMsg: undefined,
updateTime: new Date(), updateTime: new Date(),
@ -278,16 +278,16 @@ export class BookTask {
throw new Error("没有找到对应的数小说任务,请检查数据") throw new Error("没有找到对应的数小说任务,请检查数据")
} }
// 获取所有的出图中最少的 // 获取所有的出图中最少的
let bookTaskDetail = (await this.bookServiceBasic.GetBookTaskData({ let bookTaskDetail = (await this.bookServiceBasic.GetBookTaskDetailData({
bookTaskId: bookTaskId bookTaskId: bookTaskId
})).bookTasks as Book.SelectBookTaskDetail[] })) as Book.SelectBookTaskDetail[]
if (bookTaskDetail == null || bookTaskDetail.length <= 0) { if (bookTaskDetail == null || bookTaskDetail.length <= 0) {
throw new Error("没有对应的小说分镜任务,请先添加分镜任务") throw new Error("没有对应的小说分镜任务,请先添加分镜任务")
} }
for (let i = 0; i < bookTaskDetail.length; i++) { for (let i = 0; i < bookTaskDetail.length; i++) {
const element = bookTaskDetail[i]; const element = bookTaskDetail[i];
if (isEmpty(element.subImagePath)) { if (!element.subImagePath) {
throw new Error("检测到图片没有出完,请先检查出图") throw new Error("检测到图片没有出完,请先检查出图")
} }
if (element.subImagePath == null || element.subImagePath.length <= 0) { if (element.subImagePath == null || element.subImagePath.length <= 0) {
@ -324,7 +324,7 @@ export class BookTask {
// 先处理文件夹的创建,包括小说任务的和小说任务分镜的 // 先处理文件夹的创建,包括小说任务的和小说任务分镜的
for (let i = 0; i < copyCount; i++) { for (let i = 0; i < copyCount; i++) {
let no = await this.bookServiceBasic.GetMaxBookTaskNo(sourceBookTask.bookId) let no = await this.bookServiceBasic.GetMaxBookTaskNo(sourceBookTask.bookId) + i
let name = 'output_0000' + no let name = 'output_0000' + no
let imageFolder = path.join(define.project_path, `${sourceBookTask.bookId}/tmp/${name}`) let imageFolder = path.join(define.project_path, `${sourceBookTask.bookId}/tmp/${name}`)
await CheckFolderExistsOrCreate(imageFolder) await CheckFolderExistsOrCreate(imageFolder)
@ -408,35 +408,35 @@ export class BookTask {
} }
} }
let addOneBookTaskDetail = { let addOneBookTaskDetail = {} as Book.SelectBookTaskDetail
id: reverseId, addOneBookTaskDetail.id = reverseId;
no: element.no, addOneBookTaskDetail.no = element.no;
name: element.name, addOneBookTaskDetail.name = element.name;
bookId: sourceBookTask.bookId, addOneBookTaskDetail.bookId = sourceBookTask.bookId;
bookTaskId: addOneBookTask.id, addOneBookTaskDetail.bookTaskId = addOneBookTask.id;
videoPath: path.relative(define.project_path, element.videoPath), addOneBookTaskDetail.videoPath = element.videoPath ? path.relative(define.project_path, element.videoPath) : undefined;
word: element.word, addOneBookTaskDetail.word = element.word;
oldImage: path.relative(define.project_path, element.oldImage), addOneBookTaskDetail.oldImage = element.oldImage ? path.relative(define.project_path, element.oldImage) : undefined;
afterGpt: element.afterGpt, addOneBookTaskDetail.afterGpt = element.afterGpt;
startTime: element.startTime, addOneBookTaskDetail.startTime = element.startTime;
endTime: element.endTime, addOneBookTaskDetail.endTime = element.endTime;
timeLimit: element.timeLimit, addOneBookTaskDetail.timeLimit = element.timeLimit;
subValue: element.subValue && element.subValue.length > 0 ? JSON.stringify(element.subValue) : undefined, addOneBookTaskDetail.subValue = (element.subValue && element.subValue.length > 0 ? JSON.stringify(element.subValue) : undefined) as string;
characterTags: element.characterTags && element.characterTags.length > 0 ? cloneDeep(element.characterTags) : [], addOneBookTaskDetail.characterTags = element.characterTags && element.characterTags.length > 0 ? cloneDeep(element.characterTags) : [];
gptPrompt: element.gptPrompt, addOneBookTaskDetail.gptPrompt = element.gptPrompt;
outImagePath: path.relative(define.project_path, outImagePath), addOneBookTaskDetail.outImagePath = outImagePath ? path.relative(define.project_path, outImagePath) : undefined;
subImagePath: subImagePath || [], addOneBookTaskDetail.subImagePath = subImagePath || [];
prompt: element.prompt, addOneBookTaskDetail.prompt = element.prompt;
adetailer: element.adetailer, addOneBookTaskDetail.adetailer = element.adetailer;
sdConifg: sdConifg, addOneBookTaskDetail.sdConifg = sdConifg;
createTime: new Date(), addOneBookTaskDetail.createTime = new Date();
updateTime: new Date(), addOneBookTaskDetail.updateTime = new Date();
audioPath: element.audioPath, addOneBookTaskDetail.audioPath = element.audioPath;
subtitlePosition: element.subtitlePosition, addOneBookTaskDetail.subtitlePosition = element.subtitlePosition;
status: element.status, addOneBookTaskDetail.status = element.status;
reversePrompt: reverseMessage, addOneBookTaskDetail.reversePrompt = reverseMessage;
imageLock: element.imageLock addOneBookTaskDetail.imageLock = element.imageLock
} as Book.SelectBookTaskDetail
addBookTaskDetail.push(addOneBookTaskDetail) addBookTaskDetail.push(addOneBookTaskDetail)
} }
} }

View File

@ -775,7 +775,7 @@ export class MJOpt {
messageId: undefined, messageId: undefined,
id: task.bookTaskDetailId, id: task.bookTaskDetailId,
progress: 0, progress: 0,
message: error.toString(), message: errorMsg,
status: "error" status: "error"
} }
}, task.messageName) }, task.messageName)
@ -788,7 +788,7 @@ export class MJOpt {
messageId: "", messageId: "",
action: MJAction.IMAGINE, action: MJAction.IMAGINE,
status: "error", status: "error",
message: error.toString() message: errorMsg
}) })
return errorMessage(errorMsg, "MJReverse_MJImage2Text") return errorMessage(errorMsg, "MJReverse_MJImage2Text")
} }

View File

@ -9,6 +9,7 @@ import { MJSetting } from "../../../model/Setting/mjSetting"
import { GPT } from "../../Public/GPT" import { GPT } from "../../Public/GPT"
import { MJ } from "../../../model/mj" import { MJ } from "../../../model/mj"
import { LaiAPIType } from "../../../define/enum/softwareEnum" import { LaiAPIType } from "../../../define/enum/softwareEnum"
import { isEmpty } from "lodash"
/** /**
* MJ的API类 * MJ的API类
@ -96,11 +97,13 @@ class MJApi {
let _bookBackTaskListService = await BookBackTaskListService.getInstance() let _bookBackTaskListService = await BookBackTaskListService.getInstance()
// 失败 // 失败
if (code == 0) { if (code == 0) {
_bookBackTaskListService.UpdateTaskStatus({ if (!isEmpty(backTaskId)) {
id: backTaskId, _bookBackTaskListService.UpdateTaskStatus({
status: BookBackTaskStatus.FAIL, id: backTaskId,
errorMessage: res.data.message status: BookBackTaskStatus.FAIL,
}) errorMessage: res.data.message
})
}
} }
let resObj = { let resObj = {
type: MJRespoonseType.UPDATED, type: MJRespoonseType.UPDATED,

12
src/model/book.d.ts vendored
View File

@ -88,9 +88,9 @@ declare namespace Book {
// 字幕相关 // 字幕相关
type Subtitle = { type Subtitle = {
startTime: number start_time: number
endTime: number end_time: number
srtValue: string srt_value: string
id: string id: string
} }
@ -152,7 +152,7 @@ declare namespace Book {
startTime?: number // 开始时间 startTime?: number // 开始时间
endTime?: number // 结束时间 endTime?: number // 结束时间
timeLimit?: string // 事件实现0 -- 3000 timeLimit?: string // 事件实现0 -- 3000
subValue?: string // 包含的字幕数据 subValue?: Subtitle[] | string // 包含的字幕数据
characterTags?: string[] // 角色标签 characterTags?: string[] // 角色标签
sceneTags?: string[] // 场景标签 sceneTags?: string[] // 场景标签
gptPrompt?: string // GPT提示词 gptPrompt?: string // GPT提示词
@ -279,8 +279,8 @@ declare namespace Book {
type ReplaceDataRes = { type ReplaceDataRes = {
bookTaskDetailId: string, bookTaskDetailId: string,
newData: string, newData: string | Subtitle[],
type?: 'reversePrompt' | 'gptPrompt' | 'afterGpt' | 'prompt', type?: 'reversePrompt' | 'gptPrompt' | 'afterGpt' | 'prompt' | 'subValue',
reversePromptType?: 'prompt' | 'promptCN' reversePromptType?: 'prompt' | 'promptCN'
reversePromptId?: string reversePromptId?: string
} }

View File

@ -158,6 +158,8 @@ const book = {
OriginalGetPrompt: async (id: string, operateBookType: OperateBookType, coverData: boolean) => OriginalGetPrompt: async (id: string, operateBookType: OperateBookType, coverData: boolean) =>
await ipcRenderer.invoke(DEFINE_STRING.BOOK.ORIGINAL_GPT_PROMPT, id, operateBookType, coverData), await ipcRenderer.invoke(DEFINE_STRING.BOOK.ORIGINAL_GPT_PROMPT, id, operateBookType, coverData),
// 导入提示词
ImportGPTPrompt: async (bookTaskId: string, txtPath: string) => await ipcRenderer.invoke(DEFINE_STRING.BOOK.IMPORT_GPT_PROMPT, bookTaskId, txtPath),
//#endregion //#endregion
//#region 图片相关 //#region 图片相关
@ -166,6 +168,10 @@ const book = {
ResetGenerateImage: async (id: string, operateBookType: OperateBookType, coverData: boolean) => ResetGenerateImage: async (id: string, operateBookType: OperateBookType, coverData: boolean) =>
await ipcRenderer.invoke(DEFINE_STRING.BOOK.REMOVE_GENERATE_IMAGE, id, operateBookType, coverData), await ipcRenderer.invoke(DEFINE_STRING.BOOK.REMOVE_GENERATE_IMAGE, id, operateBookType, coverData),
// 获取图片的连接,然后下载图片
GetImageUrlAndDownload: async (id: string, operateBookType: OperateBookType, coverData: boolean) =>
await ipcRenderer.invoke(DEFINE_STRING.BOOK.GET_IMAGE_URL_AND_DOWNLOAD, id, operateBookType, coverData),
//#endregion //#endregion
//#region 一键反推的单个任务 //#region 一键反推的单个任务

View File

@ -1,11 +1,11 @@
<template> <template>
<div style="height: 135px; width : 100%;overflow: auto; display: flex; margin: auto"> <div style="height: 135px; width: 100%; overflow: auto; display: flex; margin: auto">
<div v-if="data.subValue && data.subValue.length != 0" style="width : 100%"> <div v-if="data.subValue && data.subValue.length != 0" style="width: 100%">
<div v-for="(item, index) in data.subValue" style="width : 100%"> <div v-for="(item, index) in data.subValue" style="width: 100%">
<n-input <n-input
v-model:value="item.srt_value" v-model:value="item.srt_value"
type="textarea" type="textarea"
style="width : 100%" style="width: 100%"
size="tiny" size="tiny"
:autosize="{ minRows: 1, maxRows: 5 }" :autosize="{ minRows: 1, maxRows: 5 }"
@input="InputDebounced" @input="InputDebounced"
@ -26,7 +26,7 @@
</template> </template>
<script> <script>
import { ref, onMounted, defineComponent, onUnmounted, toRaw, watch, computed } from 'vue' import { ref, onMounted, defineComponent } from 'vue'
import { useMessage, NInput } from 'naive-ui' import { useMessage, NInput } from 'naive-ui'
import { debounce } from 'lodash' import { debounce } from 'lodash'
@ -35,6 +35,7 @@ export default defineComponent({
props: ['initData', 'index'], props: ['initData', 'index'],
setup(props) { setup(props) {
let data = ref(props.initData) let data = ref(props.initData)
console.log('data', props.initData, props.index)
onMounted(async () => {}) onMounted(async () => {})
let InputDebounced = debounce(handleInput, 1000) let InputDebounced = debounce(handleInput, 1000)

View File

@ -75,6 +75,7 @@
type="info" type="info"
style="font-size: 24px; position: absolute; left: 8px; top: -5px" style="font-size: 24px; position: absolute; left: 8px; top: -5px"
@click="DownloadImage" @click="DownloadImage"
v-if="reverseManageStore.selectBookTask.imageCategory == 'mj'"
> >
<n-icon> <n-icon>
<Download /> <Download />
@ -116,7 +117,7 @@
</template> </template>
<script setup> <script setup>
import { ref, onMounted, onUnmounted, watch } from 'vue' import { ref, onMounted, toRaw, watch } from 'vue'
import { import {
NImage, NImage,
useDialog, useDialog,
@ -132,6 +133,8 @@ import { LockClosed, LockOpen, Download } from '@vicons/ionicons5'
import { useSoftwareStore } from '../../../../../stores/software' import { useSoftwareStore } from '../../../../../stores/software'
import { useReverseManageStore } from '../../../../../stores/reverseManage' import { useReverseManageStore } from '../../../../../stores/reverseManage'
import { useImageStore } from '../../../../../stores/image' import { useImageStore } from '../../../../../stores/image'
import { BookImageCategory, OperateBookType } from '../../../../../define/enum/bookEnum'
import { isEmpty } from 'lodash'
let props = defineProps({ let props = defineProps({
initData: undefined, initData: undefined,
index: undefined index: undefined
@ -268,53 +271,55 @@ async function ModifyLock() {
*/ */
async function DownloadImage() { async function DownloadImage() {
// mj_message // mj_message
dialog.warning({ let da = dialog.warning({
title: '下载图片警告', title: '下载图片警告',
content: '单个图片的下载,只支持 MJ API 生图,当前有图片的话会被覆盖掉,是否继续?', content:
'单个图片的下载,只支持 MJ API 生图MJ图片有效时间大约为24小时当前有图片的话会被覆盖掉是否继续',
positiveText: '继续', positiveText: '继续',
negativeText: '取消', negativeText: '取消',
onPositiveClick: async () => { onPositiveClick: async () => {
if (data.value.mj_message) { da?.destroy()
debugger if (reverseManageStore.selectBookTask.imageCategory != BookImageCategory.MJ) {
// message.error('当前图片不是MJ图片不能下载')
// success error
if (data.value.mj_message.status == 'error') {
message.error('失败状态不能采集图片')
return
}
if (isEmpty(data.value.mj_message.message_id)) {
message.error('没有消息ID不能采集图片')
return
}
window.mj.GetGeneratedMJImageAndSplit(JSON.stringify([toRaw(data.value)]), (value) => {
if (value.code == 0) {
message.error(value.message)
return
}
//
for (let i = 0; i < value.data.length; i++) {
const element = value.data[i]
//
data.value.outImagePath =
'file://' +
element.outImagePath.replaceAll('\\', '/') +
'?time=' +
new Date().getTime()
data.value.subImagePath = element.subImagePath.map(
(item) => 'file://' + item.replaceAll('\\', '/') + '?time=' + new Date().getTime()
)
data.value.mj_message.image_click = element.result
data.value.mj_message.image_path = element.image_path
data.value.mj_message.progress = 100
data.value.mj_message.status = 'success'
}
})
} else if (data.value.mjMessage) {
message.error('暂时不支持')
return return
}
// success error
if (data.value.mjMessage.status == 'error') {
message.error('失败状态不能采集图片')
return
}
if (isEmpty(data.value.mjMessage.messageId)) {
message.error('没有消息ID不能采集图片')
return
}
//
softwareStore.spin.spinning = true
softwareStore.spin.tip = '正在下载图片,请稍后...'
let res = await window.book.GetImageUrlAndDownload(
data.value.id,
OperateBookType.BOOKTASKDETAIL,
false
)
softwareStore.spin.spinning = false
if (res.code == 1) {
//
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].outImagePath =
element.data.outImagePath
reverseManageStore.selectBookTaskDetail[findIndex].subImagePath =
element.data.subImagePath
reverseManageStore.selectBookTaskDetail[findIndex].mjMessage = element.data.mjMessage
}
}
message.success(res.message)
} else { } else {
message.error('没有生图数据') message.error(res.message)
} }
} }
}) })

View File

@ -65,7 +65,11 @@ async function ReplaceAfterGpt() {
(x) => x.id == element.bookTaskDetailId (x) => x.id == element.bookTaskDetailId
) )
if (index != -1) { if (index != -1) {
reverseManageStore.selectBookTaskDetail[index].afterGpt = element.newData if (element.type == 'subValue') {
reverseManageStore.selectBookTaskDetail[index].subValue = element.newData
} else {
reverseManageStore.selectBookTaskDetail[index].afterGpt = element.newData
}
} }
} }
} else if (props.type == BookRepalceDataType.GPT_PROMPT) { } else if (props.type == BookRepalceDataType.GPT_PROMPT) {

View File

@ -1,5 +1,5 @@
<template> <template>
<div style="display: flex"> <div style="display: flex; align-items: center">
<span>出图</span> <span>出图</span>
<n-select <n-select
style="width: 100px; margin-left: 5px" style="width: 100px; margin-left: 5px"
@ -9,20 +9,29 @@
v-model:value="reverseManageStore.selectBookTask.imageCategory" v-model:value="reverseManageStore.selectBookTask.imageCategory"
placeholder="选择生图模式" placeholder="选择生图模式"
></n-select> ></n-select>
<n-button <div v-if="reverseManageStore.selectBookTask.imageCategory == 'mj'">
v-if="reverseManageStore.selectBookTask.imageCategory == 'mj'" <n-popover trigger="hover">
color="#7c461e" <template #trigger>
size="tiny" <n-button
style="margin-left: 5px" text
@click="MJGetImage" type="info"
>MJ采集图片</n-button color="#e18a3b"
> style="font-size: 24px; margin-left: 5px"
@click="DownloadAllImage"
>
<n-icon> <Download /> </n-icon>
</n-button>
</template>
<span>MJ采集全部图片</span>
</n-popover>
</div>
<n-button <n-button
v-if="reverseManageStore.selectBookTask.imageCategory == 'mj'" v-if="reverseManageStore.selectBookTask.imageCategory == 'mj'"
color="#e18a3b" color="#e18a3b"
size="tiny" size="tiny"
style="margin-left: 5px" style="margin-left: 5px"
@click="OneSplitFour" @click="OneToFourBookTask"
>1拆4</n-button >1拆4</n-button
> >
</div> </div>
@ -30,11 +39,14 @@
<script setup> <script setup>
import { ref, onMounted } from 'vue' import { ref, onMounted } from 'vue'
import { NSelect, NButton, useMessage } from 'naive-ui' import { NSelect, NButton, useMessage, useDialog, NPopover, NIcon } from 'naive-ui'
import { useSoftwareStore } from '../../../../../stores/software' import { useSoftwareStore } from '../../../../../stores/software'
import { useReverseManageStore } from '../../../../../stores/reverseManage' import { useReverseManageStore } from '../../../../../stores/reverseManage'
import { Download } from '@vicons/ionicons5'
import { OperateBookType } from '../../../../../define/enum/bookEnum'
let softwareStore = useSoftwareStore() let softwareStore = useSoftwareStore()
let message = useMessage() let message = useMessage()
let dialog = useDialog()
let reverseManageStore = useReverseManageStore() let reverseManageStore = useReverseManageStore()
let image_options = ref([]) let image_options = ref([])
@ -49,6 +61,7 @@ onMounted(async () => {
}) })
}) })
//
async function UpdateImageGenerateCategory(value, options) { async function UpdateImageGenerateCategory(value, options) {
// //
let res = await window.db.UpdateBookTaskData(reverseManageStore.selectBookTask.id, { let res = await window.db.UpdateBookTaskData(reverseManageStore.selectBookTask.id, {
@ -56,4 +69,59 @@ async function UpdateImageGenerateCategory(value, options) {
}) })
window.api.showGlobalMessage(res) window.api.showGlobalMessage(res)
} }
async function DownloadAllImage() {
let da = dialog.warning({
title: '采集所有图片提示',
content:
'即将开始采集所有的MJ生图图片满足一下条件的分镜才会被采集状态不为 error有生图信息有消息ID没有生成图片图片的有效期为24小时是否继续',
positiveText: '继续',
negativeText: '取消',
onPositiveClick: async () => {
da?.destroy()
let res = await window.book.GetImageUrlAndDownload(
reverseManageStore.selectBookTask.id,
OperateBookType.BOOKTASK,
false
)
if (res.code == 1) {
//
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].outImagePath =
element.data.outImagePath
reverseManageStore.selectBookTaskDetail[findIndex].subImagePath =
element.data.subImagePath
reverseManageStore.selectBookTaskDetail[findIndex].mjMessage = element.data.mjMessage
}
}
message.success(res.message)
} else {
message.error(res.message)
}
}
})
}
async function OneToFourBookTask() {
dialog.warning({
title: '一拆四提示',
content:
'是否一拆四(一拆四是个泛指,根据出的子图数量最少的),会自动生成多个批次任务并设置对应的图片!',
positiveText: '确定',
negativeText: '取消',
onPositiveClick: async () => {
let res = await window.book.OneToFourBookTask(reverseManageStore.selectBookTask.id)
if (res.code == 0) {
message.error(res.message)
return
}
message.success(res.message)
}
})
}
</script> </script>

View File

@ -117,7 +117,7 @@ import { ref, onMounted, defineComponent, onUnmounted, toRaw, watch } from 'vue'
import { useMessage, NButton, NForm, NFormItem, NInput, NSelect, NIcon } from 'naive-ui' import { useMessage, NButton, NForm, NFormItem, NInput, NSelect, NIcon } from 'naive-ui'
import { useReverseManageStore } from '../../../../../../stores/reverseManage.ts' import { useReverseManageStore } from '../../../../../../stores/reverseManage.ts'
import { CloseSharp } from '@vicons/ionicons5' import { CloseSharp } from '@vicons/ionicons5'
import { cloneDeep } from 'lodash' import { cloneDeep, isEmpty } from 'lodash'
import { useSoftwareStore } from '../../../../../../stores/software.ts' import { useSoftwareStore } from '../../../../../../stores/software.ts'
import { BookType } from '../../../../../../define/enum/bookEnum' import { BookType } from '../../../../../../define/enum/bookEnum'
@ -227,9 +227,15 @@ export default defineComponent({
oldVideoPath: { oldVideoPath: {
required: true, required: true,
validator(rule, value) { validator(rule, value) {
// debugger
if(reverseManageStore.selectBook.type == BookType.SD_REVERSE || reverseManageStore.selectBook.type == BookType.MJ_REVERSE){ if (isEmpty(value)) {
return new Error("MJ反推和SD反推视频文件必选") //
if (
reverseManageStore.selectBook.type == BookType.SD_REVERSE ||
reverseManageStore.selectBook.type == BookType.MJ_REVERSE
) {
return new Error('MJ反推和SD反推视频文件必选')
}
} }
}, },
trigger: ['input', 'blur', 'change'] trigger: ['input', 'blur', 'change']

View File

@ -1,66 +1,83 @@
<template> <template>
<div> <div style="display: flex; align-items: center">
<span style="margin-right: 5px">推理提示词</span> <div>
<span style="margin-right: 5px">推理提示词</span>
<n-popover trigger="hover">
<template #trigger>
<n-button
:color="softwareStore.SoftColor.ZHUYANTUO"
style="margin-left: 5px"
size="tiny"
@click="AddPrefix"
>通用前缀</n-button
>
</template>
<span>添加通用前缀只能添加英文并且通用前缀不为空推理会自动添加前缀</span>
</n-popover>
<n-popover trigger="hover">
<template #trigger>
<n-button
:color="softwareStore.SoftColor.ZHUYANTUO"
style="margin-left: 5px"
size="tiny"
@click="AddSuffix"
>通用后缀</n-button
>
</template>
<span>添加通用后缀只能添加英文并且通用后缀不为空推理会自动添加后缀</span>
</n-popover>
<n-popover trigger="hover">
<template #trigger>
<n-button
:color="softwareStore.SoftColor.ZHUYANTUO"
style="margin-left: 5px"
size="tiny"
@click="SelectStyle"
>风格</n-button
>
</template>
<span>选择生成图片的风格</span>
</n-popover>
<n-popover trigger="hover">
<template #trigger>
<span>
<n-dropdown trigger="hover" :options="translate_options" @select="TranslateAll">
<n-button
:color="softwareStore.SoftColor.ZHUYANTUO"
style="margin-left: 5px"
size="tiny"
@click="TranslateAll('english')"
>一键翻译</n-button
>
</n-dropdown>
</span>
</template>
<span
>将下面的推理提示词或者是手写的提示词翻译成英文只会翻译存在中文的数据下面的翻译中文会将所有的推理提示词翻译为中文全部都会翻译</span
>
</n-popover>
</div>
<n-popover trigger="hover"> <n-popover trigger="hover">
<template #trigger> <template #trigger>
<n-button <n-button
:color="softwareStore.SoftColor.ZHUYANTUO" color="#7c461e"
style="margin-left: 5px" @click="SelectReversePromptAndReplace"
size="tiny" text
@click="AddPrefix" style="font-size: 24px; margin-left: 3px"
>通用前缀</n-button
> >
<n-icon>
<FindReplaceRound />
</n-icon>
</n-button>
</template> </template>
<span>添加通用前缀只能添加英文并且通用前缀不为空推理会自动添加前缀</span> <span>批量替换推理提示词</span>
</n-popover>
<n-popover trigger="hover">
<template #trigger>
<n-button
:color="softwareStore.SoftColor.ZHUYANTUO"
style="margin-left: 5px"
size="tiny"
@click="AddSuffix"
>通用后缀</n-button
>
</template>
<span>添加通用后缀只能添加英文并且通用后缀不为空推理会自动添加后缀</span>
</n-popover>
<n-popover trigger="hover">
<template #trigger>
<n-button
:color="softwareStore.SoftColor.ZHUYANTUO"
style="margin-left: 5px"
size="tiny"
@click="SelectStyle"
>风格</n-button
>
</template>
<span>选择生成图片的风格</span>
</n-popover>
<n-popover trigger="hover">
<template #trigger>
<span>
<n-dropdown trigger="hover" :options="translate_options" @select="TranslateAll">
<n-button
:color="softwareStore.SoftColor.ZHUYANTUO"
style="margin-left: 5px"
size="tiny"
@click="TranslateAll('english')"
>一键翻译</n-button
>
</n-dropdown>
</span>
</template>
<span
>将下面的推理提示词或者是手写的提示词翻译成英文只会翻译存在中文的数据下面的翻译中文会将所有的推理提示词翻译为中文全部都会翻译</span
>
</n-popover> </n-popover>
</div> </div>
</template> </template>
<script setup> <script setup>
import { ref, h, toRaw } from 'vue' import { ref, h, toRaw } from 'vue'
import { NPopover, NButton, NDropdown, useMessage, useDialog } from 'naive-ui' import { NPopover, NButton, NDropdown, useMessage, useDialog, NIcon } from 'naive-ui'
import { useReverseManageStore } from '../../../../../stores/reverseManage' import { useReverseManageStore } from '../../../../../stores/reverseManage'
import { useSoftwareStore } from '../../../../../stores/software' import { useSoftwareStore } from '../../../../../stores/software'
import SelectImageStyle from '../../Components/SelectImageStyle.vue' import SelectImageStyle from '../../Components/SelectImageStyle.vue'
@ -69,6 +86,9 @@ import { ContainsChineseOrPunctuation } from '../../../../../define/Tools/common
import { isEmpty } from 'lodash' import { isEmpty } from 'lodash'
import { TranslateType } from '../../../../../define/enum/translate' import { TranslateType } from '../../../../../define/enum/translate'
import { DEFINE_STRING } from '../../../../../define/define_string' import { DEFINE_STRING } from '../../../../../define/define_string'
import FindReplaceRound from '../../Icon/FindReplaceRound.vue'
import DatatableHeaderAfterGptSelectAndReplace from '../Components/DatatableHeaderAfterGptSelectAndReplace.vue'
import { BookRepalceDataType } from '../../../../../define/enum/bookEnum'
let softwareStore = useSoftwareStore() let softwareStore = useSoftwareStore()
let reverseManageStore = useReverseManageStore() let reverseManageStore = useReverseManageStore()
@ -288,4 +308,15 @@ async function SelectStyle() {
} }
}) })
} }
//
async function SelectReversePromptAndReplace() {
dialog.create({
title: '批量查询替换反推提示词',
showIcon: false,
maskClosable: false,
content: () =>
h(DatatableHeaderAfterGptSelectAndReplace, { type: BookRepalceDataType.GPT_PROMPT })
})
}
</script> </script>

View File

@ -1,7 +1,6 @@
<template> <template>
<div style="display: flex"> <div style="display: flex">
<span style="margin-right: 5px">提示词命令</span> <span style="margin-right: 5px">提示词命令</span>
<n-popover trigger="hover"> <n-popover trigger="hover">
<template #trigger> <template #trigger>
<n-button <n-button
@ -33,6 +32,22 @@
@click="MJBadPromptCheck" @click="MJBadPromptCheck"
>敏感词检查</n-button >敏感词检查</n-button
> >
<n-popover trigger="hover">
<template #trigger>
<n-button
color="#7c461e"
@click="SelectPromptAndReplace"
text
style="font-size: 24px; margin-left: 10px"
>
<n-icon>
<FindReplaceRound />
</n-icon>
</n-button>
</template>
<span>批量查询替换文案</span>
</n-popover>
</div> </div>
</template> </template>
@ -43,7 +58,9 @@ import { Construct } from '@vicons/ionicons5'
import DynamicTagsSelect from '../../Components/DynamicTagsSelect.vue' import DynamicTagsSelect from '../../Components/DynamicTagsSelect.vue'
import { useSoftwareStore } from '../../../../../stores/software' import { useSoftwareStore } from '../../../../../stores/software'
import { useReverseManageStore } from '../../../../../stores/reverseManage' import { useReverseManageStore } from '../../../../../stores/reverseManage'
import { OperateBookType } from '../../../../../define/enum/bookEnum' import { BookRepalceDataType, OperateBookType } from '../../../../../define/enum/bookEnum'
import FindReplaceRound from '../../Icon/FindReplaceRound.vue'
import DatatableHeaderAfterGptSelectAndReplace from '../Components/DatatableHeaderAfterGptSelectAndReplace.vue'
let message = useMessage() let message = useMessage()
let dialog = useDialog() let dialog = useDialog()
let softwareStore = useSoftwareStore() let softwareStore = useSoftwareStore()
@ -118,7 +135,7 @@ async function MergePrompt(type = null) {
} else if (image_generate_category == 'mj') { } else if (image_generate_category == 'mj') {
mergeType = 'mj_merge' mergeType = 'mj_merge'
} else if (image_generate_category == 'd3') { } else if (image_generate_category == 'd3') {
message.error('D3模式不支持') mergeType = 'sd_merge'
} else if (image_generate_category == 'flux-api' || image_generate_category == 'flux-forge') { } else if (image_generate_category == 'flux-api' || image_generate_category == 'flux-forge') {
mergeType = 'sd_merge' mergeType = 'sd_merge'
} else { } else {
@ -167,4 +184,13 @@ async function MergePromptSelect(key) {
async function MJBadPromptCheck() { async function MJBadPromptCheck() {
message.error('敏感词检测功能暂未开放') message.error('敏感词检测功能暂未开放')
} }
async function SelectPromptAndReplace() {
dialog.create({
title: '批量查询替换生图提示词',
showIcon: false,
maskClosable: false,
content: () => h(DatatableHeaderAfterGptSelectAndReplace, { type: BookRepalceDataType.PROMPT })
})
}
</script> </script>

View File

@ -27,7 +27,7 @@
:color="softwareStore.SoftColor.BROWN_YELLOW" :color="softwareStore.SoftColor.BROWN_YELLOW"
style="margin-right: 5px" style="margin-right: 5px"
size="tiny" size="tiny"
@click="ImportMJImageUrl" @click="ImportImageUrl"
>导入图片</n-button >导入图片</n-button
> >
</template> </template>
@ -47,10 +47,11 @@
</template> </template>
<script setup> <script setup>
import { ref } from 'vue' import { ref, h } from 'vue'
import { NButton, NInput, NPopover, useMessage } from 'naive-ui' import { NButton, NInput, NPopover, useMessage, useDialog } from 'naive-ui'
import { useSoftwareStore } from '../../../../../stores/software' import { useSoftwareStore } from '../../../../../stores/software'
import { useReverseManageStore } from '../../../../../stores/reverseManage' import { useReverseManageStore } from '../../../../../stores/reverseManage'
import InputDialogContent from '../../Original/Components/InputDialogContent.vue'
import { import {
BookBackTaskType, BookBackTaskType,
BookImageCategory, BookImageCategory,
@ -66,7 +67,8 @@ let props = defineProps({
let softwareStore = useSoftwareStore() let softwareStore = useSoftwareStore()
let reverseManageStore = useReverseManageStore() let reverseManageStore = useReverseManageStore()
let message = useMessage() let message = useMessage()
let dialog = useDialog()
let image_url_ref = ref(null)
let row = ref(props.initData) let row = ref(props.initData)
let InputDebounced = debounce(handleInput, 500) let InputDebounced = debounce(handleInput, 500)
@ -89,7 +91,7 @@ async function SingleMergePrompt() {
} else if (image_generate_category == 'mj') { } else if (image_generate_category == 'mj') {
mergeType = 'mj_merge' mergeType = 'mj_merge'
} else if (image_generate_category == 'd3') { } else if (image_generate_category == 'd3') {
message.error('D3模式不支持') mergeType = 'sd_merge'
} else if (image_generate_category == 'flux-api' || image_generate_category == 'flux-forge') { } else if (image_generate_category == 'flux-api' || image_generate_category == 'flux-forge') {
mergeType = 'sd_merge' mergeType = 'sd_merge'
} else { } else {
@ -262,4 +264,44 @@ async function NextGenerateImage() {
message.error(res.message) message.error(res.message)
} }
} }
async function ImportImageUrl() {
//
//
let dialogWidth = 400
let dialogHeight = 150
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 () => {
let row_image_url = image_url_ref.value.data
softwareStore.spin.spinning = true
softwareStore.spin.tip = '正在下载图片/分割图片中。。。'
let res = await window.book.DownloadImageUrlAndSplit(row.value.id, row_image_url)
softwareStore.spin.spinning = false
if (res.code == 0) {
message.error(res.message)
return
}
let findIndex = reverseManageStore.selectBookTaskDetail.findIndex(
(item) => item.id == row.value.id
)
if (findIndex != -1) {
reverseManageStore.selectBookTaskDetail[findIndex].outImagePath = res.data.outImagePath
reverseManageStore.selectBookTaskDetail[findIndex].subImagePath = res.data.subImagePath
reverseManageStore.selectBookTaskDetail[findIndex].mjMessage = res.data.mjMessage
}
message.success(res.message)
}
})
}
</script> </script>

View File

@ -77,6 +77,7 @@ import {
} from '../../../../../define/enum/bookEnum' } from '../../../../../define/enum/bookEnum'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
import { DEFINE_STRING } from '../../../../../define/define_string' import { DEFINE_STRING } from '../../../../../define/define_string'
import { isEmpty } from 'lodash'
let softwareStore = useSoftwareStore() let softwareStore = useSoftwareStore()
let reverseManageStore = useReverseManageStore() let reverseManageStore = useReverseManageStore()
let dialog = useDialog() let dialog = useDialog()
@ -92,8 +93,8 @@ let GenerateImageOptions = ref([
{ label: '停止生成图片任务', key: 'stop_generate_image' } { label: '停止生成图片任务', key: 'stop_generate_image' }
]) ])
let ResetDataOptions = ref([ let ResetDataOptions = ref([
{ label: '重置GPT提示词', key: 'reset_gpt_prompt' }, { label: '重置GPT提示词', key: 'reset_prompt' },
{ label: '重置提示词', key: 'reset_prompt' }, { label: '重置提示词', key: 'reset_merge_prompt' },
{ label: '重置图片', key: 'reset_image' } { label: '重置图片', key: 'reset_image' }
]) ])
@ -246,6 +247,39 @@ async function ResetGPTPrompt() {
}) })
} }
//
async function ResetMergePrompt() {
let da = dialog.warning({
title: '重置合并提示词提示',
content: '执行该操作会删除所有的合并提示词,并且此操作不可逆,是否继续?',
positiveText: '继续',
negativeText: '取消',
onPositiveClick: async () => {
da?.destroy()
softwareStore.spin.spinning = true
softwareStore.spin.tip = '正在重置推理提示词,请稍后。。。'
for (let i = 0; i < reverseManageStore.selectBookTaskDetail.length; i++) {
const element = reverseManageStore.selectBookTaskDetail[i]
let res = await window.db.UpdateBookTaskDetailData(element.id, {
prompt: ''
})
if (res.code == 1) {
let findIndex = reverseManageStore.selectBookTaskDetail.findIndex(
(item) => item.id == element.id
)
if (findIndex != -1) {
reverseManageStore.selectBookTaskDetail[findIndex].prompt = ''
}
} else {
message.error(res.message)
}
}
softwareStore.spin.spinning = false
message.success('重置提示词数据成功')
}
})
}
// //
async function ResetImage() { async function ResetImage() {
message.info('ResetImage') message.info('ResetImage')
@ -295,6 +329,9 @@ async function ButtonSelect(key) {
} else if (key == 'generate_space_image') { } else if (key == 'generate_space_image') {
// //
await GenerateImageAll(false) await GenerateImageAll(false)
} else if (key == 'reset_merge_prompt') {
//
await ResetMergePrompt()
} else if (key == 'reset_image') { } else if (key == 'reset_image') {
await ResetImage() await ResetImage()
} else { } else {
@ -454,47 +491,44 @@ async function OpenPromptSetting() {
// //
async function ImportPrompt() { async function ImportPrompt() {
message.error('导入提示词该方法暂未实现')
return
// //
window.api.SelectFile(['txt'], async (value) => { window.api.SelectFile(['txt'], async (value) => {
if (value.code == 0) { if (value.code == 0) {
message.error(value.message) message.error(value.message)
return return
} }
// if (isEmpty(value.value)) {
await window.pmpt.OpenPromptFileTxt(value.value, (res) => { message.error('返回的文件地址为空')
debugger return
console.log(res) }
if (res.code == 0) {
message.error(res.message) let da = dialog.warning({
return title: '导入提示词提示',
} content: `即将开始导入提示词,务必保证提示词文件的行数和分镜的行数相同,否则可能会导致数据丢失,当前的导入的文件地址为 ${value.value},是否继续?`,
// positiveText: '继续',
// data negativeText: '取消',
if (res.data.length > data.value.length) { onPositiveClick: async () => {
dialog.warning({ da?.destroy()
title: '提示', softwareStore.spin.spinning = true
content: '导入的数据行数大于当前的数据行数,多余的数据会被删除,是否继续导入?', softwareStore.spin.tip = '正在导入提示词数据中。。。'
positiveText: '继续', let res = await window.book.ImportGPTPrompt(
negativeText: '取消', reverseManageStore.selectBookTask.id,
onPositiveClick: async () => { value.value
debugger )
// softwareStore.spin.spinning = false
for (let i = 0; i < data.value.length && i < res.data.length; i++) { if (res.code == 1) {
const element = res.data[i] //
// for (let i = 0; i < res.data.length; i++) {
data.value[i].gpt_prompt = element 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 {
//
for (let i = 0; i < data.value.length && i < res.data.length; i++) {
const element = res.data[i]
//
data.value[i].gpt_prompt = element
} }
window.api.showGlobalMessage(res)
} }
}) })
}) })

View File

@ -107,6 +107,7 @@ const createColumns = ({}) => {
className: 'empty-margin', className: 'empty-margin',
fixed: 'left', fixed: 'left',
render(row, index) { render(row, index) {
console.log('row', row, index)
return h(DatatableAfterGpt, { initData: row, index: index }) return h(DatatableAfterGpt, { initData: row, index: index })
} }
}, },
@ -133,7 +134,7 @@ const createColumns = ({}) => {
resizable: true, resizable: true,
minWidth: 330, minWidth: 330,
maxWidth: 700, maxWidth: 700,
width: '350', width: '360',
render(row, index) { render(row, index) {
return h(ODataTableGptPrompt, { initData: row, index: index }) return h(ODataTableGptPrompt, { initData: row, index: index })
} }

View File

@ -230,10 +230,17 @@ export const useReverseManageStore = defineStore('reverseManage', {
*/ */
async GetBookTaskDetail(bookTaskId: string, modifyProperty = null): Promise<GeneralResponse.ErrorItem | GeneralResponse.SuccessItem> { async GetBookTaskDetail(bookTaskId: string, modifyProperty = null): Promise<GeneralResponse.ErrorItem | GeneralResponse.SuccessItem> {
try { try {
debugger
//@ts-ignore //@ts-ignore
let detailRes = await window.book.GetBookTaskDetail(bookTaskId) let detailRes = await window.book.GetBookTaskDetail(bookTaskId)
let bookTaskDetail = []
if (detailRes.code == 1) { if (detailRes.code == 1) {
bookTaskDetail = detailRes.data.map(item => {
return {
...item,
outImagePath: item.outImagePath ? item.outImagePath + '?t=' + new Date().getTime() : undefined,
subImagePath: item.subImagePath ? item.subImagePath.map(item => item + '?t=' + new Date().getTime()) : []
}
})
// 这边开始修改数据, // 这边开始修改数据,
if (this.selectBookTaskDetail && this.selectBookTaskDetail.length > 0) { if (this.selectBookTaskDetail && this.selectBookTaskDetail.length > 0) {
this.selectBookTaskDetail.splice(0, this.selectBookTaskDetail.length, ...detailRes.data); this.selectBookTaskDetail.splice(0, this.selectBookTaskDetail.length, ...detailRes.data);
@ -243,7 +250,7 @@ export const useReverseManageStore = defineStore('reverseManage', {
} else { } else {
return errorMessage(detailRes.message) return errorMessage(detailRes.message)
} }
return successMessage(detailRes.data) return successMessage(bookTaskDetail)
} catch (error) { } catch (error) {
return errorMessage("获取小说任务详细数据失败,失败信息如下: " + error.toString()) return errorMessage("获取小说任务详细数据失败,失败信息如下: " + error.toString())
} }