Laitool v3.0.1-preview.1
2
.vscode/settings.json
vendored
@ -1,6 +1,6 @@
|
||||
{
|
||||
"[typescript]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
"editor.defaultFormatter": "vscode.typescript-language-features"
|
||||
},
|
||||
"[javascript]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
|
||||
3010
package-lock.json
generated
24
package.json
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "laitool",
|
||||
"version": "2.2.12",
|
||||
"version": "3.0.1-preview.1",
|
||||
"description": "An AI tool for image processing, video processing, and other functions.",
|
||||
"main": "./out/main/index.js",
|
||||
"author": "laitool.cn",
|
||||
@ -9,7 +9,7 @@
|
||||
"format": "prettier --write .",
|
||||
"lint": "eslint . --ext .js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix",
|
||||
"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",
|
||||
"postinstall": "electron-builder install-app-deps",
|
||||
"build:win": "npm run build && electron-builder --win --config",
|
||||
@ -20,7 +20,6 @@
|
||||
"@alicloud/alimt20181012": "^1.2.0",
|
||||
"@electron-toolkit/preload": "^3.0.0",
|
||||
"@electron-toolkit/utils": "^3.0.0",
|
||||
"@skit/x.naive-ui": "^0.15.0",
|
||||
"@vicons/ionicons5": "^0.12.0",
|
||||
"@vitejs/plugin-vue-jsx": "^3.1.0",
|
||||
"@volcengine/openapi": "^1.16.0",
|
||||
@ -28,21 +27,24 @@
|
||||
"artplayer": "^5.1.6",
|
||||
"awesome-js": "^2.0.0",
|
||||
"axios": "^1.6.5",
|
||||
"baidu-aip-sdk": "^4.16.16",
|
||||
"blob-to-buffer": "^1.2.9",
|
||||
"compressing": "^1.10.0",
|
||||
"compressorjs": "^1.2.1",
|
||||
"crypto-js": "^4.2.0",
|
||||
"electron-store": "^9.0.0",
|
||||
"electron-updater": "^6.1.7",
|
||||
"fluent-ffmpeg": "^2.1.3",
|
||||
"highlight.js": "^11.9.0",
|
||||
"install": "^0.13.0",
|
||||
"jimp": "^0.22.10",
|
||||
"jieba-js": "^1.0.2",
|
||||
"jsdom": "^24.0.0",
|
||||
"lodash": "^4.17.21",
|
||||
"moment-timezone": "^0.5.45",
|
||||
"music-metadata": "^7.14.0",
|
||||
"node-edge-tts": "^1.2.3",
|
||||
"node-gyp": "^10.2.0",
|
||||
"node-machine-id": "^1.1.12",
|
||||
"node-reg": "^0.2.4",
|
||||
"node-pre-gyp": "^0.17.0",
|
||||
"npm": "^10.7.0",
|
||||
"paddle": "^1.0.0",
|
||||
"pinia": "^2.1.7",
|
||||
@ -53,7 +55,6 @@
|
||||
"uuid": "^9.0.1",
|
||||
"vue-router": "^4.2.5",
|
||||
"wav-file-info": "^0.0.10",
|
||||
"winreg": "^1.2.5",
|
||||
"winston": "^3.13.0",
|
||||
"winston-daily-rotate-file": "^5.0.0",
|
||||
"ws": "^8.18.0"
|
||||
@ -63,17 +64,16 @@
|
||||
"@rushstack/eslint-patch": "^1.6.1",
|
||||
"@types/fluent-ffmpeg": "^2.1.24",
|
||||
"@types/ws": "^8.5.10",
|
||||
"@typescript-eslint/eslint-plugin": "^7.16.0",
|
||||
"@vitejs/plugin-vue": "^5.0.2",
|
||||
"@vue/eslint-config-prettier": "^9.0.0",
|
||||
"cross-env": "^7.0.3",
|
||||
"electron": "^28.1.1",
|
||||
"electron-builder": "^24.13.3",
|
||||
"electron-vite": "^2.0.0",
|
||||
"eslint": "^8.56.0",
|
||||
"eslint": "^8.57.0",
|
||||
"eslint-plugin-vue": "^9.19.2",
|
||||
"less": "^4.2.0",
|
||||
"naive-ui": "^2.38.2",
|
||||
"prettier": "^3.1.1",
|
||||
"vfonts": "^0.0.3",
|
||||
"vite": "^5.0.11",
|
||||
"vue": "^3.4.5"
|
||||
},
|
||||
@ -98,4 +98,4 @@
|
||||
"icon": "./resources/icon.ico"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
Before Width: | Height: | Size: 566 KiB After Width: | Height: | Size: 895 KiB |
BIN
resources/icon_1.ico
Normal file
|
After Width: | Height: | Size: 566 KiB |
|
After Width: | Height: | Size: 1.4 MiB |
|
Before Width: | Height: | Size: 5.3 MiB |
|
Before Width: | Height: | Size: 5.0 MiB |
|
Before Width: | Height: | Size: 5.7 MiB |
|
Before Width: | Height: | Size: 5.6 MiB |
|
Before Width: | Height: | Size: 5.4 MiB |
@ -59,5 +59,5 @@
|
||||
"value": 1.0
|
||||
},
|
||||
"visible": true,
|
||||
"volume": 1.0
|
||||
"volumn": 1.0
|
||||
}
|
||||
|
||||
@ -47,5 +47,5 @@
|
||||
"track_render_index": 0,
|
||||
"uniform_scale": null,
|
||||
"visible": true,
|
||||
"volume": 1.0
|
||||
"volumn": 1.0
|
||||
}
|
||||
|
||||
@ -69,5 +69,5 @@
|
||||
"value": 1.0
|
||||
},
|
||||
"visible": true,
|
||||
"volume": 1.0
|
||||
"volumn": 1.0
|
||||
}
|
||||
154
src/api/sdApi.js
@ -1,91 +1,87 @@
|
||||
import { basicApi } from "./apiBasic";
|
||||
import { define } from "../define/define";
|
||||
import { basicApi } from './apiBasic'
|
||||
import { define } from '../define/define'
|
||||
import { promises as fspromises } from 'fs'
|
||||
import { errorMessage, successMessage } from "../main/generalTools";
|
||||
|
||||
|
||||
export class SdApi {
|
||||
constructor() {
|
||||
this.baseUrl = global.config?.webui_api_url;
|
||||
this.sd_setting = null;
|
||||
constructor() {
|
||||
this.baseUrl = global.config?.webui_api_url
|
||||
this.sd_setting = null
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前SD的服务器中所有的lora信息
|
||||
* @returns
|
||||
*/
|
||||
async getAllLoras(baseURL = null) {
|
||||
let url = this.baseUrl + 'sdapi/v1/loras'
|
||||
if (baseURL != null) {
|
||||
url = baseURL + 'sdapi/v1/loras'
|
||||
}
|
||||
return await basicApi.get(url)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前SD的服务器中所有的lora信息
|
||||
* @returns
|
||||
*/
|
||||
async getAllLoras(baseURL = null) {
|
||||
let url = this.baseUrl + "sdapi/v1/loras";
|
||||
if (baseURL != null) {
|
||||
url = baseURL + "sdapi/v1/loras";
|
||||
}
|
||||
return await basicApi.get(url);
|
||||
/**
|
||||
* 获取当前的所有的checkpoint模型
|
||||
* @param {*} baseURL
|
||||
*/
|
||||
async getAllSDModel(baseURL = null) {
|
||||
let url = this.baseUrl + 'sdapi/v1/sd-models'
|
||||
if (baseURL != null) {
|
||||
url = baseURL + 'sdapi/v1/sd-models'
|
||||
}
|
||||
return await basicApi.get(url)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前的所有的checkpoint模型
|
||||
* @param {*} baseURL
|
||||
*/
|
||||
async getAllSDModel(baseURL = null) {
|
||||
let url = this.baseUrl + "sdapi/v1/sd-models";
|
||||
if (baseURL != null) {
|
||||
url = baseURL + "sdapi/v1/sd-models";
|
||||
}
|
||||
return await basicApi.get(url);
|
||||
/**
|
||||
* 获取当前连接的所有的samplers(采样器)
|
||||
* @param {*} baseURL
|
||||
* @returns
|
||||
*/
|
||||
async getAllSamplers(baseURL = null) {
|
||||
try {
|
||||
let url = this.baseUrl + 'sdapi/v1/samplers'
|
||||
if (baseURL != null) {
|
||||
url = baseURL + 'sdapi/v1/samplers'
|
||||
}
|
||||
return await basicApi.get(url)
|
||||
} catch (error) {
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前连接的所有的samplers(采样器)
|
||||
* @param {*} baseURL
|
||||
* @returns
|
||||
*/
|
||||
async getAllSamplers(baseURL = null) {
|
||||
try {
|
||||
async txt2img(data, baseURL = null) {
|
||||
try {
|
||||
if (this.sd_setting == null) {
|
||||
this.sd_setting = JSON.parse(await fspromises.readFile(define.sd_setting, 'utf-8'))
|
||||
this.baseUrl = this.sd_setting.setting.webui_api_url
|
||||
}
|
||||
|
||||
let url = this.baseUrl + "sdapi/v1/samplers";
|
||||
if (baseURL != null) {
|
||||
url = baseURL + "sdapi/v1/samplers";
|
||||
}
|
||||
return await basicApi.get(url);
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async txt2img(data, baseURL = null) {
|
||||
try {
|
||||
|
||||
if (this.sd_setting == null) {
|
||||
this.sd_setting = JSON.parse(await fspromises.readFile(define.sd_setting, 'utf-8'));
|
||||
this.baseUrl = this.sd_setting.setting.webui_api_url;
|
||||
}
|
||||
|
||||
// 加上通用前缀
|
||||
data.prompt = this.sd_setting.webui.prompt + data.prompt
|
||||
|
||||
data.negative_prompt = this.sd_setting.webui.negative_prompt;
|
||||
data.sampler_name = this.sd_setting.webui.sampler_name;
|
||||
data.cfg_scale = this.sd_setting.webui.cfg_scale;
|
||||
data.n_iter = 1;
|
||||
data.steps = this.sd_setting.webui.steps;
|
||||
data.save_images = false;
|
||||
data.batch_size = data.batch_size ? data.batch_size : 1;
|
||||
|
||||
if (data.width == null) {
|
||||
data.width = 512;
|
||||
}
|
||||
if (data.height == null) {
|
||||
data.height = 512;
|
||||
}
|
||||
|
||||
let url = this.baseUrl + "sdapi/v1/txt2img";
|
||||
if (baseURL != null) {
|
||||
url = baseURL + "sdapi/v1/txt2img";
|
||||
}
|
||||
let res = await basicApi.post(url, data);
|
||||
return res;
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
// 加上通用前缀
|
||||
data.prompt = this.sd_setting.webui.prompt + data.prompt
|
||||
|
||||
data.negative_prompt = this.sd_setting.webui.negative_prompt
|
||||
data.sampler_name = this.sd_setting.webui.sampler_name
|
||||
data.cfg_scale = this.sd_setting.webui.cfg_scale
|
||||
data.n_iter = 1
|
||||
data.steps = this.sd_setting.webui.steps
|
||||
data.save_images = false
|
||||
data.batch_size = data.batch_size ? data.batch_size : 1
|
||||
|
||||
if (data.width == null) {
|
||||
data.width = 512
|
||||
}
|
||||
if (data.height == null) {
|
||||
data.height = 512
|
||||
}
|
||||
|
||||
let url = this.baseUrl + 'sdapi/v1/txt2img'
|
||||
if (baseURL != null) {
|
||||
url = baseURL + 'sdapi/v1/txt2img'
|
||||
}
|
||||
let res = await basicApi.post(url, data)
|
||||
return res
|
||||
} catch (error) {
|
||||
throw error
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,3 +0,0 @@
|
||||
import fs from 'node:fs';
|
||||
|
||||
const fspromises = fs.promises;
|
||||
11
src/define/Tools/common.ts
Normal file
@ -0,0 +1,11 @@
|
||||
|
||||
/**
|
||||
* 检查字符串中是不是包含中文或者标点符号
|
||||
* @param str 需要判断的字符串
|
||||
* @returns 返回的数据,有中文或者标点符号返回true,否则返回false
|
||||
*/
|
||||
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
|
||||
)
|
||||
}
|
||||
@ -34,7 +34,7 @@ export async function CheckFolderExistsOrCreate(folderPath) {
|
||||
* @param {*} subPath 子目录的消息
|
||||
* @returns
|
||||
*/
|
||||
export function JoinPath(rootPath, subPath) {
|
||||
export function JoinPath(rootPath: string, subPath: string): string {
|
||||
// 判断第二个地址是不是存在,不存在返回null,存在返回拼接后的地址
|
||||
if (subPath && !isEmpty(subPath)) {
|
||||
return path.resolve(rootPath, subPath)
|
||||
@ -46,8 +46,9 @@ export function JoinPath(rootPath, subPath) {
|
||||
/**
|
||||
* 删除指定的文件中里面所有的文件和文件夹
|
||||
* @param {*} folderPath 文件夹地址
|
||||
* @param {*} isDeleteOut 是否删除最外层的文件夹,默认false,不删除
|
||||
*/
|
||||
export async function DeleteFolderAllFile(folderPath) {
|
||||
export async function DeleteFolderAllFile(folderPath: string, isDeleteOut: boolean = false): Promise<void> {
|
||||
try {
|
||||
let folderIsExist = await CheckFileOrDirExist(folderPath)
|
||||
if (!folderIsExist) {
|
||||
@ -55,17 +56,22 @@ export async function DeleteFolderAllFile(folderPath) {
|
||||
}
|
||||
// 开始删除
|
||||
let files = await fspromises.readdir(folderPath)
|
||||
files.forEach(async (file) => {
|
||||
const curPath = path.join(folderPath, file)
|
||||
if ((await fspromises.stat(curPath)).isDirectory()) {
|
||||
for (const file of files) {
|
||||
const curPath = path.join(folderPath, file);
|
||||
const stat = await fspromises.stat(curPath);
|
||||
if (stat.isDirectory()) {
|
||||
// 判断是不是文件夹
|
||||
await DeleteFolderAllFile(curPath) // 递归删除文件夹内容
|
||||
fspromises.rmdir(curPath) // 删除空文件夹
|
||||
await DeleteFolderAllFile(curPath); // 递归删除文件夹内容
|
||||
await fspromises.rmdir(curPath); // 删除空文件夹
|
||||
} else {
|
||||
// 删除文件
|
||||
fspromises.unlink(curPath)
|
||||
await fspromises.unlink(curPath);
|
||||
}
|
||||
})
|
||||
}
|
||||
// 判断是不是要删除最外部的文件夹
|
||||
if (isDeleteOut) {
|
||||
await fspromises.rmdir(folderPath)
|
||||
}
|
||||
} catch (error) {
|
||||
throw error
|
||||
}
|
||||
@ -142,7 +148,7 @@ export async function IsDirectory(path) {
|
||||
* @param {*} source_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 {
|
||||
// 判断父文件夹是否存在,不存在创建
|
||||
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)
|
||||
} else {
|
||||
// 复制文件
|
||||
await fspromises.copyFile(source, target)
|
||||
await fspromises.copyFile(source_path, target_path)
|
||||
}
|
||||
} catch (error) {
|
||||
throw new Error(error)
|
||||
@ -171,7 +177,7 @@ export async function BackupFileOrFolder(source_path, target_path) {
|
||||
* @param {*} extensions 拓展地址
|
||||
* @returns 返回文件中指定的后缀文件地址(绝对地址)
|
||||
*/
|
||||
export async function GetFilesWithExtensions(folderPath, extensions) {
|
||||
export async function GetFilesWithExtensions(folderPath: string, extensions: string[]): Promise<string[]> {
|
||||
try {
|
||||
// 判断当前是不是文件夹
|
||||
if (!(await IsDirectory(folderPath))) {
|
||||
@ -213,3 +219,20 @@ export async function GetFilesWithExtensions(folderPath, extensions) {
|
||||
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
|
||||
}
|
||||
}
|
||||
@ -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
@ -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> 返回一个Promise,解析为包含MIME类型的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 返回一个Promise,解析为压缩后的Blob对象
|
||||
*/
|
||||
export function CompressImageToSize(base64: string, maxSizeInBytes: number): Promise<Blob> {
|
||||
let mimeType = this.getMimeType(base64);
|
||||
let byteCharacters = atob(base64.split(',')[1]);
|
||||
let byteNumbers = new Array(byteCharacters.length);
|
||||
for (let i = 0; i < byteCharacters.length; i++) {
|
||||
byteNumbers[i] = byteCharacters.charCodeAt(i);
|
||||
}
|
||||
let byteArray = new Uint8Array(byteNumbers);
|
||||
let imageBlob = new Blob([byteArray], { type: mimeType });
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,9 +1,11 @@
|
||||
import * as image from './image';
|
||||
import * as common from './common';
|
||||
import * as file from './file'
|
||||
import * as validate from './validate';
|
||||
|
||||
export {
|
||||
image,
|
||||
common,
|
||||
file
|
||||
file,
|
||||
validate
|
||||
};
|
||||
@ -43,3 +43,12 @@ export function MillisecondsToTimeString(milliseconds) {
|
||||
timeString = timeString.replace(/(\.\d+)\./g, '$1')
|
||||
return timeString
|
||||
}
|
||||
|
||||
/**
|
||||
* 延时多少秒,返回一个Promise
|
||||
* @param time 延时时间,单位毫秒
|
||||
* @returns viod
|
||||
*/
|
||||
export async function TimeDelay(time: number): Promise<void> {
|
||||
return new Promise(resolve => setTimeout(resolve, time));
|
||||
}
|
||||
15
src/define/Tools/validate.ts
Normal file
@ -0,0 +1,15 @@
|
||||
|
||||
/**
|
||||
* 校验是不是可以进行JSON解析
|
||||
* @param str 要解析的字符串
|
||||
* @returns 可以解析返回true,否则返回false
|
||||
*/
|
||||
export function ValidateJson(str: string): boolean {
|
||||
|
||||
try {
|
||||
JSON.parse(str);
|
||||
return true
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -28,35 +28,6 @@ let apiUrl = [
|
||||
mj_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',
|
||||
value: 'b5c8c8c5-f3c4-4c88-b25c-7f5a3d5f9d1f',
|
||||
|
||||
@ -13,6 +13,8 @@ export class BookBackTaskList extends Realm.Object<BookBackTaskList> {
|
||||
executeType: TaskExecuteType // 任务执行类型,手动还是自动
|
||||
createTime: Date
|
||||
updateTime: Date
|
||||
startTime: number
|
||||
endTime: number
|
||||
|
||||
static schema: ObjectSchema = {
|
||||
name: 'BookBackTaskList',
|
||||
@ -27,7 +29,9 @@ export class BookBackTaskList extends Realm.Object<BookBackTaskList> {
|
||||
errorMessage: 'string?',
|
||||
executeType: { type: 'string', default: TaskExecuteType.AUTO },
|
||||
createTime: 'date',
|
||||
updateTime: 'date'
|
||||
updateTime: 'date',
|
||||
startTime: 'int',
|
||||
endTime: 'int'
|
||||
},
|
||||
primaryKey: 'id'
|
||||
}
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import Realm, { ObjectSchema } from 'realm'
|
||||
// @ts-ignore
|
||||
import Realm from 'realm'
|
||||
import { BookType } from '../../../enum/bookEnum'
|
||||
|
||||
export class BookModel extends Realm.Object<BookModel> {
|
||||
@ -11,12 +12,21 @@ export class BookModel extends Realm.Object<BookModel> {
|
||||
oldVideoPath: string | null
|
||||
srtPath: string | null
|
||||
audioPath: string | null
|
||||
draftSrtStyle: string | null // 草稿字幕样式
|
||||
backgroundMusic: string | null // 背景音乐ID
|
||||
friendlyReminder: string | null // 友情提示
|
||||
updateTime: Date
|
||||
createTime: Date
|
||||
version: string
|
||||
imageStyle: string[] | null // 软件内置的样式
|
||||
autoAnalyzeCharacter: string | null // 自动分析角色设置
|
||||
customizeImageStyle: string[] | null // 自定义的样式
|
||||
videoConfig: string | null // 合成视频设置
|
||||
prefixPrompt: string | null // 前缀
|
||||
suffixPrompt: string | null // 后缀
|
||||
subtitlePosition: string | null
|
||||
|
||||
static schema: ObjectSchema = {
|
||||
static schema: Realm.ObjectSchema = {
|
||||
name: 'Book',
|
||||
properties: {
|
||||
id: 'string',
|
||||
@ -27,10 +37,19 @@ export class BookModel extends Realm.Object<BookModel> {
|
||||
oldVideoPath: 'string?',
|
||||
srtPath: 'string?',
|
||||
audioPath: 'string?',
|
||||
draftSrtStyle : 'string?',
|
||||
backgroundMusic: 'string?',
|
||||
friendlyReminder: 'string?',
|
||||
imageFolder: 'string?',
|
||||
updateTime: 'date',
|
||||
createTime: 'date',
|
||||
version: 'string',
|
||||
imageStyle: 'string?[]',
|
||||
autoAnalyzeCharacter: 'string?',
|
||||
customizeImageStyle: 'string?[]',
|
||||
videoConfig: "string?",
|
||||
prefixPrompt: "string?",
|
||||
suffixPrompt: "string?",
|
||||
subtitlePosition: 'string?'
|
||||
},
|
||||
// 主键为_id
|
||||
|
||||
@ -1,5 +1,40 @@
|
||||
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> {
|
||||
id: string
|
||||
@ -9,14 +44,24 @@ export class BookTaskModel extends Realm.Object<BookTaskModel> {
|
||||
generateVideoPath: string | null
|
||||
srtPath: string | null
|
||||
audioPath: string | null
|
||||
draftSrtStyle: string | null // 草稿字幕样式
|
||||
backgroundMusic: string | null // 背景音乐ID
|
||||
friendlyReminder: string | null // 友情提示
|
||||
imageFolder: string | null
|
||||
styleList: Realm.List<string> | null
|
||||
prefix: string | null
|
||||
imageStyle: string[] | null // 软件内置的样式
|
||||
autoAnalyzeCharacter: string | null // 自动分析角色设置
|
||||
customizeImageStyle: string[] | null // 自定义的样式
|
||||
videoConfig: string | null // 合成视频设置
|
||||
prefixPrompt: string | null // 前缀
|
||||
suffixPrompt: string | null // 后缀
|
||||
styleList: string[] | null
|
||||
status: BookTaskStatus
|
||||
errorMsg: string | null
|
||||
isAuto: boolean // 是否自动
|
||||
updateTime: Date
|
||||
createTime: Date
|
||||
imageCategory: BookImageCategory // 图片出图方式
|
||||
subImageFolder: string[] | null // 出图的子文件夹
|
||||
|
||||
static schema: ObjectSchema = {
|
||||
name: 'BookTask',
|
||||
@ -28,14 +73,23 @@ export class BookTaskModel extends Realm.Object<BookTaskModel> {
|
||||
generateVideoPath: 'string?',
|
||||
srtPath: 'string?',
|
||||
audioPath: 'string?',
|
||||
draftSrtStyle: 'string?',
|
||||
backgroundMusic: 'string?',
|
||||
friendlyReminder: 'string?',
|
||||
imageFolder: 'string?',
|
||||
styleList: 'string[]',
|
||||
prefix: 'string?',
|
||||
subImageFolder: "string?[]",
|
||||
imageStyle: 'string?[]',
|
||||
autoAnalyzeCharacter: 'string?',
|
||||
customizeImageStyle: 'string?[]',
|
||||
videoConfig: "string?",
|
||||
prefixPrompt: "string?",
|
||||
suffixPrompt: "string?",
|
||||
status: 'string',
|
||||
errorMsg: 'string?',
|
||||
isAuto: 'bool',
|
||||
updateTime: 'date',
|
||||
createTime: 'date'
|
||||
createTime: 'date',
|
||||
imageCategory: 'string',
|
||||
},
|
||||
// 主键为_id
|
||||
primaryKey: 'id'
|
||||
|
||||
@ -5,8 +5,8 @@ import {
|
||||
BookTaskStatus,
|
||||
BookType,
|
||||
MJAction,
|
||||
MJCategroy
|
||||
} from '../../../enum/bookEnum'
|
||||
import { MJImageType } from '../../../enum/mjEnum'
|
||||
|
||||
export class Subtitle extends Realm.Object<Subtitle> {
|
||||
startTime: number
|
||||
@ -29,7 +29,7 @@ export class MJMessage extends Realm.Object<MJMessage> {
|
||||
id: string
|
||||
mjApiUrl: string | null
|
||||
progress: number
|
||||
category: MJCategroy
|
||||
category: MJImageType
|
||||
imageClick: string | null // 图片点击(显示的小的)
|
||||
imageShow: string | null // 图片实际的地址
|
||||
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> {
|
||||
id: string
|
||||
no: number
|
||||
@ -119,16 +139,19 @@ export class BookTaskDetailModel extends Realm.Object<BookTaskDetailModel> {
|
||||
startTime: number | null // 开始时间
|
||||
endTime: number | null // 结束时间
|
||||
timeLimit: string | null // 事件实现(0 -- 3000)
|
||||
subValue: Realm.List<Subtitle> | null // 包含的字幕数据
|
||||
subValue: string | null // 包含的字幕数据
|
||||
characterTags: string[] | null // 角色标签
|
||||
gptPrompt: string | null // GPT提示词
|
||||
mjMessage: MJMessage | null // MJ消息
|
||||
outImagePath: string | null // 输出图片地址
|
||||
subImagePath: string[] | null // 子图片地址
|
||||
imageLock: boolean // 图片锁
|
||||
prompt: string | null // 提示
|
||||
adetailer: boolean // 是否开启修脸
|
||||
sdConifg: SDConfig | null // SD配置
|
||||
reversePrompt: ReversePrompt[] | null // 反推的提示词(数组)
|
||||
subtitlePosition: string | null // 字幕位置
|
||||
status: BookTaskStatus
|
||||
createTime: Date
|
||||
updateTime: Date
|
||||
|
||||
@ -148,16 +171,19 @@ export class BookTaskDetailModel extends Realm.Object<BookTaskDetailModel> {
|
||||
startTime: 'int?',
|
||||
endTime: 'int?',
|
||||
timeLimit: 'string?',
|
||||
subValue: { type: 'list', objectType: 'Subtitle' },
|
||||
subValue: 'string?',
|
||||
reversePrompt: { type: 'list', objectType: 'ReversePrompt' },
|
||||
characterTags: { type: 'list', objectType: 'string' },
|
||||
gptPrompt: 'string?',
|
||||
mjMessage: 'MJMessage?',
|
||||
outImagePath: 'string?',
|
||||
subImagePath: 'string[]',
|
||||
imageLock: 'bool',
|
||||
prompt: 'string?',
|
||||
adetailer: 'bool',
|
||||
sdConifg: 'SDConfig?',
|
||||
subtitlePosition: 'string?',
|
||||
status: "string",
|
||||
createTime: 'date',
|
||||
updateTime: 'date'
|
||||
},
|
||||
|
||||
@ -11,6 +11,8 @@ export class SoftwareModel extends Realm.Object<SoftwareModel> {
|
||||
ttsSetting: string | null // TTS设置的json字符串
|
||||
writeSetting: string | null // 文案的相关配置的json字符串
|
||||
aiSetting: string | null // AI相关的配置的json字符串
|
||||
watermarkSetting: string | null // 水印相关的配置的json字符串
|
||||
translationSetting: string | null // 翻译相关的配置的json字符串
|
||||
|
||||
static schema: ObjectSchema = {
|
||||
name: 'Software',
|
||||
@ -23,7 +25,9 @@ export class SoftwareModel extends Realm.Object<SoftwareModel> {
|
||||
globalSetting: 'string',
|
||||
ttsSetting: 'string?', // 可空
|
||||
writeSetting: 'string?',
|
||||
aiSetting: 'string?'
|
||||
aiSetting: 'string?',
|
||||
watermarkSetting: 'string?',
|
||||
translationSetting: 'string?'
|
||||
},
|
||||
// 主键为_id
|
||||
primaryKey: 'id'
|
||||
|
||||
@ -1,21 +1,16 @@
|
||||
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 {
|
||||
BookBackTaskStatus,
|
||||
BookBackTaskType,
|
||||
BookTaskStatus,
|
||||
TaskExecuteType
|
||||
} from '../../../enum/bookEnum.js'
|
||||
import { errorMessage, successMessage } from '../../../../main/generalTools.js'
|
||||
import { errorMessage, successMessage } from '../../../../main/Public/generalTools'
|
||||
import { BaseRealmService } from './bookBasic'
|
||||
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 { BookBackTaskList } from '../../model/Book/BookBackTaskListModel.js'
|
||||
import { Book } from '../../../../model/book.js'
|
||||
import { GeneralResponse } from '../../../../model/generalResponse.js'
|
||||
const { v4: uuidv4 } = require('uuid')
|
||||
|
||||
export class BookBackTaskListService extends BaseRealmService {
|
||||
@ -44,7 +39,7 @@ export class BookBackTaskListService extends BaseRealmService {
|
||||
* bookId 和 status 必填一个
|
||||
* @param query bookId,bookTaskId,name,type,status
|
||||
*/
|
||||
GetBookBackTaskList(query) {
|
||||
GetBookBackTaskList(query: Book.QueryBookBackTaskCondition) {
|
||||
try {
|
||||
if (query == null) {
|
||||
throw new Error('查询后台队列任务失败,没有查询条件')
|
||||
@ -53,33 +48,36 @@ export class BookBackTaskListService extends BaseRealmService {
|
||||
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
|
||||
.objects('BookBackTaskList')
|
||||
.filtered(queryString)
|
||||
.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
|
||||
if (query.count) {
|
||||
res = tasks.slice(0, query.count)
|
||||
@ -107,12 +105,12 @@ export class BookBackTaskListService extends BaseRealmService {
|
||||
let tasks = this.realm
|
||||
.objects<BookBackTaskList>('BookBackTaskList')
|
||||
.filtered(
|
||||
'status == $0 && executeType == $1',
|
||||
'(status == $0 || status == $1) && executeType == $2',
|
||||
BookBackTaskStatus.WAIT,
|
||||
BookBackTaskStatus.RECONNECT,
|
||||
executeType ? executeType : TaskExecuteType.AUTO
|
||||
)
|
||||
.sorted('createTime', false)
|
||||
|
||||
if (count != null) {
|
||||
tasks = tasks.slice(0, count) as unknown as Realm.Results<BookBackTaskList>
|
||||
}
|
||||
@ -169,9 +167,8 @@ export class BookBackTaskListService extends BaseRealmService {
|
||||
}
|
||||
|
||||
// 开始往数据库中写数据
|
||||
let name = `${book.name}-${bookTask ? bookTask.name : 'default'}-${
|
||||
bookTaskDetail ? bookTaskDetail.name : 'default'
|
||||
}-${taskType}`
|
||||
let name = `${book.name}-${bookTask ? bookTask.name : 'default'}-${bookTaskDetail ? bookTaskDetail.name : 'default'
|
||||
}-${taskType}`
|
||||
|
||||
let bookBackTask = {
|
||||
id: uuidv4(),
|
||||
@ -183,15 +180,14 @@ export class BookBackTaskListService extends BaseRealmService {
|
||||
executeType: executeType,
|
||||
status: BookBackTaskStatus.WAIT,
|
||||
createTime: new Date(),
|
||||
updateTime: new Date()
|
||||
}
|
||||
updateTime: new Date(),
|
||||
startTime: 0,
|
||||
endTime: 0
|
||||
} as TaskModal.Task
|
||||
this.realm.write(() => {
|
||||
this.realm.create('BookBackTaskList', bookBackTask)
|
||||
})
|
||||
|
||||
// 添加成功之后,调用开始执行任务的方法
|
||||
await global.taskManager.ExecuteAutoTask()
|
||||
|
||||
return successMessage(
|
||||
bookBackTask,
|
||||
'新增后台队列任务到数据库成功',
|
||||
@ -210,7 +206,7 @@ export class BookBackTaskListService extends BaseRealmService {
|
||||
* (对于后台的队列任务只能修改状态)和错误信息
|
||||
* @param bookBackTask 修改的数据
|
||||
*/
|
||||
UpdateTaskStatus(bookBackTask) {
|
||||
UpdateTaskStatus(bookBackTask: Book.UpdateBookTaskListStatus): GeneralResponse.SuccessItem {
|
||||
try {
|
||||
// 判断数据是不是存在
|
||||
if (isEmpty(bookBackTask.id) || isEmpty(bookBackTask.status)) {
|
||||
@ -219,7 +215,7 @@ export class BookBackTaskListService extends BaseRealmService {
|
||||
// 开始修改
|
||||
this.transaction(() => {
|
||||
// 获取指定ID的队列任务
|
||||
let _bookBackTask = this.realm.objectForPrimaryKey('BookBackTaskList', bookBackTask.id)
|
||||
let _bookBackTask = this.realm.objectForPrimaryKey('BookBackTaskList', bookBackTask.id) as TaskModal.Task
|
||||
// 判断数据是不是存在
|
||||
if (_bookBackTask == null) {
|
||||
throw new Error('修改后台队列任务失败,数据不存在')
|
||||
@ -229,6 +225,16 @@ export class BookBackTaskListService extends BaseRealmService {
|
||||
if (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(
|
||||
bookBackTask,
|
||||
|
||||
@ -7,12 +7,13 @@ import path from 'path'
|
||||
import {
|
||||
BookTaskDetailModel,
|
||||
MJMessage,
|
||||
ReversePrompt,
|
||||
SDConfig,
|
||||
Subtitle,
|
||||
WebuiConfig
|
||||
} from '../../model/Book/bookTaskDetail'
|
||||
import { BookBackTaskList } from '../../model/Book/BookBackTaskListModel'
|
||||
import { TaskExecuteType } from '../../../enum/bookEnum'
|
||||
import { BookTaskStatus, TaskExecuteType } from '../../../enum/bookEnum'
|
||||
import { version } from '../../../../../package.json'
|
||||
|
||||
let dbPath = path.resolve(define.db_path, 'book.realm')
|
||||
@ -77,6 +78,83 @@ const migration = (oldRealm: Realm, newRealm: Realm) => {
|
||||
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 {
|
||||
@ -114,10 +192,11 @@ export class BaseRealmService extends BaseService {
|
||||
SDConfig,
|
||||
WebuiConfig,
|
||||
BookTaskModel,
|
||||
ReversePrompt,
|
||||
BookTaskDetailModel
|
||||
],
|
||||
path: this.dbpath,
|
||||
schemaVersion: 8,
|
||||
schemaVersion: 21,
|
||||
migration: migration
|
||||
}
|
||||
this.realm = await Realm.open(config)
|
||||
|
||||
@ -3,13 +3,16 @@ import { BookModel } from '../../model/Book/book.js'
|
||||
import path from 'path'
|
||||
import { BaseService } from '../baseService.js'
|
||||
import { define } from '../../../define.js'
|
||||
import { BookTaskStatus, BookType } from '../../../enum/bookEnum.js'
|
||||
import { successMessage } from '../../../../main/generalTools.js'
|
||||
import { CheckFolderExistsOrCreate, CopyFileOrFolder } from '../../../Tools/file.js'
|
||||
import { BookImageCategory, BookTaskStatus, BookType } from '../../../enum/bookEnum.js'
|
||||
import { successMessage } from '../../../../main/Public/generalTools'
|
||||
import { CheckFolderExistsOrCreate, CopyFileOrFolder } from '../../../Tools/file'
|
||||
const { v4: uuidv4 } = require('uuid')
|
||||
import { BookTaskService } from './bookTaskService'
|
||||
import { BaseRealmService } from './bookBasic.js'
|
||||
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 {
|
||||
static instance: BookService | null = null
|
||||
@ -36,14 +39,14 @@ export class BookService extends BaseRealmService {
|
||||
* 获取小说信息,没有找到返回null
|
||||
* @param bookId
|
||||
*/
|
||||
GetBookDataById(bookId) {
|
||||
GetBookDataById(bookId): Book.SelectBook | null {
|
||||
try {
|
||||
if (isEmpty(bookId)) {
|
||||
throw new Error('获取小说信息失败,缺少小说ID')
|
||||
}
|
||||
let books = this.realm.objects<BookModel>('Book').filtered('id = $0', bookId)
|
||||
if (books.length <= 0) {
|
||||
return successMessage(null, '通过ID获取小说数据成功', 'ReverseBook_GetBookDataById')
|
||||
return null
|
||||
} else {
|
||||
// 对返回的数据进行处理
|
||||
let resBooks = Array.from(books).map((book) => {
|
||||
@ -63,8 +66,7 @@ export class BookService extends BaseRealmService {
|
||||
}
|
||||
return bookObj
|
||||
})
|
||||
|
||||
return successMessage(resBooks[0], '通过ID获取小说数据成功', 'ReverseBook_GetBookDataById')
|
||||
return resBooks[0]
|
||||
}
|
||||
} catch (error) {
|
||||
throw error
|
||||
@ -120,8 +122,10 @@ export class BookService extends BaseRealmService {
|
||||
: '',
|
||||
imageFolder: book.imageFolder
|
||||
? 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
|
||||
})
|
||||
|
||||
@ -186,14 +190,31 @@ export class BookService extends BaseRealmService {
|
||||
await CopyFileOrFolder(book.oldVideoPath, oldVideoPath)
|
||||
}
|
||||
|
||||
let ffmpegOptions = new FfmpegOptions();
|
||||
let res = await ffmpegOptions.FfmpegCompressVideo(oldVideoPath, 800, "2000k")
|
||||
|
||||
// 创建对应的文件夹
|
||||
await CheckFolderExistsOrCreate(bookFolderPath)
|
||||
await CheckFolderExistsOrCreate(imageFolder)
|
||||
await CheckFolderExistsOrCreate(bookTaskImageFolder) // 创建默认的任务文件夹
|
||||
// 修改数据
|
||||
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(() => {
|
||||
book.version = version
|
||||
this.realm.create('Book', book)
|
||||
|
||||
// 添加一个任务
|
||||
let bookTask = {
|
||||
id: uuidv4(),
|
||||
@ -210,7 +231,9 @@ export class BookService extends BaseRealmService {
|
||||
errorMsg: null,
|
||||
isAuto: false,
|
||||
updateTime: new Date(),
|
||||
createTime: new Date()
|
||||
createTime: new Date(),
|
||||
version: version,
|
||||
imageCategory: imageCategory
|
||||
}
|
||||
|
||||
// 添加任务
|
||||
@ -265,7 +288,7 @@ export class BookService extends BaseRealmService {
|
||||
|
||||
// 检查小说ID对应的数据是不是存在
|
||||
let bookRes = this.GetBookDataById(bookId)
|
||||
if (bookRes.data == null) {
|
||||
if (bookRes == null) {
|
||||
throw new Error('修改小说数据失败,小说ID对应的数据不存在')
|
||||
}
|
||||
|
||||
@ -279,11 +302,11 @@ export class BookService extends BaseRealmService {
|
||||
})
|
||||
|
||||
bookRes = this.GetBookDataById(bookId)
|
||||
if (bookRes.data == null) {
|
||||
if (bookRes == null) {
|
||||
throw new Error('获取修改后的小说数据失败,小说ID对应的数据不存在')
|
||||
}
|
||||
|
||||
return successMessage(bookRes.data, '修改小说数据成功', 'ReverseBook_UpdateBookData')
|
||||
return successMessage(bookRes, '修改小说数据成功', 'ReverseBook_UpdateBookData')
|
||||
} catch (error) {
|
||||
throw error
|
||||
}
|
||||
|
||||
@ -4,19 +4,21 @@ import { BaseService } from '../baseService.js'
|
||||
import { define } from '../../../define.js'
|
||||
import { BookTaskModel } from '../../model/Book/bookTask.js'
|
||||
import { BookTaskStatus } from '../../../enum/bookEnum.js'
|
||||
import { successMessage } from '../../../../main/generalTools.js'
|
||||
import { successMessage } from '../../../../main/Public/generalTools'
|
||||
import { BaseRealmService } from './bookBasic'
|
||||
import { endsWith, isEmpty } from 'lodash'
|
||||
import { cloneDeep, endsWith, isEmpty } from 'lodash'
|
||||
import { book } from '../../../../preload/book.js'
|
||||
import { DefaultObject } from 'realm/dist/public-types/schema.js'
|
||||
import { JoinPath } from '../../../Tools/file.js'
|
||||
import { BookTaskDetailModel } from '../../model/Book/bookTaskDetail.js'
|
||||
import { JoinPath } from '../../../Tools/file'
|
||||
import { BookTaskDetailModel, ReversePrompt } from '../../model/Book/bookTaskDetail.js'
|
||||
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')
|
||||
|
||||
// 版本迁移
|
||||
const migration = (oldRealm: Realm, newRealm: Realm) => {}
|
||||
const migration = (oldRealm: Realm, newRealm: Realm) => { }
|
||||
|
||||
export class BookTaskDetailService extends BaseRealmService {
|
||||
static instance: BookTaskDetailService | null = null
|
||||
@ -43,7 +45,7 @@ export class BookTaskDetailService extends BaseRealmService {
|
||||
* 更具条件查询执行的小说的分镜信息
|
||||
* @param condition 查询的条件,id,name,bookId,bookTaskId
|
||||
*/
|
||||
GetBookTaskData(condition) {
|
||||
GetBookTaskData(condition: Book.QueryBookTaskDetailCondition) {
|
||||
try {
|
||||
if (condition == null) {
|
||||
throw new Error('查询小说分镜信息,查询条件不能为空')
|
||||
@ -62,6 +64,7 @@ export class BookTaskDetailService extends BaseRealmService {
|
||||
tasksToDelete = tasksToDelete.filtered('name==$0', condition.name)
|
||||
}
|
||||
|
||||
|
||||
let resData = Array.from(tasksToDelete).map((item) => {
|
||||
let resObj = {
|
||||
...item,
|
||||
@ -71,9 +74,17 @@ export class BookTaskDetailService extends BaseRealmService {
|
||||
outImagePath: JoinPath(define.project_path, item.outImagePath),
|
||||
subImagePath: (item.subImagePath as string[])?.map((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
|
||||
}
|
||||
}),
|
||||
mjMessage: item.mjMessage ? item.mjMessage.toJSON() : null,
|
||||
}
|
||||
return resObj
|
||||
return cloneDeep(resObj)
|
||||
})
|
||||
return successMessage(
|
||||
resData,
|
||||
@ -89,7 +100,7 @@ export class BookTaskDetailService extends BaseRealmService {
|
||||
* 通过ID获取指定的小说任务分镜详细数据
|
||||
* @param bookTaskDetailId
|
||||
*/
|
||||
public GetBookTaskDetailDataById(bookTaskDetailId: string) {
|
||||
public GetBookTaskDetailDataById(bookTaskDetailId: string): Book.SelectBookTaskDetail {
|
||||
try {
|
||||
if (bookTaskDetailId == null) {
|
||||
throw new Error('获取小说任务详细信息失败,缺少ID')
|
||||
@ -97,17 +108,9 @@ export class BookTaskDetailService extends BaseRealmService {
|
||||
|
||||
let bookTaskDetails = this.GetBookTaskData({ id: bookTaskDetailId })
|
||||
if (bookTaskDetails.data.length <= 0) {
|
||||
return successMessage(
|
||||
null,
|
||||
'未找到对应的小说任务详细信息',
|
||||
'BookTaskDetailService_GetBookTaskDetailDataById'
|
||||
)
|
||||
return null;
|
||||
} else {
|
||||
return successMessage(
|
||||
bookTaskDetails.data[0],
|
||||
'获取小说任务详细信息成功',
|
||||
'BookTaskDetailService_GetBookTaskDetailDataById'
|
||||
)
|
||||
return bookTaskDetails.data[0]
|
||||
}
|
||||
} catch (error) {
|
||||
throw error
|
||||
@ -142,6 +145,7 @@ export class BookTaskDetailService extends BaseRealmService {
|
||||
|
||||
bookTaskDetail.name = name
|
||||
bookTaskDetail.id = uuidv4()
|
||||
bookTaskDetail.imageLock = false
|
||||
bookTaskDetail.createTime = new Date()
|
||||
bookTaskDetail.updateTime = new Date()
|
||||
bookTaskDetail.adetailer = false // 先写死false
|
||||
@ -165,7 +169,7 @@ export class BookTaskDetailService extends BaseRealmService {
|
||||
* @param bookTaskDetailId
|
||||
* @param updateData
|
||||
*/
|
||||
UpdateBookTaskDetail(bookTaskDetailId: string, updateData) {
|
||||
UpdateBookTaskDetail(bookTaskDetailId: string, updateData: Book.SelectBookTaskDetail) {
|
||||
try {
|
||||
this.transaction(() => {
|
||||
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
|
||||
* @param condition bookId,bookTaskId,name,id
|
||||
@ -223,4 +274,18 @@ export class BookTaskDetailService extends BaseRealmService {
|
||||
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)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,24 +1,29 @@
|
||||
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 { BookBackTaskStatus, BookTaskStatus } from '../../../enum/bookEnum.js'
|
||||
import { successMessage } from '../../../../main/generalTools.js'
|
||||
import { BookBackTaskStatus, BookImageCategory, BookTaskStatus } from '../../../enum/bookEnum.js'
|
||||
import { successMessage } from '../../../../main/Public/generalTools'
|
||||
import { BaseRealmService } from './bookBasic'
|
||||
import { isEmpty } from 'lodash'
|
||||
import { JoinPath } from '../../../Tools/file.js'
|
||||
import { JoinPath } from '../../../Tools/file'
|
||||
import { BookBackTaskList } from '../../model/Book/BookBackTaskListModel.js'
|
||||
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')
|
||||
|
||||
export class BookTaskService extends BaseRealmService {
|
||||
static instance: BookTaskService | null = null
|
||||
realm: Realm
|
||||
tagDefine: TagDefine
|
||||
|
||||
private constructor() {
|
||||
super()
|
||||
this.tagDefine = new TagDefine()
|
||||
}
|
||||
|
||||
/**
|
||||
@ -38,7 +43,7 @@ export class BookTaskService extends BaseRealmService {
|
||||
* 查询满足条件的小说子任务信息
|
||||
* @param bookTaskCondition 查询条件 id,bookId,name,no,page, pageSize
|
||||
*/
|
||||
GetBookTaskData(bookTaskCondition) {
|
||||
GetBookTaskData(bookTaskCondition: Book.QueryBookTaskCondition): GeneralResponse.ErrorItem | GeneralResponse.SuccessItem {
|
||||
try {
|
||||
// 获取所有的小说数据,并进行时间降序排序
|
||||
let bookTasks = this.realm.objects<BookTaskModel>('BookTask')
|
||||
@ -62,7 +67,7 @@ export class BookTaskService extends BaseRealmService {
|
||||
}
|
||||
let bookTask_length = bookTasks.length
|
||||
|
||||
bookTasks = bookTasks.sorted('updateTime', true)
|
||||
// bookTasks = bookTasks.sorted('updateTime', true)
|
||||
// 判断是不是有page和pageSize,有的话对查询返回的信息做分页
|
||||
if (bookTaskCondition.page && bookTaskCondition.pageSize) {
|
||||
bookTasks = bookTasks.slice(
|
||||
@ -73,22 +78,24 @@ export class BookTaskService extends BaseRealmService {
|
||||
|
||||
// 做一下数据转换
|
||||
// 将realm对象数组转换为普通对象数组
|
||||
// 将realm对象数组转换为普通对象数组,并处理异步操作
|
||||
let res_bookTasks = Array.from(bookTasks).map((bookTask) => {
|
||||
// 这里可以直接操作普通对象
|
||||
let bookObj = {
|
||||
// 直接操作普通对象
|
||||
return {
|
||||
...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),
|
||||
srtPath: JoinPath(define.project_path, bookTask.srtPath),
|
||||
audioPath: JoinPath(define.project_path, bookTask.audioPath),
|
||||
imageFolder: JoinPath(define.project_path, bookTask.imageFolder)
|
||||
}
|
||||
return bookObj
|
||||
imageFolder: JoinPath(define.project_path, bookTask.imageFolder),
|
||||
imageCategory: bookTask.imageCategory ? bookTask.imageCategory : BookImageCategory.MJ, // 默认使用MJ出图
|
||||
} as Book.SelectBookTask;
|
||||
})
|
||||
|
||||
return successMessage(
|
||||
{
|
||||
bookTasks: res_bookTasks,
|
||||
bookTasks: JSON.parse(JSON.stringify(res_bookTasks)),
|
||||
total: bookTask_length
|
||||
},
|
||||
'查询小说任务成功',
|
||||
@ -103,7 +110,7 @@ export class BookTaskService extends BaseRealmService {
|
||||
* 通过ID获取小说批次任务的数据
|
||||
* @param bookTaskId
|
||||
*/
|
||||
GetBookTaskDataById(bookTaskId: string) {
|
||||
GetBookTaskDataById(bookTaskId: string): Book.SelectBookTask {
|
||||
try {
|
||||
if (bookTaskId == null) {
|
||||
throw new Error('小说任务ID不能为空')
|
||||
@ -111,13 +118,9 @@ export class BookTaskService extends BaseRealmService {
|
||||
|
||||
let bookTasks = this.GetBookTaskData({ id: bookTaskId })
|
||||
if (bookTasks.data.bookTasks.length <= 0) {
|
||||
return successMessage(null, '未找到对应的小说任务', 'BookTaskService_GetBookTaskDataById')
|
||||
throw new Error('未找到对应的小说任务')
|
||||
} else {
|
||||
return successMessage(
|
||||
bookTasks.data.bookTasks[0],
|
||||
'查询小说任务成功',
|
||||
'BookTaskService_GetBookTaskDataById'
|
||||
)
|
||||
return bookTasks.data.bookTasks[0]
|
||||
}
|
||||
} catch (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) {
|
||||
try {
|
||||
@ -220,4 +245,73 @@ export class BookTaskService extends BaseRealmService {
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,9 +2,9 @@ import Realm, { UpdateMode } from 'realm'
|
||||
import path from 'path'
|
||||
import { BaseService } from '../baseService.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 { errorMessage, successMessage } from '../../../../main/generalTools.js'
|
||||
import { errorMessage, successMessage } from '../../../../main/Public/generalTools'
|
||||
import { BaseSoftWareService } from './softwareBasic.js'
|
||||
import { isEmpty } from 'lodash'
|
||||
const { v4: uuidv4 } = require('uuid')
|
||||
|
||||
@ -2,13 +2,14 @@ import Realm, { UpdateMode } from 'realm'
|
||||
import path from 'path'
|
||||
import { BaseService } from '../baseService'
|
||||
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 { errorMessage, successMessage } from '../../../../main/generalTools.js'
|
||||
import { errorMessage, successMessage } from '../../../../main/Public/generalTools'
|
||||
import { BaseSoftWareService } from './softwareBasic.js'
|
||||
import { isEmpty, isNumber } from 'lodash'
|
||||
const { v4: uuidv4 } = require('uuid')
|
||||
import { version } from '../../../../../package.json'
|
||||
import { GeneralResponse } from '../../../../model/generalResponse'
|
||||
|
||||
export class MJSettingService extends BaseSoftWareService {
|
||||
static instance: MJSettingService | null = null
|
||||
@ -561,7 +562,7 @@ export class MJSettingService extends BaseSoftWareService {
|
||||
// 判断API设置的数据是不是存在
|
||||
let apiSetting = mjSetting.apiSetting ? mjSetting.apiSetting : null
|
||||
if (apiSetting != null) {
|
||||
let apiSettingRes: { code: number; data: any; message: any }
|
||||
let apiSettingRes: GeneralResponse.ErrorItem | GeneralResponse.SuccessItem
|
||||
if (isEmpty(apiSetting.id)) {
|
||||
// 新增
|
||||
apiSettingRes = this.AddAPIMjSetting(apiSetting)
|
||||
@ -577,7 +578,7 @@ export class MJSettingService extends BaseSoftWareService {
|
||||
// 判断浏览器模式的数据是不是存在
|
||||
let browserSetting = mjSetting.browserSetting ? mjSetting.browserSetting : null
|
||||
if (browserSetting != null) {
|
||||
let browserSettingRes: { code: number; data: any; message: any }
|
||||
let browserSettingRes: GeneralResponse.ErrorItem | GeneralResponse.SuccessItem
|
||||
if (isEmpty(browserSetting.id)) {
|
||||
// 新增
|
||||
browserSettingRes = this.AddBrowserMJSetting(browserSetting)
|
||||
@ -591,7 +592,7 @@ export class MJSettingService extends BaseSoftWareService {
|
||||
}
|
||||
|
||||
// 添加MJ的基础配置信息
|
||||
let mjSettingRes: { code: number; data: any; message: any }
|
||||
let mjSettingRes: GeneralResponse.ErrorItem | GeneralResponse.SuccessItem
|
||||
if (isEmpty(mjSetting.id)) {
|
||||
// 新增
|
||||
mjSettingRes = this.AddMJSetting(mjSetting)
|
||||
|
||||
@ -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 {
|
||||
@ -169,7 +185,7 @@ export class BaseSoftWareService extends BaseService {
|
||||
MjSettingModel
|
||||
],
|
||||
path: dbPath,
|
||||
schemaVersion: 17, // 当前版本号
|
||||
schemaVersion: 19, // 当前版本号
|
||||
migration: migration
|
||||
}
|
||||
// 判断当前全局是不是又当前这个
|
||||
|
||||
@ -1,10 +1,5 @@
|
||||
import Realm, { UpdateMode } from 'realm'
|
||||
import path from 'path'
|
||||
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 { successMessage } from '../../../../main/Public/generalTools'
|
||||
import { BaseSoftWareService } from './softwareBasic.js'
|
||||
const { v4: uuidv4 } = require('uuid')
|
||||
|
||||
@ -63,12 +58,26 @@ export class SoftwareService extends BaseSoftWareService {
|
||||
*/
|
||||
GetSoftwareData() {
|
||||
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(
|
||||
software.toJSON(),
|
||||
res,
|
||||
'获取软件配置信息成功',
|
||||
'SoftwareService_GetSoftwareData'
|
||||
)
|
||||
|
||||
} catch (error) {
|
||||
global.logger.error(
|
||||
'SoftwareService_GetSoftwareData',
|
||||
@ -82,17 +91,15 @@ export class SoftwareService extends BaseSoftWareService {
|
||||
* 获取当前软件指定属性的数据
|
||||
* @param property 属性名称
|
||||
*/
|
||||
GetSoftWarePropertyData(property: string) {
|
||||
GetSoftWarePropertyData(property: string): string {
|
||||
try {
|
||||
let software = this.realm.objects('Software')
|
||||
if (software.length <= 0) {
|
||||
throw new Error('数据库中没有软件配置信息')
|
||||
}
|
||||
|
||||
let softwareData = software.toJSON()[0]
|
||||
let res = softwareData[property]
|
||||
|
||||
return successMessage(res, '获取软件配置信息成功', 'SoftwareService_GetSoftWarePropertyData')
|
||||
let res = softwareData[property] as string
|
||||
return res
|
||||
} catch (error) {
|
||||
global.logger.error(
|
||||
'SoftwareService_GetSoftWarePropertyData',
|
||||
|
||||
@ -15,6 +15,8 @@ if (!app.isPackaged) {
|
||||
scripts_path: path.join(__dirname, '../../resources/scripts'),
|
||||
db_path: path.join(__dirname, '../../resources/scripts/db'),
|
||||
project_path: path.join(__dirname, '../../project'),
|
||||
tts_path: path.join(__dirname, '../../tts'),
|
||||
|
||||
logger_path: path.join(__dirname, '../../resources/logger'),
|
||||
package_path: path.join(__dirname, '../../resources/package'),
|
||||
image_path: path.join(__dirname, '../../resources/image'),
|
||||
@ -82,6 +84,7 @@ if (!app.isPackaged) {
|
||||
scripts_path: path.join(__dirname, '../../../resources/scripts'),
|
||||
db_path: path.join(__dirname, '../../../resources/scripts/db'),
|
||||
project_path: path.join(__dirname, '../../../project'),
|
||||
tts_path: path.join(__dirname, '../../../tts'),
|
||||
logger_path: path.join(__dirname, '../../../resources/logger'),
|
||||
package_path: path.join(__dirname, '../../../resources/package'),
|
||||
discordScript: path.join(__dirname, '../../../resources/scripts/discordScript.js'),
|
||||
@ -141,5 +144,7 @@ if (!app.isPackaged) {
|
||||
|
||||
define['remotemj_api'] = 'https://api.laitool.net/'
|
||||
define['serverUrl'] = 'http://lapi.laitool.cn'
|
||||
define['hkServerUrl'] = 'https://api.laitool.cc/'
|
||||
define['bakServerUrl'] = 'https://bakapi.laitool.cc/'
|
||||
define['API'] = 'f85d39ed5a40fd09966f13f12b6cf0f0'
|
||||
export { define }
|
||||
export { define }
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
export const DEFINE_STRING = {
|
||||
SHOW_GLOBAL_MAIN_NOTIFICATION: 'SHOW_GLOBAL_MAIN_NOTIFICATION',
|
||||
OPEN_DEV_TOOLS_PASSWORD: 'OPEN_DEV_TOOLS_PASSWORD',
|
||||
OPEN_DEV_TOOLS: 'OPEN_DEV_TOOLS',
|
||||
GET_FILE_BASE64: 'GET_FILE_BASE64',
|
||||
@ -21,7 +22,6 @@ export const DEFINE_STRING = {
|
||||
SAVE_KEY_FRAME_SETTING: 'SAVE_KEY_FRAME_SETTING',
|
||||
MODIFY_SAMPLE_SETTING: 'MODIFY_SAMPLE_SETTING',
|
||||
GET_SETTING_Dafault_DATA: 'GET_SETTING_Dafault_DATA',
|
||||
GET_DRAFT_FILE_LIST: 'GET_DRAFT_FILE_LIST',
|
||||
GET_FRAME: 'GET_FRAME',
|
||||
PYTHON_ERROR: 'PYTHON_ERROR',
|
||||
PYTHON_CLOSE: 'PYTHON_CLOSE',
|
||||
@ -144,9 +144,16 @@ export const DEFINE_STRING = {
|
||||
NORMAL_PERMISSION: 'NORMAL_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: {
|
||||
LOAD_SD_SERVICE_DATA: 'LOAD_SD_SERVICE_DATA',
|
||||
TXT2IMG: 'TXT2IMG'
|
||||
TXT2IMG: 'TXT2IMG',
|
||||
SD_MERGE_PROMPT: "SD_MERGE_PROMPT"
|
||||
},
|
||||
MJ: {
|
||||
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_ROBOT_MODEL: 'GET_MJ_IMAGE_ROBOT_MODEL',
|
||||
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: {
|
||||
OPERATE_REFRASH_DISCORD_URL: 'OPERATE_REFRASH_DISCORD_URL',
|
||||
@ -195,6 +205,8 @@ export const DEFINE_STRING = {
|
||||
BATCH_PROCESS_IMAGE_RESULT: 'BATCH_PROCESS_IMAGE_RESULT'
|
||||
},
|
||||
BOOK: {
|
||||
MAIN_DATA_RETURN: 'MAIN_DATA_RETURN', // 监听任务返回
|
||||
|
||||
GET_BOOK_TYPE: 'GET_BOOK_TYPE',
|
||||
ADD_OR_MODIFY_BOOK: 'ADD_OR_MODIFY_BOOK',
|
||||
GET_BOOK_DATA: 'GET_BOOK_DATA',
|
||||
@ -204,7 +216,39 @@ export const DEFINE_STRING = {
|
||||
SAVE_BOOK_SUBTITLE_POSITION: 'SAVE_BOOK_SUBTITLE_POSITION',
|
||||
OPEN_BOOK_SUBTITLE_POSITION_SCREENSHOT: 'OPEN_BOOK_SUBTITLE_POSITION_SCREENSHOT',
|
||||
GET_CURRENT_FRAME_TEXT: 'GET_CURRENT_FRAME_TEXT',
|
||||
GET_VIDEO_FRAME_TEXT: 'GET_VIDEO_FRAME_TEXT'
|
||||
GET_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: {
|
||||
OPEN_FILE: 'OPEN_FILE',
|
||||
@ -225,7 +269,9 @@ export const DEFINE_STRING = {
|
||||
GET_REMOTE_MJ_SETTINGS: 'GET_REMOTE_MJ_SETTINGS',
|
||||
ADD_REMOTE_MJ_SETTING: 'ADD_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: {
|
||||
GET_SORT_OPTIONS: 'GET_SORT_OPTIONS',
|
||||
@ -235,11 +281,16 @@ export const DEFINE_STRING = {
|
||||
},
|
||||
TTS: {
|
||||
GET_TTS_CONFIG: 'GET_TTS_CONFIG',
|
||||
GENERATE_AUDIO: 'GENERATE_AUDIO',
|
||||
SAVE_TTS_CONFIG: 'SAVE_TTS_CONFIG'
|
||||
},
|
||||
WRITE: {
|
||||
GET_WRITE_CONFIG: 'GET_WRITE_CONFIG',
|
||||
SAVE_WRITE_CONFIG: 'SAVE_WRITE_CONFIG',
|
||||
ACTION_START: 'ACTION_START'
|
||||
},
|
||||
DB: {
|
||||
UPDATE_BOOK_TASK_DATA: "UPDATE_BOOK_TASK_DATA",
|
||||
UPDATE_BOOK_TASK_DETAIL_DATA: "UPDATE_BOOK_TASK_DETAIL_DATA"
|
||||
}
|
||||
}
|
||||
@ -7,6 +7,16 @@ export enum BookType {
|
||||
MJ_REVERSE = 'mj_reverse'
|
||||
}
|
||||
|
||||
// 出图方式
|
||||
export enum BookImageCategory {
|
||||
// MJ
|
||||
MJ = 'mj',
|
||||
// SD
|
||||
SD = 'sd',
|
||||
// D3
|
||||
D3 = 'd3'
|
||||
}
|
||||
|
||||
|
||||
|
||||
export enum MJCategroy {
|
||||
@ -39,10 +49,16 @@ export enum BookBackTaskType {
|
||||
RECOGNIZE = 'recognize',
|
||||
// 抽帧
|
||||
FRAME = 'frame',
|
||||
// 反推
|
||||
REVERSE = 'reverse',
|
||||
// 生成图片
|
||||
IMAGE = 'image',
|
||||
// MJ反推
|
||||
MJ_REVERSE = BookType.MJ_REVERSE,
|
||||
// SD反推
|
||||
SD_REVERSE = BookType.SD_REVERSE,
|
||||
// MJ生成图片
|
||||
MJ_IMAGE = 'mj_image',
|
||||
// SD 生成图片
|
||||
SD_IMAGE = 'sd_image',
|
||||
// D3 生成图片
|
||||
D3_IMAGE = 'd3_image',
|
||||
// 高清
|
||||
HD = 'hd',
|
||||
// 合成视频
|
||||
@ -63,7 +79,9 @@ export enum BookBackTaskStatus {
|
||||
// 完成
|
||||
DONE = 'done',
|
||||
// 失败
|
||||
FAIL = 'fail'
|
||||
FAIL = 'fail',
|
||||
// 重连
|
||||
RECONNECT = 'reconnect'
|
||||
}
|
||||
|
||||
export enum TaskExecuteType {
|
||||
@ -74,6 +92,16 @@ export enum TaskExecuteType {
|
||||
OPERATE = 'operate'
|
||||
}
|
||||
|
||||
// 弹窗类型
|
||||
export enum DialogType {
|
||||
// 单独弹窗
|
||||
DIALOG = 'dialog',
|
||||
// 消息提示
|
||||
MESSAGE = 'message',
|
||||
// 右上角通知
|
||||
NOTIFICATION = 'notification'
|
||||
}
|
||||
|
||||
/**
|
||||
* 小说任务状态
|
||||
*/
|
||||
@ -162,3 +190,40 @@ export enum BookTaskStatus {
|
||||
// 合成视频失败
|
||||
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'
|
||||
}
|
||||
|
||||
@ -19,3 +19,31 @@ export enum MJRobotType {
|
||||
// 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'
|
||||
|
||||
}
|
||||
|
||||
@ -45,6 +45,31 @@ export enum SoftColor {
|
||||
// 棕黄色
|
||||
BROWN_YELLOW = '#e18a3b',
|
||||
|
||||
// 橘色
|
||||
ORANGE = '#ee7959',
|
||||
|
||||
// 朱颜酡
|
||||
ZHUYANTUO = '#f29a76',
|
||||
|
||||
// 错误红色
|
||||
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
@ -0,0 +1,6 @@
|
||||
export enum TaskQueueType {
|
||||
// 内存添加
|
||||
CACHE_ADD = 'cache_add',
|
||||
// 数据库添加
|
||||
DB_ADD = 'db_add'
|
||||
}
|
||||
23
src/define/enum/translate.ts
Normal 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',
|
||||
}
|
||||
@ -8,6 +8,23 @@ export enum SubtitleSavePositionType {
|
||||
// 分镜视频
|
||||
STORYBOARD_VIDEO = 'storyboard_video',
|
||||
|
||||
// 设置,只是框选
|
||||
SETTING = 'setting',
|
||||
|
||||
// 其他类型
|
||||
OTHER = 'other'
|
||||
}
|
||||
|
||||
// 图片去除水印方法,返回数据的格式
|
||||
export enum WaterMarkResponseDateType {
|
||||
// 返回的数据类型
|
||||
ArrayBuffer = "arrayBuffer",
|
||||
// 直接将文件写道本地
|
||||
File = "file"
|
||||
}
|
||||
|
||||
|
||||
export enum RemoveWatermarkType {
|
||||
LOCAL_LAMA = 'local_lama',
|
||||
IOPAINT = 'iopaint'
|
||||
}
|
||||
2754
src/define/iamgeStyleDefine.ts
Normal file
@ -2,7 +2,7 @@
|
||||
let fspromises = require('fs').promises;
|
||||
import { get, cloneDeep } from 'lodash';
|
||||
import { define } from '../define';
|
||||
import { errorMessage } from '../../main/generalTools';
|
||||
import { errorMessage } from '../../main/Public/generalTools';
|
||||
|
||||
export class DynamicSetting {
|
||||
constructor(global) {
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { successMessage } from "../../main/generalTools";
|
||||
import { successMessage } from "../../main/Public/generalTools";
|
||||
|
||||
export class MjSetting {
|
||||
constructor(golbal) {
|
||||
|
||||
@ -2,7 +2,7 @@ import { get } from "lodash";
|
||||
import { define } from "../define";
|
||||
let fspromises = require("fs").promises;
|
||||
import { Tools } from "../../main/tools";
|
||||
import { errorMessage } from "../../main/generalTools";
|
||||
import { errorMessage } from "../../main/Public/generalTools";
|
||||
let tools = new Tools();
|
||||
|
||||
// Create a shared object
|
||||
|
||||
@ -1,195 +1,196 @@
|
||||
|
||||
let fspromises = require('fs').promises;
|
||||
import { get, cloneDeep } from 'lodash';
|
||||
import { define } from './define';
|
||||
import path from 'path';
|
||||
import { Tools } from '../main/tools';
|
||||
const { v4: uuidv4 } = require('uuid');
|
||||
let fspromises = require('fs').promises
|
||||
import { get, cloneDeep } from 'lodash'
|
||||
import { define } from './define'
|
||||
import path from 'path'
|
||||
import { Tools } from '../main/tools'
|
||||
const { v4: uuidv4 } = require('uuid')
|
||||
|
||||
export class TagDefine {
|
||||
constructor(global) {
|
||||
this.global = global;
|
||||
this.tools = new Tools();
|
||||
}
|
||||
constructor(global) {
|
||||
this.global = global
|
||||
this.tools = new Tools()
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取tag选择模式(标签和下拉select)
|
||||
*/
|
||||
async getTagSelectModel() {
|
||||
return {
|
||||
code: 1,
|
||||
data:
|
||||
[
|
||||
{ label: "标签", value: "tag" },
|
||||
{ label: "下拉", value: "drop" },
|
||||
]
|
||||
/**
|
||||
* 获取tag选择模式(标签和下拉select)
|
||||
*/
|
||||
async getTagSelectModel() {
|
||||
return {
|
||||
code: 1,
|
||||
data: [
|
||||
{ label: '标签', value: 'tag' },
|
||||
{ label: '下拉', value: 'drop' }
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过指定的类型,获取数据
|
||||
* @param {*} type default:在代码中写死的 dynamic:用户自定义的 all:写死的和自定义的合并返回
|
||||
* @param {*} property 要返回的属性的名称,若是传入null,返回整个属性的数据
|
||||
* @param {*} defaultData 默认数据,默认值为null
|
||||
* @returns
|
||||
*/
|
||||
async getTagDataByTypeAndProperty(type, property, defaultData = null) {
|
||||
try {
|
||||
let res = []
|
||||
// 获取自定义的GPT数据
|
||||
let tag_setting = JSON.parse(await fspromises.readFile(define.tag_setting, 'utf-8'))
|
||||
let data = get(tag_setting, property, {})
|
||||
// 若是传入的属性名为null,直接返回当前tags里面的所有的数据
|
||||
if (property == null) {
|
||||
data = tag_setting
|
||||
}
|
||||
|
||||
if (type == 'default') {
|
||||
// res = get(this, property, defaultData);
|
||||
} else if (type == 'dynamic') {
|
||||
res = data
|
||||
} else if (type == 'all') {
|
||||
let tmp_arr = cloneDeep(get([], property, defaultData))
|
||||
tmp_arr = tmp_arr.concat(data)
|
||||
res = tmp_arr
|
||||
} else {
|
||||
throw new Error(`不存在的类型 : ${value}`)
|
||||
}
|
||||
if (property) {
|
||||
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')) {
|
||||
res.character_tags.forEach((item) => {
|
||||
if (item.show_image && item.show_image != '') {
|
||||
item.show_image = path.join(define.image_path, item.show_image)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 通过指定的类型,获取数据
|
||||
* @param {*} type default:在代码中写死的 dynamic:用户自定义的 all:写死的和自定义的合并返回
|
||||
* @param {*} property 要返回的属性的名称,若是传入null,返回整个属性的数据
|
||||
* @param {*} defaultData 默认数据,默认值为null
|
||||
* @returns
|
||||
*/
|
||||
async getTagDataByTypeAndProperty(type, property, defaultData = null) {
|
||||
try {
|
||||
let res = [];
|
||||
// 获取自定义的GPT数据
|
||||
let tag_setting = JSON.parse(await fspromises.readFile(define.tag_setting, 'utf-8'));
|
||||
let data = get(tag_setting, property, {});
|
||||
// 若是传入的属性名为null,直接返回当前tags里面的所有的数据
|
||||
if (property == null) {
|
||||
data = tag_setting;
|
||||
}
|
||||
|
||||
if (type == "default") {
|
||||
// res = get(this, property, defaultData);
|
||||
} else if (type == "dynamic") {
|
||||
res = data;
|
||||
} else if (type == "all") {
|
||||
let tmp_arr = cloneDeep(get([], property, defaultData));
|
||||
tmp_arr = tmp_arr.concat(data);
|
||||
res = tmp_arr;
|
||||
}
|
||||
else {
|
||||
throw new Error(`不存在的类型 : ${value}`);
|
||||
}
|
||||
|
||||
// 返回之前,判断里面是不是有预览图片路径
|
||||
if (res.hasOwnProperty("character_tags")) {
|
||||
res.character_tags.forEach(item => {
|
||||
if (item.show_image && item.show_image != "") {
|
||||
item.show_image = path.join(define.image_path, item.show_image);
|
||||
}
|
||||
});
|
||||
}
|
||||
if (res.hasOwnProperty("scene_tags")) {
|
||||
res.scene_tags.forEach(item => {
|
||||
if (item.show_image && item.show_image != "") {
|
||||
item.show_image = path.join(define.image_path, item.show_image);
|
||||
}
|
||||
});
|
||||
}
|
||||
if (res.hasOwnProperty("style_tags")) {
|
||||
res.style_tags.forEach(item => {
|
||||
if (item.show_image && item.show_image != "") {
|
||||
item.show_image = path.join(define.image_path, item.show_image);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
code: 1,
|
||||
data: res
|
||||
}
|
||||
} catch (error) {
|
||||
return {
|
||||
code: 0,
|
||||
message: error.toString()
|
||||
if (res.hasOwnProperty('scene_tags')) {
|
||||
res.scene_tags.forEach((item) => {
|
||||
if (item.show_image && item.show_image != '') {
|
||||
item.show_image = path.join(define.image_path, item.show_image)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存gpt指定的属性数据,判断value中的ID是不是存在,存在直接覆盖,不存在追加
|
||||
* @param {*} value
|
||||
* @param {*} property
|
||||
*/
|
||||
async saveTagPropertyData(value) {
|
||||
try {
|
||||
let property = value[1];
|
||||
value = JSON.parse(value[0]);
|
||||
let tmp_key = uuidv4();
|
||||
|
||||
// 特殊操作。为角色和场景的时候,需要copy图片
|
||||
if (property == "character_tags" || property == "scene_tags" || property == "style_tags") {
|
||||
let show_image = value.show_image;
|
||||
if (show_image && show_image != "") {
|
||||
let file_name = `c_s/${value.key ? value.key : tmp_key}.png`
|
||||
let new_image_path = path.join(define.image_path, file_name);
|
||||
await this.tools.copyFileOrDirectory(show_image, new_image_path);
|
||||
value.show_image = file_name;
|
||||
value.children?.forEach(item => {
|
||||
item.show_image = file_name;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 获取自定义的GPT数据
|
||||
let tag_setting = JSON.parse(await fspromises.readFile(define.tag_setting, 'utf-8'));
|
||||
let tag = get(tag_setting, property, []);
|
||||
if (value.key) {
|
||||
// 判断当前ID的数据是否存在,存在覆盖,不存在追加
|
||||
let index = tag.findIndex(item => item.key == value.key);
|
||||
value.value = value.key;
|
||||
if (index < 0) {
|
||||
// 判断相同名字的数据是不是存在,存在报错
|
||||
if (tag.some(item => item.label == value.label)) {
|
||||
throw new Error("已存在相同名称的数据,请修改名称后再保存");
|
||||
}
|
||||
tag.push(value);
|
||||
} else {
|
||||
tag[index] = value;
|
||||
}
|
||||
} else {
|
||||
// 判断相同名字的数据是不是存在,存在报错
|
||||
if (tag.some(item => item.label == value.label)) {
|
||||
throw new Error("已存在相同名称的数据,请修改名称后再保存");
|
||||
}
|
||||
value.key = tmp_key;
|
||||
value.value = value.key;
|
||||
tag.push(value);
|
||||
}
|
||||
tag_setting[property] = tag;
|
||||
// 写入文件
|
||||
await fspromises.writeFile(define.tag_setting, JSON.stringify(tag_setting));
|
||||
return {
|
||||
code: 1,
|
||||
message: "保存成功"
|
||||
}
|
||||
} catch (error) {
|
||||
return {
|
||||
code: 0,
|
||||
message: error.toString()
|
||||
if (res.hasOwnProperty('style_tags')) {
|
||||
res.style_tags.forEach((item) => {
|
||||
if (item.show_image && item.show_image != '') {
|
||||
item.show_image = path.join(define.image_path, item.show_image)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
code: 1,
|
||||
data: res
|
||||
}
|
||||
} catch (error) {
|
||||
return {
|
||||
code: 0,
|
||||
message: error.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除自定义GPT指定属性中的指定ID的数据
|
||||
* @param {*} id
|
||||
* @param {*} property
|
||||
*/
|
||||
async deleteTagPropertyData(value) {
|
||||
try {
|
||||
let property = value[1];
|
||||
let id = value[0];
|
||||
// 获取自定义的GPT数据
|
||||
let tag_setting = JSON.parse(await fspromises.readFile(define.tag_setting, 'utf-8'));
|
||||
let tags = tag_setting[property] ? tag_setting[property] : [];
|
||||
// 判断当前ID的数据是否存在,存在删除
|
||||
let index = tags.findIndex(item => item.key == id);
|
||||
if (index >= 0) {
|
||||
tags.splice(index, 1);
|
||||
}
|
||||
// 将修改后的数据保存
|
||||
tag_setting[property] = tags;
|
||||
// 写入文件
|
||||
await fspromises.writeFile(define.tag_setting, JSON.stringify(tag_setting));
|
||||
return {
|
||||
code: 1,
|
||||
message: "删除成功"
|
||||
}
|
||||
/**
|
||||
* 保存gpt指定的属性数据,判断value中的ID是不是存在,存在直接覆盖,不存在追加
|
||||
* @param {*} value
|
||||
* @param {*} property
|
||||
*/
|
||||
async saveTagPropertyData(value) {
|
||||
try {
|
||||
let property = value[1]
|
||||
value = JSON.parse(value[0])
|
||||
let tmp_key = uuidv4()
|
||||
|
||||
} catch (error) {
|
||||
return {
|
||||
code: 0,
|
||||
message: error.toString()
|
||||
}
|
||||
// 特殊操作。为角色和场景的时候,需要copy图片
|
||||
if (property == 'character_tags' || property == 'scene_tags' || property == 'style_tags') {
|
||||
let show_image = value.show_image
|
||||
if (show_image && show_image != '') {
|
||||
let file_name = `c_s/${value.key ? value.key : tmp_key}.png`
|
||||
let new_image_path = path.join(define.image_path, file_name)
|
||||
await this.tools.copyFileOrDirectory(show_image, new_image_path)
|
||||
value.show_image = file_name
|
||||
value.children?.forEach((item) => {
|
||||
item.show_image = file_name
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
// 获取自定义的GPT数据
|
||||
let tag_setting = JSON.parse(await fspromises.readFile(define.tag_setting, 'utf-8'))
|
||||
let tag = get(tag_setting, property, [])
|
||||
if (value.key) {
|
||||
// 判断当前ID的数据是否存在,存在覆盖,不存在追加
|
||||
let index = tag.findIndex((item) => item.key == value.key)
|
||||
value.value = value.key
|
||||
if (index < 0) {
|
||||
// 判断相同名字的数据是不是存在,存在报错
|
||||
if (tag.some((item) => item.label == value.label)) {
|
||||
throw new Error('已存在相同名称的数据,请修改名称后再保存')
|
||||
}
|
||||
tag.push(value)
|
||||
} else {
|
||||
tag[index] = value
|
||||
}
|
||||
} else {
|
||||
// 判断相同名字的数据是不是存在,存在报错
|
||||
if (tag.some((item) => item.label == value.label)) {
|
||||
throw new Error('已存在相同名称的数据,请修改名称后再保存')
|
||||
}
|
||||
value.key = tmp_key
|
||||
value.value = value.key
|
||||
tag.push(value)
|
||||
}
|
||||
tag_setting[property] = tag
|
||||
// 写入文件
|
||||
await fspromises.writeFile(define.tag_setting, JSON.stringify(tag_setting))
|
||||
return {
|
||||
code: 1,
|
||||
message: '保存成功'
|
||||
}
|
||||
} catch (error) {
|
||||
return {
|
||||
code: 0,
|
||||
message: error.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除自定义GPT指定属性中的指定ID的数据
|
||||
* @param {*} id
|
||||
* @param {*} property
|
||||
*/
|
||||
async deleteTagPropertyData(value) {
|
||||
try {
|
||||
let property = value[1]
|
||||
let id = value[0]
|
||||
// 获取自定义的GPT数据
|
||||
let tag_setting = JSON.parse(await fspromises.readFile(define.tag_setting, 'utf-8'))
|
||||
let tags = tag_setting[property] ? tag_setting[property] : []
|
||||
// 判断当前ID的数据是否存在,存在删除
|
||||
let index = tags.findIndex((item) => item.key == id)
|
||||
if (index >= 0) {
|
||||
tags.splice(index, 1)
|
||||
}
|
||||
// 将修改后的数据保存
|
||||
tag_setting[property] = tags
|
||||
// 写入文件
|
||||
await fspromises.writeFile(define.tag_setting, JSON.stringify(tag_setting))
|
||||
return {
|
||||
code: 1,
|
||||
message: '删除成功'
|
||||
}
|
||||
} catch (error) {
|
||||
return {
|
||||
code: 0,
|
||||
message: error.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -17,7 +17,7 @@ type configure = {
|
||||
proxy?: string
|
||||
rate?: string
|
||||
pitch?: string
|
||||
volume?: string
|
||||
volumn?: string
|
||||
}
|
||||
|
||||
export class EdgeTTS {
|
||||
@ -28,7 +28,7 @@ export class EdgeTTS {
|
||||
private proxy: string | null | undefined
|
||||
private rate: string
|
||||
private pitch: string
|
||||
private volume: string
|
||||
private volumn: string
|
||||
|
||||
constructor({
|
||||
voice = 'zh-CN-XiaoyiNeural',
|
||||
@ -38,7 +38,7 @@ export class EdgeTTS {
|
||||
proxy,
|
||||
rate = 'default',
|
||||
pitch = 'default',
|
||||
volume = 'default'
|
||||
volumn = 'default'
|
||||
}: configure = {}) {
|
||||
this.voice = voice
|
||||
this.lang = lang
|
||||
@ -47,7 +47,7 @@ export class EdgeTTS {
|
||||
this.proxy = proxy
|
||||
this.rate = rate
|
||||
this.pitch = pitch
|
||||
this.volume = volume
|
||||
this.volumn = volumn
|
||||
}
|
||||
|
||||
async _connectWebSocket(): Promise<WebSocket> {
|
||||
@ -138,7 +138,9 @@ export class EdgeTTS {
|
||||
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}">
|
||||
<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}
|
||||
</prosody>
|
||||
</voice>
|
||||
|
||||
@ -1,11 +1,23 @@
|
||||
import { ipcMain } from 'electron'
|
||||
import { DEFINE_STRING } from '../../define/define_string'
|
||||
import { ReverseBook } from '../ReverseManage/Book/ReverseBook'
|
||||
import { BasicReverse } from '../Task/basicReverse'
|
||||
import { WatermarkAndSubtitle } from '../Task/watermarkAndSubtitle'
|
||||
import { ReverseBook } from '../Service/Book/ReverseBook'
|
||||
import { BasicReverse } from '../Service/Book/basicReverse'
|
||||
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 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() {
|
||||
// 获取样式图片的子列表
|
||||
@ -21,6 +33,16 @@ export function BookIpc() {
|
||||
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 一键反推
|
||||
|
||||
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) =>
|
||||
watermarkAndSubtitle.SaveBookSubtitlePosition(value)
|
||||
subtitle.SaveBookSubtitlePosition(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) =>
|
||||
watermarkAndSubtitle.GetCurrentFrameText(value)
|
||||
subtitle.GetCurrentFrameText(value)
|
||||
)
|
||||
|
||||
// 获取当前视频中的所有的字幕
|
||||
ipcMain.handle(DEFINE_STRING.BOOK.GET_VIDEO_FRAME_TEXT, async (event,value)=>{
|
||||
watermarkAndSubtitle.GetVideoFrameText(value)
|
||||
ipcMain.handle(DEFINE_STRING.BOOK.GET_VIDEO_FRAME_TEXT, async (event, value) => {
|
||||
subtitle.GetVideoFrameText(value)
|
||||
})
|
||||
|
||||
|
||||
//#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
|
||||
}
|
||||
|
||||
37
src/main/IPCEvent/dbIpc.ts
Normal 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 }
|
||||
@ -71,7 +71,6 @@ function WinddowUrlRefresh(thisWindow) {
|
||||
channelID: channelID
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
// 判断是不是需要登录(登录提示)
|
||||
|
||||
@ -1,46 +1,51 @@
|
||||
import {
|
||||
ipcMain
|
||||
} from "electron";
|
||||
import { ipcMain } from 'electron'
|
||||
import { DEFINE_STRING } from '../../define/define_string'
|
||||
import { Tools } from "../tools";
|
||||
import path from "path";
|
||||
import { errorMessage, successMessage } from "../generalTools";
|
||||
let tools = new Tools();
|
||||
import { Tools } from '../tools'
|
||||
import path from 'path'
|
||||
import { errorMessage, successMessage } from '../Public/generalTools'
|
||||
let tools = new Tools()
|
||||
|
||||
function GlobalIpc() {
|
||||
/**
|
||||
* 将传入的文件地址修改为base64
|
||||
*/
|
||||
ipcMain.handle(DEFINE_STRING.GET_FILE_BASE64, async (event, value) => {
|
||||
try {
|
||||
value = path.normalize(value)
|
||||
//检查文件或者时文件夹是不是存在
|
||||
let isExists = await tools.checkExists(value)
|
||||
console.log('isExists', value, isExists)
|
||||
// 获取文件,将其转换为base64
|
||||
if (!isExists) {
|
||||
throw new Error('文件不存在')
|
||||
}
|
||||
return successMessage(await tools.readFileBase64(value))
|
||||
} catch (error) {
|
||||
return errorMessage('获取文件失败' + error)
|
||||
}
|
||||
})
|
||||
|
||||
/**
|
||||
* 将传入的文件地址修改为base64
|
||||
*/
|
||||
ipcMain.handle(DEFINE_STRING.GET_FILE_BASE64, async (event, value) => {
|
||||
try {
|
||||
value = path.normalize(value)
|
||||
//检查文件或者时文件夹是不是存在
|
||||
let isExists = await tools.checkExists(value);
|
||||
console.log("isExists", value, isExists);
|
||||
// 获取文件,将其转换为base64
|
||||
if (!isExists) {
|
||||
throw new Error("文件不存在");
|
||||
}
|
||||
return successMessage(await tools.readFileBase64(value));
|
||||
} catch (error) {
|
||||
return errorMessage("获取文件失败" + error)
|
||||
}
|
||||
});
|
||||
ipcMain.on(DEFINE_STRING.OPEN_DEV_TOOLS, (event) => {
|
||||
global.newWindow[0].win.webContents.openDevTools()
|
||||
})
|
||||
|
||||
ipcMain.on(DEFINE_STRING.OPEN_DEV_TOOLS, (event) => {
|
||||
global.newWindow[0].win.webContents.openDevTools();
|
||||
})
|
||||
ipcMain.handle(DEFINE_STRING.OPEN_DEV_TOOLS_PASSWORD, (event, value) => {
|
||||
if (value === '297ab55d41e9f5d3eba95b9df432f991') {
|
||||
return successMessage('打开成功')
|
||||
} else {
|
||||
return errorMessage('管理控制台密码错误')
|
||||
}
|
||||
})
|
||||
|
||||
ipcMain.handle(DEFINE_STRING.OPEN_DEV_TOOLS_PASSWORD, (event, value) => {
|
||||
if (value === "297ab55d41e9f5d3eba95b9df432f991") {
|
||||
return successMessage("打开成功")
|
||||
} else {
|
||||
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 {
|
||||
GlobalIpc
|
||||
}
|
||||
export { GlobalIpc }
|
||||
|
||||
@ -2,7 +2,7 @@ import { ipcMain } from 'electron'
|
||||
import { DEFINE_STRING } from '../../define/define_string'
|
||||
import { Image } from '../Public/Image'
|
||||
import { LOGGER_DEFINE } from '../../define/logger_define'
|
||||
import { errorMessage } from '../generalTools'
|
||||
import { errorMessage } from '../Public/generalTools'
|
||||
let image = new Image(global)
|
||||
|
||||
function ImageIpc() {
|
||||
|
||||
@ -14,8 +14,9 @@ import { ImageIpc } from './imageIpc.js'
|
||||
import { SystemIpc } from './systemIpc.js'
|
||||
import { BookIpc } from './bookIpc.js'
|
||||
import { TTSIpc } from './ttsIpc.js'
|
||||
import { DBIpc } from './dbIpc'
|
||||
|
||||
export function RegisterIpc(createWindow) {
|
||||
export async function RegisterIpc(createWindow) {
|
||||
PromptIpc()
|
||||
SettingIpc()
|
||||
ImageGenerateIpc()
|
||||
@ -24,6 +25,7 @@ export function RegisterIpc(createWindow) {
|
||||
TranslateIpc()
|
||||
GptIpc()
|
||||
SdIpc()
|
||||
await DBIpc()
|
||||
MjIpc()
|
||||
MainIpc(createWindow)
|
||||
OriginalImageGenerateIpc()
|
||||
|
||||
@ -1,87 +1,154 @@
|
||||
import { ipcMain } from "electron"
|
||||
import { DEFINE_STRING } from "../../define/define_string"
|
||||
import { MjSimple } from "../discord/mjSimple"
|
||||
import { TagCustomize } from "../Original/TagCustomize"
|
||||
import { ipcMain } from 'electron'
|
||||
import { DEFINE_STRING } from '../../define/define_string'
|
||||
import { MjSimple } from '../discord/mjSimple'
|
||||
import { TagCustomize } from '../Original/TagCustomize'
|
||||
import { MJOriginalImageGenerate } from '../Original/MJOriginalImageGenerate'
|
||||
import { PublicMethod } from "../Public/publicMethod"
|
||||
import { DiscordSimple } from "../discord/discordSimple"
|
||||
import { Tools } from "../tools"
|
||||
import { PublicMethod } from '../Public/publicMethod'
|
||||
import { DiscordSimple } from '../discord/discordSimple'
|
||||
import { Tools } from '../tools'
|
||||
import path from 'path'
|
||||
import { MJOpt } from '../Service/MJ/mj'
|
||||
|
||||
let mjSimple = new MjSimple(global)
|
||||
let discordSimple = new DiscordSimple(null)
|
||||
let tagCustomize = new TagCustomize(global);
|
||||
let mJOriginalImageGenerate = new MJOriginalImageGenerate(global);
|
||||
let publicMethod = new PublicMethod(global);
|
||||
let tools = new Tools();
|
||||
|
||||
let tagCustomize = new TagCustomize(global)
|
||||
let mJOriginalImageGenerate = new MJOriginalImageGenerate(global)
|
||||
let publicMethod = new PublicMethod(global)
|
||||
let mjOpt = new MJOpt()
|
||||
|
||||
function MjIpc() {
|
||||
// 监听保存mj的文案配置信息
|
||||
ipcMain.handle(DEFINE_STRING.MJ.SAVE_WORD_SRT, async (event, value) => await mjSimple.SvaeMJWordSrt(value));
|
||||
// 监听保存mj的文案配置信息
|
||||
ipcMain.handle(
|
||||
DEFINE_STRING.MJ.SAVE_WORD_SRT,
|
||||
async (event, value) => await mjSimple.SvaeMJWordSrt(value)
|
||||
)
|
||||
|
||||
// 监听获取MJ的文件配置信息
|
||||
ipcMain.handle(DEFINE_STRING.MJ.GET_MJ_CONFIG_SRT_INFORMATION, async (event, value) => await mjSimple.GetMJConfigSrtInformation(value));
|
||||
// 监听获取MJ的文件配置信息
|
||||
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 原创生图
|
||||
ipcMain.handle(DEFINE_STRING.MJ.ORIGINAL_MJ_IMAGE_GENERATE, async (event, value) => await mJOriginalImageGenerate.OriginalMJImageGenerate(value));
|
||||
// MJ 原创生图
|
||||
ipcMain.handle(
|
||||
DEFINE_STRING.MJ.ORIGINAL_MJ_IMAGE_GENERATE,
|
||||
async (event, value) => await mJOriginalImageGenerate.OriginalMJImageGenerate(value)
|
||||
)
|
||||
|
||||
// 获取discord的频道机器人
|
||||
ipcMain.handle(DEFINE_STRING.MJ.GET_CHANNEL_ROBOTS, async (event, value) => await mjSimple.GetChannelRobots(value));
|
||||
// 获取discord的频道机器人
|
||||
ipcMain.handle(
|
||||
DEFINE_STRING.MJ.GET_CHANNEL_ROBOTS,
|
||||
async (event, value) => await mjSimple.GetChannelRobots(value)
|
||||
)
|
||||
|
||||
// 获取MJ生图的方式
|
||||
// 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());
|
||||
// 获取MJ生图的方式
|
||||
// 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()
|
||||
)
|
||||
|
||||
// MJ生成的图片分割
|
||||
ipcMain.handle(DEFINE_STRING.MJ.IMAGE_SPLIT, async (event, value) => await mJOriginalImageGenerate.ImageSplit(value));
|
||||
// MJ生成的图片分割
|
||||
ipcMain.handle(
|
||||
DEFINE_STRING.MJ.IMAGE_SPLIT,
|
||||
async (event, value) => await mJOriginalImageGenerate.ImageSplit(value)
|
||||
)
|
||||
|
||||
// 添加MJ敏感词
|
||||
ipcMain.handle(DEFINE_STRING.MJ.ADD_MJ_BAD_PROMPT, async (event, value) => await mjSimple.AddMJBadPrompt(value));
|
||||
// 添加MJ敏感词
|
||||
ipcMain.handle(
|
||||
DEFINE_STRING.MJ.ADD_MJ_BAD_PROMPT,
|
||||
async (event, value) => await mjSimple.AddMJBadPrompt(value)
|
||||
)
|
||||
|
||||
// 添加MJ敏感词检查
|
||||
ipcMain.handle(DEFINE_STRING.MJ.MJ_BAD_PROMPT_CHECK, async (event, value) => await mjSimple.MJBadPromptCheck(value));
|
||||
// 添加MJ敏感词检查
|
||||
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图片的所有的分割尺寸
|
||||
ipcMain.handle(DEFINE_STRING.MJ.GET_MJ_IMAGE_SCALE, async (event) => await mjSimple.GetMJImageScale());
|
||||
// 获取MJ图片的所有的分割尺寸
|
||||
ipcMain.handle(
|
||||
DEFINE_STRING.MJ.GET_MJ_IMAGE_SCALE,
|
||||
async (event) => await mjSimple.GetMJImageScale()
|
||||
)
|
||||
|
||||
// 获取所有的MJ生图模型
|
||||
ipcMain.handle(DEFINE_STRING.MJ.GET_MJ_IMAGE_ROBOT_MODEL, async (event) => await mjSimple.GetMJImageRobotModel());
|
||||
// 获取所有的MJ生图模型
|
||||
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界面创建消息,并修改数据
|
||||
*/
|
||||
ipcMain.on(DEFINE_STRING.DISCORD.CREATE_MESSAGE, async (event, value) => await discordSimple.DiscordCreateMessage(value));
|
||||
/**
|
||||
* 监听DISCORD界面创建消息,并修改数据
|
||||
*/
|
||||
ipcMain.on(
|
||||
DEFINE_STRING.DISCORD.CREATE_MESSAGE,
|
||||
async (event, value) => await discordSimple.DiscordCreateMessage(value)
|
||||
)
|
||||
|
||||
/**
|
||||
* 监听DISCORD界面的更新消息,并修改数据
|
||||
*/
|
||||
ipcMain.on(DEFINE_STRING.DISCORD.UPDATE_MESSAGE, async (event, value) => await discordSimple.DiscordUpdateMessage(value));
|
||||
/**
|
||||
* 监听DISCORD界面的更新消息,并修改数据
|
||||
*/
|
||||
ipcMain.on(
|
||||
DEFINE_STRING.DISCORD.UPDATE_MESSAGE,
|
||||
async (event, value) => await discordSimple.DiscordUpdateMessage(value)
|
||||
)
|
||||
|
||||
/**
|
||||
* 监听DISCORD界面的删除消息,并修改数据
|
||||
*/
|
||||
ipcMain.on(DEFINE_STRING.DISCORD.DELETE_MESSAGE, async (event, value) => await discordSimple.DiscordDeleteMessage(value));
|
||||
/**
|
||||
* 监听DISCORD界面的删除消息,并修改数据
|
||||
*/
|
||||
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 {
|
||||
MjIpc
|
||||
}
|
||||
export { MjIpc }
|
||||
|
||||
@ -1,21 +1,26 @@
|
||||
import { ipcMain } from "electron";
|
||||
import { ipcMain } from 'electron'
|
||||
import { DEFINE_STRING } from '../../define/define_string'
|
||||
import { Prompt } from "../Public/Prompt";
|
||||
let prompt = new Prompt();
|
||||
import { Prompt } from '../Public/Prompt'
|
||||
let prompt = new Prompt()
|
||||
|
||||
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,指定路径)
|
||||
ipcMain.handle(DEFINE_STRING.PROMPT.OPEN_PROMPT_FILE_TXT, (event, value) => prompt.OpenPromptFileTxt(value))
|
||||
// 获取提示词文件数据(txt,指定路径)
|
||||
ipcMain.handle(DEFINE_STRING.PROMPT.OPEN_PROMPT_FILE_TXT, (event, value) =>
|
||||
prompt.OpenPromptFileTxt(value)
|
||||
)
|
||||
}
|
||||
export {
|
||||
PromptIpc
|
||||
}
|
||||
export { PromptIpc }
|
||||
|
||||
@ -1,24 +1,39 @@
|
||||
import { ipcMain } from "electron";
|
||||
import { ipcMain } from 'electron'
|
||||
import { DEFINE_STRING } from '../../define/define_string'
|
||||
import { SD } from "../Public/SD";
|
||||
let sd = new SD(global);
|
||||
import { SD } from '../Public/SD'
|
||||
import { SDOpt } from '../Service/SD/sd'
|
||||
let sd = new SD(global)
|
||||
let sdOpt = new SDOpt()
|
||||
|
||||
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服务数据
|
||||
ipcMain.handle(DEFINE_STRING.SD.LOAD_SD_SERVICE_DATA, async (event, value) => await sd.LoadSDServiceData(value));
|
||||
// 加载当前链接的SD服务数据
|
||||
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))
|
||||
|
||||
// SD合并提示词
|
||||
ipcMain.handle(
|
||||
DEFINE_STRING.SD.SD_MERGE_PROMPT,
|
||||
async (event, id, mergeType) => await sdOpt.MergePrompt(id, mergeType)
|
||||
)
|
||||
}
|
||||
export {
|
||||
SdIpc
|
||||
}
|
||||
export { SdIpc }
|
||||
|
||||
@ -11,6 +11,8 @@ import { BasicSetting } from '../setting/basicSetting'
|
||||
let basicSetting = new BasicSetting()
|
||||
import { MJSetting } from '../setting/mjSetting'
|
||||
let mjSetting = new MJSetting()
|
||||
import { Watermark } from '../Service/watermark'
|
||||
let watermark = new Watermark()
|
||||
|
||||
async function SettingIpc() {
|
||||
// 获取背景音乐配置列表
|
||||
@ -172,46 +174,67 @@ async function SettingIpc() {
|
||||
//#region MJ 设置
|
||||
|
||||
// 获取MJ基础设置信息
|
||||
ipcMain.handle(DEFINE_STRING.SETTING.GET_MJ_SETTING, async (event, value) =>
|
||||
mjSetting.GetMJSetting(value)
|
||||
ipcMain.handle(
|
||||
DEFINE_STRING.SETTING.GET_MJ_SETTING,
|
||||
async (event, value) => await mjSetting.GetMJSetting(value)
|
||||
)
|
||||
|
||||
// 保存MJ的基础设置信息
|
||||
ipcMain.handle(DEFINE_STRING.SETTING.UPDATE_MJ_SETTING, async (event, value) =>
|
||||
mjSetting.UpdateMJSetting(value)
|
||||
ipcMain.handle(
|
||||
DEFINE_STRING.SETTING.UPDATE_MJ_SETTING,
|
||||
async (event, value) => await mjSetting.UpdateMJSetting(value)
|
||||
)
|
||||
|
||||
// 获取MJ的所有设置
|
||||
ipcMain.handle(DEFINE_STRING.SETTING.GET_MJ_SETTING_TREE_DATA, async (event) =>
|
||||
mjSetting.GetMJSettingTreeData()
|
||||
ipcMain.handle(
|
||||
DEFINE_STRING.SETTING.GET_MJ_SETTING_TREE_DATA,
|
||||
async (event) => await mjSetting.GetMJSettingTreeData()
|
||||
)
|
||||
|
||||
// 保存MJ的所有设置
|
||||
ipcMain.handle(DEFINE_STRING.SETTING.SAVE_MJ_SETTING_TREE_DATA, async (event, value) =>
|
||||
mjSetting.SaveMJSettingTreeData(value)
|
||||
ipcMain.handle(
|
||||
DEFINE_STRING.SETTING.SAVE_MJ_SETTING_TREE_DATA,
|
||||
async (event, value) => await mjSetting.SaveMJSettingTreeData(value)
|
||||
)
|
||||
|
||||
// 获取所有的代理MJ信息
|
||||
ipcMain.handle(DEFINE_STRING.SETTING.GET_REMOTE_MJ_SETTINGS, async (event) =>
|
||||
mjSetting.GetRemoteMJSettings()
|
||||
ipcMain.handle(
|
||||
DEFINE_STRING.SETTING.GET_REMOTE_MJ_SETTINGS,
|
||||
async (event) => await mjSetting.GetRemoteMJSettings()
|
||||
)
|
||||
|
||||
// 创建新的代理MJ信息
|
||||
ipcMain.handle(DEFINE_STRING.SETTING.ADD_REMOTE_MJ_SETTING, async (event, value) =>
|
||||
mjSetting.AddRemoteMJSetting(value)
|
||||
ipcMain.handle(
|
||||
DEFINE_STRING.SETTING.ADD_REMOTE_MJ_SETTING,
|
||||
async (event, value) => await mjSetting.AddRemoteMJSetting(value)
|
||||
)
|
||||
|
||||
// 修改MJ账号并重连
|
||||
ipcMain.handle(DEFINE_STRING.SETTING.UPDATE_REMOTE_MJ_SETTING, async (event, value) =>
|
||||
mjSetting.UpdateRemoteMJSetting(value)
|
||||
ipcMain.handle(
|
||||
DEFINE_STRING.SETTING.UPDATE_REMOTE_MJ_SETTING,
|
||||
async (event, value) => await mjSetting.UpdateRemoteMJSetting(value)
|
||||
)
|
||||
|
||||
// 删除指定的MJ账号
|
||||
ipcMain.handle(DEFINE_STRING.SETTING.DELETE_REMOTE_MJ_SETTING, async (event, value) =>
|
||||
mjSetting.DeleteRemoteMJSetting(value)
|
||||
ipcMain.handle(
|
||||
DEFINE_STRING.SETTING.DELETE_REMOTE_MJ_SETTING,
|
||||
async (event, value) => await mjSetting.DeleteRemoteMJSetting(value)
|
||||
)
|
||||
|
||||
//#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 }
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { ipcMain } from "electron";
|
||||
import { DEFINE_STRING } from '../../define/define_string'
|
||||
import { CheckFileOrDirExist } from "../../define/Tools/file";
|
||||
import { errorMessage, successMessage } from "../generalTools";
|
||||
import { errorMessage, successMessage } from "../Public/generalTools";
|
||||
import path from 'path'
|
||||
const { shell } = require('electron')
|
||||
|
||||
|
||||
@ -1,18 +1,54 @@
|
||||
import { ipcMain } from "electron";
|
||||
import { ipcMain } from 'electron'
|
||||
import { DEFINE_STRING } from '../../define/define_string'
|
||||
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() {
|
||||
// 监听添加任务到翻译队列的任务
|
||||
ipcMain.handle(DEFINE_STRING.TRANSLATE_PROMPT, async (event, value) => await translate.TranslatePrompt(value));
|
||||
//#region 要删除的
|
||||
// 监听添加任务到翻译队列的任务
|
||||
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,
|
||||
async (event, value) => await translate.TranslateReturnNowTask(value)
|
||||
)
|
||||
//#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
|
||||
}
|
||||
export { TranslateIpc }
|
||||
|
||||
@ -1,16 +1,16 @@
|
||||
import { ipcMain } from 'electron'
|
||||
import { DEFINE_STRING } from '../../define/define_string'
|
||||
import { LOGGER_DEFINE } from '../../define/logger_define'
|
||||
import { errorMessage } from '../generalTools'
|
||||
import { TTSSetting } from '../setting/ttsSetting'
|
||||
const ttsSetting = new TTSSetting()
|
||||
import { TTS } from '../Service/tts'
|
||||
const tts = new TTS()
|
||||
|
||||
export function TTSIpc() {
|
||||
// 获取当前的TTS配置数据
|
||||
ipcMain.handle(DEFINE_STRING.TTS.GET_TTS_CONFIG, async () => ttsSetting.GetTTSCOnfig())
|
||||
ipcMain.handle(DEFINE_STRING.TTS.GET_TTS_CONFIG, async () => tts.GetTTSCOnfig())
|
||||
|
||||
// 保存TTS配置
|
||||
ipcMain.handle(DEFINE_STRING.TTS.SAVE_TTS_CONFIG, async (event, data) =>
|
||||
ttsSetting.SaveTTSConfig(data)
|
||||
)
|
||||
ipcMain.handle(DEFINE_STRING.TTS.SAVE_TTS_CONFIG, async (event, data) => tts.SaveTTSConfig(data))
|
||||
|
||||
// 生成音频
|
||||
ipcMain.handle(DEFINE_STRING.TTS.GENERATE_AUDIO, async (event, text) => tts.GenerateAudio(text))
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { ipcMain } from 'electron'
|
||||
import { DEFINE_STRING } from '../../define/define_string'
|
||||
import { Writing } from '../Task/writing'
|
||||
import { Writing } from '../Service/writing'
|
||||
let writing = new Writing(global)
|
||||
import { WritingSetting } from '../setting/writeSetting'
|
||||
let writingSetting = new WritingSetting()
|
||||
|
||||
@ -9,7 +9,7 @@ import path from 'path'
|
||||
import sharp from 'sharp'
|
||||
import { define } from '../../define/define'
|
||||
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 { DiscordAPI } from '../../api/discordApi'
|
||||
import { GPT } from '../Public/GPT'
|
||||
@ -463,7 +463,7 @@ export class MJOriginalImageGenerate {
|
||||
}
|
||||
let data = {
|
||||
prompt: prompt,
|
||||
botType: 'MID_JOURNEY',
|
||||
botType: mjSetting.selectRobot == 'niji' ? 'NIJI_JOURNEY' : 'MID_JOURNEY',
|
||||
accountFilter: {
|
||||
modes: [mj_speed == 'fast' ? 'FAST' : 'RELAX']
|
||||
}
|
||||
@ -479,7 +479,7 @@ export class MJOriginalImageGenerate {
|
||||
}
|
||||
let data = {
|
||||
prompt: prompt,
|
||||
botType: 'MID_JOURNEY',
|
||||
botType: mjSetting.selectRobot == 'niji' ? 'NIJI_JOURNEY' : 'MID_JOURNEY',
|
||||
accountFilter: {
|
||||
remark: this.global.machineId
|
||||
}
|
||||
|
||||
@ -1,355 +1,395 @@
|
||||
|
||||
import { Tools } from "../tools";
|
||||
import path from "path";
|
||||
import { DEFINE_STRING } from "../../define/define_string";
|
||||
import { define } from "../../define/define";
|
||||
import { PublicMethod } from "../Public/publicMethod";
|
||||
import { SD } from "../Public/SD"
|
||||
const util = require('util');
|
||||
import axios from "axios";
|
||||
const sharp = require('sharp');
|
||||
const { spawn, exec } = require('child_process');
|
||||
const execAsync = util.promisify(exec);
|
||||
const { v4: uuidv4 } = require('uuid'); // 引入UUID库来生成唯一标识符
|
||||
let fspromises = require("fs").promises;
|
||||
import { ImageStyleDefine } from "../../define/iamgeStyleDefine";
|
||||
|
||||
import { Tools } from '../tools'
|
||||
import path from 'path'
|
||||
import { DEFINE_STRING } from '../../define/define_string'
|
||||
import { define } from '../../define/define'
|
||||
import { PublicMethod } from '../Public/publicMethod'
|
||||
import { SD } from '../Public/SD'
|
||||
const util = require('util')
|
||||
import axios from 'axios'
|
||||
const sharp = require('sharp')
|
||||
const { spawn, exec } = require('child_process')
|
||||
const execAsync = util.promisify(exec)
|
||||
const { v4: uuidv4 } = require('uuid') // 引入UUID库来生成唯一标识符
|
||||
let fspromises = require('fs').promises
|
||||
import { ImageStyleDefine } from '../../define/iamgeStyleDefine'
|
||||
|
||||
export class OriginalImageGenerate {
|
||||
constructor(global) {
|
||||
this.global = global;
|
||||
this.tools = new Tools();
|
||||
this.pm = new PublicMethod(global);
|
||||
this.sd = new SD(global);
|
||||
}
|
||||
constructor(global) {
|
||||
this.global = global
|
||||
this.tools = new Tools()
|
||||
this.pm = new PublicMethod(global)
|
||||
this.sd = new SD(global)
|
||||
}
|
||||
|
||||
/**
|
||||
* SD原创单张图片生成
|
||||
* @param {*} value 传入的参数 0 :原创界面的data数据信息,1:是否需要格式化,2:是否需要全局提示
|
||||
*/
|
||||
async OriginalSDImageGenerate(value) {
|
||||
try {
|
||||
let data = value[0]
|
||||
if (value[1]) {
|
||||
data = JSON.parse(data)
|
||||
}
|
||||
let show_global_message = value[2]
|
||||
// 判断输出的文件夹路径是不是存在,不存在创建
|
||||
let output_crop_path = path.join(this.global.config.project_path, 'tmp/output_crop_1')
|
||||
// 检查文件是不是存在
|
||||
let isE = await this.tools.checkExists(output_crop_path)
|
||||
if (!isE) {
|
||||
output_crop_path = path.join(this.global.config.project_path, 'tmp/output_crop_00001')
|
||||
}
|
||||
await this.tools.checkFolderExistsOrCreate(output_crop_path)
|
||||
let SdOriginalImage = path.join(this.global.config.project_path, 'data/SdOriginalImage')
|
||||
await this.tools.checkFolderExistsOrCreate(SdOriginalImage)
|
||||
|
||||
// 获取当前的同用前缀后缀
|
||||
let config_json = await this.pm.GetConfigJson(JSON.stringify([null, {}]))
|
||||
let prefix_prompt = config_json.data.prefix_prompt
|
||||
let suffix_prompt = config_json.data.suffix_prompt
|
||||
let batch = DEFINE_STRING.QUEUE_BATCH.SD_ORIGINAL_GENERATE_IMAGE
|
||||
let url = this.global.config.webui_api_url + 'sdapi/v1/txt2img'
|
||||
let sd_setting = JSON.parse(await fspromises.readFile(define.sd_setting, 'utf-8'))
|
||||
|
||||
/**
|
||||
* SD原创单张图片生成
|
||||
* @param {*} value 传入的参数 0 :原创界面的data数据信息,1:是否需要格式化,2:是否需要全局提示
|
||||
*/
|
||||
async OriginalSDImageGenerate(value) {
|
||||
try {
|
||||
let data = value[0];
|
||||
if (value[1]) {
|
||||
data = JSON.parse(data);
|
||||
}
|
||||
let show_global_message = value[2];
|
||||
// 判断输出的文件夹路径是不是存在,不存在创建
|
||||
let output_crop_path = path.join(this.global.config.project_path, "tmp/output_crop_1");
|
||||
// 检查文件是不是存在
|
||||
let isE = await this.tools.checkExists(output_crop_path);
|
||||
if (!isE) {
|
||||
output_crop_path = path.join(this.global.config.project_path, "tmp/output_crop_00001");
|
||||
}
|
||||
await this.tools.checkFolderExistsOrCreate(output_crop_path);
|
||||
let SdOriginalImage = path.join(this.global.config.project_path, 'data/SdOriginalImage');
|
||||
await this.tools.checkFolderExistsOrCreate(SdOriginalImage);
|
||||
// 判断当前是不是有开修脸修手
|
||||
let ADetailer = {
|
||||
args: sd_setting.adetailer
|
||||
}
|
||||
let seed = sd_setting.setting.seed
|
||||
|
||||
// 获取当前的同用前缀后缀
|
||||
let config_json = await this.pm.GetConfigJson(JSON.stringify([null, {}]));
|
||||
let prefix_prompt = config_json.data.prefix_prompt;
|
||||
let suffix_prompt = config_json.data.suffix_prompt;
|
||||
let batch = DEFINE_STRING.QUEUE_BATCH.SD_ORIGINAL_GENERATE_IMAGE;
|
||||
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 style_ids = await this.pm.GetConfigJson(JSON.stringify(['image_style', []]), false)
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
const element = data[i]
|
||||
let adetailer = element.adetailer
|
||||
|
||||
// 判断当前是不是有开修脸修手
|
||||
let ADetailer = {
|
||||
args: sd_setting.adetailer
|
||||
};
|
||||
let seed = sd_setting.setting.seed;
|
||||
let imageJson = JSON.parse(
|
||||
await fspromises.readFile(path.normalize(element.prompt_json), 'utf-8')
|
||||
)
|
||||
let prompt = sd_setting.webui.prompt + ',' + element.prompt
|
||||
// 添加前缀
|
||||
if (prefix_prompt) {
|
||||
prompt = prefix_prompt + ',' + prompt
|
||||
}
|
||||
// 添加后缀
|
||||
if (suffix_prompt) {
|
||||
prompt = prompt + ',' + suffix_prompt
|
||||
}
|
||||
// let prompt = imageJson.webui_config.prompt;
|
||||
this.global.requestQuene.enqueue(
|
||||
async () => {
|
||||
try {
|
||||
// 开始请求
|
||||
let body = {
|
||||
prompt: prompt,
|
||||
negative_prompt: imageJson.webui_config.negative_prompt,
|
||||
seed: seed,
|
||||
sampler_name: sd_setting.webui.sampler_name,
|
||||
// 提示词相关性
|
||||
cfg_scale: sd_setting.webui.cfg_scale,
|
||||
width: sd_setting.webui.width,
|
||||
height: sd_setting.webui.height,
|
||||
batch_size: sd_setting.setting.batch_size,
|
||||
n_iter: 1,
|
||||
steps: imageJson.webui_config.steps,
|
||||
save_images: false
|
||||
}
|
||||
|
||||
let style_ids = await this.pm.GetConfigJson(JSON.stringify(["image_style", []]), false);
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
const element = data[i];
|
||||
let adetailer = element.adetailer;
|
||||
|
||||
let imageJson = JSON.parse(await fspromises.readFile(path.normalize(element.prompt_json), 'utf-8'));
|
||||
let prompt = sd_setting.webui.prompt + ',' + element.prompt;
|
||||
// 添加前缀
|
||||
if (prefix_prompt) {
|
||||
prompt = prefix_prompt + ',' + prompt;
|
||||
// 判断是不是开启修脸修手
|
||||
if (adetailer) {
|
||||
let ta = {
|
||||
ADetailer: ADetailer
|
||||
}
|
||||
// 添加后缀
|
||||
if (suffix_prompt) {
|
||||
prompt = prompt + ',' + suffix_prompt;
|
||||
body.alwayson_scripts = ta
|
||||
}
|
||||
const response = await axios.post(url, body)
|
||||
let info = JSON.parse(response.data.info)
|
||||
if (seed == -1) {
|
||||
seed = info.seed
|
||||
}
|
||||
let images = response.data.images
|
||||
let subImagePath = []
|
||||
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 input_image = path.join(
|
||||
this.global.config.project_path,
|
||||
`tmp/input_crop/${element.name}`
|
||||
)
|
||||
|
||||
for (let j = 0; j < images.length; j++) {
|
||||
const image = images[j]
|
||||
let imageData = Buffer.from(image.split(',', 1)[0], 'base64')
|
||||
// 写入数据(写入到当前当前项目文件下面的 data/SdOriginalImage 下面)
|
||||
let image_path = path.join(
|
||||
this.global.config.project_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)
|
||||
.toFile(tmp_image_path)
|
||||
.then(async () => {
|
||||
// 生图成功,删除数据
|
||||
// 判断原本的图片文件是不是存在,存在删除
|
||||
await this.tools.deletePngAndDeleteExifData(tmp_image_path, image_path)
|
||||
})
|
||||
.catch((err) => {
|
||||
throw err
|
||||
})
|
||||
// console.log("文生图成功" + image_path);
|
||||
|
||||
// 将第一个张写出到指定的文件夹中
|
||||
if (j == 0) {
|
||||
await sharp(imageData)
|
||||
.toFile(out_tmp_image_path)
|
||||
.then(async () => {
|
||||
// 生图成功,删除数据
|
||||
await this.tools.deletePngAndDeleteExifData(
|
||||
out_tmp_image_path,
|
||||
out_image_path
|
||||
)
|
||||
await this.tools.copyFileOrDirectory(out_image_path, input_image)
|
||||
})
|
||||
.catch((err) => {
|
||||
// console.log(err)
|
||||
throw err
|
||||
})
|
||||
}
|
||||
// let prompt = imageJson.webui_config.prompt;
|
||||
this.global.requestQuene.enqueue(async () => {
|
||||
try {
|
||||
// 开始请求
|
||||
let body = {
|
||||
"prompt": prompt,
|
||||
"negative_prompt": imageJson.webui_config.negative_prompt,
|
||||
"seed": seed,
|
||||
"sampler_name": sd_setting.webui.sampler_name,
|
||||
// 提示词相关性
|
||||
"cfg_scale": sd_setting.webui.cfg_scale,
|
||||
"width": sd_setting.webui.width,
|
||||
"height": sd_setting.webui.height,
|
||||
"batch_size": sd_setting.setting.batch_size,
|
||||
"n_iter": 1,
|
||||
"steps": imageJson.webui_config.steps,
|
||||
"save_images": false,
|
||||
}
|
||||
}
|
||||
// 将图片的信息写入到config.json文件中
|
||||
let index = config_json.data.srt_time_information.findIndex(
|
||||
(item) => item.id == element.id
|
||||
)
|
||||
if (index < 0) {
|
||||
throw new Error('没有找到指定的ID,请检查数据')
|
||||
}
|
||||
config_json.data.srt_time_information[index].subImagePath = subImagePath
|
||||
config_json.data.srt_time_information[index].outImagePath = out_image_path
|
||||
this.global.fileQueue.enqueue(async () => {
|
||||
await this.pm.SaveConfigJsonProperty([
|
||||
config_json.data.srt_time_information,
|
||||
'srt_time_information',
|
||||
false
|
||||
])
|
||||
})
|
||||
|
||||
// 判断是不是开启修脸修手
|
||||
if (adetailer) {
|
||||
let ta = {
|
||||
ADetailer: ADetailer
|
||||
}
|
||||
body.alwayson_scripts = ta;
|
||||
}
|
||||
const response = await axios.post(url, body);
|
||||
let info = JSON.parse(response.data.info);
|
||||
if (seed == -1) {
|
||||
seed = info.seed;
|
||||
}
|
||||
let images = response.data.images;
|
||||
let subImagePath = [];
|
||||
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 input_image = path.join(this.global.config.project_path, `tmp/input_crop/${element.name}`);
|
||||
|
||||
for (let j = 0; j < images.length; j++) {
|
||||
const image = images[j];
|
||||
let imageData = Buffer.from(image.split(",", 1)[0], 'base64');
|
||||
// 写入数据(写入到当前当前项目文件下面的 data/SdOriginalImage 下面)
|
||||
let image_path = path.join(this.global.config.project_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)
|
||||
.toFile(tmp_image_path)
|
||||
.then(async () => {
|
||||
// 生图成功,删除数据
|
||||
// 判断原本的图片文件是不是存在,存在删除
|
||||
await this.tools.deletePngAndDeleteExifData(tmp_image_path, image_path);
|
||||
}).catch(err => {
|
||||
throw err;
|
||||
});
|
||||
// console.log("文生图成功" + image_path);
|
||||
|
||||
// 将第一个张写出到指定的文件夹中
|
||||
if (j == 0) {
|
||||
await sharp(imageData)
|
||||
.toFile(out_tmp_image_path)
|
||||
.then(async () => {
|
||||
// 生图成功,删除数据
|
||||
await this.tools.deletePngAndDeleteExifData(out_tmp_image_path, out_image_path);
|
||||
await this.tools.copyFileOrDirectory(out_image_path, input_image);
|
||||
})
|
||||
.catch(err => {
|
||||
// console.log(err)
|
||||
throw err;
|
||||
});
|
||||
}
|
||||
}
|
||||
// 将图片的信息写入到config.json文件中
|
||||
let index = config_json.data.srt_time_information.findIndex(item => item.id == element.id);
|
||||
if (index < 0) {
|
||||
throw new Error("没有找到指定的ID,请检查数据");
|
||||
}
|
||||
config_json.data.srt_time_information[index].subImagePath = subImagePath;
|
||||
config_json.data.srt_time_information[index].outImagePath = out_image_path;
|
||||
this.global.fileQueue.enqueue(async () => {
|
||||
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, {
|
||||
code: 1,
|
||||
id: element.id,
|
||||
data: {
|
||||
subImagePath: subImagePath,
|
||||
outImagePath: out_image_path
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
}, `${batch}_${element.name}`, batch);
|
||||
// 返回数据,用于前台刷新,返回图片数据
|
||||
this.global.newWindow[0].win.webContents.send(
|
||||
DEFINE_STRING.SD_ORIGINAL_GENERATE_IMAGE_RETURN,
|
||||
{
|
||||
code: 1,
|
||||
id: element.id,
|
||||
data: {
|
||||
subImagePath: subImagePath,
|
||||
outImagePath: out_image_path
|
||||
}
|
||||
}
|
||||
)
|
||||
} catch (error) {
|
||||
throw error
|
||||
}
|
||||
},
|
||||
`${batch}_${element.name}`,
|
||||
batch
|
||||
)
|
||||
}
|
||||
|
||||
this.global.requestQuene.setBatchCompletionCallback(batch, (failedTasks) => {
|
||||
if (failedTasks.length > 0) {
|
||||
let message = `
|
||||
this.global.requestQuene.setBatchCompletionCallback(batch, (failedTasks) => {
|
||||
if (failedTasks.length > 0) {
|
||||
let message = `
|
||||
生图任务都已完成。
|
||||
但是以下任务执行失败:
|
||||
`
|
||||
failedTasks.forEach(({ taskId, error }) => {
|
||||
message += `${taskId}-, \n 错误信息: ${error}` + '\n';
|
||||
});
|
||||
failedTasks.forEach(({ taskId, error }) => {
|
||||
message += `${taskId}-, \n 错误信息: ${error}` + '\n'
|
||||
})
|
||||
|
||||
this.global.newWindow[0].win.webContents.send(DEFINE_STRING.SHOW_MESSAGE_DIALOG, {
|
||||
code: 0,
|
||||
message: message
|
||||
})
|
||||
} else {
|
||||
if (show_global_message) {
|
||||
this.global.newWindow[0].win.webContents.send(DEFINE_STRING.SHOW_MESSAGE_DIALOG, {
|
||||
code: 1,
|
||||
message: "所有生图任务完成"
|
||||
})
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
return {
|
||||
code: 1,
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
return {
|
||||
code: 0,
|
||||
message: error.toString()
|
||||
}
|
||||
this.global.newWindow[0].win.webContents.send(DEFINE_STRING.SHOW_MESSAGE_DIALOG, {
|
||||
code: 0,
|
||||
message: message
|
||||
})
|
||||
} else {
|
||||
if (show_global_message) {
|
||||
this.global.newWindow[0].win.webContents.send(DEFINE_STRING.SHOW_MESSAGE_DIALOG, {
|
||||
code: 1,
|
||||
message: '所有生图任务完成'
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
return {
|
||||
code: 1
|
||||
}
|
||||
} catch (error) {
|
||||
return {
|
||||
code: 0,
|
||||
message: error.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 自动保存数据到json文件
|
||||
* @param {*} value 自动保存数据到json文件
|
||||
*/
|
||||
async AutoSaveDataJson(value) {
|
||||
try {
|
||||
// 目前自动保存的信息,中文提示词,英文提示词,前缀,后缀
|
||||
value = JSON.parse(value);
|
||||
let batch = DEFINE_STRING.QUEUE_BATCH.AUTO_SAVE_DATA_JSON;
|
||||
for (let i = 0; i < value.length; i++) {
|
||||
const element = value[i];
|
||||
// 将修改文件的的方法添加到修改文件队列中
|
||||
/**
|
||||
* 自动保存数据到json文件
|
||||
* @param {*} value 自动保存数据到json文件
|
||||
*/
|
||||
async AutoSaveDataJson(value) {
|
||||
try {
|
||||
// 目前自动保存的信息,中文提示词,英文提示词,前缀,后缀
|
||||
value = JSON.parse(value)
|
||||
let batch = DEFINE_STRING.QUEUE_BATCH.AUTO_SAVE_DATA_JSON
|
||||
for (let i = 0; i < value.length; i++) {
|
||||
const element = value[i]
|
||||
// 将修改文件的的方法添加到修改文件队列中
|
||||
|
||||
this.global.fileQueue.enqueue(async () => {
|
||||
try {
|
||||
if (element.prompt_json) {
|
||||
let old_json = JSON.parse(await fspromises.readFile(path.normalize(element.prompt_json), 'utf-8'));
|
||||
old_json.webui_config.prompt = element.prompt;
|
||||
// old_json.adetailer = element.adetailer;
|
||||
old_json.chinese_prompt = element.chinese_prompt;
|
||||
// 前缀提示词
|
||||
old_json.prefix_prompt = element.prefix_prompt;
|
||||
// 后缀提示词
|
||||
old_json.suffix_prompt = element.suffix_prompt;
|
||||
old_json.adetailer = element.adetailer;
|
||||
old_json.prompt = element.prompt;
|
||||
await fspromises.writeFile(path.normalize(element.prompt_json), JSON.stringify(old_json));
|
||||
}
|
||||
} catch (error) {
|
||||
throw new Error(error);
|
||||
}
|
||||
}, `${batch}_${element.id}`, batch);
|
||||
|
||||
// 判断是不是有图片。判断图片是不是符合格式(有些格式是file:// 开头的, 以时间结尾(都要删除))
|
||||
// 判断是不是有图片
|
||||
let file_regex = /^file:\/\//;
|
||||
if (element.outImagePath && file_regex.test(element.outImagePath)) {
|
||||
// 删除 "file://" 开头
|
||||
element.outImagePath = decodeURI(element.outImagePath);
|
||||
// 判断element.outImagePath是不是不是以file://开头的,是的话,删除
|
||||
if (element.outImagePath.startsWith("file://")) {
|
||||
element.outImagePath = element.outImagePath.substring(7);
|
||||
}
|
||||
element.outImagePath = element.outImagePath.replace(/\?time=.*$/, '');
|
||||
// 判断element.outImagePath是不是以/开头的,是的话,删除
|
||||
if (element.outImagePath.startsWith("/")) {
|
||||
element.outImagePath = element.outImagePath.substring(1);
|
||||
}
|
||||
}
|
||||
if (element.subImagePath && element.subImagePath.length > 0) {
|
||||
for (let j = 0; j < element.subImagePath.length; j++) {
|
||||
if (file_regex.test(element.subImagePath[j])) {
|
||||
element.subImagePath[j] = decodeURI(element.subImagePath[j]);
|
||||
element.subImagePath[j] = element.subImagePath[j].replace(/^file:\/\//, '').replace(/\?time=.*$/, '');
|
||||
}
|
||||
}
|
||||
}
|
||||
this.global.fileQueue.enqueue(
|
||||
async () => {
|
||||
try {
|
||||
if (element.prompt_json) {
|
||||
let old_json = JSON.parse(
|
||||
await fspromises.readFile(path.normalize(element.prompt_json), 'utf-8')
|
||||
)
|
||||
old_json.webui_config.prompt = element.prompt
|
||||
// old_json.adetailer = element.adetailer;
|
||||
old_json.chinese_prompt = element.chinese_prompt
|
||||
// 前缀提示词
|
||||
old_json.prefix_prompt = element.prefix_prompt
|
||||
// 后缀提示词
|
||||
old_json.suffix_prompt = element.suffix_prompt
|
||||
old_json.adetailer = element.adetailer
|
||||
old_json.prompt = element.prompt
|
||||
await fspromises.writeFile(
|
||||
path.normalize(element.prompt_json),
|
||||
JSON.stringify(old_json)
|
||||
)
|
||||
}
|
||||
} catch (error) {
|
||||
throw new Error(error)
|
||||
}
|
||||
},
|
||||
`${batch}_${element.id}`,
|
||||
batch
|
||||
)
|
||||
|
||||
await this.tools.writeJsonFilePropertyValue(path.join(this.global.config.project_path, "scripts/config.json"), "srt_time_information", value, false);
|
||||
|
||||
return {
|
||||
code: 1
|
||||
}
|
||||
} catch (error) {
|
||||
return {
|
||||
code: 0,
|
||||
message: error.toString()
|
||||
}
|
||||
// 判断是不是有图片。判断图片是不是符合格式(有些格式是file:// 开头的, 以时间结尾(都要删除))
|
||||
// 判断是不是有图片
|
||||
let file_regex = /^file:\/\//
|
||||
if (element.outImagePath && file_regex.test(element.outImagePath)) {
|
||||
// 删除 "file://" 开头
|
||||
element.outImagePath = decodeURI(element.outImagePath)
|
||||
// 判断element.outImagePath是不是不是以file://开头的,是的话,删除
|
||||
if (element.outImagePath.startsWith('file://')) {
|
||||
element.outImagePath = element.outImagePath.substring(7)
|
||||
}
|
||||
element.outImagePath = element.outImagePath.replace(/\?time=.*$/, '')
|
||||
// 判断element.outImagePath是不是以/开头的,是的话,删除
|
||||
if (element.outImagePath.startsWith('/')) {
|
||||
element.outImagePath = element.outImagePath.substring(1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 将反推的图片的信息添加到一个json文件中
|
||||
*/
|
||||
async OriginalAddWebuiJson(value) {
|
||||
try {
|
||||
let data = JSON.parse(value);
|
||||
// 判断文件夹是不是存在
|
||||
let imput_crop_path = path.join(this.global.config.project_path, "tmp/input_crop");
|
||||
let isExist = await this.tools.checkExists(imput_crop_path);
|
||||
if (!isExist) {
|
||||
await fspromises.mkdir(imput_crop_path, { recursive: true })
|
||||
}
|
||||
|
||||
// 判断当前的数据是不是相同
|
||||
// 读取所有txt文件
|
||||
let promptJson = await this.tools.getFilesWithExtensions(path.join(global.config.project_path, 'tmp/input_crop'), '.json');
|
||||
|
||||
// json 已经存在,不做后续处理
|
||||
if (data.length == promptJson.length) {
|
||||
return {
|
||||
code: 1,
|
||||
data: path.join(this.global.config.project_path, "tmp/input_crop")
|
||||
}
|
||||
}
|
||||
let sd_config = JSON.parse(await fspromises.readFile(define.sd_setting, 'utf-8'));
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
const element = data[i];
|
||||
let name = String(element.no).padStart(5, '0') + ".png";
|
||||
// console.log(txtpath)
|
||||
let obj = {}
|
||||
obj.model = sd_config.setting.type;
|
||||
obj.api = sd_config.setting.webui_api_url + 'sdapi/v1/txt2img';
|
||||
obj.name = name;
|
||||
obj.webui_config = {
|
||||
sampler_name: sd_config.webui.sampler_name,
|
||||
prompt: "",
|
||||
negative_prompt: sd_config.webui.negative_prompt,
|
||||
batch_size: 1,
|
||||
steps: sd_config.webui.steps,
|
||||
cfg_scale: sd_config.webui.cfg_scale,
|
||||
denoising_strength: sd_config.webui.denoising_strength,
|
||||
width: sd_config.webui.width,
|
||||
height: sd_config.webui.height,
|
||||
seed: sd_config.setting.seed,
|
||||
init_images: path.normalize(path.join(this.global.config.project_path, "tmp/input_crop/" + name)),
|
||||
}
|
||||
obj.adetailer = sd_config.webui.adetailer;
|
||||
|
||||
let file_path = path.join(this.global.config.project_path, "tmp/input_crop/" + name + '.json');
|
||||
// 写入
|
||||
await fspromises.writeFile(file_path, JSON.stringify(obj));
|
||||
}
|
||||
|
||||
return {
|
||||
code: 1,
|
||||
data: path.join(this.global.config.project_path, "tmp/input_crop")
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
return {
|
||||
code: 0,
|
||||
message: error.toString()
|
||||
if (element.subImagePath && element.subImagePath.length > 0) {
|
||||
for (let j = 0; j < element.subImagePath.length; j++) {
|
||||
if (file_regex.test(element.subImagePath[j])) {
|
||||
element.subImagePath[j] = decodeURI(element.subImagePath[j])
|
||||
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
|
||||
)
|
||||
|
||||
return {
|
||||
code: 1
|
||||
}
|
||||
} catch (error) {
|
||||
return {
|
||||
code: 0,
|
||||
message: error.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将反推的图片的信息添加到一个json文件中
|
||||
*/
|
||||
async OriginalAddWebuiJson(value) {
|
||||
try {
|
||||
let data = JSON.parse(value)
|
||||
// 判断文件夹是不是存在
|
||||
let imput_crop_path = path.join(this.global.config.project_path, 'tmp/input_crop')
|
||||
let isExist = await this.tools.checkExists(imput_crop_path)
|
||||
if (!isExist) {
|
||||
await fspromises.mkdir(imput_crop_path, { recursive: true })
|
||||
}
|
||||
|
||||
// 判断当前的数据是不是相同
|
||||
// 读取所有txt文件
|
||||
let promptJson = await this.tools.getFilesWithExtensions(
|
||||
path.join(global.config.project_path, 'tmp/input_crop'),
|
||||
'.json'
|
||||
)
|
||||
|
||||
// json 已经存在,不做后续处理
|
||||
if (data.length == promptJson.length) {
|
||||
return {
|
||||
code: 1,
|
||||
data: path.join(this.global.config.project_path, 'tmp/input_crop')
|
||||
}
|
||||
}
|
||||
let sd_config = JSON.parse(await fspromises.readFile(define.sd_setting, 'utf-8'))
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
const element = data[i]
|
||||
let name = String(element.no).padStart(5, '0') + '.png'
|
||||
// console.log(txtpath)
|
||||
let obj = {}
|
||||
obj.model = sd_config.setting.type
|
||||
obj.api = sd_config.setting.webui_api_url + 'sdapi/v1/txt2img'
|
||||
obj.name = name
|
||||
obj.webui_config = {
|
||||
sampler_name: sd_config.webui.sampler_name,
|
||||
prompt: '',
|
||||
negative_prompt: sd_config.webui.negative_prompt,
|
||||
batch_size: 1,
|
||||
steps: sd_config.webui.steps,
|
||||
cfg_scale: sd_config.webui.cfg_scale,
|
||||
denoising_strength: sd_config.webui.denoising_strength,
|
||||
width: sd_config.webui.width,
|
||||
height: sd_config.webui.height,
|
||||
seed: sd_config.setting.seed,
|
||||
init_images: path.normalize(
|
||||
path.join(this.global.config.project_path, 'tmp/input_crop/' + name)
|
||||
)
|
||||
}
|
||||
obj.adetailer = sd_config.webui.adetailer
|
||||
|
||||
let file_path = path.join(
|
||||
this.global.config.project_path,
|
||||
'tmp/input_crop/' + name + '.json'
|
||||
)
|
||||
// 写入
|
||||
await fspromises.writeFile(file_path, JSON.stringify(obj))
|
||||
}
|
||||
|
||||
return {
|
||||
code: 1,
|
||||
data: path.join(this.global.config.project_path, 'tmp/input_crop')
|
||||
}
|
||||
} catch (error) {
|
||||
return {
|
||||
code: 0,
|
||||
message: error.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
import { TagDefine } from "../../define/tagDefine";
|
||||
export class TagCustomize {
|
||||
global: any;
|
||||
tagDefine: TagDefine;
|
||||
constructor(global) {
|
||||
this.global = global;
|
||||
this.tagDefine = new TagDefine(global);
|
||||
@ -5,7 +5,7 @@ import { define } from "../../define/define";
|
||||
let fspromises = require("fs").promises;
|
||||
import { gptDefine } from "../../define/gptDefine";
|
||||
import { apiUrl } from "../../define/api/apiUrlDefine";
|
||||
import { successMessage } from "../generalTools";
|
||||
import { successMessage } from "../Public/generalTools";
|
||||
|
||||
export class GPT {
|
||||
constructor(global) {
|
||||
@ -282,8 +282,7 @@ export class GPT {
|
||||
url: gpt_url,
|
||||
headers: {
|
||||
'Authorization': `Bearer ${gpt_key}`,
|
||||
'Content-Type': 'application/json',
|
||||
"Accept": "application/json"
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
data: JSON.stringify(data)
|
||||
};
|
||||
|
||||
@ -1,335 +1,378 @@
|
||||
import { errorMessage, successMessage } from "../generalTools";
|
||||
import path, { resolve } from "path";
|
||||
import { Tools } from "../tools";
|
||||
import fs from "fs";
|
||||
import { ImageSetting } from "../../define/setting/imageSetting";
|
||||
import { isEmpty } from "lodash";
|
||||
import { basicApi } from "../../api/apiBasic";
|
||||
import sharp from 'sharp';
|
||||
import { file, image } from "../../define/Tools";
|
||||
import { define } from "../../define/define";
|
||||
import { errorMessage, successMessage } from './generalTools'
|
||||
import path from 'path'
|
||||
import { Tools } from '../tools'
|
||||
import fs from 'fs'
|
||||
import { ImageSetting } from '../../define/setting/imageSetting'
|
||||
import { isEmpty } from 'lodash'
|
||||
import { basicApi } from '../../api/apiBasic'
|
||||
import { file, image } from '../../define/Tools'
|
||||
import { define } from '../../define/define'
|
||||
import { spawn } from 'child_process'
|
||||
import { LOGGER_DEFINE } from "../../define/logger_define";
|
||||
import { DEFINE_STRING } from "../../define/define_string";
|
||||
import { LOGGER_DEFINE } from '../../define/logger_define'
|
||||
import { DEFINE_STRING } from '../../define/define_string'
|
||||
|
||||
export class Image {
|
||||
constructor(global) {
|
||||
this.global = global;
|
||||
this.tools = new Tools();
|
||||
}
|
||||
constructor(global) {
|
||||
this.global = global
|
||||
this.tools = new Tools()
|
||||
}
|
||||
|
||||
// 将指定的文件夹复制到四个文件夹中
|
||||
async OneSplitFour(value) {
|
||||
try {
|
||||
value = JSON.parse(value);
|
||||
let count = value[1];
|
||||
let data = value[0];
|
||||
// 先创建输出文件
|
||||
if (count <= 1) {
|
||||
throw new Error("可选择的图片的数量必须大于1");
|
||||
}
|
||||
for (let i = 1; i < count; i++) {
|
||||
let out_folder = path.join(this.global.config.project_path, `tmp/output_crop_0000${i + 1}`);
|
||||
// 判断当前的文件夹是不是存在,存在删除
|
||||
let isH = await this.tools.checkExists(out_folder);
|
||||
if (isH) {
|
||||
await this.tools.deleteFileOrDirectory(out_folder);
|
||||
}
|
||||
await this.tools.checkFolderExistsOrCreate(out_folder)
|
||||
}
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
const element = data[i];
|
||||
let subImagePath = element.subImagePath;
|
||||
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}`);
|
||||
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);
|
||||
}
|
||||
}
|
||||
return successMessage("拆分成功");
|
||||
|
||||
} catch (error) {
|
||||
return errorMessage(error.message);
|
||||
// 将指定的文件夹复制到四个文件夹中
|
||||
async OneSplitFour(value) {
|
||||
try {
|
||||
value = JSON.parse(value)
|
||||
let count = value[1]
|
||||
let data = value[0]
|
||||
// 先创建输出文件
|
||||
if (count <= 1) {
|
||||
throw new Error('可选择的图片的数量必须大于1')
|
||||
}
|
||||
for (let i = 1; i < count; i++) {
|
||||
let out_folder = path.join(this.global.config.project_path, `tmp/output_crop_0000${i + 1}`)
|
||||
// 判断当前的文件夹是不是存在,存在删除
|
||||
let isH = await this.tools.checkExists(out_folder)
|
||||
if (isH) {
|
||||
await this.tools.deleteFileOrDirectory(out_folder)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将base64转换为文件
|
||||
* @param {*} value
|
||||
* @returns
|
||||
*/
|
||||
async Base64ToFile(value) {
|
||||
try {
|
||||
value = JSON.parse(value);
|
||||
let base64 = value[0];
|
||||
let out_file_str = value[1];
|
||||
let base64Data = base64.replace(/^data:image\/\w+;base64,/, "");
|
||||
let dataBuffer = Buffer.from(base64Data, 'base64');
|
||||
let out_file = path.join(this.global.config.project_path, out_file_str);
|
||||
let out_folder = path.dirname(out_file);
|
||||
await this.tools.checkFolderExistsOrCreate(out_folder);
|
||||
|
||||
await fs.promises.writeFile(out_file, dataBuffer);
|
||||
// await this.tools.writeArrayToFile(dataBuffer, out_file);
|
||||
return successMessage(out_file, "base64保存到本地图片成功");
|
||||
} catch (error) {
|
||||
return errorMessage("base64保存到本地图片失败,失败原因如下:" + error.message);
|
||||
await this.tools.checkFolderExistsOrCreate(out_folder)
|
||||
}
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
const element = data[i]
|
||||
let subImagePath = element.subImagePath
|
||||
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}`
|
||||
)
|
||||
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)
|
||||
}
|
||||
}
|
||||
return successMessage('拆分成功')
|
||||
} catch (error) {
|
||||
return errorMessage(error.message)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 图片处理,去除水印
|
||||
* @param {*} value
|
||||
* @returns
|
||||
*/
|
||||
async ProcessImage(value) {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
try {
|
||||
value = JSON.parse(value);
|
||||
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 isRemote = mask_setting.isRemote ? mask_setting.isRemote : false;
|
||||
let urls = mask_setting.localUrl ? mask_setting.localUrl + "api/v1/inpaint" : "";
|
||||
/**
|
||||
* 将base64转换为文件
|
||||
* @param {*} value
|
||||
* @returns
|
||||
*/
|
||||
async Base64ToFile(value) {
|
||||
try {
|
||||
value = JSON.parse(value)
|
||||
let base64 = value[0]
|
||||
let out_file_str = value[1]
|
||||
let base64Data = base64.replace(/^data:image\/\w+;base64,/, '')
|
||||
let dataBuffer = Buffer.from(base64Data, 'base64')
|
||||
let out_file = path.join(this.global.config.project_path, out_file_str)
|
||||
let out_folder = path.dirname(out_file)
|
||||
await this.tools.checkFolderExistsOrCreate(out_folder)
|
||||
await fs.promises.writeFile(out_file, dataBuffer)
|
||||
// await this.tools.writeArrayToFile(dataBuffer, out_file);
|
||||
return successMessage(out_file, 'base64保存到本地图片成功')
|
||||
} catch (error) {
|
||||
return errorMessage('base64保存到本地图片失败,失败原因如下:' + error.message)
|
||||
}
|
||||
}
|
||||
|
||||
if (isRemote && isEmpty(urls)) {
|
||||
throw new Error("使用iopaint图片处理,但是没有配置图片处理地址");
|
||||
}
|
||||
if (!isRemote && isEmpty(value.out_file)) {
|
||||
throw new Error("水印处理,使用软件直接处理类型为file,需要指定输出的文件地址");
|
||||
}
|
||||
// TODO 这个方法后面还要改,现在有问题(直接重写,后面这个重构掉,先重写一个)
|
||||
/**
|
||||
* 图片处理,去除水印
|
||||
* @param {*} value
|
||||
* @returns
|
||||
*/
|
||||
async ProcessImage(value) {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
try {
|
||||
value = JSON.parse(value)
|
||||
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 isRemote = mask_setting.isRemote ? mask_setting.isRemote : false
|
||||
let urls = mask_setting.localUrl ? mask_setting.localUrl + 'api/v1/inpaint' : ''
|
||||
|
||||
let out_file;
|
||||
if (!isEmpty(value.out_file)) {
|
||||
out_file = path.join(this.global.config.project_path, value.out_file);
|
||||
let out_folder = path.dirname(out_file);
|
||||
await this.tools.checkFolderExistsOrCreate(out_folder);
|
||||
}
|
||||
if (isRemote && isEmpty(urls)) {
|
||||
throw new Error('使用iopaint图片处理,但是没有配置图片处理地址')
|
||||
}
|
||||
if (!isRemote && isEmpty(value.out_file)) {
|
||||
throw new Error('水印处理,使用软件直接处理类型为file,需要指定输出的文件地址')
|
||||
}
|
||||
|
||||
let res;
|
||||
if (isRemote) {
|
||||
let headers = {
|
||||
"accept": '*/*',
|
||||
'accept-language': 'zh-CN,zh;q=0.9',
|
||||
'content-type': 'application/json'
|
||||
}
|
||||
let data = {
|
||||
"image": value.image,
|
||||
"mask": value.mask,
|
||||
"ldm_steps": 30,
|
||||
"ldm_sampler": "ddim",
|
||||
"zits_wireframe": true,
|
||||
"cv2_flag": "INPAINT_NS",
|
||||
"cv2_radius": 5,
|
||||
"hd_strategy": "Crop",
|
||||
"hd_strategy_crop_triger_size": 640,
|
||||
"hd_strategy_crop_margin": 128,
|
||||
"hd_trategy_resize_imit": 2048 * 5,
|
||||
"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",
|
||||
"use_croper": false,
|
||||
"croper_x": 284,
|
||||
"croper_y": 284,
|
||||
"croper_height": 512,
|
||||
"croper_width": 512,
|
||||
"use_extender": false,
|
||||
"extender_x": 0,
|
||||
"extender_y": 0,
|
||||
"extender_height": 1080,
|
||||
"extender_width": 1080,
|
||||
"sd_mask_blur": 12,
|
||||
"sd_strength": 1,
|
||||
"sd_steps": 50,
|
||||
"sd_guidance_scale": 7.5,
|
||||
"sd_sampler": "DPM++ 2M",
|
||||
"sd_seed": -1,
|
||||
"sd_match_histograms": false,
|
||||
"sd_lcm_lora": false,
|
||||
"paint_by_example_example_image": null,
|
||||
"p2p_image_guidance_scale": 1.5,
|
||||
"enable_controlnet": false,
|
||||
"controlnet_conditioning_scale": 0.4,
|
||||
"controlnet_method": "",
|
||||
"enable_brushnet": false,
|
||||
"brushnet_method": "random_mask",
|
||||
"brushnet_conditioning_scale": 1,
|
||||
"enable_powerpaint_v2": false,
|
||||
"powerpaint_task": "text-guided"
|
||||
};
|
||||
res = await basicApi.post(urls, data, headers);
|
||||
this.global.logger.info(LOGGER_DEFINE.REMOVE_WATERMARK, `iopaint去除水印请求成功`);
|
||||
if (value.type == 'arrayBuffer') {
|
||||
resolve(successMessage(res.data, "图片处理成功", LOGGER_DEFINE.REMOVE_WATERMARK));
|
||||
} else if (value.type == "file") {
|
||||
let buffer = Buffer.from(res.data);
|
||||
await fs.promises.writeFile(out_file, buffer)
|
||||
resolve(successMessage(out_file, "图片处理成功", LOGGER_DEFINE.REMOVE_WATERMARK));
|
||||
}
|
||||
let out_file
|
||||
if (!isEmpty(value.out_file)) {
|
||||
out_file = path.join(this.global.config.project_path, value.out_file)
|
||||
let out_folder = path.dirname(out_file)
|
||||
await this.tools.checkFolderExistsOrCreate(out_folder)
|
||||
}
|
||||
|
||||
} else {
|
||||
let lama_script = path.resolve(define.scripts_path, `lama/lama_inpaint.exe`);
|
||||
// 就是判断指定的文件和文件夹是不是存在
|
||||
let has_exe = await file.CheckFileOrDirExist(lama_script);
|
||||
if (!has_exe) {
|
||||
throw new Error("图片水印处理组件不存在,请看教程自行下载");
|
||||
}
|
||||
let has_model = await file.CheckFileOrDirExist(path.resolve(define.scripts_path, 'lama/model/big-lama.pt'))
|
||||
if (!has_model) {
|
||||
throw new Error("图片水印处理的模型不存在,请看教程自行下载")
|
||||
}
|
||||
let res
|
||||
if (isRemote) {
|
||||
let headers = {
|
||||
accept: '*/*',
|
||||
'accept-language': 'zh-CN,zh;q=0.9',
|
||||
'content-type': 'application/json'
|
||||
}
|
||||
let data = {
|
||||
image: value.image,
|
||||
mask: value.mask,
|
||||
ldm_steps: 30,
|
||||
ldm_sampler: 'ddim',
|
||||
zits_wireframe: true,
|
||||
cv2_flag: 'INPAINT_NS',
|
||||
cv2_radius: 5,
|
||||
hd_strategy: 'Crop',
|
||||
hd_strategy_crop_triger_size: 640,
|
||||
hd_strategy_crop_margin: 128,
|
||||
hd_trategy_resize_imit: 2048 * 5,
|
||||
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',
|
||||
use_croper: false,
|
||||
croper_x: 284,
|
||||
croper_y: 284,
|
||||
croper_height: 512,
|
||||
croper_width: 512,
|
||||
use_extender: false,
|
||||
extender_x: 0,
|
||||
extender_y: 0,
|
||||
extender_height: 1080,
|
||||
extender_width: 1080,
|
||||
sd_mask_blur: 12,
|
||||
sd_strength: 1,
|
||||
sd_steps: 50,
|
||||
sd_guidance_scale: 7.5,
|
||||
sd_sampler: 'DPM++ 2M',
|
||||
sd_seed: -1,
|
||||
sd_match_histograms: false,
|
||||
sd_lcm_lora: false,
|
||||
paint_by_example_example_image: null,
|
||||
p2p_image_guidance_scale: 1.5,
|
||||
enable_controlnet: false,
|
||||
controlnet_conditioning_scale: 0.4,
|
||||
controlnet_method: '',
|
||||
enable_brushnet: false,
|
||||
brushnet_method: 'random_mask',
|
||||
brushnet_conditioning_scale: 1,
|
||||
enable_powerpaint_v2: false,
|
||||
powerpaint_task: 'text-guided'
|
||||
}
|
||||
res = await basicApi.post(urls, data, headers)
|
||||
this.global.logger.info(LOGGER_DEFINE.REMOVE_WATERMARK, `iopaint去除水印请求成功`)
|
||||
if (value.type == 'arrayBuffer') {
|
||||
resolve(successMessage(res.data, '图片处理成功', LOGGER_DEFINE.REMOVE_WATERMARK))
|
||||
} else if (value.type == 'file') {
|
||||
let buffer = Buffer.from(res.data)
|
||||
await fs.promises.writeFile(out_file, buffer)
|
||||
resolve(successMessage(out_file, '图片处理成功', LOGGER_DEFINE.REMOVE_WATERMARK))
|
||||
}
|
||||
} else {
|
||||
let lama_script = path.resolve(define.scripts_path, `lama/lama_inpaint.exe`)
|
||||
// 就是判断指定的文件和文件夹是不是存在
|
||||
let has_exe = await file.CheckFileOrDirExist(lama_script)
|
||||
if (!has_exe) {
|
||||
throw new Error('图片水印处理组件不存在,请看教程自行下载')
|
||||
}
|
||||
let has_model = await file.CheckFileOrDirExist(
|
||||
path.resolve(define.scripts_path, 'lama/model/big-lama.pt')
|
||||
)
|
||||
if (!has_model) {
|
||||
throw new Error('图片水印处理的模型不存在,请看教程自行下载')
|
||||
}
|
||||
|
||||
this.global.logger.info(LOGGER_DEFINE.REMOVE_WATERMARK, `开始使用lama去除水印,开始调用lama程序`);
|
||||
// 先将对应的base64文件写道本地
|
||||
let image_path = await this.Base64ToFile(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) {
|
||||
throw new Error(image_path.message)
|
||||
}
|
||||
if (mask_path.code == 0) {
|
||||
throw new Error(mask_path.message)
|
||||
}
|
||||
this.global.logger.info(
|
||||
LOGGER_DEFINE.REMOVE_WATERMARK,
|
||||
`开始使用lama去除水印,开始调用lama程序`
|
||||
)
|
||||
// 先将对应的base64文件写道本地
|
||||
let image_path = await this.Base64ToFile(
|
||||
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) {
|
||||
throw new Error(image_path.message)
|
||||
}
|
||||
if (mask_path.code == 0) {
|
||||
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('python', [lama_script, '-l', image_path.data, mask_path.data, out_file], { encoding: 'utf-8' });
|
||||
child.on('error', (error) => {
|
||||
reject(error.toString())
|
||||
return
|
||||
})
|
||||
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' });
|
||||
child.on('error', (error) => {
|
||||
reject(error.toString())
|
||||
return
|
||||
})
|
||||
|
||||
child.stdout.on('data', (data) => {
|
||||
console.log(data.toString())
|
||||
})
|
||||
child.stdout.on('data', (data) => {
|
||||
console.log(data.toString())
|
||||
})
|
||||
|
||||
child.stderr.on('data', (data) => {
|
||||
reject(data.toString())
|
||||
return
|
||||
})
|
||||
child.stderr.on('data', (data) => {
|
||||
reject(data.toString())
|
||||
return
|
||||
})
|
||||
|
||||
child.on('close', async (data) => {
|
||||
if (data != 0) {
|
||||
this.global.logger.error(LOGGER_DEFINE.REMOVE_WATERMARK, `lama去除水印失败,错误码:${data.toString()}`);
|
||||
reject("lama去除水印错误。请看日志详细信息!")
|
||||
return
|
||||
}
|
||||
// 判断是不是有输出文件
|
||||
let has_out = await file.CheckFileOrDirExist(out_file);
|
||||
if (!has_out) {
|
||||
reject("lama去除水印失败,没有输出文件")
|
||||
return
|
||||
}
|
||||
|
||||
if (value.type == 'arrayBuffer') {
|
||||
// 读取导出的文件
|
||||
let res_data = await fs.promises.readFile(out_file);
|
||||
resolve(successMessage(res_data, "图片处理成功", LOGGER_DEFINE.REMOVE_WATERMARK));
|
||||
} else if (value.type == "file") {
|
||||
resolve(successMessage(out_file, "图片处理成功", LOGGER_DEFINE.REMOVE_WATERMARK));
|
||||
}
|
||||
})
|
||||
}
|
||||
} catch (error) {
|
||||
reject("图片处理失败,失败原因如下:" + error.message)
|
||||
child.on('close', async (data) => {
|
||||
if (data != 0) {
|
||||
this.global.logger.error(
|
||||
LOGGER_DEFINE.REMOVE_WATERMARK,
|
||||
`lama去除水印失败,错误码:${data.toString()}`
|
||||
)
|
||||
reject('lama去除水印错误。请看日志详细信息!')
|
||||
return
|
||||
}
|
||||
// 判断是不是有输出文件
|
||||
let has_out = await file.CheckFileOrDirExist(out_file)
|
||||
if (!has_out) {
|
||||
reject('lama去除水印失败,没有输出文件')
|
||||
return
|
||||
}
|
||||
|
||||
if (value.type == 'arrayBuffer') {
|
||||
// 读取导出的文件
|
||||
let res_data = await fs.promises.readFile(out_file)
|
||||
resolve(successMessage(res_data, '图片处理成功', LOGGER_DEFINE.REMOVE_WATERMARK))
|
||||
} else if (value.type == 'file') {
|
||||
resolve(successMessage(out_file, '图片处理成功', LOGGER_DEFINE.REMOVE_WATERMARK))
|
||||
}
|
||||
})
|
||||
}
|
||||
} catch (error) {
|
||||
reject('图片处理失败,失败原因如下:' + error.message)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// TODO 该方法后面需删除,使用ProcessImage方法
|
||||
// 批量处理所有的图片,去除水印
|
||||
async BatchProcessImage(value) {
|
||||
try {
|
||||
let input_folder = value
|
||||
input_folder = path.resolve(this.global.config.project_path, input_folder)
|
||||
if (!(await this.tools.checkExists(input_folder))) {
|
||||
throw new Error('输入的文件夹不存在')
|
||||
}
|
||||
|
||||
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)
|
||||
if (has_files) {
|
||||
let files = await file.GetFilesWithExtensions(new_input_folder, ['.png'])
|
||||
if (files.length <= 0) {
|
||||
// 删除指定的文件夹
|
||||
await fs.promises.rm(new_input_folder, { recursive: true })
|
||||
await file.BackupFileOrFolder(input_folder, new_input_folder)
|
||||
// 创建新的input_folder
|
||||
await fs.promises.mkdir(input_folder, { recursive: true })
|
||||
}
|
||||
} else {
|
||||
await file.BackupFileOrFolder(input_folder, new_input_folder)
|
||||
// 创建新的input_folder
|
||||
await fs.promises.mkdir(input_folder, { recursive: true })
|
||||
}
|
||||
// 开始备份
|
||||
|
||||
// 获取蒙板
|
||||
let mask_setting = (
|
||||
await ImageSetting.GetDefineConfigJsonByProperty(
|
||||
JSON.stringify(['img_base', 'mask_setting', false, {}])
|
||||
)
|
||||
).data
|
||||
if (mask_setting.isRemote && isEmpty(mask_setting.localUrl)) {
|
||||
throw new Error('使用iopaint图片处理,但是没有配置图片处理地址')
|
||||
}
|
||||
if (isEmpty(mask_setting.mask_path)) {
|
||||
throw new Error('没有配置蒙板的路径')
|
||||
}
|
||||
|
||||
// 获取文件夹里面所有的图片文件
|
||||
// let png_files = await this.tools.getFilesWithExtensions(input_folder, '.png');
|
||||
let png_files = await file.GetFilesWithExtensions(new_input_folder, ['.png'])
|
||||
if (png_files.length == 0) {
|
||||
throw new Error('没有找到任何的抽帧图片文件')
|
||||
}
|
||||
|
||||
// 获取图片的总数,将数据返回前端,更新进度条
|
||||
this.global.newWindow[0].win.webContents.send(
|
||||
DEFINE_STRING.IMG.BATCH_PROCESS_IMAGE_RESULT,
|
||||
successMessage({
|
||||
total: png_files.length,
|
||||
current: 0
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
// 批量处理所有的图片,去除水印
|
||||
async BatchProcessImage(value) {
|
||||
try {
|
||||
let input_folder = value;
|
||||
input_folder = path.resolve(this.global.config.project_path, input_folder)
|
||||
if (!(await this.tools.checkExists(input_folder))) {
|
||||
throw new Error("输入的文件夹不存在");
|
||||
}
|
||||
// 默认所有的的宽高都是一样的,获取第一张图片的宽高
|
||||
let first_image = png_files[0]
|
||||
let first_image_size = await image.GetImageSize(first_image)
|
||||
|
||||
let new_input_folder = path.join(this.global.config.project_path, `tmp/bak/${path.basename(input_folder)}`);
|
||||
// 重新设置蒙板的宽高,和图片的宽高一样
|
||||
let mask_base = await image.ResizeImage(
|
||||
mask_setting.mask_path,
|
||||
first_image_size.width,
|
||||
first_image_size.height,
|
||||
'base64'
|
||||
)
|
||||
mask_base = `data:image/png;base64,${mask_base}`
|
||||
|
||||
// 在备份之前判断,旧的文件是不是存在,里面是不是有图片,没有图片,直接删除,在备份过去,存在直接开始下面的请求
|
||||
let has_files = await file.CheckFileOrDirExist(new_input_folder);
|
||||
if (has_files) {
|
||||
let files = await file.GetFilesWithExtensions(new_input_folder, ['.png']);
|
||||
if (files.length <= 0) {
|
||||
// 删除指定的文件夹
|
||||
await fs.promises.rm(new_input_folder, { recursive: true });
|
||||
await file.BackupFileOrFolder(input_folder, new_input_folder);
|
||||
// 创建新的input_folder
|
||||
await fs.promises.mkdir(input_folder, { recursive: true })
|
||||
}
|
||||
} else {
|
||||
await file.BackupFileOrFolder(input_folder, new_input_folder);
|
||||
// 创建新的input_folder
|
||||
await fs.promises.mkdir(input_folder, { recursive: true })
|
||||
}
|
||||
// 开始备份
|
||||
// 开始处理所有的图片
|
||||
for (let i = 0; i < png_files.length; i++) {
|
||||
const element = png_files[i]
|
||||
// 获取指定的图片,并且转换为base64
|
||||
let image_base = await fs.promises.readFile(element)
|
||||
image_base = image_base.toString('base64')
|
||||
image_base = `data:image/png;base64,${image_base}`
|
||||
|
||||
// 开始处理图片
|
||||
let res = await this.ProcessImage(
|
||||
JSON.stringify({
|
||||
image: image_base,
|
||||
mask: mask_base,
|
||||
type: 'file',
|
||||
out_file: `tmp/input_crop/${path.basename(element)}`
|
||||
})
|
||||
)
|
||||
|
||||
// 获取蒙板
|
||||
let mask_setting = (await ImageSetting.GetDefineConfigJsonByProperty(JSON.stringify(["img_base", "mask_setting", false, {}]))).data;
|
||||
if (mask_setting.isRemote && isEmpty(mask_setting.localUrl)) {
|
||||
throw new Error("使用iopaint图片处理,但是没有配置图片处理地址");
|
||||
}
|
||||
if (isEmpty(mask_setting.mask_path)) {
|
||||
throw new Error("没有配置蒙板的路径");
|
||||
}
|
||||
|
||||
// 获取文件夹里面所有的图片文件
|
||||
// let png_files = await this.tools.getFilesWithExtensions(input_folder, '.png');
|
||||
let png_files = await file.GetFilesWithExtensions(new_input_folder, ['.png']);
|
||||
if (png_files.length == 0) {
|
||||
throw new Error("没有找到任何的抽帧图片文件");
|
||||
}
|
||||
|
||||
// 获取图片的总数,将数据返回前端,更新进度条
|
||||
this.global.newWindow[0].win.webContents.send(DEFINE_STRING.IMG.BATCH_PROCESS_IMAGE_RESULT, successMessage({
|
||||
total: png_files.length,
|
||||
current: 0
|
||||
}))
|
||||
|
||||
// 默认所有的的宽高都是一样的,获取第一张图片的宽高
|
||||
let first_image = png_files[0];
|
||||
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');
|
||||
mask_base = `data:image/png;base64,${mask_base}`;
|
||||
|
||||
// 开始处理所有的图片
|
||||
for (let i = 0; i < png_files.length; i++) {
|
||||
const element = png_files[i];
|
||||
// 获取指定的图片,并且转换为base64
|
||||
let image_base = await fs.promises.readFile(element);
|
||||
image_base = image_base.toString('base64');
|
||||
image_base = `data:image/png;base64,${image_base}`;
|
||||
|
||||
// 开始处理图片
|
||||
let res = await this.ProcessImage(JSON.stringify({
|
||||
image: image_base,
|
||||
mask: mask_base,
|
||||
type: "file",
|
||||
out_file: `tmp/input_crop/${path.basename(element)}`
|
||||
}))
|
||||
|
||||
if (res.code == 0) {
|
||||
throw new Error(res.message);
|
||||
}
|
||||
|
||||
// 删除之前的
|
||||
await this.tools.deleteFileOrDirectory(element)
|
||||
await this.tools.delay(1000)
|
||||
// 当前图片处理成功,将信息返回前端,更新进度条
|
||||
this.global.newWindow[0].win.webContents.send(DEFINE_STRING.IMG.BATCH_PROCESS_IMAGE_RESULT, successMessage({
|
||||
total: png_files.length,
|
||||
current: i + 1
|
||||
}))
|
||||
}
|
||||
|
||||
return successMessage("所有的图片处理成功", LOGGER_DEFINE.REMOVE_WATERMARK);
|
||||
|
||||
} catch (error) {
|
||||
return errorMessage("图片处理失败,失败原因如下:" + error.message, LOGGER_DEFINE.REMOVE_WATERMARK);
|
||||
if (res.code == 0) {
|
||||
throw new Error(res.message)
|
||||
}
|
||||
|
||||
// 删除之前的
|
||||
await this.tools.deleteFileOrDirectory(element)
|
||||
await this.tools.delay(1000)
|
||||
// 当前图片处理成功,将信息返回前端,更新进度条
|
||||
this.global.newWindow[0].win.webContents.send(
|
||||
DEFINE_STRING.IMG.BATCH_PROCESS_IMAGE_RESULT,
|
||||
successMessage({
|
||||
total: png_files.length,
|
||||
current: i + 1
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
return successMessage('所有的图片处理成功', LOGGER_DEFINE.REMOVE_WATERMARK)
|
||||
} catch (error) {
|
||||
return errorMessage(
|
||||
'图片处理失败,失败原因如下:' + error.message,
|
||||
LOGGER_DEFINE.REMOVE_WATERMARK
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { errorMessage, successMessage } from "../generalTools";
|
||||
import { errorMessage, successMessage } from "../Public/generalTools";
|
||||
import { LOGGER_DEFINE } from '../../define/logger_define'
|
||||
import { Setting } from "../setting/setting";
|
||||
import { isEmpty } from "lodash";
|
||||
|
||||
@ -9,7 +9,7 @@ const sharp = require('sharp');
|
||||
import { SdSettingDefine } from "../../define/setting/sdSettingDefine";
|
||||
import { PublicMethod } from "./publicMethod";
|
||||
import { Tools } from "../tools";
|
||||
import { errorMessage, successMessage } from "../generalTools";
|
||||
import { errorMessage, successMessage } from "../Public/generalTools";
|
||||
import { SdApi } from "../../api/sdApi";
|
||||
const { v4: uuidv4 } = require('uuid');
|
||||
|
||||
|
||||
69
src/main/Public/chineseSimilarity.ts
Normal 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);
|
||||
@ -11,7 +11,7 @@ import { cloneDeep } from "lodash";
|
||||
const compressing = require("compressing");
|
||||
|
||||
export class ClipDraft {
|
||||
constructor(global, value) {
|
||||
constructor(global, value, draftName = null) {
|
||||
this.speedId = null;
|
||||
this.canvasesId = null;
|
||||
this.soundChannelId = null;
|
||||
@ -33,10 +33,11 @@ export class ClipDraft {
|
||||
this.global = global;
|
||||
this.value = value;
|
||||
this.pm = new PublicMethod(global);
|
||||
this.draft_name = draftName;
|
||||
}
|
||||
|
||||
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);
|
||||
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]));
|
||||
@ -124,9 +125,6 @@ export class ClipDraft {
|
||||
materialVideoTmpJson.id = materialId;
|
||||
|
||||
// 获取输入的图片宽高
|
||||
// let image = await Jimp.read(imagePath);
|
||||
// let width = image.bitmap.width;
|
||||
// let height = image.bitmap.height;
|
||||
materialVideoTmpJson.width = 1000;
|
||||
materialVideoTmpJson.height = 1000;
|
||||
|
||||
|
||||
114
src/main/Public/generalTools.ts
Normal file
@ -0,0 +1,114 @@
|
||||
import { GeneralResponse } from '../../model/generalResponse'
|
||||
|
||||
/**
|
||||
* 判断字符串的值是不是存在,不是null,不是undefined,不是空字符串,存在的话添加指定的后缀
|
||||
* @param {*} value 要检查的字符串
|
||||
* @param {*} suffix 要添加的后缀
|
||||
* @returns
|
||||
*/
|
||||
function checkStringValueAddSuffix(value: string, suffix: string): string {
|
||||
if (value && value !== null && value !== undefined && value !== '') {
|
||||
return value + suffix
|
||||
} else {
|
||||
return ''
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断字符串的值是不是存在,不是null,不是undefined,不是空字符串,存在的话添加指定的前缀
|
||||
* @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 ''
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 返回成功的消息,包含code,data,message
|
||||
* @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
|
||||
}
|
||||
@ -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
|
||||
}
|
||||
],
|
||||
'获取小说类型成功'
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -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
|
||||
}
|
||||
@ -13,7 +13,7 @@ const execAsync = util.promisify(exec);
|
||||
const { v4: uuidv4 } = require('uuid'); // 引入UUID库来生成唯一标识符
|
||||
let fspromises = require("fs").promises;
|
||||
import { ImageSetting } from "../../define/setting/imageSetting";
|
||||
import { errorMessage } from "../generalTools";
|
||||
import { errorMessage } from "../Public/generalTools";
|
||||
import { LOGGER_DEFINE } from "../../define/logger_define";
|
||||
|
||||
|
||||
|
||||
283
src/main/Service/Book/BooKBasic.ts
Normal 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
|
||||
}
|
||||
588
src/main/Service/Book/ReverseBook.ts
Normal 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
|
||||
}
|
||||
@ -3,24 +3,26 @@ import fs from 'fs'
|
||||
const util = require('util')
|
||||
const { exec } = require('child_process')
|
||||
const execAsync = util.promisify(exec)
|
||||
import { define } from '../../define/define'
|
||||
import { BookService } from '../../define/db/service/Book/bookService'
|
||||
import { TaskScheduler } from './taskScheduler'
|
||||
import { LoggerStatus, LoggerType, OtherData } from '../../define/enum/softwareEnum'
|
||||
import { errorMessage, successMessage } from '../generalTools'
|
||||
import { CheckFileOrDirExist, CheckFolderExistsOrCreate } from '../../define/Tools/file'
|
||||
import { BookTaskDetailService } from '../../define/db/service/Book/bookTaskDetailService'
|
||||
import { BookBackTaskListService } from '../../define/db/service/Book/bookBackTaskListService'
|
||||
import { BookTaskService } from '../../define/db/service/Book/bookTaskService'
|
||||
import { define } from '../../../define/define'
|
||||
import { BookService } from '../../../define/db/service/Book/bookService'
|
||||
import { TaskScheduler } from '../taskScheduler'
|
||||
import { LoggerStatus, LoggerType, OtherData } from '../../../define/enum/softwareEnum'
|
||||
import { errorMessage, successMessage } from '../../Public/generalTools'
|
||||
import { CheckFileOrDirExist, CheckFolderExistsOrCreate } from '../../../define/Tools/file'
|
||||
import { BookTaskDetailService } from '../../../define/db/service/Book/bookTaskDetailService'
|
||||
import { BookBackTaskListService } from '../../../define/db/service/Book/bookBackTaskListService'
|
||||
import { BookTaskService } from '../../../define/db/service/Book/bookTaskService'
|
||||
import { isEmpty, set } from 'lodash'
|
||||
import { TimeStringToMilliseconds, MillisecondsToTimeString } from '../../define/Tools/time'
|
||||
import { FfmpegOptions } from './ffmpegOptions'
|
||||
import { TimeStringToMilliseconds, MillisecondsToTimeString } from '../../../define/Tools/time'
|
||||
import { FfmpegOptions } from '../ffmpegOptions'
|
||||
import {
|
||||
BookBackTaskType,
|
||||
BookTaskStatus,
|
||||
BookType,
|
||||
TaskExecuteType
|
||||
} from '../../define/enum/bookEnum'
|
||||
} from '../../../define/enum/bookEnum'
|
||||
import { Book } from '../../../model/book'
|
||||
import { GeneralResponse } from '../../../model/generalResponse'
|
||||
|
||||
const fspromises = fs.promises
|
||||
|
||||
@ -28,6 +30,14 @@ const fspromises = fs.promises
|
||||
* 后台执行的任务函数,直接调用改函数即可,抽帧,分镜,提取字幕等
|
||||
*/
|
||||
export class BasicReverse {
|
||||
bookService: BookService
|
||||
bookTaskService: BookTaskService
|
||||
bookTaskDetailService: BookTaskDetailService
|
||||
bookBackTaskListService: BookBackTaskListService
|
||||
|
||||
taskScheduler: TaskScheduler
|
||||
ffmpegOptions: FfmpegOptions
|
||||
|
||||
constructor() {
|
||||
this.taskScheduler = new TaskScheduler()
|
||||
this.ffmpegOptions = new FfmpegOptions()
|
||||
@ -75,10 +85,11 @@ export class BasicReverse {
|
||||
}
|
||||
|
||||
// 获取小说对应的批次任务数据,默认初始化为第一个
|
||||
let bookTaskRes = this.bookTaskService.GetBookTaskData({
|
||||
let bookTaskRes = await this.bookTaskService.GetBookTaskData({
|
||||
bookId: bookId,
|
||||
name: 'output_00001'
|
||||
})
|
||||
|
||||
if (bookTaskRes.data.bookTasks.length <= 0 || bookTaskRes.data.total <= 0) {
|
||||
throw new Error('没有找到对应的小说批次任务数据,请检查bookId是否正确')
|
||||
}
|
||||
@ -107,11 +118,133 @@ export class BasicReverse {
|
||||
)
|
||||
return successMessage(null, '添加分镜任务成功', 'BasicReverse_AddFrameDataTask')
|
||||
} catch (error) {
|
||||
return errorMessage(
|
||||
'添加分镜任务失败,错误信息如下: ' + error.message,
|
||||
'BasicReverse_AddFrameDataTask'
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行分镜任务
|
||||
* @param bookId 小说ID
|
||||
* @param bookTaskId 小说人物ID,只能是第一个
|
||||
* @returns
|
||||
*/
|
||||
async ComputeStoryboardFunc(bookId: string, bookTaskId: string): Promise<string> {
|
||||
await this.InitService()
|
||||
let book = this.bookService.GetBookDataById(bookId)
|
||||
if (book == null) {
|
||||
throw new Error('没有找到对应的小说数据')
|
||||
}
|
||||
|
||||
this.bookTaskService.UpdateBookTaskStatus(bookTaskId, BookTaskStatus.STORYBOARD)
|
||||
|
||||
// 分镜之前,删除之前的老数据
|
||||
let deleteBookTaskRes = this.bookTaskDetailService.DeleteBookTaskDetail({
|
||||
bookId: bookId,
|
||||
bookTaskId: bookTaskId
|
||||
})
|
||||
|
||||
let oldVideoPath = book.oldVideoPath
|
||||
let frameJson = oldVideoPath + '.json'
|
||||
|
||||
let sensitivity = 30
|
||||
// 开始之前,推送日志
|
||||
let log_content = `开始进行分镜操作,视频地址:${oldVideoPath},敏感度:${sensitivity},正在调用程序进行处理`
|
||||
await this.taskScheduler.AddLogToDB(
|
||||
bookId,
|
||||
book.type,
|
||||
log_content,
|
||||
OtherData.DEFAULT,
|
||||
LoggerStatus.DOING
|
||||
)
|
||||
|
||||
// 小说进行分镜(python进行,将结果写道一个json里面)
|
||||
// 使用异步的方法调用一个python程序,然后写入到指定的json文件中k
|
||||
let command = `"${path.join(
|
||||
define.scripts_path,
|
||||
'Lai.exe'
|
||||
)}" "-ka" "${oldVideoPath}" "${frameJson}" "${sensitivity}"`
|
||||
const output = await execAsync(command, {
|
||||
maxBuffer: 1024 * 1024 * 10,
|
||||
encoding: 'utf-8'
|
||||
})
|
||||
// 有错误输出
|
||||
if (output.stderr != '') {
|
||||
let error_msg = `分镜成功,但有警告提示:${output.stderr}`
|
||||
await this.taskScheduler.AddLogToDB(
|
||||
bookId,
|
||||
book.type,
|
||||
error_msg,
|
||||
OtherData.DEFAULT,
|
||||
LoggerStatus.FAIL
|
||||
)
|
||||
}
|
||||
// 分镜成功,处理输出
|
||||
let josnIsExist = await CheckFileOrDirExist(frameJson)
|
||||
if (!josnIsExist) {
|
||||
let error_message = `分镜失败,没有找到对应的分镜输出文件:${frameJson}`
|
||||
this.bookTaskService.UpdateBookTaskStatus(
|
||||
bookTaskId,
|
||||
BookTaskStatus.STORYBOARD_FAIL,
|
||||
error_message
|
||||
)
|
||||
await this.taskScheduler.AddLogToDB(
|
||||
bookId,
|
||||
book.type,
|
||||
error_message,
|
||||
OtherData.DEFAULT,
|
||||
LoggerStatus.FAIL
|
||||
)
|
||||
throw new Error(error_message)
|
||||
}
|
||||
|
||||
let frameJsonData = JSON.parse(await fspromises.readFile(frameJson, 'utf-8'))
|
||||
if (frameJsonData.length <= 0) {
|
||||
let error_msg = `分镜失败,没有找到对应的分镜数据`
|
||||
this.bookTaskService.UpdateBookTaskStatus(
|
||||
bookTaskId,
|
||||
BookTaskStatus.STORYBOARD_FAIL,
|
||||
error_msg
|
||||
)
|
||||
await this.taskScheduler.AddLogToDB(
|
||||
bookId,
|
||||
book.type,
|
||||
error_msg,
|
||||
OtherData.DEFAULT,
|
||||
LoggerStatus.FAIL
|
||||
)
|
||||
throw new Error(error_msg)
|
||||
}
|
||||
// 循环写入小说人物详细数据
|
||||
for (let i = 0; i < frameJsonData.length; i++) {
|
||||
let dataArray = frameJsonData[i]
|
||||
let bookTaskDetail = {
|
||||
bookId: bookId,
|
||||
bookTaskId: bookTaskId,
|
||||
startTime: 0,
|
||||
endTime: 0
|
||||
} as Book.SelectBookTaskDetail
|
||||
|
||||
// 将字符串转换为number
|
||||
bookTaskDetail.startTime = TimeStringToMilliseconds(dataArray[0])
|
||||
bookTaskDetail.endTime = TimeStringToMilliseconds(dataArray[1])
|
||||
bookTaskDetail.status = BookTaskStatus.STORYBOARD_DONE // 分镜完成
|
||||
|
||||
let res = this.bookTaskDetailService.AddBookTaskDetail(bookTaskDetail)
|
||||
if (res.code == 0) {
|
||||
throw new Error(res.message)
|
||||
}
|
||||
}
|
||||
|
||||
this.bookTaskService.UpdateBookTaskStatus(bookTaskId, BookTaskStatus.STORYBOARD_DONE)
|
||||
// 分镜成功,推送日志
|
||||
await this.taskScheduler.AddLogToDB(
|
||||
bookId,
|
||||
book.type,
|
||||
`分镜成功,分镜数据如下:${frameJsonData}`,
|
||||
OtherData.DEFAULT,
|
||||
LoggerStatus.SUCCESS
|
||||
)
|
||||
return `分镜成功,分镜信息在 ${frameJson}`
|
||||
}
|
||||
|
||||
/**
|
||||
@ -130,121 +263,11 @@ export class BasicReverse {
|
||||
let bookId = task.bookId
|
||||
let bookTaskId = task.bookTaskId
|
||||
|
||||
let bookRes = this.bookService.GetBookDataById(bookId)
|
||||
if (bookRes.data == null) {
|
||||
throw new Error('没有找到对应的小说数据')
|
||||
}
|
||||
let book = bookRes.data
|
||||
let frameRes = await this.ComputeStoryboardFunc(bookId, bookTaskId)
|
||||
return successMessage(frameRes)
|
||||
|
||||
this.bookTaskService.UpdateBookTaskStatus(bookTaskId, BookTaskStatus.STORYBOARD)
|
||||
|
||||
// 分镜之前,删除之前的老数据
|
||||
let deleteBookTaskRes = this.bookTaskDetailService.DeleteBookTaskDetail({
|
||||
bookId: bookId,
|
||||
bookTaskId: bookTaskId
|
||||
})
|
||||
|
||||
let oldVideoPath = book.oldVideoPath
|
||||
let frameJson = oldVideoPath + '.json'
|
||||
|
||||
let sensitivity = 30
|
||||
// 开始之前,推送日志
|
||||
let log_content = `开始进行分镜操作,视频地址:${oldVideoPath},敏感度:${sensitivity},正在调用程序进行处理`
|
||||
await this.taskScheduler.AddLogToDB(
|
||||
bookId,
|
||||
book.type,
|
||||
log_content,
|
||||
OtherData.DEFAULT,
|
||||
LoggerStatus.DOING
|
||||
)
|
||||
|
||||
// 小说进行分镜(python进行,将结果写道一个json里面)
|
||||
// 使用异步的方法调用一个python程序,然后写入到指定的json文件中k
|
||||
let command = `"${path.join(
|
||||
define.scripts_path,
|
||||
'Lai.exe'
|
||||
)}" "-ka" "${oldVideoPath}" "${frameJson}" "${sensitivity}"`
|
||||
const output = await execAsync(command, {
|
||||
maxBuffer: 1024 * 1024 * 10,
|
||||
encoding: 'utf-8'
|
||||
})
|
||||
// 有错误输出
|
||||
if (output.stderr != '') {
|
||||
let error_msg = `分镜成功,但有警告提示:${output.stderr}`
|
||||
await this.taskScheduler.AddLogToDB(
|
||||
bookId,
|
||||
book.type,
|
||||
error_msg,
|
||||
OtherData.DEFAULT,
|
||||
LoggerStatus.FAIL
|
||||
)
|
||||
}
|
||||
// 分镜成功,处理输出
|
||||
let josnIsExist = await CheckFileOrDirExist(frameJson)
|
||||
if (!josnIsExist) {
|
||||
let error_message = `分镜失败,没有找到对应的分镜输出文件:${frameJson}`
|
||||
this.bookTaskService.UpdateBookTaskStatus(
|
||||
bookTaskId,
|
||||
BookTaskStatus.STORYBOARD_FAIL,
|
||||
error_message
|
||||
)
|
||||
await this.taskScheduler.AddLogToDB(
|
||||
bookId,
|
||||
book.type,
|
||||
error_message,
|
||||
OtherData.DEFAULT,
|
||||
LoggerStatus.FAIL
|
||||
)
|
||||
throw new Error(error_message)
|
||||
}
|
||||
|
||||
let frameJsonData = JSON.parse(await fspromises.readFile(frameJson, 'utf-8'))
|
||||
if (frameJsonData.length <= 0) {
|
||||
let error_msg = `分镜失败,没有找到对应的分镜数据`
|
||||
this.bookTaskService.UpdateBookTaskStatus(
|
||||
bookTaskId,
|
||||
BookTaskStatus.STORYBOARD_FAIL,
|
||||
error_msg
|
||||
)
|
||||
await this.taskScheduler.AddLogToDB(
|
||||
bookId,
|
||||
book.type,
|
||||
error_msg,
|
||||
OtherData.DEFAULT,
|
||||
LoggerStatus.FAIL
|
||||
)
|
||||
throw new Error(error_msg)
|
||||
}
|
||||
// 循环写入小说人物详细数据
|
||||
for (let i = 0; i < frameJsonData.length; i++) {
|
||||
let dataArray = frameJsonData[i]
|
||||
let bookTaskDetail = {
|
||||
bookId: bookId,
|
||||
bookTaskId: bookTaskId
|
||||
}
|
||||
|
||||
// 将字符串转换为number
|
||||
bookTaskDetail.startTime = TimeStringToMilliseconds(dataArray[0])
|
||||
bookTaskDetail.endTime = TimeStringToMilliseconds(dataArray[1])
|
||||
|
||||
let res = this.bookTaskDetailService.AddBookTaskDetail(bookTaskDetail)
|
||||
if (res.code == 0) {
|
||||
throw new Error(res.message)
|
||||
}
|
||||
}
|
||||
|
||||
this.bookTaskService.UpdateBookTaskStatus(task.bookTaskId, BookTaskStatus.STORYBOARD_DONE)
|
||||
// 分镜成功,推送日志
|
||||
await this.taskScheduler.AddLogToDB(
|
||||
bookId,
|
||||
book.type,
|
||||
`分镜成功,分镜数据如下:${frameJsonData}`,
|
||||
OtherData.DEFAULT,
|
||||
LoggerStatus.SUCCESS
|
||||
)
|
||||
return successMessage(null, `分镜成功,分镜信息在 ${frameJson}`, 'BasicReverse_GetFrameData')
|
||||
} catch (error) {
|
||||
return errorMessage(error.message, 'BasicReverse_GetFrameData')
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
@ -264,16 +287,14 @@ export class BasicReverse {
|
||||
await this.InitService()
|
||||
|
||||
// 判断小说是不是存在
|
||||
let bookRes = this.bookService.GetBookDataById(bookId)
|
||||
let book = this.bookService.GetBookDataById(bookId)
|
||||
|
||||
if (bookRes == null) {
|
||||
if (book == null) {
|
||||
throw new Error('没有找到对应的小说数据')
|
||||
}
|
||||
|
||||
let book = bookRes.data
|
||||
|
||||
// 找到对应的小说ID和对应的小说批次任务ID,判断是不是有分镜数据
|
||||
let bookTaskRes = this.bookTaskService.GetBookTaskData({
|
||||
let bookTaskRes = await this.bookTaskService.GetBookTaskData({
|
||||
bookId: bookId,
|
||||
name: 'output_00001'
|
||||
})
|
||||
@ -296,9 +317,9 @@ export class BasicReverse {
|
||||
OtherData.DEFAULT,
|
||||
LoggerStatus.DOING
|
||||
)
|
||||
let frameRes = this.GetFrameData(bookId)
|
||||
let frameRes = await this.GetFrameData(bookId)
|
||||
if (frameRes.code == 0) {
|
||||
throw new Error((await frameRes).message)
|
||||
throw new Error(frameRes.message)
|
||||
}
|
||||
}
|
||||
|
||||
@ -351,104 +372,93 @@ export class BasicReverse {
|
||||
)
|
||||
return successMessage(null, '添加视频裁剪任务成功', 'BasicReverse_AddCutVideoDataTask')
|
||||
} catch (error) {
|
||||
await this.taskScheduler.AddLogToDB(
|
||||
bookId,
|
||||
book.type,
|
||||
error.message,
|
||||
OtherData.DEFAULT,
|
||||
LoggerStatus.FAIL
|
||||
)
|
||||
return errorMessage(
|
||||
'添加视频裁剪任务失败,错误信息如下: ' + error.message,
|
||||
'BasicReverse_CutVideoData'
|
||||
)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 裁剪视频操作
|
||||
* @param bookTaskDetailId 小说详细的分镜ID
|
||||
* @returns
|
||||
*/
|
||||
async FrameDataToCutVideoData(bookTaskDetail: Book.SelectBookTaskDetail): Promise<string> {
|
||||
await this.InitService()
|
||||
let startTime = bookTaskDetail.startTime
|
||||
let endTime = bookTaskDetail.endTime
|
||||
|
||||
let book = this.bookService.GetBookDataById(bookTaskDetail.bookId)
|
||||
if (book == null) {
|
||||
throw new Error('没有找到对应的小说数据')
|
||||
}
|
||||
|
||||
if (startTime == null || endTime == null) {
|
||||
this.bookTaskService.UpdateBookTaskStatus(
|
||||
bookTaskDetail.bookTaskId,
|
||||
BookTaskStatus.SPLIT_FAIL,
|
||||
'开始时间和结束时间不能为空'
|
||||
)
|
||||
throw new Error('开始时间和结束时间不能为空')
|
||||
}
|
||||
let outVideoFile = path.join(book.bookFolderPath, `data/frame/${bookTaskDetail.name}.mp4`)
|
||||
|
||||
let res = await this.ffmpegOptions.FfmpegCutVideo(
|
||||
startTime,
|
||||
endTime,
|
||||
book.oldVideoPath,
|
||||
outVideoFile
|
||||
)
|
||||
if (res.code == 0) {
|
||||
this.bookTaskService.UpdateBookTaskStatus(
|
||||
bookTaskDetail.bookTaskId,
|
||||
BookTaskStatus.SPLIT_FAIL,
|
||||
res.message
|
||||
)
|
||||
throw new Error(res.message)
|
||||
}
|
||||
|
||||
// 视频裁剪完成,要将裁剪后的视频地址写入到数据库中
|
||||
this.bookTaskDetailService.UpdateBookTaskDetail(bookTaskDetail.id, {
|
||||
videoPath: path.relative(define.project_path, outVideoFile)
|
||||
})
|
||||
|
||||
// 小改小说批次的状态
|
||||
this.bookTaskService.UpdateBookTaskStatus(bookTaskDetail.bookTaskId, BookTaskStatus.SPLIT_DONE)
|
||||
// 结束,分镜完毕,推送日志,返回成功
|
||||
await this.taskScheduler.AddLogToDB(
|
||||
bookTaskDetail.bookId,
|
||||
book.type,
|
||||
`${bookTaskDetail.name}_视频裁剪完成`,
|
||||
OtherData.DEFAULT,
|
||||
LoggerStatus.SUCCESS
|
||||
)
|
||||
return `${bookTaskDetail.name}_视频裁剪完成`;
|
||||
}
|
||||
|
||||
/**
|
||||
* 开始执行指定的任务
|
||||
* @param {*} task
|
||||
* @returns
|
||||
*/
|
||||
async CutVideoData(task) {
|
||||
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 bookTaskDetailRes = this.bookTaskDetailService.GetBookTaskDetailDataById(
|
||||
|
||||
let bookTaskDetail = this.bookTaskDetailService.GetBookTaskDetailDataById(
|
||||
task.bookTaskDetailId
|
||||
)
|
||||
if (bookTaskDetailRes.code == 0) {
|
||||
throw new Error(bookTaskDetailRes.message)
|
||||
}
|
||||
if (bookTaskDetailRes.data == null) {
|
||||
if (bookTaskDetail == null) {
|
||||
throw new Error('没有找到对应的分镜数据')
|
||||
}
|
||||
// 开始执行裁剪视频
|
||||
let bookTaskDetail = bookTaskDetailRes.data
|
||||
let startTime = bookTaskDetail.startTime
|
||||
let endTime = bookTaskDetail.endTime
|
||||
|
||||
let bookRes = this.bookService.GetBookDataById(task.bookId)
|
||||
if (bookRes.data == null) {
|
||||
throw new Error('没有找到对应的小说数据')
|
||||
}
|
||||
let book = bookRes.data
|
||||
let cur_res = await this.FrameDataToCutVideoData(bookTaskDetail);
|
||||
|
||||
if (startTime == null || endTime == null) {
|
||||
this.bookTaskService.UpdateBookTaskStatus(
|
||||
task.bookTaskId,
|
||||
BookTaskStatus.SPLIT_FAIL,
|
||||
'开始时间和结束时间不能为空'
|
||||
)
|
||||
throw new Error('开始时间和结束时间不能为空')
|
||||
}
|
||||
let outVideoFile = path.join(book.bookFolderPath, `data/frame/${bookTaskDetail.name}.mp4`)
|
||||
|
||||
let res = await this.ffmpegOptions.FfmpegCutVideo(
|
||||
startTime,
|
||||
endTime,
|
||||
book.oldVideoPath,
|
||||
outVideoFile
|
||||
)
|
||||
if (res.code == 0) {
|
||||
this.bookTaskService.UpdateBookTaskStatus(
|
||||
task.bookTaskId,
|
||||
BookTaskStatus.SPLIT_FAIL,
|
||||
res.message
|
||||
)
|
||||
throw new Error(res.message)
|
||||
}
|
||||
|
||||
// 视频裁剪完成,要将裁剪后的视频地址写入到数据库中
|
||||
this.bookTaskDetailService.UpdateBookTaskDetail(task.bookTaskDetailId, {
|
||||
videoPath: path.relative(define.project_path, outVideoFile)
|
||||
})
|
||||
// 小改小说批次的状态
|
||||
this.bookTaskService.UpdateBookTaskStatus(task.bookTaskId, BookTaskStatus.SPLIT_DONE)
|
||||
// 结束,分镜完毕,推送日志,返回成功
|
||||
await this.taskScheduler.AddLogToDB(
|
||||
task.bookId,
|
||||
book.type,
|
||||
`${task.name}_视频裁剪完成`,
|
||||
OtherData.DEFAULT,
|
||||
LoggerStatus.SUCCESS
|
||||
)
|
||||
return successMessage(null, `${task.name}_视频裁剪完成`, 'BasicReverse_CutVideoData')
|
||||
return successMessage(null, `${task.name}_视频裁剪完成`, "BasicReverse_CutVideoData");
|
||||
} catch (error) {
|
||||
await this.taskScheduler.AddLogToDB(
|
||||
task.bookId,
|
||||
task.type,
|
||||
error.message,
|
||||
OtherData.DEFAULT,
|
||||
LoggerStatus.FAIL
|
||||
)
|
||||
return errorMessage(
|
||||
'裁剪视频失败,错误信息如下: ' + error.message,
|
||||
'BasicReverse_CutVideoData'
|
||||
)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
@ -465,17 +475,16 @@ export class BasicReverse {
|
||||
async AddSplitAudioDataTask(bookId, bookTaskId = null) {
|
||||
try {
|
||||
await this.InitService()
|
||||
let bookRes = this.bookService.GetBookDataById(bookId)
|
||||
if (bookRes == null) {
|
||||
let book = this.bookService.GetBookDataById(bookId)
|
||||
if (book == null) {
|
||||
throw new Error('没有找到对应的小说数据')
|
||||
}
|
||||
let book = bookRes.data
|
||||
|
||||
let bookTaskRes
|
||||
if (bookTaskId != null) {
|
||||
bookTaskRes = this.bookTaskService.GetBookTaskData({ id: bookTaskId })
|
||||
bookTaskRes = await this.bookTaskService.GetBookTaskData({ id: bookTaskId })
|
||||
} else {
|
||||
bookTaskRes = this.bookTaskService.GetBookTaskData({
|
||||
bookTaskRes = await this.bookTaskService.GetBookTaskData({
|
||||
bookId: bookId,
|
||||
name: 'output_00001'
|
||||
})
|
||||
@ -532,10 +541,7 @@ export class BasicReverse {
|
||||
}
|
||||
return successMessage(null, `添加所有音频分离任务成功`, 'BasicReverse_AddSplitAudioDataTask')
|
||||
} catch (error) {
|
||||
return errorMessage(
|
||||
'添加音频分离任务失败,错误信息如下: ' + error.message,
|
||||
'BasicReverse_AddSplitAudioDataTask'
|
||||
)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
@ -551,19 +557,17 @@ export class BasicReverse {
|
||||
throw new Error('分镜任务,bookId, bookTaskId, bookTaskDetailId不能为空')
|
||||
}
|
||||
await this.InitService()
|
||||
let bookRes = this.bookService.GetBookDataById(task.bookId)
|
||||
if (bookRes.data == null) {
|
||||
let book = this.bookService.GetBookDataById(task.bookId)
|
||||
if (book == null) {
|
||||
throw new Error('没有找到对应的小说数据')
|
||||
}
|
||||
let book = bookRes.data
|
||||
|
||||
let bookTaskDetails = this.bookTaskDetailService.GetBookTaskDetailDataById(
|
||||
let bookTaskDetail = this.bookTaskDetailService.GetBookTaskDetailDataById(
|
||||
task.bookTaskDetailId
|
||||
)
|
||||
if (bookTaskDetails.data == null) {
|
||||
if (bookTaskDetail == null) {
|
||||
throw new Error('没有找到对应的分镜数据')
|
||||
}
|
||||
let bookTaskDetail = bookTaskDetails.data
|
||||
let videoPath = bookTaskDetail.videoPath
|
||||
let audioPath = path.join(book.bookFolderPath, `data/audio/${bookTaskDetail.name}.wav`)
|
||||
await CheckFolderExistsOrCreate(path.dirname(audioPath))
|
||||
@ -599,15 +603,7 @@ export class BasicReverse {
|
||||
'BasicReverse_SplitAudioData'
|
||||
)
|
||||
} catch (error) {
|
||||
let error_message = `分离音频失败,错误信息如下:${error.message}`
|
||||
await this.taskScheduler.AddLogToDB(
|
||||
task.bookId,
|
||||
task.type,
|
||||
error_message,
|
||||
OtherData.DEFAULT,
|
||||
LoggerStatus.FAIL
|
||||
)
|
||||
return errorMessage(error_message, 'BasicReverse_SplitAudioData')
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
@ -620,17 +616,16 @@ export class BasicReverse {
|
||||
try {
|
||||
// 开始添加任务
|
||||
await this.InitService()
|
||||
let bookRes = this.bookService.GetBookDataById(bookId)
|
||||
if (bookRes.data == null) {
|
||||
let book = this.bookService.GetBookDataById(bookId)
|
||||
if (book == null) {
|
||||
throw new Error('没有找到对应的小说数据')
|
||||
}
|
||||
|
||||
let book = bookRes.data
|
||||
let bookTaskRes
|
||||
if (bookTaskId != null) {
|
||||
bookTaskRes = this.bookTaskService.GetBookTaskData({ id: bookTaskId })
|
||||
bookTaskRes = await this.bookTaskService.GetBookTaskData({ id: bookTaskId })
|
||||
} else {
|
||||
bookTaskRes = this.bookTaskService.GetBookTaskData({
|
||||
bookTaskRes = await this.bookTaskService.GetBookTaskData({
|
||||
bookId: bookId,
|
||||
name: 'output_00001'
|
||||
})
|
||||
@ -671,13 +666,53 @@ export class BasicReverse {
|
||||
}
|
||||
return successMessage(null, '添加所有抽帧任务成功', 'BasicReverse_AddGetFrameTask')
|
||||
} catch (error) {
|
||||
return errorMessage(
|
||||
'添加抽帧任务失败,错误信息如下: ' + error.message,
|
||||
'BasicReverse_AddGetFrameTask'
|
||||
)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 对分镜人物进行抽帧
|
||||
* @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 执行的任务
|
||||
@ -689,67 +724,25 @@ export class BasicReverse {
|
||||
throw new Error('分镜任务,bookId, bookTaskId, bookTaskDetailId不能为空')
|
||||
}
|
||||
await this.InitService()
|
||||
let bookRes = this.bookService.GetBookDataById(task.bookId)
|
||||
if (bookRes.data == null) {
|
||||
let book = this.bookService.GetBookDataById(task.bookId)
|
||||
if (book == null) {
|
||||
throw new Error('没有找到对应的小说数据')
|
||||
}
|
||||
let book = bookRes.data
|
||||
|
||||
let bookTaskDetailRes = this.bookTaskDetailService.GetBookTaskDetailDataById(
|
||||
let bookTaskDetail = this.bookTaskDetailService.GetBookTaskDetailDataById(
|
||||
task.bookTaskDetailId
|
||||
)
|
||||
if (bookTaskDetailRes.data == null) {
|
||||
if (bookTaskDetail == null) {
|
||||
throw new Error('没有找到对应的分镜数据')
|
||||
}
|
||||
let bookTaskDetail = bookTaskDetailRes.data
|
||||
|
||||
let videoPath = bookTaskDetail.videoPath
|
||||
let outputFramePath = path.join(
|
||||
book.bookFolderPath,
|
||||
`data/tmp/input/${bookTaskDetail.name}.png`
|
||||
)
|
||||
let frameTime = (bookTaskDetail.endTime - bookTaskDetail.startTime) / 2
|
||||
let res = await this.FrameFunc(book, bookTaskDetail);
|
||||
|
||||
let res = await this.ffmpegOptions.FfmpegGetFrame(frameTime, videoPath, outputFramePath)
|
||||
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')
|
||||
return successMessage(null, res, 'BasicReverse_GetFrame')
|
||||
} catch (error) {
|
||||
let errorMessage = `抽帧失败,错误信息如下:${error.message}`
|
||||
await this.taskScheduler.AddLogToDB(
|
||||
task.bookId,
|
||||
task.type,
|
||||
errorMessage,
|
||||
OtherData.DEFAULT,
|
||||
LoggerStatus.FAIL
|
||||
)
|
||||
return errorMessage('抽帧失败,错误信息如下: ' + error.message, 'BasicReverse_GetFrame')
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region 提取字幕相关操作
|
||||
@ -769,9 +762,9 @@ export class BasicReverse {
|
||||
}
|
||||
let bookTask
|
||||
if (bookTaskId != null) {
|
||||
bookTaskId = this.bookTaskService.GetBookTaskData({ id: bookTaskId })
|
||||
bookTaskId = await this.bookTaskService.GetBookTaskData({ id: bookTaskId })
|
||||
} else {
|
||||
bookTask = this.bookTaskService.GetBookTaskData({
|
||||
bookTask = await this.bookTaskService.GetBookTaskData({
|
||||
bookId: bookId,
|
||||
name: 'output_00001'
|
||||
})
|
||||
@ -794,7 +787,7 @@ export class BasicReverse {
|
||||
const element = bookTaskDetails.data[i]
|
||||
let taskRes = await this.bookBackTaskListService.AddBookBackTask(
|
||||
bookId,
|
||||
book.type,
|
||||
BookBackTaskType.RECOGNIZE,
|
||||
TaskExecuteType.AUTO,
|
||||
bookTask.id,
|
||||
element.id
|
||||
@ -817,13 +810,61 @@ export class BasicReverse {
|
||||
'BasicReverse_AddExtractSubtitlesDataTask '
|
||||
)
|
||||
} catch (error) {
|
||||
return errorMessage(
|
||||
'添加提取字幕任务失败,错误信息如下: ' + error.message,
|
||||
'BasicReverse_AddExtractSubtitlesDataTask'
|
||||
)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
@ -837,75 +878,22 @@ export class BasicReverse {
|
||||
}
|
||||
await this.InitService()
|
||||
// 获取详细的小说分镜信息
|
||||
let bookTaskDetailRes = this.bookTaskDetailService.GetBookTaskDetailDataById(
|
||||
let bookTaskDetail = this.bookTaskDetailService.GetBookTaskDetailDataById(
|
||||
task.bookTaskDetailId
|
||||
)
|
||||
if (bookTaskDetailRes.data == null) {
|
||||
if (bookTaskDetail == null) {
|
||||
throw new Error('没有找到对应的分镜数据')
|
||||
}
|
||||
let bookTaskDetail = bookTaskDetailRes.data
|
||||
let txt = ''
|
||||
// 开始提取,调用本地的服务识别字幕
|
||||
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 outTxtPath = path.join(out_dir, `${bookTaskDetail.name}.txt`)
|
||||
txt = await fspromises.readFile(outTxtPath, 'utf-8')
|
||||
} else {
|
||||
// 使用网络服务
|
||||
|
||||
let book = this.bookService.GetBookDataById(task.bookId)
|
||||
if (book == null) {
|
||||
throw new Error('没有找到对应的小说数据')
|
||||
}
|
||||
|
||||
// 修改分镜数据的字幕
|
||||
this.bookTaskDetailService.UpdateBookTaskDetail(task.bookTaskDetailId, {
|
||||
word: txt,
|
||||
afterGpt: txt
|
||||
})
|
||||
let res = await this.GetCopywritingFunc(book, bookTaskDetail)
|
||||
return successMessage(null, res, 'BasicReverse_ExtractSubtitlesData')
|
||||
|
||||
// 提取字幕成功,推送日志
|
||||
await this.taskScheduler.AddLogToDB(
|
||||
bookId,
|
||||
book.type,
|
||||
`${task.name} 提取字幕成功`,
|
||||
OtherData.DEFAULT,
|
||||
LoggerStatus.SUCCESS
|
||||
)
|
||||
return successMessage(null, `${task.name} 提取字幕成功`, 'BasicReverse_ExtractSubtitlesData')
|
||||
} catch (error) {
|
||||
let errorMessage = `${task.name} 提取字幕失败,错误信息如下:${error.message}`
|
||||
await this.taskScheduler.AddLogToDB(
|
||||
bookId,
|
||||
book.type,
|
||||
errorMessage,
|
||||
OtherData.DEFAULT,
|
||||
LoggerStatus.FAIL
|
||||
)
|
||||
|
||||
return errorMessage(errorMessage, 'BasicReverse_ExtractSubtitlesData')
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
346
src/main/Service/Book/bookImage.ts
Normal 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 操作的类型(支持BOOK,BOOKTASK两种)
|
||||
*/
|
||||
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('高清倍率只能是2,3,4')
|
||||
}
|
||||
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 需要操作的的ID,可以是整个小说的ID,也可以单个批次的ID
|
||||
* @param fileSize 单个图片的文件大小
|
||||
* @param operateBookType 操作的类型(支持BOOK,BOOKTASK两种)
|
||||
*/
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
78
src/main/Service/Book/bookTask.ts
Normal 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");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
235
src/main/Service/Book/bookVideo.ts
Normal 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");
|
||||
}
|
||||
}
|
||||
}
|
||||
97
src/main/Service/Book/imageStyle.ts
Normal 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
@ -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
|
||||
}
|
||||
289
src/main/Service/MJ/mjApi.ts
Normal 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
|
||||
100
src/main/Service/MJ/mjReverse.ts
Normal 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
@ -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")
|
||||
}
|
||||
}
|
||||
}
|
||||
1173
src/main/Service/Translate/Translate.ts
Normal file
226
src/main/Service/Translate/TranslateService.ts
Normal 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")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,11 +1,11 @@
|
||||
import path from 'path'
|
||||
import { TaskScheduler } from './taskScheduler'
|
||||
import { errorMessage, successMessage } from '../generalTools'
|
||||
import { CheckFileOrDirExist, CheckFolderExistsOrCreate } from '../../define/Tools/file'
|
||||
import { errorMessage, successMessage } from '../Public/generalTools'
|
||||
import { CheckFileOrDirExist, CheckFolderExistsOrCreate, CopyFileOrFolder, DeleteFolderAllFile } from '../../define/Tools/file'
|
||||
import { MillisecondsToTimeString } from '../../define/Tools/time'
|
||||
import Ffmpeg from 'fluent-ffmpeg'
|
||||
import { SetFfmpegPath } from '../setting/ffmpegSetting'
|
||||
import fs from 'fs'
|
||||
import { GeneralResponse } from '../../model/generalResponse'
|
||||
const fspromises = fs.promises
|
||||
|
||||
SetFfmpegPath()
|
||||
@ -14,8 +14,11 @@ SetFfmpegPath()
|
||||
* FFmpeg 封装的一些操作
|
||||
*/
|
||||
export class FfmpegOptions {
|
||||
ecode: string // 编码的方式
|
||||
dcode: string // 解码的方式
|
||||
|
||||
|
||||
constructor() {
|
||||
this.taskScheduler = new TaskScheduler()
|
||||
}
|
||||
|
||||
InitCodec() {
|
||||
@ -33,6 +36,64 @@ export class FfmpegOptions {
|
||||
} else if (global.gpu.type === 'AMD') {
|
||||
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 视频文件地址
|
||||
* @returns
|
||||
*/
|
||||
async FfmpegGetVideoSize(videoPath) {
|
||||
async FfmpegGetVideoSize(videoPath: string): Promise<GeneralResponse.ErrorItem | GeneralResponse.SuccessItem> {
|
||||
try {
|
||||
let videoIsExist = await CheckFileOrDirExist(videoPath)
|
||||
if (videoIsExist == false) {
|
||||
@ -267,6 +328,13 @@ export class FfmpegOptions {
|
||||
throw new Error(frameRes.message)
|
||||
}
|
||||
let outImagePaths = []
|
||||
if(await CheckFileOrDirExist(outImagePath) == false){
|
||||
return successMessage(
|
||||
outImagePaths,
|
||||
'获取指定位置的帧和裁剪成功',
|
||||
'WatermarkAndSubtitle_FfmpegGetVideoFramdAndClip'
|
||||
)
|
||||
}
|
||||
// 这边可以会裁剪多个,所以需要循环
|
||||
for (let i = 0; i < clipRanges.length; i++) {
|
||||
const element = clipRanges[i]
|
||||