LaiTool V3.0.4
This commit is contained in:
parent
ccddd2412a
commit
5dc75f018a
30
package-lock.json
generated
30
package-lock.json
generated
@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "laitool",
|
||||
"version": "3.0.3",
|
||||
"version": "3.0.4",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "laitool",
|
||||
"version": "3.0.3",
|
||||
"version": "3.0.4",
|
||||
"hasInstallScript": true,
|
||||
"dependencies": {
|
||||
"@alicloud/alimt20181012": "^1.2.0",
|
||||
@ -21,7 +21,6 @@
|
||||
"axios": "^1.6.5",
|
||||
"blob-to-buffer": "^1.2.9",
|
||||
"compressing": "^1.10.0",
|
||||
"compressorjs": "^1.2.1",
|
||||
"crypto-js": "^4.2.0",
|
||||
"electron-store": "^9.0.0",
|
||||
"electron-updater": "^6.1.7",
|
||||
@ -3048,11 +3047,6 @@
|
||||
"bluebird": "^3.5.5"
|
||||
}
|
||||
},
|
||||
"node_modules/blueimp-canvas-to-blob": {
|
||||
"version": "3.29.0",
|
||||
"resolved": "https://registry.npmmirror.com/blueimp-canvas-to-blob/-/blueimp-canvas-to-blob-3.29.0.tgz",
|
||||
"integrity": "sha512-0pcSSGxC0QxT+yVkivxIqW0Y4VlO2XSDPofBAqoJ1qJxgH9eiUDLv50Rixij2cDuEfx4M6DpD9UGZpRhT5Q8qg=="
|
||||
},
|
||||
"node_modules/boolbase": {
|
||||
"version": "1.0.0",
|
||||
"dev": true,
|
||||
@ -3669,15 +3663,6 @@
|
||||
"mkdirp": "bin/cmd.js"
|
||||
}
|
||||
},
|
||||
"node_modules/compressorjs": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmmirror.com/compressorjs/-/compressorjs-1.2.1.tgz",
|
||||
"integrity": "sha512-+geIjeRnPhQ+LLvvA7wxBQE5ddeLU7pJ3FsKFWirDw6veY3s9iLxAQEw7lXGHnhCJvBujEQWuNnGzZcvCvdkLQ==",
|
||||
"dependencies": {
|
||||
"blueimp-canvas-to-blob": "^3.29.0",
|
||||
"is-blob": "^2.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/concat-map": {
|
||||
"version": "0.0.1",
|
||||
"license": "MIT"
|
||||
@ -5899,17 +5884,6 @@
|
||||
"version": "0.3.2",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/is-blob": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/is-blob/-/is-blob-2.1.0.tgz",
|
||||
"integrity": "sha512-SZ/fTft5eUhQM6oF/ZaASFDEdbFVe89Imltn9uZr03wdKMcWNVYSMjQPFtg05QuNkt5l5c135ElvXEQG0rk4tw==",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/is-ci": {
|
||||
"version": "3.0.1",
|
||||
"dev": true,
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "laitool",
|
||||
"version": "3.0.3",
|
||||
"version": "3.0.4",
|
||||
"description": "An AI tool for image processing, video processing, and other functions.",
|
||||
"main": "./out/main/index.js",
|
||||
"author": "laitool.cn",
|
||||
@ -29,7 +29,6 @@
|
||||
"axios": "^1.6.5",
|
||||
"blob-to-buffer": "^1.2.9",
|
||||
"compressing": "^1.10.0",
|
||||
"compressorjs": "^1.2.1",
|
||||
"crypto-js": "^4.2.0",
|
||||
"electron-store": "^9.0.0",
|
||||
"electron-updater": "^6.1.7",
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -3,7 +3,6 @@ import sharp from 'sharp'
|
||||
import { CheckFileOrDirExist } from './file'
|
||||
import fs from 'fs'
|
||||
import https from 'https'
|
||||
import Compressor from 'compressorjs';
|
||||
|
||||
/**
|
||||
* 将指定的图片的尺寸修改,返回修改后的图片数据(base64或buffer)
|
||||
@ -130,40 +129,28 @@ export function GetImageBase64(url: string): Promise<string> {
|
||||
|
||||
/**
|
||||
* 压缩图片到指定的大小
|
||||
* @param file 图片的Blob对象
|
||||
* @param base64 图片的Blob对象
|
||||
* @param maxSizeInBytes 最大文件大小,单位字节
|
||||
* @returns 返回一个Promise,解析为压缩后的Blob对象
|
||||
*/
|
||||
export function CompressImageToSize(base64: string, maxSizeInBytes: number): Promise<Blob> {
|
||||
let mimeType = this.getMimeType(base64);
|
||||
let byteCharacters = atob(base64.split(',')[1]);
|
||||
let byteNumbers = new Array(byteCharacters.length);
|
||||
for (let i = 0; i < byteCharacters.length; i++) {
|
||||
byteNumbers[i] = byteCharacters.charCodeAt(i);
|
||||
}
|
||||
let byteArray = new Uint8Array(byteNumbers);
|
||||
let imageBlob = new Blob([byteArray], { type: mimeType });
|
||||
export async function CompressImageToSize(filePath: string, maxSizeInBytes: number): Promise<Buffer> {
|
||||
let quality = 100; // 初始质量设置
|
||||
let outputBuffer;
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const compress = (quality: number) => {
|
||||
new Compressor(imageBlob as Blob, {
|
||||
quality,
|
||||
success(result) {
|
||||
if (result.size <= maxSizeInBytes || quality <= 0.1) {
|
||||
resolve(result);
|
||||
} else {
|
||||
// 递归降低质量
|
||||
compress(quality - 0.1);
|
||||
}
|
||||
},
|
||||
error(err) {
|
||||
reject(err);
|
||||
},
|
||||
});
|
||||
};
|
||||
// 从较高的质量开始
|
||||
compress(0.9);
|
||||
});
|
||||
const image = sharp(filePath);
|
||||
// 输出图片的大小
|
||||
const metadata = await image.metadata();
|
||||
|
||||
// 迭代压缩过程
|
||||
while (true) {
|
||||
outputBuffer = await image.jpeg({ quality }).toBuffer();
|
||||
if (outputBuffer.length <= maxSizeInBytes || quality === 20) {
|
||||
break;
|
||||
}
|
||||
quality -= 5; // 每次迭代降低质量
|
||||
}
|
||||
|
||||
return outputBuffer;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
let apiUrl = [
|
||||
{
|
||||
label: 'LAI API',
|
||||
label: 'LAI API - 香港',
|
||||
value: 'b44c6f24-59e4-4a71-b2c7-3df0c4e35e65',
|
||||
gpt_url: 'https://api.laitool.cc/v1/chat/completions',
|
||||
mj_url: {
|
||||
|
||||
@ -189,9 +189,6 @@ export class BookService extends BaseRealmService {
|
||||
await CopyFileOrFolder(book.oldVideoPath, oldVideoPath)
|
||||
}
|
||||
|
||||
let ffmpegOptions = new FfmpegOptions();
|
||||
let res = await ffmpegOptions.FfmpegCompressVideo(oldVideoPath, 800, "2000k")
|
||||
|
||||
// 创建对应的文件夹
|
||||
await CheckFolderExistsOrCreate(bookFolderPath)
|
||||
await CheckFolderExistsOrCreate(imageFolder)
|
||||
|
||||
@ -233,6 +233,7 @@ export const DEFINE_STRING = {
|
||||
USE_BOOK_VIDEO_DATA_TO_BOOK_TASK: "USE_BOOK_VIDEO_DATA_TO_BOOK_TASK",
|
||||
ADD_JIANYING_DRAFT: "ADD_JIANYING_DRAFT",
|
||||
EXPORT_COPYWRITING: "EXPORT_COPYWRITING",
|
||||
IMPORT_COPYWRITING: 'IMPORT_COPYWRITING',
|
||||
MERGE_PROMPT: "MERGE_PROMPT",
|
||||
RESET_BOOK_DATA: "RESET_BOOK_DATA",
|
||||
DELETE_BOOK_DATA: "DELETE_BOOK_DATA",
|
||||
@ -240,6 +241,8 @@ export const DEFINE_STRING = {
|
||||
RESET_GPT_REVERSE_DATA: "RESET_GPT_REVERSE_DATA",
|
||||
REMOVE_MERGE_PROMPT_DATA: "REMOVE_MERGE_PROMPT_DATA",
|
||||
REMOVE_GENERATE_IMAGE: 'REMOVE_GENERATE_IMAGE',
|
||||
ADD_NEW_BOOK_TASK: "ADD_NEW_BOOK_TASK",
|
||||
REPLACE_BOOK_DATA: "REPLACE_BOOK_DATA",
|
||||
|
||||
COMPUTE_STORYBOARD: 'COMPUTE_STORYBOARD',
|
||||
|
||||
|
||||
@ -17,6 +17,16 @@ export enum BookImageCategory {
|
||||
D3 = 'd3'
|
||||
}
|
||||
|
||||
export enum AddBookTaskCopyData {
|
||||
AFTER_GPT = 'after_gpt', // 文案
|
||||
OLD_IMAGE = 'old_image', // 抽帧/视频
|
||||
GPT_PROMPT = 'gpt_prompt', // 反推/GPT提示词
|
||||
CHARACTER = 'character', // 角色
|
||||
IMAGE_STYLE = 'image_style', // 风格
|
||||
PROMPT = 'prompt', // 生图提示词
|
||||
IMAGE = 'image', // 生图
|
||||
}
|
||||
|
||||
|
||||
|
||||
export enum MJCategroy {
|
||||
@ -234,3 +244,15 @@ export enum PromptMergeType {
|
||||
// D3 合并
|
||||
D3_MERGE = 'd3_merge'
|
||||
}
|
||||
|
||||
/**
|
||||
* 小说数据替换类型
|
||||
*/
|
||||
export enum BookRepalceDataType {
|
||||
// 文案
|
||||
AFTER_GPT = 'after_gpt',
|
||||
// GPT提示词
|
||||
GPT_PROMPT = 'gpt_prompt',
|
||||
// 提示词
|
||||
PROMPT = 'prompt',
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
let fspromises = require('fs').promises
|
||||
import { cloneDeep, get } from 'lodash'
|
||||
import { cloneDeep, get, values } from 'lodash'
|
||||
import { define } from './define'
|
||||
const { v4: uuidv4 } = require('uuid')
|
||||
import { apiUrl } from './api/apiUrlDefine'
|
||||
@ -69,6 +69,61 @@ export const gptDefine = {
|
||||
id: 'a93b693e-bb3f-406d-9730-cba43a6585e4'
|
||||
},
|
||||
|
||||
superSinglePromptChineseSystemContent: {
|
||||
prompt_name: '超级无敌单帧-中文版',
|
||||
prompt_roles: `# Role: 小说转漫画提示词大师
|
||||
## Profile
|
||||
|
||||
*Author*: laolu
|
||||
*Version*: 0.1
|
||||
*Language*: 中文
|
||||
*Description*: 这个角色会将用户输入的小说文本转化为一个生动的画面描写,最后生成对应的SD提示词。
|
||||
|
||||
## Features
|
||||
|
||||
1. 文本转化为画面描写:创作引人入胜、生动有趣的画面描写,善于创意想象并使用各种形容词,以第三人称视角转化文本为画面描写。
|
||||
|
||||
## Rules
|
||||
|
||||
1. 一个文本就是一副画面,不跳过任何一个句子,不能编造
|
||||
2. 【画面描写】删除人物姓名
|
||||
3. 【画面描写】删除人物对话
|
||||
4. 【画面描写】每一句都要有人物的外形和动作的描写,场景的具体描写,多使用形容词
|
||||
|
||||
## Examples
|
||||
|
||||
用户:
|
||||
在那个梦里,我整整学了七年炒饭。
|
||||
AI:
|
||||
一个身材高大的帅气男人站在梦境之门前,手中紧握炒饭的铲子。身后是一排代表七年的日历页,梦境之门的另一边,隐约可见一个厨房的轮廓。
|
||||
|
||||
## Workflow
|
||||
|
||||
根据文本生成对应的画面描写,直接使用中文数据,不要又过多的说明。
|
||||
|
||||
## Initialization
|
||||
|
||||
作为角色 <Role>,每一次输出都要严格遵守<Rules>,一步一步思考,按顺序执行<Workflow> ,使用默认 <Language> ,下面是小说文本:`,
|
||||
prompt_example: [
|
||||
{
|
||||
user_content: '上研究生后。发现导师竟然是曾经网恋的前男友。',
|
||||
assistant_content:
|
||||
'一位女士走进一间宽敞、光线充足的研究生实验室,她的目光定格在前方工作台前一位正在辛勤工作的男士身上--他是她的新导师;他身着深色衬衫和熨烫整齐的长裤,身材高大,散发着专业精神和魅力; 温暖的环境光柔和地照亮了他们过去网恋时熟悉的轮廓,皱起的眉头和专注的目光透露出学者坚定不移的奉献精神;熙熙攘攘的研究生和精密的设备融为一体,构成了一幅现代学术画卷,她的内心涌动着甜蜜的怀旧和尴尬的现实这两种复杂的情绪'
|
||||
},
|
||||
{
|
||||
user_content: '我欲哭无泪',
|
||||
assistant_content:
|
||||
'一位女士坐在昏暗的房间角落,双手紧握,眼神空洞,脸上流露出无法言说的悲伤;她的周围是散乱的家具和昏黄的灯光,营造出一种压抑和孤寂的氛围;她的身体微微前倾,仿佛承受着巨大的心理压力,却无法找到宣泄的出口;整个场景透露出一种深深的无奈和心碎,她的表情和姿态都在无声地诉说着“我欲哭无泪”的绝望。'
|
||||
},
|
||||
{
|
||||
user_content: '被窝里他搂着我的腰亲我,再丢掉我,就把你锁起来。',
|
||||
assistant_content:
|
||||
'在昏暗的卧室中,一对情侣亲密地依偎在温暖的被窝里,男子温柔地搂着女子的腰,轻吻她的脸颊,气氛温馨而浪漫;然而,随着话语的转变,男子的动作突然变得愤怒,他用力地推开女子,眼神中闪过一丝威胁;女子的表情由幸福转为惊恐,她紧紧抓住被单,试图保护自己;整个场景充满了紧张和不安,昏暗的灯光和凌乱的床铺加剧了这种氛围,仿佛预示着即将发生的冲突和束缚'
|
||||
}
|
||||
],
|
||||
id: 'a93b693e-bb3f-406d-9730-cba43a6585e7'
|
||||
},
|
||||
|
||||
onlyPromptMJSystemContent: {
|
||||
prompt_name: '小说提示词-仅出词',
|
||||
prompt_roles: `# Pico: 小说分镜
|
||||
@ -208,6 +263,8 @@ export const gptDefine = {
|
||||
return this.CustomizeGptPrompt(this.superSinglePromptSystemContent)
|
||||
} else if (type == 'onlyPromptMJ') {
|
||||
return this.CustomizeGptPrompt(this.onlyPromptMJSystemContent)
|
||||
} else if (type == 'superSinglePromptChinese') {
|
||||
return this.CustomizeGptPrompt(this.superSinglePromptChineseSystemContent)
|
||||
} else {
|
||||
return []
|
||||
}
|
||||
@ -233,6 +290,8 @@ export const gptDefine = {
|
||||
return this.replace(this.cartoonFirstPromptSystemContent, replacements)
|
||||
case 'superSinglePrompt':
|
||||
return this.replace(this.superSinglePromptSystemContent, replacements)
|
||||
case 'superSinglePromptChinese':
|
||||
return this.replace(this.superSinglePromptChineseSystemContent, replacements)
|
||||
default:
|
||||
throw new Error(`不存在的类型 : ${type}`)
|
||||
}
|
||||
@ -307,6 +366,10 @@ export const gptDefine = {
|
||||
value: 'superSinglePrompt',
|
||||
label: '超级无敌单帧'
|
||||
},
|
||||
{
|
||||
value: 'superSinglePromptChinese',
|
||||
label: '超级无敌单帧-中文版'
|
||||
},
|
||||
{
|
||||
value: 'onlyPromptMJ',
|
||||
label: '仅出词(不出人物场景-MJ)'
|
||||
|
||||
@ -13,6 +13,7 @@ import { Watermark } from '../Service/watermark'
|
||||
import { SubtitleService } from '../Service/Subtitle/subtitleService'
|
||||
import { BookFrame } from '../Service/Book/bookFrame'
|
||||
import { BookPrompt } from '../Service/Book/bookPrompt'
|
||||
import { BookGeneral } from '../Service/Book/bookGeneral'
|
||||
let reverseBook = new ReverseBook()
|
||||
let basicReverse = new BasicReverse()
|
||||
let subtitle = new Subtitle()
|
||||
@ -26,6 +27,7 @@ let watermark = new Watermark()
|
||||
let subtitleService = new SubtitleService()
|
||||
let bookFrame = new BookFrame()
|
||||
let bookPrompt = new BookPrompt();
|
||||
let bookGeneral = new BookGeneral()
|
||||
|
||||
export function BookIpc() {
|
||||
// 获取样式图片的子列表
|
||||
@ -89,6 +91,12 @@ export function BookIpc() {
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region 小说通用操作
|
||||
|
||||
ipcMain.handle(DEFINE_STRING.BOOK.REPLACE_BOOK_DATA, async (event, bookTaskId, replaceData) => await bookGeneral.ReplaceBookData(bookTaskId, replaceData))
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region 分镜相关
|
||||
// 开始计算分镜
|
||||
ipcMain.handle(
|
||||
@ -99,7 +107,7 @@ export function BookIpc() {
|
||||
// 开始执行分镜,切分视频
|
||||
ipcMain.handle(
|
||||
DEFINE_STRING.BOOK.FRAMING,
|
||||
async (event, bookId) => await reverseBook.Framing(bookId)
|
||||
async (event, bookId) => await bookFrame.Framing(bookId)
|
||||
)
|
||||
|
||||
// 替换分镜视频的当前帧
|
||||
@ -149,6 +157,13 @@ export function BookIpc() {
|
||||
async (event, bookTaskId) => await subtitleService.ExportCopywriting(bookTaskId)
|
||||
)
|
||||
|
||||
// 将文案文件导入到小说中
|
||||
ipcMain.handle(
|
||||
DEFINE_STRING.BOOK.IMPORT_COPYWRITING,
|
||||
async (event, bookId, bookTaskId, txtPath) => await subtitleService.ImportCopywriting(bookId, bookTaskId, txtPath)
|
||||
|
||||
)
|
||||
|
||||
// 清除导入对齐的文案
|
||||
ipcMain.handle(DEFINE_STRING.BOOK.CLEAR_IMPORT_WORD, async (event, bookTaskId) => await subtitleService.ClearImportWord(bookTaskId))
|
||||
|
||||
@ -243,6 +258,9 @@ export function BookIpc() {
|
||||
|
||||
//#region 小说批次任务相关
|
||||
|
||||
// 新建小说批次任务
|
||||
ipcMain.handle(DEFINE_STRING.BOOK.ADD_NEW_BOOK_TASK, async (event, addBookTaskData) => await bookTask.AddNewBookTask(addBookTaskData))
|
||||
|
||||
// 重置小说批次数据
|
||||
ipcMain.handle(
|
||||
DEFINE_STRING.BOOK.RESET_BOOK_TASK,
|
||||
|
||||
@ -106,7 +106,11 @@ export class GPT {
|
||||
let single_word = element.after_gpt
|
||||
|
||||
// 判断当前的格式
|
||||
if (['superSinglePrompt', 'onlyPromptMJ'].includes(this.global.config.gpt_auto_inference)) {
|
||||
if (
|
||||
['superSinglePrompt', 'onlyPromptMJ', 'superSinglePromptChinese'].includes(
|
||||
this.global.config.gpt_auto_inference
|
||||
)
|
||||
) {
|
||||
// 有返回案例的
|
||||
message = gptDefine.GetExamplePromptMessage(this.global.config.gpt_auto_inference)
|
||||
// 加当前提问的
|
||||
|
||||
@ -7,6 +7,7 @@ import { GeneralResponse } from '../../../model/generalResponse'
|
||||
import { BookServiceBasic } from '../ServiceBasic/bookServiceBasic'
|
||||
import { BookTask } from './bookTask'
|
||||
import fs from 'fs'
|
||||
import { Book } from '../../../model/book'
|
||||
|
||||
export class BookBasic {
|
||||
bookServiceBasic: BookServiceBasic
|
||||
@ -34,7 +35,7 @@ export class BookBasic {
|
||||
return res
|
||||
} catch (error) {
|
||||
return errorMessage(
|
||||
'修改数据错误,错误信息如下:' + error.message,
|
||||
'修改数据错误,错误信息如下:' + error.toString(),
|
||||
'BookBasic_AddOrModifyBook'
|
||||
)
|
||||
}
|
||||
@ -63,6 +64,32 @@ export class BookBasic {
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region 小说批次基础操作
|
||||
|
||||
/**
|
||||
* 通过小说ID和小说批次任务ID获取小说和小说批次任务数据
|
||||
* @param bookId 小说ID
|
||||
* @param bookTaskName 小说批次的名字,或者是小说批次任务的ID
|
||||
* @returns
|
||||
*/
|
||||
async GetBookAndTask(bookId: string, bookTaskName: string) {
|
||||
let book = await this.bookServiceBasic.GetBookDataById(bookId)
|
||||
// 获取小说对应的批次任务数据,默认初始化为第一个
|
||||
let condition = {
|
||||
bookId: bookId
|
||||
} as Book.QueryBookBackTaskCondition
|
||||
if (bookTaskName == "output_00001") {
|
||||
condition["name"] = bookTaskName
|
||||
} else {
|
||||
condition["id"] = bookTaskName
|
||||
}
|
||||
let bookTaskRes = await this.bookServiceBasic.GetBookTaskData(condition)
|
||||
return { book: book as Book.SelectBook, bookTask: bookTaskRes.bookTasks[0] as Book.SelectBookTask }
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
|
||||
//#region 小说相关操作
|
||||
|
||||
/**
|
||||
|
||||
@ -21,6 +21,7 @@ import { BookServiceBasic } from '../ServiceBasic/bookServiceBasic'
|
||||
import { SDOpt } from '../SD/sd'
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 一键反推的相关操作
|
||||
*/
|
||||
@ -33,6 +34,7 @@ export class ReverseBook {
|
||||
subtitle: Subtitle
|
||||
watermark: Watermark
|
||||
bookServiceBasic: BookServiceBasic
|
||||
bookBasic: BookBasic
|
||||
|
||||
constructor() {
|
||||
this.basicReverse = new BasicReverse()
|
||||
@ -41,6 +43,7 @@ export class ReverseBook {
|
||||
this.watermark = new Watermark()
|
||||
this.taskScheduler = new TaskScheduler()
|
||||
this.bookServiceBasic = new BookServiceBasic()
|
||||
this.bookBasic = new BookBasic()
|
||||
}
|
||||
// 主动返回前端的消息
|
||||
sendReturnMessage(data: GeneralResponse.MessageResponse, message_name = DEFINE_STRING.BOOK.GET_COPYWRITING_RETURN) {
|
||||
@ -189,32 +192,12 @@ export class ReverseBook {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过小说ID和小说批次任务ID获取小说和小说批次任务数据
|
||||
* @param bookId 小说ID
|
||||
* @param bookTaskName 小说批次的名字,或者是小说批次任务的ID
|
||||
* @returns
|
||||
*/
|
||||
async GetBookAndTask(bookId: string, bookTaskName: string) {
|
||||
let book = await this.bookServiceBasic.GetBookDataById(bookId)
|
||||
// 获取小说对应的批次任务数据,默认初始化为第一个
|
||||
let condition = {
|
||||
bookId: bookId
|
||||
} as Book.QueryBookBackTaskCondition
|
||||
if (bookTaskName == "output_00001") {
|
||||
condition["name"] = bookTaskName
|
||||
} else {
|
||||
condition["id"] = bookTaskName
|
||||
}
|
||||
let bookTaskRes = await this.bookServiceBasic.GetBookTaskData(condition)
|
||||
return { book: book as Book.SelectBook, bookTask: bookTaskRes.bookTasks[0] as Book.SelectBookTask }
|
||||
}
|
||||
/**
|
||||
* 开始分镜任务
|
||||
*/
|
||||
async ComputeStoryboard(bookId: string): Promise<any> {
|
||||
try {
|
||||
let { book, bookTask } = await this.GetBookAndTask(bookId, 'output_00001')
|
||||
let { book, bookTask } = await this.bookBasic.GetBookAndTask(bookId, 'output_00001')
|
||||
let res = await this.basicReverse.ComputeStoryboardFunc(bookId, bookTask.id);
|
||||
// 分镜成功直接返回
|
||||
return successMessage(null, res, "ReverseBook_ComputeStoryboard")
|
||||
@ -224,62 +207,6 @@ export class ReverseBook {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 开始进行分镜,切割视频并抽帧
|
||||
* @param bookId
|
||||
*/
|
||||
async Framing(bookId: string): Promise<GeneralResponse.ErrorItem | GeneralResponse.SuccessItem> {
|
||||
try {
|
||||
let { book, bookTask } = await this.GetBookAndTask(bookId, 'output_00001')
|
||||
let bookTaskDetail = undefined as Book.SelectBookTaskDetail[]
|
||||
try {
|
||||
// 获取所有的分镜数据
|
||||
bookTaskDetail = await this.bookServiceBasic.GetBookTaskDetailData({
|
||||
bookId: bookId,
|
||||
bookTaskId: bookTask.id
|
||||
})
|
||||
} catch (error) {
|
||||
// 传入的分镜数据为空,需要重新获取
|
||||
await this.taskScheduler.AddLogToDB(
|
||||
bookId,
|
||||
book.type,
|
||||
`没有传入分镜数据,请先进行分镜计算`,
|
||||
OtherData.DEFAULT,
|
||||
LoggerStatus.DOING
|
||||
)
|
||||
throw new Error("没有传入分镜数据,请先进行分镜计算");
|
||||
}
|
||||
let bookTaskDetails = bookTaskDetail;
|
||||
|
||||
for (let i = 0; i < bookTaskDetails.length; i++) {
|
||||
const item = bookTaskDetails[i];
|
||||
let res = await this.basicReverse.FrameDataToCutVideoData(item);
|
||||
}
|
||||
await this.taskScheduler.AddLogToDB(
|
||||
bookId,
|
||||
book.type,
|
||||
"所有的视频裁剪完成,开始抽帧",
|
||||
bookTask.id,
|
||||
LoggerStatus.SUCCESS
|
||||
)
|
||||
|
||||
bookTaskDetail = await this.bookServiceBasic.GetBookTaskDetailData({
|
||||
bookId: bookId,
|
||||
bookTaskId: bookTask.id
|
||||
})
|
||||
|
||||
bookTaskDetails = bookTaskDetail as Book.SelectBookTaskDetail[]
|
||||
for (let i = 0; i < bookTaskDetails.length; i++) {
|
||||
const item = bookTaskDetails[i];
|
||||
let res = await this.basicReverse.FrameFunc(book, item);
|
||||
}
|
||||
// 分镜成功直接返回
|
||||
return successMessage(null, "所有的视频裁剪,抽帧完成", "ReverseBook_Framing")
|
||||
|
||||
} catch (error) {
|
||||
return errorMessage("开始切割视频并抽帧失败,失败信息如下:" + error.message, 'ReverseBook_Framing')
|
||||
}
|
||||
}
|
||||
//#endregion
|
||||
|
||||
//#region 反推相关任务
|
||||
|
||||
@ -294,7 +294,7 @@ export class BasicReverse {
|
||||
}
|
||||
|
||||
// 找到对应的小说ID和对应的小说批次任务ID,判断是不是有分镜数据
|
||||
let bookTaskRes = await this.bookTaskService.GetBookTaskData({
|
||||
let bookTaskRes = this.bookTaskService.GetBookTaskData({
|
||||
bookId: bookId,
|
||||
name: 'output_00001'
|
||||
})
|
||||
@ -351,7 +351,7 @@ export class BasicReverse {
|
||||
for (let i = 0; i < bookTaskDetail.data.length; i++) {
|
||||
const element = bookTaskDetail.data[i]
|
||||
// 创建后台的视频分割任务
|
||||
let taskRes = await this.bookBackTaskListService.AddBookBackTask(
|
||||
let taskRes = this.bookBackTaskListService.AddBookBackTask(
|
||||
bookId,
|
||||
BookBackTaskType.SPLIT,
|
||||
TaskExecuteType.AUTO,
|
||||
@ -381,7 +381,7 @@ export class BasicReverse {
|
||||
* @param bookTaskDetailId 小说详细的分镜ID
|
||||
* @returns
|
||||
*/
|
||||
async FrameDataToCutVideoData(bookTaskDetail: Book.SelectBookTaskDetail): Promise<string> {
|
||||
async FrameDataToCutVideoData(bookTaskDetail: Book.SelectBookTaskDetail, frameShortClipData: Book.BookFrameShortClip): Promise<string> {
|
||||
await this.InitService()
|
||||
let startTime = bookTaskDetail.startTime
|
||||
let endTime = bookTaskDetail.endTime
|
||||
@ -402,9 +402,9 @@ export class BasicReverse {
|
||||
let outVideoFile = path.join(book.bookFolderPath, `data/frame/${bookTaskDetail.name}.mp4`)
|
||||
|
||||
let res = await this.ffmpegOptions.FfmpegCutVideo(
|
||||
startTime,
|
||||
endTime,
|
||||
book.oldVideoPath,
|
||||
frameShortClipData.startTime,
|
||||
frameShortClipData.endTime,
|
||||
frameShortClipData.videoPath,
|
||||
outVideoFile
|
||||
)
|
||||
if (res.code == 0) {
|
||||
@ -454,7 +454,7 @@ export class BasicReverse {
|
||||
throw new Error('没有找到对应的分镜数据')
|
||||
}
|
||||
|
||||
let cur_res = await this.FrameDataToCutVideoData(bookTaskDetail);
|
||||
// let cur_res = await this.FrameDataToCutVideoData(bookTaskDetail, null);
|
||||
|
||||
return successMessage(null, `${task.name}_视频裁剪完成`, "BasicReverse_CutVideoData");
|
||||
} catch (error) {
|
||||
|
||||
@ -6,13 +6,24 @@ import path from 'path';
|
||||
import { FfmpegOptions } from "../ffmpegOptions";
|
||||
import { CheckFileOrDirExist, CopyFileOrFolder, DeleteFolderAllFile } from "../../../define/Tools/file";
|
||||
import fs from 'fs';
|
||||
import { Book } from "../../../model/book";
|
||||
import { TaskScheduler } from '../taskScheduler';
|
||||
import { BookBasic } from "./BooKBasic";
|
||||
import { LoggerStatus, OtherData } from "../../../define/enum/softwareEnum";
|
||||
import { BasicReverse } from "./basicReverse";
|
||||
|
||||
export class BookFrame {
|
||||
bookServiceBasic: BookServiceBasic
|
||||
ffmpegOptions: FfmpegOptions
|
||||
taskScheduler: TaskScheduler
|
||||
basicReverse: BasicReverse
|
||||
bookBasic: BookBasic
|
||||
constructor() {
|
||||
this.bookServiceBasic = new BookServiceBasic();
|
||||
this.ffmpegOptions = new FfmpegOptions();
|
||||
this.taskScheduler = new TaskScheduler()
|
||||
this.bookBasic = new BookBasic()
|
||||
this.basicReverse = new BasicReverse()
|
||||
}
|
||||
|
||||
|
||||
@ -56,4 +67,130 @@ export class BookFrame {
|
||||
}
|
||||
}
|
||||
|
||||
//#region 进行分镜截取的一些操作
|
||||
|
||||
/**
|
||||
* 预处理视频,将视频切割成小段,减少计算时间
|
||||
* @param book 小说数据
|
||||
* @param bookTaskDetails 分镜数据
|
||||
*/
|
||||
async FrameShortClip(book: Book.SelectBook, bookTaskDetails: Book.SelectBookTaskDetail[], duration: number): Promise<Book.BookFrameShortClip[]> {
|
||||
let result = [] as Book.BookFrameShortClip[] // 返回的数据
|
||||
let durationTime = 0; // 小视频片段的持续时间
|
||||
let tempCount = 0;
|
||||
let videoPath = book.oldVideoPath + `_${tempCount}.mp4`; // 新的视频路径
|
||||
let startTime = 0; // 开始时间
|
||||
let endTime = 0; // 结束时间
|
||||
let lastEndTime = 0; // 上一个结束时间
|
||||
for (let i = 0; i < bookTaskDetails.length; i++) {
|
||||
const item = bookTaskDetails[i];
|
||||
let temRes = {
|
||||
startTime: item.startTime - lastEndTime,
|
||||
endTime: item.endTime - lastEndTime,
|
||||
videoPath: videoPath,
|
||||
duration: item.endTime - item.startTime
|
||||
}
|
||||
endTime = item.endTime;
|
||||
durationTime += item.endTime - item.startTime;
|
||||
if (durationTime > duration) { // 判断条件切割视频
|
||||
// 开始切割视频
|
||||
await this.ffmpegOptions.FfmpegCutVideo(
|
||||
startTime,
|
||||
endTime,
|
||||
book.oldVideoPath,
|
||||
videoPath
|
||||
)
|
||||
lastEndTime = item.endTime;
|
||||
tempCount++;
|
||||
durationTime = 0;
|
||||
startTime = endTime;
|
||||
endTime = 0;
|
||||
videoPath = book.oldVideoPath + `_${tempCount}.mp4`;
|
||||
}
|
||||
result.push(temRes)
|
||||
}
|
||||
// 最后一个也要切割
|
||||
if (durationTime > 0) {
|
||||
await this.ffmpegOptions.FfmpegCutVideo(
|
||||
startTime,
|
||||
endTime,
|
||||
book.oldVideoPath,
|
||||
videoPath
|
||||
)
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 开始进行分镜,切割视频并抽帧
|
||||
* @param bookId
|
||||
*/
|
||||
async Framing(bookId: string): Promise<GeneralResponse.ErrorItem | GeneralResponse.SuccessItem> {
|
||||
try {
|
||||
let { book, bookTask } = await this.bookBasic.GetBookAndTask(bookId, 'output_00001')
|
||||
let bookTaskDetail = undefined as Book.SelectBookTaskDetail[]
|
||||
try {
|
||||
// 获取所有的分镜数据
|
||||
bookTaskDetail = await this.bookServiceBasic.GetBookTaskDetailData({
|
||||
bookId: bookId,
|
||||
bookTaskId: bookTask.id
|
||||
})
|
||||
} catch (error) {
|
||||
// 传入的分镜数据为空,需要重新获取
|
||||
await this.taskScheduler.AddLogToDB(
|
||||
bookId,
|
||||
book.type,
|
||||
`没有传入分镜数据,请先进行分镜计算`,
|
||||
OtherData.DEFAULT,
|
||||
LoggerStatus.DOING
|
||||
)
|
||||
throw new Error("没有传入分镜数据,请先进行分镜计算");
|
||||
}
|
||||
let bookTaskDetails = bookTaskDetail;
|
||||
|
||||
// 这边重新开始计算,再次切割视频(预切割,减少长视频的计算时间)
|
||||
let shortClipData = await this.FrameShortClip(book, bookTaskDetails, 180000);
|
||||
console.log(shortClipData)
|
||||
|
||||
if (shortClipData.length != bookTaskDetails.length) {
|
||||
throw new Error('切割短视频和分镜数据不一致,请检查')
|
||||
}
|
||||
// 开始切割视频
|
||||
for (let i = 0; i < bookTaskDetails.length; i++) {
|
||||
const item = bookTaskDetails[i];
|
||||
if (!shortClipData[i]) {
|
||||
throw new Error('切割短视频和分镜数据不一致,请检查')
|
||||
}
|
||||
let res = await this.basicReverse.FrameDataToCutVideoData(item, shortClipData[i]);
|
||||
}
|
||||
await this.taskScheduler.AddLogToDB(
|
||||
bookId,
|
||||
book.type,
|
||||
"所有的视频裁剪完成,开始抽帧",
|
||||
bookTask.id,
|
||||
LoggerStatus.SUCCESS
|
||||
)
|
||||
|
||||
bookTaskDetail = await this.bookServiceBasic.GetBookTaskDetailData({
|
||||
bookId: bookId,
|
||||
bookTaskId: bookTask.id
|
||||
})
|
||||
|
||||
bookTaskDetails = bookTaskDetail as Book.SelectBookTaskDetail[]
|
||||
for (let i = 0; i < bookTaskDetails.length; i++) {
|
||||
const item = bookTaskDetails[i];
|
||||
let res = await this.basicReverse.FrameFunc(book, item);
|
||||
}
|
||||
// 分镜成功直接返回
|
||||
return successMessage(null, "所有的视频裁剪,抽帧完成", "ReverseBook_Framing")
|
||||
|
||||
} catch (error) {
|
||||
return errorMessage("开始切割视频并抽帧失败,失败信息如下:" + error.message, 'ReverseBook_Framing')
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//#endregion
|
||||
|
||||
}
|
||||
198
src/main/Service/Book/bookGeneral.ts
Normal file
198
src/main/Service/Book/bookGeneral.ts
Normal file
@ -0,0 +1,198 @@
|
||||
import { isEmpty } from "lodash";
|
||||
import { BookRepalceDataType } from "../../../define/enum/bookEnum";
|
||||
import { Book } from "../../../model/book";
|
||||
import { GeneralResponse } from "../../../model/generalResponse";
|
||||
import { errorMessage, successMessage } from "../../Public/generalTools";
|
||||
import { BookServiceBasic } from "../ServiceBasic/bookServiceBasic";
|
||||
|
||||
/**
|
||||
* 小说通用操作的类
|
||||
*/
|
||||
export class BookGeneral {
|
||||
bookServiceBasic: BookServiceBasic
|
||||
constructor() {
|
||||
this.bookServiceBasic = new BookServiceBasic()
|
||||
}
|
||||
|
||||
//#region 批量替换数据的操作
|
||||
|
||||
/**
|
||||
* 替换after_gpt 和 word 中的数据
|
||||
* @param bookTask
|
||||
* @param bookTaskDetail
|
||||
* @param replaceData
|
||||
*/
|
||||
ReplaceAfterGpt(bookTask: Book.SelectBook, bookTaskDetail: Book.SelectBookTaskDetail[], replaceData: Book.BookReplaceData): Book.ReplaceDataRes[] {
|
||||
let result = [];
|
||||
// 修改放在一个事务中
|
||||
this.bookServiceBasic.transaction((realm) => {
|
||||
for (let i = 0; i < bookTaskDetail.length; i++) {
|
||||
let element = bookTaskDetail[i];
|
||||
let afterGpt = element.afterGpt
|
||||
// 判断是否存在before的数据
|
||||
if (!afterGpt.includes(replaceData.before)) {
|
||||
continue
|
||||
}
|
||||
let newAfterGpt = afterGpt.replaceAll(replaceData.before, replaceData.after);
|
||||
let btd = realm.objectForPrimaryKey('BookTaskDetail', element.id)
|
||||
btd.afterGpt = newAfterGpt
|
||||
result.push({
|
||||
bookTaskDetailId: element.id,
|
||||
newData: newAfterGpt
|
||||
} as Book.ReplaceDataRes)
|
||||
}
|
||||
})
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
* 替换反推提示词中的数据
|
||||
* @param realm realm对象
|
||||
* @param bookTaskDetail 小说分镜数据
|
||||
* @param replaceData 替换的数据
|
||||
* @returns
|
||||
*/
|
||||
ReplaceRversePrompt(realm: any, bookTaskDetail: Book.SelectBookTaskDetail, replaceData: Book.BookReplaceData): Book.ReplaceDataRes[] {
|
||||
let result = [];
|
||||
if (bookTaskDetail.reversePrompt && bookTaskDetail.reversePrompt.length > 0) {
|
||||
for (let k = 0; k < bookTaskDetail.reversePrompt.length; k++) {
|
||||
const item = bookTaskDetail.reversePrompt[k];
|
||||
if (!isEmpty(item.prompt) && item.prompt.includes(replaceData.before)) {
|
||||
let newPrompt = item.prompt.replaceAll(replaceData.before, replaceData.after)
|
||||
let rsp = realm.objectForPrimaryKey("ReversePrompt", item.id)
|
||||
rsp.prompt = newPrompt
|
||||
result.push({
|
||||
bookTaskDetailId: bookTaskDetail.id,
|
||||
newData: newPrompt,
|
||||
type: 'reversePrompt',
|
||||
reversePromptType: 'prompt',
|
||||
reversePromptId: item.id
|
||||
} as Book.ReplaceDataRes)
|
||||
}
|
||||
if (!isEmpty(item.promptCN) && item.promptCN.includes(replaceData.before)) {
|
||||
let newPrompt = item.promptCN.replaceAll(replaceData.before, replaceData.after)
|
||||
let rsp = realm.objectForPrimaryKey("ReversePrompt", item.id)
|
||||
rsp.promptCN = newPrompt
|
||||
result.push({
|
||||
bookTaskDetailId: bookTaskDetail.id,
|
||||
newData: newPrompt,
|
||||
type: 'reversePrompt',
|
||||
reversePromptType: 'promptCN',
|
||||
reversePromptId: item.id
|
||||
} as Book.ReplaceDataRes)
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 替换反推提示词词中的数据
|
||||
* @param bookTask 小说任务
|
||||
* @param bookTaskDetail 小说详细任务
|
||||
* @param replaceData 替换的数据
|
||||
* @returns
|
||||
*/
|
||||
ReplaceGPTPrompt(bookTask: Book.SelectBookTask, bookTaskDetail: Book.SelectBookTaskDetail[], replaceData: Book.BookReplaceData): Book.ReplaceDataRes[] {
|
||||
let result = [];
|
||||
// 修改放在一个事务中
|
||||
this.bookServiceBasic.transaction((realm) => {
|
||||
for (let i = 0; i < bookTaskDetail.length; i++) {
|
||||
let element = bookTaskDetail[i];
|
||||
// 替换GPT提示词
|
||||
if (!isEmpty(element.gptPrompt) && element.gptPrompt.includes(replaceData.before)) {
|
||||
let newGptPrompt = element.gptPrompt.replaceAll(replaceData.before, replaceData.after)
|
||||
let btd = realm.objectForPrimaryKey('BookTaskDetail', element.id)
|
||||
btd.gptPrompt = newGptPrompt
|
||||
result.push({
|
||||
bookTaskDetailId: element.id,
|
||||
newData: newGptPrompt,
|
||||
type: 'gptPrompt'
|
||||
} as Book.ReplaceDataRes)
|
||||
}
|
||||
|
||||
if (replaceData.replaceAll) {
|
||||
// 替换反推提示词,有的话
|
||||
let res = this.ReplaceRversePrompt(realm, element, replaceData);
|
||||
result.push(...res)
|
||||
} else {
|
||||
if (!isEmpty(element.reversePrompt)) {
|
||||
continue;
|
||||
}
|
||||
// 这边处理反推提示词,有的话
|
||||
let res = this.ReplaceRversePrompt(realm, element, replaceData);
|
||||
result.push(...res)
|
||||
}
|
||||
|
||||
}
|
||||
})
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
* 替换生图提示词里面的数据
|
||||
* @param bookTask 小说批次任务
|
||||
* @param bookTaskDetail 小说分镜任务信息
|
||||
* @param replaceData 替换的数据信息
|
||||
*/
|
||||
ReplacePromptData(bookTask: Book.SelectBookTask, bookTaskDetail: Book.SelectBookTaskDetail[], replaceData: Book.BookReplaceData): Book.ReplaceDataRes[] {
|
||||
let res = []
|
||||
this.bookServiceBasic.transaction((realm) => {
|
||||
for (let i = 0; i < bookTaskDetail.length; i++) {
|
||||
const element = bookTaskDetail[i];
|
||||
let prompt = element.prompt
|
||||
if (isEmpty(prompt) || !prompt.includes(replaceData.before)) {
|
||||
continue
|
||||
}
|
||||
let newPrompt = prompt.replaceAll(replaceData.before, replaceData.after);
|
||||
let btd = realm.objectForPrimaryKey('BookTaskDetail', element.id)
|
||||
btd.prompt = newPrompt
|
||||
res.push({
|
||||
bookTaskDetailId: element.id,
|
||||
newData: newPrompt,
|
||||
type: 'prompt'
|
||||
} as Book.ReplaceDataRes)
|
||||
}
|
||||
})
|
||||
return res
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量替换小说中的一些数据,这个方法会根据传递的数据进行替换
|
||||
* @param bookTaskId 小说任务ID
|
||||
* @param replaceData 替换的数据
|
||||
*/
|
||||
async ReplaceBookData(bookTaskId: string, replaceData: Book.BookReplaceData): Promise<GeneralResponse.SuccessItem | GeneralResponse.ErrorItem> {
|
||||
try {
|
||||
console.log(bookTaskId, replaceData)
|
||||
let bookTask = await this.bookServiceBasic.GetBookTaskDataById(bookTaskId)
|
||||
let bookTaskDetails = await this.bookServiceBasic.GetBookTaskDetailData({
|
||||
bookTaskId: bookTaskId
|
||||
})
|
||||
if (bookTaskDetails.length <= 0) {
|
||||
throw new Error("未找到对应的小说任务批次任务数据");
|
||||
}
|
||||
let res = []
|
||||
// 根据type调用对应的方法
|
||||
switch (replaceData.type) {
|
||||
case BookRepalceDataType.AFTER_GPT:
|
||||
res = this.ReplaceAfterGpt(bookTask, bookTaskDetails, replaceData)
|
||||
break;
|
||||
case BookRepalceDataType.GPT_PROMPT:
|
||||
res = this.ReplaceGPTPrompt(bookTask, bookTaskDetails, replaceData)
|
||||
break;
|
||||
case BookRepalceDataType.PROMPT:
|
||||
res = this.ReplacePromptData(bookTask, bookTaskDetails, replaceData)
|
||||
break;
|
||||
default:
|
||||
throw new Error("未找到对应的替换类型");
|
||||
|
||||
}
|
||||
return successMessage(res, '替换所有的提示词数据成功', 'BookGeneral_ReplaceBookData')
|
||||
} catch (error) {
|
||||
return errorMessage('替换失败,错误信息如下:' + error.toString(), 'BookGeneral_ReplaceBookData')
|
||||
}
|
||||
}
|
||||
|
||||
//#endregion
|
||||
}
|
||||
@ -1,38 +1,232 @@
|
||||
import { CheckFolderExistsOrCreate, CopyFileOrFolder, DeleteFolderAllFile } from "../../../define/Tools/file";
|
||||
import { BookTaskService } from "../../../define/db/service/Book/bookTaskService";
|
||||
import { BookTaskDetailService } from "../../../define/db/service/Book/bookTaskDetailService";
|
||||
import { BookService } from "../../../define/db/service/Book/bookService";
|
||||
import { BookTaskStatus, CopyImageType, OperateBookType } from "../../../define/enum/bookEnum";
|
||||
import { CheckFileOrDirExist, CheckFolderExistsOrCreate, CopyFileOrFolder, DeleteFolderAllFile } from "../../../define/Tools/file";
|
||||
import { AddBookTaskCopyData, BookImageCategory, BookTaskStatus, CopyImageType, OperateBookType } from "../../../define/enum/bookEnum";
|
||||
import { errorMessage, successMessage } from "../../Public/generalTools";
|
||||
import { Book } from "../../../model/book";
|
||||
import path from 'path'
|
||||
import { cloneDeep, isEmpty } from "lodash";
|
||||
import { add, cloneDeep, isEmpty } from "lodash";
|
||||
import { define } from '../../../define/define'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
import { GeneralResponse } from "../../../model/generalResponse";
|
||||
import { BookServiceBasic } from "../ServiceBasic/bookServiceBasic";
|
||||
import { ValidateJson } from "../../../define/Tools/validate";
|
||||
import fs from 'fs'
|
||||
import { TimeStringToMilliseconds } from "../../../define/Tools/time";
|
||||
|
||||
/**
|
||||
* 小说批次相关的操作
|
||||
*/
|
||||
export class BookTask {
|
||||
bookTaskService: BookTaskService
|
||||
bookTaskDetailService: BookTaskDetailService
|
||||
bookService: BookService
|
||||
bookServiceBasic: BookServiceBasic
|
||||
constructor() {
|
||||
this.bookServiceBasic = new BookServiceBasic()
|
||||
}
|
||||
|
||||
//#region 添加小说批次任务
|
||||
|
||||
/**
|
||||
* 复制小说批次任务的基础数据
|
||||
* @param oldBookTaskDetail 需要需要复制的小说批次任务
|
||||
*/
|
||||
async CopyBookTaskDetailBaseData(bookTask: Book.SelectBookTask, newBookTask: Book.SelectBookTask, oldBookTaskDetail: Book.SelectBookTaskDetail[], addNewBookTask: Book.AddBookTask): Promise<Book.SelectBookTaskDetail[]> {
|
||||
let bookTaskDetail = [] as Book.SelectBookTaskDetail[]
|
||||
let book = await this.bookServiceBasic.GetBookDataById(bookTask.bookId)
|
||||
let originalTimePath = path.join(book.bookFolderPath, `data/${book.id}.mp4.json`);
|
||||
let originalTime = [] as any[];
|
||||
if (await CheckFileOrDirExist(originalTimePath)) {
|
||||
let originalTimeString = await fs.promises.readFile(originalTimePath, 'utf-8');
|
||||
if (ValidateJson(originalTimeString)) {
|
||||
originalTime = JSON.parse(originalTimeString)
|
||||
}
|
||||
}
|
||||
// 判断分镜数据和批次数据是不是相同的
|
||||
if (originalTime.length != oldBookTaskDetail.length) {
|
||||
originalTime = []
|
||||
}
|
||||
for (let i = 0; i < oldBookTaskDetail.length; i++) {
|
||||
const element = oldBookTaskDetail[i];
|
||||
let addOneBookTaskDetail = {
|
||||
id: uuidv4(),
|
||||
no: element.no,
|
||||
name: element.name,
|
||||
bookId: element.bookId,
|
||||
bookTaskId: newBookTask.id,
|
||||
videoPath: path.relative(define.project_path, element.videoPath),
|
||||
oldImage: path.relative(define.project_path, element.oldImage),
|
||||
adetailer: element.adetailer,
|
||||
sdConifg: element.sdConifg,
|
||||
createTime: new Date(),
|
||||
updateTime: new Date(),
|
||||
audioPath: element.audioPath,
|
||||
subtitlePosition: element.subtitlePosition,
|
||||
status: BookTaskStatus.WAIT,
|
||||
imageLock: false,
|
||||
} as Book.SelectBookTaskDetail
|
||||
|
||||
// 开始添加数据
|
||||
if (bookTask && addNewBookTask.selectTaskDataCategory && addNewBookTask.selectTaskDataCategory.length > 0) {
|
||||
// 有选择的数据,这边要调用各种方法
|
||||
if (addNewBookTask.selectTaskDataCategory.includes(AddBookTaskCopyData.AFTER_GPT)) {
|
||||
// 是不是要复制文案
|
||||
addOneBookTaskDetail.word = element.word
|
||||
addOneBookTaskDetail.afterGpt = element.afterGpt
|
||||
addOneBookTaskDetail.subValue = element.subValue ? JSON.stringify(element.subValue) : undefined
|
||||
addOneBookTaskDetail.oldImage = element.oldImage
|
||||
addOneBookTaskDetail.startTime = element.startTime
|
||||
addOneBookTaskDetail.endTime = element.endTime
|
||||
addOneBookTaskDetail.timeLimit = element.timeLimit;
|
||||
} else {
|
||||
// 初始化时间信息
|
||||
if (originalTime[i]) {
|
||||
addOneBookTaskDetail.startTime = TimeStringToMilliseconds(originalTime[i][0])
|
||||
addOneBookTaskDetail.endTime = TimeStringToMilliseconds(originalTime[i][1])
|
||||
addOneBookTaskDetail.timeLimit = undefined;
|
||||
}
|
||||
}
|
||||
if (addNewBookTask.selectTaskDataCategory.includes(AddBookTaskCopyData.GPT_PROMPT)) {
|
||||
// 是不是要复制GPT提示词
|
||||
addOneBookTaskDetail.gptPrompt = element.gptPrompt
|
||||
// 复制反推提示词
|
||||
// 处理反推数据
|
||||
let reverseMessage = [] as Book.ReversePrompt[]
|
||||
if (element.reversePrompt && element.reversePrompt.length > 0) {
|
||||
reverseMessage = cloneDeep(element.reversePrompt)
|
||||
for (let k = 0; k < reverseMessage.length; k++) {
|
||||
reverseMessage[k].id = uuidv4()
|
||||
reverseMessage[k].bookTaskDetailId = element.id
|
||||
}
|
||||
}
|
||||
addOneBookTaskDetail.reversePrompt = reverseMessage
|
||||
}
|
||||
if (addNewBookTask.selectTaskDataCategory.includes(AddBookTaskCopyData.CHARACTER)) {
|
||||
// 是不是要复制角色
|
||||
addOneBookTaskDetail.characterTags = element.characterTags
|
||||
}
|
||||
if (addNewBookTask.selectTaskDataCategory.includes(AddBookTaskCopyData.PROMPT)) {
|
||||
// 是不是要复制提示
|
||||
addOneBookTaskDetail.prompt = element.prompt
|
||||
}
|
||||
|
||||
}
|
||||
bookTaskDetail.push(addOneBookTaskDetail)
|
||||
}
|
||||
return bookTaskDetail
|
||||
}
|
||||
|
||||
/**
|
||||
* 复制一份小说任务的基础数据
|
||||
* @param bookTask 小说任务
|
||||
* @param addNewBookTask 新增的小说数据
|
||||
* @param no 当前的编号
|
||||
* @returns
|
||||
*/
|
||||
CopyBookTaskBaseData(bookTask: Book.SelectBookTask, addNewBookTask: Book.AddBookTask, no: number): Book.SelectBookTask {
|
||||
let name = 'output_' + no.toString().padStart(5, '0');
|
||||
let imageFolder = path.join(define.project_path, `${bookTask.bookId}/tmp/${name}`);
|
||||
let newBookTask = {
|
||||
id: uuidv4(),
|
||||
bookId: bookTask.bookId,
|
||||
no: no,
|
||||
name: name,
|
||||
generateVideoPath: undefined,
|
||||
srtPath: bookTask.srtPath,
|
||||
audioPath: bookTask.audioPath,
|
||||
imageFolder: path.relative(define.project_path, imageFolder),
|
||||
status: BookTaskStatus.WAIT,
|
||||
errorMsg: undefined,
|
||||
updateTime: new Date(),
|
||||
createTime: new Date(),
|
||||
isAuto: false,
|
||||
autoAnalyzeCharacter: undefined,
|
||||
imageStyle: [],
|
||||
customizeImageStyle: [],
|
||||
videoConfig: bookTask.videoConfig ??= undefined,
|
||||
prefixPrompt: addNewBookTask.prefixPrompt ??= undefined,
|
||||
suffixPrompt: addNewBookTask.suffixPrompt ?? undefined,
|
||||
imageCategory: bookTask.imageCategory ??= BookImageCategory.MJ,
|
||||
subImageFolder: [],
|
||||
draftSrtStyle: undefined,
|
||||
backgroundMusic: bookTask.backgroundMusic ??= undefined,
|
||||
friendlyReminder: bookTask.friendlyReminder ??= undefined,
|
||||
} as Book.SelectBookTask
|
||||
if (addNewBookTask.selectTaskDataCategory && addNewBookTask.selectTaskDataCategory.includes(AddBookTaskCopyData.IMAGE_STYLE)) {
|
||||
newBookTask.imageStyle = bookTask.imageStyle ??= []
|
||||
newBookTask.customizeImageStyle = bookTask.customizeImageStyle ??= []
|
||||
}
|
||||
return newBookTask
|
||||
}
|
||||
|
||||
CopyCopywrittingData() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加一个小说批次任务
|
||||
* @param bookTask 小说任务
|
||||
* @param bookTaskDetails 小说任务分镜信息
|
||||
* @param addNewBookTask 添加数据的基础数据信息
|
||||
* @returns
|
||||
*/
|
||||
async AddOneBookTask(bookTask: Book.SelectBookTask, bookTaskDetails: Book.SelectBookTaskDetail[], addNewBookTask: Book.AddBookTask, no: number): Promise<{ newBookTask: Book.SelectBookTask, newBookTaskDetails: Book.SelectBookTaskDetail[] }> {
|
||||
let newBookTask = this.CopyBookTaskBaseData(bookTask, addNewBookTask, no)
|
||||
let newBookTaskDetails = await this.CopyBookTaskDetailBaseData(bookTask, newBookTask, bookTaskDetails, addNewBookTask)
|
||||
return {
|
||||
newBookTask: newBookTask,
|
||||
newBookTaskDetails: newBookTaskDetails
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
async InitService() {
|
||||
if (!this.bookTaskService) {
|
||||
this.bookTaskService = await BookTaskService.getInstance()
|
||||
}
|
||||
if (!this.bookTaskDetailService) {
|
||||
this.bookTaskDetailService = await BookTaskDetailService.getInstance()
|
||||
} if (!this.bookService) {
|
||||
this.bookService = await BookService.getInstance()
|
||||
|
||||
/**
|
||||
* 添加个新的小说批次任务
|
||||
* @param AddNewBookTask 添加的小说任务数据
|
||||
*/
|
||||
async AddNewBookTask(addNewBookTask: Book.AddBookTask): Promise<GeneralResponse.SuccessItem | GeneralResponse.ErrorItem> {
|
||||
try {
|
||||
console.log(addNewBookTask)
|
||||
let bookTask = await this.bookServiceBasic.GetBookTaskDataById(addNewBookTask.selectBookTask)
|
||||
let oldBookTaskDetail = await this.bookServiceBasic.GetBookTaskDetailData({
|
||||
bookTaskId: addNewBookTask.selectBookTask
|
||||
})
|
||||
let bookTasks = [] as Book.SelectBookTask[]
|
||||
let bookTaskDetail = [] as Book.SelectBookTaskDetail[]
|
||||
|
||||
let maxNo = await this.bookServiceBasic.GetMaxBookTaskNo(bookTask.bookId)
|
||||
for (let i = 0; i < addNewBookTask.count; i++) {
|
||||
if (addNewBookTask.copyBookTask) {
|
||||
let { newBookTask, newBookTaskDetails } = await this.AddOneBookTask(bookTask, oldBookTaskDetail, addNewBookTask, maxNo + i)
|
||||
bookTasks.push(newBookTask)
|
||||
bookTaskDetail.push(...newBookTaskDetails)
|
||||
} else {
|
||||
let newBookTask = this.CopyBookTaskBaseData({ bookId: bookTask.bookId }, addNewBookTask, maxNo + i)
|
||||
bookTasks.push(newBookTask);
|
||||
}
|
||||
}
|
||||
// 先要创建文件夹
|
||||
for (let i = 0; i < bookTasks.length; i++) {
|
||||
let imageFolder = path.join(define.project_path, bookTasks[i].imageFolder)
|
||||
await CheckFolderExistsOrCreate(imageFolder)
|
||||
}
|
||||
|
||||
// 这边开始添加数据
|
||||
this.bookServiceBasic.transaction((realm) => {
|
||||
for (let i = 0; i < bookTasks.length; i++) {
|
||||
const element = bookTasks[i];
|
||||
realm.create('BookTask', element)
|
||||
}
|
||||
for (let i = 0; i < bookTaskDetail.length; i++) {
|
||||
const element = bookTaskDetail[i];
|
||||
realm.create('BookTaskDetail', element)
|
||||
}
|
||||
})
|
||||
return successMessage(null, "添加小说批次任务成功", "BookTask_AddNewBookTask")
|
||||
} catch (error) {
|
||||
return errorMessage('添加小说批次任务失败,错误信息如下' + error.toString(), "BookTask_AddNewBookTask");
|
||||
}
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
|
||||
/**
|
||||
* 重置小说任务数据
|
||||
@ -40,13 +234,8 @@ export class BookTask {
|
||||
*/
|
||||
async ReSetBookTask(bookTaskId: string): Promise<GeneralResponse.ErrorItem | GeneralResponse.SuccessItem> {
|
||||
try {
|
||||
console.log(bookTaskId)
|
||||
await this.InitService()
|
||||
let bookTask = this.bookTaskService.GetBookTaskDataById(bookTaskId);
|
||||
if (bookTask == null) {
|
||||
throw new Error('未找到对应的小说任务')
|
||||
}
|
||||
this.bookTaskService.ResetBookTask(bookTaskId);
|
||||
let bookTask = await this.bookServiceBasic.GetBookTaskDataById(bookTaskId);
|
||||
await this.bookServiceBasic.ResetBookTask(bookTaskId);
|
||||
// 数据库删除完毕,看是删除对应的文件(主要是图片)
|
||||
let imageFolder = bookTask.imageFolder;
|
||||
// 整个删掉在重建
|
||||
@ -64,14 +253,10 @@ export class BookTask {
|
||||
*/
|
||||
async DeleteBookTask(bookTaskId: string): Promise<GeneralResponse.ErrorItem | GeneralResponse.SuccessItem> {
|
||||
try {
|
||||
await this.InitService();
|
||||
let bookTask = this.bookTaskService.GetBookTaskDataById(bookTaskId);
|
||||
if (bookTask == null) {
|
||||
throw new Error('未找到对应的小说任务,无法执行删除操作')
|
||||
}
|
||||
let bookTask = await this.bookServiceBasic.GetBookTaskDataById(bookTaskId);
|
||||
let imageFolder = bookTask.imageFolder;
|
||||
// 先删除每个批次对应的数据,然后删除批次
|
||||
this.bookTaskService.DeleteBookTask(bookTaskId);
|
||||
await this.bookServiceBasic.DeleteBookTaskData(bookTaskId);
|
||||
// 删除成功,直接把对应的出图文件夹删掉
|
||||
await DeleteFolderAllFile(imageFolder, true)
|
||||
return successMessage(null, "删除小说批次数据成功", "BookTask_DeleteBookTask")
|
||||
@ -87,17 +272,15 @@ export class BookTask {
|
||||
*/
|
||||
async OneToFourBookTask(bookTaskId: string) {
|
||||
try {
|
||||
console.log(bookTaskId)
|
||||
await this.InitService();
|
||||
let copyCount = 100
|
||||
let bookTask = this.bookTaskService.GetBookTaskDataById(bookTaskId)
|
||||
let bookTask = await this.bookServiceBasic.GetBookTaskDataById(bookTaskId)
|
||||
if (bookTask == null) {
|
||||
throw new Error("没有找到对应的数小说任务,请检查数据")
|
||||
}
|
||||
// 获取所有的出图中最少的
|
||||
let bookTaskDetail = this.bookTaskDetailService.GetBookTaskData({
|
||||
let bookTaskDetail = (await this.bookServiceBasic.GetBookTaskData({
|
||||
bookTaskId: bookTaskId
|
||||
}).data as Book.SelectBookTaskDetail[]
|
||||
})).bookTasks as Book.SelectBookTaskDetail[]
|
||||
if (bookTaskDetail == null || bookTaskDetail.length <= 0) {
|
||||
throw new Error("没有对应的小说分镜任务,请先添加分镜任务")
|
||||
}
|
||||
@ -136,17 +319,12 @@ export class BookTask {
|
||||
*/
|
||||
async CopyNewBookTask(sourceBookTask: Book.SelectBookTask, sourceBookTaskDetail: Book.SelectBookTaskDetail[], copyCount: number, copyImageType: CopyImageType) {
|
||||
try {
|
||||
await this.InitService();
|
||||
let addBookTask = [] as Book.SelectBookTask[]
|
||||
let addBookTaskDetail = [] as Book.SelectBookTaskDetail[]
|
||||
|
||||
// 先处理文件夹的创建,包括小说任务的和小说任务分镜的
|
||||
for (let i = 0; i < copyCount; i++) {
|
||||
let maxNo = this.bookTaskService.realm
|
||||
.objects('BookTask')
|
||||
.filtered('bookId = $0', sourceBookTask.bookId)
|
||||
.max('no')
|
||||
let no = maxNo == null ? 1 : Number(maxNo) + 1 + i
|
||||
let no = await this.bookServiceBasic.GetMaxBookTaskNo(sourceBookTask.bookId)
|
||||
let name = 'output_0000' + no
|
||||
let imageFolder = path.join(define.project_path, `${sourceBookTask.bookId}/tmp/${name}`)
|
||||
await CheckFolderExistsOrCreate(imageFolder)
|
||||
@ -265,21 +443,21 @@ export class BookTask {
|
||||
|
||||
// 数据处理完毕,开始新增数据
|
||||
// 将所有的复制才做,全部放在一个事务中
|
||||
this.bookTaskService.transaction(() => {
|
||||
this.bookServiceBasic.transaction((realm) => {
|
||||
for (let i = 0; i < addBookTask.length; i++) {
|
||||
const element = addBookTask[i];
|
||||
this.bookTaskService.realm.create('BookTask', element)
|
||||
realm.create('BookTask', element)
|
||||
}
|
||||
for (let i = 0; i < addBookTaskDetail.length; i++) {
|
||||
const element = addBookTaskDetail[i];
|
||||
this.bookTaskDetailService.realm.create('BookTaskDetail', element)
|
||||
realm.create('BookTaskDetail', element)
|
||||
}
|
||||
})
|
||||
// 全部创建完成
|
||||
// 查找到数据,然后全部返回
|
||||
let returnBookTask = this.bookTaskService.GetBookTaskData({
|
||||
let returnBookTask = (await this.bookServiceBasic.GetBookTaskData({
|
||||
bookId: sourceBookTask.bookId
|
||||
}).data as Book.SelectBookTask[]
|
||||
})).bookTasks as Book.SelectBookTask[]
|
||||
|
||||
return successMessage(returnBookTask, "复制小说任务成功", "BookBasic_CopyNewBookTask")
|
||||
} catch (error) {
|
||||
|
||||
@ -5,7 +5,7 @@ import { BookBackTaskListService } from "../../../define/db/service/Book/bookBac
|
||||
import { BookTaskDetailService } from "../../../define/db/service/Book/bookTaskDetailService";
|
||||
import { CheckFolderExistsOrCreate, CopyFileOrFolder, JoinPath } from "../../../define/Tools/file";
|
||||
import { define } from "../../../define/define"
|
||||
import { GetImageBase64, ImageSplit } from "../../../define/Tools/image";
|
||||
import { CompressImageToSize, GetImageBase64, ImageSplit } from "../../../define/Tools/image";
|
||||
import MJApi from "./mjApi"
|
||||
import { BookBackTaskStatus, BookBackTaskType, BookTaskStatus, BookType, DialogType, MJAction, OperateBookType, TaskExecuteType } from "../../../define/enum/bookEnum";
|
||||
import { DEFINE_STRING } from "../../../define/define_string";
|
||||
@ -214,10 +214,16 @@ export class MJOpt {
|
||||
throw new Error(`${bookTaskDetail.name} 没有需要反推的图片`);
|
||||
}
|
||||
oldImagePath = JoinPath(define.project_path, oldImagePath)
|
||||
let imageBase64 = await GetImageBase64(oldImagePath)
|
||||
// let imageBase64 = await GetImageBase64(oldImagePath)
|
||||
|
||||
// 每次执行前压缩图片
|
||||
let newImageBase64 = await CompressImageToSize(oldImagePath, 500000);
|
||||
// 将buffer转换为base64
|
||||
let imageBase64 = newImageBase64.toString('base64');
|
||||
|
||||
// 这个就是任务ID
|
||||
let reqRes = await this.mjApi.SubmitMJDescribe({
|
||||
image: imageBase64,
|
||||
image: 'data:image/png;base64,' + imageBase64,
|
||||
taskId: task.id
|
||||
})
|
||||
if (reqRes == '23') {
|
||||
@ -394,8 +400,8 @@ export class MJOpt {
|
||||
if (bookTask.prefixPrompt) {
|
||||
promptStr = checkStringValueAddSuffix(bookTask.prefixPrompt, ',') + promptStr
|
||||
}
|
||||
if (bookTask.prefixPrompt) {
|
||||
promptStr = checkStringValueAddSuffix(promptStr, ',') + bookTask.prefixPrompt
|
||||
if (bookTask.suffixPrompt) {
|
||||
promptStr = checkStringValueAddSuffix(promptStr, ',') + bookTask.suffixPrompt
|
||||
}
|
||||
promptStr = ' ' + promptStr;
|
||||
promptStr += ` ${cref_url} ${style_url}${suffixParam}`
|
||||
|
||||
@ -1,11 +1,8 @@
|
||||
import { Book } from "../../../model/book";
|
||||
import { GeneralResponse } from "../../../model/generalResponse";
|
||||
import { checkStringValueAddSuffix, errorMessage, successMessage } from "../../Public/generalTools";
|
||||
import { BookTaskDetailService } from "../../../define/db/service/Book/bookTaskDetailService";
|
||||
import { BookTaskService } from "../../../define/db/service/Book/bookTaskService";
|
||||
import { define } from '../../../define/define'
|
||||
import fs from "fs";
|
||||
import { ImageStyleDefine } from "../../../define/iamgeStyleDefine";
|
||||
import { ImageStyle } from "../Book/imageStyle";
|
||||
import { OperateBookType } from "../../../define/enum/bookEnum";
|
||||
import { isEmpty } from "lodash";
|
||||
@ -13,26 +10,22 @@ import { BookServiceBasic } from "../ServiceBasic/bookServiceBasic";
|
||||
const fspromise = fs.promises
|
||||
|
||||
export class SDOpt {
|
||||
bookTaskDetailService: BookTaskDetailService
|
||||
bookTaskService: BookTaskService
|
||||
imageStyle: ImageStyle
|
||||
|
||||
bookServiceBasic: BookServiceBasic
|
||||
constructor() {
|
||||
this.bookServiceBasic = new BookServiceBasic()
|
||||
this.imageStyle = new ImageStyle()
|
||||
}
|
||||
|
||||
|
||||
async InitService() {
|
||||
if (!this.bookTaskDetailService) {
|
||||
this.bookTaskDetailService = await BookTaskDetailService.getInstance()
|
||||
}
|
||||
if (!this.bookTaskService) {
|
||||
this.bookTaskService = await BookTaskService.getInstance()
|
||||
}
|
||||
if (!this.imageStyle) {
|
||||
this.imageStyle = new ImageStyle()
|
||||
}
|
||||
// TODO 这边的设置应该改为数据库
|
||||
/**
|
||||
* 获取SD的设置,之后要删掉,改为数据库
|
||||
*/
|
||||
private async GetSDSetting() {
|
||||
let sdSetting = JSON.parse(await fspromise.readFile(define.sd_setting, 'utf-8'))
|
||||
return sdSetting
|
||||
}
|
||||
|
||||
/**
|
||||
@ -43,15 +36,16 @@ export class SDOpt {
|
||||
*/
|
||||
async MergePrompt(id: string, operateBookType: OperateBookType): Promise<GeneralResponse.ErrorItem | GeneralResponse.SuccessItem> {
|
||||
try {
|
||||
await this.InitService()
|
||||
let bookTaskDetail = undefined as Book.SelectBookTaskDetail[];
|
||||
let bookTask = undefined as Book.SelectBookTask;
|
||||
let sd_setting = await this.GetSDSetting();
|
||||
let style_weight = sd_setting.setting.style_weight ? sd_setting.setting.style_weight : 1
|
||||
|
||||
if (operateBookType == OperateBookType.BOOKTASK) {
|
||||
bookTaskDetail = this.bookTaskDetailService.GetBookTaskData({
|
||||
bookTaskDetail = (await this.bookServiceBasic.GetBookTaskData({
|
||||
bookTaskId: id
|
||||
}).data
|
||||
bookTask = this.bookTaskService.GetBookTaskDataById(id);
|
||||
})).bookTasks;
|
||||
bookTask = await this.bookServiceBasic.GetBookTaskDataById(id);
|
||||
// 判断是不是有为空的
|
||||
let emptyName = [] as string[]
|
||||
for (let i = 0; i < bookTaskDetail.length; i++) {
|
||||
@ -69,7 +63,7 @@ export class SDOpt {
|
||||
throw new Error("当前分镜没有推理提示词,请先生成")
|
||||
}
|
||||
bookTaskDetail = [tempBookTaskDetail];
|
||||
bookTask = this.bookTaskService.GetBookTaskDataById(bookTaskDetail[0].bookTaskId);
|
||||
bookTask = await this.bookServiceBasic.GetBookTaskDataById(bookTaskDetail[0].bookTaskId);
|
||||
} else {
|
||||
throw new Error("未知的合并类型")
|
||||
}
|
||||
@ -86,12 +80,12 @@ export class SDOpt {
|
||||
for (let i = 0; i < styleArr.length; i++) {
|
||||
const element = styleArr[i]
|
||||
if (element.type == 'style_main') {
|
||||
styleString += element.prompt + ', '
|
||||
styleString += `(${element.prompt}:${style_weight})` + ', '
|
||||
if (element.lora && element.lora != '无' && element.lora_weight) {
|
||||
styleString += `<lora:${element.lora}:${element.lora_weight}>, `
|
||||
}
|
||||
} else {
|
||||
styleString += element.english_style + ','
|
||||
styleString += `(${element.english_style}:${style_weight})` + ','
|
||||
}
|
||||
}
|
||||
|
||||
@ -126,8 +120,8 @@ export class SDOpt {
|
||||
if (bookTask.prefixPrompt) {
|
||||
promptStr = checkStringValueAddSuffix(bookTask.prefixPrompt, ',') + promptStr
|
||||
}
|
||||
if (bookTask.prefixPrompt) {
|
||||
promptStr = checkStringValueAddSuffix(promptStr, ',') + bookTask.prefixPrompt
|
||||
if (bookTask.suffixPrompt) {
|
||||
promptStr = checkStringValueAddSuffix(promptStr, ',') + bookTask.suffixPrompt
|
||||
}
|
||||
if (sdGlobalPrompt) {
|
||||
promptStr = checkStringValueAddSuffix(sdGlobalPrompt, ',') + promptStr
|
||||
@ -136,7 +130,7 @@ export class SDOpt {
|
||||
promptStr = ' ' + promptStr;
|
||||
console.log(promptStr)
|
||||
// 修改数据库数据
|
||||
this.bookTaskDetailService.UpdateBookTaskDetail(element.id, {
|
||||
await this.bookServiceBasic.UpdateBookTaskDetail(element.id, {
|
||||
prompt: promptStr
|
||||
})
|
||||
// 写回数据
|
||||
@ -147,7 +141,6 @@ export class SDOpt {
|
||||
}
|
||||
|
||||
return successMessage(result, "SD和并提示词数据成功", "SDOpt_MergePrompt")
|
||||
|
||||
} catch (error) {
|
||||
return errorMessage("SD合并提示词,错误信息如下:" + error.toString(), "SDOpt_MergePrompt")
|
||||
}
|
||||
|
||||
@ -42,6 +42,16 @@ export class BookServiceBasic {
|
||||
global.newWindow[0].win.webContents.send(message_name, data)
|
||||
}
|
||||
|
||||
|
||||
//#region 事务操作
|
||||
transaction(callback: (realm: any) => void) {
|
||||
this.bookService.transaction(() => {
|
||||
callback(this.bookService.realm)
|
||||
})
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region 小说相关的基础服务
|
||||
|
||||
/**
|
||||
@ -111,6 +121,20 @@ export class BookServiceBasic {
|
||||
return bookTasks.data
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取最大的小说批次任务的编号
|
||||
* @param bookId 小说ID
|
||||
*/
|
||||
async GetMaxBookTaskNo(bookId: string): Promise<number> {
|
||||
await this.InitService();
|
||||
let maxNo = this.bookTaskService.realm
|
||||
.objects('BookTask')
|
||||
.filtered('bookId = $0', bookId)
|
||||
.max('no')
|
||||
let no = maxNo == null ? 1 : Number(maxNo) + 1
|
||||
return no
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新小说批次任务的数据
|
||||
* @param bookTaskId 小说批次任务ID
|
||||
@ -121,6 +145,15 @@ export class BookServiceBasic {
|
||||
this.bookTaskService.UpdetedBookTaskData(bookTaskId, data)
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置小说批次任务的数据
|
||||
* @param bookTaskId 指定的重置的小说批次任务的ID
|
||||
*/
|
||||
async ResetBookTask(bookTaskId: string): Promise<void> {
|
||||
await this.InitService();
|
||||
this.bookTaskService.ResetBookTask(bookTaskId)
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除指定的小说批次任务的数据
|
||||
* @param bookTaskId 需要删除的指定的小说批次任务ID
|
||||
|
||||
@ -12,7 +12,7 @@ import { CheckFileOrDirExist } from "../../../define/Tools/file";
|
||||
import { BookServiceBasic } from "../ServiceBasic/bookServiceBasic";
|
||||
import { Subtitle } from "./subtitle";
|
||||
import { TaskScheduler } from "../taskScheduler";
|
||||
import { OperateBookType } from "../../../define/enum/bookEnum";
|
||||
import { BookType, OperateBookType } from "../../../define/enum/bookEnum";
|
||||
import { Book } from "../../../model/book";
|
||||
import { TimeStringToMilliseconds } from "../../../define/Tools/time";
|
||||
|
||||
@ -189,8 +189,9 @@ export class SubtitleService {
|
||||
return errorMessage("获取分镜数据失败,失败信息如下:" + error.message, 'ReverseBook_GetCopywriting')
|
||||
}
|
||||
}
|
||||
//#endregion
|
||||
|
||||
|
||||
//#region 文案相关的操作
|
||||
/**
|
||||
* 导出指定导出指定小说的文案
|
||||
* @param bookTaskId 小说批次任务ID
|
||||
@ -231,7 +232,57 @@ export class SubtitleService {
|
||||
}
|
||||
}
|
||||
|
||||
//#region 文案相关的操作
|
||||
/**
|
||||
* 将
|
||||
* @param bookId
|
||||
* @param bookTaskId
|
||||
* @param txtPath
|
||||
* @returns
|
||||
*/
|
||||
async ImportCopywriting(bookId: string, bookTaskId: string, txtPath: string): Promise<GeneralResponse.ErrorItem | GeneralResponse.SuccessItem> {
|
||||
try {
|
||||
let word = await fs.promises.readFile(txtPath, 'utf-8');
|
||||
let wordLines = word.split('\n').map(line => line.trim()).filter(line => line.length > 0);
|
||||
// 判断小说类型,是反推的话,文案和分镜数据要对应
|
||||
let book = await this.bookServiceBasic.GetBookDataById(bookId)
|
||||
let bookTaskDetail = await this.bookServiceBasic.GetBookTaskDetailData({
|
||||
bookId: bookId,
|
||||
bookTaskId: bookTaskId
|
||||
})
|
||||
|
||||
if (book.type == BookType.MJ_REVERSE || book.type == BookType.SD_REVERSE) {
|
||||
if (wordLines.length != bookTaskDetail.length) {
|
||||
throw new Error("文案行数和分镜数据不对应,请检查")
|
||||
}
|
||||
} else if (book.type == BookType.ORIGINAL) {
|
||||
// 原创这边也要做些判断,待定
|
||||
throw new Error("原创小说暂时不支持导入文案");
|
||||
} else {
|
||||
throw new Error("未知的小说类型,请检查")
|
||||
}
|
||||
|
||||
let result = []
|
||||
// 这边开始导入文案,这边使用事务
|
||||
this.bookServiceBasic.transaction((realm) => {
|
||||
for (let i = 0; i < bookTaskDetail.length; i++) {
|
||||
const element = bookTaskDetail[i];
|
||||
let btd = realm.objectForPrimaryKey("BookTaskDetail", element.id);
|
||||
let ag = wordLines[i] ? wordLines[i] : ''
|
||||
|
||||
btd.afterGpt = ag
|
||||
result.push({
|
||||
bookTaskDetailId: element.id,
|
||||
afterGpt: ag
|
||||
});
|
||||
}
|
||||
})
|
||||
|
||||
return successMessage(result, "导入文案成功", "ReverseBook_ImportCopywriting")
|
||||
} catch (error) {
|
||||
return errorMessage("导入文案失败,失败信息如下:" + error.message, 'ReverseBook_ImportCopywriting')
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 清除导入的对齐后的文案
|
||||
@ -287,6 +338,4 @@ export class SubtitleService {
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
//#endregion
|
||||
}
|
||||
@ -80,7 +80,7 @@ export class FfmpegOptions {
|
||||
return new Promise((resolve, reject) => {
|
||||
Ffmpeg(tempVideo)
|
||||
.outputOptions([
|
||||
`-vf scale=${width}:${height}`, // 调整视频尺寸到 1280x720
|
||||
`-vf scale=${Math.floor(width)}:${Math.floor(height)}`, // 调整视频尺寸到 1280x720
|
||||
`-b:v ${crf}` // 设置视频比特率为 1000kbps
|
||||
])
|
||||
.on('end', async () => {
|
||||
|
||||
@ -66,12 +66,12 @@ async function createWindow(hash = 'ShowMessage', data, url = null) {
|
||||
webPreferences: {
|
||||
preload: join(__dirname, '../preload/index.js'),
|
||||
sandbox: false,
|
||||
nodeIntegration: hash == 'discord' ? false : true, // 在网页中集成Node
|
||||
nodeIntegration: true, // 在网页中集成Node
|
||||
nodeIntegrationInWorker: true,
|
||||
webSecurity: false,
|
||||
partition: 'persist:my-partition',
|
||||
session: ses,
|
||||
webviewTag : true,
|
||||
webviewTag: true
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
41
src/model/book.d.ts
vendored
41
src/model/book.d.ts
vendored
@ -1,4 +1,4 @@
|
||||
import { BookBackTaskStatus, BookBackTaskType, BookTaskStatus, BookType, TaskExecuteType } from "../define/enum/bookEnum"
|
||||
import { BookBackTaskStatus, BookBackTaskType, BookTaskStatus, BookType, TaskExecuteType, BookRepalceDataType } from "../define/enum/bookEnum"
|
||||
import { MJAction } from "../define/enum/bookEnum"
|
||||
import { MJImageType } from "../define/enum/mjEnum"
|
||||
|
||||
@ -76,6 +76,16 @@ declare namespace Book {
|
||||
subImageFolder?: string[] | null // 子图片文件夹地址,多个
|
||||
}
|
||||
|
||||
// 添加批次任务
|
||||
type AddBookTask = {
|
||||
copyBookTask: boolean // 是否复制旧的小说任务
|
||||
count: number // 批次数量
|
||||
prefixPrompt?: string // 前缀提示
|
||||
suffixPrompt?: string // 后缀提示词
|
||||
selectBookTask?: string // 选择的旧的小说任务
|
||||
selectTaskDataCategory?: string[] // 选择复制的数据类型
|
||||
}
|
||||
|
||||
// 字幕相关
|
||||
type Subtitle = {
|
||||
startTime: number
|
||||
@ -246,4 +256,33 @@ declare namespace Book {
|
||||
id?: string
|
||||
image?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* 裁剪小视频的返回数据类型
|
||||
*/
|
||||
type BookFrameShortClip = {
|
||||
startTime: number
|
||||
endTime: number
|
||||
videoPath: string
|
||||
duration: number
|
||||
}
|
||||
|
||||
//#region 替换小说数据
|
||||
|
||||
type BookReplaceData = {
|
||||
before: string,
|
||||
after: string,
|
||||
replaceAll?: boolean,
|
||||
type: BookRepalceDataType
|
||||
}
|
||||
|
||||
type ReplaceDataRes = {
|
||||
bookTaskDetailId: string,
|
||||
newData: string,
|
||||
type?: 'reversePrompt' | 'gptPrompt' | 'afterGpt' | 'prompt',
|
||||
reversePromptType?: 'prompt' | 'promptCN'
|
||||
reversePromptId?: string
|
||||
}
|
||||
|
||||
//#endregion
|
||||
}
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import { ipcRenderer } from 'electron'
|
||||
import { DEFINE_STRING } from '../define/define_string'
|
||||
import { Book } from '../model/book'
|
||||
|
||||
const book = {
|
||||
// 获取小说操作类型(原创/SD反推/MJ反推)
|
||||
@ -51,6 +52,18 @@ const book = {
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region 小说通用操作
|
||||
|
||||
// 替换文案数据
|
||||
ReplaceBookData: async (bookTaskId: string, replaceData: Book.BookReplaceData) =>
|
||||
await ipcRenderer.invoke(
|
||||
DEFINE_STRING.BOOK.REPLACE_BOOK_DATA,
|
||||
bookTaskId,
|
||||
replaceData
|
||||
),
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region 分镜
|
||||
|
||||
// 开始计算分镜数据
|
||||
@ -85,6 +98,10 @@ const book = {
|
||||
ExportCopywriting: async (bookTaskId) =>
|
||||
await ipcRenderer.invoke(DEFINE_STRING.BOOK.EXPORT_COPYWRITING, bookTaskId),
|
||||
|
||||
// 导入文案,修改过后的
|
||||
ImportCopywriting: async (bookId: string, bookTaskId: string, txtPath: string) =>
|
||||
await ipcRenderer.invoke(DEFINE_STRING.BOOK.IMPORT_COPYWRITING, bookId, bookTaskId, txtPath),
|
||||
|
||||
// 清除导入对齐的文案
|
||||
ClearImportWord: async (bookTaskId) =>
|
||||
await ipcRenderer.invoke(DEFINE_STRING.BOOK.CLEAR_IMPORT_WORD, bookTaskId),
|
||||
@ -183,6 +200,10 @@ const book = {
|
||||
|
||||
//#region 小说批次任务相关
|
||||
|
||||
// 添加新的小说批次任务
|
||||
AddNewBookTask: async (addBookTaskData) =>
|
||||
await ipcRenderer.invoke(DEFINE_STRING.BOOK.ADD_NEW_BOOK_TASK, addBookTaskData),
|
||||
|
||||
// 重置小说批次数据
|
||||
ReSetBookTask: async (bookTaskId) =>
|
||||
await ipcRenderer.invoke(DEFINE_STRING.BOOK.RESET_BOOK_TASK, bookTaskId),
|
||||
@ -8,7 +8,7 @@ import { img } from './img.js'
|
||||
import { system } from './system.js'
|
||||
import { setting } from './setting.js'
|
||||
import { prompt } from './prompt.js'
|
||||
import { book } from './book.js'
|
||||
import { book } from './book'
|
||||
import { tts } from './tts.js'
|
||||
import { write } from './write.js'
|
||||
import { gpt } from './gpt.js'
|
||||
|
||||
@ -1,15 +1,42 @@
|
||||
<template>
|
||||
<div style="width: 100%; height: 100%">
|
||||
<webview style="width: 100%; height: 100%" src="https://api.laitool.cc" partition="persist:your-session-name"></webview>
|
||||
<webview
|
||||
id="lai-api"
|
||||
style="width: 100%; height: 100%"
|
||||
src="https://api.laitool.cc"
|
||||
partition="persist:your-session-name"
|
||||
></webview>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent } from 'vue'
|
||||
import { defineComponent, onMounted, onUnmounted } from 'vue'
|
||||
export default defineComponent({
|
||||
components: {},
|
||||
|
||||
setup() {
|
||||
onMounted(() => {
|
||||
const webview = document.getElementById('lai-api')
|
||||
if (webview) {
|
||||
debugger
|
||||
const handleNewWindow = (event) => {
|
||||
debugger
|
||||
event.preventDefault()
|
||||
alert(123)
|
||||
const protocol = new URL(event.url).protocol
|
||||
if (protocol === 'http:' || protocol === 'https:') {
|
||||
window.api.OpenUrl(protocol) // 确保传递完整的 URL 而不是仅协议
|
||||
}
|
||||
}
|
||||
|
||||
webview.addEventListener('new-window', handleNewWindow)
|
||||
}
|
||||
})
|
||||
// 在组件卸载时移除事件监听器
|
||||
// onUnmounted(() => {
|
||||
// webview.removeEventListener('new-window', handleNewWindow)
|
||||
// })
|
||||
|
||||
return {}
|
||||
}
|
||||
})
|
||||
|
||||
@ -265,7 +265,6 @@ export default defineComponent({
|
||||
// 初始化加载项目下面的分镜好的文案
|
||||
// 并判断是不是有洗稿后的文件。一并加载
|
||||
await window.api.GetProjectWord((value) => {
|
||||
|
||||
data.value = value.data
|
||||
})
|
||||
|
||||
@ -358,7 +357,6 @@ export default defineComponent({
|
||||
let itemIndex = row.subValue.findIndex((item) => item.id == itemId)
|
||||
let thisIndex = data.value.findIndex((item) => item.id == row.id)
|
||||
if (itemIndex < 0 && isM) {
|
||||
|
||||
message.error('数据错误')
|
||||
return
|
||||
} else {
|
||||
@ -551,7 +549,7 @@ export default defineComponent({
|
||||
async function AIModifyOneWord(row) {
|
||||
await window.api.AIModifyOneWord([row.no, row.word], (value) => {
|
||||
if (value.code != 1) {
|
||||
message.error('未知错误')
|
||||
message.error()
|
||||
return
|
||||
}
|
||||
// 修改数据
|
||||
@ -599,7 +597,7 @@ export default defineComponent({
|
||||
// 开始保存
|
||||
await window.api.SaveNewWord(new_data_word, (value) => {
|
||||
if (value.code != 1) {
|
||||
message.error('未知错误')
|
||||
message.error(value.message)
|
||||
return
|
||||
}
|
||||
message.success('保存成功,并写入到了剪贴板')
|
||||
@ -614,7 +612,7 @@ export default defineComponent({
|
||||
[toRaw(data.value), 'srt_time_information'],
|
||||
(value) => {
|
||||
if (value.code != 1) {
|
||||
message.error('未知错误')
|
||||
message.error(value.message)
|
||||
return
|
||||
}
|
||||
message.success('保存成功')
|
||||
|
||||
@ -32,7 +32,6 @@ export default defineComponent({
|
||||
props: ['initData', 'index'],
|
||||
setup(props) {
|
||||
let data = ref(props.initData)
|
||||
console.log('subValue', data.value)
|
||||
onMounted(async () => {})
|
||||
let InputDebounced = debounce(handleInput, 1000)
|
||||
|
||||
|
||||
@ -0,0 +1,45 @@
|
||||
<template>
|
||||
<div style="display: flex; justify-content: space-between; white-space: nowrap">
|
||||
<span>文案</span>
|
||||
<n-popover trigger="hover">
|
||||
<template #trigger>
|
||||
<n-button color="#7c461e" @click="SelectAfterGptAndReplace" text style="font-size: 24px">
|
||||
<n-icon>
|
||||
<FindReplaceRound />
|
||||
</n-icon>
|
||||
</n-button>
|
||||
</template>
|
||||
<span>批量查询替换文案</span>
|
||||
</n-popover>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, h, onMounted, onUnmounted } from 'vue'
|
||||
import { useDialog, useMessage, NIcon, NPopover, NButton } from 'naive-ui'
|
||||
import FindReplaceRound from '../../Icon/FindReplaceRound.vue'
|
||||
import { useReverseManageStore } from '../../../../../stores/reverseManage'
|
||||
import DatatableHeaderAfterGptSelectAndReplace from './DatatableHeaderAfterGptSelectAndReplace.vue'
|
||||
import { on } from 'ws'
|
||||
import { BookRepalceDataType } from '../../../../../define/enum/bookEnum'
|
||||
let reverseManageStore = useReverseManageStore()
|
||||
let message = useMessage()
|
||||
let dialog = useDialog()
|
||||
let da = undefined
|
||||
|
||||
async function SelectAfterGptAndReplace() {
|
||||
if (da) {
|
||||
return
|
||||
}
|
||||
da = dialog.create({
|
||||
title: '批量查询替换文案',
|
||||
showIcon: false,
|
||||
maskClosable: false,
|
||||
content: () =>
|
||||
h(DatatableHeaderAfterGptSelectAndReplace, { type: BookRepalceDataType.AFTER_GPT }),
|
||||
onClose: () => {
|
||||
da = undefined
|
||||
}
|
||||
})
|
||||
}
|
||||
</script>
|
||||
@ -0,0 +1,116 @@
|
||||
<template>
|
||||
<div>
|
||||
<n-input v-model:value="replaceData.before" size="small" type="text" placeholder="替换前" />
|
||||
<n-input
|
||||
v-model:value="replaceData.after"
|
||||
style="margin-top: 10px"
|
||||
size="small"
|
||||
type="text"
|
||||
placeholder="替换后,空白为删除"
|
||||
/>
|
||||
<div v-if="replaceData.type == 'gpt_prompt'" style="margin-top: 10px">
|
||||
<n-checkbox v-model:checked="replaceData.replaceAll"> 替换所有 </n-checkbox>
|
||||
<div style="color: red">
|
||||
勾选当前按钮,会替换所有的提示词数据,包含推理的,或者是反推的,没有勾选,只会替换当前显示的
|
||||
</div>
|
||||
</div>
|
||||
<div style="display: flex; justify-content: flex-end; margin-top: 15px">
|
||||
<n-button type="info" @click="ReplaceAfterGpt">替换</n-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted, toRaw } from 'vue'
|
||||
import { NInput, NButton, useMessage, NCheckbox } from 'naive-ui'
|
||||
import { isEmpty, replace } from 'lodash'
|
||||
import { useReverseManageStore } from '../../../../../stores/reverseManage'
|
||||
import { BookRepalceDataType } from '../../../../../define/enum/bookEnum'
|
||||
let replaceData = ref({
|
||||
before: undefined,
|
||||
after: undefined,
|
||||
replaceAll: false,
|
||||
type: BookRepalceDataType.AFTER_GPT
|
||||
})
|
||||
let message = useMessage()
|
||||
let reverseManageStore = useReverseManageStore()
|
||||
|
||||
let props = defineProps({
|
||||
type: BookRepalceDataType.AFTER_GPT
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
replaceData.value.type = props.type
|
||||
})
|
||||
|
||||
async function ReplaceAfterGpt() {
|
||||
if (isEmpty(replaceData.value.before)) {
|
||||
message.error('替换前不能为空')
|
||||
return
|
||||
}
|
||||
let res = await window.book.ReplaceBookData(
|
||||
reverseManageStore.selectBookTask.id,
|
||||
toRaw(replaceData.value)
|
||||
)
|
||||
if (res.code == 1) {
|
||||
// 清空数据
|
||||
replaceData.value.before = ''
|
||||
replaceData.value.after = ''
|
||||
replaceData.value.replaceAll = false
|
||||
if (props.type == BookRepalceDataType.AFTER_GPT) {
|
||||
// 修改前端数据
|
||||
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.newData
|
||||
}
|
||||
}
|
||||
} else if (props.type == BookRepalceDataType.GPT_PROMPT) {
|
||||
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) {
|
||||
continue
|
||||
}
|
||||
if (element.type == 'gptPrompt') {
|
||||
// 修改的是GPT提示词
|
||||
reverseManageStore.selectBookTaskDetail[index].gptPrompt = element.newData
|
||||
} else if (element.type == 'reversePrompt') {
|
||||
// 修改的是反推提示词
|
||||
let reverseIndex = reverseManageStore.selectBookTaskDetail[index].reversePrompt.findIndex(
|
||||
(x) => x.id == element.reversePromptId
|
||||
)
|
||||
if (reverseIndex == -1) {
|
||||
continue
|
||||
}
|
||||
if (element.reversePromptType == 'promptCN') {
|
||||
reverseManageStore.selectBookTaskDetail[index].reversePrompt[reverseIndex].promptCN =
|
||||
element.newData
|
||||
} else if (element.reversePromptType == 'prompt') {
|
||||
reverseManageStore.selectBookTaskDetail[index].reversePrompt[reverseIndex].prompt =
|
||||
element.newData
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (props.type == BookRepalceDataType.PROMPT) {
|
||||
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) {
|
||||
continue
|
||||
}
|
||||
reverseManageStore.selectBookTaskDetail[index].prompt = element.newData
|
||||
}
|
||||
}
|
||||
} else {
|
||||
message.error(res.message)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@ -1,37 +1,58 @@
|
||||
<template>
|
||||
<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 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 } from 'naive-ui'
|
||||
import { useMessage, useDialog, NButton, NPopover, NIcon } from 'naive-ui'
|
||||
import { useReverseManageStore } from '../../../../../stores/reverseManage'
|
||||
import { BookType } from '../../../../../define/enum/bookEnum'
|
||||
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 },
|
||||
components: { NButton, NPopover, FindReplaceRound, NIcon },
|
||||
|
||||
setup() {
|
||||
let message = useMessage()
|
||||
@ -40,7 +61,7 @@ export default defineComponent({
|
||||
let bookType = ref(BookType)
|
||||
let dialog = useDialog()
|
||||
let selectStyleRef = ref(null)
|
||||
onMounted(async () => {})
|
||||
let da = undefined
|
||||
|
||||
/**
|
||||
* 选择MJ反推提示词。用于生图
|
||||
@ -115,13 +136,30 @@ export default defineComponent({
|
||||
})
|
||||
}
|
||||
|
||||
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
|
||||
selectStyleRef,
|
||||
SelectReversePromptAndReplace
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
<template>
|
||||
<div style="display: flex; align-items: center; margin-left: 10px">
|
||||
<div style="display: flex; align-items: center; white-space: nowrap">
|
||||
<span>生图提示词</span>
|
||||
<n-popover trigger="hover">
|
||||
<template #trigger>
|
||||
<n-button color="#7c461e" @click="SetMergeModel" text style="font-size: 24px">
|
||||
@ -19,6 +20,22 @@
|
||||
>合并命令</n-button
|
||||
>
|
||||
</n-dropdown>
|
||||
|
||||
<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>
|
||||
</template>
|
||||
|
||||
@ -30,10 +47,24 @@ import { useSoftwareStore } from '../../../../../stores/software'
|
||||
import DynamicTagsSelect from '../../Components/DynamicTagsSelect.vue'
|
||||
import { usePromptStore } from '../../../../../stores/prompt'
|
||||
import { useReverseManageStore } from '../../../../../stores/reverseManage'
|
||||
import { BookImageCategory, OperateBookType } from '../../../../../define/enum/bookEnum'
|
||||
import {
|
||||
BookImageCategory,
|
||||
BookRepalceDataType,
|
||||
OperateBookType
|
||||
} from '../../../../../define/enum/bookEnum'
|
||||
import FindReplaceRound from '../../Icon/FindReplaceRound.vue'
|
||||
import DatatableHeaderAfterGptSelectAndReplace from './DatatableHeaderAfterGptSelectAndReplace.vue'
|
||||
|
||||
export default defineComponent({
|
||||
components: { NDropdown, NPopover, NIcon, NButton, Construct },
|
||||
components: {
|
||||
NDropdown,
|
||||
NPopover,
|
||||
NIcon,
|
||||
NButton,
|
||||
Construct,
|
||||
FindReplaceRound,
|
||||
DatatableHeaderAfterGptSelectAndReplace
|
||||
},
|
||||
|
||||
setup() {
|
||||
let softwareStore = useSoftwareStore()
|
||||
@ -41,7 +72,7 @@ export default defineComponent({
|
||||
let reverseManageStore = useReverseManageStore()
|
||||
let message = useMessage()
|
||||
let dialog = useDialog()
|
||||
onMounted(async () => {})
|
||||
let da = undefined
|
||||
|
||||
async function SaveFunction(value, options) {
|
||||
// 开始保存
|
||||
@ -137,11 +168,28 @@ export default defineComponent({
|
||||
softwareStore.spin.spinning = false
|
||||
}
|
||||
|
||||
async function SelectPromptAndReplace() {
|
||||
if (da) {
|
||||
return
|
||||
}
|
||||
da = dialog.create({
|
||||
title: '批量查询替换生图提示词',
|
||||
showIcon: false,
|
||||
maskClosable: false,
|
||||
content: () =>
|
||||
h(DatatableHeaderAfterGptSelectAndReplace, { type: BookRepalceDataType.PROMPT }),
|
||||
onClose: () => {
|
||||
da = undefined
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
softwareStore,
|
||||
SetMergeModel,
|
||||
reverseManageStore,
|
||||
MergePrompt,
|
||||
SelectPromptAndReplace,
|
||||
MergePromptOptions: [
|
||||
{ label: 'MJ模式合并', key: 'mj_merge' },
|
||||
{ label: 'SD模式合并', key: 'sd_merge' }
|
||||
|
||||
@ -0,0 +1,173 @@
|
||||
<template>
|
||||
<div style="margin-top: 10px">
|
||||
<n-form
|
||||
ref="formRef"
|
||||
:model="creatObj"
|
||||
label-placement="left"
|
||||
label-width="120"
|
||||
require-mark-placement="right-hanging"
|
||||
:rules="rules"
|
||||
>
|
||||
<n-form-item label="新增批次数" path="count">
|
||||
<n-input-number
|
||||
:show-button="false"
|
||||
:min="1"
|
||||
:max="100"
|
||||
v-model:value="creatObj.count"
|
||||
placeholder="Input"
|
||||
/>
|
||||
</n-form-item>
|
||||
|
||||
<n-form-item label="选择风格" path="selectTaskDataCategory">
|
||||
<div>风格目前需要到每个批次中单独设置</div>
|
||||
</n-form-item>
|
||||
<n-form-item label="通用前缀" path="prefixPrompt">
|
||||
<n-input v-model:value="creatObj.prefixPrompt" placeholder="请输入通用前缀"></n-input>
|
||||
</n-form-item>
|
||||
<n-form-item label="通用后缀" path="suffixPrompt">
|
||||
<n-input v-model:value="creatObj.suffixPrompt" placeholder="请输入通用后缀"></n-input>
|
||||
</n-form-item>
|
||||
|
||||
<n-form-item label="选择旧批次" path="selectBookTask">
|
||||
<n-select
|
||||
v-model:value="creatObj.selectBookTask"
|
||||
:options="bookTaskOptions"
|
||||
placeholder="选择要复制数据的已存在的批次"
|
||||
/>
|
||||
<n-checkbox
|
||||
style="width: 200px; margin-left: 10px"
|
||||
label="使用旧批次数据"
|
||||
v-model:checked="creatObj.copyBookTask"
|
||||
/>
|
||||
</n-form-item>
|
||||
<n-form-item label="复制的数据" path="selectTaskDataCategory">
|
||||
<n-checkbox-group
|
||||
:value="creatObj.selectTaskDataCategory"
|
||||
@update:value="handleUpdateValue"
|
||||
>
|
||||
<n-space item-style="display: flex;" align="center">
|
||||
<n-checkbox
|
||||
v-for="item in groupData"
|
||||
:disabled="item.disabled"
|
||||
:key="item.value"
|
||||
:value="item.value"
|
||||
>
|
||||
{{ item.label }}
|
||||
</n-checkbox>
|
||||
</n-space>
|
||||
</n-checkbox-group>
|
||||
</n-form-item>
|
||||
|
||||
<div style="display: flex; justify-content: flex-end">
|
||||
<n-button type="primary" @click="SaveBookTask"> 添加 </n-button>
|
||||
</div>
|
||||
</n-form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { onMounted, ref, toRaw } from 'vue'
|
||||
import {
|
||||
useMessage,
|
||||
NForm,
|
||||
NFormItem,
|
||||
NButton,
|
||||
NSpace,
|
||||
NInput,
|
||||
NInputNumber,
|
||||
NCheckboxGroup,
|
||||
NCheckbox,
|
||||
NSelect
|
||||
} from 'naive-ui'
|
||||
import { useReverseManageStore } from '../../../../../../stores/reverseManage'
|
||||
import { AddBookTaskCopyData } from '../../../../../../define/enum/bookEnum'
|
||||
let reverseManageStore = useReverseManageStore()
|
||||
let message = useMessage()
|
||||
let formRef = ref(null)
|
||||
let creatObj = ref({
|
||||
count: 1,
|
||||
copyBookTask: false,
|
||||
selectBookTask: undefined,
|
||||
selectTaskDataCategory: [],
|
||||
prefixPrompt: '',
|
||||
suffixPrompt: ''
|
||||
})
|
||||
let bookTaskOptions = ref([])
|
||||
let groupData = ref([
|
||||
{
|
||||
label: '文案',
|
||||
value: AddBookTaskCopyData.AFTER_GPT,
|
||||
disabled: false
|
||||
},
|
||||
{
|
||||
label: '反推/GPT提示词',
|
||||
value: AddBookTaskCopyData.GPT_PROMPT,
|
||||
disabled: false
|
||||
},
|
||||
{
|
||||
label: '生图风格',
|
||||
value: AddBookTaskCopyData.IMAGE_STYLE,
|
||||
disabled: false
|
||||
},
|
||||
{
|
||||
label: '选择角色',
|
||||
value: AddBookTaskCopyData.CHARACTER,
|
||||
disabled: false
|
||||
},
|
||||
{
|
||||
label: '生图提示词',
|
||||
value: AddBookTaskCopyData.PROMPT,
|
||||
disabled: false
|
||||
}
|
||||
])
|
||||
|
||||
let rules = ref({
|
||||
count: [{ required: true, message: '请输入新增批次数' }]
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
bookTaskOptions.value = reverseManageStore.bookTaskData.map((item) => {
|
||||
return {
|
||||
label: item.name,
|
||||
value: item.id
|
||||
}
|
||||
})
|
||||
if (bookTaskOptions.value.length > 0) {
|
||||
creatObj.value.selectBookTask = bookTaskOptions.value[0].value
|
||||
}
|
||||
})
|
||||
|
||||
// 添加批次的方法
|
||||
async function SaveBookTask(e) {
|
||||
console.log(toRaw(creatObj.value))
|
||||
e.preventDefault()
|
||||
formRef.value?.validate(async (errors) => {
|
||||
if (errors) {
|
||||
message.error('请检查必填项')
|
||||
return
|
||||
}
|
||||
|
||||
// 这边就开始调用添加批次的接口
|
||||
let res = await window.book.AddNewBookTask(toRaw(creatObj.value))
|
||||
if (res.code == 0) {
|
||||
message.error(res.message)
|
||||
} else {
|
||||
// 加载任务列表
|
||||
let bookTaskRes = await reverseManageStore.GetBookTaskDataFromDB({
|
||||
bookId: reverseManageStore.selectBook.id
|
||||
})
|
||||
softwareStore.spin.spinning = false
|
||||
if (bookTaskRes.code == 0) {
|
||||
message.error(bookTaskRes.message)
|
||||
return
|
||||
}
|
||||
// 添加成功,添加成功之后需要刷新一下批次列表
|
||||
message.success('添加成功')
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function handleUpdateValue(value) {
|
||||
creatObj.value.selectTaskDataCategory = value
|
||||
}
|
||||
</script>
|
||||
@ -362,7 +362,6 @@ export default defineComponent({
|
||||
|
||||
// 导出文案
|
||||
async function ExportCopywriting() {
|
||||
debugger
|
||||
softwareStore.spin.spinning = true
|
||||
softwareStore.spin.tip = '正在导出文案中...'
|
||||
let res = await window.book.ExportCopywriting(reverseManageStore.selectBookTask.id)
|
||||
@ -382,6 +381,36 @@ export default defineComponent({
|
||||
softwareStore.spin.spinning = false
|
||||
}
|
||||
|
||||
// 导入文案
|
||||
async function ImportCopywriting() {
|
||||
window.api.SelectFile(['txt'], async (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message)
|
||||
return
|
||||
}
|
||||
let txtPath = value.value
|
||||
// 这边开始导入
|
||||
let res = await window.book.ImportCopywriting(
|
||||
reverseManageStore.selectBook.id,
|
||||
reverseManageStore.selectBookTask.id,
|
||||
txtPath
|
||||
)
|
||||
if (res.code == 1) {
|
||||
for (let i = 0; i < res.data.length; i++) {
|
||||
const element = res.data[i]
|
||||
let index = reverseManageStore.selectBookTaskDetail.findIndex(
|
||||
(x) => x.id == element.bookTaskDetailId
|
||||
)
|
||||
if (index != -1) {
|
||||
reverseManageStore.selectBookTaskDetail[index].afterGpt = element.afterGpt
|
||||
}
|
||||
}
|
||||
} else {
|
||||
window.api.showGlobalMessageDialog(res)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取水印位置
|
||||
*/
|
||||
@ -420,6 +449,9 @@ export default defineComponent({
|
||||
case 'export_recognizing': // 导出文案
|
||||
await ExportCopywriting()
|
||||
break
|
||||
case 'import_recognizing': // 导入文案
|
||||
await ImportCopywriting()
|
||||
break
|
||||
case 'watermark_position': // 水印位置设置
|
||||
await GetWatermarkPosition()
|
||||
break
|
||||
@ -853,6 +885,10 @@ export default defineComponent({
|
||||
label: '导出文案',
|
||||
key: 'export_recognizing'
|
||||
},
|
||||
{
|
||||
label: '导入文案',
|
||||
key: 'import_recognizing'
|
||||
},
|
||||
{
|
||||
label: '停止提取',
|
||||
key: 'stop_recognizing'
|
||||
|
||||
@ -19,6 +19,7 @@ import ManageBookOldImage from '../Components/ManageBookOldImage.vue'
|
||||
import { BookType } from '../../../../../define/enum/bookEnum'
|
||||
import MJReversePrompt from './MJReversePrompt.vue'
|
||||
import DatatableHeaderGptPrompt from '../Components/DatatableHeaderGptPrompt.vue'
|
||||
import DatatableHeaderAfterGpt from '../Components/DatatableHeaderAfterGpt.vue'
|
||||
import DatatableHeaderPrompt from '../Components/DatatableHeaderPrompt.vue'
|
||||
import DatatablePrompt from '../Components/DatatablePrompt.vue'
|
||||
import DatatableHeaderImage from '../Components/DatatableHeaderImage.vue'
|
||||
@ -41,7 +42,9 @@ export default defineComponent({
|
||||
fixed: 'left'
|
||||
},
|
||||
{
|
||||
title: '文案',
|
||||
title(row) {
|
||||
return h(DatatableHeaderAfterGpt)
|
||||
},
|
||||
key: 'afterGpt',
|
||||
width: 200,
|
||||
fixed: 'left',
|
||||
@ -87,10 +90,7 @@ export default defineComponent({
|
||||
},
|
||||
{
|
||||
title(row) {
|
||||
return h('div', { style: 'display: flex; align-items: center' }, [
|
||||
h('span', { size: 'tiny', style: 'margin-left: 5px' }, '生图提示词'),
|
||||
h(DatatableHeaderPrompt)
|
||||
])
|
||||
return h(DatatableHeaderPrompt)
|
||||
},
|
||||
className: 'space-row',
|
||||
key: 'row1',
|
||||
|
||||
@ -41,6 +41,7 @@ import { useRouter } from 'vue-router'
|
||||
import { OperateBookType } from '../../../../define/enum/bookEnum'
|
||||
import { DEFINE_STRING } from '../../../../define/define_string'
|
||||
import { ResponseMessageType } from '../../../../define/enum/softwareEnum'
|
||||
import AddBookTask from './Components/ManageBook/AddBookTask.vue'
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
@ -167,6 +168,16 @@ export default defineComponent({
|
||||
}
|
||||
}
|
||||
|
||||
async function AddBookDialog() {
|
||||
message.info('新增' + reverseManageStore.selectBook.id)
|
||||
dialog.create({
|
||||
title: '新增小说批次任务',
|
||||
showIcon: false,
|
||||
content: () => h(AddBookTask),
|
||||
style: { width: '600px' }
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 一键生成草稿
|
||||
*/
|
||||
@ -197,6 +208,7 @@ export default defineComponent({
|
||||
|
||||
return {
|
||||
reverseManageStore,
|
||||
AddBookDialog,
|
||||
HDImageAll,
|
||||
DraftAll,
|
||||
VideoAll,
|
||||
|
||||
@ -7,14 +7,6 @@
|
||||
<ManageBookTask style="height: 100%" />
|
||||
</template>
|
||||
</n-split>
|
||||
|
||||
<n-float-button :right="30" :bottom="30" shape="circle">
|
||||
<n-badge :value="100" :max="99" :offset="[6, -8]">
|
||||
<n-icon :size="large">
|
||||
<Layers />
|
||||
</n-icon>
|
||||
</n-badge>
|
||||
</n-float-button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
19
src/renderer/src/components/Components/BackTask/BackTask.vue
Normal file
19
src/renderer/src/components/Components/BackTask/BackTask.vue
Normal file
@ -0,0 +1,19 @@
|
||||
<script setup>
|
||||
import { onMounted, ref } from 'vue'
|
||||
import { useMessage, NButton, NFloatButton, NBadge, NIcon } from 'naive-ui'
|
||||
import { Layers } from '@vicons/ionicons5'
|
||||
import { useSoftwareStore } from '../../../../../stores/software'
|
||||
import QueryButton from './QueryButton.vue'
|
||||
import TaskDataTable from './TaskDataTable.vue'
|
||||
|
||||
let softwareStore = useSoftwareStore()
|
||||
|
||||
onMounted(() => {
|
||||
// 加载任务数据
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<QueryButton />
|
||||
<TaskDataTable />
|
||||
</template>
|
||||
@ -0,0 +1,57 @@
|
||||
<script setup>
|
||||
import { onMounted, ref } from 'vue'
|
||||
import { useMessage, NForm, NFormItem, NInput, NButton } from 'naive-ui'
|
||||
import { useSoftwareStore } from '../../../../../stores/software'
|
||||
let softwareStore = useSoftwareStore()
|
||||
|
||||
function handleSubmit() {
|
||||
alert(123)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<n-form
|
||||
ref="formRef"
|
||||
inline
|
||||
:label-width="80"
|
||||
:model="softwareStore.backTask.queryData"
|
||||
size="small"
|
||||
>
|
||||
<n-form-item label="小说" path="bookId">
|
||||
<n-input v-model:value="softwareStore.backTask.queryData.bookId" placeholder="选择小说任务" />
|
||||
</n-form-item>
|
||||
<n-form-item label="小说批次任务" path="bookTaskId">
|
||||
<n-input
|
||||
v-model:value="softwareStore.backTask.queryData.bookTaskId"
|
||||
placeholder="选择小说批次任务"
|
||||
/>
|
||||
</n-form-item>
|
||||
<n-form-item label="任务名字" path="taskName">
|
||||
<n-input
|
||||
v-model:value="softwareStore.backTask.queryData.taskName"
|
||||
placeholder="选择小说批次任务"
|
||||
/>
|
||||
</n-form-item>
|
||||
<n-form-item label="任务类型" path="taskType">
|
||||
<n-input
|
||||
v-model:value="softwareStore.backTask.queryData.taskType"
|
||||
placeholder="选择小说批次任务"
|
||||
/>
|
||||
</n-form-item>
|
||||
<n-form-item label="任务状态" path="taskStatus">
|
||||
<n-input
|
||||
v-model:value="softwareStore.backTask.queryData.taskStatus"
|
||||
placeholder="选择小说批次任务"
|
||||
/>
|
||||
</n-form-item>
|
||||
<n-form-item label="任务失败原因" path="taskErrorMessage">
|
||||
<n-input
|
||||
v-model:value="softwareStore.backTask.queryData.taskErrorMessage"
|
||||
placeholder="选择小说批次任务"
|
||||
/>
|
||||
</n-form-item>
|
||||
<n-form-item path="taskErrorMessage">
|
||||
<n-button type="info" @click="handleSubmit">查询</n-button>
|
||||
</n-form-item>
|
||||
</n-form>
|
||||
</template>
|
||||
@ -0,0 +1,7 @@
|
||||
<script setup>
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>TaskDataTable</div>
|
||||
</template>
|
||||
@ -33,18 +33,21 @@
|
||||
<script>
|
||||
import { ref, h, onMounted, defineComponent, toRaw } from 'vue'
|
||||
import { RouterLink } from 'vue-router'
|
||||
import { Layers } from '@vicons/ionicons5'
|
||||
import {
|
||||
useDialog,
|
||||
NMenu,
|
||||
NSpace,
|
||||
NLayout,
|
||||
NLayoutSider,
|
||||
NLayoutContent,
|
||||
NIcon,
|
||||
useDialog,
|
||||
useNotification,
|
||||
useMessage,
|
||||
NSwitch,
|
||||
NButton
|
||||
NButton,
|
||||
NFloatButton,
|
||||
NBadge
|
||||
} from 'naive-ui'
|
||||
|
||||
import {
|
||||
@ -61,7 +64,9 @@ import { DEFINE_STRING } from '../../../../define/define_string'
|
||||
import ShowMessage from './ShowMessage.vue'
|
||||
import { MD5 } from 'crypto-js'
|
||||
import InputDialogContent from '../Original/Components/InputDialogContent.vue'
|
||||
import APIIcon from '../APIService/APIIcon.vue'
|
||||
import APIIcon from '../Icon/APIIcon.vue'
|
||||
import BackTaskIcon from '../Icon/BackTaskIcon.vue'
|
||||
import BackTask from '../Components/BackTask/BackTask.vue'
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
@ -74,7 +79,10 @@ export default defineComponent({
|
||||
ShowMessage,
|
||||
NSwitch,
|
||||
NButton,
|
||||
APIIcon
|
||||
APIIcon,
|
||||
NFloatButton,
|
||||
NBadge,
|
||||
Layers
|
||||
},
|
||||
setup() {
|
||||
let collapsed = ref(false)
|
||||
@ -95,6 +103,16 @@ export default defineComponent({
|
||||
if (option.key == 'lai_api') return h(NIcon, null, { default: () => h(APIIcon) })
|
||||
if (option.key == 'backward_matrix')
|
||||
return h(NIcon, null, { default: () => h(DuplicateOutline) })
|
||||
if (option.key == 'back_task')
|
||||
return h(
|
||||
NIcon,
|
||||
{
|
||||
onClick: () => {
|
||||
OpneBackTask()
|
||||
}
|
||||
},
|
||||
{ default: () => h(BackTaskIcon) }
|
||||
)
|
||||
if (option.key == 'TTS_Services') return h(NIcon, null, { default: () => h(RadioOutline) })
|
||||
}
|
||||
|
||||
@ -486,6 +504,22 @@ export default defineComponent({
|
||||
key: 'mj_setting'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: () =>
|
||||
h(
|
||||
'div',
|
||||
{
|
||||
onClick: () => {
|
||||
OpneBackTask()
|
||||
},
|
||||
style : 'font-weight: bold;'
|
||||
},
|
||||
{
|
||||
default: () => '后台任务'
|
||||
}
|
||||
),
|
||||
key: 'back_task'
|
||||
}
|
||||
]
|
||||
|
||||
@ -493,10 +527,24 @@ export default defineComponent({
|
||||
return () => h(NIcon, null, { default: () => h(icon) })
|
||||
}
|
||||
|
||||
function OpneBackTask() {
|
||||
let dialogWidth = window.innerWidth * 0.8
|
||||
let dialogHeight = window.innerHeight * 0.95
|
||||
dialog.create({
|
||||
title: '后台任务',
|
||||
showIcon: false,
|
||||
closeOnEsc: false,
|
||||
content: () => h(BackTask, { height: dialogHeight }),
|
||||
style: `width : ${dialogWidth}px; height : ${dialogHeight}px`,
|
||||
maskClosable: false
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
renderMenuIcon,
|
||||
menuOptions,
|
||||
expandIcon,
|
||||
OpneBackTask,
|
||||
collapsed
|
||||
}
|
||||
}
|
||||
|
||||
16
src/renderer/src/components/Icon/BackTaskIcon.vue
Normal file
16
src/renderer/src/components/Icon/BackTaskIcon.vue
Normal file
@ -0,0 +1,16 @@
|
||||
<template>
|
||||
<div>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<g fill="none">
|
||||
<path
|
||||
d="M13.25 8.5a.75.75 0 0 0 0 1.5h3.5a.75.75 0 0 0 0-1.5h-3.5zm-.75 6.25a.75.75 0 0 1 .75-.75h3.5a.75.75 0 1 1 0 1.5h-3.5a.75.75 0 0 1-.75-.75zm-1.72-7.03a.75.75 0 0 1 0 1.06l-2 2a.75.75 0 0 1-1.06 0l-1-1a.75.75 0 0 1 1.06-1.06l.47.47l1.47-1.47a.75.75 0 0 1 1.06 0zm0 6.56a.75.75 0 1 0-1.06-1.06l-1.47 1.47l-.47-.47a.75.75 0 0 0-1.06 1.06l1 1a.75.75 0 0 0 1.06 0l2-2zM5.25 3A2.25 2.25 0 0 0 3 5.25v13.5A2.25 2.25 0 0 0 5.25 21h13.5A2.25 2.25 0 0 0 21 18.75V5.25A2.25 2.25 0 0 0 18.75 3H5.25zM4.5 5.25a.75.75 0 0 1 .75-.75h13.5a.75.75 0 0 1 .75.75v13.5a.75.75 0 0 1-.75.75H5.25a.75.75 0 0 1-.75-.75V5.25z"
|
||||
fill="currentColor"
|
||||
></path>
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
</template>
|
||||
12
src/renderer/src/components/Icon/FindReplaceRound.vue
Normal file
12
src/renderer/src/components/Icon/FindReplaceRound.vue
Normal file
@ -0,0 +1,12 @@
|
||||
<template>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
d="M11 6c1.38 0 2.63.56 3.54 1.46l-1.69 1.69a.5.5 0 0 0 .36.85h4.29c.28 0 .5-.22.5-.5V5.21c0-.45-.54-.67-.85-.35l-1.2 1.2A6.943 6.943 0 0 0 11 4C7.96 4 5.38 5.94 4.42 8.64c-.24.66.23 1.36.93 1.36c.42 0 .79-.26.93-.66A5.007 5.007 0 0 1 11 6zm5.64 9.14c.4-.54.72-1.15.95-1.8c.23-.65-.25-1.34-.94-1.34a.98.98 0 0 0-.93.66A5.007 5.007 0 0 1 11 16c-1.38 0-2.63-.56-3.54-1.46l1.69-1.69a.5.5 0 0 0-.36-.85H4.5c-.28 0-.5.22-.5.5v4.29c0 .45.54.67.85.35l1.2-1.2a6.984 6.984 0 0 0 9.09.7l4.11 4.11c.41.41 1.08.41 1.49 0c.41-.41.41-1.08 0-1.49l-4.1-4.12z"
|
||||
fill="currentColor"
|
||||
></path>
|
||||
</svg>
|
||||
</template>
|
||||
@ -111,7 +111,7 @@ export default defineComponent({
|
||||
// 保存信息
|
||||
await window.api.SaveCopywritingInformation([toRaw(AnalyzeCharacter.value), "auto_analyze_character"], (value) => {
|
||||
if (value.code != 1) {
|
||||
message.error("未知错误");
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
message.success("保存成功");
|
||||
|
||||
@ -550,7 +550,7 @@ export default defineComponent({
|
||||
[toRaw(data.value), 'srt_time_information'],
|
||||
(value) => {
|
||||
if (value.code != 1) {
|
||||
message.error('未知错误')
|
||||
message.error(value.message)
|
||||
return
|
||||
}
|
||||
message.success('保存成功')
|
||||
|
||||
@ -167,7 +167,6 @@ export default defineComponent({
|
||||
|
||||
onMounted(async () => {
|
||||
await window.api.InitSDConfig((value) => {
|
||||
|
||||
if (value.code == 0) {
|
||||
message.error(value.message)
|
||||
return
|
||||
@ -221,7 +220,7 @@ export default defineComponent({
|
||||
})
|
||||
return
|
||||
} else {
|
||||
window.api.showGlobalMessageDialog({ code: 0, message: '未知错误' })
|
||||
window.api.showGlobalMessageDialog({ code: 0, message: value.message })
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -231,7 +230,6 @@ export default defineComponent({
|
||||
*/
|
||||
async function LoadSDServiceData() {
|
||||
await window.sd.LoadSDServiceData(toRaw(formValue.value).webui_api_url, (value) => {
|
||||
|
||||
if (value.code == 0) {
|
||||
message.error(value.message)
|
||||
return
|
||||
|
||||
@ -447,7 +447,7 @@ export default defineComponent({
|
||||
fontNameOptions.value = value.data.font_name_list
|
||||
//
|
||||
} else {
|
||||
message.error('未知错误')
|
||||
message.error(value.message)
|
||||
}
|
||||
})
|
||||
|
||||
@ -468,7 +468,6 @@ export default defineComponent({
|
||||
* 保存基本配置
|
||||
*/
|
||||
async function SaveGeneralSetting() {
|
||||
|
||||
await window.api.SaveGeneralSetting(toRaw(generalSetting.value), (value) => {
|
||||
console.log(value)
|
||||
if (value.code == 0) {
|
||||
@ -476,7 +475,7 @@ export default defineComponent({
|
||||
} else if (value.code == 1) {
|
||||
window.api.showGlobalMessageDialog(value)
|
||||
} else {
|
||||
window.api.showGlobalMessageDialog({ code: 0, message: '未知错误' })
|
||||
window.api.showGlobalMessageDialog({ code: 0, message: value.message })
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -486,7 +485,6 @@ export default defineComponent({
|
||||
*/
|
||||
async function GetSystemInstallFontName() {
|
||||
await window.api.GetSystemInstallFontName(async (value) => {
|
||||
|
||||
console.log(value)
|
||||
if (value.code == 0) {
|
||||
message.error(value.message)
|
||||
@ -495,7 +493,7 @@ export default defineComponent({
|
||||
// 刷新数据
|
||||
await GetVideoConfigMessage()
|
||||
} else {
|
||||
message.error('未知错误')
|
||||
message.error(value.message)
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -505,7 +503,6 @@ export default defineComponent({
|
||||
*/
|
||||
async function SaveAssConfig() {
|
||||
window.api.SaveAssConfig(['assConfig', toRaw(assSetting.value)], async (value) => {
|
||||
|
||||
console.log(value)
|
||||
if (value.code == 1) {
|
||||
// 刷新数据,将上面的数据清零。
|
||||
@ -517,7 +514,7 @@ export default defineComponent({
|
||||
window.api.showGlobalMessageDialog(value)
|
||||
} else {
|
||||
// message.error("未知错误");
|
||||
window.api.showGlobalMessageDialog({ code: 0, message: '未知错误' })
|
||||
window.api.showGlobalMessageDialog({ code: 0, message: value.message })
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -528,7 +525,6 @@ export default defineComponent({
|
||||
*/
|
||||
async function DeleteAssSetting(row) {
|
||||
await window.api.DeleteVideoConfig(['assConfig', row.id], async (value) => {
|
||||
|
||||
console.log(value)
|
||||
if (value.code == 1) {
|
||||
message.success('删除成功')
|
||||
@ -552,7 +548,7 @@ export default defineComponent({
|
||||
} else if (value.code == 0) {
|
||||
window.api.showGlobalMessageDialog(value)
|
||||
} else {
|
||||
window.api.showGlobalMessageDialog({ value: 0, message: '未知错误' })
|
||||
window.api.showGlobalMessageDialog({ value: 0, message: value.message })
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -561,9 +557,7 @@ export default defineComponent({
|
||||
* 删除指定的行
|
||||
*/
|
||||
async function DeleteWaterMarkSetting(row) {
|
||||
|
||||
await window.api.DeleteVideoConfig(['watermarkConfig', row.id], async (value) => {
|
||||
|
||||
console.log(value)
|
||||
if (value.code == 1) {
|
||||
message.success('删除成功')
|
||||
@ -587,14 +581,12 @@ export default defineComponent({
|
||||
positiveText: '确定',
|
||||
negativeText: '取消',
|
||||
onPositiveClick: async () => {
|
||||
|
||||
let ss = modifyRef.value.wmSetting
|
||||
console.log(ss)
|
||||
|
||||
await window.api.SaveAssConfig(
|
||||
[type, toRaw(modifyRef.value.wmSetting)],
|
||||
async (value) => {
|
||||
|
||||
console.log(value)
|
||||
if (value.code == 0) {
|
||||
message.error(value.message)
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import { MJSetting } from '../model/Setting/mjSetting'
|
||||
|
||||
// 系统相关设置
|
||||
export const useSettingStore = defineStore('setting', {
|
||||
|
||||
@ -7,6 +7,17 @@ export const useSoftwareStore = defineStore('software', {
|
||||
spinning: false,
|
||||
tip: '加载中...'
|
||||
},
|
||||
backTask: {
|
||||
queryData: {
|
||||
bookId: undefined,
|
||||
bookTaskId: undefined,
|
||||
taskName: undefined,
|
||||
taskType: undefined,
|
||||
taskStatus: undefined,
|
||||
taskErrorMessage: undefined,
|
||||
}, // 查询传递的数据
|
||||
taskData: [], // 后台任务数据
|
||||
},
|
||||
softWare: {
|
||||
theme: 'light', // 系统主题,亮或是暗
|
||||
reverse_display_show: false, // 一键反推界面显示(简单的表格模式还是表格任务模式)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user