Laitool v3.0.1-preview.1

This commit is contained in:
lq1405 2024-08-03 12:46:12 +08:00
parent c8a46d59fb
commit 7312053a20
196 changed files with 19825 additions and 8736 deletions

View File

@ -1,6 +1,6 @@
{ {
"[typescript]": { "[typescript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode" "editor.defaultFormatter": "vscode.typescript-language-features"
}, },
"[javascript]": { "[javascript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode" "editor.defaultFormatter": "esbenp.prettier-vscode"

3008
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{ {
"name": "laitool", "name": "laitool",
"version": "2.2.12", "version": "3.0.1-preview.1",
"description": "An AI tool for image processing, video processing, and other functions.", "description": "An AI tool for image processing, video processing, and other functions.",
"main": "./out/main/index.js", "main": "./out/main/index.js",
"author": "laitool.cn", "author": "laitool.cn",
@ -9,7 +9,7 @@
"format": "prettier --write .", "format": "prettier --write .",
"lint": "eslint . --ext .js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix", "lint": "eslint . --ext .js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix",
"start": "electron-vite preview", "start": "electron-vite preview",
"dev": "electron-vite dev --watch", "dev": "cross-env chcp 65001 && cross-env NODE_ENV=development electron-vite dev --watch",
"build": "electron-vite build", "build": "electron-vite build",
"postinstall": "electron-builder install-app-deps", "postinstall": "electron-builder install-app-deps",
"build:win": "npm run build && electron-builder --win --config", "build:win": "npm run build && electron-builder --win --config",
@ -20,7 +20,6 @@
"@alicloud/alimt20181012": "^1.2.0", "@alicloud/alimt20181012": "^1.2.0",
"@electron-toolkit/preload": "^3.0.0", "@electron-toolkit/preload": "^3.0.0",
"@electron-toolkit/utils": "^3.0.0", "@electron-toolkit/utils": "^3.0.0",
"@skit/x.naive-ui": "^0.15.0",
"@vicons/ionicons5": "^0.12.0", "@vicons/ionicons5": "^0.12.0",
"@vitejs/plugin-vue-jsx": "^3.1.0", "@vitejs/plugin-vue-jsx": "^3.1.0",
"@volcengine/openapi": "^1.16.0", "@volcengine/openapi": "^1.16.0",
@ -28,21 +27,24 @@
"artplayer": "^5.1.6", "artplayer": "^5.1.6",
"awesome-js": "^2.0.0", "awesome-js": "^2.0.0",
"axios": "^1.6.5", "axios": "^1.6.5",
"baidu-aip-sdk": "^4.16.16",
"blob-to-buffer": "^1.2.9", "blob-to-buffer": "^1.2.9",
"compressing": "^1.10.0", "compressing": "^1.10.0",
"compressorjs": "^1.2.1",
"crypto-js": "^4.2.0", "crypto-js": "^4.2.0",
"electron-store": "^9.0.0", "electron-store": "^9.0.0",
"electron-updater": "^6.1.7", "electron-updater": "^6.1.7",
"fluent-ffmpeg": "^2.1.3", "fluent-ffmpeg": "^2.1.3",
"highlight.js": "^11.9.0", "highlight.js": "^11.9.0",
"install": "^0.13.0", "install": "^0.13.0",
"jimp": "^0.22.10", "jieba-js": "^1.0.2",
"jsdom": "^24.0.0", "jsdom": "^24.0.0",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"moment-timezone": "^0.5.45",
"music-metadata": "^7.14.0", "music-metadata": "^7.14.0",
"node-edge-tts": "^1.2.3",
"node-gyp": "^10.2.0",
"node-machine-id": "^1.1.12", "node-machine-id": "^1.1.12",
"node-reg": "^0.2.4", "node-pre-gyp": "^0.17.0",
"npm": "^10.7.0", "npm": "^10.7.0",
"paddle": "^1.0.0", "paddle": "^1.0.0",
"pinia": "^2.1.7", "pinia": "^2.1.7",
@ -53,7 +55,6 @@
"uuid": "^9.0.1", "uuid": "^9.0.1",
"vue-router": "^4.2.5", "vue-router": "^4.2.5",
"wav-file-info": "^0.0.10", "wav-file-info": "^0.0.10",
"winreg": "^1.2.5",
"winston": "^3.13.0", "winston": "^3.13.0",
"winston-daily-rotate-file": "^5.0.0", "winston-daily-rotate-file": "^5.0.0",
"ws": "^8.18.0" "ws": "^8.18.0"
@ -63,17 +64,16 @@
"@rushstack/eslint-patch": "^1.6.1", "@rushstack/eslint-patch": "^1.6.1",
"@types/fluent-ffmpeg": "^2.1.24", "@types/fluent-ffmpeg": "^2.1.24",
"@types/ws": "^8.5.10", "@types/ws": "^8.5.10",
"@typescript-eslint/eslint-plugin": "^7.16.0",
"@vitejs/plugin-vue": "^5.0.2", "@vitejs/plugin-vue": "^5.0.2",
"@vue/eslint-config-prettier": "^9.0.0", "cross-env": "^7.0.3",
"electron": "^28.1.1", "electron": "^28.1.1",
"electron-builder": "^24.13.3", "electron-builder": "^24.13.3",
"electron-vite": "^2.0.0", "electron-vite": "^2.0.0",
"eslint": "^8.56.0", "eslint": "^8.57.0",
"eslint-plugin-vue": "^9.19.2", "eslint-plugin-vue": "^9.19.2",
"less": "^4.2.0",
"naive-ui": "^2.38.2", "naive-ui": "^2.38.2",
"prettier": "^3.1.1", "prettier": "^3.1.1",
"vfonts": "^0.0.3",
"vite": "^5.0.11", "vite": "^5.0.11",
"vue": "^3.4.5" "vue": "^3.4.5"
}, },

Binary file not shown.

Before

Width:  |  Height:  |  Size: 566 KiB

After

Width:  |  Height:  |  Size: 895 KiB

BIN
resources/icon_1.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 566 KiB

Binary file not shown.

Binary file not shown.

View File

@ -59,5 +59,5 @@
"value": 1.0 "value": 1.0
}, },
"visible": true, "visible": true,
"volume": 1.0 "volumn": 1.0
} }

View File

@ -47,5 +47,5 @@
"track_render_index": 0, "track_render_index": 0,
"uniform_scale": null, "uniform_scale": null,
"visible": true, "visible": true,
"volume": 1.0 "volumn": 1.0
} }

View File

@ -69,5 +69,5 @@
"value": 1.0 "value": 1.0
}, },
"visible": true, "visible": true,
"volume": 1.0 "volumn": 1.0
} }

View File

@ -1,13 +1,11 @@
import { basicApi } from "./apiBasic"; import { basicApi } from './apiBasic'
import { define } from "../define/define"; import { define } from '../define/define'
import { promises as fspromises } from 'fs' import { promises as fspromises } from 'fs'
import { errorMessage, successMessage } from "../main/generalTools";
export class SdApi { export class SdApi {
constructor() { constructor() {
this.baseUrl = global.config?.webui_api_url; this.baseUrl = global.config?.webui_api_url
this.sd_setting = null; this.sd_setting = null
} }
/** /**
@ -15,11 +13,11 @@ export class SdApi {
* @returns * @returns
*/ */
async getAllLoras(baseURL = null) { async getAllLoras(baseURL = null) {
let url = this.baseUrl + "sdapi/v1/loras"; let url = this.baseUrl + 'sdapi/v1/loras'
if (baseURL != null) { if (baseURL != null) {
url = baseURL + "sdapi/v1/loras"; url = baseURL + 'sdapi/v1/loras'
} }
return await basicApi.get(url); return await basicApi.get(url)
} }
/** /**
@ -27,11 +25,11 @@ export class SdApi {
* @param {*} baseURL * @param {*} baseURL
*/ */
async getAllSDModel(baseURL = null) { async getAllSDModel(baseURL = null) {
let url = this.baseUrl + "sdapi/v1/sd-models"; let url = this.baseUrl + 'sdapi/v1/sd-models'
if (baseURL != null) { if (baseURL != null) {
url = baseURL + "sdapi/v1/sd-models"; url = baseURL + 'sdapi/v1/sd-models'
} }
return await basicApi.get(url); return await basicApi.get(url)
} }
/** /**
@ -41,51 +39,49 @@ export class SdApi {
*/ */
async getAllSamplers(baseURL = null) { async getAllSamplers(baseURL = null) {
try { try {
let url = this.baseUrl + 'sdapi/v1/samplers'
let url = this.baseUrl + "sdapi/v1/samplers";
if (baseURL != null) { if (baseURL != null) {
url = baseURL + "sdapi/v1/samplers"; url = baseURL + 'sdapi/v1/samplers'
} }
return await basicApi.get(url); return await basicApi.get(url)
} catch (error) { } catch (error) {
throw error; throw error
} }
} }
async txt2img(data, baseURL = null) { async txt2img(data, baseURL = null) {
try { try {
if (this.sd_setting == null) { if (this.sd_setting == null) {
this.sd_setting = JSON.parse(await fspromises.readFile(define.sd_setting, 'utf-8')); this.sd_setting = JSON.parse(await fspromises.readFile(define.sd_setting, 'utf-8'))
this.baseUrl = this.sd_setting.setting.webui_api_url; this.baseUrl = this.sd_setting.setting.webui_api_url
} }
// 加上通用前缀 // 加上通用前缀
data.prompt = this.sd_setting.webui.prompt + data.prompt data.prompt = this.sd_setting.webui.prompt + data.prompt
data.negative_prompt = this.sd_setting.webui.negative_prompt; data.negative_prompt = this.sd_setting.webui.negative_prompt
data.sampler_name = this.sd_setting.webui.sampler_name; data.sampler_name = this.sd_setting.webui.sampler_name
data.cfg_scale = this.sd_setting.webui.cfg_scale; data.cfg_scale = this.sd_setting.webui.cfg_scale
data.n_iter = 1; data.n_iter = 1
data.steps = this.sd_setting.webui.steps; data.steps = this.sd_setting.webui.steps
data.save_images = false; data.save_images = false
data.batch_size = data.batch_size ? data.batch_size : 1; data.batch_size = data.batch_size ? data.batch_size : 1
if (data.width == null) { if (data.width == null) {
data.width = 512; data.width = 512
} }
if (data.height == null) { if (data.height == null) {
data.height = 512; data.height = 512
} }
let url = this.baseUrl + "sdapi/v1/txt2img"; let url = this.baseUrl + 'sdapi/v1/txt2img'
if (baseURL != null) { if (baseURL != null) {
url = baseURL + "sdapi/v1/txt2img"; url = baseURL + 'sdapi/v1/txt2img'
} }
let res = await basicApi.post(url, data); let res = await basicApi.post(url, data)
return res; return res
} catch (error) { } catch (error) {
throw error; throw error
} }
} }
} }

View File

@ -1,3 +0,0 @@
import fs from 'node:fs';
const fspromises = fs.promises;

View File

@ -0,0 +1,11 @@
/**
*
* @param str
* @returns truefalse
*/
export function ContainsChineseOrPunctuation(str: string): boolean {
return /[\u4e00-\u9fa5]|[\u3000-\u301e\u2013\u2014\u2018\u2019\u201c\u201d\u2026\u203b\uff08\uff09\uff1a\uff1b\uff1f\uff01\uff0c\u3001\uff0e\u3002\uff1f\uff01\u2018\u2019\u201c\u201d]/.test(
str
)
}

View File

@ -34,7 +34,7 @@ export async function CheckFolderExistsOrCreate(folderPath) {
* @param {*} subPath * @param {*} subPath
* @returns * @returns
*/ */
export function JoinPath(rootPath, subPath) { export function JoinPath(rootPath: string, subPath: string): string {
// 判断第二个地址是不是存在不存在返回null存在返回拼接后的地址 // 判断第二个地址是不是存在不存在返回null存在返回拼接后的地址
if (subPath && !isEmpty(subPath)) { if (subPath && !isEmpty(subPath)) {
return path.resolve(rootPath, subPath) return path.resolve(rootPath, subPath)
@ -46,8 +46,9 @@ export function JoinPath(rootPath, subPath) {
/** /**
* *
* @param {*} folderPath * @param {*} folderPath
* @param {*} isDeleteOut false
*/ */
export async function DeleteFolderAllFile(folderPath) { export async function DeleteFolderAllFile(folderPath: string, isDeleteOut: boolean = false): Promise<void> {
try { try {
let folderIsExist = await CheckFileOrDirExist(folderPath) let folderIsExist = await CheckFileOrDirExist(folderPath)
if (!folderIsExist) { if (!folderIsExist) {
@ -55,17 +56,22 @@ export async function DeleteFolderAllFile(folderPath) {
} }
// 开始删除 // 开始删除
let files = await fspromises.readdir(folderPath) let files = await fspromises.readdir(folderPath)
files.forEach(async (file) => { for (const file of files) {
const curPath = path.join(folderPath, file) const curPath = path.join(folderPath, file);
if ((await fspromises.stat(curPath)).isDirectory()) { const stat = await fspromises.stat(curPath);
if (stat.isDirectory()) {
// 判断是不是文件夹 // 判断是不是文件夹
await DeleteFolderAllFile(curPath) // 递归删除文件夹内容 await DeleteFolderAllFile(curPath); // 递归删除文件夹内容
fspromises.rmdir(curPath) // 删除空文件夹 await fspromises.rmdir(curPath); // 删除空文件夹
} else { } else {
// 删除文件 // 删除文件
fspromises.unlink(curPath) await fspromises.unlink(curPath);
}
}
// 判断是不是要删除最外部的文件夹
if (isDeleteOut) {
await fspromises.rmdir(folderPath)
} }
})
} catch (error) { } catch (error) {
throw error throw error
} }
@ -142,7 +148,7 @@ export async function IsDirectory(path) {
* @param {*} source_path / * @param {*} source_path /
* @param {*} target_path / * @param {*} target_path /
*/ */
export async function BackupFileOrFolder(source_path, target_path) { export async function BackupFileOrFolder(source_path: string, target_path: string): Promise<void> {
try { try {
// 判断父文件夹是否存在,不存在创建 // 判断父文件夹是否存在,不存在创建
const parent_path = path.dirname(target_path) const parent_path = path.dirname(target_path)
@ -158,7 +164,7 @@ export async function BackupFileOrFolder(source_path, target_path) {
await fspromises.rename(source_path, target_path) await fspromises.rename(source_path, target_path)
} else { } else {
// 复制文件 // 复制文件
await fspromises.copyFile(source, target) await fspromises.copyFile(source_path, target_path)
} }
} catch (error) { } catch (error) {
throw new Error(error) throw new Error(error)
@ -171,7 +177,7 @@ export async function BackupFileOrFolder(source_path, target_path) {
* @param {*} extensions * @param {*} extensions
* @returns * @returns
*/ */
export async function GetFilesWithExtensions(folderPath, extensions) { export async function GetFilesWithExtensions(folderPath: string, extensions: string[]): Promise<string[]> {
try { try {
// 判断当前是不是文件夹 // 判断当前是不是文件夹
if (!(await IsDirectory(folderPath))) { if (!(await IsDirectory(folderPath))) {
@ -213,3 +219,20 @@ export async function GetFilesWithExtensions(folderPath, extensions) {
throw new Error(error) throw new Error(error)
} }
} }
/**
*
* @param filePath
* @returns kb单位
*/
export async function GetFileSize(filePath: string): Promise<number> {
try {
if (!(await CheckFileOrDirExist(filePath))) {
throw new Error("获取文件大小,指定的文件不存在");
}
const stats = await fspromises.stat(filePath);
return stats.size / 1024
} catch (error) {
throw error
}
}

View File

@ -1,72 +0,0 @@
import sharp from "sharp";
import { CheckFileOrDirExist } from "./file"
/**
* 将指定的图片的尺寸修改返回修改后的图片数据(base64或buffer)
* @param {*} image_path
* @param {*} width
* @param {*} height
* @param {*} type
* @returns 返回修改后的图片数据(base64或buffer)
*/
export async function ResizeImage(image_path, width, height, type) {
try {
// 检查 type 参数
if (type !== 'base64' && type !== 'buffer') {
throw new Error('type 参数必须是 "base64" 或 "buffer"');
}
// 判断是不是图片文件
if (!image_path.match(/\.(jpg|jpeg|png)$/)) {
throw new Error("输入的文件地址不是图片文件地址");
}
// 判断文件是否存在
if (!(await CheckFileOrDirExist(image_path))) {
throw new Error("文件不存在");
}
// 修改图片尺寸
const image = sharp(image_path);
image.resize(width, height);
let data = await image.toBuffer();
if (type === 'base64') {
return data.toString('base64');
} else {
return data;
}
} catch (error) {
throw error;
}
}
/**
* 获取指定图片文件的宽高
* @param {*} image_path 图片文件的路径
* @returns 返回以一个对象包含width和height属性
*/
export async function GetImageSize(image_path) {
try {
// 判断文件是否存在
if (!(await CheckFileOrDirExist(image_path))) {
throw new Error("文件不存在");
}
// 判断是不是图片文件
if (!image_path.match(/\.(jpg|jpeg|png)$/)) {
throw new Error("输入的文件地址不是图片文件地址");
}
// 获取图片的宽高
const metadata = await sharp(image_path).metadata();
return {
width: metadata.width,
height: metadata.height
}
} catch (error) {
throw error;
}
}

282
src/define/Tools/image.ts Normal file
View File

@ -0,0 +1,282 @@
import path from 'path'
import sharp from 'sharp'
import { CheckFileOrDirExist } from './file'
import fs from 'fs'
import https from 'https'
import Compressor from 'compressorjs';
/**
* (base64或buffer)
* @param {*} image_path
* @param {*} width
* @param {*} height
* @param {*} type
* @returns (base64或buffer)
*/
export async function ResizeImage(image_path: string, width: number | sharp.ResizeOptions, height: number, type: string) {
try {
// 检查 type 参数
if (type !== 'base64' && type !== 'buffer') {
throw new Error('type 参数必须是 "base64" 或 "buffer"')
}
// 判断是不是图片文件
if (!image_path.match(/\.(jpg|jpeg|png)$/)) {
throw new Error('输入的文件地址不是图片文件地址')
}
// 判断文件是否存在
if (!(await CheckFileOrDirExist(image_path))) {
throw new Error('文件不存在')
}
// 修改图片尺寸
const image = sharp(image_path)
image.resize(width, height)
let data = await image.toBuffer()
if (type === 'base64') {
return data.toString('base64')
} else {
return data
}
} catch (error) {
throw error
}
}
/**
*
* @param {*} image_path
* @returns width和height属性
*/
export async function GetImageSize(image_path: string) {
try {
// 判断文件是否存在
if (!(await CheckFileOrDirExist(image_path))) {
throw new Error('文件不存在')
}
// 判断是不是图片文件
if (!image_path.match(/\.(jpg|jpeg|png)$/)) {
throw new Error('输入的文件地址不是图片文件地址')
}
// 获取图片的宽高
const metadata = await sharp(image_path).metadata()
return {
width: metadata.width,
height: metadata.height
}
} catch (error) {
throw error
}
}
/**
* MIME类型
* @param filePath
* @returns MIME类型字符串
*/
export function GetMimeType(filePath: string): string {
const extension = path.extname(filePath).toLowerCase();
const mimeTypes: { [key: string]: string } = {
'.jpg': 'image/jpeg',
'.jpeg': 'image/jpeg',
'.png': 'image/png',
'.gif': 'image/gif',
'.webp': 'image/webp',
// 添加更多文件类型和对应的MIME类型
};
return mimeTypes[extension] || 'application/octet-stream';
}
/**
* MIME类型的base64字符串
* @param url URL
* @returns Promise<string> PromiseMIME类型的base64字符串
*/
export function GetImageBase64(url: string): Promise<string> {
if (!url) {
return Promise.reject('URL不能为空');
}
if (url.startsWith('http://') || url.startsWith('https://')) {
return new Promise((resolve, reject) => {
https.get(url, (response) => {
const mimeType = response.headers['content-type'] || 'application/octet-stream';
const data: any[] = [];
response.on('data', (chunk) => data.push(chunk));
response.on('end', () => {
const buffer = Buffer.concat(data);
const base64Data = `data:${mimeType};base64,${buffer.toString('base64')}`;
resolve(base64Data);
});
}).on('error', (err) => reject(err));
});
} else {
return new Promise((resolve, reject) => {
fs.readFile(url, (err, data) => {
if (err) {
reject(err);
} else {
const mimeType = GetMimeType(url);
const base64Data = `data:${mimeType};base64,${data.toString('base64')}`;
resolve(base64Data);
}
});
});
}
}
/**
*
* @param file Blob对象
* @param maxSizeInBytes
* @returns PromiseBlob对象
*/
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 });
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);
});
}
/**
*
*
* @param inputPath
* @param outputPath
* @param region x, y, width, height属性
* @param markColor { r: 255, g: 255, b: 255 }
* @param backColor { r: 0, g: 0, b: 0 }
*/
export async function ProcessImage(inputPath: string,
outputPath: string,
region: { width: any; height: any; x: any; y: any },
markColor: { r: number; g: number; b: number } = { r: 255, g: 255, b: 255 },
backColor: { r: number; g: number; b: number } = { r: 0, g: 0, b: 0 },
): Promise<void> {
try {
// 读取图片并进行处理
const image = sharp(inputPath);
// 获取图片的元数据
const { width, height } = await image.metadata();
// 创建一个新的空白图片,背景为白色
const whiteBackground = await sharp({
create: {
width,
height,
channels: 3, // RGB channels
background: backColor // White color
}
}).png().toBuffer();
// 创建一个黑色的矩形
const blackRegion = await sharp({
create: {
width: region.width,
height: region.height,
channels: 3, // RGB channels
background: markColor // Black color
}
}).png().toBuffer();
// 在白色背景上叠加黑色矩形
await sharp(whiteBackground)
.composite([
{
input: blackRegion,
left: region.x,
top: Math.floor(region.y)
}
])
.toFile(outputPath);
} catch (err) {
throw err;
}
}
/**
* base64写到本地文件
* @param base64 base64字符串
* @param outFilePath
*/
export async function Base64ToFile(base64: string, outFilePath: string): Promise<void> {
try {
let base64Data = base64.replace(/^data:image\/\w+;base64,/, '')
let dataBuffer = Buffer.from(base64Data, 'base64')
let out_folder = path.dirname(outFilePath)
await this.tools.checkFolderExistsOrCreate(out_folder)
await fs.promises.writeFile(outFilePath, dataBuffer)
// await this.tools.writeArrayToFile(dataBuffer, out_file);
} catch (error) {
throw new Error('将base64转换为文件失败失败信息如下' + error.toString())
}
}
/**
* 4
* @param inputPath
* @param reName
* @param outputDir
* @returns
*/
export async function ImageSplit(inputPath: string, reName: string, outputDir: string) {
try {
const metadata = await sharp(inputPath).metadata()
const smallWidth = metadata.width / 2
const smallHeight = metadata.height / 2
let times = new Date().getTime()
let imgs = []
for (let i = 0; i < 4; i++) {
const xOffset = i % 2 === 0 ? 0 : smallWidth
const yOffset = Math.floor(i / 2) * smallHeight
let out_file = path.join(outputDir, `/${reName}_${times}_${i}.png`)
await sharp(inputPath)
.extract({
left: xOffset,
top: yOffset,
width: smallWidth,
height: smallHeight
})
.resize(smallWidth, smallHeight)
.toFile(out_file)
imgs.push(out_file)
}
return imgs
} catch (err) {
throw err
}
}

View File

@ -1,9 +1,11 @@
import * as image from './image'; import * as image from './image';
import * as common from './common'; import * as common from './common';
import * as file from './file' import * as file from './file'
import * as validate from './validate';
export { export {
image, image,
common, common,
file file,
validate
}; };

View File

@ -43,3 +43,12 @@ export function MillisecondsToTimeString(milliseconds) {
timeString = timeString.replace(/(\.\d+)\./g, '$1') timeString = timeString.replace(/(\.\d+)\./g, '$1')
return timeString return timeString
} }
/**
* Promise
* @param time
* @returns viod
*/
export async function TimeDelay(time: number): Promise<void> {
return new Promise(resolve => setTimeout(resolve, time));
}

View File

@ -0,0 +1,15 @@
/**
* JSON解析
* @param str
* @returns truefalse
*/
export function ValidateJson(str: string): boolean {
try {
JSON.parse(str);
return true
} catch (e) {
return false;
}
}

View File

@ -28,35 +28,6 @@ let apiUrl = [
mj_url: null, mj_url: null,
buy_url: null buy_url: null
}, },
{
label: 'DrawAPI(MJ)',
value: '2cabf684-ac48-4733-a427-8c41626f7d8f',
gpt_url: null,
mj_url: {
imagine: 'https://mjapi.deepwl.net/api/mj/submit/imagine',
describe: 'https://mjapi.deepwl.net/api/mj/submit/describe',
update_file: 'https://mjapi.deepwl.net/api/mj/submit/upload-discord-images',
once_get_task: 'https://mjapi.deepwl.net/api/mj/query/task/${id}',
get_task_list: 'https://mjapi.deepwl.net/api/mj/task/list-by-condition'
},
d3_url: null,
buy_url: 'https://mjapi.deepwl.net/#/home'
},
{
label: 'ePhoneAPI',
value: 'b8866543-8c27-4888-869c-00aa1eb31272',
gpt_url: 'https://api.ephone.ai/v1/chat/completions',
mj_url: {
imagine: 'https://api.ephone.ai/mj/submit/imagine',
describe: 'https://api.ephone.ai/mj/submit/describe',
update_file: 'https://api.ephone.ai/mj/submit/upload-discord-images',
once_get_task: 'https://api.ephone.ai/mj/task/${id}/fetch'
},
d3_url: {
image: 'https://api.ephone.ai/v1/images/generations'
},
buy_url: 'https://ephone.ai/register?aff=55XT'
},
{ {
label: 'KIMI', label: 'KIMI',
value: 'b5c8c8c5-f3c4-4c88-b25c-7f5a3d5f9d1f', value: 'b5c8c8c5-f3c4-4c88-b25c-7f5a3d5f9d1f',

View File

@ -13,6 +13,8 @@ export class BookBackTaskList extends Realm.Object<BookBackTaskList> {
executeType: TaskExecuteType // 任务执行类型,手动还是自动 executeType: TaskExecuteType // 任务执行类型,手动还是自动
createTime: Date createTime: Date
updateTime: Date updateTime: Date
startTime: number
endTime: number
static schema: ObjectSchema = { static schema: ObjectSchema = {
name: 'BookBackTaskList', name: 'BookBackTaskList',
@ -27,7 +29,9 @@ export class BookBackTaskList extends Realm.Object<BookBackTaskList> {
errorMessage: 'string?', errorMessage: 'string?',
executeType: { type: 'string', default: TaskExecuteType.AUTO }, executeType: { type: 'string', default: TaskExecuteType.AUTO },
createTime: 'date', createTime: 'date',
updateTime: 'date' updateTime: 'date',
startTime: 'int',
endTime: 'int'
}, },
primaryKey: 'id' primaryKey: 'id'
} }

View File

@ -1,4 +1,5 @@
import Realm, { ObjectSchema } from 'realm' // @ts-ignore
import Realm from 'realm'
import { BookType } from '../../../enum/bookEnum' import { BookType } from '../../../enum/bookEnum'
export class BookModel extends Realm.Object<BookModel> { export class BookModel extends Realm.Object<BookModel> {
@ -11,12 +12,21 @@ export class BookModel extends Realm.Object<BookModel> {
oldVideoPath: string | null oldVideoPath: string | null
srtPath: string | null srtPath: string | null
audioPath: string | null audioPath: string | null
draftSrtStyle: string | null // 草稿字幕样式
backgroundMusic: string | null // 背景音乐ID
friendlyReminder: string | null // 友情提示
updateTime: Date updateTime: Date
createTime: Date createTime: Date
version: string version: string
imageStyle: string[] | null // 软件内置的样式
autoAnalyzeCharacter: string | null // 自动分析角色设置
customizeImageStyle: string[] | null // 自定义的样式
videoConfig: string | null // 合成视频设置
prefixPrompt: string | null // 前缀
suffixPrompt: string | null // 后缀
subtitlePosition: string | null subtitlePosition: string | null
static schema: ObjectSchema = { static schema: Realm.ObjectSchema = {
name: 'Book', name: 'Book',
properties: { properties: {
id: 'string', id: 'string',
@ -27,10 +37,19 @@ export class BookModel extends Realm.Object<BookModel> {
oldVideoPath: 'string?', oldVideoPath: 'string?',
srtPath: 'string?', srtPath: 'string?',
audioPath: 'string?', audioPath: 'string?',
draftSrtStyle : 'string?',
backgroundMusic: 'string?',
friendlyReminder: 'string?',
imageFolder: 'string?', imageFolder: 'string?',
updateTime: 'date', updateTime: 'date',
createTime: 'date', createTime: 'date',
version: 'string', version: 'string',
imageStyle: 'string?[]',
autoAnalyzeCharacter: 'string?',
customizeImageStyle: 'string?[]',
videoConfig: "string?",
prefixPrompt: "string?",
suffixPrompt: "string?",
subtitlePosition: 'string?' subtitlePosition: 'string?'
}, },
// 主键为_id // 主键为_id

View File

@ -1,5 +1,40 @@
import Realm, { ObjectSchema } from 'realm' import Realm, { ObjectSchema } from 'realm'
import { BookTaskStatus, BookType } from '../../../enum/bookEnum' import { BookImageCategory, BookTaskStatus, BookType } from '../../../enum/bookEnum'
export class ImageDefineModel extends Realm.Object<ImageDefineModel> {
label: string
key: string
value: string
children: string
type: string
prompt: string
image_url: string
cref_cw: number
lora: string
chinese_prompt: string
lora_weight: number
show_image: string
isShow: true
static schema: ObjectSchema = {
name: 'ImageDefine',
properties: {
label: 'string',
key: 'string',
value: 'string',
children: 'string',
type: 'string',
prompt: 'string',
image_url: 'string',
cref_cw: 'int',
lora: 'string',
chinese_prompt: 'string',
lora_weight: 'int',
show_image: 'string',
isShow: 'bool'
},
primaryKey: 'key'
}
}
export class BookTaskModel extends Realm.Object<BookTaskModel> { export class BookTaskModel extends Realm.Object<BookTaskModel> {
id: string id: string
@ -9,14 +44,24 @@ export class BookTaskModel extends Realm.Object<BookTaskModel> {
generateVideoPath: string | null generateVideoPath: string | null
srtPath: string | null srtPath: string | null
audioPath: string | null audioPath: string | null
draftSrtStyle: string | null // 草稿字幕样式
backgroundMusic: string | null // 背景音乐ID
friendlyReminder: string | null // 友情提示
imageFolder: string | null imageFolder: string | null
styleList: Realm.List<string> | null imageStyle: string[] | null // 软件内置的样式
prefix: string | null autoAnalyzeCharacter: string | null // 自动分析角色设置
customizeImageStyle: string[] | null // 自定义的样式
videoConfig: string | null // 合成视频设置
prefixPrompt: string | null // 前缀
suffixPrompt: string | null // 后缀
styleList: string[] | null
status: BookTaskStatus status: BookTaskStatus
errorMsg: string | null errorMsg: string | null
isAuto: boolean // 是否自动 isAuto: boolean // 是否自动
updateTime: Date updateTime: Date
createTime: Date createTime: Date
imageCategory: BookImageCategory // 图片出图方式
subImageFolder: string[] | null // 出图的子文件夹
static schema: ObjectSchema = { static schema: ObjectSchema = {
name: 'BookTask', name: 'BookTask',
@ -28,14 +73,23 @@ export class BookTaskModel extends Realm.Object<BookTaskModel> {
generateVideoPath: 'string?', generateVideoPath: 'string?',
srtPath: 'string?', srtPath: 'string?',
audioPath: 'string?', audioPath: 'string?',
draftSrtStyle: 'string?',
backgroundMusic: 'string?',
friendlyReminder: 'string?',
imageFolder: 'string?', imageFolder: 'string?',
styleList: 'string[]', subImageFolder: "string?[]",
prefix: 'string?', imageStyle: 'string?[]',
autoAnalyzeCharacter: 'string?',
customizeImageStyle: 'string?[]',
videoConfig: "string?",
prefixPrompt: "string?",
suffixPrompt: "string?",
status: 'string', status: 'string',
errorMsg: 'string?', errorMsg: 'string?',
isAuto: 'bool', isAuto: 'bool',
updateTime: 'date', updateTime: 'date',
createTime: 'date' createTime: 'date',
imageCategory: 'string',
}, },
// 主键为_id // 主键为_id
primaryKey: 'id' primaryKey: 'id'

View File

@ -5,8 +5,8 @@ import {
BookTaskStatus, BookTaskStatus,
BookType, BookType,
MJAction, MJAction,
MJCategroy
} from '../../../enum/bookEnum' } from '../../../enum/bookEnum'
import { MJImageType } from '../../../enum/mjEnum'
export class Subtitle extends Realm.Object<Subtitle> { export class Subtitle extends Realm.Object<Subtitle> {
startTime: number startTime: number
@ -29,7 +29,7 @@ export class MJMessage extends Realm.Object<MJMessage> {
id: string id: string
mjApiUrl: string | null mjApiUrl: string | null
progress: number progress: number
category: MJCategroy category: MJImageType
imageClick: string | null // 图片点击(显示的小的) imageClick: string | null // 图片点击(显示的小的)
imageShow: string | null // 图片实际的地址 imageShow: string | null // 图片实际的地址
messageId: string // 消息ID可以是MJ中的也可以是API中的 messageId: string // 消息ID可以是MJ中的也可以是API中的
@ -105,6 +105,26 @@ export class SDConfig extends Realm.Object<SDConfig> {
} }
} }
// 放反推的提示词的对象
export class ReversePrompt extends Realm.Object<ReversePrompt> {
id: string
bookTaskDetailId: string
prompt: string
promptCN: string
isSelect: boolean
static schema: ObjectSchema = {
name: 'ReversePrompt',
properties: {
id: 'string',
bookTaskDetailId: "string",
prompt: 'string',
promptCN: 'string',
isSelect: 'bool'
},
primaryKey: 'id'
}
}
export class BookTaskDetailModel extends Realm.Object<BookTaskDetailModel> { export class BookTaskDetailModel extends Realm.Object<BookTaskDetailModel> {
id: string id: string
no: number no: number
@ -119,16 +139,19 @@ export class BookTaskDetailModel extends Realm.Object<BookTaskDetailModel> {
startTime: number | null // 开始时间 startTime: number | null // 开始时间
endTime: number | null // 结束时间 endTime: number | null // 结束时间
timeLimit: string | null // 事件实现0 -- 3000 timeLimit: string | null // 事件实现0 -- 3000
subValue: Realm.List<Subtitle> | null // 包含的字幕数据 subValue: string | null // 包含的字幕数据
characterTags: string[] | null // 角色标签 characterTags: string[] | null // 角色标签
gptPrompt: string | null // GPT提示词 gptPrompt: string | null // GPT提示词
mjMessage: MJMessage | null // MJ消息 mjMessage: MJMessage | null // MJ消息
outImagePath: string | null // 输出图片地址 outImagePath: string | null // 输出图片地址
subImagePath: string[] | null // 子图片地址 subImagePath: string[] | null // 子图片地址
imageLock: boolean // 图片锁
prompt: string | null // 提示 prompt: string | null // 提示
adetailer: boolean // 是否开启修脸 adetailer: boolean // 是否开启修脸
sdConifg: SDConfig | null // SD配置 sdConifg: SDConfig | null // SD配置
reversePrompt: ReversePrompt[] | null // 反推的提示词(数组)
subtitlePosition: string | null // 字幕位置 subtitlePosition: string | null // 字幕位置
status: BookTaskStatus
createTime: Date createTime: Date
updateTime: Date updateTime: Date
@ -148,16 +171,19 @@ export class BookTaskDetailModel extends Realm.Object<BookTaskDetailModel> {
startTime: 'int?', startTime: 'int?',
endTime: 'int?', endTime: 'int?',
timeLimit: 'string?', timeLimit: 'string?',
subValue: { type: 'list', objectType: 'Subtitle' }, subValue: 'string?',
reversePrompt: { type: 'list', objectType: 'ReversePrompt' },
characterTags: { type: 'list', objectType: 'string' }, characterTags: { type: 'list', objectType: 'string' },
gptPrompt: 'string?', gptPrompt: 'string?',
mjMessage: 'MJMessage?', mjMessage: 'MJMessage?',
outImagePath: 'string?', outImagePath: 'string?',
subImagePath: 'string[]', subImagePath: 'string[]',
imageLock: 'bool',
prompt: 'string?', prompt: 'string?',
adetailer: 'bool', adetailer: 'bool',
sdConifg: 'SDConfig?', sdConifg: 'SDConfig?',
subtitlePosition: 'string?', subtitlePosition: 'string?',
status: "string",
createTime: 'date', createTime: 'date',
updateTime: 'date' updateTime: 'date'
}, },

View File

@ -11,6 +11,8 @@ export class SoftwareModel extends Realm.Object<SoftwareModel> {
ttsSetting: string | null // TTS设置的json字符串 ttsSetting: string | null // TTS设置的json字符串
writeSetting: string | null // 文案的相关配置的json字符串 writeSetting: string | null // 文案的相关配置的json字符串
aiSetting: string | null // AI相关的配置的json字符串 aiSetting: string | null // AI相关的配置的json字符串
watermarkSetting: string | null // 水印相关的配置的json字符串
translationSetting: string | null // 翻译相关的配置的json字符串
static schema: ObjectSchema = { static schema: ObjectSchema = {
name: 'Software', name: 'Software',
@ -23,7 +25,9 @@ export class SoftwareModel extends Realm.Object<SoftwareModel> {
globalSetting: 'string', globalSetting: 'string',
ttsSetting: 'string?', // 可空 ttsSetting: 'string?', // 可空
writeSetting: 'string?', writeSetting: 'string?',
aiSetting: 'string?' aiSetting: 'string?',
watermarkSetting: 'string?',
translationSetting: 'string?'
}, },
// 主键为_id // 主键为_id
primaryKey: 'id' primaryKey: 'id'

View File

@ -1,21 +1,16 @@
import Realm from 'realm' import Realm from 'realm'
import path from 'path'
import { BaseService } from '../baseService.js'
import { define } from '../../../define.js'
import { BookTaskModel } from '../../model/Book/bookTask.js'
import { import {
BookBackTaskStatus, BookBackTaskStatus,
BookBackTaskType, BookBackTaskType,
BookTaskStatus,
TaskExecuteType TaskExecuteType
} from '../../../enum/bookEnum.js' } from '../../../enum/bookEnum.js'
import { errorMessage, successMessage } from '../../../../main/generalTools.js' import { errorMessage, successMessage } from '../../../../main/Public/generalTools'
import { BaseRealmService } from './bookBasic' import { BaseRealmService } from './bookBasic'
import { isEmpty } from 'lodash' import { isEmpty } from 'lodash'
import { DefaultObject } from 'realm/dist/public-types/schema.js'
import { BookModel } from '../../model/Book/book.js'
import { OtherData } from '../../../enum/softwareEnum.js' import { OtherData } from '../../../enum/softwareEnum.js'
import { BookBackTaskList } from '../../model/Book/BookBackTaskListModel.js' import { BookBackTaskList } from '../../model/Book/BookBackTaskListModel.js'
import { Book } from '../../../../model/book.js'
import { GeneralResponse } from '../../../../model/generalResponse.js'
const { v4: uuidv4 } = require('uuid') const { v4: uuidv4 } = require('uuid')
export class BookBackTaskListService extends BaseRealmService { export class BookBackTaskListService extends BaseRealmService {
@ -44,7 +39,7 @@ export class BookBackTaskListService extends BaseRealmService {
* bookId status * bookId status
* @param query bookIdbookTaskIdnametypestatus * @param query bookIdbookTaskIdnametypestatus
*/ */
GetBookBackTaskList(query) { GetBookBackTaskList(query: Book.QueryBookBackTaskCondition) {
try { try {
if (query == null) { if (query == null) {
throw new Error('查询后台队列任务失败,没有查询条件') throw new Error('查询后台队列任务失败,没有查询条件')
@ -53,33 +48,36 @@ export class BookBackTaskListService extends BaseRealmService {
throw new Error('查询后台队列任务失败,没有查询条件') throw new Error('查询后台队列任务失败,没有查询条件')
} }
// 构建查询条件
// 下面时可空的条件
let queryString = ''
if (query.bookId) {
queryString = `bookId = ${query.bookId}`
}
if (query.bookTaskId) {
queryString += ` && bookTaskId = ${query.bookTaskId}`
}
if (query.name) {
queryString += ` && name = ${query.name}`
}
if (query.type) {
queryString += ` && type = ${query.type}`
}
if (query.status) {
queryString += ` && status = ${query.status}`
}
if (query.executeType) {
queryString += ` && executeType = ${query.executeType}`
}
// 获取数据 // 获取数据
let tasks = this.realm let tasks = this.realm
.objects('BookBackTaskList') .objects('BookBackTaskList')
.filtered(queryString)
.sorted('createTime', true) .sorted('createTime', true)
// 构建查询条件
if (query.id) {
tasks.filtered('id = $0', query.id)
}
if (query.bookId) {
tasks.filtered('bookId = $0', query.bookId)
}
if (query.bookTaskId) {
tasks.filtered('bookTaskId = $0', query.bookTaskId)
}
if (query.name) {
tasks.filtered('name = $0', query.name)
}
if (query.type) {
tasks.filtered('type = $0', query.type)
}
if (query.status) {
tasks.filtered('status = $0', query.status)
}
if (query.executeType) {
tasks.filtered('executeType = $0', query.executeType)
}
let res let res
if (query.count) { if (query.count) {
res = tasks.slice(0, query.count) res = tasks.slice(0, query.count)
@ -107,12 +105,12 @@ export class BookBackTaskListService extends BaseRealmService {
let tasks = this.realm let tasks = this.realm
.objects<BookBackTaskList>('BookBackTaskList') .objects<BookBackTaskList>('BookBackTaskList')
.filtered( .filtered(
'status == $0 && executeType == $1', '(status == $0 || status == $1) && executeType == $2',
BookBackTaskStatus.WAIT, BookBackTaskStatus.WAIT,
BookBackTaskStatus.RECONNECT,
executeType ? executeType : TaskExecuteType.AUTO executeType ? executeType : TaskExecuteType.AUTO
) )
.sorted('createTime', false) .sorted('createTime', false)
if (count != null) { if (count != null) {
tasks = tasks.slice(0, count) as unknown as Realm.Results<BookBackTaskList> tasks = tasks.slice(0, count) as unknown as Realm.Results<BookBackTaskList>
} }
@ -169,8 +167,7 @@ export class BookBackTaskListService extends BaseRealmService {
} }
// 开始往数据库中写数据 // 开始往数据库中写数据
let name = `${book.name}-${bookTask ? bookTask.name : 'default'}-${ let name = `${book.name}-${bookTask ? bookTask.name : 'default'}-${bookTaskDetail ? bookTaskDetail.name : 'default'
bookTaskDetail ? bookTaskDetail.name : 'default'
}-${taskType}` }-${taskType}`
let bookBackTask = { let bookBackTask = {
@ -183,15 +180,14 @@ export class BookBackTaskListService extends BaseRealmService {
executeType: executeType, executeType: executeType,
status: BookBackTaskStatus.WAIT, status: BookBackTaskStatus.WAIT,
createTime: new Date(), createTime: new Date(),
updateTime: new Date() updateTime: new Date(),
} startTime: 0,
endTime: 0
} as TaskModal.Task
this.realm.write(() => { this.realm.write(() => {
this.realm.create('BookBackTaskList', bookBackTask) this.realm.create('BookBackTaskList', bookBackTask)
}) })
// 添加成功之后,调用开始执行任务的方法
await global.taskManager.ExecuteAutoTask()
return successMessage( return successMessage(
bookBackTask, bookBackTask,
'新增后台队列任务到数据库成功', '新增后台队列任务到数据库成功',
@ -210,7 +206,7 @@ export class BookBackTaskListService extends BaseRealmService {
* *
* @param bookBackTask * @param bookBackTask
*/ */
UpdateTaskStatus(bookBackTask) { UpdateTaskStatus(bookBackTask: Book.UpdateBookTaskListStatus): GeneralResponse.SuccessItem {
try { try {
// 判断数据是不是存在 // 判断数据是不是存在
if (isEmpty(bookBackTask.id) || isEmpty(bookBackTask.status)) { if (isEmpty(bookBackTask.id) || isEmpty(bookBackTask.status)) {
@ -219,7 +215,7 @@ export class BookBackTaskListService extends BaseRealmService {
// 开始修改 // 开始修改
this.transaction(() => { this.transaction(() => {
// 获取指定ID的队列任务 // 获取指定ID的队列任务
let _bookBackTask = this.realm.objectForPrimaryKey('BookBackTaskList', bookBackTask.id) let _bookBackTask = this.realm.objectForPrimaryKey('BookBackTaskList', bookBackTask.id) as TaskModal.Task
// 判断数据是不是存在 // 判断数据是不是存在
if (_bookBackTask == null) { if (_bookBackTask == null) {
throw new Error('修改后台队列任务失败,数据不存在') throw new Error('修改后台队列任务失败,数据不存在')
@ -229,6 +225,16 @@ export class BookBackTaskListService extends BaseRealmService {
if (bookBackTask.errorMessage) { if (bookBackTask.errorMessage) {
_bookBackTask.errorMessage = bookBackTask.errorMessage _bookBackTask.errorMessage = bookBackTask.errorMessage
} }
// 根据状态修改结束和完成时间
if (bookBackTask.status == BookBackTaskStatus.RUNNING) {
_bookBackTask.startTime = new Date().getTime()
} else if (bookBackTask.status == BookBackTaskStatus.DONE || bookBackTask.status == BookBackTaskStatus.FAIL || bookBackTask.status == BookBackTaskStatus.PAUSE) {
_bookBackTask.endTime = new Date().getTime()
} else if (bookBackTask.status == BookBackTaskStatus.RECONNECT) {
_bookBackTask.startTime = 0;
_bookBackTask.endTime = 0;
}
}) })
return successMessage( return successMessage(
bookBackTask, bookBackTask,

View File

@ -7,12 +7,13 @@ import path from 'path'
import { import {
BookTaskDetailModel, BookTaskDetailModel,
MJMessage, MJMessage,
ReversePrompt,
SDConfig, SDConfig,
Subtitle, Subtitle,
WebuiConfig WebuiConfig
} from '../../model/Book/bookTaskDetail' } from '../../model/Book/bookTaskDetail'
import { BookBackTaskList } from '../../model/Book/BookBackTaskListModel' import { BookBackTaskList } from '../../model/Book/BookBackTaskListModel'
import { TaskExecuteType } from '../../../enum/bookEnum' import { BookTaskStatus, TaskExecuteType } from '../../../enum/bookEnum'
import { version } from '../../../../../package.json' import { version } from '../../../../../package.json'
let dbPath = path.resolve(define.db_path, 'book.realm') let dbPath = path.resolve(define.db_path, 'book.realm')
@ -77,6 +78,83 @@ const migration = (oldRealm: Realm, newRealm: Realm) => {
newBookTask[i].subtitlePosition = null // 设置字幕位置的默认值 newBookTask[i].subtitlePosition = null // 设置字幕位置的默认值
} }
} }
if (oldRealm.schemaVersion < 9) {
const oldBookTask = oldRealm.objects('BookBackTaskList')
const newBookTask = newRealm.objects('BookBackTaskList')
for (let i = 0; i < oldBookTask.length; i++) {
newBookTask[i].startTime = 0
newBookTask[i].endTime = 0
}
}
if (oldRealm.schemaVersion < 10) {
const oldBookTask = oldRealm.objects('BookTaskDetail')
const newBookTask = newRealm.objects('BookTaskDetail')
for (let i = 0; i < oldBookTask.length; i++) {
newBookTask[i].status = BookTaskStatus.WAIT
}
}
if (oldRealm.schemaVersion < 11) {
const oldBookTask = oldRealm.objects('BookTaskDetail')
const newBookTask = newRealm.objects('BookTaskDetail')
for (let i = 0; i < oldBookTask.length; i++) {
newBookTask[i].reversePrompt = null
}
}
if (oldRealm.schemaVersion < 13) {
const oldBookTask = oldRealm.objects('ReversePrompt')
const newBookTask = newRealm.objects('ReversePrompt')
for (let i = 0; i < oldBookTask.length; i++) {
newBookTask[i].promptCN = null;
newBookTask[i].isSelect = false;
}
}
if (oldRealm.schemaVersion < 15) {
const oldBookTask = oldRealm.objects('BookTaskDetail')
const newBookTask = newRealm.objects('BookTaskDetail')
for (let i = 0; i < oldBookTask.length; i++) {
newBookTask[i].imageCategory = null;
}
}
if (oldRealm.schemaVersion < 16) {
const oldBookTask = oldRealm.objects('BookTask')
const newBookTask = newRealm.objects('BookTask')
for (let i = 0; i < oldBookTask.length; i++) {
newBookTask[i].subImageFolder = null;
}
}
if (oldRealm.schemaVersion < 17) {
const oldBookTask = oldRealm.objects('BookTaskDetail')
const newBookTask = newRealm.objects('BookTaskDetail')
for (let i = 0; i < oldBookTask.length; i++) {
newBookTask[i].imageLock = false;
}
}
if (oldRealm.schemaVersion < 19) {
const oldBookTask = oldRealm.objects('Book')
const newBookTask = newRealm.objects('Book')
for (let i = 0; i < oldBookTask.length; i++) {
newBookTask[i].draftSrtStyle = null;
newBookTask[i].backgroundMusic = null;
newBookTask[i].friendlyReminder = null;
}
}
if (oldRealm.schemaVersion < 20) {
const oldBookTask = oldRealm.objects('BookTask')
const newBookTask = newRealm.objects('BookTask')
for (let i = 0; i < oldBookTask.length; i++) {
newBookTask[i].draftSrtStyle = null;
newBookTask[i].backgroundMusic = null;
newBookTask[i].friendlyReminder = null;
}
}
if (oldRealm.schemaVersion < 21) {
const oldBookTask = oldRealm.objects('BookTaskDetail')
const newBookTask = newRealm.objects('BookTaskDetail')
for (let i = 0; i < oldBookTask.length; i++) {
newBookTask[i].subValue = '[]'
}
}
} }
export class BaseRealmService extends BaseService { export class BaseRealmService extends BaseService {
@ -114,10 +192,11 @@ export class BaseRealmService extends BaseService {
SDConfig, SDConfig,
WebuiConfig, WebuiConfig,
BookTaskModel, BookTaskModel,
ReversePrompt,
BookTaskDetailModel BookTaskDetailModel
], ],
path: this.dbpath, path: this.dbpath,
schemaVersion: 8, schemaVersion: 21,
migration: migration migration: migration
} }
this.realm = await Realm.open(config) this.realm = await Realm.open(config)

View File

@ -3,13 +3,16 @@ import { BookModel } from '../../model/Book/book.js'
import path from 'path' import path from 'path'
import { BaseService } from '../baseService.js' import { BaseService } from '../baseService.js'
import { define } from '../../../define.js' import { define } from '../../../define.js'
import { BookTaskStatus, BookType } from '../../../enum/bookEnum.js' import { BookImageCategory, BookTaskStatus, BookType } from '../../../enum/bookEnum.js'
import { successMessage } from '../../../../main/generalTools.js' import { successMessage } from '../../../../main/Public/generalTools'
import { CheckFolderExistsOrCreate, CopyFileOrFolder } from '../../../Tools/file.js' import { CheckFolderExistsOrCreate, CopyFileOrFolder } from '../../../Tools/file'
const { v4: uuidv4 } = require('uuid') const { v4: uuidv4 } = require('uuid')
import { BookTaskService } from './bookTaskService' import { BookTaskService } from './bookTaskService'
import { BaseRealmService } from './bookBasic.js' import { BaseRealmService } from './bookBasic.js'
import { isEmpty } from 'lodash' import { isEmpty } from 'lodash'
import { FfmpegOptions } from '../../../../main/Service/ffmpegOptions.js'
import { version } from '../../../../../package.json'
import { Book } from '../../../../model/book.js'
export class BookService extends BaseRealmService { export class BookService extends BaseRealmService {
static instance: BookService | null = null static instance: BookService | null = null
@ -36,14 +39,14 @@ export class BookService extends BaseRealmService {
* null * null
* @param bookId * @param bookId
*/ */
GetBookDataById(bookId) { GetBookDataById(bookId): Book.SelectBook | null {
try { try {
if (isEmpty(bookId)) { if (isEmpty(bookId)) {
throw new Error('获取小说信息失败缺少小说ID') throw new Error('获取小说信息失败缺少小说ID')
} }
let books = this.realm.objects<BookModel>('Book').filtered('id = $0', bookId) let books = this.realm.objects<BookModel>('Book').filtered('id = $0', bookId)
if (books.length <= 0) { if (books.length <= 0) {
return successMessage(null, '通过ID获取小说数据成功', 'ReverseBook_GetBookDataById') return null
} else { } else {
// 对返回的数据进行处理 // 对返回的数据进行处理
let resBooks = Array.from(books).map((book) => { let resBooks = Array.from(books).map((book) => {
@ -63,8 +66,7 @@ export class BookService extends BaseRealmService {
} }
return bookObj return bookObj
}) })
return resBooks[0]
return successMessage(resBooks[0], '通过ID获取小说数据成功', 'ReverseBook_GetBookDataById')
} }
} catch (error) { } catch (error) {
throw error throw error
@ -120,8 +122,10 @@ export class BookService extends BaseRealmService {
: '', : '',
imageFolder: book.imageFolder imageFolder: book.imageFolder
? path.resolve(define.project_path, book.imageFolder.replace(/\\/g, '/')) ? path.resolve(define.project_path, book.imageFolder.replace(/\\/g, '/'))
: '' : '',
} imageStyle: book.imageStyle ? Array.from(book.imageStyle) : [],
customizeImageStyle: book.customizeImageStyle ? Array.from(book.imageStyle) : [],
} as Book.SelectBook
return bookObj return bookObj
}) })
@ -186,14 +190,31 @@ export class BookService extends BaseRealmService {
await CopyFileOrFolder(book.oldVideoPath, oldVideoPath) await CopyFileOrFolder(book.oldVideoPath, oldVideoPath)
} }
let ffmpegOptions = new FfmpegOptions();
let res = await ffmpegOptions.FfmpegCompressVideo(oldVideoPath, 800, "2000k")
// 创建对应的文件夹 // 创建对应的文件夹
await CheckFolderExistsOrCreate(bookFolderPath) await CheckFolderExistsOrCreate(bookFolderPath)
await CheckFolderExistsOrCreate(imageFolder) await CheckFolderExistsOrCreate(imageFolder)
await CheckFolderExistsOrCreate(bookTaskImageFolder) // 创建默认的任务文件夹 await CheckFolderExistsOrCreate(bookTaskImageFolder) // 创建默认的任务文件夹
// 修改数据 // 修改数据
book.oldVideoPath = path.relative(define.project_path, oldVideoPath) book.oldVideoPath = path.relative(define.project_path, oldVideoPath)
let imageCategory = BookImageCategory.MJ
if (book.type == BookType.SD_REVERSE) {
imageCategory = BookImageCategory.SD
} else if (book.type == BookType.MJ_REVERSE) {
imageCategory = BookImageCategory.MJ
} else if (book.type == BookType.ORIGINAL) {
imageCategory = BookImageCategory.MJ
} else {
throw new Error('未知的小说类型')
}
this.realm.write(() => { this.realm.write(() => {
book.version = version
this.realm.create('Book', book) this.realm.create('Book', book)
// 添加一个任务 // 添加一个任务
let bookTask = { let bookTask = {
id: uuidv4(), id: uuidv4(),
@ -210,7 +231,9 @@ export class BookService extends BaseRealmService {
errorMsg: null, errorMsg: null,
isAuto: false, isAuto: false,
updateTime: new Date(), updateTime: new Date(),
createTime: new Date() createTime: new Date(),
version: version,
imageCategory: imageCategory
} }
// 添加任务 // 添加任务
@ -265,7 +288,7 @@ export class BookService extends BaseRealmService {
// 检查小说ID对应的数据是不是存在 // 检查小说ID对应的数据是不是存在
let bookRes = this.GetBookDataById(bookId) let bookRes = this.GetBookDataById(bookId)
if (bookRes.data == null) { if (bookRes == null) {
throw new Error('修改小说数据失败小说ID对应的数据不存在') throw new Error('修改小说数据失败小说ID对应的数据不存在')
} }
@ -279,11 +302,11 @@ export class BookService extends BaseRealmService {
}) })
bookRes = this.GetBookDataById(bookId) bookRes = this.GetBookDataById(bookId)
if (bookRes.data == null) { if (bookRes == null) {
throw new Error('获取修改后的小说数据失败小说ID对应的数据不存在') throw new Error('获取修改后的小说数据失败小说ID对应的数据不存在')
} }
return successMessage(bookRes.data, '修改小说数据成功', 'ReverseBook_UpdateBookData') return successMessage(bookRes, '修改小说数据成功', 'ReverseBook_UpdateBookData')
} catch (error) { } catch (error) {
throw error throw error
} }

View File

@ -4,19 +4,21 @@ import { BaseService } from '../baseService.js'
import { define } from '../../../define.js' import { define } from '../../../define.js'
import { BookTaskModel } from '../../model/Book/bookTask.js' import { BookTaskModel } from '../../model/Book/bookTask.js'
import { BookTaskStatus } from '../../../enum/bookEnum.js' import { BookTaskStatus } from '../../../enum/bookEnum.js'
import { successMessage } from '../../../../main/generalTools.js' import { successMessage } from '../../../../main/Public/generalTools'
import { BaseRealmService } from './bookBasic' import { BaseRealmService } from './bookBasic'
import { endsWith, isEmpty } from 'lodash' import { cloneDeep, endsWith, isEmpty } from 'lodash'
import { book } from '../../../../preload/book.js' import { book } from '../../../../preload/book.js'
import { DefaultObject } from 'realm/dist/public-types/schema.js' import { DefaultObject } from 'realm/dist/public-types/schema.js'
import { JoinPath } from '../../../Tools/file.js' import { JoinPath } from '../../../Tools/file'
import { BookTaskDetailModel } from '../../model/Book/bookTaskDetail.js' import { BookTaskDetailModel, ReversePrompt } from '../../model/Book/bookTaskDetail.js'
const { v4: uuidv4 } = require('uuid') const { v4: uuidv4 } = require('uuid')
import { Book } from "../../../../model/book"
import { GeneralResponse } from '../../../../model/generalResponse.js'
let dbPath = path.resolve(define.db_path, 'book.realm') let dbPath = path.resolve(define.db_path, 'book.realm')
// 版本迁移 // 版本迁移
const migration = (oldRealm: Realm, newRealm: Realm) => {} const migration = (oldRealm: Realm, newRealm: Realm) => { }
export class BookTaskDetailService extends BaseRealmService { export class BookTaskDetailService extends BaseRealmService {
static instance: BookTaskDetailService | null = null static instance: BookTaskDetailService | null = null
@ -43,7 +45,7 @@ export class BookTaskDetailService extends BaseRealmService {
* *
* @param condition idnamebookIdbookTaskId * @param condition idnamebookIdbookTaskId
*/ */
GetBookTaskData(condition) { GetBookTaskData(condition: Book.QueryBookTaskDetailCondition) {
try { try {
if (condition == null) { if (condition == null) {
throw new Error('查询小说分镜信息,查询条件不能为空') throw new Error('查询小说分镜信息,查询条件不能为空')
@ -62,6 +64,7 @@ export class BookTaskDetailService extends BaseRealmService {
tasksToDelete = tasksToDelete.filtered('name==$0', condition.name) tasksToDelete = tasksToDelete.filtered('name==$0', condition.name)
} }
let resData = Array.from(tasksToDelete).map((item) => { let resData = Array.from(tasksToDelete).map((item) => {
let resObj = { let resObj = {
...item, ...item,
@ -71,9 +74,17 @@ export class BookTaskDetailService extends BaseRealmService {
outImagePath: JoinPath(define.project_path, item.outImagePath), outImagePath: JoinPath(define.project_path, item.outImagePath),
subImagePath: (item.subImagePath as string[])?.map((subImage) => { subImagePath: (item.subImagePath as string[])?.map((subImage) => {
return JoinPath(define.project_path, subImage) return JoinPath(define.project_path, subImage)
}) }),
characterTags: item.characterTags ? item.characterTags.map((tag) => tag) : null,
subValue: item.subValue,
reversePrompt: item.reversePrompt.map((reversePrompt) => {
return {
...reversePrompt
} }
return resObj }),
mjMessage: item.mjMessage ? item.mjMessage.toJSON() : null,
}
return cloneDeep(resObj)
}) })
return successMessage( return successMessage(
resData, resData,
@ -89,7 +100,7 @@ export class BookTaskDetailService extends BaseRealmService {
* ID获取指定的小说任务分镜详细数据 * ID获取指定的小说任务分镜详细数据
* @param bookTaskDetailId * @param bookTaskDetailId
*/ */
public GetBookTaskDetailDataById(bookTaskDetailId: string) { public GetBookTaskDetailDataById(bookTaskDetailId: string): Book.SelectBookTaskDetail {
try { try {
if (bookTaskDetailId == null) { if (bookTaskDetailId == null) {
throw new Error('获取小说任务详细信息失败缺少ID') throw new Error('获取小说任务详细信息失败缺少ID')
@ -97,17 +108,9 @@ export class BookTaskDetailService extends BaseRealmService {
let bookTaskDetails = this.GetBookTaskData({ id: bookTaskDetailId }) let bookTaskDetails = this.GetBookTaskData({ id: bookTaskDetailId })
if (bookTaskDetails.data.length <= 0) { if (bookTaskDetails.data.length <= 0) {
return successMessage( return null;
null,
'未找到对应的小说任务详细信息',
'BookTaskDetailService_GetBookTaskDetailDataById'
)
} else { } else {
return successMessage( return bookTaskDetails.data[0]
bookTaskDetails.data[0],
'获取小说任务详细信息成功',
'BookTaskDetailService_GetBookTaskDetailDataById'
)
} }
} catch (error) { } catch (error) {
throw error throw error
@ -142,6 +145,7 @@ export class BookTaskDetailService extends BaseRealmService {
bookTaskDetail.name = name bookTaskDetail.name = name
bookTaskDetail.id = uuidv4() bookTaskDetail.id = uuidv4()
bookTaskDetail.imageLock = false
bookTaskDetail.createTime = new Date() bookTaskDetail.createTime = new Date()
bookTaskDetail.updateTime = new Date() bookTaskDetail.updateTime = new Date()
bookTaskDetail.adetailer = false // 先写死false bookTaskDetail.adetailer = false // 先写死false
@ -165,7 +169,7 @@ export class BookTaskDetailService extends BaseRealmService {
* @param bookTaskDetailId * @param bookTaskDetailId
* @param updateData * @param updateData
*/ */
UpdateBookTaskDetail(bookTaskDetailId: string, updateData) { UpdateBookTaskDetail(bookTaskDetailId: string, updateData: Book.SelectBookTaskDetail) {
try { try {
this.transaction(() => { this.transaction(() => {
let bookTaskDetail = this.realm.objectForPrimaryKey('BookTaskDetail', bookTaskDetailId) let bookTaskDetail = this.realm.objectForPrimaryKey('BookTaskDetail', bookTaskDetailId)
@ -188,6 +192,53 @@ export class BookTaskDetailService extends BaseRealmService {
} }
} }
UpdateBookTaskDetailMjMessage(bookTaskDetailId: string, mjMessage: Book.MJMessage): void {
try {
this.transaction(() => {
let mjMessageRes = this.realm.objectForPrimaryKey('MJMessage', bookTaskDetailId)
let bookTaskDetail = this.realm.objectForPrimaryKey('BookTaskDetail', bookTaskDetailId)
if (bookTaskDetail.mjMessage == null) {
// 新增
mjMessage.id = bookTaskDetailId
bookTaskDetail.mjMessage = mjMessage
} else {
for (const key in mjMessage) {
mjMessageRes[key] = mjMessage[key]
}
}
})
} catch (error) {
throw error
}
}
/**
* ID的反推提示词数据
* @param bookTaskDetailId ID
* @param reversePromptId ID
* @param updateData
*/
UpdateBookTaskDetailReversePrompt(bookTaskDetailId: string, reversePromptId: string, reversePrompt: Book.ReversePrompt): GeneralResponse.SuccessItem {
try {
this.transaction(() => {
let bookTaskDetails = this.realm.objects<ReversePrompt>("ReversePrompt");
bookTaskDetails = bookTaskDetails.filtered("id = $0 && bookTaskDetailId = $1", reversePromptId, bookTaskDetailId);
if (bookTaskDetails.length <= 0) {
throw new Error("未找到执行的翻译数据,无法写回")
}
let bookTaskDetail = bookTaskDetails[0];
// 直接写入
for (const key in reversePrompt) {
bookTaskDetail[key] = reversePrompt[key]
}
})
return successMessage(null, `${reversePromptId} 更新完成`, "BookTaskDetailService_UpdateBookTaskDetailReversePrompt")
} catch (error) {
throw error
}
}
/** /**
* ID和小说任务ID * ID和小说任务ID
* @param condition bookIdbookTaskIdnameid * @param condition bookIdbookTaskIdnameid
@ -223,4 +274,18 @@ export class BookTaskDetailService extends BaseRealmService {
throw error throw error
} }
} }
/**
* ID的小说任务详细数据中的所有的反推提示词数据
* @param bookTaskDetailId ID
*/
DeleteBookTaskDetailReversePromptById(bookTaskDetailId: string): void {
let bookTaskDetail = this.realm.objectForPrimaryKey('BookTaskDetail', bookTaskDetailId);
if (bookTaskDetail == null) {
throw new Error('删除小说任务详细信息的反推提示词失败,未找到对应的分镜信息')
}
this.transaction(() => {
this.realm.delete(bookTaskDetail.reversePrompt)
})
}
} }

View File

@ -1,24 +1,29 @@
import Realm from 'realm' import Realm from 'realm'
import path from 'path' import path from 'path'
import { BaseService } from '../baseService.js'
import { define } from '../../../define.js' import { define } from '../../../define.js'
import { BookTaskModel } from '../../model/Book/bookTask.js' import { BookTaskModel } from '../../model/Book/bookTask.js'
import { BookBackTaskStatus, BookTaskStatus } from '../../../enum/bookEnum.js' import { BookBackTaskStatus, BookImageCategory, BookTaskStatus } from '../../../enum/bookEnum.js'
import { successMessage } from '../../../../main/generalTools.js' import { successMessage } from '../../../../main/Public/generalTools'
import { BaseRealmService } from './bookBasic' import { BaseRealmService } from './bookBasic'
import { isEmpty } from 'lodash' import { JoinPath } from '../../../Tools/file'
import { JoinPath } from '../../../Tools/file.js'
import { BookBackTaskList } from '../../model/Book/BookBackTaskListModel.js' import { BookBackTaskList } from '../../model/Book/BookBackTaskListModel.js'
const { v4: uuidv4 } = require('uuid') const { v4: uuidv4 } = require('uuid')
import { Book } from '../../../../model/book'
import { TagDefine } from '../../../tagDefine.js'
import { ImageStyleDefine } from "../../../../define/iamgeStyleDefine"
import { cloneDeep } from 'lodash'
import { GeneralResponse } from '../../../../model/generalResponse'
let dbPath = path.resolve(define.db_path, 'book.realm') let dbPath = path.resolve(define.db_path, 'book.realm')
export class BookTaskService extends BaseRealmService { export class BookTaskService extends BaseRealmService {
static instance: BookTaskService | null = null static instance: BookTaskService | null = null
realm: Realm realm: Realm
tagDefine: TagDefine
private constructor() { private constructor() {
super() super()
this.tagDefine = new TagDefine()
} }
/** /**
@ -38,7 +43,7 @@ export class BookTaskService extends BaseRealmService {
* *
* @param bookTaskCondition idbookIdnamenopage, pageSize * @param bookTaskCondition idbookIdnamenopage, pageSize
*/ */
GetBookTaskData(bookTaskCondition) { GetBookTaskData(bookTaskCondition: Book.QueryBookTaskCondition): GeneralResponse.ErrorItem | GeneralResponse.SuccessItem {
try { try {
// 获取所有的小说数据,并进行时间降序排序 // 获取所有的小说数据,并进行时间降序排序
let bookTasks = this.realm.objects<BookTaskModel>('BookTask') let bookTasks = this.realm.objects<BookTaskModel>('BookTask')
@ -62,7 +67,7 @@ export class BookTaskService extends BaseRealmService {
} }
let bookTask_length = bookTasks.length let bookTask_length = bookTasks.length
bookTasks = bookTasks.sorted('updateTime', true) // bookTasks = bookTasks.sorted('updateTime', true)
// 判断是不是有page和pageSize有的话对查询返回的信息做分页 // 判断是不是有page和pageSize有的话对查询返回的信息做分页
if (bookTaskCondition.page && bookTaskCondition.pageSize) { if (bookTaskCondition.page && bookTaskCondition.pageSize) {
bookTasks = bookTasks.slice( bookTasks = bookTasks.slice(
@ -73,22 +78,24 @@ export class BookTaskService extends BaseRealmService {
// 做一下数据转换 // 做一下数据转换
// 将realm对象数组转换为普通对象数组 // 将realm对象数组转换为普通对象数组
// 将realm对象数组转换为普通对象数组并处理异步操作
let res_bookTasks = Array.from(bookTasks).map((bookTask) => { let res_bookTasks = Array.from(bookTasks).map((bookTask) => {
// 这里可以直接操作普通对象 // 直接操作普通对象
let bookObj = { return {
...bookTask, ...bookTask,
styleList: bookTask.styleList ? Array.from(bookTask.styleList) : [], imageStyle: bookTask.imageStyle ? Array.from(bookTask.imageStyle) : [],
customizeImageStyle: bookTask.customizeImageStyle ? Array.from(bookTask.customizeImageStyle) : [],
generateVideoPath: JoinPath(define.project_path, bookTask.generateVideoPath), generateVideoPath: JoinPath(define.project_path, bookTask.generateVideoPath),
srtPath: JoinPath(define.project_path, bookTask.srtPath), srtPath: JoinPath(define.project_path, bookTask.srtPath),
audioPath: JoinPath(define.project_path, bookTask.audioPath), audioPath: JoinPath(define.project_path, bookTask.audioPath),
imageFolder: JoinPath(define.project_path, bookTask.imageFolder) imageFolder: JoinPath(define.project_path, bookTask.imageFolder),
} imageCategory: bookTask.imageCategory ? bookTask.imageCategory : BookImageCategory.MJ, // 默认使用MJ出图
return bookObj } as Book.SelectBookTask;
}) })
return successMessage( return successMessage(
{ {
bookTasks: res_bookTasks, bookTasks: JSON.parse(JSON.stringify(res_bookTasks)),
total: bookTask_length total: bookTask_length
}, },
'查询小说任务成功', '查询小说任务成功',
@ -103,7 +110,7 @@ export class BookTaskService extends BaseRealmService {
* ID获取小说批次任务的数据 * ID获取小说批次任务的数据
* @param bookTaskId * @param bookTaskId
*/ */
GetBookTaskDataById(bookTaskId: string) { GetBookTaskDataById(bookTaskId: string): Book.SelectBookTask {
try { try {
if (bookTaskId == null) { if (bookTaskId == null) {
throw new Error('小说任务ID不能为空') throw new Error('小说任务ID不能为空')
@ -111,13 +118,9 @@ export class BookTaskService extends BaseRealmService {
let bookTasks = this.GetBookTaskData({ id: bookTaskId }) let bookTasks = this.GetBookTaskData({ id: bookTaskId })
if (bookTasks.data.bookTasks.length <= 0) { if (bookTasks.data.bookTasks.length <= 0) {
return successMessage(null, '未找到对应的小说任务', 'BookTaskService_GetBookTaskDataById') throw new Error('未找到对应的小说任务')
} else { } else {
return successMessage( return bookTasks.data.bookTasks[0]
bookTasks.data.bookTasks[0],
'查询小说任务成功',
'BookTaskService_GetBookTaskDataById'
)
} }
} catch (error) { } catch (error) {
throw error throw error
@ -183,6 +186,28 @@ export class BookTaskService extends BaseRealmService {
} }
} }
/**
*
* @param bookTaskId ID
* @param data
*/
UpdetedBookTaskData(bookTaskId: string, data: Book.SelectBookTask): void {
try {
this.transaction(() => {
let updateData = this.realm.objectForPrimaryKey('BookTask', bookTaskId)
if (updateData == null) {
throw new Error('未找到对应的小说任务详细信息')
}
// 开始修改
for (let key in data) {
updateData[key] = data[key]
}
})
} catch (error) {
throw error
}
}
// 添加一条数据 // 添加一条数据
AddOrModifyBookTask(bookTask) { AddOrModifyBookTask(bookTask) {
try { try {
@ -220,4 +245,73 @@ export class BookTaskService extends BaseRealmService {
throw error throw error
} }
} }
private resetBookTask(bookTaskId: string) {
let modifyBookTask = this.realm.objectForPrimaryKey('BookTask', bookTaskId)
modifyBookTask.status = BookTaskStatus.WAIT
modifyBookTask.errorMsg = "";
modifyBookTask.updateTime = new Date()
modifyBookTask.imageStyle = []
modifyBookTask.autoAnalyzeCharacter = undefined
modifyBookTask.customizeImageStyle = []
modifyBookTask.videoConfig = undefined
modifyBookTask.prefixPrompt = undefined
modifyBookTask.suffixPrompt = undefined
modifyBookTask.subImageFolder = []
let bookTaskDetails = this.realm.objects('BookTaskDetail').filtered('bookTaskId = $0', bookTaskId)
// 开始删除数据
bookTaskDetails.forEach(bookTaskDetail => {
// 删除MJMessage
if (bookTaskDetail.mjMessage) {
this.realm.delete(bookTaskDetail.mjMessage)
}
if (bookTaskDetail.reversePrompt) {
(bookTaskDetail.reversePrompt as any[]).forEach(item => {
this.realm.delete(item)
})
}
if (bookTaskDetail.sdConifg) {
this.realm.delete(bookTaskDetail.sdConifg)
}
this.realm.delete(bookTaskDetail)
})
}
/**
*
* @param bookTaskId ID
*/
ResetBookTask(bookTaskId: string): void {
try {
// 开始重置数据,先重置小说批次数据,在重置其他
this.transaction(() => {
this.resetBookTask(bookTaskId)
})
} catch (error) {
throw error
}
}
/**
*
* @param bookTaskId ID
*/
DeleteBookTask(bookTaskId: string): void {
try {
this.transaction(() => {
// 先调用清除数据的方法
this.resetBookTask(bookTaskId)
// 删除批次数据
let bookTask = this.realm.objectForPrimaryKey('BookTask', bookTaskId)
if (bookTask == null) {
throw new Error('未找到对应的小说任务,无法执行删除操作')
}
this.realm.delete(bookTask)
})
} catch (error) {
throw error
}
}
} }

View File

@ -2,9 +2,9 @@ import Realm, { UpdateMode } from 'realm'
import path from 'path' import path from 'path'
import { BaseService } from '../baseService.js' import { BaseService } from '../baseService.js'
import { define } from '../../../define.js' import { define } from '../../../define.js'
import { SoftwareModel } from '../../model/SoftWare/software.js' import { SoftwareModel } from '../../model/SoftWare/software'
import { ComponentSize, SoftwareThemeType } from '../../../enum/softwareEnum.js' import { ComponentSize, SoftwareThemeType } from '../../../enum/softwareEnum.js'
import { errorMessage, successMessage } from '../../../../main/generalTools.js' import { errorMessage, successMessage } from '../../../../main/Public/generalTools'
import { BaseSoftWareService } from './softwareBasic.js' import { BaseSoftWareService } from './softwareBasic.js'
import { isEmpty } from 'lodash' import { isEmpty } from 'lodash'
const { v4: uuidv4 } = require('uuid') const { v4: uuidv4 } = require('uuid')

View File

@ -2,13 +2,14 @@ import Realm, { UpdateMode } from 'realm'
import path from 'path' import path from 'path'
import { BaseService } from '../baseService' import { BaseService } from '../baseService'
import { define } from '../../../define.js' import { define } from '../../../define.js'
import { SoftwareModel } from '../../model/SoftWare/software.js' import { SoftwareModel } from '../../model/SoftWare/software'
import { ComponentSize, SoftwareThemeType } from '../../../enum/softwareEnum.js' import { ComponentSize, SoftwareThemeType } from '../../../enum/softwareEnum.js'
import { errorMessage, successMessage } from '../../../../main/generalTools.js' import { errorMessage, successMessage } from '../../../../main/Public/generalTools'
import { BaseSoftWareService } from './softwareBasic.js' import { BaseSoftWareService } from './softwareBasic.js'
import { isEmpty, isNumber } from 'lodash' import { isEmpty, isNumber } from 'lodash'
const { v4: uuidv4 } = require('uuid') const { v4: uuidv4 } = require('uuid')
import { version } from '../../../../../package.json' import { version } from '../../../../../package.json'
import { GeneralResponse } from '../../../../model/generalResponse'
export class MJSettingService extends BaseSoftWareService { export class MJSettingService extends BaseSoftWareService {
static instance: MJSettingService | null = null static instance: MJSettingService | null = null
@ -561,7 +562,7 @@ export class MJSettingService extends BaseSoftWareService {
// 判断API设置的数据是不是存在 // 判断API设置的数据是不是存在
let apiSetting = mjSetting.apiSetting ? mjSetting.apiSetting : null let apiSetting = mjSetting.apiSetting ? mjSetting.apiSetting : null
if (apiSetting != null) { if (apiSetting != null) {
let apiSettingRes: { code: number; data: any; message: any } let apiSettingRes: GeneralResponse.ErrorItem | GeneralResponse.SuccessItem
if (isEmpty(apiSetting.id)) { if (isEmpty(apiSetting.id)) {
// 新增 // 新增
apiSettingRes = this.AddAPIMjSetting(apiSetting) apiSettingRes = this.AddAPIMjSetting(apiSetting)
@ -577,7 +578,7 @@ export class MJSettingService extends BaseSoftWareService {
// 判断浏览器模式的数据是不是存在 // 判断浏览器模式的数据是不是存在
let browserSetting = mjSetting.browserSetting ? mjSetting.browserSetting : null let browserSetting = mjSetting.browserSetting ? mjSetting.browserSetting : null
if (browserSetting != null) { if (browserSetting != null) {
let browserSettingRes: { code: number; data: any; message: any } let browserSettingRes: GeneralResponse.ErrorItem | GeneralResponse.SuccessItem
if (isEmpty(browserSetting.id)) { if (isEmpty(browserSetting.id)) {
// 新增 // 新增
browserSettingRes = this.AddBrowserMJSetting(browserSetting) browserSettingRes = this.AddBrowserMJSetting(browserSetting)
@ -591,7 +592,7 @@ export class MJSettingService extends BaseSoftWareService {
} }
// 添加MJ的基础配置信息 // 添加MJ的基础配置信息
let mjSettingRes: { code: number; data: any; message: any } let mjSettingRes: GeneralResponse.ErrorItem | GeneralResponse.SuccessItem
if (isEmpty(mjSetting.id)) { if (isEmpty(mjSetting.id)) {
// 新增 // 新增
mjSettingRes = this.AddMJSetting(mjSetting) mjSettingRes = this.AddMJSetting(mjSetting)

View File

@ -131,6 +131,22 @@ const migration = (oldRealm: Realm, newRealm: Realm) => {
} }
}) })
} }
if (oldRealm.schemaVersion < 18) {
newRealm.write(() => {
const newSoftwares = newRealm.objects('Software')
for (let software of newSoftwares) {
software.watermarkSetting = null // 水印的默认设置
}
})
}
if (oldRealm.schemaVersion < 19) {
newRealm.write(() => {
const newSoftwares = newRealm.objects('Software')
for (let software of newSoftwares) {
software.translationSetting = null // 翻译的默认设置
}
})
}
} }
export class BaseSoftWareService extends BaseService { export class BaseSoftWareService extends BaseService {
@ -169,7 +185,7 @@ export class BaseSoftWareService extends BaseService {
MjSettingModel MjSettingModel
], ],
path: dbPath, path: dbPath,
schemaVersion: 17, // 当前版本号 schemaVersion: 19, // 当前版本号
migration: migration migration: migration
} }
// 判断当前全局是不是又当前这个 // 判断当前全局是不是又当前这个

View File

@ -1,10 +1,5 @@
import Realm, { UpdateMode } from 'realm' import Realm, { UpdateMode } from 'realm'
import path from 'path' import { successMessage } from '../../../../main/Public/generalTools'
import { BaseService } from '../baseService.js'
import { define } from '../../../define.js'
import { SoftwareModel } from '../../model/SoftWare/software.js'
import { ComponentSize, SoftwareThemeType } from '../../../enum/softwareEnum.js'
import { successMessage } from '../../../../main/generalTools.js'
import { BaseSoftWareService } from './softwareBasic.js' import { BaseSoftWareService } from './softwareBasic.js'
const { v4: uuidv4 } = require('uuid') const { v4: uuidv4 } = require('uuid')
@ -63,12 +58,26 @@ export class SoftwareService extends BaseSoftWareService {
*/ */
GetSoftwareData() { GetSoftwareData() {
try { try {
let software = this.realm.objects('Software') let softwares = this.realm.objects('Software')
let res = Array.from(softwares).map((software) => {
// 这里可以直接操作普通对象
let bookObj = {
id: software.id,
theme: software.theme,
reverse_show_book_striped: software.reverse_show_book_striped,
reverse_data_table_size: software.reverse_data_table_size,
reverse_display_show: software.reverse_display_show,
}
return bookObj
})
return successMessage( return successMessage(
software.toJSON(), res,
'获取软件配置信息成功', '获取软件配置信息成功',
'SoftwareService_GetSoftwareData' 'SoftwareService_GetSoftwareData'
) )
} catch (error) { } catch (error) {
global.logger.error( global.logger.error(
'SoftwareService_GetSoftwareData', 'SoftwareService_GetSoftwareData',
@ -82,17 +91,15 @@ export class SoftwareService extends BaseSoftWareService {
* *
* @param property * @param property
*/ */
GetSoftWarePropertyData(property: string) { GetSoftWarePropertyData(property: string): string {
try { try {
let software = this.realm.objects('Software') let software = this.realm.objects('Software')
if (software.length <= 0) { if (software.length <= 0) {
throw new Error('数据库中没有软件配置信息') throw new Error('数据库中没有软件配置信息')
} }
let softwareData = software.toJSON()[0] let softwareData = software.toJSON()[0]
let res = softwareData[property] let res = softwareData[property] as string
return res
return successMessage(res, '获取软件配置信息成功', 'SoftwareService_GetSoftWarePropertyData')
} catch (error) { } catch (error) {
global.logger.error( global.logger.error(
'SoftwareService_GetSoftWarePropertyData', 'SoftwareService_GetSoftWarePropertyData',

View File

@ -15,6 +15,8 @@ if (!app.isPackaged) {
scripts_path: path.join(__dirname, '../../resources/scripts'), scripts_path: path.join(__dirname, '../../resources/scripts'),
db_path: path.join(__dirname, '../../resources/scripts/db'), db_path: path.join(__dirname, '../../resources/scripts/db'),
project_path: path.join(__dirname, '../../project'), project_path: path.join(__dirname, '../../project'),
tts_path: path.join(__dirname, '../../tts'),
logger_path: path.join(__dirname, '../../resources/logger'), logger_path: path.join(__dirname, '../../resources/logger'),
package_path: path.join(__dirname, '../../resources/package'), package_path: path.join(__dirname, '../../resources/package'),
image_path: path.join(__dirname, '../../resources/image'), image_path: path.join(__dirname, '../../resources/image'),
@ -82,6 +84,7 @@ if (!app.isPackaged) {
scripts_path: path.join(__dirname, '../../../resources/scripts'), scripts_path: path.join(__dirname, '../../../resources/scripts'),
db_path: path.join(__dirname, '../../../resources/scripts/db'), db_path: path.join(__dirname, '../../../resources/scripts/db'),
project_path: path.join(__dirname, '../../../project'), project_path: path.join(__dirname, '../../../project'),
tts_path: path.join(__dirname, '../../../tts'),
logger_path: path.join(__dirname, '../../../resources/logger'), logger_path: path.join(__dirname, '../../../resources/logger'),
package_path: path.join(__dirname, '../../../resources/package'), package_path: path.join(__dirname, '../../../resources/package'),
discordScript: path.join(__dirname, '../../../resources/scripts/discordScript.js'), discordScript: path.join(__dirname, '../../../resources/scripts/discordScript.js'),
@ -141,5 +144,7 @@ if (!app.isPackaged) {
define['remotemj_api'] = 'https://api.laitool.net/' define['remotemj_api'] = 'https://api.laitool.net/'
define['serverUrl'] = 'http://lapi.laitool.cn' define['serverUrl'] = 'http://lapi.laitool.cn'
define['hkServerUrl'] = 'https://api.laitool.cc/'
define['bakServerUrl'] = 'https://bakapi.laitool.cc/'
define['API'] = 'f85d39ed5a40fd09966f13f12b6cf0f0' define['API'] = 'f85d39ed5a40fd09966f13f12b6cf0f0'
export { define } export { define }

View File

@ -1,4 +1,5 @@
export const DEFINE_STRING = { export const DEFINE_STRING = {
SHOW_GLOBAL_MAIN_NOTIFICATION: 'SHOW_GLOBAL_MAIN_NOTIFICATION',
OPEN_DEV_TOOLS_PASSWORD: 'OPEN_DEV_TOOLS_PASSWORD', OPEN_DEV_TOOLS_PASSWORD: 'OPEN_DEV_TOOLS_PASSWORD',
OPEN_DEV_TOOLS: 'OPEN_DEV_TOOLS', OPEN_DEV_TOOLS: 'OPEN_DEV_TOOLS',
GET_FILE_BASE64: 'GET_FILE_BASE64', GET_FILE_BASE64: 'GET_FILE_BASE64',
@ -21,7 +22,6 @@ export const DEFINE_STRING = {
SAVE_KEY_FRAME_SETTING: 'SAVE_KEY_FRAME_SETTING', SAVE_KEY_FRAME_SETTING: 'SAVE_KEY_FRAME_SETTING',
MODIFY_SAMPLE_SETTING: 'MODIFY_SAMPLE_SETTING', MODIFY_SAMPLE_SETTING: 'MODIFY_SAMPLE_SETTING',
GET_SETTING_Dafault_DATA: 'GET_SETTING_Dafault_DATA', GET_SETTING_Dafault_DATA: 'GET_SETTING_Dafault_DATA',
GET_DRAFT_FILE_LIST: 'GET_DRAFT_FILE_LIST',
GET_FRAME: 'GET_FRAME', GET_FRAME: 'GET_FRAME',
PYTHON_ERROR: 'PYTHON_ERROR', PYTHON_ERROR: 'PYTHON_ERROR',
PYTHON_CLOSE: 'PYTHON_CLOSE', PYTHON_CLOSE: 'PYTHON_CLOSE',
@ -144,9 +144,16 @@ export const DEFINE_STRING = {
NORMAL_PERMISSION: 'NORMAL_PERMISSION', NORMAL_PERMISSION: 'NORMAL_PERMISSION',
AUTO_SAVE_IMAGE_PERMISSION: 'AUTO_SAVE_IMAGE_PERMISSION' AUTO_SAVE_IMAGE_PERMISSION: 'AUTO_SAVE_IMAGE_PERMISSION'
}, },
TRANSLATE: {
TRANSLATE_NOW_RETURN: 'TRANSLATE_NOW_RETURN',
GET_TRANSLATE_SETTING: 'GET_TRANSLATE_SETTING',
RESET_TRANSLATE_SETTING: 'RESET_TRANSLATE_SETTING',
SAVE_TRANSLATE_SETTING: 'SAVE_TRANSLATE_SETTING'
},
SD: { SD: {
LOAD_SD_SERVICE_DATA: 'LOAD_SD_SERVICE_DATA', LOAD_SD_SERVICE_DATA: 'LOAD_SD_SERVICE_DATA',
TXT2IMG: 'TXT2IMG' TXT2IMG: 'TXT2IMG',
SD_MERGE_PROMPT: "SD_MERGE_PROMPT"
}, },
MJ: { MJ: {
SAVE_WORD_SRT: 'SAVE_WORD_SRT', SAVE_WORD_SRT: 'SAVE_WORD_SRT',
@ -167,7 +174,10 @@ export const DEFINE_STRING = {
GET_MJ_IMAGE_SCALE: 'GET_MJ_IMAGE_SCALE', GET_MJ_IMAGE_SCALE: 'GET_MJ_IMAGE_SCALE',
GET_MJ_IMAGE_ROBOT_MODEL: 'GET_MJ_IMAGE_ROBOT_MODEL', GET_MJ_IMAGE_ROBOT_MODEL: 'GET_MJ_IMAGE_ROBOT_MODEL',
MACTH_USER_RETURN: 'MACTH_USER_RETURN', MACTH_USER_RETURN: 'MACTH_USER_RETURN',
AUTO_MATCH_USER: 'AUTO_MATCH_USER' AUTO_MATCH_USER: 'AUTO_MATCH_USER',
MJ_MERGE_PROMPT: "MJ_MERGE_PROMPT",
ADD_MJ_GENADD_MJ_GENERATE_IMAGE_TASK: "ADD_MJ_GENADD_MJ_GENERATE_IMAGE_TASK",
MJ_IMAGE: "MJ_IMAGE"
}, },
DISCORD: { DISCORD: {
OPERATE_REFRASH_DISCORD_URL: 'OPERATE_REFRASH_DISCORD_URL', OPERATE_REFRASH_DISCORD_URL: 'OPERATE_REFRASH_DISCORD_URL',
@ -195,6 +205,8 @@ export const DEFINE_STRING = {
BATCH_PROCESS_IMAGE_RESULT: 'BATCH_PROCESS_IMAGE_RESULT' BATCH_PROCESS_IMAGE_RESULT: 'BATCH_PROCESS_IMAGE_RESULT'
}, },
BOOK: { BOOK: {
MAIN_DATA_RETURN: 'MAIN_DATA_RETURN', // 监听任务返回
GET_BOOK_TYPE: 'GET_BOOK_TYPE', GET_BOOK_TYPE: 'GET_BOOK_TYPE',
ADD_OR_MODIFY_BOOK: 'ADD_OR_MODIFY_BOOK', ADD_OR_MODIFY_BOOK: 'ADD_OR_MODIFY_BOOK',
GET_BOOK_DATA: 'GET_BOOK_DATA', GET_BOOK_DATA: 'GET_BOOK_DATA',
@ -204,7 +216,39 @@ export const DEFINE_STRING = {
SAVE_BOOK_SUBTITLE_POSITION: 'SAVE_BOOK_SUBTITLE_POSITION', SAVE_BOOK_SUBTITLE_POSITION: 'SAVE_BOOK_SUBTITLE_POSITION',
OPEN_BOOK_SUBTITLE_POSITION_SCREENSHOT: 'OPEN_BOOK_SUBTITLE_POSITION_SCREENSHOT', OPEN_BOOK_SUBTITLE_POSITION_SCREENSHOT: 'OPEN_BOOK_SUBTITLE_POSITION_SCREENSHOT',
GET_CURRENT_FRAME_TEXT: 'GET_CURRENT_FRAME_TEXT', GET_CURRENT_FRAME_TEXT: 'GET_CURRENT_FRAME_TEXT',
GET_VIDEO_FRAME_TEXT: 'GET_VIDEO_FRAME_TEXT' GET_VIDEO_FRAME_TEXT: 'GET_VIDEO_FRAME_TEXT',
GET_BOOK_TASK_DETAIL: 'GET_BOOK_TASK_DETAIL',
REVERSE_PROMPT_TO_GPT_PROMPT: 'REVERSE_PROMPT_TO_GPT_PROMPT',
SINGLE_REVERSE_TO_GPT_PROMPT: 'SINGLE_REVERSE_TO_GPT_PROMPT',
SAVE_IMAGE_STYLE: 'SAVE_IMAGE_STYLE',
IMAGE_LOCK_OPERATION: "IMAGE_LOCK_OPERATION",
DOWNLOAD_IMAGE_AND_SPLIT: "DOWNLOAD_IMAGE_AND_SPLIT",
ONE_TO_FOUR_BOOK_TASK: "ONE_TO_FOUR_BOOK_TASK",
RESET_BOOK_TASK: "RESET_BOOK_TASK",
DELETE_BOOK_TASK: "DELETE_BOOK_TASK",
GENERATE_IMAGE_ALL: "GENERATE_IMAGE_ALL",
CHECK_IMAGE_FILE_SIZE: "CHECK_IMAGE_FILE_SIZE",
HD_IMAGE: "HD_IMAGE",
USE_BOOK_VIDEO_DATA_TO_BOOK_TASK: "USE_BOOK_VIDEO_DATA_TO_BOOK_TASK",
ADD_JIANYING_DRAFT: "ADD_JIANYING_DRAFT",
COMPUTE_STORYBOARD: 'COMPUTE_STORYBOARD',
GET_FRAME: 'GET_FRAME',
FRAMING: 'FRAMING',
GET_COPYWRITING: 'GET_COPYWRITING',
GET_COPYWRITING_RETURN: 'GET_COPYWRITING_RETURN',
REMOVE_WATERMARK: 'REMOVE_WATERMARK',
REMOVE_WATERMARK_RETURN: 'REMOVE_WATERMARK_RETURN',
SPLI_TAUDIO: 'SPLI_TAUDIO',
SPLI_TAUDIO_RETURN: 'SPLI_TAUDIO_RETURN',
ADD_REVERSE_PROMPT: 'ADD_REVERSE_PROMPT',
REMOVE_REVERSE_DATA: 'REMOVE_REVERSE_DATA'
}, },
SYSTEM: { SYSTEM: {
OPEN_FILE: 'OPEN_FILE', OPEN_FILE: 'OPEN_FILE',
@ -225,7 +269,9 @@ export const DEFINE_STRING = {
GET_REMOTE_MJ_SETTINGS: 'GET_REMOTE_MJ_SETTINGS', GET_REMOTE_MJ_SETTINGS: 'GET_REMOTE_MJ_SETTINGS',
ADD_REMOTE_MJ_SETTING: 'ADD_REMOTE_MJ_SETTING', ADD_REMOTE_MJ_SETTING: 'ADD_REMOTE_MJ_SETTING',
UPDATE_REMOTE_MJ_SETTING: 'UPDATE_REMOTE_MJ_SETTING', UPDATE_REMOTE_MJ_SETTING: 'UPDATE_REMOTE_MJ_SETTING',
DELETE_REMOTE_MJ_SETTING: 'DELETE_REMOTE_MJ_SETTING' DELETE_REMOTE_MJ_SETTING: 'DELETE_REMOTE_MJ_SETTING',
GET_WATER_MARK_SETTING: 'GET_WATER_MARK_SETTING',
SAVE_WATER_MARK_SETTING: 'SAVE_WATER_MARK_SETTING'
}, },
PROMPT: { PROMPT: {
GET_SORT_OPTIONS: 'GET_SORT_OPTIONS', GET_SORT_OPTIONS: 'GET_SORT_OPTIONS',
@ -235,11 +281,16 @@ export const DEFINE_STRING = {
}, },
TTS: { TTS: {
GET_TTS_CONFIG: 'GET_TTS_CONFIG', GET_TTS_CONFIG: 'GET_TTS_CONFIG',
GENERATE_AUDIO: 'GENERATE_AUDIO',
SAVE_TTS_CONFIG: 'SAVE_TTS_CONFIG' SAVE_TTS_CONFIG: 'SAVE_TTS_CONFIG'
}, },
WRITE: { WRITE: {
GET_WRITE_CONFIG: 'GET_WRITE_CONFIG', GET_WRITE_CONFIG: 'GET_WRITE_CONFIG',
SAVE_WRITE_CONFIG: 'SAVE_WRITE_CONFIG', SAVE_WRITE_CONFIG: 'SAVE_WRITE_CONFIG',
ACTION_START: 'ACTION_START' ACTION_START: 'ACTION_START'
},
DB: {
UPDATE_BOOK_TASK_DATA: "UPDATE_BOOK_TASK_DATA",
UPDATE_BOOK_TASK_DETAIL_DATA: "UPDATE_BOOK_TASK_DETAIL_DATA"
} }
} }

View File

@ -7,6 +7,16 @@ export enum BookType {
MJ_REVERSE = 'mj_reverse' MJ_REVERSE = 'mj_reverse'
} }
// 出图方式
export enum BookImageCategory {
// MJ
MJ = 'mj',
// SD
SD = 'sd',
// D3
D3 = 'd3'
}
export enum MJCategroy { export enum MJCategroy {
@ -39,10 +49,16 @@ export enum BookBackTaskType {
RECOGNIZE = 'recognize', RECOGNIZE = 'recognize',
// 抽帧 // 抽帧
FRAME = 'frame', FRAME = 'frame',
// 反推 // MJ反推
REVERSE = 'reverse', MJ_REVERSE = BookType.MJ_REVERSE,
// 生成图片 // SD反推
IMAGE = 'image', SD_REVERSE = BookType.SD_REVERSE,
// MJ生成图片
MJ_IMAGE = 'mj_image',
// SD 生成图片
SD_IMAGE = 'sd_image',
// D3 生成图片
D3_IMAGE = 'd3_image',
// 高清 // 高清
HD = 'hd', HD = 'hd',
// 合成视频 // 合成视频
@ -63,7 +79,9 @@ export enum BookBackTaskStatus {
// 完成 // 完成
DONE = 'done', DONE = 'done',
// 失败 // 失败
FAIL = 'fail' FAIL = 'fail',
// 重连
RECONNECT = 'reconnect'
} }
export enum TaskExecuteType { export enum TaskExecuteType {
@ -74,6 +92,16 @@ export enum TaskExecuteType {
OPERATE = 'operate' OPERATE = 'operate'
} }
// 弹窗类型
export enum DialogType {
// 单独弹窗
DIALOG = 'dialog',
// 消息提示
MESSAGE = 'message',
// 右上角通知
NOTIFICATION = 'notification'
}
/** /**
* *
*/ */
@ -162,3 +190,40 @@ export enum BookTaskStatus {
// 合成视频失败 // 合成视频失败
COMPOSING_FAIL = 'composing_fail' COMPOSING_FAIL = 'composing_fail'
} }
export enum TagDefineType {
// 默认风格
DEFAULT_STYLE = "default_style",
// 角色标签
CHARACTER_MAIN = "character_main",
// 角色小标签
CHARACTER_SUB = "min",
// 风格主标签
STYLE_MAIN = "style_main",
// 场景主标签
SCENE_MAIN = "scene_main",
}
export enum MergeType {
BOOKTASK = 'bookTask', // 整个小说批次分镜合并
BOOKTASKDETAIL = 'bookTaskDetail' // 单个分镜合并
}
export enum OperateBookType {
BOOK = 'book', // 这个小说的所有批次
BOOKTASK = 'bookTask', // 整个小说批次分镜合并
BOOKTASKDETAIL = 'bookTaskDetail', // 单个分镜合并
UNDERBOOKTASK = 'underBookTask' // 执行小说批次任务的指定ID以及后面的所有的东西
}
export enum CopyImageType {
// 所有,包括原图
ALL = 'all',
// 出原图外其他,一个个对应
ONE = 'one',
// 不包含图
NONE = 'none'
}

View File

@ -19,3 +19,31 @@ export enum MJRobotType {
// niji // niji
NIJI = 'niji' NIJI = 'niji'
} }
export enum MJSpeed {
// 快速
FAST = 'fast',
// 休闲
RELAX = 'relaxed'
}
export enum MJRespoonseType {
// 创建
CREATED = "created",
// 更新
UPDATED = "updated",
// 完成
FINISHED = "finished",
// 删除
DELETE = "delete"
}
export enum MJAction {
// 出图
IMAGINE = 'IMAGINE',
// 反推
DESCRIBE = 'DESCRIBE'
}

View File

@ -45,6 +45,31 @@ export enum SoftColor {
// 棕黄色 // 棕黄色
BROWN_YELLOW = '#e18a3b', BROWN_YELLOW = '#e18a3b',
// 橘色
ORANGE = '#ee7959',
// 朱颜酡
ZHUYANTUO = '#f29a76',
// 错误红色 // 错误红色
ERROR_RED = '#c8161d' ERROR_RED = '#c8161d'
}
export enum ResponseMessageType {
GET_TEXT = 'getText', // 获取文案
REMOVE_WATERMARK = "REMOVE_WATERMARK",// 删除水印
MJ_REVERSE = 'MJ_REVERSE',// MJ反推返回反推结果
PROMPT_TRANSLATE = 'PROMPT_TRANSLATE',// 提示词翻译
MJ_IMAGE = 'MJ_IMAGE',// MJ 生成图片
HD_IMAGE = 'HD_IMAGE',// HD 生成图片
}
export enum LaiAPIType {
// 主要的
MAIN = "main",
// 香港代理
HK_PROXY = "hk-proxy",
// 备用站点
BAK_MAIN = 'bak-main'
} }

6
src/define/enum/task.ts Normal file
View File

@ -0,0 +1,6 @@
export enum TaskQueueType {
// 内存添加
CACHE_ADD = 'cache_add',
// 数据库添加
DB_ADD = 'db_add'
}

View File

@ -0,0 +1,23 @@
// 翻译类型(主要用于后端逻辑处理)
export enum TranslateType {
// 反推提示词翻译
REVERSE_PROMPT_TRANSLATE = 'reverse_prompt_translate',
// GPT提示词翻译
GPT_PROMPT_TRANSLATE = 'gpt_prompt_translate',
}
// 翻译API类型
export enum TranslateAPIType {
// 百度翻译
BAIDU = 'baidu',
// 腾讯翻译
TENCENT = 'tencent',
// 火山翻译
VOLCENGINE = 'volcengine',
// 阿里翻译
ALI = 'ali',
}

View File

@ -8,6 +8,23 @@ export enum SubtitleSavePositionType {
// 分镜视频 // 分镜视频
STORYBOARD_VIDEO = 'storyboard_video', STORYBOARD_VIDEO = 'storyboard_video',
// 设置,只是框选
SETTING = 'setting',
// 其他类型 // 其他类型
OTHER = 'other' OTHER = 'other'
} }
// 图片去除水印方法,返回数据的格式
export enum WaterMarkResponseDateType {
// 返回的数据类型
ArrayBuffer = "arrayBuffer",
// 直接将文件写道本地
File = "file"
}
export enum RemoveWatermarkType {
LOCAL_LAMA = 'local_lama',
IOPAINT = 'iopaint'
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -2,7 +2,7 @@
let fspromises = require('fs').promises; let fspromises = require('fs').promises;
import { get, cloneDeep } from 'lodash'; import { get, cloneDeep } from 'lodash';
import { define } from '../define'; import { define } from '../define';
import { errorMessage } from '../../main/generalTools'; import { errorMessage } from '../../main/Public/generalTools';
export class DynamicSetting { export class DynamicSetting {
constructor(global) { constructor(global) {

View File

@ -1,4 +1,4 @@
import { successMessage } from "../../main/generalTools"; import { successMessage } from "../../main/Public/generalTools";
export class MjSetting { export class MjSetting {
constructor(golbal) { constructor(golbal) {

View File

@ -2,7 +2,7 @@ import { get } from "lodash";
import { define } from "../define"; import { define } from "../define";
let fspromises = require("fs").promises; let fspromises = require("fs").promises;
import { Tools } from "../../main/tools"; import { Tools } from "../../main/tools";
import { errorMessage } from "../../main/generalTools"; import { errorMessage } from "../../main/Public/generalTools";
let tools = new Tools(); let tools = new Tools();
// Create a shared object // Create a shared object

View File

@ -1,15 +1,14 @@
let fspromises = require('fs').promises
let fspromises = require('fs').promises; import { get, cloneDeep } from 'lodash'
import { get, cloneDeep } from 'lodash'; import { define } from './define'
import { define } from './define'; import path from 'path'
import path from 'path'; import { Tools } from '../main/tools'
import { Tools } from '../main/tools'; const { v4: uuidv4 } = require('uuid')
const { v4: uuidv4 } = require('uuid');
export class TagDefine { export class TagDefine {
constructor(global) { constructor(global) {
this.global = global; this.global = global
this.tools = new Tools(); this.tools = new Tools()
} }
/** /**
@ -18,15 +17,13 @@ export class TagDefine {
async getTagSelectModel() { async getTagSelectModel() {
return { return {
code: 1, code: 1,
data: data: [
[ { label: '标签', value: 'tag' },
{ label: "标签", value: "tag" }, { label: '下拉', value: 'drop' }
{ label: "下拉", value: "drop" },
] ]
} }
} }
/** /**
* 通过指定的类型获取数据 * 通过指定的类型获取数据
* @param {*} type default在代码中写死的 dynamic用户自定义的 all写死的和自定义的合并返回 * @param {*} type default在代码中写死的 dynamic用户自定义的 all写死的和自定义的合并返回
@ -36,49 +33,55 @@ export class TagDefine {
*/ */
async getTagDataByTypeAndProperty(type, property, defaultData = null) { async getTagDataByTypeAndProperty(type, property, defaultData = null) {
try { try {
let res = []; let res = []
// 获取自定义的GPT数据 // 获取自定义的GPT数据
let tag_setting = JSON.parse(await fspromises.readFile(define.tag_setting, 'utf-8')); let tag_setting = JSON.parse(await fspromises.readFile(define.tag_setting, 'utf-8'))
let data = get(tag_setting, property, {}); let data = get(tag_setting, property, {})
// 若是传入的属性名为null直接返回当前tags里面的所有的数据 // 若是传入的属性名为null直接返回当前tags里面的所有的数据
if (property == null) { if (property == null) {
data = tag_setting; data = tag_setting
} }
if (type == "default") { if (type == 'default') {
// res = get(this, property, defaultData); // res = get(this, property, defaultData);
} else if (type == "dynamic") { } else if (type == 'dynamic') {
res = data; res = data
} else if (type == "all") { } else if (type == 'all') {
let tmp_arr = cloneDeep(get([], property, defaultData)); let tmp_arr = cloneDeep(get([], property, defaultData))
tmp_arr = tmp_arr.concat(data); tmp_arr = tmp_arr.concat(data)
res = tmp_arr; res = tmp_arr
} else {
throw new Error(`不存在的类型 : ${value}`)
} }
else { if (property) {
throw new Error(`不存在的类型 : ${value}`); res.forEach((item) => {
if (item.show_image && item.show_image != '') {
item.show_image = path.join(define.image_path, item.show_image)
} }
})
} else {
// 返回之前,判断里面是不是有预览图片路径 // 返回之前,判断里面是不是有预览图片路径
if (res.hasOwnProperty("character_tags")) { if (res.hasOwnProperty('character_tags')) {
res.character_tags.forEach(item => { res.character_tags.forEach((item) => {
if (item.show_image && item.show_image != "") { if (item.show_image && item.show_image != '') {
item.show_image = path.join(define.image_path, item.show_image); item.show_image = path.join(define.image_path, item.show_image)
} }
}); })
} }
if (res.hasOwnProperty("scene_tags")) { if (res.hasOwnProperty('scene_tags')) {
res.scene_tags.forEach(item => { res.scene_tags.forEach((item) => {
if (item.show_image && item.show_image != "") { if (item.show_image && item.show_image != '') {
item.show_image = path.join(define.image_path, item.show_image); item.show_image = path.join(define.image_path, item.show_image)
} }
}); })
} }
if (res.hasOwnProperty("style_tags")) { if (res.hasOwnProperty('style_tags')) {
res.style_tags.forEach(item => { res.style_tags.forEach((item) => {
if (item.show_image && item.show_image != "") { if (item.show_image && item.show_image != '') {
item.show_image = path.join(define.image_path, item.show_image); item.show_image = path.join(define.image_path, item.show_image)
}
})
} }
});
} }
return { return {
@ -100,55 +103,55 @@ export class TagDefine {
*/ */
async saveTagPropertyData(value) { async saveTagPropertyData(value) {
try { try {
let property = value[1]; let property = value[1]
value = JSON.parse(value[0]); value = JSON.parse(value[0])
let tmp_key = uuidv4(); let tmp_key = uuidv4()
// 特殊操作。为角色和场景的时候需要copy图片 // 特殊操作。为角色和场景的时候需要copy图片
if (property == "character_tags" || property == "scene_tags" || property == "style_tags") { if (property == 'character_tags' || property == 'scene_tags' || property == 'style_tags') {
let show_image = value.show_image; let show_image = value.show_image
if (show_image && show_image != "") { if (show_image && show_image != '') {
let file_name = `c_s/${value.key ? value.key : tmp_key}.png` let file_name = `c_s/${value.key ? value.key : tmp_key}.png`
let new_image_path = path.join(define.image_path, file_name); let new_image_path = path.join(define.image_path, file_name)
await this.tools.copyFileOrDirectory(show_image, new_image_path); await this.tools.copyFileOrDirectory(show_image, new_image_path)
value.show_image = file_name; value.show_image = file_name
value.children?.forEach(item => { value.children?.forEach((item) => {
item.show_image = file_name; item.show_image = file_name
}); })
} }
} }
// 获取自定义的GPT数据 // 获取自定义的GPT数据
let tag_setting = JSON.parse(await fspromises.readFile(define.tag_setting, 'utf-8')); let tag_setting = JSON.parse(await fspromises.readFile(define.tag_setting, 'utf-8'))
let tag = get(tag_setting, property, []); let tag = get(tag_setting, property, [])
if (value.key) { if (value.key) {
// 判断当前ID的数据是否存在存在覆盖不存在追加 // 判断当前ID的数据是否存在存在覆盖不存在追加
let index = tag.findIndex(item => item.key == value.key); let index = tag.findIndex((item) => item.key == value.key)
value.value = value.key; value.value = value.key
if (index < 0) { if (index < 0) {
// 判断相同名字的数据是不是存在,存在报错 // 判断相同名字的数据是不是存在,存在报错
if (tag.some(item => item.label == value.label)) { if (tag.some((item) => item.label == value.label)) {
throw new Error("已存在相同名称的数据,请修改名称后再保存"); throw new Error('已存在相同名称的数据,请修改名称后再保存')
} }
tag.push(value); tag.push(value)
} else { } else {
tag[index] = value; tag[index] = value
} }
} else { } else {
// 判断相同名字的数据是不是存在,存在报错 // 判断相同名字的数据是不是存在,存在报错
if (tag.some(item => item.label == value.label)) { if (tag.some((item) => item.label == value.label)) {
throw new Error("已存在相同名称的数据,请修改名称后再保存"); throw new Error('已存在相同名称的数据,请修改名称后再保存')
} }
value.key = tmp_key; value.key = tmp_key
value.value = value.key; value.value = value.key
tag.push(value); tag.push(value)
} }
tag_setting[property] = tag; tag_setting[property] = tag
// 写入文件 // 写入文件
await fspromises.writeFile(define.tag_setting, JSON.stringify(tag_setting)); await fspromises.writeFile(define.tag_setting, JSON.stringify(tag_setting))
return { return {
code: 1, code: 1,
message: "保存成功" message: '保存成功'
} }
} catch (error) { } catch (error) {
return { return {
@ -165,25 +168,24 @@ export class TagDefine {
*/ */
async deleteTagPropertyData(value) { async deleteTagPropertyData(value) {
try { try {
let property = value[1]; let property = value[1]
let id = value[0]; let id = value[0]
// 获取自定义的GPT数据 // 获取自定义的GPT数据
let tag_setting = JSON.parse(await fspromises.readFile(define.tag_setting, 'utf-8')); let tag_setting = JSON.parse(await fspromises.readFile(define.tag_setting, 'utf-8'))
let tags = tag_setting[property] ? tag_setting[property] : []; let tags = tag_setting[property] ? tag_setting[property] : []
// 判断当前ID的数据是否存在存在删除 // 判断当前ID的数据是否存在存在删除
let index = tags.findIndex(item => item.key == id); let index = tags.findIndex((item) => item.key == id)
if (index >= 0) { if (index >= 0) {
tags.splice(index, 1); tags.splice(index, 1)
} }
// 将修改后的数据保存 // 将修改后的数据保存
tag_setting[property] = tags; tag_setting[property] = tags
// 写入文件 // 写入文件
await fspromises.writeFile(define.tag_setting, JSON.stringify(tag_setting)); await fspromises.writeFile(define.tag_setting, JSON.stringify(tag_setting))
return { return {
code: 1, code: 1,
message: "删除成功" message: '删除成功'
} }
} catch (error) { } catch (error) {
return { return {
code: 0, code: 0,
@ -191,5 +193,4 @@ export class TagDefine {
} }
} }
} }
} }

View File

@ -17,7 +17,7 @@ type configure = {
proxy?: string proxy?: string
rate?: string rate?: string
pitch?: string pitch?: string
volume?: string volumn?: string
} }
export class EdgeTTS { export class EdgeTTS {
@ -28,7 +28,7 @@ export class EdgeTTS {
private proxy: string | null | undefined private proxy: string | null | undefined
private rate: string private rate: string
private pitch: string private pitch: string
private volume: string private volumn: string
constructor({ constructor({
voice = 'zh-CN-XiaoyiNeural', voice = 'zh-CN-XiaoyiNeural',
@ -38,7 +38,7 @@ export class EdgeTTS {
proxy, proxy,
rate = 'default', rate = 'default',
pitch = 'default', pitch = 'default',
volume = 'default' volumn = 'default'
}: configure = {}) { }: configure = {}) {
this.voice = voice this.voice = voice
this.lang = lang this.lang = lang
@ -47,7 +47,7 @@ export class EdgeTTS {
this.proxy = proxy this.proxy = proxy
this.rate = rate this.rate = rate
this.pitch = pitch this.pitch = pitch
this.volume = volume this.volumn = volumn
} }
async _connectWebSocket(): Promise<WebSocket> { async _connectWebSocket(): Promise<WebSocket> {
@ -138,7 +138,9 @@ export class EdgeTTS {
end: Math.floor((element['Data']['Offset'] + element['Data']['Duration']) / 10000) end: Math.floor((element['Data']['Offset'] + element['Data']['Duration']) / 10000)
}) })
}) })
} catch {} } catch {
throw new Error('解析元数据失败')
}
} }
} }
}) })
@ -148,7 +150,7 @@ export class EdgeTTS {
` + ` +
`<speak version="1.0" xmlns="http://www.w3.org/2001/10/synthesis" xmlns:mstts="https://www.w3.org/2001/mstts" xml:lang="${this.lang}"> `<speak version="1.0" xmlns="http://www.w3.org/2001/10/synthesis" xmlns:mstts="https://www.w3.org/2001/mstts" xml:lang="${this.lang}">
<voice name="${this.voice}"> <voice name="${this.voice}">
<prosody rate="${this.rate}" pitch="${this.pitch}" volume="${this.volume}"> <prosody rate="${this.rate}" pitch="${this.pitch}" volumn="${this.volumn}">
${text} ${text}
</prosody> </prosody>
</voice> </voice>

View File

@ -1,11 +1,23 @@
import { ipcMain } from 'electron' import { ipcMain } from 'electron'
import { DEFINE_STRING } from '../../define/define_string' import { DEFINE_STRING } from '../../define/define_string'
import { ReverseBook } from '../ReverseManage/Book/ReverseBook' import { ReverseBook } from '../Service/Book/ReverseBook'
import { BasicReverse } from '../Task/basicReverse' import { BasicReverse } from '../Service/Book/basicReverse'
import { WatermarkAndSubtitle } from '../Task/watermarkAndSubtitle' import { Subtitle } from '../Service/subtitle'
import { BookBasic } from '../Service/Book/BooKBasic'
import { MJOpt } from '../Service/MJ/mj'
import { BookImage } from '../Service/Book/bookImage'
import { ImageStyle } from '../Service/Book/imageStyle'
import { BookTask } from '../Service/Book/bookTask'
import { BookVideo } from '../Service/Book/bookVideo'
let reverseBook = new ReverseBook() let reverseBook = new ReverseBook()
let basicReverse = new BasicReverse() let basicReverse = new BasicReverse()
let watermarkAndSubtitle = new WatermarkAndSubtitle() let subtitle = new Subtitle()
let mjOpt = new MJOpt()
let bookBasic = new BookBasic()
let bookImage = new BookImage()
let imageStyle = new ImageStyle()
let bookTask = new BookTask()
let bookVideo = new BookVideo()
export function BookIpc() { export function BookIpc() {
// 获取样式图片的子列表 // 获取样式图片的子列表
@ -21,6 +33,16 @@ export function BookIpc() {
reverseBook.GetBookData(bookQuery) reverseBook.GetBookData(bookQuery)
) )
// 获取小说的所有的数据
ipcMain.handle(DEFINE_STRING.BOOK.GET_BOOK_TASK_DETAIL, async (event, bookTaskId) =>
reverseBook.GetBookTaskDetail(bookTaskId)
)
// 保存小说任务的风格
ipcMain.handle(DEFINE_STRING.BOOK.SAVE_IMAGE_STYLE, async (event, styleList, bookTaskId) =>
imageStyle.SaveImageStyle(styleList, bookTaskId)
)
//#region 一键反推 //#region 一键反推
ipcMain.handle(DEFINE_STRING.BOOK.GET_BOOK_TASK_DATA, async (event, bookTaskCondition) => ipcMain.handle(DEFINE_STRING.BOOK.GET_BOOK_TASK_DATA, async (event, bookTaskCondition) =>
@ -39,23 +61,145 @@ export function BookIpc() {
// 保存一键反推文案位置 // 保存一键反推文案位置
ipcMain.handle(DEFINE_STRING.BOOK.SAVE_BOOK_SUBTITLE_POSITION, async (event, value) => ipcMain.handle(DEFINE_STRING.BOOK.SAVE_BOOK_SUBTITLE_POSITION, async (event, value) =>
watermarkAndSubtitle.SaveBookSubtitlePosition(value) subtitle.SaveBookSubtitlePosition(value)
) )
// 打开对应的字幕提取的图片文件夹 // 打开对应的字幕提取的图片文件夹
ipcMain.handle(DEFINE_STRING.BOOK.OPEN_BOOK_SUBTITLE_POSITION_SCREENSHOT, async (event, value) => ipcMain.handle(DEFINE_STRING.BOOK.OPEN_BOOK_SUBTITLE_POSITION_SCREENSHOT, async (event, value) =>
watermarkAndSubtitle.OpenBookSubtitlePositionScreenshot(value) subtitle.OpenBookSubtitlePositionScreenshot(value)
) )
// 获取当前帧的字幕文字 // 获取当前帧的字幕文字
ipcMain.handle(DEFINE_STRING.BOOK.GET_CURRENT_FRAME_TEXT, async (event, value) => ipcMain.handle(DEFINE_STRING.BOOK.GET_CURRENT_FRAME_TEXT, async (event, value) =>
watermarkAndSubtitle.GetCurrentFrameText(value) subtitle.GetCurrentFrameText(value)
) )
// 获取当前视频中的所有的字幕 // 获取当前视频中的所有的字幕
ipcMain.handle(DEFINE_STRING.BOOK.GET_VIDEO_FRAME_TEXT, async (event,value)=>{ ipcMain.handle(DEFINE_STRING.BOOK.GET_VIDEO_FRAME_TEXT, async (event, value) => {
watermarkAndSubtitle.GetVideoFrameText(value) subtitle.GetVideoFrameText(value)
}) })
//#endregion //#endregion
//#region 一键反推的单个任务
// 开始计算分镜
ipcMain.handle(
DEFINE_STRING.BOOK.COMPUTE_STORYBOARD,
async (event, bookId) => await reverseBook.ComputeStoryboard(bookId)
)
// 开始执行分镜,切分视频
ipcMain.handle(
DEFINE_STRING.BOOK.FRAMING,
async (event, bookId) => await reverseBook.Framing(bookId)
)
// 开始执行分镜任务
ipcMain.handle(
DEFINE_STRING.BOOK.GET_COPYWRITING,
async (event, bookId) => await reverseBook.GetCopywriting(bookId)
)
// 执行去除水印
ipcMain.handle(
DEFINE_STRING.BOOK.REMOVE_WATERMARK,
async (event, bookId) => await reverseBook.RemoveWatermark(bookId)
)
// 添加反推任务到任务列表
ipcMain.handle(
DEFINE_STRING.BOOK.ADD_REVERSE_PROMPT,
async (event, bookTaskDetailIds, type) =>
await reverseBook.AddReversePromptTask(bookTaskDetailIds, type)
)
// 将反推出来的提示词写入到GPT提示词里面
ipcMain.handle(
DEFINE_STRING.BOOK.REVERSE_PROMPT_TO_GPT_PROMPT,
async (event, bookId, bookTaskId, index) =>
await mjOpt.ReversePromptToGptPrompt(bookId, bookTaskId, index)
)
// 重选单个反推提示词到输出种
ipcMain.handle(
DEFINE_STRING.BOOK.SINGLE_REVERSE_TO_GPT_PROMPT,
async (event, bookTaskDetailId, index) =>
await mjOpt.SingleReverseToGptPrompt(bookTaskDetailId, index)
)
// 删除所有的反推出来的提示词
ipcMain.handle(
DEFINE_STRING.BOOK.REMOVE_REVERSE_DATA,
async (event, bookTaskDetailIds) => await reverseBook.RemoveReverseData(bookTaskDetailIds)
)
//#endregion
// 对指定的小说任务的图片进行锁定和解锁
ipcMain.handle(
DEFINE_STRING.BOOK.IMAGE_LOCK_OPERATION,
async (event, id, lockType, operateBookType) =>
await bookImage.ImageLockOperation(id, lockType, operateBookType)
)
// 下载或者使用指定的图片并裁剪
ipcMain.handle(
DEFINE_STRING.BOOK.DOWNLOAD_IMAGE_AND_SPLIT,
async (event, bookTaskDetailId, imageUrl) =>
await bookImage.DownloadImageUrlAndSplit(bookTaskDetailId, imageUrl)
)
// 一拆四,将一个任务拆分成四个任务,并且复制对应的图片
ipcMain.handle(
DEFINE_STRING.BOOK.ONE_TO_FOUR_BOOK_TASK,
async (event, bookTaskDetailId) => await bookBasic.OneToFourBookTask(bookTaskDetailId)
)
//#region 小说批次任务相关
// 重置小说批次数据
ipcMain.handle(
DEFINE_STRING.BOOK.RESET_BOOK_TASK,
async (event, bookTaskId) => await bookTask.ReSetBookTask(bookTaskId)
)
// 删除小说批次数据
ipcMain.handle(
DEFINE_STRING.BOOK.DELETE_BOOK_TASK,
async (event, bookTaskId) => await bookTask.DeleteBookTask(bookTaskId)
)
// 一键生成当前批次的所有图片
ipcMain.handle(
DEFINE_STRING.BOOK.GENERATE_IMAGE_ALL,
async (event, bookTaskId, imageCategory) =>
await bookImage.GenerateImageAll(bookTaskId, imageCategory)
)
// 高清图片前检查,主要是检查图片大小
ipcMain.handle(
DEFINE_STRING.BOOK.CHECK_IMAGE_FILE_SIZE,
async (event, id, fileSize, operateBookType) =>
await bookImage.CheckImageFileSize(id, fileSize, operateBookType)
)
// 开始执行高清图片
ipcMain.handle(
DEFINE_STRING.BOOK.HD_IMAGE,
async (event, id, scale, operateBookType) => await bookImage.HDImage(id, scale, operateBookType)
)
// 将小说视频相关的设置添加到小说任务批次
ipcMain.handle(
DEFINE_STRING.BOOK.USE_BOOK_VIDEO_DATA_TO_BOOK_TASK,
async (event, id, operateBookType) =>
await bookVideo.UseBookVideoDataToBookTask(id, operateBookType)
)
// 将小说任务添加到剪映草稿
ipcMain.handle(
DEFINE_STRING.BOOK.ADD_JIANYING_DRAFT,
async (event, id, operateBookType) => await bookVideo.AddJianyingDraft(id, operateBookType)
)
//#endregion
} }

View File

@ -0,0 +1,37 @@
import { ipcMain } from 'electron'
import { DEFINE_STRING } from '../../define/define_string'
import { errorMessage, successMessage } from '../Public/generalTools'
import { Book } from '../../model/book'
import { BookTaskService } from '../../define/db/service/Book/bookTaskService'
import { BookTaskDetailService } from '../../define/db/service/Book/bookTaskDetailService'
async function DBIpc() {
let bookTaskService = await BookTaskService.getInstance()
let bookTaskDetailService = await BookTaskDetailService.getInstance()
//#region 小说相关的修改
// 修改小说任务的数据
ipcMain.handle(DEFINE_STRING.DB.UPDATE_BOOK_TASK_DATA, async (event, bookTaskId: string, data: Book.SelectBookTask) => {
try {
bookTaskService.UpdetedBookTaskData(bookTaskId, data)
return successMessage(null, "修改小说任务数据成功", "DBIpc_UpdateBookTaskData")
} catch (error) {
return errorMessage("修改小说任务数据失败", "DBIpc_UpdateBookTaskData")
}
})
// 修改小说分镜的详细任务数据
ipcMain.handle(DEFINE_STRING.DB.UPDATE_BOOK_TASK_DETAIL_DATA, async (event, bookTaskDetailId: string, data: Book.SelectBookTaskDetail) => {
try {
bookTaskDetailService.UpdateBookTaskDetail(bookTaskDetailId, data)
return successMessage(null, "修改小说分镜详细任务数据成功", "DBIpc_UpdateBookTaskDetailData")
} catch (error) {
return errorMessage("修改小说分镜详细任务数据失败", "DBIpc_UpdateBookTaskDetailData")
}
})
//#endregion
}
export { DBIpc }

View File

@ -71,7 +71,6 @@ function WinddowUrlRefresh(thisWindow) {
channelID: channelID channelID: channelID
} }
}); });
} }
// 判断是不是需要登录(登录提示) // 判断是不是需要登录(登录提示)

View File

@ -1,14 +1,11 @@
import { import { ipcMain } from 'electron'
ipcMain
} from "electron";
import { DEFINE_STRING } from '../../define/define_string' import { DEFINE_STRING } from '../../define/define_string'
import { Tools } from "../tools"; import { Tools } from '../tools'
import path from "path"; import path from 'path'
import { errorMessage, successMessage } from "../generalTools"; import { errorMessage, successMessage } from '../Public/generalTools'
let tools = new Tools(); let tools = new Tools()
function GlobalIpc() { function GlobalIpc() {
/** /**
* 将传入的文件地址修改为base64 * 将传入的文件地址修改为base64
*/ */
@ -16,31 +13,39 @@ function GlobalIpc() {
try { try {
value = path.normalize(value) value = path.normalize(value)
//检查文件或者时文件夹是不是存在 //检查文件或者时文件夹是不是存在
let isExists = await tools.checkExists(value); let isExists = await tools.checkExists(value)
console.log("isExists", value, isExists); console.log('isExists', value, isExists)
// 获取文件将其转换为base64 // 获取文件将其转换为base64
if (!isExists) { if (!isExists) {
throw new Error("文件不存在"); throw new Error('文件不存在')
} }
return successMessage(await tools.readFileBase64(value)); return successMessage(await tools.readFileBase64(value))
} catch (error) { } catch (error) {
return errorMessage("获取文件失败" + error) return errorMessage('获取文件失败' + error)
} }
}); })
ipcMain.on(DEFINE_STRING.OPEN_DEV_TOOLS, (event) => { ipcMain.on(DEFINE_STRING.OPEN_DEV_TOOLS, (event) => {
global.newWindow[0].win.webContents.openDevTools(); global.newWindow[0].win.webContents.openDevTools()
}) })
ipcMain.handle(DEFINE_STRING.OPEN_DEV_TOOLS_PASSWORD, (event, value) => { ipcMain.handle(DEFINE_STRING.OPEN_DEV_TOOLS_PASSWORD, (event, value) => {
if (value === "297ab55d41e9f5d3eba95b9df432f991") { if (value === '297ab55d41e9f5d3eba95b9df432f991') {
return successMessage("打开成功") return successMessage('打开成功')
} else { } else {
return errorMessage("管理控制台密码错误") return errorMessage('管理控制台密码错误')
} }
}) })
// 监听打开全局窗口事件
ipcMain.on(DEFINE_STRING.SHOW_GLOABAL_MESSAGE_DIALOG, (event, value) => {
global.newWindow[0].win.webContents.send(DEFINE_STRING.SHOW_MESSAGE_DIALOG, value)
})
// 监听打开notification事件
ipcMain.on(DEFINE_STRING.SHOW_GLOBAL_MAIN_NOTIFICATION, (event, value) => {
global.newWindow[0].win.webContents.send(DEFINE_STRING.SHOW_MAIN_NOTIFICATION, value)
})
} }
export { export { GlobalIpc }
GlobalIpc
}

View File

@ -2,7 +2,7 @@ import { ipcMain } from 'electron'
import { DEFINE_STRING } from '../../define/define_string' import { DEFINE_STRING } from '../../define/define_string'
import { Image } from '../Public/Image' import { Image } from '../Public/Image'
import { LOGGER_DEFINE } from '../../define/logger_define' import { LOGGER_DEFINE } from '../../define/logger_define'
import { errorMessage } from '../generalTools' import { errorMessage } from '../Public/generalTools'
let image = new Image(global) let image = new Image(global)
function ImageIpc() { function ImageIpc() {

View File

@ -14,8 +14,9 @@ import { ImageIpc } from './imageIpc.js'
import { SystemIpc } from './systemIpc.js' import { SystemIpc } from './systemIpc.js'
import { BookIpc } from './bookIpc.js' import { BookIpc } from './bookIpc.js'
import { TTSIpc } from './ttsIpc.js' import { TTSIpc } from './ttsIpc.js'
import { DBIpc } from './dbIpc'
export function RegisterIpc(createWindow) { export async function RegisterIpc(createWindow) {
PromptIpc() PromptIpc()
SettingIpc() SettingIpc()
ImageGenerateIpc() ImageGenerateIpc()
@ -24,6 +25,7 @@ export function RegisterIpc(createWindow) {
TranslateIpc() TranslateIpc()
GptIpc() GptIpc()
SdIpc() SdIpc()
await DBIpc()
MjIpc() MjIpc()
MainIpc(createWindow) MainIpc(createWindow)
OriginalImageGenerateIpc() OriginalImageGenerateIpc()

View File

@ -1,87 +1,154 @@
import { ipcMain } from "electron" import { ipcMain } from 'electron'
import { DEFINE_STRING } from "../../define/define_string" import { DEFINE_STRING } from '../../define/define_string'
import { MjSimple } from "../discord/mjSimple" import { MjSimple } from '../discord/mjSimple'
import { TagCustomize } from "../Original/TagCustomize" import { TagCustomize } from '../Original/TagCustomize'
import { MJOriginalImageGenerate } from '../Original/MJOriginalImageGenerate' import { MJOriginalImageGenerate } from '../Original/MJOriginalImageGenerate'
import { PublicMethod } from "../Public/publicMethod" import { PublicMethod } from '../Public/publicMethod'
import { DiscordSimple } from "../discord/discordSimple" import { DiscordSimple } from '../discord/discordSimple'
import { Tools } from "../tools" import { Tools } from '../tools'
import path from 'path' import path from 'path'
import { MJOpt } from '../Service/MJ/mj'
let mjSimple = new MjSimple(global) let mjSimple = new MjSimple(global)
let discordSimple = new DiscordSimple(null) let discordSimple = new DiscordSimple(null)
let tagCustomize = new TagCustomize(global); let tagCustomize = new TagCustomize(global)
let mJOriginalImageGenerate = new MJOriginalImageGenerate(global); let mJOriginalImageGenerate = new MJOriginalImageGenerate(global)
let publicMethod = new PublicMethod(global); let publicMethod = new PublicMethod(global)
let tools = new Tools(); let mjOpt = new MJOpt()
function MjIpc() { function MjIpc() {
// 监听保存mj的文案配置信息 // 监听保存mj的文案配置信息
ipcMain.handle(DEFINE_STRING.MJ.SAVE_WORD_SRT, async (event, value) => await mjSimple.SvaeMJWordSrt(value)); ipcMain.handle(
DEFINE_STRING.MJ.SAVE_WORD_SRT,
async (event, value) => await mjSimple.SvaeMJWordSrt(value)
)
// 监听获取MJ的文件配置信息 // 监听获取MJ的文件配置信息
ipcMain.handle(DEFINE_STRING.MJ.GET_MJ_CONFIG_SRT_INFORMATION, async (event, value) => await mjSimple.GetMJConfigSrtInformation(value)); ipcMain.handle(
DEFINE_STRING.MJ.GET_MJ_CONFIG_SRT_INFORMATION,
async (event, value) => await mjSimple.GetMJConfigSrtInformation(value)
)
// 监听获取标签数据 // 监听获取标签数据
ipcMain.handle(DEFINE_STRING.MJ.GET_TAG_DATA_BY_TYPE_AND_PROPERTY, async (event, value) => await tagCustomize.GetTagDataByTypeAndProperty(value)); ipcMain.handle(
DEFINE_STRING.MJ.GET_TAG_DATA_BY_TYPE_AND_PROPERTY,
async (event, value) => await tagCustomize.GetTagDataByTypeAndProperty(value)
)
// 保存指定的标签数据 // 保存指定的标签数据
ipcMain.handle(DEFINE_STRING.MJ.SAVE_TAG_PROPERTY_DATA, async (event, value) => await tagCustomize.SaveTagPropertyData(value)); ipcMain.handle(
DEFINE_STRING.MJ.SAVE_TAG_PROPERTY_DATA,
async (event, value) => await tagCustomize.SaveTagPropertyData(value)
)
// 删除指定的标签数据 // 删除指定的标签数据
ipcMain.handle(DEFINE_STRING.MJ.DELETE_TAG_PROPERTY_DATA, async (event, value) => await tagCustomize.DeleteTagPropertyData(value)); ipcMain.handle(
DEFINE_STRING.MJ.DELETE_TAG_PROPERTY_DATA,
async (event, value) => await tagCustomize.DeleteTagPropertyData(value)
)
// MJ 原创生图 // MJ 原创生图
ipcMain.handle(DEFINE_STRING.MJ.ORIGINAL_MJ_IMAGE_GENERATE, async (event, value) => await mJOriginalImageGenerate.OriginalMJImageGenerate(value)); ipcMain.handle(
DEFINE_STRING.MJ.ORIGINAL_MJ_IMAGE_GENERATE,
async (event, value) => await mJOriginalImageGenerate.OriginalMJImageGenerate(value)
)
// 获取discord的频道机器人 // 获取discord的频道机器人
ipcMain.handle(DEFINE_STRING.MJ.GET_CHANNEL_ROBOTS, async (event, value) => await mjSimple.GetChannelRobots(value)); ipcMain.handle(
DEFINE_STRING.MJ.GET_CHANNEL_ROBOTS,
async (event, value) => await mjSimple.GetChannelRobots(value)
)
// 获取MJ生图的方式 // 获取MJ生图的方式
// GetMJGenerateCategory: async (callback) => callback(await ipcRenderer.invoke(DEFINE_STRING.MJ.GET_MJ_GENERATE_CATEGORY)), // GetMJGenerateCategory: async (callback) => callback(await ipcRenderer.invoke(DEFINE_STRING.MJ.GET_MJ_GENERATE_CATEGORY)),
ipcMain.handle(DEFINE_STRING.MJ.GET_MJ_GENERATE_CATEGORY, async (event) => await mjSimple.GetMJGenerateCategory()); ipcMain.handle(
DEFINE_STRING.MJ.GET_MJ_GENERATE_CATEGORY,
async (event) => await mjSimple.GetMJGenerateCategory()
)
// MJ生成的图片分割 // MJ生成的图片分割
ipcMain.handle(DEFINE_STRING.MJ.IMAGE_SPLIT, async (event, value) => await mJOriginalImageGenerate.ImageSplit(value)); ipcMain.handle(
DEFINE_STRING.MJ.IMAGE_SPLIT,
async (event, value) => await mJOriginalImageGenerate.ImageSplit(value)
)
// 添加MJ敏感词 // 添加MJ敏感词
ipcMain.handle(DEFINE_STRING.MJ.ADD_MJ_BAD_PROMPT, async (event, value) => await mjSimple.AddMJBadPrompt(value)); ipcMain.handle(
DEFINE_STRING.MJ.ADD_MJ_BAD_PROMPT,
async (event, value) => await mjSimple.AddMJBadPrompt(value)
)
// 添加MJ敏感词检查 // 添加MJ敏感词检查
ipcMain.handle(DEFINE_STRING.MJ.MJ_BAD_PROMPT_CHECK, async (event, value) => await mjSimple.MJBadPromptCheck(value)); ipcMain.handle(
DEFINE_STRING.MJ.MJ_BAD_PROMPT_CHECK,
async (event, value) => await mjSimple.MJBadPromptCheck(value)
)
// 获取已经生图完成的数据,并获取图片 // 获取已经生图完成的数据,并获取图片
ipcMain.handle(DEFINE_STRING.MJ.GET_GENERATED_MJ_IMAGE_AND_SPLIT, async (event, value) => await mJOriginalImageGenerate.GetGeneratedMJImageAndSplit(value)); ipcMain.handle(
DEFINE_STRING.MJ.GET_GENERATED_MJ_IMAGE_AND_SPLIT,
async (event, value) => await mJOriginalImageGenerate.GetGeneratedMJImageAndSplit(value)
)
// 给图片链接,下载指定的图片并分割保存 // 给图片链接,下载指定的图片并分割保存
ipcMain.handle(DEFINE_STRING.MJ.DOWNLOAD_IMAGE_URL_AND_SPLIT, async (event, value) => await mJOriginalImageGenerate.DownloadImageUrlAndSplit(value)); ipcMain.handle(
DEFINE_STRING.MJ.DOWNLOAD_IMAGE_URL_AND_SPLIT,
async (event, value) => await mJOriginalImageGenerate.DownloadImageUrlAndSplit(value)
)
// 获取MJ图片的所有的分割尺寸 // 获取MJ图片的所有的分割尺寸
ipcMain.handle(DEFINE_STRING.MJ.GET_MJ_IMAGE_SCALE, async (event) => await mjSimple.GetMJImageScale()); ipcMain.handle(
DEFINE_STRING.MJ.GET_MJ_IMAGE_SCALE,
async (event) => await mjSimple.GetMJImageScale()
)
// 获取所有的MJ生图模型 // 获取所有的MJ生图模型
ipcMain.handle(DEFINE_STRING.MJ.GET_MJ_IMAGE_ROBOT_MODEL, async (event) => await mjSimple.GetMJImageRobotModel()); ipcMain.handle(
DEFINE_STRING.MJ.GET_MJ_IMAGE_ROBOT_MODEL,
async (event) => await mjSimple.GetMJImageRobotModel()
)
// 自动匹配用户表标签 // 自动匹配用户表标签
ipcMain.handle(DEFINE_STRING.MJ.AUTO_MATCH_USER, async (event, value) => await mJOriginalImageGenerate.AutoMatchUser(value)); ipcMain.handle(
DEFINE_STRING.MJ.AUTO_MATCH_USER,
async (event, value) => await mJOriginalImageGenerate.AutoMatchUser(value)
)
/** /**
* 监听DISCORD界面创建消息并修改数据 * 监听DISCORD界面创建消息并修改数据
*/ */
ipcMain.on(DEFINE_STRING.DISCORD.CREATE_MESSAGE, async (event, value) => await discordSimple.DiscordCreateMessage(value)); ipcMain.on(
DEFINE_STRING.DISCORD.CREATE_MESSAGE,
async (event, value) => await discordSimple.DiscordCreateMessage(value)
)
/** /**
* 监听DISCORD界面的更新消息并修改数据 * 监听DISCORD界面的更新消息并修改数据
*/ */
ipcMain.on(DEFINE_STRING.DISCORD.UPDATE_MESSAGE, async (event, value) => await discordSimple.DiscordUpdateMessage(value)); ipcMain.on(
DEFINE_STRING.DISCORD.UPDATE_MESSAGE,
async (event, value) => await discordSimple.DiscordUpdateMessage(value)
)
/** /**
* 监听DISCORD界面的删除消息并修改数据 * 监听DISCORD界面的删除消息并修改数据
*/ */
ipcMain.on(DEFINE_STRING.DISCORD.DELETE_MESSAGE, async (event, value) => await discordSimple.DiscordDeleteMessage(value)); ipcMain.on(
DEFINE_STRING.DISCORD.DELETE_MESSAGE,
async (event, value) => await discordSimple.DiscordDeleteMessage(value)
)
// MJ合并提示词命令
ipcMain.handle(
DEFINE_STRING.MJ.MJ_MERGE_PROMPT,
async (event, id, mergeType) => await mjOpt.MergePrompt(id, mergeType)
)
// MJ出单张图
ipcMain.handle(
DEFINE_STRING.MJ.ADD_MJ_GENADD_MJ_GENERATE_IMAGE_TASK,
async (event, id, operateBookType) => await mjOpt.AddMJGenerateImageTask(id, operateBookType)
)
} }
export { export { MjIpc }
MjIpc
}

View File

@ -1,21 +1,26 @@
import { ipcMain } from "electron"; import { ipcMain } from 'electron'
import { DEFINE_STRING } from '../../define/define_string' import { DEFINE_STRING } from '../../define/define_string'
import { Prompt } from "../Public/Prompt"; import { Prompt } from '../Public/Prompt'
let prompt = new Prompt(); let prompt = new Prompt()
function PromptIpc() { function PromptIpc() {
// 获取所有的排序选项 // 获取所有的排序选项
ipcMain.handle(DEFINE_STRING.PROMPT.GET_SORT_OPTIONS, (event) => prompt.GetPromptSortOptions()); ipcMain.handle(DEFINE_STRING.PROMPT.GET_SORT_OPTIONS, (event) => prompt.GetPromptSortOptions())
// 保存提示词排序数据 // 保存提示词排序数据
ipcMain.handle(DEFINE_STRING.PROMPT.SAVE_PROMPT_SORT_DATA, (event, value) => prompt.SavePromptSort(value)); ipcMain.handle(DEFINE_STRING.PROMPT.SAVE_PROMPT_SORT_DATA, (event, value) =>
prompt.SavePromptSort(value)
)
// 获取已经保存的提示词数据 // 获取已经保存的提示词数据
ipcMain.handle(DEFINE_STRING.PROMPT.GET_PROMPT_SORT_DATA, (event) => prompt.GetPromptSort()) ipcMain.handle(
DEFINE_STRING.PROMPT.GET_PROMPT_SORT_DATA,
async (event) => await prompt.GetPromptSort()
)
// 获取提示词文件数据txt指定路径 // 获取提示词文件数据txt指定路径
ipcMain.handle(DEFINE_STRING.PROMPT.OPEN_PROMPT_FILE_TXT, (event, value) => prompt.OpenPromptFileTxt(value)) ipcMain.handle(DEFINE_STRING.PROMPT.OPEN_PROMPT_FILE_TXT, (event, value) =>
} prompt.OpenPromptFileTxt(value)
export { )
PromptIpc
} }
export { PromptIpc }

View File

@ -1,24 +1,39 @@
import { ipcMain } from "electron"; import { ipcMain } from 'electron'
import { DEFINE_STRING } from '../../define/define_string' import { DEFINE_STRING } from '../../define/define_string'
import { SD } from "../Public/SD"; import { SD } from '../Public/SD'
let sd = new SD(global); import { SDOpt } from '../Service/SD/sd'
let sd = new SD(global)
let sdOpt = new SDOpt()
function SdIpc() { function SdIpc() {
// 获取样式图片的子列表 // 获取样式图片的子列表
ipcMain.handle(DEFINE_STRING.GET_STYLE_IMAGE_SUB_LIST, async (event, value) => await sd.GetStyleImageSubList(value)); ipcMain.handle(
DEFINE_STRING.GET_STYLE_IMAGE_SUB_LIST,
async (event, value) => await sd.GetStyleImageSubList(value)
)
// 获取图片样式信息 // 获取图片样式信息
ipcMain.handle(DEFINE_STRING.GET_IMAGE_STYLE_INFOMATION, async (event, value) => await sd.GetImageStyleInfomation(value)); ipcMain.handle(
DEFINE_STRING.GET_IMAGE_STYLE_INFOMATION,
async (event, value) => await sd.GetImageStyleInfomation(value)
)
// 获取图片样式菜单 // 获取图片样式菜单
ipcMain.handle(DEFINE_STRING.GET_IMAGE_STYLE_MENU, async (event) => await sd.GetImageStyleMenu()); ipcMain.handle(DEFINE_STRING.GET_IMAGE_STYLE_MENU, async (event) => await sd.GetImageStyleMenu())
// 加载当前链接的SD服务数据 // 加载当前链接的SD服务数据
ipcMain.handle(DEFINE_STRING.SD.LOAD_SD_SERVICE_DATA, async (event, value) => await sd.LoadSDServiceData(value)); ipcMain.handle(
DEFINE_STRING.SD.LOAD_SD_SERVICE_DATA,
async (event, value) => await sd.LoadSDServiceData(value)
)
// 文生图,单张 // 文生图,单张
ipcMain.handle(DEFINE_STRING.SD.TXT2IMG, async (event, value) => await sd.txt2img(value)); ipcMain.handle(DEFINE_STRING.SD.TXT2IMG, async (event, value) => await sd.txt2img(value))
}
export { // SD合并提示词
SdIpc ipcMain.handle(
DEFINE_STRING.SD.SD_MERGE_PROMPT,
async (event, id, mergeType) => await sdOpt.MergePrompt(id, mergeType)
)
} }
export { SdIpc }

View File

@ -11,6 +11,8 @@ import { BasicSetting } from '../setting/basicSetting'
let basicSetting = new BasicSetting() let basicSetting = new BasicSetting()
import { MJSetting } from '../setting/mjSetting' import { MJSetting } from '../setting/mjSetting'
let mjSetting = new MJSetting() let mjSetting = new MJSetting()
import { Watermark } from '../Service/watermark'
let watermark = new Watermark()
async function SettingIpc() { async function SettingIpc() {
// 获取背景音乐配置列表 // 获取背景音乐配置列表
@ -172,46 +174,67 @@ async function SettingIpc() {
//#region MJ 设置 //#region MJ 设置
// 获取MJ基础设置信息 // 获取MJ基础设置信息
ipcMain.handle(DEFINE_STRING.SETTING.GET_MJ_SETTING, async (event, value) => ipcMain.handle(
mjSetting.GetMJSetting(value) DEFINE_STRING.SETTING.GET_MJ_SETTING,
async (event, value) => await mjSetting.GetMJSetting(value)
) )
// 保存MJ的基础设置信息 // 保存MJ的基础设置信息
ipcMain.handle(DEFINE_STRING.SETTING.UPDATE_MJ_SETTING, async (event, value) => ipcMain.handle(
mjSetting.UpdateMJSetting(value) DEFINE_STRING.SETTING.UPDATE_MJ_SETTING,
async (event, value) => await mjSetting.UpdateMJSetting(value)
) )
// 获取MJ的所有设置 // 获取MJ的所有设置
ipcMain.handle(DEFINE_STRING.SETTING.GET_MJ_SETTING_TREE_DATA, async (event) => ipcMain.handle(
mjSetting.GetMJSettingTreeData() DEFINE_STRING.SETTING.GET_MJ_SETTING_TREE_DATA,
async (event) => await mjSetting.GetMJSettingTreeData()
) )
// 保存MJ的所有设置 // 保存MJ的所有设置
ipcMain.handle(DEFINE_STRING.SETTING.SAVE_MJ_SETTING_TREE_DATA, async (event, value) => ipcMain.handle(
mjSetting.SaveMJSettingTreeData(value) DEFINE_STRING.SETTING.SAVE_MJ_SETTING_TREE_DATA,
async (event, value) => await mjSetting.SaveMJSettingTreeData(value)
) )
// 获取所有的代理MJ信息 // 获取所有的代理MJ信息
ipcMain.handle(DEFINE_STRING.SETTING.GET_REMOTE_MJ_SETTINGS, async (event) => ipcMain.handle(
mjSetting.GetRemoteMJSettings() DEFINE_STRING.SETTING.GET_REMOTE_MJ_SETTINGS,
async (event) => await mjSetting.GetRemoteMJSettings()
) )
// 创建新的代理MJ信息 // 创建新的代理MJ信息
ipcMain.handle(DEFINE_STRING.SETTING.ADD_REMOTE_MJ_SETTING, async (event, value) => ipcMain.handle(
mjSetting.AddRemoteMJSetting(value) DEFINE_STRING.SETTING.ADD_REMOTE_MJ_SETTING,
async (event, value) => await mjSetting.AddRemoteMJSetting(value)
) )
// 修改MJ账号并重连 // 修改MJ账号并重连
ipcMain.handle(DEFINE_STRING.SETTING.UPDATE_REMOTE_MJ_SETTING, async (event, value) => ipcMain.handle(
mjSetting.UpdateRemoteMJSetting(value) DEFINE_STRING.SETTING.UPDATE_REMOTE_MJ_SETTING,
async (event, value) => await mjSetting.UpdateRemoteMJSetting(value)
) )
// 删除指定的MJ账号 // 删除指定的MJ账号
ipcMain.handle(DEFINE_STRING.SETTING.DELETE_REMOTE_MJ_SETTING, async (event, value) => ipcMain.handle(
mjSetting.DeleteRemoteMJSetting(value) DEFINE_STRING.SETTING.DELETE_REMOTE_MJ_SETTING,
async (event, value) => await mjSetting.DeleteRemoteMJSetting(value)
) )
//#endregion //#endregion
//#region 去除水印设置
ipcMain.handle(
DEFINE_STRING.SETTING.GET_WATER_MARK_SETTING,
async (event) => await watermark.GetWatermarkSetting()
)
ipcMain.handle(
DEFINE_STRING.SETTING.SAVE_WATER_MARK_SETTING,
async (event, value) => await watermark.SaveWatermarkSetting(value)
)
//#endregion
} }
export { SettingIpc } export { SettingIpc }

View File

@ -1,7 +1,7 @@
import { ipcMain } from "electron"; import { ipcMain } from "electron";
import { DEFINE_STRING } from '../../define/define_string' import { DEFINE_STRING } from '../../define/define_string'
import { CheckFileOrDirExist } from "../../define/Tools/file"; import { CheckFileOrDirExist } from "../../define/Tools/file";
import { errorMessage, successMessage } from "../generalTools"; import { errorMessage, successMessage } from "../Public/generalTools";
import path from 'path' import path from 'path'
const { shell } = require('electron') const { shell } = require('electron')

View File

@ -1,18 +1,54 @@
import { ipcMain } from "electron"; import { ipcMain } from 'electron'
import { DEFINE_STRING } from '../../define/define_string' import { DEFINE_STRING } from '../../define/define_string'
import { Translate } from '../Public/Translate' import { Translate } from '../Public/Translate'
let translate = new Translate(global); let translate = new Translate(global)
import { TranslateService } from '../Service/Translate/TranslateService'
const translateService = new TranslateService()
function TranslateIpc() { function TranslateIpc() {
//#region 要删除的
// 监听添加任务到翻译队列的任务 // 监听添加任务到翻译队列的任务
ipcMain.handle(DEFINE_STRING.TRANSLATE_PROMPT, async (event, value) => await translate.TranslatePrompt(value)); ipcMain.handle(
DEFINE_STRING.TRANSLATE_PROMPT,
async (event, value) => await translate.TranslatePrompt(value)
)
// 添加立即返回的翻译任务 // 添加立即返回的翻译任务
ipcMain.handle(DEFINE_STRING.TRANSLATE_RETURN_NOW, async (event, value) => await translate.TranslateReturnNow(value)); ipcMain.handle(
DEFINE_STRING.TRANSLATE_RETURN_NOW,
async (event, value) => await translate.TranslateReturnNow(value)
)
// 添加立即返回的翻译任务到队列中 // 添加立即返回的翻译任务到队列中
ipcMain.handle(DEFINE_STRING.MJ.TRANSLATE_RETURN_NOW_TASK, async (event, value) => await translate.TranslateReturnNowTask(value)); ipcMain.handle(
} DEFINE_STRING.MJ.TRANSLATE_RETURN_NOW_TASK,
export { async (event, value) => await translate.TranslateReturnNowTask(value)
TranslateIpc )
//#endregion
// 获取翻译设置
ipcMain.handle(
DEFINE_STRING.TRANSLATE.GET_TRANSLATE_SETTING,
async () => await translateService.GetTranslateSetting()
)
// 重置翻译设置
ipcMain.handle(
DEFINE_STRING.TRANSLATE.RESET_TRANSLATE_SETTING,
async () => await translateService.ResetTranslateSetting()
)
// 保存翻译设置
ipcMain.handle(
DEFINE_STRING.TRANSLATE.SAVE_TRANSLATE_SETTING,
async (event, value) => await translateService.SaveTranslateSetting(value)
)
// 直接返回的翻译任务
ipcMain.handle(
DEFINE_STRING.TRANSLATE.TRANSLATE_NOW_RETURN,
async (event, value) => await translateService.TranslateNowReturn(value)
)
} }
export { TranslateIpc }

View File

@ -1,16 +1,16 @@
import { ipcMain } from 'electron' import { ipcMain } from 'electron'
import { DEFINE_STRING } from '../../define/define_string' import { DEFINE_STRING } from '../../define/define_string'
import { LOGGER_DEFINE } from '../../define/logger_define' import { LOGGER_DEFINE } from '../../define/logger_define'
import { errorMessage } from '../generalTools' import { TTS } from '../Service/tts'
import { TTSSetting } from '../setting/ttsSetting' const tts = new TTS()
const ttsSetting = new TTSSetting()
export function TTSIpc() { export function TTSIpc() {
// 获取当前的TTS配置数据 // 获取当前的TTS配置数据
ipcMain.handle(DEFINE_STRING.TTS.GET_TTS_CONFIG, async () => ttsSetting.GetTTSCOnfig()) ipcMain.handle(DEFINE_STRING.TTS.GET_TTS_CONFIG, async () => tts.GetTTSCOnfig())
// 保存TTS配置 // 保存TTS配置
ipcMain.handle(DEFINE_STRING.TTS.SAVE_TTS_CONFIG, async (event, data) => ipcMain.handle(DEFINE_STRING.TTS.SAVE_TTS_CONFIG, async (event, data) => tts.SaveTTSConfig(data))
ttsSetting.SaveTTSConfig(data)
) // 生成音频
ipcMain.handle(DEFINE_STRING.TTS.GENERATE_AUDIO, async (event, text) => tts.GenerateAudio(text))
} }

View File

@ -1,6 +1,6 @@
import { ipcMain } from 'electron' import { ipcMain } from 'electron'
import { DEFINE_STRING } from '../../define/define_string' import { DEFINE_STRING } from '../../define/define_string'
import { Writing } from '../Task/writing' import { Writing } from '../Service/writing'
let writing = new Writing(global) let writing = new Writing(global)
import { WritingSetting } from '../setting/writeSetting' import { WritingSetting } from '../setting/writeSetting'
let writingSetting = new WritingSetting() let writingSetting = new WritingSetting()

View File

@ -9,7 +9,7 @@ import path from 'path'
import sharp from 'sharp' import sharp from 'sharp'
import { define } from '../../define/define' import { define } from '../../define/define'
import { AwesomeHelp } from 'awesome-js' import { AwesomeHelp } from 'awesome-js'
import { checkStringValueAddSuffix, errorMessage, successMessage } from '../generalTools' import { checkStringValueAddSuffix, errorMessage, successMessage } from '../Public/generalTools'
import { ImageSetting } from '../../define/setting/imageSetting' import { ImageSetting } from '../../define/setting/imageSetting'
import { DiscordAPI } from '../../api/discordApi' import { DiscordAPI } from '../../api/discordApi'
import { GPT } from '../Public/GPT' import { GPT } from '../Public/GPT'
@ -463,7 +463,7 @@ export class MJOriginalImageGenerate {
} }
let data = { let data = {
prompt: prompt, prompt: prompt,
botType: 'MID_JOURNEY', botType: mjSetting.selectRobot == 'niji' ? 'NIJI_JOURNEY' : 'MID_JOURNEY',
accountFilter: { accountFilter: {
modes: [mj_speed == 'fast' ? 'FAST' : 'RELAX'] modes: [mj_speed == 'fast' ? 'FAST' : 'RELAX']
} }
@ -479,7 +479,7 @@ export class MJOriginalImageGenerate {
} }
let data = { let data = {
prompt: prompt, prompt: prompt,
botType: 'MID_JOURNEY', botType: mjSetting.selectRobot == 'niji' ? 'NIJI_JOURNEY' : 'MID_JOURNEY',
accountFilter: { accountFilter: {
remark: this.global.machineId remark: this.global.machineId
} }

View File

@ -1,98 +1,97 @@
import { Tools } from '../tools'
import { Tools } from "../tools"; import path from 'path'
import path from "path"; import { DEFINE_STRING } from '../../define/define_string'
import { DEFINE_STRING } from "../../define/define_string"; import { define } from '../../define/define'
import { define } from "../../define/define"; import { PublicMethod } from '../Public/publicMethod'
import { PublicMethod } from "../Public/publicMethod"; import { SD } from '../Public/SD'
import { SD } from "../Public/SD" const util = require('util')
const util = require('util'); import axios from 'axios'
import axios from "axios"; const sharp = require('sharp')
const sharp = require('sharp'); const { spawn, exec } = require('child_process')
const { spawn, exec } = require('child_process'); const execAsync = util.promisify(exec)
const execAsync = util.promisify(exec); const { v4: uuidv4 } = require('uuid') // 引入UUID库来生成唯一标识符
const { v4: uuidv4 } = require('uuid'); // 引入UUID库来生成唯一标识符 let fspromises = require('fs').promises
let fspromises = require("fs").promises; import { ImageStyleDefine } from '../../define/iamgeStyleDefine'
import { ImageStyleDefine } from "../../define/iamgeStyleDefine";
export class OriginalImageGenerate { export class OriginalImageGenerate {
constructor(global) { constructor(global) {
this.global = global; this.global = global
this.tools = new Tools(); this.tools = new Tools()
this.pm = new PublicMethod(global); this.pm = new PublicMethod(global)
this.sd = new SD(global); this.sd = new SD(global)
} }
/** /**
* SD原创单张图片生成 * SD原创单张图片生成
* @param {*} value 传入的参数 0 原创界面的data数据信息1是否需要格式化2是否需要全局提示 * @param {*} value 传入的参数 0 原创界面的data数据信息1是否需要格式化2是否需要全局提示
*/ */
async OriginalSDImageGenerate(value) { async OriginalSDImageGenerate(value) {
try { try {
let data = value[0]; let data = value[0]
if (value[1]) { if (value[1]) {
data = JSON.parse(data); data = JSON.parse(data)
} }
let show_global_message = value[2]; let show_global_message = value[2]
// 判断输出的文件夹路径是不是存在,不存在创建 // 判断输出的文件夹路径是不是存在,不存在创建
let output_crop_path = path.join(this.global.config.project_path, "tmp/output_crop_1"); let output_crop_path = path.join(this.global.config.project_path, 'tmp/output_crop_1')
// 检查文件是不是存在 // 检查文件是不是存在
let isE = await this.tools.checkExists(output_crop_path); let isE = await this.tools.checkExists(output_crop_path)
if (!isE) { if (!isE) {
output_crop_path = path.join(this.global.config.project_path, "tmp/output_crop_00001"); output_crop_path = path.join(this.global.config.project_path, 'tmp/output_crop_00001')
} }
await this.tools.checkFolderExistsOrCreate(output_crop_path); await this.tools.checkFolderExistsOrCreate(output_crop_path)
let SdOriginalImage = path.join(this.global.config.project_path, 'data/SdOriginalImage'); let SdOriginalImage = path.join(this.global.config.project_path, 'data/SdOriginalImage')
await this.tools.checkFolderExistsOrCreate(SdOriginalImage); await this.tools.checkFolderExistsOrCreate(SdOriginalImage)
// 获取当前的同用前缀后缀 // 获取当前的同用前缀后缀
let config_json = await this.pm.GetConfigJson(JSON.stringify([null, {}])); let config_json = await this.pm.GetConfigJson(JSON.stringify([null, {}]))
let prefix_prompt = config_json.data.prefix_prompt; let prefix_prompt = config_json.data.prefix_prompt
let suffix_prompt = config_json.data.suffix_prompt; let suffix_prompt = config_json.data.suffix_prompt
let batch = DEFINE_STRING.QUEUE_BATCH.SD_ORIGINAL_GENERATE_IMAGE; let batch = DEFINE_STRING.QUEUE_BATCH.SD_ORIGINAL_GENERATE_IMAGE
let url = this.global.config.webui_api_url + 'sdapi/v1/txt2img'; let url = this.global.config.webui_api_url + 'sdapi/v1/txt2img'
let sd_setting = JSON.parse(await fspromises.readFile(define.sd_setting, 'utf-8')); let sd_setting = JSON.parse(await fspromises.readFile(define.sd_setting, 'utf-8'))
// 判断当前是不是有开修脸修手 // 判断当前是不是有开修脸修手
let ADetailer = { let ADetailer = {
args: sd_setting.adetailer args: sd_setting.adetailer
}; }
let seed = sd_setting.setting.seed; let seed = sd_setting.setting.seed
let style_ids = await this.pm.GetConfigJson(JSON.stringify(["image_style", []]), false); let style_ids = await this.pm.GetConfigJson(JSON.stringify(['image_style', []]), false)
for (let i = 0; i < data.length; i++) { for (let i = 0; i < data.length; i++) {
const element = data[i]; const element = data[i]
let adetailer = element.adetailer; let adetailer = element.adetailer
let imageJson = JSON.parse(await fspromises.readFile(path.normalize(element.prompt_json), 'utf-8')); let imageJson = JSON.parse(
let prompt = sd_setting.webui.prompt + ',' + element.prompt; await fspromises.readFile(path.normalize(element.prompt_json), 'utf-8')
)
let prompt = sd_setting.webui.prompt + ',' + element.prompt
// 添加前缀 // 添加前缀
if (prefix_prompt) { if (prefix_prompt) {
prompt = prefix_prompt + ',' + prompt; prompt = prefix_prompt + ',' + prompt
} }
// 添加后缀 // 添加后缀
if (suffix_prompt) { if (suffix_prompt) {
prompt = prompt + ',' + suffix_prompt; prompt = prompt + ',' + suffix_prompt
} }
// let prompt = imageJson.webui_config.prompt; // let prompt = imageJson.webui_config.prompt;
this.global.requestQuene.enqueue(async () => { this.global.requestQuene.enqueue(
async () => {
try { try {
// 开始请求 // 开始请求
let body = { let body = {
"prompt": prompt, prompt: prompt,
"negative_prompt": imageJson.webui_config.negative_prompt, negative_prompt: imageJson.webui_config.negative_prompt,
"seed": seed, seed: seed,
"sampler_name": sd_setting.webui.sampler_name, sampler_name: sd_setting.webui.sampler_name,
// 提示词相关性 // 提示词相关性
"cfg_scale": sd_setting.webui.cfg_scale, cfg_scale: sd_setting.webui.cfg_scale,
"width": sd_setting.webui.width, width: sd_setting.webui.width,
"height": sd_setting.webui.height, height: sd_setting.webui.height,
"batch_size": sd_setting.setting.batch_size, batch_size: sd_setting.setting.batch_size,
"n_iter": 1, n_iter: 1,
"steps": imageJson.webui_config.steps, steps: imageJson.webui_config.steps,
"save_images": false, save_images: false
} }
// 判断是不是开启修脸修手 // 判断是不是开启修脸修手
@ -100,36 +99,46 @@ export class OriginalImageGenerate {
let ta = { let ta = {
ADetailer: ADetailer ADetailer: ADetailer
} }
body.alwayson_scripts = ta; body.alwayson_scripts = ta
} }
const response = await axios.post(url, body); const response = await axios.post(url, body)
let info = JSON.parse(response.data.info); let info = JSON.parse(response.data.info)
if (seed == -1) { if (seed == -1) {
seed = info.seed; seed = info.seed
} }
let images = response.data.images; let images = response.data.images
let subImagePath = []; let subImagePath = []
let out_tmp_image_path = path.join(output_crop_path, `tmp_${element.name}`); let out_tmp_image_path = path.join(output_crop_path, `tmp_${element.name}`)
let out_image_path = path.join(output_crop_path, `${element.name}`); let out_image_path = path.join(output_crop_path, `${element.name}`)
let input_image = path.join(this.global.config.project_path, `tmp/input_crop/${element.name}`); let input_image = path.join(
this.global.config.project_path,
`tmp/input_crop/${element.name}`
)
for (let j = 0; j < images.length; j++) { for (let j = 0; j < images.length; j++) {
const image = images[j]; const image = images[j]
let imageData = Buffer.from(image.split(",", 1)[0], 'base64'); let imageData = Buffer.from(image.split(',', 1)[0], 'base64')
// 写入数据(写入到当前当前项目文件下面的 data/SdOriginalImage 下面) // 写入数据(写入到当前当前项目文件下面的 data/SdOriginalImage 下面)
let image_path = path.join(this.global.config.project_path, `data/SdOriginalImage/${element.name.split('.')[0]}_${j}.png`); let image_path = path.join(
let tmp_image_path = path.join(this.global.config.project_path, `data/SdOriginalImage/tmp_${element.name.split('.')[0]}_${j}.png`); this.global.config.project_path,
subImagePath.push(image_path); `data/SdOriginalImage/${element.name.split('.')[0]}_${j}.png`
)
let tmp_image_path = path.join(
this.global.config.project_path,
`data/SdOriginalImage/tmp_${element.name.split('.')[0]}_${j}.png`
)
subImagePath.push(image_path)
await sharp(imageData) await sharp(imageData)
.toFile(tmp_image_path) .toFile(tmp_image_path)
.then(async () => { .then(async () => {
// 生图成功,删除数据 // 生图成功,删除数据
// 判断原本的图片文件是不是存在,存在删除 // 判断原本的图片文件是不是存在,存在删除
await this.tools.deletePngAndDeleteExifData(tmp_image_path, image_path); await this.tools.deletePngAndDeleteExifData(tmp_image_path, image_path)
}).catch(err => { })
throw err; .catch((err) => {
}); throw err
})
// console.log("文生图成功" + image_path); // console.log("文生图成功" + image_path);
// 将第一个张写出到指定的文件夹中 // 将第一个张写出到指定的文件夹中
@ -138,42 +147,54 @@ export class OriginalImageGenerate {
.toFile(out_tmp_image_path) .toFile(out_tmp_image_path)
.then(async () => { .then(async () => {
// 生图成功,删除数据 // 生图成功,删除数据
await this.tools.deletePngAndDeleteExifData(out_tmp_image_path, out_image_path); await this.tools.deletePngAndDeleteExifData(
await this.tools.copyFileOrDirectory(out_image_path, input_image); out_tmp_image_path,
out_image_path
)
await this.tools.copyFileOrDirectory(out_image_path, input_image)
}) })
.catch(err => { .catch((err) => {
// console.log(err) // console.log(err)
throw err; throw err
}); })
} }
} }
// 将图片的信息写入到config.json文件中 // 将图片的信息写入到config.json文件中
let index = config_json.data.srt_time_information.findIndex(item => item.id == element.id); let index = config_json.data.srt_time_information.findIndex(
(item) => item.id == element.id
)
if (index < 0) { if (index < 0) {
throw new Error("没有找到指定的ID请检查数据"); throw new Error('没有找到指定的ID请检查数据')
} }
config_json.data.srt_time_information[index].subImagePath = subImagePath; config_json.data.srt_time_information[index].subImagePath = subImagePath
config_json.data.srt_time_information[index].outImagePath = out_image_path; config_json.data.srt_time_information[index].outImagePath = out_image_path
this.global.fileQueue.enqueue(async () => { this.global.fileQueue.enqueue(async () => {
await this.pm.SaveConfigJsonProperty([config_json.data.srt_time_information, "srt_time_information", false]); await this.pm.SaveConfigJsonProperty([
}); config_json.data.srt_time_information,
'srt_time_information',
false
])
})
// 返回数据,用于前台刷新,返回图片数据 // 返回数据,用于前台刷新,返回图片数据
this.global.newWindow[0].win.webContents.send(DEFINE_STRING.SD_ORIGINAL_GENERATE_IMAGE_RETURN, { this.global.newWindow[0].win.webContents.send(
DEFINE_STRING.SD_ORIGINAL_GENERATE_IMAGE_RETURN,
{
code: 1, code: 1,
id: element.id, id: element.id,
data: { data: {
subImagePath: subImagePath, subImagePath: subImagePath,
outImagePath: out_image_path outImagePath: out_image_path
} }
});
} catch (error) {
throw error;
} }
)
}, `${batch}_${element.name}`, batch); } catch (error) {
throw error
}
},
`${batch}_${element.name}`,
batch
)
} }
this.global.requestQuene.setBatchCompletionCallback(batch, (failedTasks) => { this.global.requestQuene.setBatchCompletionCallback(batch, (failedTasks) => {
@ -183,8 +204,8 @@ export class OriginalImageGenerate {
但是以下任务执行失败 但是以下任务执行失败
` `
failedTasks.forEach(({ taskId, error }) => { failedTasks.forEach(({ taskId, error }) => {
message += `${taskId}-, \n 错误信息: ${error}` + '\n'; message += `${taskId}-, \n 错误信息: ${error}` + '\n'
}); })
this.global.newWindow[0].win.webContents.send(DEFINE_STRING.SHOW_MESSAGE_DIALOG, { this.global.newWindow[0].win.webContents.send(DEFINE_STRING.SHOW_MESSAGE_DIALOG, {
code: 0, code: 0,
@ -194,17 +215,15 @@ export class OriginalImageGenerate {
if (show_global_message) { if (show_global_message) {
this.global.newWindow[0].win.webContents.send(DEFINE_STRING.SHOW_MESSAGE_DIALOG, { this.global.newWindow[0].win.webContents.send(DEFINE_STRING.SHOW_MESSAGE_DIALOG, {
code: 1, code: 1,
message: "所有生图任务完成" message: '所有生图任务完成'
}) })
} }
} }
}); })
return { return {
code: 1, code: 1
} }
} catch (error) { } catch (error) {
return { return {
code: 0, code: 0,
@ -220,59 +239,75 @@ export class OriginalImageGenerate {
async AutoSaveDataJson(value) { async AutoSaveDataJson(value) {
try { try {
// 目前自动保存的信息,中文提示词,英文提示词,前缀,后缀 // 目前自动保存的信息,中文提示词,英文提示词,前缀,后缀
value = JSON.parse(value); value = JSON.parse(value)
let batch = DEFINE_STRING.QUEUE_BATCH.AUTO_SAVE_DATA_JSON; let batch = DEFINE_STRING.QUEUE_BATCH.AUTO_SAVE_DATA_JSON
for (let i = 0; i < value.length; i++) { for (let i = 0; i < value.length; i++) {
const element = value[i]; const element = value[i]
// 将修改文件的的方法添加到修改文件队列中 // 将修改文件的的方法添加到修改文件队列中
this.global.fileQueue.enqueue(async () => { this.global.fileQueue.enqueue(
async () => {
try { try {
if (element.prompt_json) { if (element.prompt_json) {
let old_json = JSON.parse(await fspromises.readFile(path.normalize(element.prompt_json), 'utf-8')); let old_json = JSON.parse(
old_json.webui_config.prompt = element.prompt; await fspromises.readFile(path.normalize(element.prompt_json), 'utf-8')
)
old_json.webui_config.prompt = element.prompt
// old_json.adetailer = element.adetailer; // old_json.adetailer = element.adetailer;
old_json.chinese_prompt = element.chinese_prompt; old_json.chinese_prompt = element.chinese_prompt
// 前缀提示词 // 前缀提示词
old_json.prefix_prompt = element.prefix_prompt; old_json.prefix_prompt = element.prefix_prompt
// 后缀提示词 // 后缀提示词
old_json.suffix_prompt = element.suffix_prompt; old_json.suffix_prompt = element.suffix_prompt
old_json.adetailer = element.adetailer; old_json.adetailer = element.adetailer
old_json.prompt = element.prompt; old_json.prompt = element.prompt
await fspromises.writeFile(path.normalize(element.prompt_json), JSON.stringify(old_json)); await fspromises.writeFile(
path.normalize(element.prompt_json),
JSON.stringify(old_json)
)
} }
} catch (error) { } catch (error) {
throw new Error(error); throw new Error(error)
} }
}, `${batch}_${element.id}`, batch); },
`${batch}_${element.id}`,
batch
)
// 判断是不是有图片。判断图片是不是符合格式有些格式是file:// 开头的, 以时间结尾(都要删除)) // 判断是不是有图片。判断图片是不是符合格式有些格式是file:// 开头的, 以时间结尾(都要删除))
// 判断是不是有图片 // 判断是不是有图片
let file_regex = /^file:\/\//; let file_regex = /^file:\/\//
if (element.outImagePath && file_regex.test(element.outImagePath)) { if (element.outImagePath && file_regex.test(element.outImagePath)) {
// 删除 "file://" 开头 // 删除 "file://" 开头
element.outImagePath = decodeURI(element.outImagePath); element.outImagePath = decodeURI(element.outImagePath)
// 判断element.outImagePath是不是不是以file://开头的,是的话,删除 // 判断element.outImagePath是不是不是以file://开头的,是的话,删除
if (element.outImagePath.startsWith("file://")) { if (element.outImagePath.startsWith('file://')) {
element.outImagePath = element.outImagePath.substring(7); element.outImagePath = element.outImagePath.substring(7)
} }
element.outImagePath = element.outImagePath.replace(/\?time=.*$/, ''); element.outImagePath = element.outImagePath.replace(/\?time=.*$/, '')
// 判断element.outImagePath是不是以/开头的,是的话,删除 // 判断element.outImagePath是不是以/开头的,是的话,删除
if (element.outImagePath.startsWith("/")) { if (element.outImagePath.startsWith('/')) {
element.outImagePath = element.outImagePath.substring(1); element.outImagePath = element.outImagePath.substring(1)
} }
} }
if (element.subImagePath && element.subImagePath.length > 0) { if (element.subImagePath && element.subImagePath.length > 0) {
for (let j = 0; j < element.subImagePath.length; j++) { for (let j = 0; j < element.subImagePath.length; j++) {
if (file_regex.test(element.subImagePath[j])) { if (file_regex.test(element.subImagePath[j])) {
element.subImagePath[j] = decodeURI(element.subImagePath[j]); element.subImagePath[j] = decodeURI(element.subImagePath[j])
element.subImagePath[j] = element.subImagePath[j].replace(/^file:\/\//, '').replace(/\?time=.*$/, ''); element.subImagePath[j] = element.subImagePath[j]
.replace(/^file:\/\//, '')
.replace(/\?time=.*$/, '')
} }
} }
} }
} }
await this.tools.writeJsonFilePropertyValue(path.join(this.global.config.project_path, "scripts/config.json"), "srt_time_information", value, false); await this.tools.writeJsonFilePropertyValue(
path.join(this.global.config.project_path, 'scripts/config.json'),
'srt_time_information',
value,
false
)
return { return {
code: 1 code: 1
@ -285,43 +320,45 @@ export class OriginalImageGenerate {
} }
} }
/** /**
* 将反推的图片的信息添加到一个json文件中 * 将反推的图片的信息添加到一个json文件中
*/ */
async OriginalAddWebuiJson(value) { async OriginalAddWebuiJson(value) {
try { try {
let data = JSON.parse(value); let data = JSON.parse(value)
// 判断文件夹是不是存在 // 判断文件夹是不是存在
let imput_crop_path = path.join(this.global.config.project_path, "tmp/input_crop"); let imput_crop_path = path.join(this.global.config.project_path, 'tmp/input_crop')
let isExist = await this.tools.checkExists(imput_crop_path); let isExist = await this.tools.checkExists(imput_crop_path)
if (!isExist) { if (!isExist) {
await fspromises.mkdir(imput_crop_path, { recursive: true }) await fspromises.mkdir(imput_crop_path, { recursive: true })
} }
// 判断当前的数据是不是相同 // 判断当前的数据是不是相同
// 读取所有txt文件 // 读取所有txt文件
let promptJson = await this.tools.getFilesWithExtensions(path.join(global.config.project_path, 'tmp/input_crop'), '.json'); let promptJson = await this.tools.getFilesWithExtensions(
path.join(global.config.project_path, 'tmp/input_crop'),
'.json'
)
// json 已经存在,不做后续处理 // json 已经存在,不做后续处理
if (data.length == promptJson.length) { if (data.length == promptJson.length) {
return { return {
code: 1, code: 1,
data: path.join(this.global.config.project_path, "tmp/input_crop") data: path.join(this.global.config.project_path, 'tmp/input_crop')
} }
} }
let sd_config = JSON.parse(await fspromises.readFile(define.sd_setting, 'utf-8')); let sd_config = JSON.parse(await fspromises.readFile(define.sd_setting, 'utf-8'))
for (let i = 0; i < data.length; i++) { for (let i = 0; i < data.length; i++) {
const element = data[i]; const element = data[i]
let name = String(element.no).padStart(5, '0') + ".png"; let name = String(element.no).padStart(5, '0') + '.png'
// console.log(txtpath) // console.log(txtpath)
let obj = {} let obj = {}
obj.model = sd_config.setting.type; obj.model = sd_config.setting.type
obj.api = sd_config.setting.webui_api_url + 'sdapi/v1/txt2img'; obj.api = sd_config.setting.webui_api_url + 'sdapi/v1/txt2img'
obj.name = name; obj.name = name
obj.webui_config = { obj.webui_config = {
sampler_name: sd_config.webui.sampler_name, sampler_name: sd_config.webui.sampler_name,
prompt: "", prompt: '',
negative_prompt: sd_config.webui.negative_prompt, negative_prompt: sd_config.webui.negative_prompt,
batch_size: 1, batch_size: 1,
steps: sd_config.webui.steps, steps: sd_config.webui.steps,
@ -330,20 +367,24 @@ export class OriginalImageGenerate {
width: sd_config.webui.width, width: sd_config.webui.width,
height: sd_config.webui.height, height: sd_config.webui.height,
seed: sd_config.setting.seed, seed: sd_config.setting.seed,
init_images: path.normalize(path.join(this.global.config.project_path, "tmp/input_crop/" + name)), init_images: path.normalize(
path.join(this.global.config.project_path, 'tmp/input_crop/' + name)
)
} }
obj.adetailer = sd_config.webui.adetailer; obj.adetailer = sd_config.webui.adetailer
let file_path = path.join(this.global.config.project_path, "tmp/input_crop/" + name + '.json'); let file_path = path.join(
this.global.config.project_path,
'tmp/input_crop/' + name + '.json'
)
// 写入 // 写入
await fspromises.writeFile(file_path, JSON.stringify(obj)); await fspromises.writeFile(file_path, JSON.stringify(obj))
} }
return { return {
code: 1, code: 1,
data: path.join(this.global.config.project_path, "tmp/input_crop") data: path.join(this.global.config.project_path, 'tmp/input_crop')
} }
} catch (error) { } catch (error) {
return { return {
code: 0, code: 0,
@ -351,5 +392,4 @@ export class OriginalImageGenerate {
} }
} }
} }
} }

View File

@ -1,5 +1,7 @@
import { TagDefine } from "../../define/tagDefine"; import { TagDefine } from "../../define/tagDefine";
export class TagCustomize { export class TagCustomize {
global: any;
tagDefine: TagDefine;
constructor(global) { constructor(global) {
this.global = global; this.global = global;
this.tagDefine = new TagDefine(global); this.tagDefine = new TagDefine(global);

View File

@ -5,7 +5,7 @@ import { define } from "../../define/define";
let fspromises = require("fs").promises; let fspromises = require("fs").promises;
import { gptDefine } from "../../define/gptDefine"; import { gptDefine } from "../../define/gptDefine";
import { apiUrl } from "../../define/api/apiUrlDefine"; import { apiUrl } from "../../define/api/apiUrlDefine";
import { successMessage } from "../generalTools"; import { successMessage } from "../Public/generalTools";
export class GPT { export class GPT {
constructor(global) { constructor(global) {
@ -282,8 +282,7 @@ export class GPT {
url: gpt_url, url: gpt_url,
headers: { headers: {
'Authorization': `Bearer ${gpt_key}`, 'Authorization': `Bearer ${gpt_key}`,
'Content-Type': 'application/json', 'Content-Type': 'application/json'
"Accept": "application/json"
}, },
data: JSON.stringify(data) data: JSON.stringify(data)
}; };

View File

@ -1,58 +1,59 @@
import { errorMessage, successMessage } from "../generalTools"; import { errorMessage, successMessage } from './generalTools'
import path, { resolve } from "path"; import path from 'path'
import { Tools } from "../tools"; import { Tools } from '../tools'
import fs from "fs"; import fs from 'fs'
import { ImageSetting } from "../../define/setting/imageSetting"; import { ImageSetting } from '../../define/setting/imageSetting'
import { isEmpty } from "lodash"; import { isEmpty } from 'lodash'
import { basicApi } from "../../api/apiBasic"; import { basicApi } from '../../api/apiBasic'
import sharp from 'sharp'; import { file, image } from '../../define/Tools'
import { file, image } from "../../define/Tools"; import { define } from '../../define/define'
import { define } from "../../define/define";
import { spawn } from 'child_process' import { spawn } from 'child_process'
import { LOGGER_DEFINE } from "../../define/logger_define"; import { LOGGER_DEFINE } from '../../define/logger_define'
import { DEFINE_STRING } from "../../define/define_string"; import { DEFINE_STRING } from '../../define/define_string'
export class Image { export class Image {
constructor(global) { constructor(global) {
this.global = global; this.global = global
this.tools = new Tools(); this.tools = new Tools()
} }
// 将指定的文件夹复制到四个文件夹中 // 将指定的文件夹复制到四个文件夹中
async OneSplitFour(value) { async OneSplitFour(value) {
try { try {
value = JSON.parse(value); value = JSON.parse(value)
let count = value[1]; let count = value[1]
let data = value[0]; let data = value[0]
// 先创建输出文件 // 先创建输出文件
if (count <= 1) { if (count <= 1) {
throw new Error("可选择的图片的数量必须大于1"); throw new Error('可选择的图片的数量必须大于1')
} }
for (let i = 1; i < count; i++) { for (let i = 1; i < count; i++) {
let out_folder = path.join(this.global.config.project_path, `tmp/output_crop_0000${i + 1}`); let out_folder = path.join(this.global.config.project_path, `tmp/output_crop_0000${i + 1}`)
// 判断当前的文件夹是不是存在,存在删除 // 判断当前的文件夹是不是存在,存在删除
let isH = await this.tools.checkExists(out_folder); let isH = await this.tools.checkExists(out_folder)
if (isH) { if (isH) {
await this.tools.deleteFileOrDirectory(out_folder); await this.tools.deleteFileOrDirectory(out_folder)
} }
await this.tools.checkFolderExistsOrCreate(out_folder) await this.tools.checkFolderExistsOrCreate(out_folder)
} }
for (let i = 0; i < data.length; i++) { for (let i = 0; i < data.length; i++) {
const element = data[i]; const element = data[i]
let subImagePath = element.subImagePath; let subImagePath = element.subImagePath
for (let j = 1; j < count; j++) { for (let j = 1; j < count; j++) {
let out_file = path.join(this.global.config.project_path, `tmp/output_crop_0000${j + 1}/${element.name}`); let out_file = path.join(
if (subImagePath[j] && subImagePath[j].startsWith("file")) { this.global.config.project_path,
subImagePath[j] = subImagePath[j].replace("file://", ""); `tmp/output_crop_0000${j + 1}/${element.name}`
subImagePath[j] = subImagePath[j].replace(/\?time=.*$/, ''); )
if (subImagePath[j] && subImagePath[j].startsWith('file')) {
subImagePath[j] = subImagePath[j].replace('file://', '')
subImagePath[j] = subImagePath[j].replace(/\?time=.*$/, '')
} }
await this.tools.copyFileOrDirectory(subImagePath[j], out_file); await this.tools.copyFileOrDirectory(subImagePath[j], out_file)
} }
} }
return successMessage("拆分成功"); return successMessage('拆分成功')
} catch (error) { } catch (error) {
return errorMessage(error.message); return errorMessage(error.message)
} }
} }
@ -63,23 +64,23 @@ export class Image {
*/ */
async Base64ToFile(value) { async Base64ToFile(value) {
try { try {
value = JSON.parse(value); value = JSON.parse(value)
let base64 = value[0]; let base64 = value[0]
let out_file_str = value[1]; let out_file_str = value[1]
let base64Data = base64.replace(/^data:image\/\w+;base64,/, ""); let base64Data = base64.replace(/^data:image\/\w+;base64,/, '')
let dataBuffer = Buffer.from(base64Data, 'base64'); let dataBuffer = Buffer.from(base64Data, 'base64')
let out_file = path.join(this.global.config.project_path, out_file_str); let out_file = path.join(this.global.config.project_path, out_file_str)
let out_folder = path.dirname(out_file); let out_folder = path.dirname(out_file)
await this.tools.checkFolderExistsOrCreate(out_folder); await this.tools.checkFolderExistsOrCreate(out_folder)
await fs.promises.writeFile(out_file, dataBuffer)
await fs.promises.writeFile(out_file, dataBuffer);
// await this.tools.writeArrayToFile(dataBuffer, out_file); // await this.tools.writeArrayToFile(dataBuffer, out_file);
return successMessage(out_file, "base64保存到本地图片成功"); return successMessage(out_file, 'base64保存到本地图片成功')
} catch (error) { } catch (error) {
return errorMessage("base64保存到本地图片失败失败原因如下" + error.message); return errorMessage('base64保存到本地图片失败失败原因如下' + error.message)
} }
} }
// TODO 这个方法后面还要改,现在有问题(直接重写,后面这个重构掉,先重写一个)
/** /**
* 图片处理去除水印 * 图片处理去除水印
* @param {*} value * @param {*} value
@ -88,103 +89,118 @@ export class Image {
async ProcessImage(value) { async ProcessImage(value) {
return new Promise(async (resolve, reject) => { return new Promise(async (resolve, reject) => {
try { try {
value = JSON.parse(value); value = JSON.parse(value)
value.out_file = value.out_file ? value.out_file : `data/mask/temp/${new Date().getTime()}.png`; value.out_file = value.out_file
? value.out_file
: `data/mask/temp/${new Date().getTime()}.png`
// 判断当前使用的是什么 // 判断当前使用的是什么
let mask_setting = (await ImageSetting.GetDefineConfigJsonByProperty(JSON.stringify(["img_base", "mask_setting", false, {}]))).data; let mask_setting = (
let isRemote = mask_setting.isRemote ? mask_setting.isRemote : false; await ImageSetting.GetDefineConfigJsonByProperty(
let urls = mask_setting.localUrl ? mask_setting.localUrl + "api/v1/inpaint" : ""; JSON.stringify(['img_base', 'mask_setting', false, {}])
)
).data
let isRemote = mask_setting.isRemote ? mask_setting.isRemote : false
let urls = mask_setting.localUrl ? mask_setting.localUrl + 'api/v1/inpaint' : ''
if (isRemote && isEmpty(urls)) { if (isRemote && isEmpty(urls)) {
throw new Error("使用iopaint图片处理但是没有配置图片处理地址"); throw new Error('使用iopaint图片处理但是没有配置图片处理地址')
} }
if (!isRemote && isEmpty(value.out_file)) { if (!isRemote && isEmpty(value.out_file)) {
throw new Error("水印处理使用软件直接处理类型为file需要指定输出的文件地址"); throw new Error('水印处理使用软件直接处理类型为file需要指定输出的文件地址')
} }
let out_file; let out_file
if (!isEmpty(value.out_file)) { if (!isEmpty(value.out_file)) {
out_file = path.join(this.global.config.project_path, value.out_file); out_file = path.join(this.global.config.project_path, value.out_file)
let out_folder = path.dirname(out_file); let out_folder = path.dirname(out_file)
await this.tools.checkFolderExistsOrCreate(out_folder); await this.tools.checkFolderExistsOrCreate(out_folder)
} }
let res; let res
if (isRemote) { if (isRemote) {
let headers = { let headers = {
"accept": '*/*', accept: '*/*',
'accept-language': 'zh-CN,zh;q=0.9', 'accept-language': 'zh-CN,zh;q=0.9',
'content-type': 'application/json' 'content-type': 'application/json'
} }
let data = { let data = {
"image": value.image, image: value.image,
"mask": value.mask, mask: value.mask,
"ldm_steps": 30, ldm_steps: 30,
"ldm_sampler": "ddim", ldm_sampler: 'ddim',
"zits_wireframe": true, zits_wireframe: true,
"cv2_flag": "INPAINT_NS", cv2_flag: 'INPAINT_NS',
"cv2_radius": 5, cv2_radius: 5,
"hd_strategy": "Crop", hd_strategy: 'Crop',
"hd_strategy_crop_triger_size": 640, hd_strategy_crop_triger_size: 640,
"hd_strategy_crop_margin": 128, hd_strategy_crop_margin: 128,
"hd_trategy_resize_imit": 2048 * 5, hd_trategy_resize_imit: 2048 * 5,
"prompt": "", prompt: '',
"negative_prompt": "out of frame, lowres, error, cropped, worst quality, low quality, jpeg artifacts, ugly, duplicate, morbid, mutilated, out of frame, mutation, deformed, blurry, dehydrated, bad anatomy, bad proportions, extra limbs, disfigured, gross proportions, malformed limbs, watermark, signature", negative_prompt:
"use_croper": false, 'out of frame, lowres, error, cropped, worst quality, low quality, jpeg artifacts, ugly, duplicate, morbid, mutilated, out of frame, mutation, deformed, blurry, dehydrated, bad anatomy, bad proportions, extra limbs, disfigured, gross proportions, malformed limbs, watermark, signature',
"croper_x": 284, use_croper: false,
"croper_y": 284, croper_x: 284,
"croper_height": 512, croper_y: 284,
"croper_width": 512, croper_height: 512,
"use_extender": false, croper_width: 512,
"extender_x": 0, use_extender: false,
"extender_y": 0, extender_x: 0,
"extender_height": 1080, extender_y: 0,
"extender_width": 1080, extender_height: 1080,
"sd_mask_blur": 12, extender_width: 1080,
"sd_strength": 1, sd_mask_blur: 12,
"sd_steps": 50, sd_strength: 1,
"sd_guidance_scale": 7.5, sd_steps: 50,
"sd_sampler": "DPM++ 2M", sd_guidance_scale: 7.5,
"sd_seed": -1, sd_sampler: 'DPM++ 2M',
"sd_match_histograms": false, sd_seed: -1,
"sd_lcm_lora": false, sd_match_histograms: false,
"paint_by_example_example_image": null, sd_lcm_lora: false,
"p2p_image_guidance_scale": 1.5, paint_by_example_example_image: null,
"enable_controlnet": false, p2p_image_guidance_scale: 1.5,
"controlnet_conditioning_scale": 0.4, enable_controlnet: false,
"controlnet_method": "", controlnet_conditioning_scale: 0.4,
"enable_brushnet": false, controlnet_method: '',
"brushnet_method": "random_mask", enable_brushnet: false,
"brushnet_conditioning_scale": 1, brushnet_method: 'random_mask',
"enable_powerpaint_v2": false, brushnet_conditioning_scale: 1,
"powerpaint_task": "text-guided" enable_powerpaint_v2: false,
}; powerpaint_task: 'text-guided'
res = await basicApi.post(urls, data, headers); }
this.global.logger.info(LOGGER_DEFINE.REMOVE_WATERMARK, `iopaint去除水印请求成功`); res = await basicApi.post(urls, data, headers)
this.global.logger.info(LOGGER_DEFINE.REMOVE_WATERMARK, `iopaint去除水印请求成功`)
if (value.type == 'arrayBuffer') { if (value.type == 'arrayBuffer') {
resolve(successMessage(res.data, "图片处理成功", LOGGER_DEFINE.REMOVE_WATERMARK)); resolve(successMessage(res.data, '图片处理成功', LOGGER_DEFINE.REMOVE_WATERMARK))
} else if (value.type == "file") { } else if (value.type == 'file') {
let buffer = Buffer.from(res.data); let buffer = Buffer.from(res.data)
await fs.promises.writeFile(out_file, buffer) await fs.promises.writeFile(out_file, buffer)
resolve(successMessage(out_file, "图片处理成功", LOGGER_DEFINE.REMOVE_WATERMARK)); resolve(successMessage(out_file, '图片处理成功', LOGGER_DEFINE.REMOVE_WATERMARK))
} }
} else { } else {
let lama_script = path.resolve(define.scripts_path, `lama/lama_inpaint.exe`); let lama_script = path.resolve(define.scripts_path, `lama/lama_inpaint.exe`)
// 就是判断指定的文件和文件夹是不是存在 // 就是判断指定的文件和文件夹是不是存在
let has_exe = await file.CheckFileOrDirExist(lama_script); let has_exe = await file.CheckFileOrDirExist(lama_script)
if (!has_exe) { if (!has_exe) {
throw new Error("图片水印处理组件不存在,请看教程自行下载"); throw new Error('图片水印处理组件不存在,请看教程自行下载')
} }
let has_model = await file.CheckFileOrDirExist(path.resolve(define.scripts_path, 'lama/model/big-lama.pt')) let has_model = await file.CheckFileOrDirExist(
path.resolve(define.scripts_path, 'lama/model/big-lama.pt')
)
if (!has_model) { if (!has_model) {
throw new Error("图片水印处理的模型不存在,请看教程自行下载") throw new Error('图片水印处理的模型不存在,请看教程自行下载')
} }
this.global.logger.info(LOGGER_DEFINE.REMOVE_WATERMARK, `开始使用lama去除水印开始调用lama程序`); this.global.logger.info(
LOGGER_DEFINE.REMOVE_WATERMARK,
`开始使用lama去除水印开始调用lama程序`
)
// 先将对应的base64文件写道本地 // 先将对应的base64文件写道本地
let image_path = await this.Base64ToFile(JSON.stringify([value.image, `data/mask/temp/${new Date().getTime()}.png`])) let image_path = await this.Base64ToFile(
let mask_path = await this.Base64ToFile(JSON.stringify([value.mask, `data/mask/mask_temp_${new Date().getTime()}.png`])) JSON.stringify([value.image, `data/mask/temp/${new Date().getTime()}.png`])
)
let mask_path = await this.Base64ToFile(
JSON.stringify([value.mask, `data/mask/mask_temp_${new Date().getTime()}.png`])
)
if (image_path.code == 0) { if (image_path.code == 0) {
throw new Error(image_path.message) throw new Error(image_path.message)
} }
@ -192,7 +208,9 @@ export class Image {
throw new Error(mask_path.message) throw new Error(mask_path.message)
} }
let child = spawn(lama_script, ['-l', image_path.data, mask_path.data, out_file], { encoding: 'utf-8' }); let child = spawn(lama_script, ['-l', image_path.data, mask_path.data, out_file], {
encoding: 'utf-8'
})
// let child = spawn('python', [lama_script, '-l', image_path.data, mask_path.data, out_file], { encoding: 'utf-8' }); // let child = spawn('python', [lama_script, '-l', image_path.data, mask_path.data, out_file], { encoding: 'utf-8' });
child.on('error', (error) => { child.on('error', (error) => {
reject(error.toString()) reject(error.toString())
@ -210,126 +228,151 @@ export class Image {
child.on('close', async (data) => { child.on('close', async (data) => {
if (data != 0) { if (data != 0) {
this.global.logger.error(LOGGER_DEFINE.REMOVE_WATERMARK, `lama去除水印失败错误码${data.toString()}`); this.global.logger.error(
reject("lama去除水印错误。请看日志详细信息") LOGGER_DEFINE.REMOVE_WATERMARK,
`lama去除水印失败错误码${data.toString()}`
)
reject('lama去除水印错误。请看日志详细信息')
return return
} }
// 判断是不是有输出文件 // 判断是不是有输出文件
let has_out = await file.CheckFileOrDirExist(out_file); let has_out = await file.CheckFileOrDirExist(out_file)
if (!has_out) { if (!has_out) {
reject("lama去除水印失败没有输出文件") reject('lama去除水印失败没有输出文件')
return return
} }
if (value.type == 'arrayBuffer') { if (value.type == 'arrayBuffer') {
// 读取导出的文件 // 读取导出的文件
let res_data = await fs.promises.readFile(out_file); let res_data = await fs.promises.readFile(out_file)
resolve(successMessage(res_data, "图片处理成功", LOGGER_DEFINE.REMOVE_WATERMARK)); resolve(successMessage(res_data, '图片处理成功', LOGGER_DEFINE.REMOVE_WATERMARK))
} else if (value.type == "file") { } else if (value.type == 'file') {
resolve(successMessage(out_file, "图片处理成功", LOGGER_DEFINE.REMOVE_WATERMARK)); resolve(successMessage(out_file, '图片处理成功', LOGGER_DEFINE.REMOVE_WATERMARK))
} }
}) })
} }
} catch (error) { } catch (error) {
reject("图片处理失败,失败原因如下:" + error.message) reject('图片处理失败,失败原因如下:' + error.message)
} }
}) })
} }
// TODO 该方法后面需删除使用ProcessImage方法
// 批量处理所有的图片,去除水印 // 批量处理所有的图片,去除水印
async BatchProcessImage(value) { async BatchProcessImage(value) {
try { try {
let input_folder = value; let input_folder = value
input_folder = path.resolve(this.global.config.project_path, input_folder) input_folder = path.resolve(this.global.config.project_path, input_folder)
if (!(await this.tools.checkExists(input_folder))) { if (!(await this.tools.checkExists(input_folder))) {
throw new Error("输入的文件夹不存在"); throw new Error('输入的文件夹不存在')
} }
let new_input_folder = path.join(this.global.config.project_path, `tmp/bak/${path.basename(input_folder)}`); let new_input_folder = path.join(
this.global.config.project_path,
`tmp/bak/${path.basename(input_folder)}`
)
// 在备份之前判断,旧的文件是不是存在,里面是不是有图片,没有图片,直接删除,在备份过去,存在直接开始下面的请求 // 在备份之前判断,旧的文件是不是存在,里面是不是有图片,没有图片,直接删除,在备份过去,存在直接开始下面的请求
let has_files = await file.CheckFileOrDirExist(new_input_folder); let has_files = await file.CheckFileOrDirExist(new_input_folder)
if (has_files) { if (has_files) {
let files = await file.GetFilesWithExtensions(new_input_folder, ['.png']); let files = await file.GetFilesWithExtensions(new_input_folder, ['.png'])
if (files.length <= 0) { if (files.length <= 0) {
// 删除指定的文件夹 // 删除指定的文件夹
await fs.promises.rm(new_input_folder, { recursive: true }); await fs.promises.rm(new_input_folder, { recursive: true })
await file.BackupFileOrFolder(input_folder, new_input_folder); await file.BackupFileOrFolder(input_folder, new_input_folder)
// 创建新的input_folder // 创建新的input_folder
await fs.promises.mkdir(input_folder, { recursive: true }) await fs.promises.mkdir(input_folder, { recursive: true })
} }
} else { } else {
await file.BackupFileOrFolder(input_folder, new_input_folder); await file.BackupFileOrFolder(input_folder, new_input_folder)
// 创建新的input_folder // 创建新的input_folder
await fs.promises.mkdir(input_folder, { recursive: true }) await fs.promises.mkdir(input_folder, { recursive: true })
} }
// 开始备份 // 开始备份
// 获取蒙板 // 获取蒙板
let mask_setting = (await ImageSetting.GetDefineConfigJsonByProperty(JSON.stringify(["img_base", "mask_setting", false, {}]))).data; let mask_setting = (
await ImageSetting.GetDefineConfigJsonByProperty(
JSON.stringify(['img_base', 'mask_setting', false, {}])
)
).data
if (mask_setting.isRemote && isEmpty(mask_setting.localUrl)) { if (mask_setting.isRemote && isEmpty(mask_setting.localUrl)) {
throw new Error("使用iopaint图片处理但是没有配置图片处理地址"); throw new Error('使用iopaint图片处理但是没有配置图片处理地址')
} }
if (isEmpty(mask_setting.mask_path)) { if (isEmpty(mask_setting.mask_path)) {
throw new Error("没有配置蒙板的路径"); throw new Error('没有配置蒙板的路径')
} }
// 获取文件夹里面所有的图片文件 // 获取文件夹里面所有的图片文件
// let png_files = await this.tools.getFilesWithExtensions(input_folder, '.png'); // let png_files = await this.tools.getFilesWithExtensions(input_folder, '.png');
let png_files = await file.GetFilesWithExtensions(new_input_folder, ['.png']); let png_files = await file.GetFilesWithExtensions(new_input_folder, ['.png'])
if (png_files.length == 0) { if (png_files.length == 0) {
throw new Error("没有找到任何的抽帧图片文件"); throw new Error('没有找到任何的抽帧图片文件')
} }
// 获取图片的总数,将数据返回前端,更新进度条 // 获取图片的总数,将数据返回前端,更新进度条
this.global.newWindow[0].win.webContents.send(DEFINE_STRING.IMG.BATCH_PROCESS_IMAGE_RESULT, successMessage({ this.global.newWindow[0].win.webContents.send(
DEFINE_STRING.IMG.BATCH_PROCESS_IMAGE_RESULT,
successMessage({
total: png_files.length, total: png_files.length,
current: 0 current: 0
})) })
)
// 默认所有的的宽高都是一样的,获取第一张图片的宽高 // 默认所有的的宽高都是一样的,获取第一张图片的宽高
let first_image = png_files[0]; let first_image = png_files[0]
let first_image_size = await image.GetImageSize(first_image); let first_image_size = await image.GetImageSize(first_image)
// 重新设置蒙板的宽高,和图片的宽高一样 // 重新设置蒙板的宽高,和图片的宽高一样
let mask_base = await image.ResizeImage(mask_setting.mask_path, first_image_size.width, first_image_size.height, 'base64'); let mask_base = await image.ResizeImage(
mask_base = `data:image/png;base64,${mask_base}`; mask_setting.mask_path,
first_image_size.width,
first_image_size.height,
'base64'
)
mask_base = `data:image/png;base64,${mask_base}`
// 开始处理所有的图片 // 开始处理所有的图片
for (let i = 0; i < png_files.length; i++) { for (let i = 0; i < png_files.length; i++) {
const element = png_files[i]; const element = png_files[i]
// 获取指定的图片并且转换为base64 // 获取指定的图片并且转换为base64
let image_base = await fs.promises.readFile(element); let image_base = await fs.promises.readFile(element)
image_base = image_base.toString('base64'); image_base = image_base.toString('base64')
image_base = `data:image/png;base64,${image_base}`; image_base = `data:image/png;base64,${image_base}`
// 开始处理图片 // 开始处理图片
let res = await this.ProcessImage(JSON.stringify({ let res = await this.ProcessImage(
JSON.stringify({
image: image_base, image: image_base,
mask: mask_base, mask: mask_base,
type: "file", type: 'file',
out_file: `tmp/input_crop/${path.basename(element)}` out_file: `tmp/input_crop/${path.basename(element)}`
})) })
)
if (res.code == 0) { if (res.code == 0) {
throw new Error(res.message); throw new Error(res.message)
} }
// 删除之前的 // 删除之前的
await this.tools.deleteFileOrDirectory(element) await this.tools.deleteFileOrDirectory(element)
await this.tools.delay(1000) await this.tools.delay(1000)
// 当前图片处理成功,将信息返回前端,更新进度条 // 当前图片处理成功,将信息返回前端,更新进度条
this.global.newWindow[0].win.webContents.send(DEFINE_STRING.IMG.BATCH_PROCESS_IMAGE_RESULT, successMessage({ this.global.newWindow[0].win.webContents.send(
DEFINE_STRING.IMG.BATCH_PROCESS_IMAGE_RESULT,
successMessage({
total: png_files.length, total: png_files.length,
current: i + 1 current: i + 1
})) })
)
} }
return successMessage("所有的图片处理成功", LOGGER_DEFINE.REMOVE_WATERMARK); return successMessage('所有的图片处理成功', LOGGER_DEFINE.REMOVE_WATERMARK)
} catch (error) { } catch (error) {
return errorMessage("图片处理失败,失败原因如下:" + error.message, LOGGER_DEFINE.REMOVE_WATERMARK); return errorMessage(
'图片处理失败,失败原因如下:' + error.message,
LOGGER_DEFINE.REMOVE_WATERMARK
)
} }
} }
} }

View File

@ -1,4 +1,4 @@
import { errorMessage, successMessage } from "../generalTools"; import { errorMessage, successMessage } from "../Public/generalTools";
import { LOGGER_DEFINE } from '../../define/logger_define' import { LOGGER_DEFINE } from '../../define/logger_define'
import { Setting } from "../setting/setting"; import { Setting } from "../setting/setting";
import { isEmpty } from "lodash"; import { isEmpty } from "lodash";

View File

@ -9,7 +9,7 @@ const sharp = require('sharp');
import { SdSettingDefine } from "../../define/setting/sdSettingDefine"; import { SdSettingDefine } from "../../define/setting/sdSettingDefine";
import { PublicMethod } from "./publicMethod"; import { PublicMethod } from "./publicMethod";
import { Tools } from "../tools"; import { Tools } from "../tools";
import { errorMessage, successMessage } from "../generalTools"; import { errorMessage, successMessage } from "../Public/generalTools";
import { SdApi } from "../../api/sdApi"; import { SdApi } from "../../api/sdApi";
const { v4: uuidv4 } = require('uuid'); const { v4: uuidv4 } = require('uuid');

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,69 @@
// import jieba from 'jieba-js';
// import natural from 'natural';
// // 示例中文文本数组
// const texts: string[] = [
// "这是一个示例文本。",
// "这是一个示例文本。",
// "这是一个不同的文本。",
// "另一个文本示例。",
// "又一个不同的文本。"
// ];
// // 删除完全重复的文本
// const uniqueTexts = Array.from(new Set(texts));
// // 定义相似度阈值,通常 0.8 是一个常用的阈值,表示 80% 的相似度
// const SIMILARITY_THRESHOLD = 0.8;
// // 分词函数
// async function tokenize(text: string): Promise<string[]> {
// return await jieba.cut(text);
// }
// // 计算两个文本的余弦相似度
// async function cosineSimilarity(text1: string, text2: string): Promise<number> {
// const tfidf = new natural.TfIdf();
// tfidf.addDocument((await tokenize(text1)).join(' '));
// tfidf.addDocument((await tokenize(text2)).join(' '));
// const vector1 = tfidf.documents[0] as Record<string, number>;
// const vector2 = tfidf.documents[1] as Record<string, number>;
// const allTerms = new Set<string>([...Object.keys(vector1), ...Object.keys(vector2)]);
// const vector1Array = Array.from(allTerms).map(term => vector1[term] || 0);
// const vector2Array = Array.from(allTerms).map(term => vector2[term] || 0);
// const dotProduct = vector1Array.reduce((sum, value, index) => sum + value * vector2Array[index], 0);
// const magnitude1 = Math.sqrt(vector1Array.reduce((sum, value) => sum + value * value, 0));
// const magnitude2 = Math.sqrt(vector2Array.reduce((sum, value) => sum + value * value, 0));
// return dotProduct / (magnitude1 * magnitude2);
// }
// // 检查相似度并去重
// export async function RemoveSimilarTexts(texts: string[]): Promise<string[]> {
// const result: string[] = [];
// for (let i = 0; i < texts.length; i++) {
// let isSimilar = false;
// for (let j = 0; j < result.length; j++) {
// const similarity = await cosineSimilarity(texts[i], result[j]);
// if (similarity >= SIMILARITY_THRESHOLD) {
// isSimilar = true;
// break;
// }
// }
// if (!isSimilar) {
// result.push(texts[i]);
// }
// }
// return result;
// }
// // const finalTexts = await removeSimilarTexts(uniqueTexts);
// // console.log("原始文本:", texts);
// // console.log("唯一文本:", uniqueTexts);
// // console.log("去除相似后的最终文本:", finalTexts);

View File

@ -11,7 +11,7 @@ import { cloneDeep } from "lodash";
const compressing = require("compressing"); const compressing = require("compressing");
export class ClipDraft { export class ClipDraft {
constructor(global, value) { constructor(global, value, draftName = null) {
this.speedId = null; this.speedId = null;
this.canvasesId = null; this.canvasesId = null;
this.soundChannelId = null; this.soundChannelId = null;
@ -33,10 +33,11 @@ export class ClipDraft {
this.global = global; this.global = global;
this.value = value; this.value = value;
this.pm = new PublicMethod(global); this.pm = new PublicMethod(global);
this.draft_name = draftName;
} }
async InitData() { async InitData() {
this.draft_name = this.global.config.project_name + '_' + this.value[0]; this.draft_name = this.draft_name ? this.draft_name : this.global.config.project_name + '_' + this.value[0];
let draft_path = path.join(this.global.config.draft_path, this.draft_name); let draft_path = path.join(this.global.config.draft_path, this.draft_name);
await fspromises.rm(draft_path, { recursive: true, force: true }); await fspromises.rm(draft_path, { recursive: true, force: true });
await compressing.zip.uncompress(define.draft_temp_path, path.join(this.global.config.draft_path, this.global.config.project_name + '_' + this.value[0])); await compressing.zip.uncompress(define.draft_temp_path, path.join(this.global.config.draft_path, this.global.config.project_name + '_' + this.value[0]));
@ -124,9 +125,6 @@ export class ClipDraft {
materialVideoTmpJson.id = materialId; materialVideoTmpJson.id = materialId;
// 获取输入的图片宽高 // 获取输入的图片宽高
// let image = await Jimp.read(imagePath);
// let width = image.bitmap.width;
// let height = image.bitmap.height;
materialVideoTmpJson.width = 1000; materialVideoTmpJson.width = 1000;
materialVideoTmpJson.height = 1000; materialVideoTmpJson.height = 1000;

View File

@ -0,0 +1,114 @@
import { GeneralResponse } from '../../model/generalResponse'
/**
* nullundefined
* @param {*} value
* @param {*} suffix
* @returns
*/
function checkStringValueAddSuffix(value: string, suffix: string): string {
if (value && value !== null && value !== undefined && value !== '') {
return value + suffix
} else {
return ''
}
}
/**
* nullundefined
* @param {*} value
* @param {*} prefix
* @returns
*/
function checkStringValueAddPrefix(value: string, prefix: string): string {
if (value && value !== null && value !== undefined && value !== '') {
return prefix + value
} else {
return ''
}
}
/**
*
* @param {*} value
* @param {*} suffix
* @returns
*/
function checkStringValueDeleteSuffix(value: string, suffix: string): string {
// 增加一个判断,当前删除的数量是不是大于字符串的长度
if (value && value !== null && value !== undefined && value !== '') {
if (suffix.length > value.length) {
return ''
} else {
return value.slice(0, value.length - suffix.length)
}
} else {
return ''
}
}
/**
*
* @param {*} value
* @param {*} prefix
* @returns
*/
function checkStringValueDeletePrefix(value: string, prefix: string): string {
// 增加一个判断,当前删除的数量是不是大于字符串的长度
if (value && value !== null && value !== undefined && value !== '') {
if (prefix.length > value.length) {
return ''
} else {
return value.slice(prefix.length)
}
} else {
return ''
}
}
/**
* codedatamessage
* @param {*} data
* @param {*} message
* @param {*} service
* @returns
*/
function successMessage(
data?: any,
message?: string,
service?: string
): GeneralResponse.SuccessItem {
if (service) {
global.logger.info(service, message ? message : '成功返回数据')
}
return {
code: 1,
data: data,
message: message
}
}
/**
*
* @param {*} message
* @param {*} service
* @returns
*/
function errorMessage(message: string, service?: string): GeneralResponse.ErrorItem {
if (service) {
global.logger.error(service, message ? message : '未知报错,没有捕获的错误')
}
return {
code: 0,
message: message
}
}
export {
checkStringValueAddSuffix,
checkStringValueAddPrefix,
checkStringValueDeletePrefix,
checkStringValueDeleteSuffix,
successMessage,
errorMessage
}

View File

@ -1,56 +0,0 @@
import { BookType } from '../../../define/enum/bookEnum'
import { errorMessage, successMessage } from '../../generalTools'
import { BookService } from '../../../define/db/service/Book/bookService'
import { BookTaskService } from '../../../define/db/service/Book/bookTaskService'
const { v4: uuidv4 } = require('uuid')
import { define } from '../../../define/define'
import path from 'path'
import { CheckFolderExistsOrCreate } from '../../../define/Tools/file'
export class BookBasic {
constructor() {}
/**
* 新增或者是修小说数据
* @param {*} book 小说信息
* @returns
*/
async AddOrModifyBook(book) {
try {
if (book == null) {
return errorMessage('小说数据为空,无法修改')
}
// 处理一下数据,处理文件地址(删除前缀,转换为默认地址)
// 当前的小说的名字是不是在数据库中以存在
let _bookService = await BookService.getInstance()
let res = await _bookService.AddOrModifyBook(book)
return res
} catch (error) {
return errorMessage(
'修改数据错误,错误信息如下:' + error.message,
'BookBasic_AddOrModifyBook'
)
}
}
// 小说类型返回
GetBookType() {
return successMessage(
[
{
label: 'SD反推',
value: BookType.SD_REVERSE
},
{
label: 'MJ反推',
value: BookType.MJ_REVERSE
},
{
label: '原创',
value: BookType.ORIGINAL
}
],
'获取小说类型成功'
)
}
}

View File

@ -1,120 +0,0 @@
import { successMessage, errorMessage } from '../../generalTools.js'
import { BookBasic } from './BooKBasic.js'
import { BookService } from '../../../define/db/service/Book/bookService'
import { BookTaskService } from '../../../define/db/service/Book/bookTaskService'
import { define } from '../../../define/define.js'
import path from 'path'
import { BasicReverse } from '../../Task/basicReverse.js'
/**
* 一键反推的相关操作
*/
export class ReverseBook extends BookBasic {
constructor() {
super()
this.basicReverse = new BasicReverse()
}
//#region 小说相关操作
/**
* 获取当前的小说数据
* @param {*} bookQuery
*/
async GetBookData(bookQuery) {
try {
let _bookService = await BookService.getInstance()
// 添加小说
let res = _bookService.GetBookData(bookQuery)
if (res.code == 0) {
throw new Error(res.message)
}
return res
} catch (error) {
return errorMessage(error.message, 'ReverseBook_GetBookData')
}
}
//#endregion
//#region 小说批次任务相关操作
/**
* 获取小说的任务列表
* @param {*} bookTaskCondition 查询任务列表的条件
*/
async GetBookTaskData(bookTaskCondition) {
try {
let _bookTaskService = await BookTaskService.getInstance()
let res = await _bookTaskService.GetBookTaskData(bookTaskCondition)
if (res.code == 0) {
throw new Error(res.message)
}
return res
} catch (error) {
return errorMessage(
'获取小说对应批次错误,错误信息入校:' + error.message,
'ReverseBook_GetBookTaskData'
)
}
}
//#endregion
//#region 一键全自动
/**
* 全自动任务这边是任务入口都是在这边调用
* @param {*} value
* @returns
*/
async AutoAction(bookId) {
try {
// 在一键全自动之前当前小说对应的批次任务中的所有的子任务都改为fail然后再执行
// 获取对应的小说小说数据,找到对应的小说视频地址
// let _bookService = await BookService.getInstance()
// let _bookTaskService = await BookTaskService.getInstance()
// let bookData = _bookService.GetBookDataById(bookId)
// if (bookData.data == null) {
// throw new Error('没有找到对应的小说数据请检查bookId是否正确')
// }
// // 获取小说对应的批次任务数据,默认初始化为第一个
// let bookTaskRes = _bookTaskService.GetBookTaskData({
// bookId: bookId,
// name: 'output_00001'
// })
// if (bookTaskRes.data.bookTasks.length <= 0 || bookTaskRes.data.total <= 0) {
// throw new Error('没有找到对应的小说批次任务数据请检查bookId是否正确')
// }
// // 获取小说的视频地址
// let book = bookData.data
// let bookTask = bookTaskRes.data.bookTasks[0]
// // 将当前小说对应的批次任务中的所有的子任务都改为fail
// let updateTaskRes = _bookTaskService.UpdetedBookTaskToFail(bookId, bookTask.id)
// // 添加分镜任务 后面就会全自动的开始执行
// let res = await this.basicReverse.AddFrameDataTask(bookId)
// 添加分割视频任务
// let res = await this.basicReverse.AddCutVideoDataTask(bookId)
// 添加音频分离任务
// let res = await this.basicReverse.AddSplitAudioDataTask(bookId)
// 添加图片抽帧任务
let res = await this.basicReverse.AddGetFrameTask(bookId)
if (res.code == 0) {
throw new Error(res.message)
}
} catch (error) {
return errorMessage(error.message, 'ReverseBook_AutoAction')
}
}
//#endregion
}

View File

@ -13,7 +13,7 @@ const execAsync = util.promisify(exec);
const { v4: uuidv4 } = require('uuid'); // 引入UUID库来生成唯一标识符 const { v4: uuidv4 } = require('uuid'); // 引入UUID库来生成唯一标识符
let fspromises = require("fs").promises; let fspromises = require("fs").promises;
import { ImageSetting } from "../../define/setting/imageSetting"; import { ImageSetting } from "../../define/setting/imageSetting";
import { errorMessage } from "../generalTools"; import { errorMessage } from "../Public/generalTools";
import { LOGGER_DEFINE } from "../../define/logger_define"; import { LOGGER_DEFINE } from "../../define/logger_define";

View File

@ -0,0 +1,283 @@
import { BookType, OperateBookType, TagDefineType } from '../../../define/enum/bookEnum'
import { errorMessage, successMessage } from '../../Public/generalTools'
import { BookService } from '../../../define/db/service/Book/bookService'
import { BookTaskService } from '../../../define/db/service/Book/bookTaskService'
import { BookTaskDetailService } from '../../../define/db/service/Book/bookTaskDetailService'
import { CopyImageType } from '../../../define/enum/bookEnum'
const { v4: uuidv4 } = require('uuid')
import { define } from '../../../define/define'
import path from 'path'
import { CheckFileOrDirExist, CheckFolderExistsOrCreate, CopyFileOrFolder } from '../../../define/Tools/file'
import { Book } from '../../../model/book'
import { GeneralResponse } from '../../../model/generalResponse'
import { cloneDeep, isEmpty } from 'lodash'
export class BookBasic {
constructor() { }
bookTaskService: BookTaskService
bookTaskDetailService: BookTaskDetailService
async InitService() {
if (!this.bookTaskService) {
this.bookTaskService = await BookTaskService.getInstance()
}
if (!this.bookTaskDetailService) {
this.bookTaskDetailService = await BookTaskDetailService.getInstance()
}
}
//#region 小说相关操作
/**
*
* @param {*} book
* @returns
*/
async AddOrModifyBook(book) {
try {
if (book == null) {
return errorMessage('小说数据为空,无法修改')
}
// 处理一下数据,处理文件地址(删除前缀,转换为默认地址)
// 当前的小说的名字是不是在数据库中以存在
let _bookService = await BookService.getInstance()
let res = await _bookService.AddOrModifyBook(book)
return res
} catch (error) {
return errorMessage(
'修改数据错误,错误信息如下:' + error.message,
'BookBasic_AddOrModifyBook'
)
}
}
// 小说类型返回
GetBookType() {
return successMessage(
[
{
label: 'SD反推',
value: BookType.SD_REVERSE
},
{
label: 'MJ反推',
value: BookType.MJ_REVERSE
},
{
label: '原创',
value: BookType.ORIGINAL
}
],
'获取小说类型成功'
)
}
//#endregion
//#region 小说批次任务相关操作
async OneToFourBookTask(bookTaskId: string) {
try {
console.log(bookTaskId)
await this.InitService();
let copyCount = 100
let bookTask = this.bookTaskService.GetBookTaskDataById(bookTaskId)
if (bookTask == null) {
throw new Error("没有找到对应的数小说任务,请检查数据")
}
// 获取所有的出图中最少的
let bookTaskDetail = this.bookTaskDetailService.GetBookTaskData({
bookTaskId: bookTaskId
}).data as Book.SelectBookTaskDetail[]
if (bookTaskDetail == null || bookTaskDetail.length <= 0) {
throw new Error("没有对应的小说分镜任务,请先添加分镜任务")
}
for (let i = 0; i < bookTaskDetail.length; i++) {
const element = bookTaskDetail[i];
if (isEmpty(element.subImagePath)) {
throw new Error("检测到图片没有出完,请先检查出图")
}
if (element.subImagePath == null || element.subImagePath.length <= 0) {
throw new Error("检测到图片没有出完,请先检查出图")
}
if (element.subImagePath.length < copyCount) {
copyCount = element.subImagePath.length
}
}
if (copyCount <= 0) {
throw new Error("批次设置错误,无法进行一拆四")
}
// 开始复制
let res = await this.CopyNewBookTask(bookTask, bookTaskDetail, copyCount - 1, CopyImageType.ONE)
if (res.code == 0) {
throw new Error(res.message)
}
return successMessage(res.data, "一拆四成功", "BookBasic_OneToFourBookTask")
} catch (error) {
return errorMessage("一拆四失败,失败信息如下:" + error.message, "BookBasic_OneToFourBookTask")
}
}
/**
*
* @param oldBookTaskId
* @param copyCount
* @param isCopyImage
*/
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 name = 'output_0000' + no
let imageFolder = path.join(define.project_path, `${sourceBookTask.bookId}/tmp/${name}`)
await CheckFolderExistsOrCreate(imageFolder)
// 创建对应的文件夹
let addOneBookTask = {
id: uuidv4(),
bookId: sourceBookTask.bookId,
no: no,
name: name,
generateVideoPath: sourceBookTask.generateVideoPath,
srtPath: sourceBookTask.srtPath,
audioPath: sourceBookTask.audioPath,
draftSrtStyle: sourceBookTask.draftSrtStyle,
backgroundMusic: sourceBookTask.backgroundMusic,
friendlyReminder: sourceBookTask.friendlyReminder,
imageFolder: path.relative(define.project_path, imageFolder),
status: sourceBookTask.status,
errorMsg: sourceBookTask.errorMsg,
updateTime: new Date(),
createTime: new Date(),
isAuto: sourceBookTask.isAuto,
imageStyle: sourceBookTask.imageStyle,
autoAnalyzeCharacter: sourceBookTask.autoAnalyzeCharacter,
customizeImageStyle: sourceBookTask.customizeImageStyle,
videoConfig: sourceBookTask.videoConfig,
prefixPrompt: sourceBookTask.prefixPrompt,
suffixPrompt: sourceBookTask.suffixPrompt,
version: sourceBookTask.version,
imageCategory: sourceBookTask.imageCategory,
} as Book.SelectBookTask
addBookTask.push(addOneBookTask)
for (let j = 0; j < sourceBookTaskDetail.length; j++) {
const element = sourceBookTaskDetail[j];
let outImagePath = undefined as string
let subImagePath = [] as string[]
if (copyImageType == CopyImageType.ALL) { // 直接全部复制
outImagePath = element.outImagePath
subImagePath = element.subImagePath
} else if (copyImageType == CopyImageType.ONE) { // 只复制对应的
let oldImage = element.subImagePath[i + 1]
outImagePath = path.join(imageFolder, path.basename(element.outImagePath))
await CopyFileOrFolder(oldImage, outImagePath)
subImagePath = []
}
else if (copyImageType == CopyImageType.NONE) {
outImagePath = undefined
subImagePath = []
} else {
throw new Error("无效的图片复制类型")
}
if (outImagePath) {
// 单独处理一下显示的图片
let imageBaseName = path.basename(element.outImagePath);
let newImageBaseName = path.join(define.project_path, `${sourceBookTask.bookId}/tmp/${name}/${imageBaseName}`)
await CopyFileOrFolder(outImagePath, newImageBaseName)
}
// 处理SD设置
let sdConifg = undefined
if (element.sdConifg) {
let sdConifg = cloneDeep(element.sdConifg)
if (sdConifg.webuiConfig) {
let tempSdConfig = cloneDeep(sdConifg.webuiConfig);
tempSdConfig.id = uuidv4()
sdConifg.webuiConfig = tempSdConfig
}
}
let reverseId = uuidv4()
// 处理反推数据
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 = reverseId
}
}
let addOneBookTaskDetail = {
id: reverseId,
no: element.no,
name: element.name,
bookId: sourceBookTask.bookId,
bookTaskId: addOneBookTask.id,
videoPath: path.relative(define.project_path, element.videoPath),
word: element.word,
oldImage: path.relative(define.project_path, element.oldImage),
afterGpt: element.afterGpt,
startTime: element.startTime,
endTime: element.endTime,
timeLimit: element.timeLimit,
subValue: element.subValue,
characterTags: element.characterTags && element.characterTags.length > 0 ? cloneDeep(element.characterTags) : [],
gptPrompt: element.gptPrompt,
outImagePath: path.relative(define.project_path, outImagePath),
subImagePath: subImagePath || [],
prompt: element.prompt,
adetailer: element.adetailer,
sdConifg: sdConifg,
createTime: new Date(),
updateTime: new Date(),
audioPath: element.audioPath,
subtitlePosition: element.subtitlePosition,
status: element.status,
reversePrompt: reverseMessage,
imageLock: element.imageLock
} as Book.SelectBookTaskDetail
addBookTaskDetail.push(addOneBookTaskDetail)
}
}
// 数据处理完毕,开始新增数据
// 将所有的复制才做,全部放在一个事务中
this.bookTaskService.transaction(() => {
for (let i = 0; i < addBookTask.length; i++) {
const element = addBookTask[i];
this.bookTaskService.realm.create('BookTask', element)
}
for (let i = 0; i < addBookTaskDetail.length; i++) {
const element = addBookTaskDetail[i];
this.bookTaskDetailService.realm.create('BookTaskDetail', element)
}
})
// 全部创建完成
// 查找到数据,然后全部返回
let returnBookTask = this.bookTaskService.GetBookTaskData({
bookId: sourceBookTask.bookId
}).data as Book.SelectBookTask[]
return successMessage(returnBookTask, "复制小说任务成功", "BookBasic_CopyNewBookTask")
} catch (error) {
console.log(error)
throw error
}
}
//#endregion
}

View File

@ -0,0 +1,588 @@
import { successMessage, errorMessage } from '../../Public/generalTools'
import { BookBasic } from './BooKBasic'
import { BookService } from '../../../define/db/service/Book/bookService'
import { BookTaskService } from '../../../define/db/service/Book/bookTaskService'
import { define } from '../../../define/define.js'
import fs from 'fs'
import { DEFINE_STRING } from "../../../define/define_string";
import path from 'path'
import { BasicReverse } from './basicReverse'
import { BookTaskDetailService } from '../../../define/db/service/Book/bookTaskDetailService'
import { TaskScheduler } from "../../Service/taskScheduler"
import { Book } from '../../../model/book'
import { LoggerStatus, OtherData, ResponseMessageType } from '../../../define/enum/softwareEnum'
import { GeneralResponse } from '../../../model/generalResponse'
import { cloneDeep, isEmpty } from 'lodash'
import { Subtitle } from '../../Service/subtitle'
import { Watermark } from '../watermark'
import { SubtitleSavePositionType } from '../../../define/enum/waterMarkAndSubtitle'
import { CheckFileOrDirExist, CheckFolderExistsOrCreate, CopyFileOrFolder, GetFilesWithExtensions } from '../../../define/Tools/file'
import { ValidateJson } from '../../../define/Tools/validate'
import { GetImageBase64, ProcessImage } from '../../../define/Tools/image'
import { BookBackTaskType, BookType, TagDefineType, TaskExecuteType } from '../../../define/enum/bookEnum'
import { BookBackTaskListService } from '../../../define/db/service/Book/bookBackTaskListService'
import { MJOpt } from '../MJ/mj'
import { TagDefine } from '../../../define/tagDefine'
import { ImageStyleDefine } from '../../../define/iamgeStyleDefine'
/**
*
*/
export class ReverseBook extends BookBasic {
basicReverse: BasicReverse
bookTaskService: BookTaskService
taskScheduler: TaskScheduler
bookService: BookService
bookTaskDetailService: BookTaskDetailService
bookBackTaskListService: BookBackTaskListService
mjOpt: MJOpt = new MJOpt()
tagDefine: TagDefine
subtitle: Subtitle
watermark: Watermark
constructor() {
super()
this.basicReverse = new BasicReverse()
this.tagDefine = new TagDefine()
}
async InitService() {
if (!this.bookTaskService) {
this.bookTaskService = await BookTaskService.getInstance()
}
if (!this.taskScheduler) {
this.taskScheduler = new TaskScheduler()
}
if (!this.bookService) {
this.bookService = await BookService.getInstance()
}
if (!this.bookTaskDetailService) {
this.bookTaskDetailService = await BookTaskDetailService.getInstance()
}
if (!this.subtitle) {
this.subtitle = new Subtitle()
}
if (!this.watermark) {
this.watermark = new Watermark()
}
if (!this.bookBackTaskListService) {
this.bookBackTaskListService = await BookBackTaskListService.getInstance()
}
}
// 主动返回前端的消息
sendReturnMessage(data: GeneralResponse.MessageResponse, message_name = DEFINE_STRING.BOOK.GET_COPYWRITING_RETURN) {
global.newWindow[0].win.webContents.send(message_name, data)
}
//#region 小说相关操作
/**
*
* @param {*} bookQuery
*/
async GetBookData(bookQuery) {
try {
let _bookService = await BookService.getInstance()
// 添加小说
let res = _bookService.GetBookData(bookQuery)
if (res.code == 0) {
throw new Error(res.message)
}
return res
} catch (error) {
return errorMessage(error.message, 'ReverseBook_GetBookData')
}
}
//#endregion
//#region 小说批次任务相关操作
/**
*
* @param {*} bookTaskCondition
*/
async GetBookTaskData(bookTaskCondition): Promise<GeneralResponse.ErrorItem | GeneralResponse.SuccessItem> {
try {
await this.InitService()
let _bookTaskService = await BookTaskService.getInstance()
let res = _bookTaskService.GetBookTaskData(bookTaskCondition)
if (res.code == 0) {
throw new Error(res.message)
}
// //TODO 这个后面是不是将所有的数据都是用数据库
// // 这边加载自定义风格
// let styleLists = await this.tagDefine.getTagDataByTypeAndProperty('dynamic', "style_tags");
// if (styleLists.code == 0) {
// throw new Error('获取自定义风格失败')
// }
// let styleTags = styleLists.data as Book.BookStyle[];
// // 异步操作获取风格信息
// for (let index = 0; index < res.data.bookTasks.length; index++) {
// const element = res.data.bookTasks[index];
// let styleList = [] as Book.BookStyle[] | Book.DefineBookStyle[];
// // if (element.imageStyle.length > 0) { // 软件自带的风格
// // let infoRes = ImageStyleDefine.getImageStyleInfomation(JSON.stringify(element.imageStyle));
// // if (infoRes.code == 0) {
// // throw new Error('获取风格失败');
// // }
// // styleList = infoRes.data;
// // }
// if (element.customizeImageStyle.length > 0) { // 自定义的风格
// element.customizeImageStyle.forEach((item) => {
// let style = styleTags.find((tag) => tag.key == item);
// if (style) {
// styleList.push(style);
// }
// });
// }
// res.data.bookTasks[index].styleList = styleList;
// }
return successMessage(res.data, '获取小说任务成功', 'ReverseBook_GetBookTaskData')
} catch (error) {
return errorMessage(
'获取小说对应批次错误,错误信息入校:' + error.message,
'ReverseBook_GetBookTaskData'
)
}
}
/**
*
* @param bookTaskId
*/
async GetBookTaskDetail(bookTaskId: string) {
try {
await this.InitService()
let _bookTaskDetailService = await BookTaskDetailService.getInstance()
let res = _bookTaskDetailService.GetBookTaskData({ bookTaskId: bookTaskId })
if (res.code == 0) {
throw new Error(res.message)
}
return res
} catch (error) {
return errorMessage(
'获取小说对应批次错误,错误信息入校:' + error.message,
'ReverseBook_GetBookTaskData'
)
}
}
//#endregion
//#region 一键全自动
/**
*
* @param {*} value
* @returns
*/
async AutoAction(bookId) {
try {
// 在一键全自动之前当前小说对应的批次任务中的所有的子任务都改为fail然后再执行
// 获取对应的小说小说数据,找到对应的小说视频地址
// let _bookService = await BookService.getInstance()
// let _bookTaskService = await BookTaskService.getInstance()
// let bookData = _bookService.GetBookDataById(bookId)
// if (bookData.data == null) {
// throw new Error('没有找到对应的小说数据请检查bookId是否正确')
// }
// // 获取小说对应的批次任务数据,默认初始化为第一个
// let bookTaskRes = _bookTaskService.GetBookTaskData({
// bookId: bookId,
// name: 'output_00001'
// })
// if (bookTaskRes.data.bookTasks.length <= 0 || bookTaskRes.data.total <= 0) {
// throw new Error('没有找到对应的小说批次任务数据请检查bookId是否正确')
// }
// // 获取小说的视频地址
// let book = bookData.data
// let bookTask = bookTaskRes.data.bookTasks[0]
// // 将当前小说对应的批次任务中的所有的子任务都改为fail
// let updateTaskRes = _bookTaskService.UpdetedBookTaskToFail(bookId, bookTask.id)
// // 添加分镜任务 后面就会全自动的开始执行
// let res = await this.basicReverse.AddFrameDataTask(bookId)
// 添加分割视频任务
// let res = await this.basicReverse.AddCutVideoDataTask(bookId)
// 添加音频分离任务
// let res = await this.basicReverse.AddSplitAudioDataTask(bookId)
// 添加图片抽帧任务
let res = await this.basicReverse.AddGetFrameTask(bookId)
if (res.code == 0) {
throw new Error(res.message)
}
} catch (error) {
return errorMessage(error.message, 'ReverseBook_AutoAction')
}
}
async GetBookAndTask(bookId: string, bookTaskName: string) {
let book = this.bookService.GetBookDataById(bookId)
if (book == null) {
throw new Error("查找小说数据失败");
}
// 获取小说对应的批次任务数据,默认初始化为第一个
let bookTaskRes = await this.bookTaskService.GetBookTaskData({
bookId: bookId,
name: bookTaskName
})
if (bookTaskRes.data.bookTasks.length <= 0 || bookTaskRes.data.total <= 0) {
let msg = "没有找到对应的小说批次任务数据"
this.taskScheduler.AddLogToDB(bookId, book.type, msg, OtherData.DEFAULT, LoggerStatus.FAIL)
throw new Error(msg)
}
return { book: book as Book.SelectBook, bookTask: bookTaskRes.data.bookTasks[0] as Book.SelectBookTask }
}
/**
*
*/
async ComputeStoryboard(bookId: string): Promise<any> {
try {
await this.InitService()
let { book, bookTask } = await this.GetBookAndTask(bookId, 'output_00001')
let res = await this.basicReverse.ComputeStoryboardFunc(bookId, bookTask.id);
// 分镜成功直接返回
return successMessage(null, res, "ReverseBook_ComputeStoryboard")
} catch (error) {
return errorMessage("分镜计算失败,失败信息如下 " + error.message, 'ReverseBook_ComputeStoryboard')
}
}
/**
*
* @param bookId
*/
async Framing(bookId: string): Promise<GeneralResponse.ErrorItem | GeneralResponse.SuccessItem> {
try {
await this.InitService()
let { book, bookTask } = await this.GetBookAndTask(bookId, 'output_00001')
// 获取所有的分镜数据
let bookTaskDetail = this.bookTaskDetailService.GetBookTaskData({
bookId: bookId,
bookTaskId: bookTask.id
})
if (bookTaskDetail.data.length <= 0) {
// 传入的分镜数据为空,需要重新获取
await this.taskScheduler.AddLogToDB(
bookId,
book.type,
`没有传入分镜数据,请先进行分镜计算`,
OtherData.DEFAULT,
LoggerStatus.DOING
)
throw new Error("没有传入分镜数据,请先进行分镜计算");
}
let bookTaskDetails = bookTaskDetail.data as Book.SelectBookTaskDetail[]
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 = this.bookTaskDetailService.GetBookTaskData({
bookId: bookId,
bookTaskId: bookTask.id
})
bookTaskDetails = bookTaskDetail.data 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')
}
}
/**
*
*/
async GetCopywriting(bookId: string): Promise<GeneralResponse.ErrorItem | GeneralResponse.SuccessItem> {
try {
await this.InitService()
let { book, bookTask } = await this.GetBookAndTask(bookId, 'output_00001')
if (isEmpty(book.subtitlePosition)) {
throw new Error("请先设置小说的字幕位置")
}
// 获取所有的分镜数据
let bookTaskDetail = this.bookTaskDetailService.GetBookTaskData({
bookId: bookId,
bookTaskId: bookTask.id
})
if (bookTaskDetail.data.length <= 0) {
// 传入的分镜数据为空,需要重新获取
await this.taskScheduler.AddLogToDB(
bookId,
book.type,
`没有传入分镜数据,请先进行分镜计算`,
OtherData.DEFAULT,
LoggerStatus.DOING
)
throw new Error("没有传入分镜数据,请先进行分镜计算");
}
let bookTaskDetails = bookTaskDetail.data as Book.SelectBookTaskDetail[]
for (let i = 0; i < bookTaskDetails.length; i++) {
const item = bookTaskDetails[i];
let res = await this.subtitle.GetVideoFrameText({
id: item.id,
videoPath: item.videoPath,
type: SubtitleSavePositionType.STORYBOARD_VIDEO,
subtitlePosition: book.subtitlePosition
})
if (res.code == 0) {
throw new Error(res.message)
}
// 修改数据
this.bookTaskDetailService.UpdateBookTaskDetail(item.id, {
word: res.data,
afterGpt: res.data
});
// let res = await this.basicReverse.GetCopywritingFunc(book, item);
// 将当前的数据实时返回,前端进行修改
this.sendReturnMessage({
code: 1,
id: item.id,
type: ResponseMessageType.GET_TEXT,
data: res.data // 返回识别到的文案
})
// 添加日志
await this.taskScheduler.AddLogToDB(
bookId,
book.type,
`${item.name} 识别文案成功`,
bookTask.id,
LoggerStatus.SUCCESS
)
}
return successMessage(null, "识别是所有文案成功", "ReverseBook_GetCopywriting")
} catch (error) {
return errorMessage("获取分镜数据失败,失败信息如下:" + error.message, 'ReverseBook_GetCopywriting')
}
}
/**
*
* @param bookId ID
*/
async RemoveWatermark(bookId) {
try {
await this.InitService()
let { book, bookTask } = await this.GetBookAndTask(bookId, 'output_00001')
let subtitlePosition = book.subtitlePosition;
if (isEmpty(subtitlePosition)) {
throw new Error("当前没有文案位置的内容,请先进行 ‘开始提取文案 -> 提取文案设置 -> 框选大概位置 -> 保存位置’ 操作保存文案的位置")
}
if (!ValidateJson(subtitlePosition)) {
throw new Error("文案位置格式有误,请先进行 ‘开始提取文案 -> 提取文案设置 -> 框选大概位置 -> 保存位置’ 操作保存文案的位置")
}
let subtitlePositionParse = JSON.parse(subtitlePosition)
if (subtitlePositionParse.length <= 0) {
throw new Error("文案位置格式有误,请先进行 ‘开始提取文案 -> 提取文案设置 -> 框选大概位置 -> 保存位置’ 操作保存文案的位置")
}
let inputImageFolder = path.resolve(define.project_path, `${book.id}/tmp/input`)
if (!(await CheckFileOrDirExist(inputImageFolder))) {
throw new Error("输出文件夹不存在,请先进行抽帧等操作")
}
let iamgePaths = await GetFilesWithExtensions(inputImageFolder, ['.png'])
if (iamgePaths.length <= 0) {
throw new Error("没有检查到抽帧图片,请先进行抽帧等操作")
}
// 处理位置生成蒙板
let inputImage = iamgePaths[0]
let outImagePath = path.resolve(define.project_path, `${book.id}/data/mask/mask_temp_${new Date().getTime()}.png`)
await CheckFolderExistsOrCreate(path.dirname(outImagePath));
await ProcessImage(inputImage, outImagePath, {
x: subtitlePositionParse[0].startX,
y: subtitlePositionParse[0].startY,
width: subtitlePositionParse[0].width,
height: subtitlePositionParse[0].height
})
if (!(await CheckFileOrDirExist(outImagePath))) {
throw new Error("生成蒙板失败")
}
// 开始去除水印
let bookTaskDetails = this.bookTaskDetailService.GetBookTaskData({
bookId: bookId,
bookTaskId: bookTask.id
}).data as Book.SelectBookTaskDetail[]
for (let i = 0; i < bookTaskDetails.length; i++) {
const element = bookTaskDetails[i];
let bookInputImage = element.oldImage;
let name = path.basename(bookInputImage)
let bak = path.resolve(define.project_path, `${book.id}/tmp/input/bak/${name}`);
// 做个备份吧
await CopyFileOrFolder(bookInputImage, bak)
await fs.promises.unlink(bookInputImage) // 删除原来的图片
// 开始调用去除水印的方法
let imageBase64 = await GetImageBase64(bak)
let maskBase64 = await GetImageBase64(outImagePath)
let res = await this.watermark.ProcessImage({
imageBase64: imageBase64,
maskBase64: maskBase64,
type: 'file',
inputFilePath: bak,
maskPath: outImagePath,
outFilePath: bookInputImage
})
// 去水印执行完毕
this.sendReturnMessage({
code: 1,
id: element.id,
type: ResponseMessageType.REMOVE_WATERMARK,
data: res
}, DEFINE_STRING.BOOK.REMOVE_WATERMARK_RETURN)
this.taskScheduler.AddLogToDB(bookId, book.type, `${element.name} 去除水印完成`, element.bookTaskId, LoggerStatus.SUCCESS)
}
// 全部完毕
return successMessage(null, "全部图片去除水印完成", "ReverseBook_RemoveWatermark")
} catch (error) {
return errorMessage("去除水印失败,错误信息如下:" + error.message, "ReverseBook_RemoveWatermark")
}
}
//#endregion
//#region 反推相关任务
/**
*
* @param bookTaskDetailId ID
* @param type
* @returns
*/
async AddReversePromptTask(bookTaskDetailIds: string[], type: BookType): Promise<GeneralResponse.ErrorItem | GeneralResponse.SuccessItem> {
try {
await this.InitService()
for (let index = 0; index < bookTaskDetailIds.length; index++) {
const bookTaskDetailId = bookTaskDetailIds[index];
let bookTaskDetail = this.bookTaskDetailService.GetBookTaskDetailDataById(bookTaskDetailId)
if (bookTaskDetail == null) {
throw new Error("没有找到对应的分镜数据")
}
let book = this.bookService.GetBookDataById(bookTaskDetail.bookId)
// 是不是又额外的类型,没有的话试用小说的类型
let task_type = undefined as BookBackTaskType
if (!type) {
type = book.type
}
switch (type) {
case BookType.MJ_REVERSE:
task_type = BookBackTaskType.MJ_REVERSE
break;
case BookType.SD_REVERSE:
task_type = BookBackTaskType.SD_REVERSE
break;
default:
throw new Error("暂不支持的推理类型")
}
let taskRes = await this.bookBackTaskListService.AddBookBackTask(book.id, task_type, TaskExecuteType.AUTO, bookTaskDetail.bookTaskId, bookTaskDetail.id
);
if (taskRes.code == 0) {
throw new Error(taskRes.message)
}
// 添加返回日志
await this.taskScheduler.AddLogToDB(book.id, book.type, `添加 ${task_type} 反推任务成功`, bookTaskDetail.bookTaskId, LoggerStatus.SUCCESS)
}
return successMessage(null, "添加反推任务成功", "ReverseBook_AddReversePromptTask")
} catch (error) {
return errorMessage("添加单个反推失败,错误信息如下:" + error.message, "ReverseBook_SingleReversePrompt")
}
}
// TODO SD反推待重写
async SDReversePrompt(task: TaskModal.Task): Promise<GeneralResponse.ErrorItem | GeneralResponse.SuccessItem> {
return successMessage(null, "", "ReverseBook_SDReversePrompt")
}
/**
*
* @param task
*/
async SingleReversePrompt(task: TaskModal.Task): Promise<GeneralResponse.ErrorItem | GeneralResponse.SuccessItem> {
try {
// 开始处理
let res = undefined
switch (task.type) {
case BookBackTaskType.MJ_REVERSE:
res = await this.mjOpt.MJImage2Text(task);
break
case BookBackTaskType.SD_REVERSE:
res = await this.SDReversePrompt(task);
break
default:
throw new Error("未知的任务类型")
}
// 正常返回信息处理
return successMessage(null, `${task.name} _ ${task.id} 反推任务完成`, 'ReverseBook_SingleReversePrompt')
} catch (error) {
// 进行错误信息处理
return errorMessage("单个反推失败,错误信息如下:" + error.message, "ReverseBook_SingleReversePrompt")
}
}
/**
*
* @param bookTaskDetailIds ID
*/
async RemoveReverseData(bookTaskDetailIds: string[]) {
try {
await this.InitService()
// 开始删除
if (bookTaskDetailIds.length <= 0) {
throw new Error("没有传入要删除的数据")
}
for (let i = 0; i < bookTaskDetailIds.length; i++) {
const element = bookTaskDetailIds[i];
this.bookTaskDetailService.DeleteBookTaskDetailReversePromptById(element)
}
// 全部删除完毕
return successMessage(null, "删除反推数据成功", "ReverseBook_RemoveReverseData")
} catch (error) {
return errorMessage("删除反推数据失败,错误信息如下:" + error.message, "ReverseBook_RemoveReverseData")
}
}
//#endregion
}

View File

@ -3,24 +3,26 @@ import fs from 'fs'
const util = require('util') const util = require('util')
const { exec } = require('child_process') const { exec } = require('child_process')
const execAsync = util.promisify(exec) const execAsync = util.promisify(exec)
import { define } from '../../define/define' import { define } from '../../../define/define'
import { BookService } from '../../define/db/service/Book/bookService' import { BookService } from '../../../define/db/service/Book/bookService'
import { TaskScheduler } from './taskScheduler' import { TaskScheduler } from '../taskScheduler'
import { LoggerStatus, LoggerType, OtherData } from '../../define/enum/softwareEnum' import { LoggerStatus, LoggerType, OtherData } from '../../../define/enum/softwareEnum'
import { errorMessage, successMessage } from '../generalTools' import { errorMessage, successMessage } from '../../Public/generalTools'
import { CheckFileOrDirExist, CheckFolderExistsOrCreate } from '../../define/Tools/file' import { CheckFileOrDirExist, CheckFolderExistsOrCreate } from '../../../define/Tools/file'
import { BookTaskDetailService } from '../../define/db/service/Book/bookTaskDetailService' import { BookTaskDetailService } from '../../../define/db/service/Book/bookTaskDetailService'
import { BookBackTaskListService } from '../../define/db/service/Book/bookBackTaskListService' import { BookBackTaskListService } from '../../../define/db/service/Book/bookBackTaskListService'
import { BookTaskService } from '../../define/db/service/Book/bookTaskService' import { BookTaskService } from '../../../define/db/service/Book/bookTaskService'
import { isEmpty, set } from 'lodash' import { isEmpty, set } from 'lodash'
import { TimeStringToMilliseconds, MillisecondsToTimeString } from '../../define/Tools/time' import { TimeStringToMilliseconds, MillisecondsToTimeString } from '../../../define/Tools/time'
import { FfmpegOptions } from './ffmpegOptions' import { FfmpegOptions } from '../ffmpegOptions'
import { import {
BookBackTaskType, BookBackTaskType,
BookTaskStatus, BookTaskStatus,
BookType, BookType,
TaskExecuteType TaskExecuteType
} from '../../define/enum/bookEnum' } from '../../../define/enum/bookEnum'
import { Book } from '../../../model/book'
import { GeneralResponse } from '../../../model/generalResponse'
const fspromises = fs.promises const fspromises = fs.promises
@ -28,6 +30,14 @@ const fspromises = fs.promises
* *
*/ */
export class BasicReverse { export class BasicReverse {
bookService: BookService
bookTaskService: BookTaskService
bookTaskDetailService: BookTaskDetailService
bookBackTaskListService: BookBackTaskListService
taskScheduler: TaskScheduler
ffmpegOptions: FfmpegOptions
constructor() { constructor() {
this.taskScheduler = new TaskScheduler() this.taskScheduler = new TaskScheduler()
this.ffmpegOptions = new FfmpegOptions() this.ffmpegOptions = new FfmpegOptions()
@ -75,10 +85,11 @@ export class BasicReverse {
} }
// 获取小说对应的批次任务数据,默认初始化为第一个 // 获取小说对应的批次任务数据,默认初始化为第一个
let bookTaskRes = this.bookTaskService.GetBookTaskData({ let bookTaskRes = await this.bookTaskService.GetBookTaskData({
bookId: bookId, bookId: bookId,
name: 'output_00001' name: 'output_00001'
}) })
if (bookTaskRes.data.bookTasks.length <= 0 || bookTaskRes.data.total <= 0) { if (bookTaskRes.data.bookTasks.length <= 0 || bookTaskRes.data.total <= 0) {
throw new Error('没有找到对应的小说批次任务数据请检查bookId是否正确') throw new Error('没有找到对应的小说批次任务数据请检查bookId是否正确')
} }
@ -107,34 +118,22 @@ export class BasicReverse {
) )
return successMessage(null, '添加分镜任务成功', 'BasicReverse_AddFrameDataTask') return successMessage(null, '添加分镜任务成功', 'BasicReverse_AddFrameDataTask')
} catch (error) { } catch (error) {
return errorMessage( throw error
'添加分镜任务失败,错误信息如下: ' + error.message,
'BasicReverse_AddFrameDataTask'
)
} }
} }
/** /**
* task获取 *
* @param {*} task * @param bookId ID
* @param bookTaskId ID
* @returns * @returns
*/ */
async GetFrameData(task) { async ComputeStoryboardFunc(bookId: string, bookTaskId: string): Promise<string> {
try {
// 分镜任务一定有对应的小说ID和对应的小说批次任务ID没有直接报错
if (isEmpty(task.bookId) || isEmpty(task.bookTaskId)) {
throw new Error('分镜任务bookId, bookTaskId不能为空')
}
await this.InitService() await this.InitService()
let book = this.bookService.GetBookDataById(bookId)
let bookId = task.bookId if (book == null) {
let bookTaskId = task.bookTaskId
let bookRes = this.bookService.GetBookDataById(bookId)
if (bookRes.data == null) {
throw new Error('没有找到对应的小说数据') throw new Error('没有找到对应的小说数据')
} }
let book = bookRes.data
this.bookTaskService.UpdateBookTaskStatus(bookTaskId, BookTaskStatus.STORYBOARD) this.bookTaskService.UpdateBookTaskStatus(bookTaskId, BookTaskStatus.STORYBOARD)
@ -220,12 +219,15 @@ export class BasicReverse {
let dataArray = frameJsonData[i] let dataArray = frameJsonData[i]
let bookTaskDetail = { let bookTaskDetail = {
bookId: bookId, bookId: bookId,
bookTaskId: bookTaskId bookTaskId: bookTaskId,
} startTime: 0,
endTime: 0
} as Book.SelectBookTaskDetail
// 将字符串转换为number // 将字符串转换为number
bookTaskDetail.startTime = TimeStringToMilliseconds(dataArray[0]) bookTaskDetail.startTime = TimeStringToMilliseconds(dataArray[0])
bookTaskDetail.endTime = TimeStringToMilliseconds(dataArray[1]) bookTaskDetail.endTime = TimeStringToMilliseconds(dataArray[1])
bookTaskDetail.status = BookTaskStatus.STORYBOARD_DONE // 分镜完成
let res = this.bookTaskDetailService.AddBookTaskDetail(bookTaskDetail) let res = this.bookTaskDetailService.AddBookTaskDetail(bookTaskDetail)
if (res.code == 0) { if (res.code == 0) {
@ -233,7 +235,7 @@ export class BasicReverse {
} }
} }
this.bookTaskService.UpdateBookTaskStatus(task.bookTaskId, BookTaskStatus.STORYBOARD_DONE) this.bookTaskService.UpdateBookTaskStatus(bookTaskId, BookTaskStatus.STORYBOARD_DONE)
// 分镜成功,推送日志 // 分镜成功,推送日志
await this.taskScheduler.AddLogToDB( await this.taskScheduler.AddLogToDB(
bookId, bookId,
@ -242,9 +244,30 @@ export class BasicReverse {
OtherData.DEFAULT, OtherData.DEFAULT,
LoggerStatus.SUCCESS LoggerStatus.SUCCESS
) )
return successMessage(null, `分镜成功,分镜信息在 ${frameJson}`, 'BasicReverse_GetFrameData') return `分镜成功,分镜信息在 ${frameJson}`
}
/**
* task获取
* @param {*} task
* @returns
*/
async GetFrameData(task) {
try {
// 分镜任务一定有对应的小说ID和对应的小说批次任务ID没有直接报错
if (isEmpty(task.bookId) || isEmpty(task.bookTaskId)) {
throw new Error('分镜任务bookId, bookTaskId不能为空')
}
await this.InitService()
let bookId = task.bookId
let bookTaskId = task.bookTaskId
let frameRes = await this.ComputeStoryboardFunc(bookId, bookTaskId)
return successMessage(frameRes)
} catch (error) { } catch (error) {
return errorMessage(error.message, 'BasicReverse_GetFrameData') throw error
} }
} }
@ -264,16 +287,14 @@ export class BasicReverse {
await this.InitService() await this.InitService()
// 判断小说是不是存在 // 判断小说是不是存在
let bookRes = this.bookService.GetBookDataById(bookId) let book = this.bookService.GetBookDataById(bookId)
if (bookRes == null) { if (book == null) {
throw new Error('没有找到对应的小说数据') throw new Error('没有找到对应的小说数据')
} }
let book = bookRes.data
// 找到对应的小说ID和对应的小说批次任务ID判断是不是有分镜数据 // 找到对应的小说ID和对应的小说批次任务ID判断是不是有分镜数据
let bookTaskRes = this.bookTaskService.GetBookTaskData({ let bookTaskRes = await this.bookTaskService.GetBookTaskData({
bookId: bookId, bookId: bookId,
name: 'output_00001' name: 'output_00001'
}) })
@ -296,9 +317,9 @@ export class BasicReverse {
OtherData.DEFAULT, OtherData.DEFAULT,
LoggerStatus.DOING LoggerStatus.DOING
) )
let frameRes = this.GetFrameData(bookId) let frameRes = await this.GetFrameData(bookId)
if (frameRes.code == 0) { if (frameRes.code == 0) {
throw new Error((await frameRes).message) throw new Error(frameRes.message)
} }
} }
@ -351,55 +372,28 @@ export class BasicReverse {
) )
return successMessage(null, '添加视频裁剪任务成功', 'BasicReverse_AddCutVideoDataTask') return successMessage(null, '添加视频裁剪任务成功', 'BasicReverse_AddCutVideoDataTask')
} catch (error) { } catch (error) {
await this.taskScheduler.AddLogToDB( throw error
bookId,
book.type,
error.message,
OtherData.DEFAULT,
LoggerStatus.FAIL
)
return errorMessage(
'添加视频裁剪任务失败,错误信息如下: ' + error.message,
'BasicReverse_CutVideoData'
)
} }
} }
/** /**
* *
* @param {*} task * @param bookTaskDetailId ID
* @returns * @returns
*/ */
async CutVideoData(task) { async FrameDataToCutVideoData(bookTaskDetail: Book.SelectBookTaskDetail): Promise<string> {
try {
// 视频分割任务一定有对应的小说ID和对应的小说批次任务ID和对应的分镜ID没有直接报错
if (isEmpty(task.bookId) || isEmpty(task.bookTaskId) || isEmpty(task.bookTaskDetailId)) {
throw new Error('分镜任务bookId, bookTaskId, bookTaskDetailId不能为空')
}
await this.InitService() await this.InitService()
let bookTaskDetailRes = this.bookTaskDetailService.GetBookTaskDetailDataById(
task.bookTaskDetailId
)
if (bookTaskDetailRes.code == 0) {
throw new Error(bookTaskDetailRes.message)
}
if (bookTaskDetailRes.data == null) {
throw new Error('没有找到对应的分镜数据')
}
// 开始执行裁剪视频
let bookTaskDetail = bookTaskDetailRes.data
let startTime = bookTaskDetail.startTime let startTime = bookTaskDetail.startTime
let endTime = bookTaskDetail.endTime let endTime = bookTaskDetail.endTime
let bookRes = this.bookService.GetBookDataById(task.bookId) let book = this.bookService.GetBookDataById(bookTaskDetail.bookId)
if (bookRes.data == null) { if (book == null) {
throw new Error('没有找到对应的小说数据') throw new Error('没有找到对应的小说数据')
} }
let book = bookRes.data
if (startTime == null || endTime == null) { if (startTime == null || endTime == null) {
this.bookTaskService.UpdateBookTaskStatus( this.bookTaskService.UpdateBookTaskStatus(
task.bookTaskId, bookTaskDetail.bookTaskId,
BookTaskStatus.SPLIT_FAIL, BookTaskStatus.SPLIT_FAIL,
'开始时间和结束时间不能为空' '开始时间和结束时间不能为空'
) )
@ -415,7 +409,7 @@ export class BasicReverse {
) )
if (res.code == 0) { if (res.code == 0) {
this.bookTaskService.UpdateBookTaskStatus( this.bookTaskService.UpdateBookTaskStatus(
task.bookTaskId, bookTaskDetail.bookTaskId,
BookTaskStatus.SPLIT_FAIL, BookTaskStatus.SPLIT_FAIL,
res.message res.message
) )
@ -423,32 +417,48 @@ export class BasicReverse {
} }
// 视频裁剪完成,要将裁剪后的视频地址写入到数据库中 // 视频裁剪完成,要将裁剪后的视频地址写入到数据库中
this.bookTaskDetailService.UpdateBookTaskDetail(task.bookTaskDetailId, { this.bookTaskDetailService.UpdateBookTaskDetail(bookTaskDetail.id, {
videoPath: path.relative(define.project_path, outVideoFile) videoPath: path.relative(define.project_path, outVideoFile)
}) })
// 小改小说批次的状态 // 小改小说批次的状态
this.bookTaskService.UpdateBookTaskStatus(task.bookTaskId, BookTaskStatus.SPLIT_DONE) this.bookTaskService.UpdateBookTaskStatus(bookTaskDetail.bookTaskId, BookTaskStatus.SPLIT_DONE)
// 结束,分镜完毕,推送日志,返回成功 // 结束,分镜完毕,推送日志,返回成功
await this.taskScheduler.AddLogToDB( await this.taskScheduler.AddLogToDB(
task.bookId, bookTaskDetail.bookId,
book.type, book.type,
`${task.name}_视频裁剪完成`, `${bookTaskDetail.name}_视频裁剪完成`,
OtherData.DEFAULT, OtherData.DEFAULT,
LoggerStatus.SUCCESS LoggerStatus.SUCCESS
) )
return successMessage(null, `${task.name}_视频裁剪完成`, 'BasicReverse_CutVideoData') return `${bookTaskDetail.name}_视频裁剪完成`;
}
/**
*
* @param {*} task
* @returns
*/
async CutVideoData(task: TaskModal.Task): Promise<GeneralResponse.ErrorItem | GeneralResponse.SuccessItem> {
try {
// 视频分割任务一定有对应的小说ID和对应的小说批次任务ID和对应的分镜ID没有直接报错
if (isEmpty(task.bookId) || isEmpty(task.bookTaskId) || isEmpty(task.bookTaskDetailId)) {
throw new Error('分镜任务bookId, bookTaskId, bookTaskDetailId不能为空')
}
await this.InitService()
let bookTaskDetail = this.bookTaskDetailService.GetBookTaskDetailDataById(
task.bookTaskDetailId
)
if (bookTaskDetail == null) {
throw new Error('没有找到对应的分镜数据')
}
let cur_res = await this.FrameDataToCutVideoData(bookTaskDetail);
return successMessage(null, `${task.name}_视频裁剪完成`, "BasicReverse_CutVideoData");
} catch (error) { } catch (error) {
await this.taskScheduler.AddLogToDB( throw error
task.bookId,
task.type,
error.message,
OtherData.DEFAULT,
LoggerStatus.FAIL
)
return errorMessage(
'裁剪视频失败,错误信息如下: ' + error.message,
'BasicReverse_CutVideoData'
)
} }
} }
@ -465,17 +475,16 @@ export class BasicReverse {
async AddSplitAudioDataTask(bookId, bookTaskId = null) { async AddSplitAudioDataTask(bookId, bookTaskId = null) {
try { try {
await this.InitService() await this.InitService()
let bookRes = this.bookService.GetBookDataById(bookId) let book = this.bookService.GetBookDataById(bookId)
if (bookRes == null) { if (book == null) {
throw new Error('没有找到对应的小说数据') throw new Error('没有找到对应的小说数据')
} }
let book = bookRes.data
let bookTaskRes let bookTaskRes
if (bookTaskId != null) { if (bookTaskId != null) {
bookTaskRes = this.bookTaskService.GetBookTaskData({ id: bookTaskId }) bookTaskRes = await this.bookTaskService.GetBookTaskData({ id: bookTaskId })
} else { } else {
bookTaskRes = this.bookTaskService.GetBookTaskData({ bookTaskRes = await this.bookTaskService.GetBookTaskData({
bookId: bookId, bookId: bookId,
name: 'output_00001' name: 'output_00001'
}) })
@ -532,10 +541,7 @@ export class BasicReverse {
} }
return successMessage(null, `添加所有音频分离任务成功`, 'BasicReverse_AddSplitAudioDataTask') return successMessage(null, `添加所有音频分离任务成功`, 'BasicReverse_AddSplitAudioDataTask')
} catch (error) { } catch (error) {
return errorMessage( throw error
'添加音频分离任务失败,错误信息如下: ' + error.message,
'BasicReverse_AddSplitAudioDataTask'
)
} }
} }
@ -551,19 +557,17 @@ export class BasicReverse {
throw new Error('分镜任务bookId, bookTaskId, bookTaskDetailId不能为空') throw new Error('分镜任务bookId, bookTaskId, bookTaskDetailId不能为空')
} }
await this.InitService() await this.InitService()
let bookRes = this.bookService.GetBookDataById(task.bookId) let book = this.bookService.GetBookDataById(task.bookId)
if (bookRes.data == null) { if (book == null) {
throw new Error('没有找到对应的小说数据') throw new Error('没有找到对应的小说数据')
} }
let book = bookRes.data
let bookTaskDetails = this.bookTaskDetailService.GetBookTaskDetailDataById( let bookTaskDetail = this.bookTaskDetailService.GetBookTaskDetailDataById(
task.bookTaskDetailId task.bookTaskDetailId
) )
if (bookTaskDetails.data == null) { if (bookTaskDetail == null) {
throw new Error('没有找到对应的分镜数据') throw new Error('没有找到对应的分镜数据')
} }
let bookTaskDetail = bookTaskDetails.data
let videoPath = bookTaskDetail.videoPath let videoPath = bookTaskDetail.videoPath
let audioPath = path.join(book.bookFolderPath, `data/audio/${bookTaskDetail.name}.wav`) let audioPath = path.join(book.bookFolderPath, `data/audio/${bookTaskDetail.name}.wav`)
await CheckFolderExistsOrCreate(path.dirname(audioPath)) await CheckFolderExistsOrCreate(path.dirname(audioPath))
@ -599,15 +603,7 @@ export class BasicReverse {
'BasicReverse_SplitAudioData' 'BasicReverse_SplitAudioData'
) )
} catch (error) { } catch (error) {
let error_message = `分离音频失败,错误信息如下:${error.message}` throw error
await this.taskScheduler.AddLogToDB(
task.bookId,
task.type,
error_message,
OtherData.DEFAULT,
LoggerStatus.FAIL
)
return errorMessage(error_message, 'BasicReverse_SplitAudioData')
} }
} }
@ -620,17 +616,16 @@ export class BasicReverse {
try { try {
// 开始添加任务 // 开始添加任务
await this.InitService() await this.InitService()
let bookRes = this.bookService.GetBookDataById(bookId) let book = this.bookService.GetBookDataById(bookId)
if (bookRes.data == null) { if (book == null) {
throw new Error('没有找到对应的小说数据') throw new Error('没有找到对应的小说数据')
} }
let book = bookRes.data
let bookTaskRes let bookTaskRes
if (bookTaskId != null) { if (bookTaskId != null) {
bookTaskRes = this.bookTaskService.GetBookTaskData({ id: bookTaskId }) bookTaskRes = await this.bookTaskService.GetBookTaskData({ id: bookTaskId })
} else { } else {
bookTaskRes = this.bookTaskService.GetBookTaskData({ bookTaskRes = await this.bookTaskService.GetBookTaskData({
bookId: bookId, bookId: bookId,
name: 'output_00001' name: 'output_00001'
}) })
@ -671,13 +666,53 @@ export class BasicReverse {
} }
return successMessage(null, '添加所有抽帧任务成功', 'BasicReverse_AddGetFrameTask') return successMessage(null, '添加所有抽帧任务成功', 'BasicReverse_AddGetFrameTask')
} catch (error) { } catch (error) {
return errorMessage( throw error
'添加抽帧任务失败,错误信息如下: ' + error.message,
'BasicReverse_AddGetFrameTask'
)
} }
} }
/**
*
* @param book
* @param bookTaskDetail
* @returns
*/
async FrameFunc(book: Book.SelectBook, bookTaskDetail: Book.SelectBookTaskDetail): Promise<string> {
let videoPath = bookTaskDetail.videoPath
let outputFramePath = path.join(
book.bookFolderPath,
`tmp/input/${bookTaskDetail.name}.png`
)
let frameTime = (bookTaskDetail.endTime - bookTaskDetail.startTime) / 2
let res = await this.ffmpegOptions.FfmpegGetFrame(frameTime, videoPath, outputFramePath)
if (res.code == 0) {
let errorMessage = `抽帧失败,错误信息如下:${res.message}`
this.bookTaskService.UpdateBookTaskStatus(
bookTaskDetail.bookTaskId,
BookTaskStatus.FRAME_FAIL,
errorMessage
)
throw new Error(errorMessage)
}
// 抽帧成功,将抽帧的地址写入到数据库中
this.bookTaskDetailService.UpdateBookTaskDetail(bookTaskDetail.id, {
oldImage: path.relative(define.project_path, outputFramePath)
})
// 推送成功消息
await this.taskScheduler.AddLogToDB(
book.id,
book.type,
`${bookTaskDetail.name}抽帧成功,输出地址:${outputFramePath}`,
OtherData.DEFAULT,
LoggerStatus.SUCCESS
)
// 修改状态为抽帧成功
this.bookTaskService.UpdateBookTaskStatus(bookTaskDetail.bookTaskId, BookTaskStatus.FRAME_DONE)
return `${bookTaskDetail.name}抽帧成功,输出地址:${outputFramePath}`
}
/** /**
* *
* @param {*} task * @param {*} task
@ -689,67 +724,25 @@ export class BasicReverse {
throw new Error('分镜任务bookId, bookTaskId, bookTaskDetailId不能为空') throw new Error('分镜任务bookId, bookTaskId, bookTaskDetailId不能为空')
} }
await this.InitService() await this.InitService()
let bookRes = this.bookService.GetBookDataById(task.bookId) let book = this.bookService.GetBookDataById(task.bookId)
if (bookRes.data == null) { if (book == null) {
throw new Error('没有找到对应的小说数据') throw new Error('没有找到对应的小说数据')
} }
let book = bookRes.data
let bookTaskDetailRes = this.bookTaskDetailService.GetBookTaskDetailDataById( let bookTaskDetail = this.bookTaskDetailService.GetBookTaskDetailDataById(
task.bookTaskDetailId task.bookTaskDetailId
) )
if (bookTaskDetailRes.data == null) { if (bookTaskDetail == null) {
throw new Error('没有找到对应的分镜数据') throw new Error('没有找到对应的分镜数据')
} }
let bookTaskDetail = bookTaskDetailRes.data
let videoPath = bookTaskDetail.videoPath let res = await this.FrameFunc(book, bookTaskDetail);
let outputFramePath = path.join(
book.bookFolderPath,
`data/tmp/input/${bookTaskDetail.name}.png`
)
let frameTime = (bookTaskDetail.endTime - bookTaskDetail.startTime) / 2
let res = await this.ffmpegOptions.FfmpegGetFrame(frameTime, videoPath, outputFramePath) return successMessage(null, res, 'BasicReverse_GetFrame')
if (res.code == 0) {
let errorMessage = `抽帧失败,错误信息如下:${res.message}`
this.bookTaskService.UpdateBookTaskStatus(
task.bookTaskId,
BookTaskStatus.FRAME_FAIL,
errorMessage
)
throw new Error(errorMessage)
}
// 抽帧成功,将抽帧的地址写入到数据库中
this.bookTaskDetailService.UpdateBookTaskDetail(task.bookTaskDetailId, {
oldImage: path.relative(define.project_path, outputFramePath)
})
// 推送成功消息
await this.taskScheduler.AddLogToDB(
task.bookId,
book.type,
`${bookTaskDetail.name}抽帧成功,输出地址:${outputFramePath}`,
OtherData.DEFAULT,
LoggerStatus.SUCCESS
)
// 修改状态为抽帧成功
this.bookTaskService.UpdateBookTaskStatus(task.bookTaskId, BookTaskStatus.FRAME_DONE)
return successMessage(null, '抽帧成功', 'BasicReverse_GetFrame')
} catch (error) { } catch (error) {
let errorMessage = `抽帧失败,错误信息如下:${error.message}` throw error
await this.taskScheduler.AddLogToDB(
task.bookId,
task.type,
errorMessage,
OtherData.DEFAULT,
LoggerStatus.FAIL
)
return errorMessage('抽帧失败,错误信息如下: ' + error.message, 'BasicReverse_GetFrame')
} }
} }
//#endregion //#endregion
//#region 提取字幕相关操作 //#region 提取字幕相关操作
@ -769,9 +762,9 @@ export class BasicReverse {
} }
let bookTask let bookTask
if (bookTaskId != null) { if (bookTaskId != null) {
bookTaskId = this.bookTaskService.GetBookTaskData({ id: bookTaskId }) bookTaskId = await this.bookTaskService.GetBookTaskData({ id: bookTaskId })
} else { } else {
bookTask = this.bookTaskService.GetBookTaskData({ bookTask = await this.bookTaskService.GetBookTaskData({
bookId: bookId, bookId: bookId,
name: 'output_00001' name: 'output_00001'
}) })
@ -794,7 +787,7 @@ export class BasicReverse {
const element = bookTaskDetails.data[i] const element = bookTaskDetails.data[i]
let taskRes = await this.bookBackTaskListService.AddBookBackTask( let taskRes = await this.bookBackTaskListService.AddBookBackTask(
bookId, bookId,
book.type, BookBackTaskType.RECOGNIZE,
TaskExecuteType.AUTO, TaskExecuteType.AUTO,
bookTask.id, bookTask.id,
element.id element.id
@ -817,13 +810,61 @@ export class BasicReverse {
'BasicReverse_AddExtractSubtitlesDataTask ' 'BasicReverse_AddExtractSubtitlesDataTask '
) )
} catch (error) { } catch (error) {
return errorMessage( throw error
'添加提取字幕任务失败,错误信息如下: ' + error.message,
'BasicReverse_AddExtractSubtitlesDataTask'
)
} }
} }
async GetCopywritingFunc(book: Book.SelectBook, bookTaskDetail: Book.SelectBookTaskDetail): Promise<string> {
let txt = ''
// 开始提取,调用本地的服务识别字幕
let isWisper = true
// 判断是不是用本地的wisper服务
if (isWisper) {
// 开始调用wisper
// 使用异步的方法调用一个python程序然后写入到指定的json文件中k
let out_dir = path.dirname(bookTaskDetail.videoPath)
let command = `"${path.join(define.scripts_path, 'Lai.exe')}" "-t" "${out_dir}" "${bookTaskDetail.audioPath
}" "${bookTaskDetail.name}"`
const output = await execAsync(command, {
maxBuffer: 1024 * 1024 * 10,
encoding: 'utf-8'
})
// 有错误输出
if (output.stderr != '') {
let error_msg = `提取字幕成功,但有警告提示:${output.stderr}`
await this.taskScheduler.AddLogToDB(
book.id,
book.type,
error_msg,
OtherData.DEFAULT,
LoggerStatus.FAIL
)
}
// 没有的话,去读取对应的字幕文件
let outTxtPath = path.join(out_dir, `${bookTaskDetail.name}.txt`)
txt = await fspromises.readFile(outTxtPath, 'utf-8')
} else {
// 使用网络服务
}
// 修改分镜数据的字幕
this.bookTaskDetailService.UpdateBookTaskDetail(bookTaskDetail.id, {
word: txt,
afterGpt: txt
})
// 提取字幕成功,推送日志
await this.taskScheduler.AddLogToDB(
book.id,
book.type,
`${bookTaskDetail.name} 提取字幕成功`,
OtherData.DEFAULT,
LoggerStatus.SUCCESS
)
return txt;
}
/** /**
* *
* @param {*} task * @param {*} task
@ -837,75 +878,22 @@ export class BasicReverse {
} }
await this.InitService() await this.InitService()
// 获取详细的小说分镜信息 // 获取详细的小说分镜信息
let bookTaskDetailRes = this.bookTaskDetailService.GetBookTaskDetailDataById( let bookTaskDetail = this.bookTaskDetailService.GetBookTaskDetailDataById(
task.bookTaskDetailId task.bookTaskDetailId
) )
if (bookTaskDetailRes.data == null) { if (bookTaskDetail == null) {
throw new Error('没有找到对应的分镜数据') throw new Error('没有找到对应的分镜数据')
} }
let bookTaskDetail = bookTaskDetailRes.data let book = this.bookService.GetBookDataById(task.bookId)
let txt = '' if (book == null) {
// 开始提取,调用本地的服务识别字幕 throw new Error('没有找到对应的小说数据')
let isWisper = true
// 判断是不是用本地的wisper服务
if (isWisper) {
// 开始调用wisper
// 使用异步的方法调用一个python程序然后写入到指定的json文件中k
let out_dir = path.dirname(bookTaskDetail.audioPath)
let command = `"${path.join(define.scripts_path, 'Lai.exe')}" "-t" "${out_dir}" "${
bookTaskDetail.audioPath
}" "${bookTaskDetail.name}"`
const output = await execAsync(command, {
maxBuffer: 1024 * 1024 * 10,
encoding: 'utf-8'
})
// 有错误输出
if (output.stderr != '') {
let error_msg = `提取字幕成功,但有警告提示:${output.stderr}`
await this.taskScheduler.AddLogToDB(
task.bookId,
book.type,
error_msg,
OtherData.DEFAULT,
LoggerStatus.FAIL
)
} }
// 没有的话,去读取对应的字幕文件 let res = await this.GetCopywritingFunc(book, bookTaskDetail)
let outTxtPath = path.join(out_dir, `${bookTaskDetail.name}.txt`) return successMessage(null, res, 'BasicReverse_ExtractSubtitlesData')
txt = await fspromises.readFile(outTxtPath, 'utf-8')
} else {
// 使用网络服务
}
// 修改分镜数据的字幕
this.bookTaskDetailService.UpdateBookTaskDetail(task.bookTaskDetailId, {
word: txt,
afterGpt: txt
})
// 提取字幕成功,推送日志
await this.taskScheduler.AddLogToDB(
bookId,
book.type,
`${task.name} 提取字幕成功`,
OtherData.DEFAULT,
LoggerStatus.SUCCESS
)
return successMessage(null, `${task.name} 提取字幕成功`, 'BasicReverse_ExtractSubtitlesData')
} catch (error) { } catch (error) {
let errorMessage = `${task.name} 提取字幕失败,错误信息如下:${error.message}` throw error
await this.taskScheduler.AddLogToDB(
bookId,
book.type,
errorMessage,
OtherData.DEFAULT,
LoggerStatus.FAIL
)
return errorMessage(errorMessage, 'BasicReverse_ExtractSubtitlesData')
} }
} }

View File

@ -0,0 +1,346 @@
import { BookImageCategory, BookType, OperateBookType } from "../../../define/enum/bookEnum";
import { GeneralResponse } from "../../../model/generalResponse";
import { errorMessage, successMessage } from "../../Public/generalTools";
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 { Book } from "../../../model/book";
import path from 'path'
import { Tools } from "../../../main/tools"
import { ImageSplit } from "../../../define/Tools/image";
import { BackupFileOrFolder, CheckFileOrDirExist, CheckFolderExistsOrCreate, CopyFileOrFolder, DeleteFolderAllFile, GetFileSize, GetFilesWithExtensions } from "../../../define/Tools/file";
import { define } from "../../../define/define";
import { MJOpt } from "../MJ/mj";
import { isEmpty } from "lodash";
import { DEFINE_STRING } from "../../../define/define_string";
import { ResponseMessageType } from "../../../define/enum/softwareEnum";
import util from 'util'
import { exec } from 'child_process'
const execAsync = util.promisify(exec);
/**
*
*/
export class BookImage {
bookTaskService: BookTaskService
bookTaskDetailService: BookTaskDetailService
bookService: BookService
tools: Tools;
mjOpt: MJOpt;
constructor() {
this.tools = new Tools()
this.mjOpt = new MJOpt()
}
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();
}
}
/**
* MJ的数据到前端界面
* @param {*} data
*/
sendChangeMessage(data: GeneralResponse.MessageResponse, message_name = DEFINE_STRING.BOOK.MAIN_DATA_RETURN) {
global.newWindow[0].win.webContents.send(message_name, data)
}
/**
*
* @param id ID
* @param scale
* @param operateBookType BOOKBOOKTASK两种
*/
async HDImage(id: string, scale: number, operateBookType: OperateBookType): Promise<GeneralResponse.ErrorItem | GeneralResponse.SuccessItem> {
try {
await this.InitService()
if (scale <= 0 || scale > 4) {
throw new Error('高清倍率只能是234')
}
let bookTasks = undefined as Book.SelectBookTask[]
if (operateBookType == OperateBookType.BOOK) {
// 获取所有的小说批次任务
bookTasks = this.bookTaskService.GetBookTaskData({
bookId: id
}).data.bookTasks
} else if (operateBookType == OperateBookType.BOOKTASK) {
// 获取当前的小说批次任务
bookTasks = this.bookTaskService.GetBookTaskData({
id: id
}).data.bookTasks
} else {
throw new Error("不支持的操作类型,请检查")
}
if (bookTasks.length <= 0) {
throw new Error("没有找到需要高清的批次任务,请检查");
}
// 高清前,先备份文件
let bakImages = []
for (let i = 0; i < bookTasks.length; i++) {
const element = bookTasks[i];
let imageFolder = element.imageFolder
let baseName = path.basename(imageFolder)
let bakFolder = path.join(path.dirname(imageFolder), 'bak/' + baseName);
if (await CheckFileOrDirExist(bakFolder)) {
await DeleteFolderAllFile(bakFolder, true)
}
await BackupFileOrFolder(imageFolder, bakFolder)
// 创建原本的
await CheckFolderExistsOrCreate(imageFolder);
// bakFolders.push(bakFolder)
let imgs = await GetFilesWithExtensions(bakFolder, ['.png', '.jpg', '.jpeg'])
bakImages.push(...imgs)
}
// 这边返回下数据,前端修改进度
this.sendChangeMessage({
code: 1,
id: undefined,
type: ResponseMessageType.HD_IMAGE,
data: {
total: bakImages.length,
current: 0
}
})
// 所有的文件备份完毕,开始高高清
for (let i = 0; i < bakImages.length; i++) {
const element = bakImages[i];
// 解析路径
const parsedPath = path.parse(element);
// 获取 `bak` 之前的目录路径
const dirWithoutBak = path.dirname(path.dirname(parsedPath.dir));
// 组合新路径
const newDir = path.join(dirWithoutBak, path.basename(parsedPath.dir));
const newPath = path.join(newDir, parsedPath.base);
// 开始高清
let command = `"${path.join(define.package_path, "Improve/rnv.exe")}" -i "${element}" -o "${newPath}" -s ${scale}`;
let out = await execAsync(command, { maxBuffer: 1024 * 1024 * 10, encoding: 'utf-8' });
this.sendChangeMessage({
code: 1,
id: undefined,
type: ResponseMessageType.HD_IMAGE,
data: {
total: bakImages.length,
current: i + 1
}
})
}
// 高清完毕,返回数据
return successMessage(null, "所有高清图片成功", "BookImage_HDImage")
} catch (error) {
return errorMessage("高清图片失败,失败信息如下:" + error.toString(), "BookImage_HDImage")
}
}
/**
* fileSize false
* @param id IDIDID
* @param fileSize
* @param operateBookType BOOKBOOKTASK两种
*/
async CheckImageFileSize(id: string, fileSize: number, operateBookType: OperateBookType) {
try {
await this.InitService()
let bookTasks = undefined as Book.SelectBookTask[]
if (operateBookType == OperateBookType.BOOK) {
// 获取所有的小说批次任务
bookTasks = this.bookTaskService.GetBookTaskData({
bookId: id
}).data.bookTasks
} else if (operateBookType == OperateBookType.BOOKTASK) {
// 获取当前的小说批次任务
bookTasks = this.bookTaskService.GetBookTaskData({
id: id
}).data.bookTasks
} else {
throw new Error("不支持的操作类型,请检查")
}
if (bookTasks.length <= 0) {
throw new Error("没有找到需要高清的批次任务,请检查");
}
let result = []
for (let i = 0; i < bookTasks.length; i++) {
const element = bookTasks[i];
let bookTaskDetail = this.bookTaskDetailService.GetBookTaskData({
bookTaskId: element.id
}).data
for (let i = 0; i < bookTaskDetail.length; i++) {
const item = bookTaskDetail[i];
if (isEmpty(item.outImagePath)) {
throw new Error(`检查高清失败,批次任务 ${element.name} 的分镜 ${item.name} 的输出图片没有找到。请先生成!`);
}
let size = await GetFileSize(item.outImagePath);
if (size >= fileSize) {
result.push({
bookTaskId: element.id,
bookTaskDetailId: item.id,
outImagePath: item.outImagePath
})
}
}
}
return successMessage(result, "高清前图片检查成功", "BookImage_CheckImageFileSize")
} catch (error) {
return errorMessage("检查图片的大小失败,失败信息如下:" + error.toString(), "BookImage_CheckImageFileSize")
}
}
/**
*
* @param bookTaskId
*/
async GenerateImageAll(bookTaskId: string, imageCategory: BookImageCategory): Promise<GeneralResponse.ErrorItem | GeneralResponse.SuccessItem> {
try {
console.log('开始生成所有的图片', bookTaskId)
await this.InitService()
let res = undefined as GeneralResponse.ErrorItem | GeneralResponse.SuccessItem
switch (imageCategory) {
case BookImageCategory.SD:
throw new Error('暂时不支持SD的生成')
case BookImageCategory.MJ:
res = await this.mjOpt.AddMJGenerateImageTask(bookTaskId, OperateBookType.BOOKTASK)
break;
case BookImageCategory.D3:
throw new Error('暂时不支持D3的生成')
default:
throw new Error('未知的生成类型')
}
return res
} catch (error) {
return errorMessage("添加生成所有的图片失败,失败信息如下:" + error.toString(), "BookImage_GenerateImageAll")
}
}
/**
*
* @param id id
* @param type lock或者是unlock
* @param operateBookType
*/
async ImageLockOperation(id: string, type: string, operateBookType: OperateBookType): Promise<GeneralResponse.ErrorItem | GeneralResponse.SuccessItem> {
try {
await this.InitService()
let bookTaskDetail = undefined as Book.SelectBookTaskDetail[]
if (operateBookType == OperateBookType.BOOKTASK) {
bookTaskDetail = this.bookTaskDetailService.GetBookTaskData({
bookTaskId: id
}).data
} else if (operateBookType == OperateBookType.BOOKTASKDETAIL) {
bookTaskDetail = [this.bookTaskDetailService.GetBookTaskDetailDataById(id)];
}
else {
throw new Error('操作的对象类型不正确')
}
if (type == "lock") {
bookTaskDetail = bookTaskDetail.filter(item => (item.imageLock == null) || (item.imageLock == false && item.outImagePath != null))
} else if (type == "unlock") {
bookTaskDetail = bookTaskDetail.filter(item => item.imageLock == true && item.outImagePath != null)
} else {
throw new Error('未知的锁定类型')
}
if (bookTaskDetail.length <= 0) {
throw new Error('没有要操作的数据,请检查')
}
let result = []
for (let i = 0; i < bookTaskDetail.length; i++) {
const element = bookTaskDetail[i];
let lock = type == "lock" ? true : false
this.bookTaskDetailService.UpdateBookTaskDetail(element.id, {
imageLock: lock
})
result.push({
id: element.id,
imageLock: lock
})
}
return successMessage(result, "全部图片执行锁定或解锁成功", "BookImage_ImageLockOperation")
} catch (error) {
return errorMessage("图片执行锁定或解锁失败,失败信息如下:" + error.toString(), "BookImage_ImageLockOperation")
}
}
/**
*
* @param bookTaskDetailId ID
* @param imageUrl
* @returns
*/
async DownloadImageUrlAndSplit(bookTaskDetailId: string, imageUrl: string,) {
try {
await this.InitService();
let bookTaskDetail = this.bookTaskDetailService.GetBookTaskDetailDataById(bookTaskDetailId)
if (bookTaskDetail == null) {
throw new Error('获取到的数据分镜为空,无法执行操作')
}
let book = this.bookService.GetBookDataById(bookTaskDetail.bookId)
if (book == null) {
throw new Error('获取到的小说为空,无法执行操作')
}
let bookTask = this.bookTaskService.GetBookTaskDataById(bookTaskDetail.bookTaskId)
if (bookTask == null) {
throw new Error('获取到的任务为空,无法执行操作')
}
let imagePath = path.join(book.bookFolderPath, `data\\MJOriginalImage\\${bookTaskDetail.id}.png`)
// 判断是不是一个链接
const urlRegex = /^(http|https):\/\/[^ "]+$/
if (!urlRegex.test(imageUrl)) {
// 本地图片,直接复制
await CopyFileOrFolder(imageUrl, imagePath)
} else {
// 网络图片下载
await this.tools.downloadFileUrl(imageUrl, imagePath)
}
// 进行图片裁剪
let imageRes = await ImageSplit(imagePath, bookTaskDetail.name, path.join(book.bookFolderPath, 'data\\MJOriginalImage'));
if (imageRes && imageRes.length < 4) {
throw new Error("图片裁剪失败")
}
// 修改数据
// 修改数据库数据,将图片保存到对应的文件夹中
let firstImage = imageRes[0];
if (book.type == BookType.ORIGINAL) {
await CopyFileOrFolder(firstImage, path.join(book.bookFolderPath, `tmp\\input\\${bookTaskDetail.name}.png`));
}
let out_file = path.join(bookTask.imageFolder, `${bookTaskDetail.name}.png`)
await CopyFileOrFolder(firstImage, out_file);
// 修改分镜的数据
this.bookTaskDetailService.UpdateBookTaskDetail(bookTaskDetailId, {
outImagePath: path.relative(define.project_path, out_file),
subImagePath: imageRes.map((item) => path.relative(define.project_path, item))
})
return successMessage({
outImagePath: out_file + '?time=' + new Date().getTime(),
subImagePath: imageRes.map((item) => item + '?time=' + new Date().getTime())
}, "下载指定的图片地址并且分割成功", "BookImage_DownloadImageUrlAndSplit")
} catch (error) {
return {
code: 0,
message: '下载指定的图片地址并且分割错误,错误信息如下:' + error.message
}
}
}
}

View File

@ -0,0 +1,78 @@
import { CheckFolderExistsOrCreate, 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, OperateBookType } from "../../../define/enum/bookEnum";
import { errorMessage, successMessage } from "../../Public/generalTools";
import { Book } from "../../../model/book";
/**
*
*/
export class BookTask {
bookTaskService: BookTaskService
bookTaskDetailService: BookTaskDetailService
bookService: BookService
constructor() {
}
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 bookTaskId ID
*/
async ReSetBookTask(bookTaskId: string) {
try {
console.log(bookTaskId)
await this.InitService()
let bookTask = this.bookTaskService.GetBookTaskDataById(bookTaskId);
if (bookTask == null) {
throw new Error('未找到对应的小说任务')
}
this.bookTaskService.ResetBookTask(bookTaskId);
// 数据库删除完毕,看是删除对应的文件(主要是图片)
let imageFolder = bookTask.imageFolder;
// 整个删掉在重建
await DeleteFolderAllFile(imageFolder)
await CheckFolderExistsOrCreate(imageFolder)
return successMessage(null, "重置小说数据成功", "BookTask_ReSetBookTask")
} catch (error) {
return errorMessage('重置小说批次数据失败,错误信息如下' + error.toString(), "BookTask_ReSetBookTask");
}
}
/**
*
* @param bookTaskId ID
*/
async DeleteBookTask(bookTaskId: string) {
try {
await this.InitService();
let bookTask = this.bookTaskService.GetBookTaskDataById(bookTaskId);
if (bookTask == null) {
throw new Error('未找到对应的小说任务,无法执行删除操作')
}
let imageFolder = bookTask.imageFolder;
// 先删除每个批次对应的数据,然后删除批次
this.bookTaskService.DeleteBookTask(bookTaskId);
// 删除成功,直接把对应的出图文件夹删掉
await DeleteFolderAllFile(imageFolder, true)
return successMessage(null, "删除小说批次数据成功", "BookTask_DeleteBookTask")
} catch (error) {
return errorMessage('删除小说批次数据失败,错误信息如下' + error.toString(), "BookTask_DeleteBookTask");
}
}
}

View File

@ -0,0 +1,235 @@
import { OperateBookType } from "../../../define/enum/bookEnum";
import { Book } from "../../../model/book";
import { errorMessage, successMessage } from "../../Public/generalTools";
import { BookService } from "../../../define/db/service/Book/bookService";
import { BookTaskService } from "../../../define/db/service/Book/bookTaskService";
import { GeneralResponse } from "../../../model/generalResponse";
import { Setting } from '../../../main/setting/setting'
import path from 'path'
import { CheckFolderExistsOrCreate } from "../../../define/Tools/file";
import { BookTaskDetailService } from "../../../define/db/service/Book/bookTaskDetailService";
import fs from 'fs'
import { ClipDraft } from '../../Public/clipDraft'
export class BookVideo {
bookService: BookService
bookTaskService: BookTaskService
setting: Setting
bookTaskDetailService: BookTaskDetailService
constructor() {
this.setting = new Setting(global)
}
async InitService() {
if (!this.bookService) {
this.bookService = await BookService.getInstance()
}
if (!this.bookTaskService) {
this.bookTaskService = await BookTaskService.getInstance()
}
if (!this.bookTaskDetailService) {
this.bookTaskDetailService = await BookTaskDetailService.getInstance()
}
}
/**
*
* @param id
* @param operateBookType
*/
async UseBookVideoDataToBookTask(id: string, operateBookType: OperateBookType) {
try {
console.log(id, operateBookType)
await this.InitService();
let book = undefined as Book.SelectBook;
let bookTasks = undefined as Book.SelectBookTask[];
if (operateBookType == OperateBookType.BOOK) {
book = this.bookService.GetBookDataById(id);
if (book == null) {
throw new Error('未找到对应的小说')
}
bookTasks = this.bookTaskService.GetBookTaskData({
bookId: id,
}).data;
} else if (operateBookType == OperateBookType.BOOKTASK) {
let bookTask = this.bookTaskService.GetBookTaskDataById(id);
if (bookTask == null) {
throw new Error('未找到对应的小说任务')
}
book = this.bookService.GetBookDataById(bookTask.bookId);
if (book == null) {
throw new Error('未找到对应的小说')
}
bookTasks = [bookTask];
} else {
throw new Error("未知的操作类型");
}
if (bookTasks.length <= 0) {
throw new Error("没有需要操作的小说数据,请检查");
}
// 将修改数据放在一个事务中
this.bookService.transaction(() => {
for (let i = 0; i < bookTasks.length; i++) {
const element = bookTasks[i];
let modifyBookTask = this.bookService.realm.objectForPrimaryKey('BookTask', element.id);
modifyBookTask.backgroundMusic = book.backgroundMusic;
modifyBookTask.friendlyReminder = book.friendlyReminder;
modifyBookTask.draftSrtStyle = book.draftSrtStyle;
modifyBookTask.srtPath = book.srtPath;
modifyBookTask.audioPath = book.audioPath;
}
})
return successMessage({
backgroundMusic: book.backgroundMusic,
friendlyReminder: book.friendlyReminder,
draftSrtStyle: book.draftSrtStyle,
srtPath: book.srtPath,
audioPath: book.audioPath,
}, "将小说中的生成视频的数据,写到小说批次任务中成功", "BookTask_UseBookVideoDataToBookTask")
} catch (error) {
return errorMessage('将小说中的生成视频的数据,写到小说批次任务中失败,错误信息如下:' + error.toString(), "BookTask_UseBookVideoDataToBookTask");
}
}
/**
* 稿
* @param book
* @param bookTask
*/
private async GenerateConfigFile(book: Book.SelectBook, bookTask: Book.SelectBookTask): Promise<void> {
try {
// 先修改通用设置
let saveProjectRes = await this.setting.ModifySampleSetting(JSON.stringify({
project_path: book.bookFolderPath,
project_name: book.name,
}))
if (saveProjectRes.code == 0) {
throw new Error("修改通用设置失败")
}
// 开始生成配置文件
let configPath = path.join(book.bookFolderPath, "scripts/config.json");
await CheckFolderExistsOrCreate(path.dirname(configPath));
// 开始配置
let bookTaskDetail = this.bookTaskDetailService.GetBookTaskData({
bookTaskId: bookTask.id
}).data;
let configData = {
srt_time_information: [],
video_config: {
srt_path: bookTask.srtPath,
audio_path: bookTask.audioPath,
draft_srt_style: bookTask.draftSrtStyle ? bookTask.draftSrtStyle : "0",
background_music: bookTask.backgroundMusic,
friendly_reminder: bookTask.friendlyReminder ? bookTask.friendlyReminder : "0",
}
}
for (let i = 0; i < bookTaskDetail.length; i++) {
const element = bookTaskDetail[i];
let frameData = {
no: element.no,
id: element.id,
lastId: i == 0 ? '' : bookTaskDetail[i - 1].id,
word: element.word,
old_image: element.oldImage,
after_gpt: element.afterGpt,
start_time: element.startTime,
end_time: element.endTime,
timeLimit: `${element.startTime} -- ${element.endTime}`,
subValue: element.subValue?.map(item => {
return {
start_time: item.startTime,
end_time: item.endTime,
srt_value: item.srtValue,
id: item.id
}
}),
character_tags: [],
gpt_prompt: element.gptPrompt,
mjMessage: element.mjMessage,
prompt_json: '',
name: element.name + '.png',
outImagePath: element.outImagePath,
subImagePath: element.subImagePath,
scene_tags: [],
imageLock: element.imageLock,
prompt: element.prompt
}
configData.srt_time_information.push(frameData)
}
// 完毕,将数据写出
await fs.promises.writeFile(configPath, JSON.stringify(configData));
} catch (error) {
throw error
}
}
async AddJianyingDraft(id: string, operateBookType: OperateBookType): Promise<GeneralResponse.ErrorItem | GeneralResponse.SuccessItem> {
try {
await this.InitService();
console.log(id, operateBookType)
let book = undefined as Book.SelectBook
let bookTasks = undefined as Book.SelectBookTask[]
if (operateBookType == OperateBookType.BOOK) {
book = this.bookService.GetBookDataById(id);
if (book == null) {
throw new Error("没有找到对应的小说数据,请检查");
}
bookTasks = this.bookTaskService.GetBookTaskData({
bookId: id
}).data.bookTasks
} else if (operateBookType == OperateBookType.BOOKTASK) {
// 直接获取对应的数据
let tempBookTask = this.bookTaskService.GetBookTaskDataById(id);
if (tempBookTask == null) {
throw new Error("没有找到小说批次任务,请检查");
}
bookTasks = [tempBookTask]
book = this.bookService.GetBookDataById(tempBookTask.bookId);
if (book == null) {
throw new Error
}
} else {
throw new Error("未知的操作类型");
}
if (bookTasks.length <= 0) {
throw new Error("没有找到小说批次任务,请检查")
}
// 判断是不是生成单个,每次生成都要修改一下配置文件
// TODO 后面这个地方要修改
// 调用生成草稿的方法
let result = []
for (let i = 0; i < bookTasks.length; i++) {
const element = bookTasks[i];
await this.GenerateConfigFile(book, element);
// 数据处理完毕,开始输出
let clipDraft = new ClipDraft(global, [element.name, {
srt_path: element.srtPath,
audio_path: element.audioPath,
draft_srt_style: element.draftSrtStyle ? element.draftSrtStyle : "0",
background_music: element.backgroundMusic,
friendly_reminder: element.friendlyReminder ? element.friendlyReminder : "0",
}])
let res = await clipDraft.addDraft();
if (res.code == 0) {
throw new Error(res.message)
}
result.push(res.draft_name);
}
return successMessage(result, `${result.join('\n')} ${'\n'} 剪映草稿添加成功`, "BookTask_AddJianyingDraft")
} catch (error) {
return errorMessage('添加剪映草稿失败,错误信息如下:' + error.toString(), "BookTask_AddJianyingDraft");
}
}
}

View File

@ -0,0 +1,97 @@
import { TagDefineType } from "../../../define/enum/bookEnum";
import { ImageStyleDefine } from "../../../define/iamgeStyleDefine";
import { Book } from "../../../model/book";
import { GeneralResponse } from "../../../model/generalResponse";
import { TagCustomize } from "../../Original/TagCustomize";
import { errorMessage, successMessage } from "../../Public/generalTools";
import { BookTaskService } from "../../../define/db/service/Book/bookTaskService";
export class ImageStyle {
tagCustomize: TagCustomize
bookTaskService: BookTaskService
constructor() {
this.tagCustomize = new TagCustomize(global)
}
async InitService() {
if (!this.bookTaskService) {
this.bookTaskService = await BookTaskService.getInstance()
}
}
/**
*
* @param imageStyle ID
* @param customizeImageStyle ID
*/
async GetAllImageStyleList(imageStyle: string[], customizeImageStyle: string[]): Promise<any[]> {
try {
// 拿到所有的风格
let styleArr = [];
if (imageStyle && imageStyle.length > 0) {
let imageStyleRes = ImageStyleDefine.getImageStyleInfomation(JSON.stringify(imageStyle)) // 获取软件默认的数据
if (imageStyleRes.code == 0) {
throw new Error(imageStyleRes.message)
}
styleArr.push(...imageStyleRes.data);
}
// 获取自定义的风格
if (customizeImageStyle && customizeImageStyle.length > 0) {
let customizeImageStyleRes = await this.tagCustomize.GetTagDataByTypeAndProperty(['dynamic', 'style_tags']) // 获取自定义的数据
if (customizeImageStyleRes.code == 0) {
throw new Error(customizeImageStyleRes.message)
}
let customizeImageStyleArr = customizeImageStyleRes.data
for (let i = 0; i < customizeImageStyle.length; i++) {
const element = customizeImageStyle[i];
let style = customizeImageStyleArr.find(x => x.key == element)
if (style) {
styleArr.push(style)
}
}
}
return styleArr
} catch (error) {
throw error
}
}
/**
*
* @param styleList
* @param bookTaskId ID
* @returns
*/
async SaveImageStyle(styleList: Book.BookStyle[], bookTaskId: string): Promise<GeneralResponse.ErrorItem | GeneralResponse.SuccessItem> {
try {
await this.InitService()
console.log(styleList)
let imageStyle = [] as string[]
let customizeImageStyle = [] as string[]
styleList.forEach((style) => {
if (style.type == TagDefineType.STYLE_MAIN) {
customizeImageStyle.push(style.key)
} else {
imageStyle.push(style.id)
}
})
// 保存数据
this.bookTaskService.UpdetedBookTaskData(bookTaskId, {
imageStyle: imageStyle,
customizeImageStyle: customizeImageStyle
})
return successMessage({
imageStyle: imageStyle,
customizeImageStyle: customizeImageStyle
}, '保存小说风格成功', 'BookBasic_SaveImageStyle')
} catch (error) {
return errorMessage('保存小说风格失败,错误信息如下:' + error.message, 'BookBasic_SaveImageStyle')
}
}
}

771
src/main/Service/MJ/mj.ts Normal file
View File

@ -0,0 +1,771 @@
import { isEmpty, join } from "lodash";
import { Book } from "../../../model/book";
import { checkStringValueAddPrefix, checkStringValueAddSuffix, errorMessage, successMessage } from "../../Public/generalTools";
import { BookBackTaskListService } from "../../../define/db/service/Book/bookBackTaskListService";
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 MJApi from "./mjApi"
import { BookBackTaskStatus, BookBackTaskType, BookTaskStatus, BookType, DialogType, MJAction, MergeType, OperateBookType, TaskExecuteType } from "../../../define/enum/bookEnum";
import { DEFINE_STRING } from "../../../define/define_string";
import { MJ } from "../../../model/mj";
import { MJRespoonseType } from "../../../define/enum/mjEnum";
import { MJSetting } from "../../../model/Setting/mjSetting";
import { GeneralResponse } from "../../../model/generalResponse"
import { LoggerStatus, ResponseMessageType } from "../../../define/enum/softwareEnum";
import { BookTaskService } from "../../../define/db/service/Book/bookTaskService";
import { ReverseBook } from "../Book/ReverseBook";
import { ImageStyle } from "../Book/imageStyle";
import { TaskScheduler } from "../taskScheduler";
import { BookService } from "../../../define/db/service/Book/bookService";
import { Tools } from "../../../main/tools"
import { MJSettingService } from "../../../define/db/service/SoftWare/mjSettingService";
import path from "path"
const { v4: uuidv4 } = require('uuid')
import fs from "fs"
const fspromise = fs.promises
export class MJOpt {
bookBackTaskList: BookBackTaskListService
bookTaskDetail: BookTaskDetailService
reverseBook: ReverseBook;
bookTaskService: BookTaskService
mjApi: MJApi;
mjSetting: MJSetting.MjSetting
imageStyle: ImageStyle;
taskScheduler: TaskScheduler;
bookService: BookService
tools: Tools;
mjSettingService: MJSettingService;
constructor() {
this.imageStyle = new ImageStyle()
this.taskScheduler = new TaskScheduler()
this.tools = new Tools()
}
async InitService() {
if (!this.reverseBook) {
this.reverseBook = new ReverseBook()
}
if (!this.bookBackTaskList) {
this.bookBackTaskList = await BookBackTaskListService.getInstance()
}
if (!this.bookTaskDetail) {
this.bookTaskDetail = await BookTaskDetailService.getInstance()
}
if (!this.mjApi) {
this.mjApi = new MJApi()
}
if (!this.bookTaskService) {
this.bookTaskService = await BookTaskService.getInstance()
}
if (!this.bookService) {
this.bookService = await BookService.getInstance()
}
if (!this.mjSettingService) {
this.mjSettingService = await MJSettingService.getInstance()
}
this.mjSetting = await this.mjApi.InitMJSetting()
}
/**
* MJ的数据到前端界面
* @param {*} data
*/
sendChangeMessage(data: GeneralResponse.MessageResponse, message_name = DEFINE_STRING.BOOK.MAIN_DATA_RETURN) {
global.newWindow[0].win.webContents.send(message_name, data)
}
//#region 选择反推的提示词相关
/**
* MJ反推出来的数据GPT提示词中
* @param bookId ID
* @param bookTaskId ID
* @param index
* @returns
*/
async ReversePromptToGptPrompt(bookId: string, bookTaskId: string, index: number): Promise<GeneralResponse.ErrorItem | GeneralResponse.SuccessItem> {
try {
await this.InitService();
let bookTaskDetails = this.bookTaskDetail.GetBookTaskData({
bookId: bookId,
bookTaskId: bookTaskId
}).data as Book.SelectBookTaskDetail[]
if (bookTaskDetails.length <= 0) {
throw new Error("没有找到对应的分镜数据")
}
// 开始修改数据
for (let i = 0; i < bookTaskDetails.length; i++) {
const element = bookTaskDetails[i];
let reversePrompts = element.reversePrompt
let reversePrompt = reversePrompts[index - 1]
let gptPrompt = undefined
if (!isEmpty(reversePrompt.promptCN) && reversePrompt.promptCN != "") {
gptPrompt = reversePrompt.promptCN
}
if (!gptPrompt) {
gptPrompt = reversePrompt.prompt
}
// 开始修改
this.bookTaskDetail.UpdateBookTaskDetail(element.id, {
gptPrompt: gptPrompt
})
}
// 全部修改完毕,将修改后的数据返回
let res = await this.reverseBook.GetBookTaskDetail(bookTaskId);
if (res.code == 0) {
throw new Error(res.message)
}
return successMessage(res.data, "反推数据写出成功", "ReverseBook_ReversePromptToGptPrompt")
} catch (error) {
return errorMessage("反推数据写出失败,错误信息如下:" + error.message, "ReverseBook_ReversePromptToGptPrompt")
}
}
/**
* MJ反推GPT中
* @param bookTaskDetailId
* @param index
*/
async SingleReverseToGptPrompt(bookTaskDetailId: string, index: number): Promise<GeneralResponse.ErrorItem | GeneralResponse.SuccessItem> {
try {
await this.InitService();
let bookTaskDetail = this.bookTaskDetail.GetBookTaskDetailDataById(bookTaskDetailId)
if (bookTaskDetail == null) {
throw new Error("没有找到对应的数据")
}
let reversePrompts = bookTaskDetail.reversePrompt
if (!reversePrompts || reversePrompts.length <= 0) {
throw new Error("没有找到对应的反推提示词数据")
}
let reversePrompt = reversePrompts[index]
let gptPrompt = reversePrompt.promptCN ? reversePrompt.promptCN : reversePrompt.prompt
// 开始修改
this.bookTaskDetail.UpdateBookTaskDetail(bookTaskDetailId, {
gptPrompt: gptPrompt
})
// 保存完毕
return successMessage(gptPrompt, "反推数据写出成功", "ReverseBook_ReversePromptToGptPrompt")
} catch (error) {
return errorMessage("反推数据写出失败,错误信息如下:" + error.message, "ReverseBook_ReversePromptToGptPrompt")
}
}
//#endregion
//#region MJ反推相关
/**
*
* @param task
* @param reqRes
*/
async fetchWithRetry(task: TaskModal.Task, reqRes: string) {
while (true) {
try {
// 执行你的操作
let task_res = await this.mjApi.GetMJAPITaskById(reqRes, task.id);
// 判断他的状态是不是成功
if (task_res.code == 0) {
// 反推失败
this.bookTaskDetail.UpdateBookTaskDetail(task.bookTaskDetailId, {
status: BookTaskStatus.REVERSE_FAIL
});
this.bookBackTaskList.UpdateTaskStatus({
id: task.id,
status: BookBackTaskStatus.FAIL,
errorMessage: task_res.message
});
throw new Error(`${task_res.message}`);
} else {
if (task_res.progress == 100) {
task_res.type == MJRespoonseType.FINISHED;
this.bookBackTaskList.UpdateTaskStatus({
id: task.id,
status: BookBackTaskStatus.DONE
});
// 这边还要再处理一下数据,将反推获取的数据进行切割处理
let reversePrompt = [];
if (task_res.prompt != undefined && task_res.prompt != "" && task_res.prompt != null) {
let string_res = task_res.prompt.split(/(?=\d⃣)/).map(part => part.replace(/^\d⃣\s*/, '').trim());
reversePrompt = string_res.map((item) => {
return {
id: uuidv4(),
bookTaskDetailId: task.bookTaskDetailId,
prompt: item,
promptCN: item,
isSelect: false
};
});
}
this.bookTaskDetail.UpdateBookTaskDetail(task.bookTaskDetailId, {
status: BookTaskStatus.REVERSE_DONE,
reversePrompt: reversePrompt
});
task_res.prompt = JSON.stringify(reversePrompt);
task_res.id = task.bookTaskDetailId;
this.sendChangeMessage({
code: 1,
type: ResponseMessageType.MJ_REVERSE,
id: task.bookTaskDetailId,
data: task_res
});
break;
} else {
// 当获取的图片的进度小于100的时候等待5秒继续监听
await new Promise(resolve => setTimeout(resolve, 5000));
}
}
task_res.id = task.bookTaskDetailId;
this.sendChangeMessage({
code: 1,
type: ResponseMessageType.MJ_REVERSE,
id: task.bookTaskDetailId,
data: task_res
});
} catch (error) {
throw error;
}
}
};
/**
* MJ反推
* @param task
*/
async MJImage2Text(task: TaskModal.Task): Promise<GeneralResponse.SuccessItem | GeneralResponse.ErrorItem> {
try {
if (isEmpty(task.bookTaskDetailId)) {
throw new Error("MJ反推没有找到对应的分镜信息")
}
await this.InitService()
let bookTaskDetail = this.bookTaskDetail.GetBookTaskDetailDataById(task.bookTaskDetailId);
let oldImagePath = bookTaskDetail.oldImage
if (isEmpty(oldImagePath)) {
throw new Error(`${bookTaskDetail.name} 没有需要反推的图片`);
}
oldImagePath = JoinPath(define.project_path, oldImagePath)
let imageBase64 = await GetImageBase64(oldImagePath)
// 这个就是任务ID
let reqRes = await this.mjApi.SubmitMJDescribe({
image: imageBase64,
taskId: task.id
})
if (reqRes == '23') {
// 任务队列过多,重新提交排队
this.bookBackTaskList.UpdateTaskStatus({
id: task.id,
status: BookBackTaskStatus.RECONNECT,
})
// throw new Error(`任务队列过多,${task.bookTaskDetailId} 重新提交排队`);
return;
}
this.bookTaskDetail.UpdateBookTaskDetail(task.bookTaskDetailId, {
status: BookTaskStatus.REVERSE
})
this.sendChangeMessage({
code: 1,
type: ResponseMessageType.MJ_REVERSE,
id: task.bookTaskDetailId,
data: {
code: 1,
type: MJRespoonseType.UPDATED,
mjType: MJAction.DESCRIBE,
category: this.mjSetting.type,
message_id: reqRes,
id: task.bookTaskDetailId,
progress: 0,
status: "success"
} as MJ.MJResponseToFront
})
await this.fetchWithRetry(task, reqRes);
} catch (error) {
console.log(error.toString())
let errorMsg = "MJ反推失败失败信息如下" + error.toString()
this.bookBackTaskList.UpdateTaskStatus({
id: task.id,
status: BookBackTaskStatus.FAIL,
errorMessage: errorMsg
})
this.sendChangeMessage({
code: 0,
id: task.bookTaskDetailId,
type: ResponseMessageType.MJ_REVERSE,
dialogType: DialogType.NOTIFICATION,
message: errorMsg,
data: {
code: 0,
type: MJRespoonseType.UPDATED,
mjType: MJAction.DESCRIBE,
category: this.mjSetting.type,
message_id: undefined,
id: task.bookTaskDetailId,
progress: 0,
message: error.toString(),
status: "failure"
}
})
return errorMessage(errorMsg, "MJReverse_MJImage2Text")
}
}
//#endregion
//#region 合并提示词相关
/**
* MJ
* @param id ID
* @param mergeType
*/
async MergePrompt(id: string, mergeType: MergeType): Promise<GeneralResponse.ErrorItem | GeneralResponse.SuccessItem> {
try {
await this.InitService()
let bookTaskDetail = undefined as Book.SelectBookTaskDetail[];
let bookTask = undefined as Book.SelectBookTask;
if (mergeType == MergeType.BOOKTASK) {
bookTaskDetail = this.bookTaskDetail.GetBookTaskData({
bookTaskId: id
}).data
bookTask = this.bookTaskService.GetBookTaskDataById(id);
} else if (mergeType == MergeType.BOOKTASKDETAIL) {
bookTaskDetail = [this.bookTaskDetail.GetBookTaskDetailDataById(id)]
bookTask = this.bookTaskService.GetBookTaskDataById(bookTaskDetail[0].bookTaskId);
} else {
throw new Error("未知的合并类型")
}
// 获取合并的排序
let imageBaseSetting = JSON.parse(await fspromise.readFile(define.img_base, 'utf-8')); // 没有就直接报错
let promptSort = imageBaseSetting.prompt_sort; // 没有就直接报错
if (!promptSort) {
throw new Error("未找到提示词排序,请先设置")
}
// let suffixParam = imageBaseSetting.mj_config.image_suffix ; // 没有就直接报错
let mjSettingDb = this.mjSettingService.GetMjSetting({}).data
if (mjSettingDb.length <= 0) {
throw new Error("请先添加MJ配置")
}
let suffixParam = mjSettingDb[0].imageSuffix
// let styleString = '';
// 拿到所有的风格
let styleArr = await this.imageStyle.GetAllImageStyleList(bookTask.imageStyle, bookTask.customizeImageStyle);
let result = []
for (let i = 0; i < bookTaskDetail.length; i++) {
const element = bookTaskDetail[i];
let promptStr = '';
for (let i = 0; i < promptSort.length; i++) {
const element = promptSort[i];
promptStr += `${'${' + element.value + '}'} `
}
console.log(promptStr)
// TODO 后面需要完善人物和场景的提示词
let characterString = '' // 人物
let sceneString = "" // 场景
// 获取当前的自定义风格的垫图字符串
let styleString = ""
let style_url = ''
let sw = undefined
styleArr.forEach((item) => {
if (item.type && item.type == 'style_main') {
if (sw == undefined) {
sw = item.sref_sw
}
if (!isEmpty(item.image_url)) {
let url = item.image_url ? item.image_url : ''
style_url += ' ' + url
}
if (item.prompt) {
styleString += item.prompt + ','
}
} else {
styleString += item.english_style + ','
}
})
style_url = checkStringValueAddPrefix(style_url, '--sref ')
if (sw != undefined) {
style_url = checkStringValueAddSuffix(style_url, ` --sw ${sw}`)
}
let cref_url = ''
promptStr = promptStr.replace('${style}', styleString)
promptStr = promptStr.replace('${character}', characterString)
promptStr = promptStr.replace('${scene}', sceneString)
promptStr = promptStr.replace(
'${prompt}',
checkStringValueAddSuffix(element.gptPrompt, ',')
)
// 判断是不是需要加前后缀
if (bookTask.prefixPrompt) {
promptStr = checkStringValueAddSuffix(bookTask.prefixPrompt, ',') + promptStr
}
if (bookTask.prefixPrompt) {
promptStr = checkStringValueAddSuffix(promptStr, ',') + bookTask.prefixPrompt
}
promptStr = ' ' + promptStr;
promptStr += ` ${cref_url} ${style_url}${suffixParam}`
// 修改数据库数据
this.bookTaskDetail.UpdateBookTaskDetail(element.id, {
prompt: promptStr
})
// 写回数据
result.push({
id: element.id,
prompt: promptStr
})
}
return successMessage(result, "MJ模式合并数据成功", "MJOpt_MergePrompt")
} catch (error) {
return errorMessage("MJ合并提示词失败错误信息如下" + error.message, "MJOpt_MergePrompt")
}
}
//#endregion
//#region MJ生成图片相关
/**
*
* @param id ID
* @param operateBookType
*/
async AddMJGenerateImageTask(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;
if (operateBookType == OperateBookType.BOOKTASK) {
bookTaskDetail = this.bookTaskDetail.GetBookTaskData({
bookTaskId: id
}).data
// bookTask = this.bookTaskService.GetBookTaskDataById(id);
} else if (operateBookType == OperateBookType.BOOKTASKDETAIL) {
bookTaskDetail = [this.bookTaskDetail.GetBookTaskDetailDataById(id)]
// bookTask = this.bookTaskService.GetBookTaskDataById(bookTaskDetail[0].bookTaskId);
} else if (operateBookType == OperateBookType.UNDERBOOKTASK) {
let thisBookTaskDetail = this.bookTaskDetail.GetBookTaskDetailDataById(id);
if (thisBookTaskDetail == null) {
throw new Error("没有找到对应的数据")
}
// 获取批次的所有数据
bookTaskDetail = this.bookTaskDetail.GetBookTaskData({
bookTaskId: thisBookTaskDetail.bookTaskId
}).data
// bookTask = this.bookTaskService.GetBookTaskDataById(thisBookTaskDetail.bookTaskId);
bookTaskDetail = bookTaskDetail.filter((item) => item.no >= thisBookTaskDetail.no) // 需要包含自己
}
else {
throw new Error("MJOpt_AddGenerateImageTask未知的操作类型")
}
// 将被锁定的数据过滤掉
bookTaskDetail = bookTaskDetail.filter((item) => item.imageLock == false)
if (bookTaskDetail.length <= 0) {
throw new Error("没有找到可以生成图片的分镜,可能是已经被锁定")
}
// 将任务添加到队列中
for (let i = 0; i < bookTaskDetail.length; i++) {
const element = bookTaskDetail[i];
let taskRes = await this.bookBackTaskList.AddBookBackTask(element.bookId, BookBackTaskType.MJ_IMAGE, TaskExecuteType.AUTO, element.bookTaskId, element.id
);
if (taskRes.code == 0) {
throw new Error(taskRes.message)
}
// 添加返回日志
await this.taskScheduler.AddLogToDB(element.bookId, BookBackTaskType.MJ_IMAGE, `添加 ${element.name} MJ生成视频任务成功`, element.bookTaskId, LoggerStatus.SUCCESS)
}
// 全部完毕
return successMessage(null, "MJ添加生成图片任务成功", "MJOpt_AddGenerateImageTask")
} catch (error) {
return errorMessage("MJ添加生成图片任务失败错误信息如下" + error.message, "MJOpt_AddGenerateImageTask")
}
}
/**
*
* @param task
* @param reqRes
*/
async FetchImageTask(task: TaskModal.Task, reqRes: string, book: Book.SelectBook, bookTask: Book.SelectBookTask, bookTaskDetail: Book.SelectBookTaskDetail) {
while (true) {
try {
// 执行你的操作
let task_res = await this.mjApi.GetMJAPITaskById(reqRes, task.id);
task_res.id = task.bookTaskDetailId;
// 判断他的状态是不是成功
if (task_res.code == 0) {
// 生图失败
this.bookTaskDetail.UpdateBookTaskDetail(task.bookTaskDetailId, {
status: BookTaskStatus.IMAGE_FAIL,
});
this.bookTaskDetail.UpdateBookTaskDetailMjMessage(task.bookTaskDetailId, {
mjApiUrl: this.mjApi.imagineUrl,
progress: 0,
category: this.mjApi.mjSetting.type,
imageClick: task_res.image_click,
imageShow: task_res.image_show,
messageId: task_res.message_id,
action: MJAction.IMAGINE,
status: 'error',
message: task_res.message
})
this.bookBackTaskList.UpdateTaskStatus({
id: task.id,
status: BookBackTaskStatus.FAIL,
errorMessage: task_res.message
});
this.sendChangeMessage({
code: 0,
type: ResponseMessageType.MJ_IMAGE,
id: task.bookTaskDetailId,
data: {
...task_res,
status: "error"
},
message: task_res.message
});
return;
// throw new Error(`${task_res.message}`);
} else {
if (task_res.progress == 100) {
task_res.type == MJRespoonseType.FINISHED;
console.log(task.id, "22222")
this.bookBackTaskList.UpdateTaskStatus({
id: task.id,
status: BookBackTaskStatus.DONE
});
// 下载图片
let imagePath = path.join(book.bookFolderPath, `data\\MJOriginalImage\\${task_res.message_id}.png`);
await CheckFolderExistsOrCreate(path.dirname(imagePath))
await this.tools.downloadFileUrl(task_res.image_click, imagePath)
// 进行图片裁剪
let imageRes = await ImageSplit(imagePath, bookTaskDetail.name, path.join(book.bookFolderPath, 'data\\MJOriginalImage'));
if (imageRes && imageRes.length < 4) {
throw new Error("图片裁剪失败")
}
// 修改数据库数据,将图片保存到对应的文件夹中
let firstImage = imageRes[0];
if (book.type == BookType.ORIGINAL) {
await CopyFileOrFolder(firstImage, path.join(book.bookFolderPath, `tmp\\input\\${bookTaskDetail.name}.png`));
}
let out_file = path.join(bookTask.imageFolder, `${bookTaskDetail.name}.png`)
await CopyFileOrFolder(firstImage, out_file);
task_res.outImagePath = firstImage;
task_res.subImagePath = imageRes;
task_res.id = task.bookTaskDetailId;
// 修改分镜的数据
this.bookTaskDetail.UpdateBookTaskDetail(task.bookTaskDetailId, {
outImagePath: path.relative(define.project_path, out_file),
subImagePath: imageRes.map((item) => path.relative(define.project_path, item))
})
this.bookTaskDetail.UpdateBookTaskDetailMjMessage(task.bookTaskDetailId, {
mjApiUrl: this.mjApi.imagineUrl,
progress: 100,
category: this.mjApi.mjSetting.type,
imageClick: task_res.image_click,
imageShow: task_res.image_show,
messageId: task_res.message_id,
action: MJAction.IMAGINE,
status: task_res.status,
})
this.sendChangeMessage({
code: 1,
type: ResponseMessageType.MJ_IMAGE,
id: task.bookTaskDetailId,
data: task_res
});
break;
}
}
// 这边也要修改数据
task_res.id = task.bookTaskDetailId;
this.bookTaskDetail.UpdateBookTaskDetailMjMessage(task.bookTaskDetailId, {
mjApiUrl: this.mjApi.imagineUrl,
progress: task_res.progress,
category: this.mjApi.mjSetting.type,
imageClick: task_res.image_click,
imageShow: task_res.image_show,
messageId: task_res.message_id,
action: MJAction.IMAGINE,
status: task_res.status,
message: task_res.message
})
task_res.outImagePath = task_res.image_path;
this.sendChangeMessage({
code: 1,
type: ResponseMessageType.MJ_IMAGE,
id: task.bookTaskDetailId,
data: task_res
});
// 当获取的图片的进度小于100的时候等待5秒继续监听
await new Promise(resolve => setTimeout(resolve, 5000));
} catch (error) {
throw error;
}
}
};
/**
* MJ生成图片
* @param task
*/
async MJImagine(task: TaskModal.Task): Promise<GeneralResponse.SuccessItem | GeneralResponse.ErrorItem> {
try {
if (isEmpty(task.bookTaskDetailId)) {
throw new Error("MJ出图没有找到对应的分镜信息")
}
await this.InitService()
let bookTaskDetail = this.bookTaskDetail.GetBookTaskDetailDataById(task.bookTaskDetailId);
if (bookTaskDetail == null) {
throw new Error("没有找到对应的分镜信息")
}
let book = this.bookService.GetBookDataById(bookTaskDetail.bookId)
if (book == null) {
throw new Error("没有找到对应的小说信息")
}
let bookTask = this.bookTaskService.GetBookTaskDataById(bookTaskDetail.bookTaskId)
if (bookTask == null) {
throw new Error("没有找到对应的任务信息")
}
let prompt = bookTaskDetail.prompt
if (isEmpty(prompt)) {
throw new Error(`${bookTaskDetail.name} 没有找到对应的提示词`)
}
// 这个就是任务ID
let reqRes = await this.mjApi.SubmitMJImagineAPI(task.id, prompt)
if (reqRes == '23') {
console.log(task.id, "33333")
// 任务队列过多,重新提交排队
this.bookBackTaskList.UpdateTaskStatus({
id: task.id,
status: BookBackTaskStatus.RECONNECT,
})
this.sendChangeMessage({
code: 1,
type: ResponseMessageType.MJ_IMAGE,
id: task.bookTaskDetailId,
data: {
code: 1,
type: MJRespoonseType.UPDATED,
mjType: MJAction.IMAGINE,
category: this.mjSetting.type,
message_id: '',
id: task.bookTaskDetailId,
progress: 0,
status: "re_connect"
} as MJ.MJResponseToFront
})
this.bookTaskDetail.UpdateBookTaskDetailMjMessage(task.bookTaskDetailId, {
mjApiUrl: this.mjApi.imagineUrl,
progress: 0,
category: this.mjApi.mjSetting.type,
imageClick: "",
imageShow: "",
messageId: "",
action: MJAction.IMAGINE,
status: "re_connect",
})
// throw new Error(`任务队列过多,${task.bookTaskDetailId} 重新提交排队`);
return;
}
this.bookTaskDetail.UpdateBookTaskDetail(task.bookTaskDetailId, {
status: BookTaskStatus.IMAGE
})
this.bookBackTaskList.UpdateTaskStatus({
id: task.id,
status: BookBackTaskStatus.RUNNING
})
this.sendChangeMessage({
code: 1,
type: ResponseMessageType.MJ_IMAGE,
id: task.bookTaskDetailId,
data: {
code: 1,
type: MJRespoonseType.UPDATED,
mjType: MJAction.IMAGINE,
category: this.mjSetting.type,
message_id: reqRes,
id: task.bookTaskDetailId,
progress: 0,
status: "submited"
} as MJ.MJResponseToFront
})
await this.FetchImageTask(task, reqRes, book, bookTask, bookTaskDetail);
} catch (error) {
console.log(error.toString())
let errorMsg = "MJ生图失败失败信息如下" + error.toString()
console.log(task.id, "44444")
this.bookBackTaskList.UpdateTaskStatus({
id: task.id,
status: BookBackTaskStatus.FAIL,
errorMessage: errorMsg
})
this.sendChangeMessage({
code: 0,
id: task.bookTaskDetailId,
type: ResponseMessageType.MJ_IMAGE,
dialogType: DialogType.NOTIFICATION,
message: errorMsg,
data: {
code: 0,
type: MJRespoonseType.UPDATED,
mjType: MJAction.IMAGINE,
category: this.mjSetting.type,
message_id: undefined,
id: task.bookTaskDetailId,
progress: 0,
message: error.toString(),
status: "error"
}
})
this.bookTaskDetail.UpdateBookTaskDetailMjMessage(task.bookTaskDetailId, {
mjApiUrl: this.mjApi.imagineUrl,
progress: 0,
category: this.mjApi.mjSetting.type,
imageClick: "",
imageShow: "",
messageId: "",
action: MJAction.IMAGINE,
status: "error",
message: error.toString()
})
return errorMessage(errorMsg, "MJReverse_MJImage2Text")
}
}
//#endregion
}

View File

@ -0,0 +1,289 @@
import axios from "axios"
import { define } from "../../../define/define"
import { GetImageBase64 } from "../../../define/Tools/image"
import { MJImageType, MJRespoonseType, MJSpeed } from "../../../define/enum/mjEnum"
import { MJSettingService } from "../../../define/db/service/SoftWare/mjSettingService"
import { BookBackTaskListService } from "../../../define/db/service/Book/bookBackTaskListService"
import { BookBackTaskStatus } from "../../../define/enum/bookEnum"
import { MJSetting } from "../../../model/Setting/mjSetting"
import { GPT } from "../../Public/GPT"
import { MJ } from "../../../model/mj"
import { LaiAPIType } from "../../../define/enum/softwareEnum"
/**
* MJ的API类
*/
class MJApi {
mjSetting: MJSetting.MjSetting
bootType: string
imagineUrl: string
fetchTaskUrl: string
describeUrl: string
constructor() {
this.bootType = "MID_JOURNEY"
}
async InitMJSetting(): Promise<MJSetting.MjSetting> {
// 获取MJ配置从数据库中
let _mjSettingService = await MJSettingService.getInstance()
let mjSettings = _mjSettingService.GetMJSettingTreeData()
if (mjSettings.code == 0) {
throw new Error(mjSettings.message)
}
this.mjSetting = mjSettings.data
this.bootType = this.mjSetting.selectRobot == "niji" ? "NIJI_JOURNEY" : "MID_JOURNEY"
if (this.mjSetting.type == MJImageType.REMOTE_MJ) {
this.imagineUrl = define.remotemj_api + 'mj/submit/imagine'
this.describeUrl = define.remotemj_api + 'mj/submit/describe'
this.fetchTaskUrl = define.remotemj_api + 'mj/task/${id}/fetch'
} else {
if (global.config.laiApiSelect == LaiAPIType.HK_PROXY) {
this.imagineUrl = define.hkServerUrl + 'mj/submit/imagine'
this.describeUrl = define.hkServerUrl + 'mj/submit/describe'
this.fetchTaskUrl = define.hkServerUrl + 'mj/task/${id}/fetch'
} else if (global.config.laiApiSelect == LaiAPIType.BAK_MAIN) {
this.imagineUrl = define.bakServerUrl + 'mj/submit/imagine'
this.describeUrl = define.bakServerUrl + 'mj/submit/describe'
this.fetchTaskUrl = define.bakServerUrl + 'mj/task/${id}/fetch'
} else {
let gpt = new GPT()
let mj_api = (await gpt.GetGPTBusinessOption('all', (value) => value.mj_url)).data
let mj_api_url_index = mj_api.findIndex((item) => item.value == this.mjSetting.apiSetting.mjApiUrl)
if (mj_api_url_index == -1) {
throw new Error('没有找到对应的MJ API的配置请先检查配置')
}
this.imagineUrl = mj_api[mj_api_url_index].mj_url.imagine;
this.fetchTaskUrl = mj_api[mj_api_url_index].mj_url.once_get_task;
this.describeUrl = mj_api[mj_api_url_index].mj_url.imagine.replace("imagine", "describe");
}
}
return mjSettings.data
}
//#region 获取对应的任务通过ID
/**
* MJ的API任务
* @param taskId
*
*/
async GetMJAPITaskById(taskId: string, backTaskId: string) {
try {
await this.InitMJSetting();
let url = this.fetchTaskUrl.replace("${id}", taskId)
let headers = undefined
if (this.mjSetting.type == MJImageType.REMOTE_MJ) {
headers = {
'mj-api-secret': define.API
}
} else {
headers = {
Authorization: this.mjSetting.apiSetting.apiKey
}
}
let res = await axios.get(url, {
headers: headers
})
let progress = res.data.progress && res.data.progress.length > 0
? parseInt(res.data.progress.slice(0, -1))
: 0
let status = res.data.status.toLowerCase()
let code = status == 'failure' || status == 'cancel' ? 0 : 1
let _bookBackTaskListService = await BookBackTaskListService.getInstance()
// 失败
if (code == 0) {
_bookBackTaskListService.UpdateTaskStatus({
id: backTaskId,
status: BookBackTaskStatus.FAIL,
errorMessage: res.data.message
})
}
let resObj = {
type: MJRespoonseType.UPDATED,
progress: isNaN(progress) ? 0 : progress,
category: this.mjSetting.type,
image_click: res.data.imageUrl,
image_show: res.data.imageUrl,
image_path: res.data.imageUrl,
message_id: taskId,
status: status,
code: code,
prompt: res.data.prompt == "" ? res.data.promptEn : res.data.prompt,
message: res.data.failReason,
mj_api_url: this.fetchTaskUrl,
} as MJ.MJResponseToFront
return resObj
} catch (error) {
throw error
}
}
//#endregion
//#region MJ反推相关操作
/**
* MJ的反推
*/
async SubmitMJDescribe(param: MJ.APIDescribeParams): Promise<string> {
await this.InitMJSetting()
let res = undefined
switch (this.mjSetting.type) {
case MJImageType.REMOTE_MJ:
case MJImageType.API_MJ:
res = await this.SubmitMJDescribeAPI(param)
break
default:
throw new Error("MJ出图的类型不支持")
}
return res
}
/**
* API和代理模式的反推
* @param param
* @returns ID
*/
async SubmitMJDescribeAPI(param: MJ.APIDescribeParams): Promise<string> {
let _bookBackTaskListService = await BookBackTaskListService.getInstance()
// 提交API的反推
let data = {
botType: this.bootType,
base64: param.image,
accountFilter: {
modes: [this.mjSetting.apiSetting.mjSpeed == MJSpeed.FAST ? "FAST" : "RELAX"],
remark: global.machineId
},
}
let config = {
headers: {
'Content-Type': 'application/json'
}
}
if (this.mjSetting.type == MJImageType.REMOTE_MJ) {
config.headers["mj-api-secret"] = define.API;
} else {
delete data.accountFilter.remark
config.headers["Authorization"] = this.mjSetting.apiSetting.apiKey;
}
// 开始请求
let res = await axios.post(this.describeUrl, data, config)
// 某些API的返回的code为23表示队列已满需要重新请求
if (res.data.code == 23) {
_bookBackTaskListService.UpdateTaskStatus({
id: param.taskId,
status: BookBackTaskStatus.RECONNECT
})
return '23'
}
if (res.data.code != 1 && res.data.code != 22) {
_bookBackTaskListService.UpdateTaskStatus({
id: param.taskId,
status: BookBackTaskStatus.FAIL,
errorMessage: res.data.description
})
throw new Error(res.data.description)
}
_bookBackTaskListService.UpdateTaskStatus({
id: param.taskId,
status: BookBackTaskStatus.RUNNING
})
return res.data.result as string
}
//#endregion
//#region 提交MJ生图任务
/**
* MJ生图任务
* @param param
* @returns
*/
async SubmitMJImagine(taskId: string, prompt: string): Promise<string> {
await this.InitMJSetting()
let res = undefined
switch (this.mjSetting.type) {
case MJImageType.REMOTE_MJ:
case MJImageType.API_MJ:
res = await this.SubmitMJImagineAPI(taskId, prompt)
break
default:
throw new Error("MJ出图的类型不支持")
}
return res
}
/**
* MJ API/
* @param taskId
*/
async SubmitMJImagineAPI(taskId: string, prompt: string): Promise<string> {
let _bookBackTaskListService = await BookBackTaskListService.getInstance()
// 提交API的出图任务
let data = {
botType: this.bootType,
prompt: prompt,
accountFilter: {
modes: [this.mjSetting.apiSetting.mjSpeed == MJSpeed.FAST ? "FAST" : "RELAX"],
remark: global.machineId
},
}
let config = {
headers: {
'Content-Type': 'application/json'
}
}
if (this.mjSetting.type == MJImageType.REMOTE_MJ) {
config.headers["mj-api-secret"] = define.API;
} else {
delete data.accountFilter.remark
config.headers["Authorization"] = this.mjSetting.apiSetting.apiKey;
}
// 开始请求
let res = await axios.post(this.imagineUrl, data, config)
// 某些API的返回的code为23表示队列已满需要重新请求
if (res.data.code == 23) {
_bookBackTaskListService.UpdateTaskStatus({
id: taskId,
status: BookBackTaskStatus.RECONNECT
})
return '23'
}
if (res.data.code != 1 && res.data.code != 22) {
_bookBackTaskListService.UpdateTaskStatus({
id: taskId,
status: BookBackTaskStatus.FAIL,
errorMessage: res.data.description
})
throw new Error(res.data.description)
}
_bookBackTaskListService.UpdateTaskStatus({
id: taskId,
status: BookBackTaskStatus.RUNNING
})
return res.data.result as string
}
//#endregion
}
export default MJApi

View File

@ -0,0 +1,100 @@
// import { isEmpty } from "lodash";
// import { BookTaskDetailService } from "../../../define/db/service/Book/bookTaskDetailService";
// import { Book } from "../../../model/book";
// import { GeneralResponse } from "../../../model/generalResponse";
// import { ReverseBook } from "../Book/ReverseBook";
// import { errorMessage, successMessage } from "../../Public/generalTools";
// export class MJReverse {
// bookTaskDetailService: BookTaskDetailService;
// reverseBook: ReverseBook;
// constructor() { }
// private async InitService() {
// // 初始化一些服务
// if (!this.bookTaskDetailService) {
// this.bookTaskDetailService = await BookTaskDetailService.getInstance()
// }
// if (!this.reverseBook) {
// this.reverseBook = new ReverseBook()
// }
// }
// /**
// * 将MJ反推出来的数据选择指定的写入到GPT提示词中
// * @param bookId 小说的ID
// * @param bookTaskId 小说的任务ID
// * @param index 反推的数据的索引
// * @returns
// */
// async ReversePromptToGptPrompt(bookId: string, bookTaskId: string, index: number): Promise<GeneralResponse.ErrorItem | GeneralResponse.SuccessItem> {
// try {
// await this.InitService();
// let bookTaskDetails = this.bookTaskDetailService.GetBookTaskData({
// bookId: bookId,
// bookTaskId: bookTaskId
// }).data as Book.SelectBookTaskDetail[]
// if (bookTaskDetails.length <= 0) {
// throw new Error("没有找到对应的分镜数据")
// }
// // 开始修改数据
// for (let i = 0; i < bookTaskDetails.length; i++) {
// const element = bookTaskDetails[i];
// let reversePrompts = element.reversePrompt
// let reversePrompt = reversePrompts[index - 1]
// let gptPrompt = undefined
// if (!isEmpty(reversePrompt.promptCN) && reversePrompt.promptCN != "") {
// gptPrompt = reversePrompt.promptCN
// }
// if (!gptPrompt) {
// gptPrompt = reversePrompt.prompt
// }
// // 开始修改
// this.bookTaskDetailService.UpdateBookTaskDetail(element.id, {
// gptPrompt: gptPrompt
// })
// }
// // 全部修改完毕,将修改后的数据返回
// let res = await this.reverseBook.GetBookTaskDetail(bookTaskId);
// if (res.code == 0) {
// throw new Error(res.message)
// }
// return successMessage(res.data, "反推数据写出成功", "ReverseBook_ReversePromptToGptPrompt")
// } catch (error) {
// return errorMessage("反推数据写出失败,错误信息如下:" + error.message, "ReverseBook_ReversePromptToGptPrompt")
// }
// }
// /**
// * MJ反推将反推的数据写入到GPT中
// * @param bookTaskDetailId
// * @param index
// */
// async SingleReverseToGptPrompt(bookTaskDetailId: string, index: number): Promise<GeneralResponse.ErrorItem | GeneralResponse.SuccessItem> {
// try {
// await this.InitService();
// let bookTaskDetail = this.bookTaskDetailService.GetBookTaskDetailDataById(bookTaskDetailId)
// if (bookTaskDetail == null) {
// throw new Error("没有找到对应的数据")
// }
// let reversePrompts = bookTaskDetail.reversePrompt
// if (!reversePrompts || reversePrompts.length <= 0) {
// throw new Error("没有找到对应的反推提示词数据")
// }
// let reversePrompt = reversePrompts[index]
// let gptPrompt = reversePrompt.promptCN ? reversePrompt.promptCN : reversePrompt.prompt
// // 开始修改
// this.bookTaskDetailService.UpdateBookTaskDetail(bookTaskDetailId, {
// gptPrompt: gptPrompt
// })
// // 保存完毕
// return successMessage(gptPrompt, "反推数据写出成功", "ReverseBook_ReversePromptToGptPrompt")
// } catch (error) {
// return errorMessage("反推数据写出失败,错误信息如下:" + error.message, "ReverseBook_ReversePromptToGptPrompt")
// }
// }
// }

136
src/main/Service/SD/sd.ts Normal file
View File

@ -0,0 +1,136 @@
import { MergeType } from "../../../define/enum/bookEnum";
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";
const fspromise = fs.promises
export class SDOpt {
bookTaskDetailService: BookTaskDetailService
bookTaskService: BookTaskService
imageStyle: ImageStyle
constructor() {
}
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()
}
}
/**
* SD的提示词合并
* @param id ID
* @param mergeType
* @returns
*/
async MergePrompt(id: string, mergeType: MergeType): Promise<GeneralResponse.ErrorItem | GeneralResponse.SuccessItem> {
try {
await this.InitService()
let bookTaskDetail = undefined as Book.SelectBookTaskDetail[];
let bookTask = undefined as Book.SelectBookTask;
if (mergeType == MergeType.BOOKTASK) {
bookTaskDetail = this.bookTaskDetailService.GetBookTaskData({
bookTaskId: id
}).data
bookTask = this.bookTaskService.GetBookTaskDataById(id);
} else if (mergeType == MergeType.BOOKTASKDETAIL) {
bookTaskDetail = [this.bookTaskDetailService.GetBookTaskDetailDataById(id)];
bookTask = this.bookTaskService.GetBookTaskDataById(bookTaskDetail[0].bookTaskId);
} else {
throw new Error("未知的合并类型")
}
// 获取合并的排序
let promptSort = JSON.parse(await fspromise.readFile(define.img_base, 'utf-8')).prompt_sort; // 没有就直接报错
if (!promptSort) {
throw new Error("未找到提示词排序,请先设置")
}
let styleString = '';
// 拿到所有的风格
let styleArr = await this.imageStyle.GetAllImageStyleList(bookTask.imageStyle, bookTask.customizeImageStyle);
for (let i = 0; i < styleArr.length; i++) {
const element = styleArr[i]
if (element.type == 'style_main') {
styleString += element.prompt + ', '
if (element.lora && element.lora != '无' && element.lora_weight) {
styleString += `<lora:${element.lora}:${element.lora_weight}>, `
}
} else {
styleString += element.english_style + ','
}
}
// 获取SD的通用前缀
let sdGlobalPrompt = JSON.parse(await fspromise.readFile(define.sd_setting, 'utf-8')).webui.prompt;
// TODO 反推这边目前就只有风格和提示词,暂时没有人物和场景
let sceneString = ""; // 场景
let characterString = ""; // 人物
let result = []; // 返回前端的数据数组
for (let i = 0; i < bookTaskDetail.length; i++) {
const element = bookTaskDetail[i];
let promptStr = '';
for (let i = 0; i < promptSort.length; i++) {
const element = promptSort[i];
promptStr += `${'${' + element.value + '}'} `
}
// 开始合并
promptStr = promptStr.replace('${style}', styleString) // 风格
promptStr = promptStr.replace('${character}', characterString) // 人物
promptStr = promptStr.replace('${scene}', sceneString) // 场景
promptStr = promptStr.replace(
'${prompt}',
checkStringValueAddSuffix(element.gptPrompt, ',')
)
// 判断是不是需要加前后缀
if (bookTask.prefixPrompt) {
promptStr = checkStringValueAddSuffix(bookTask.prefixPrompt, ',') + promptStr
}
if (bookTask.prefixPrompt) {
promptStr = checkStringValueAddSuffix(promptStr, ',') + bookTask.prefixPrompt
}
if (sdGlobalPrompt) {
promptStr = checkStringValueAddSuffix(sdGlobalPrompt, ',') + promptStr
}
promptStr = ' ' + promptStr;
console.log(promptStr)
// 修改数据库数据
this.bookTaskDetailService.UpdateBookTaskDetail(element.id, {
prompt: promptStr
})
// 写回数据
result.push({
id: element.id,
prompt: promptStr
})
}
return successMessage(result, "SD和并提示词数据成功", "SDOpt_MergePrompt")
} catch (error) {
return errorMessage("SD合并提示词错误信息如下" + error.toString(), "SDOpt_MergePrompt")
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,226 @@
import { GeneralResponse } from "../../../model/generalResponse";
import { TranslateModel } from "../../../model/translate";
import { errorMessage, successMessage } from "../../Public/generalTools";
import { Translate } from "./Translate";
import { DEFINE_STRING } from "../../../define/define_string"
import { TranslateAPIType, TranslateType } from "../../../define/enum/translate";
import { BookTaskDetailService } from "../../../define/db/service/Book/bookTaskDetailService";
import { Book } from "../../../model/book";
import { ResponseMessageType } from "../../../define/enum/softwareEnum";
import { SoftwareService } from '../../../define/db/service/SoftWare/softwareService'
import { isEmpty } from "lodash";
import { ValidateJson } from "../../../define/Tools/validate";
/**
*
*/
export class TranslateService {
translate: Translate
bookTaskDetail: BookTaskDetailService;
softwareService: SoftwareService
constructor() {
}
async InitService() {
if (!this.bookTaskDetail) {
this.bookTaskDetail = await BookTaskDetailService.getInstance()
}
if (!this.softwareService) {
this.softwareService = await SoftwareService.getInstance()
}
if (!this.translate) {
this.translate = new Translate()
}
}
// 返回翻译结果。用于前端修改
private sendTranslateReturn(windowId: number, data: GeneralResponse.MessageResponse): void {
let win = global.newWindow[0]
if (windowId) {
win = global.newWindow.filter(item => item.id == windowId)[0];
}
win.win.webContents.send(DEFINE_STRING.BOOK.MAIN_DATA_RETURN, data)
}
//#region 翻译设置
/**
*
* @returns
*/
InitialTranslateSetting(): TranslateModel.TranslateModel {
return {
selectModel: TranslateAPIType.BAIDU,
translation_auto: false,
translates: [
{
name: TranslateAPIType.BAIDU,
translation_business: "https://fanyi-api.baidu.com/api/trans/vip/translate",
translation_app_id: undefined,
translation_secret: undefined
},
{
name: TranslateAPIType.TENCENT,
translation_business: "https://tmt.tencentcloudapi.com",
translation_app_id: undefined,
translation_secret: undefined
},
{
name: TranslateAPIType.VOLCENGINE,
translation_business: "https://translate.volcengineapi.com?",
translation_app_id: undefined,
translation_secret: undefined
},
{
name: TranslateAPIType.ALI,
translation_business: "https://mt.cn-hangzhou.aliyuncs.com",
translation_app_id: undefined,
translation_secret: undefined
}
]
}
}
/**
*
*/
async GetTranslateSetting(): Promise<GeneralResponse.SuccessItem | GeneralResponse.ErrorItem> {
try {
await this.InitService()
let translateSetting = undefined as TranslateModel.TranslateModel
let translateSettingString = this.softwareService.GetSoftWarePropertyData('translationSetting');
if (isEmpty(translateSettingString)) {
// 初始化
translateSetting = this.InitialTranslateSetting()
} else {
// 解析
let tryParse = ValidateJson(translateSettingString)
if (!tryParse) {
throw new Error("翻译设置数据解析失败")
}
translateSetting = JSON.parse(translateSettingString);
}
return successMessage(translateSetting, "获取翻译设置成功", "TranslateService_GetTranslateSetting")
} catch (error) {
return errorMessage("获取翻译设置失败,失败信息如下:" + error.toString(), "TranslateService_GetTranslateSetting")
}
}
/**
*
*/
async ResetTranslateSetting(): Promise<GeneralResponse.SuccessItem | GeneralResponse.ErrorItem> {
try {
let translateSetting = this.InitialTranslateSetting()
let res = this.softwareService.SaveSoftwarePropertyData('translationSetting', JSON.stringify(translateSetting))
return successMessage(translateSetting, "重置翻译设置成功", "TranslateService_ResetTranslateSetting")
} catch (error) {
return errorMessage("重置翻译设置失败,失败信息如下:" + error.toString(), "TranslateService_ResetTranslateSetting")
}
}
/**
*
* @param value
* @returns
*/
async SaveTranslateSetting(value: TranslateModel.TranslateModel): Promise<GeneralResponse.SuccessItem | GeneralResponse.ErrorItem> {
try {
let res = this.softwareService.SaveSoftwarePropertyData('translationSetting', JSON.stringify(value))
return successMessage(value, "保存翻译设置成功", "TranslateService_SaveTranslateSetting")
} catch (error) {
return errorMessage("保存翻译设置失败,失败信息如下:" + error.toString(), "TranslateService_SaveTranslateSetting")
}
}
//#endregion
/**
*
* @param bookTaskDetailId ID
* @param reversePromptId ID
* @param to
* @param dstString
*/
private async TranslateProcessReversePrompt(bookTaskDetailId: string, reversePromptId: string, to: string, dstString: string): Promise<void> {
// 开始修改翻译后的数据
let updateData = {} as Book.ReversePrompt
if (to == "zh") {
updateData.promptCN = dstString
} else if (to == "en") {
updateData.prompt = dstString
updateData.promptCN = dstString
}
// 修改数据
this.bookTaskDetail.UpdateBookTaskDetailReversePrompt(bookTaskDetailId, reversePromptId, updateData)
}
// 处理返回的数据
async TranslateReturnProcess(value: TranslateModel.TranslateNowIPCParams, to: string, dstString: string) {
switch (value.type) {
case TranslateType.REVERSE_PROMPT_TRANSLATE:
await this.TranslateProcessReversePrompt(value.bookTaskDetailId, value.reversePromptId, to, dstString)
break;
default:
throw new Error("未知的翻译类型");
}
}
// 翻译
async TranslateNowReturn(value: TranslateModel.TranslateNowIPCParams[]): Promise<GeneralResponse.SuccessItem | GeneralResponse.ErrorItem> {
try {
await this.InitService()
// 循环所有的数据,返回翻译结果
for (let i = 0; i < value.length; i++) {
const element = value[i];
let res = await this.translate.TranslateReturnNow(element)
// 单个翻译,将返回的数据写入到原数据中
// 添加一个对返回信息进行处理的函数
let data = res.data
// 先将数据进行拼接
let srcString = ""
if (element.isSplit) {
let dstStrs = []
} else {
// 没有拆分的,只有一句
srcString = data.data[0].dst
}
// 写回数据库
await this.TranslateReturnProcess(element, data.to, srcString);
// 做个返回数据
this.sendTranslateReturn(element.windowId, {
code: 1,
id: element.bookTaskDetailId,
type: ResponseMessageType.PROMPT_TRANSLATE,
data: {
progress: i + 1,
total: value.length,
from: element.from,
to: data.to,
bookTaskDetailId: element.bookTaskDetailId,
reversePromptId: element.reversePromptId,
prompt: srcString,
promptCN: srcString
}
})
}
return successMessage(null, "全部翻译完成", "TranslateService_TranslateNowReturn")
} catch (error) {
return errorMessage("翻译失败,失败信息如下:" + error.toString(), "TranslateService_TranslateNowReturn")
}
}
}

View File

@ -1,11 +1,11 @@
import path from 'path' import path from 'path'
import { TaskScheduler } from './taskScheduler' import { errorMessage, successMessage } from '../Public/generalTools'
import { errorMessage, successMessage } from '../generalTools' import { CheckFileOrDirExist, CheckFolderExistsOrCreate, CopyFileOrFolder, DeleteFolderAllFile } from '../../define/Tools/file'
import { CheckFileOrDirExist, CheckFolderExistsOrCreate } from '../../define/Tools/file'
import { MillisecondsToTimeString } from '../../define/Tools/time' import { MillisecondsToTimeString } from '../../define/Tools/time'
import Ffmpeg from 'fluent-ffmpeg' import Ffmpeg from 'fluent-ffmpeg'
import { SetFfmpegPath } from '../setting/ffmpegSetting' import { SetFfmpegPath } from '../setting/ffmpegSetting'
import fs from 'fs' import fs from 'fs'
import { GeneralResponse } from '../../model/generalResponse'
const fspromises = fs.promises const fspromises = fs.promises
SetFfmpegPath() SetFfmpegPath()
@ -14,8 +14,11 @@ SetFfmpegPath()
* FFmpeg * FFmpeg
*/ */
export class FfmpegOptions { export class FfmpegOptions {
ecode: string // 编码的方式
dcode: string // 解码的方式
constructor() { constructor() {
this.taskScheduler = new TaskScheduler()
} }
InitCodec() { InitCodec() {
@ -33,6 +36,64 @@ export class FfmpegOptions {
} else if (global.gpu.type === 'AMD') { } else if (global.gpu.type === 'AMD') {
videoDcodec = 'h264_amf' videoDcodec = 'h264_amf'
} }
this.dcode = videoDcodec
}
/**
*
* @param videoPath
* @param wh
* @param crf crf值
* @returns
*/
async FfmpegCompressVideo(videoPath: string, maxV: number, crf: string): Promise<string> {
// 判断视频地址是不是存在
let videoIsExist = await CheckFileOrDirExist(videoPath)
if (!videoIsExist) {
throw new Error('视频地址对应的文件不存在')
}
if (!videoPath.toLowerCase().endsWith('.mp4')) {
throw new Error("只支持MP4文件");
}
let tempVideo = path.join(path.dirname(videoPath), "tmp.mp4");
await CopyFileOrFolder(videoPath, tempVideo);
await fspromises.unlink(videoPath)
let wh = await this.FfmpegGetVideoSize(tempVideo)
if (wh.code != 1) {
throw new Error(wh.message)
}
let rate = undefined
let width = undefined
let height = undefined
// 计算尺寸
if (wh.data.width > wh.data.height) {
rate = maxV / wh.data.width
width = maxV
height = wh.data.height * rate
} else {
height = maxV
rate = maxV / wh.data.height
width = wh.data.width * rate
}
return new Promise((resolve, reject) => {
Ffmpeg(tempVideo)
.outputOptions([
`-vf scale=${width}:${height}`, // 调整视频尺寸到 1280x720
`-b:v ${crf}` // 设置视频比特率为 1000kbps
])
.on('end', async () => {
// 删除缓存文件
await fspromises.unlink(tempVideo)
resolve('视频压缩处理完成');
})
.on('error', (err) => {
reject('视频压缩处理失败: ' + err.toString());
})
.save(videoPath)
})
} }
/** /**
@ -188,7 +249,7 @@ export class FfmpegOptions {
* @param {*} videoPath * @param {*} videoPath
* @returns * @returns
*/ */
async FfmpegGetVideoSize(videoPath) { async FfmpegGetVideoSize(videoPath: string): Promise<GeneralResponse.ErrorItem | GeneralResponse.SuccessItem> {
try { try {
let videoIsExist = await CheckFileOrDirExist(videoPath) let videoIsExist = await CheckFileOrDirExist(videoPath)
if (videoIsExist == false) { if (videoIsExist == false) {
@ -267,6 +328,13 @@ export class FfmpegOptions {
throw new Error(frameRes.message) throw new Error(frameRes.message)
} }
let outImagePaths = [] let outImagePaths = []
if(await CheckFileOrDirExist(outImagePath) == false){
return successMessage(
outImagePaths,
'获取指定位置的帧和裁剪成功',
'WatermarkAndSubtitle_FfmpegGetVideoFramdAndClip'
)
}
// 这边可以会裁剪多个,所以需要循环 // 这边可以会裁剪多个,所以需要循环
for (let i = 0; i < clipRanges.length; i++) { for (let i = 0; i < clipRanges.length; i++) {
const element = clipRanges[i] const element = clipRanges[i]

Some files were not shown because too many files have changed in this diff Show More