1. 添加软件高清倍率设置,实现自定义高清倍率(支持两倍、三倍和四倍,默认是两倍)
2. (聚合推文)新增导出草稿。可以依赖做好的草稿文件,只替换图片文件
3. (聚合推文)新增批量生成草稿
4. (聚合推文)修改小说批次任务进行详细界面操作为双击
This commit is contained in:
lq1405 2024-10-23 22:59:29 +08:00
parent f4d042f699
commit 0c5988ed41
22 changed files with 1438 additions and 1227 deletions

4
package-lock.json generated
View File

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

View File

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

Binary file not shown.

View File

@ -12,6 +12,7 @@ export class BookModel extends Realm.Object<BookModel> {
oldVideoPath: string | null
srtPath: string | null
audioPath: string | null
draftDepend?: string // 草稿依赖
draftSrtStyle: string | null // 草稿字幕样式
backgroundMusic: string | null // 背景音乐ID
friendlyReminder: string | null // 友情提示
@ -38,6 +39,7 @@ export class BookModel extends Realm.Object<BookModel> {
oldVideoPath: 'string?',
srtPath: 'string?',
audioPath: 'string?',
draftDepend: "string?",
draftSrtStyle: 'string?',
backgroundMusic: 'string?',
friendlyReminder: 'string?',

View File

@ -43,6 +43,7 @@ export class BookTaskModel extends Realm.Object<BookTaskModel> {
name: string
generateVideoPath: string | null
srtPath: string | null
draftDepend?: string // 草稿依赖
audioPath: string | null
draftSrtStyle: string | null // 草稿字幕样式
backgroundMusic: string | null // 背景音乐ID
@ -72,6 +73,7 @@ export class BookTaskModel extends Realm.Object<BookTaskModel> {
name: 'string',
generateVideoPath: 'string?',
srtPath: 'string?',
draftDepend: "string?",
audioPath: 'string?',
draftSrtStyle: 'string?',
backgroundMusic: 'string?',

View File

@ -176,6 +176,27 @@ const migration = (oldRealm: Realm, newRealm: Realm) => {
newBookTask[i].messageName = ''
}
}
// if (oldRealm.schemaVersion < 25) {
// const oldBookTask = oldRealm.objects('BookTaskDetail')
// const newBookTask = newRealm.objects('BookTaskDetail')
// for (let i = 0; i < oldBookTask.length; i++) {
// newBookTask[i].draftDepend = undefined
// }
// }
if (oldRealm.schemaVersion < 26) {
const oldBookTask = oldRealm.objects('BookTask')
const newBookTask = newRealm.objects('BookTask')
for (let i = 0; i < oldBookTask.length; i++) {
newBookTask[i].draftDepend = ''
}
}
if (oldRealm.schemaVersion < 27) {
const oldBookTask = oldRealm.objects('Book')
const newBookTask = newRealm.objects('Book')
for (let i = 0; i < oldBookTask.length; i++) {
newBookTask[i].draftDepend = ''
}
}
}
export class BaseRealmService extends BaseService {
@ -217,7 +238,7 @@ export class BaseRealmService extends BaseService {
BookTaskDetailModel
],
path: this.dbpath,
schemaVersion: 24,
schemaVersion: 27,
migration: migration
}
this.realm = await Realm.open(config)

View File

@ -232,7 +232,8 @@ export enum OperateBookType {
BOOK = 'book', // 这个小说的所有批次
BOOKTASK = 'bookTask', // 整个小说批次分镜合并
BOOKTASKDETAIL = 'bookTaskDetail', // 单个分镜合并
UNDERBOOKTASK = 'underBookTask' // 执行小说批次任务的指定ID以及后面的所有的东西
UNDERBOOKTASK = 'underBookTask', // 执行小说批次任务的指定ID以及后面的所有的东西
ASSIGNBOOKTASK = 'assignBookTask' // 指定小说批次任务
}
export enum CopyImageType {

View File

@ -241,14 +241,14 @@ export function BookIpc() {
// 高清图片前检查,主要是检查图片大小
ipcMain.handle(
DEFINE_STRING.BOOK.CHECK_IMAGE_FILE_SIZE,
async (event, id, fileSize, operateBookType) =>
async (event, id: string | string[], fileSize: number, operateBookType: 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)
async (event, id: string | string[], scale: number, operateBookType: OperateBookType) => await bookImage.HDImage(id, scale, operateBookType)
)
// 删除所有的生成图片
@ -310,7 +310,7 @@ export function BookIpc() {
// 将小说任务添加到剪映草稿
ipcMain.handle(
DEFINE_STRING.BOOK.ADD_JIANYING_DRAFT,
async (event, id, operateBookType) => await bookVideo.AddJianyingDraft(id, operateBookType)
async (event, id: string | string[], operateBookType: OperateBookType) => await bookVideo.AddJianyingDraft(id, operateBookType)
)
//#endregion
}

View File

@ -1,335 +1,370 @@
import path from "path";
import { define } from "../../define/define";
import { Tools } from "../tools";
import { DEFINE_STRING } from "../../define/define_string";
import { get, has } from "lodash";
const util = require('util');
const { spawn, exec } = require('child_process');
const execAsync = util.promisify(exec);
const fspromises = require("fs").promises;
import path from 'path'
import { define } from '../../define/define'
import { Tools } from '../tools'
import { DEFINE_STRING } from '../../define/define_string'
import { get, has } from 'lodash'
const util = require('util')
const { spawn, exec } = require('child_process')
const execAsync = util.promisify(exec)
const fspromises = require('fs').promises
export class PublicMethod {
constructor(global) {
this.global = global;
this.tools = new Tools();
constructor(global) {
this.global = global
this.tools = new Tools()
}
/**
* 修改config.json文件中的指定的属性
* @param {*} value 0: 要修改的数据 1: 要修改的属性 2: 是否需要解析
* @returns
*/
async SaveConfigJsonProperty(value) {
try {
let data = value[0]
let property = value[1]
let parse = value[2]
if (parse) {
data = JSON.parse(value[0])
}
let json_path = path.join(global.config.project_path, 'scripts/config.json')
// 判断文件是不是存在
let isExit = await this.tools.checkExists(json_path)
let json_data = {}
if (!isExit) {
const dirPath = path.dirname(json_path)
await fspromises.mkdir(dirPath, { recursive: true })
await fspromises.writeFile(json_path, '{}')
} else {
const o_data = await fspromises.readFile(json_path, 'utf8')
// 将读取的 JSON 字符串转换为 JavaScript 对象
let obj = JSON.parse(o_data)
json_data = obj
}
json_data[property] = data
await fspromises.writeFile(json_path, JSON.stringify(json_data))
return {
code: 1
}
} catch (error) {
throw error
}
}
/**
* 修改config.json文件中的指定的属性
* @param {*} value 0: 要修改的数据 1: 要修改的属性 2: 是否需要解析
* @returns
*/
async SaveConfigJsonProperty(value) {
/**
* 返回当前项目的config.json文件中的指定的属性信息若是没有传入属性则返回所有的信息
* @param {Array} value 0 要获取的属性 1 返回的默认值
* @param {Boolean} ckeck 是否需要校验属性不存在
* @returns
*/
async GetConfigJson(value, ckeck = true) {
try {
value = JSON.parse(value)
let srt_config_path = path.join(global.config.project_path, 'scripts/config.json')
let data = await this.tools.getJsonFilePropertyValue(
srt_config_path,
value[0],
value[1],
ckeck
)
return {
code: 1,
data: data
}
} catch (error) {
throw error
}
}
/**
* 修改生成图片的任务队列数据
* @param {传入的要修改的数据数组} value
*/
async ModifyImageTaskList(value) {
this.global.fileQueue.enqueue(
async () => {
try {
let data = value[0];
let property = value[1];
let parse = value[2];
if (parse) {
data = JSON.parse(value[0])
}
let json_path = path.join(global.config.project_path, "scripts/config.json");
// 判断文件是不是存在
let isExit = await this.tools.checkExists(json_path);
let json_data = {};
if (!isExit) {
const dirPath = path.dirname(json_path);
await fspromises.mkdir(dirPath, { recursive: true });
await fspromises.writeFile(json_path, '{}');
}
else {
const o_data = await fspromises.readFile(json_path, 'utf8');
// 将读取的 JSON 字符串转换为 JavaScript 对象
let obj = JSON.parse(o_data);
json_data = obj;
}
json_data[property] = data;
await fspromises.writeFile(json_path, JSON.stringify(json_data));
return {
code: 1
}
let task_list_path = path.join(this.global.config.project_path, 'scripts/task_list.json')
let isE = await this.tools.checkExists(task_list_path)
if (!isE) {
throw new Error('任务队列文件不存在。请先添加 批次任务')
}
let task_list_json = JSON.parse(await fspromises.readFile(task_list_path, 'utf-8'))
// 循环循环数据。修改
for (let i = 0; i < value.length; i++) {
const element = value[i]
let index = task_list_json.task_list.findIndex((item) => item.id == element.id)
task_list_json.task_list[index] = element
}
await fspromises.writeFile(task_list_path, JSON.stringify(task_list_json))
} catch (error) {
throw error;
throw error
}
}
/**
* 返回当前项目的config.json文件中的指定的属性信息若是没有传入属性则返回所有的信息
* @param {Array} value 0 要获取的属性 1 返回的默认值
* @param {Boolean} ckeck 是否需要校验属性不存在
* @returns
*/
async GetConfigJson(value, ckeck = true) {
try {
value = JSON.parse(value)
let srt_config_path = path.join(global.config.project_path, "scripts/config.json");
let data = await this.tools.getJsonFilePropertyValue(srt_config_path, value[0], value[1], ckeck);
return {
code: 1,
data: data
}
} catch (error) {
throw error;
},
'modifyFile',
'modifyFile',
'task_list'
)
this.global.fileQueue.setSubBatchCompletionCallback(
'modifyFile',
'task_list',
async (failedTasks) => {
// 报错
if (failedTasks.length > 0) {
let message = ''
failedTasks.forEach(({ taskId, error }) => {
message += `${taskId}-, \n 错误信息: ${error}` + '\n'
})
throw new Error(message)
// this.global.newWindow[0].win.webContents.send(DEFINE_STRING.SHOW_MESSAGE_DIALOG, {
// code: 0,
// message: message
// })
}
// else {
// this.global.newWindow[0].win.webContents.send(DEFINE_STRING.SHOW_MESSAGE_DIALOG, {
// code: 1,
// message: "修改成功"
// })
// }
return true
}
)
}
/**
* 高清指定的文件夹
* @param {要高清的文件夹} folder
*/
async ImproveFolder(folder) {
try {
let bakPath = path.join(this.global.config.project_path, 'tmp/bak')
let oldInput = path.join(this.global.config.project_path, 'tmp/' + folder)
let newInput = path.join(this.global.config.project_path, 'tmp/bak/' + folder)
// 创建文件夹
let existFolder = await this.tools.checkExists(bakPath)
if (!existFolder) {
await fspromises.mkdir(bakPath, { recursive: true })
}
let isExistNewFolder = await this.tools.checkExists(newInput)
if (isExistNewFolder) {
await fspromises.rm(newInput, { recursive: true, force: true })
}
// 备份文件
await fspromises.rename(oldInput, newInput)
//创建同名的文件,用作输出
await fspromises.mkdir(oldInput, { recursive: true })
// 开始高清
let command = `"${path.join(
define.package_path,
'Improve/rnv.exe'
)}" -i "${newInput}" -o "${oldInput}" -s ${this.global.config.hdScale ?? 2}`
let out = await execAsync(command, { maxBuffer: 1024 * 1024 * 10, encoding: 'utf-8' })
console.log(out)
await this.ModifyTaskStatus('out_folder', folder, 'video_improvied')
this.global.newWindow[0].win.webContents.send(DEFINE_STRING.VIDEO_GENERATE_STATUS_REFRESH, {
out_folder: folder,
status: 'video_improvied'
})
} catch (error) {
await this.ModifyTaskStatus('out_folder', folder, 'video_improvie_error')
this.global.newWindow[0].win.webContents.send(DEFINE_STRING.VIDEO_GENERATE_STATUS_REFRESH, {
out_folder: folder,
status: 'video_improvie_error'
})
throw error
}
}
/**
* 修改生成图片的任务队列数据
* @param {传入的要修改的数据数组} value
*/
async ModifyImageTaskList(value) {
this.global.fileQueue.enqueue(async () => {
try {
let task_list_path = path.join(this.global.config.project_path, "scripts/task_list.json");
let isE = await this.tools.checkExists(task_list_path);
if (!isE) {
throw new Error("任务队列文件不存在。请先添加 批次任务");
}
let task_list_json = JSON.parse(await fspromises.readFile(task_list_path, "utf-8"));
// 循环循环数据。修改
for (let i = 0; i < value.length; i++) {
const element = value[i];
let index = task_list_json.task_list.findIndex(item => item.id == element.id);
task_list_json.task_list[index] = element;
}
await fspromises.writeFile(task_list_path, JSON.stringify(task_list_json));
} catch (error) {
throw error;
}
}, "modifyFile", "modifyFile", "task_list")
this.global.fileQueue.setSubBatchCompletionCallback("modifyFile", "task_list", async (failedTasks) => {
// 报错
if (failedTasks.length > 0) {
let message = "";
failedTasks.forEach(({ taskId, error }) => {
message += `${taskId}-, \n 错误信息: ${error}` + '\n';
});
throw new Error(message);
// this.global.newWindow[0].win.webContents.send(DEFINE_STRING.SHOW_MESSAGE_DIALOG, {
// code: 0,
// message: message
// })
}
// else {
// this.global.newWindow[0].win.webContents.send(DEFINE_STRING.SHOW_MESSAGE_DIALOG, {
// code: 1,
// message: "修改成功"
// })
// }
return true;
})
}
/**
* 生成SD相对的JSON文件删除反推的txt文件
*/
async AddWebuiJson() {
try {
// 读取所有txt文件
let txtfile = await this.tools.getFilesWithExtensions(
path.join(this.global.config.project_path, 'tmp/input_crop'),
'.txt'
)
let image = await this.tools.getFilesWithExtensions(
path.join(this.global.config.project_path, 'tmp/input_crop'),
'.png'
)
let promptJson = await this.tools.getFilesWithExtensions(
path.join(this.global.config.project_path, 'tmp/input_crop'),
'.json'
)
/**
* 高清指定的文件夹
* @param {要高清的文件夹} folder
*/
async ImproveFolder(folder) {
try {
let bakPath = path.join(this.global.config.project_path, "tmp/bak");
let oldInput = path.join(this.global.config.project_path, "tmp/" + folder);
let newInput = path.join(this.global.config.project_path, "tmp/bak/" + folder)
// 创建文件夹
let existFolder = await this.tools.checkExists(bakPath);
if (!existFolder) {
await fspromises.mkdir(bakPath, { recursive: true });
}
let isExistNewFolder = await this.tools.checkExists(newInput);
if (isExistNewFolder) {
await fspromises.rm(newInput, { recursive: true, force: true });
}
// 备份文件
await fspromises.rename(oldInput, newInput);
//创建同名的文件,用作输出
await fspromises.mkdir(oldInput, { recursive: true });
// 开始高清
let command = `"${path.join(define.package_path, "Improve/rnv.exe")}" -i "${newInput}" -o "${oldInput}"`;
let out = await execAsync(command, { maxBuffer: 1024 * 1024 * 10, encoding: 'utf-8' });
console.log(out);
await this.ModifyTaskStatus('out_folder', folder, "video_improvied");
this.global.newWindow[0].win.webContents.send(DEFINE_STRING.VIDEO_GENERATE_STATUS_REFRESH, {
out_folder: folder,
status: "video_improvied"
})
} catch (error) {
await this.ModifyTaskStatus('out_folder', folder, "video_improvie_error");
this.global.newWindow[0].win.webContents.send(DEFINE_STRING.VIDEO_GENERATE_STATUS_REFRESH, {
out_folder: folder,
status: "video_improvie_error"
})
throw error;
// json 已经存在,不做后续处理
if (image.length == promptJson.length) {
return {
code: 1
}
}
}
/**
* 生成SD相对的JSON文件删除反推的txt文件
*/
async AddWebuiJson() {
try {
// 读取所有txt文件
let txtfile = await this.tools.getFilesWithExtensions(path.join(this.global.config.project_path, 'tmp/input_crop'), '.txt');
let image = await this.tools.getFilesWithExtensions(path.join(this.global.config.project_path, 'tmp/input_crop'), '.png');
let promptJson = await this.tools.getFilesWithExtensions(path.join(this.global.config.project_path, 'tmp/input_crop'), '.json');
if (txtfile.length != image.length) {
throw new Error('关键词文件和图片数量对不上,请检查!!')
}
let sd_config = JSON.parse(await fspromises.readFile(define.sd_setting, 'utf-8'))
// json 已经存在,不做后续处理
if (image.length == promptJson.length) {
return {
code: 1,
}
}
if (txtfile.length != image.length) {
throw new Error("关键词文件和图片数量对不上,请检查!!")
}
let sd_config = JSON.parse(await fspromises.readFile(define.sd_setting, 'utf-8'));
for (let i = 0; i < image.length; i++) {
const element = image[i];
let txtpath = element.split('.png')[0] + '.txt';
let prompt = await fspromises.readFile(txtpath, 'utf-8');
// console.log(txtpath)
let obj = {}
obj.model = sd_config.setting.type;
obj.api = sd_config.setting.webui_api_url + 'sdapi/v1/img2img';
obj.webui_config = {
sampler_name: sd_config.webui.sampler_name,
prompt: prompt + ',' + sd_config.webui.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: element,
}
obj.adetailer = sd_config.webui.adetailer;
// 写入
await fspromises.writeFile(element + '.json', JSON.stringify(obj));
// 删除对应的txt文件
await fspromises.unlink(txtpath);
}
return {
code: 1,
}
} catch (error) {
throw error;
for (let i = 0; i < image.length; i++) {
const element = image[i]
let txtpath = element.split('.png')[0] + '.txt'
let prompt = await fspromises.readFile(txtpath, 'utf-8')
// console.log(txtpath)
let obj = {}
obj.model = sd_config.setting.type
obj.api = sd_config.setting.webui_api_url + 'sdapi/v1/img2img'
obj.webui_config = {
sampler_name: sd_config.webui.sampler_name,
prompt: prompt + ',' + sd_config.webui.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: element
}
}
obj.adetailer = sd_config.webui.adetailer
/**
* 获取当前项目的生图任务列表
*/
async GetImageTask() {
try {
let json_path = path.join(this.global.config.project_path, "scripts/task_list.json");
let isExit = await this.tools.checkExists(json_path);
if (!isExit) {
return {
code: 1,
data: null
}
}
let json_data = JSON.parse(await fspromises.readFile(json_path));
return {
code: 1,
data: json_data
}
} catch (error) {
return {
code: 0,
message: error
}
// 写入
await fspromises.writeFile(element + '.json', JSON.stringify(obj))
// 删除对应的txt文件
await fspromises.unlink(txtpath)
}
return {
code: 1
}
} catch (error) {
throw error
}
}
/**
* 获取当前项目的生图任务列表
*/
async GetImageTask() {
try {
let json_path = path.join(this.global.config.project_path, 'scripts/task_list.json')
let isExit = await this.tools.checkExists(json_path)
if (!isExit) {
return {
code: 1,
data: null
}
}
let json_data = JSON.parse(await fspromises.readFile(json_path))
return {
code: 1,
data: json_data
}
} catch (error) {
return {
code: 0,
message: error
}
}
}
/**
* 修改任务的状态
* @param {查找的类型 id out_folder} type
* @param {查找的类型} id
* @param {新的状态} newStatus
*/
async ModifyTaskStatus(type, id, newStatus) {
this.global.fileQueue.enqueue(async () => {
try {
// 将修改数据写入到一个并发为 1 的队列中
let tmp_task = await fspromises.readFile(path.join(this.global.config.project_path, 'scripts/task_list.json'), 'utf-8');
console.log(tmp_task)
let task = JSON.parse(tmp_task);
if (type == "id") {
let index = task.task_list.findIndex(item => item.id == id);
if (index < 0) {
throw new Error("传入的数据未找到");
} else {
task.task_list[index].status = newStatus;
}
} else if (type == "out_folder") {
let index = task.task_list.findIndex(item => item.out_folder == id);
if (index < 0) {
throw new Error("传入的数据未找到");
} else {
task.task_list[index].status = newStatus;
}
} else {
throw new Error("输入类型错误")
}
await fspromises.writeFile(path.join(this.global.config.project_path, 'scripts/task_list.json'), JSON.stringify(task));
} catch (error) {
throw error;
}
}, "modifyFile", "modifyFile", "task_list")
this.global.fileQueue.setSubBatchCompletionCallback("modifyFile", "task_list", async (failedTasks) => {
// 报错
if (failedTasks.length > 0) {
let message = "";
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
})
}
})
}
/**
* 获取指定文件夹下面特定条件的文件夹
* @param {指定的文件夹目录} parentFolder
* @param {查询条件 start end include} condition
* @param {查询的值} value
* @returns
*/
async getSubFolderList(parentFolder, condition, value) {
/**
* 修改任务的状态
* @param {查找的类型 id out_folder} type
* @param {查找的类型} id
* @param {新的状态} newStatus
*/
async ModifyTaskStatus(type, id, newStatus) {
this.global.fileQueue.enqueue(
async () => {
try {
// console.log(value);
let folders = await fspromises.readdir(parentFolder, { withFileTypes: true });
folders = folders.filter(item => item.isDirectory())
.map(item => item.name)
if (condition == "start") {
folders = folders.filter(item => item.startsWith(value));
} else if (condition == "end") {
folders = folders.filter(item => item.endsWith(value));
} else if (condition == "include") {
//包含过滤
folders = folders.filter(item => item.includes(value));
// 将修改数据写入到一个并发为 1 的队列中
let tmp_task = await fspromises.readFile(
path.join(this.global.config.project_path, 'scripts/task_list.json'),
'utf-8'
)
console.log(tmp_task)
let task = JSON.parse(tmp_task)
if (type == 'id') {
let index = task.task_list.findIndex((item) => item.id == id)
if (index < 0) {
throw new Error('传入的数据未找到')
} else {
throw new Error("条件参数错误");
task.task_list[index].status = newStatus
}
return folders;
} else if (type == 'out_folder') {
let index = task.task_list.findIndex((item) => item.out_folder == id)
if (index < 0) {
throw new Error('传入的数据未找到')
} else {
task.task_list[index].status = newStatus
}
} else {
throw new Error('输入类型错误')
}
await fspromises.writeFile(
path.join(this.global.config.project_path, 'scripts/task_list.json'),
JSON.stringify(task)
)
} catch (error) {
throw error;
throw error
}
},
'modifyFile',
'modifyFile',
'task_list'
)
this.global.fileQueue.setSubBatchCompletionCallback(
'modifyFile',
'task_list',
async (failedTasks) => {
// 报错
if (failedTasks.length > 0) {
let message = ''
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
})
}
}
)
}
/**
* 获取指定文件夹下面特定条件的文件夹
* @param {指定的文件夹目录} parentFolder
* @param {查询条件 start end include} condition
* @param {查询的值} value
* @returns
*/
async getSubFolderList(parentFolder, condition, value) {
try {
// console.log(value);
let folders = await fspromises.readdir(parentFolder, { withFileTypes: true })
folders = folders.filter((item) => item.isDirectory()).map((item) => item.name)
if (condition == 'start') {
folders = folders.filter((item) => item.startsWith(value))
} else if (condition == 'end') {
folders = folders.filter((item) => item.endsWith(value))
} else if (condition == 'include') {
//包含过滤
folders = folders.filter((item) => item.includes(value))
} else {
throw new Error('条件参数错误')
}
return folders
} catch (error) {
throw error
}
}
}
}

View File

@ -101,22 +101,30 @@ export class BookImage {
* @param scale
* @param operateBookType BOOKBOOKTASK两种
*/
async HDImage(id: string, scale: number, operateBookType: OperateBookType): Promise<GeneralResponse.ErrorItem | GeneralResponse.SuccessItem> {
async HDImage(id: string | string[], scale: number, operateBookType: OperateBookType): Promise<GeneralResponse.ErrorItem | GeneralResponse.SuccessItem> {
try {
if (scale <= 0 || scale > 4) {
throw new Error('高清倍率只能是234')
}
let bookTasks = undefined as Book.SelectBookTask[]
let bookTasks = [] as Book.SelectBookTask[]
if (operateBookType == OperateBookType.BOOK) {
// 获取所有的小说批次任务
bookTasks = (await this.bookServiceBasic.GetBookTaskData({
bookId: id
bookId: id as string
})).bookTasks
} else if (operateBookType == OperateBookType.BOOKTASK) {
// 获取当前的小说批次任务
bookTasks = (await this.bookServiceBasic.GetBookTaskData({
id: id
id: id as string
})).bookTasks
} else if (operateBookType == OperateBookType.ASSIGNBOOKTASK) {
for (let i = 0; i < id.length; i++) {
const element = id[i];
let tmpBookTask = (await this.bookServiceBasic.GetBookTaskData({
id: element
})).bookTasks
bookTasks.push(...tmpBookTask)
}
} else {
throw new Error("不支持的操作类型,请检查")
}
@ -195,20 +203,29 @@ export class BookImage {
* @param fileSize
* @param operateBookType BOOKBOOKTASK两种
*/
async CheckImageFileSize(id: string, fileSize: number, operateBookType: OperateBookType) {
async CheckImageFileSize(id: string | string[], fileSize: number, operateBookType: OperateBookType) {
try {
let bookTasks = undefined as Book.SelectBookTask[]
let bookTasks = [] as Book.SelectBookTask[]
if (operateBookType == OperateBookType.BOOK) {
// 获取所有的小说批次任务
bookTasks = (await this.bookServiceBasic.GetBookTaskData({
bookId: id
bookId: id as string
})).bookTasks
} else if (operateBookType == OperateBookType.BOOKTASK) {
// 获取当前的小说批次任务
bookTasks = (await this.bookServiceBasic.GetBookTaskData({
id: id
id: id as string
})).bookTasks
} else {
} else if (operateBookType == OperateBookType.ASSIGNBOOKTASK) {
for (let i = 0; i < id.length; i++) {
const element = id[i];
let tempBookTask = (await this.bookServiceBasic.GetBookTaskData({
id: element
})).bookTasks
bookTasks.push(...tempBookTask)
}
}
else {
throw new Error("不支持的操作类型,请检查")
}

View File

@ -1,38 +1,24 @@
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'
import { BookServiceBasic } from "../ServiceBasic/bookServiceBasic";
import { isEmpty } from "lodash";
import JianyingService from "../jianying/jianyingService";
export class BookVideo {
bookService: BookService
bookTaskService: BookTaskService
setting: Setting
bookTaskDetailService: BookTaskDetailService
bookServiceBasic: BookServiceBasic
jianyingService: JianyingService
constructor() {
this.setting = new Setting(global)
this.bookServiceBasic = new BookServiceBasic()
}
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()
}
this.jianyingService = new JianyingService()
}
@ -44,23 +30,22 @@ export class BookVideo {
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);
book = await this.bookServiceBasic.GetBookDataById(id);
if (book == null) {
throw new Error('未找到对应的小说')
}
bookTasks = this.bookTaskService.GetBookTaskData({
bookTasks = (await this.bookServiceBasic.GetBookTaskData({
bookId: id,
}).data;
})).bookTasks;
} else if (operateBookType == OperateBookType.BOOKTASK) {
let bookTask = this.bookTaskService.GetBookTaskDataById(id);
let bookTask = await this.bookServiceBasic.GetBookTaskDataById(id);
if (bookTask == null) {
throw new Error('未找到对应的小说任务')
}
book = this.bookService.GetBookDataById(bookTask.bookId);
book = await this.bookServiceBasic.GetBookDataById(bookTask.bookId);
if (book == null) {
throw new Error('未找到对应的小说')
}
@ -117,9 +102,9 @@ export class BookVideo {
await CheckFolderExistsOrCreate(path.dirname(configPath));
// 开始配置
let bookTaskDetail = this.bookTaskDetailService.GetBookTaskData({
let bookTaskDetail = await this.bookServiceBasic.GetBookTaskDetailData({
bookTaskId: bookTask.id
}).data;
});
let configData = {
srt_time_information: [],
@ -144,14 +129,7 @@ export class BookVideo {
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
}
}),
subValue: element.subValue,
character_tags: [],
gpt_prompt: element.gptPrompt,
mjMessage: element.mjMessage,
@ -179,28 +157,29 @@ export class BookVideo {
* @param operateBookType
* @returns
*/
async AddJianyingDraft(id: string, operateBookType: OperateBookType): Promise<GeneralResponse.ErrorItem | GeneralResponse.SuccessItem> {
async AddJianyingDraft(id: string | 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("没有找到对应的小说数据,请检查");
let bookTasks = [] as Book.SelectBookTask[]
if (operateBookType == OperateBookType.ASSIGNBOOKTASK) {
if (id.length <= 0) {
throw new Error("没有找到小说批次任务,请检查");
}
for (let i = 0; i < id.length; i++) {
const element = id[i];
let tempBookTask = await this.bookServiceBasic.GetBookTaskDataById(element as string);
bookTasks.push(tempBookTask)
book = await this.bookServiceBasic.GetBookDataById(tempBookTask.bookId);
}
bookTasks = this.bookTaskService.GetBookTaskData({
bookId: id
}).data.bookTasks
} else if (operateBookType == OperateBookType.BOOKTASK) {
// 直接获取对应的数据
let tempBookTask = this.bookTaskService.GetBookTaskDataById(id);
let tempBookTask = await this.bookServiceBasic.GetBookTaskDataById(id as string);
if (tempBookTask == null) {
throw new Error("没有找到小说批次任务,请检查");
}
bookTasks = [tempBookTask]
book = this.bookService.GetBookDataById(tempBookTask.bookId);
book = await this.bookServiceBasic.GetBookDataById(tempBookTask.bookId);
if (book == null) {
throw new Error
}
@ -213,26 +192,38 @@ export class BookVideo {
}
// 判断是不是生成单个,每次生成都要修改一下配置文件
// 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)
// 判断是不是又依赖的草稿,又依赖的草稿,对于依赖的草稿进行操作
if ((!isEmpty(element.draftDepend) && operateBookType != OperateBookType.ASSIGNBOOKTASK) || (operateBookType == OperateBookType.ASSIGNBOOKTASK && !isEmpty(book.draftDepend))) {
let draft_name = `${book.name}_${element.name}`;
if (draft_name == element.draftDepend) {
throw new Error("生成的草稿名称和依赖的草稿名称一样,请检查");
}
await this.jianyingService.GenerateDraftFromDepend(
operateBookType == OperateBookType.ASSIGNBOOKTASK ? book.draftDepend : element.draftDepend,
element.imageFolder, draft_name);
result.push(draft_name);
} else {
await this.GenerateConfigFile(book, element);
// 数据处理完毕,开始输出
let clipDraft = new ClipDraft(global, [element.name, {
srt_path: operateBookType == OperateBookType.ASSIGNBOOKTASK ? book.srtPath : element.srtPath,
audio_path: operateBookType == OperateBookType.ASSIGNBOOKTASK ? book.audioPath : element.audioPath,
draft_srt_style: operateBookType == OperateBookType.ASSIGNBOOKTASK ? (book.draftSrtStyle ? book.draftSrtStyle : '0') : (element.draftSrtStyle ? element.draftSrtStyle : "0"),
background_music: operateBookType == OperateBookType.ASSIGNBOOKTASK ? book.backgroundMusic : element.backgroundMusic,
friendly_reminder: operateBookType == OperateBookType.ASSIGNBOOKTASK ? (book.friendlyReminder ? book.bookFolderPath : '0') : (element.friendlyReminder ? element.friendlyReminder : "0"),
}])
let res = await clipDraft.addDraft();
if (res.code == 0) {
throw new Error(res.message)
}
result.push(res.draft_name);
}
result.push(res.draft_name);
}
return successMessage(result, `${result.join('\n')} ${'\n'} 剪映草稿添加成功`, "BookTask_AddJianyingDraft")

View File

@ -1,5 +1,5 @@
import path from 'path';
import { CheckFileOrDirExist, DeleteFolderAllFile } from '../../../define/Tools/file';
import { CheckFileOrDirExist, CopyFileOrFolder, DeleteFolderAllFile, GetFilesWithExtensions } from '../../../define/Tools/file';
import fs from "fs";
import { ValidateJson } from '../../../define/Tools/validate';
import { FfmpegOptions } from '../ffmpegOptions';
@ -38,7 +38,7 @@ class JianyingService {
* @param projectDir
* @param packagePath
*/
async GetDraftFrameAndText(draftDir: string, projectDir: string, packagePath: string) {
public async GetDraftFrameAndText(draftDir: string, projectDir: string, packagePath: string) {
try {
// 获取草稿文件路径
let draftJsonPath = path.resolve(draftDir, "draft_content.json");
@ -101,6 +101,81 @@ class JianyingService {
}
}
/**
*
* @param draftName
* @param imageFolder
* 1稿
* 2稿
* 3稿
* 3
* 3
*/
public async GenerateDraftFromDepend(dependDraftName: string, imageFolder: string, draftName: string) {
console.log(draftName);
let dependDraftPath = path.resolve(global.config.draft_path, dependDraftName);
if (!await CheckFileOrDirExist(dependDraftPath)) {
throw new Error("依赖的草稿文件不存在,请检查");
}
let draftPath = path.resolve(global.config.draft_path, draftName);
if (await CheckFileOrDirExist(draftPath)) {
// 删除文件夹
await DeleteFolderAllFile(draftPath, true);
}
// 复制
await CopyFileOrFolder(dependDraftPath, draftPath)
let images = await GetFilesWithExtensions(imageFolder, ['.png']);
// 开始处理数据
let draftJsonPath = path.resolve(draftPath, "draft_content.json");
if (!await CheckFileOrDirExist(draftJsonPath)) {
throw new Error("剪映草稿文件数据文件不存在,请先检查");
}
// 读取草稿文件内容
let draftJsonString = await fs.promises.readFile(draftJsonPath, "utf-8");
if (!ValidateJson(draftJsonString)) {
throw new Error("剪映草稿文件格式错误,请检查");
}
let draftJson = JSON.parse(draftJsonString);
let draftTracks = draftJson.tracks;
if (draftTracks.length <= 0) {
throw new Error("剪映草稿文件格式错误,没有轨道,请检查");
}
let videoTrack = draftTracks[0];// 只处理主轨道
if (videoTrack.type !== "video") {
throw new Error("剪映草稿文件格式错误主轨道不是Video请检查");
}
let videoTrackSegments = videoTrack.segments;
if (videoTrackSegments.length <= 0) {
throw new Error("剪映草稿文件格式错误,主轨道没有对应的图片节点,请检查");
}
let segmentMaterialNodes = [];
for (let i = 0; i < videoTrackSegments.length; i++) {
const element = videoTrackSegments[i];
let materialId = element.material_id;
let materialNode = this.FindNode(draftJson.materials.videos, "id", materialId);
segmentMaterialNodes.push(materialNode);
}
if (images.length !== segmentMaterialNodes.length) {
throw new Error("当前新增的任务对应的图片数量和依赖的草稿的主轨道的图片数量不一致,请检查");
}
// 直接修改
for (let i = 0; i < segmentMaterialNodes.length; i++) {
const element = segmentMaterialNodes[i];
if (!images[i]) {
throw new Error("图片数量不对应,请检查");
}
element.path = images[i];
}
// 好了 写出草稿文件
await fs.promises.writeFile(draftJsonPath, JSON.stringify(draftJson), "utf-8");
}
/**
*
* @param nodes

View File

@ -39,5 +39,6 @@ declare namespace SoftwareSettingModel {
space_image: string = undefined // 空白图片
gpt_key: string = undefined // GPT KEY,
laiApiSelect: string = undefined // LaiAPI选择
hdScale: number = 2 // HD缩放
}
}

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

@ -13,6 +13,7 @@ declare namespace Book {
oldVideoPath?: string,
srtPath?: string,
audioPath?: string,
draftDepend?: string, // 草稿依赖
imageFolder?: string,
draftSrtStyle?: string | null // 草稿字幕样式
backgroundMusic?: string | null // 背景音乐ID
@ -54,6 +55,7 @@ declare namespace Book {
generateVideoPath?: string,
srtPath?: string,
audioPath?: string,
draftDepend?: string,
draftSrtStyle?: string | null // 草稿字幕样式
backgroundMusic?: string | null // 背景音乐ID
friendlyReminder?: string | null // 友情提示
@ -147,6 +149,7 @@ declare namespace Book {
bookTaskId?: string
videoPath?: string // 视频地址
audioPath?: string // 音频地址
draftDepend?: string // 草稿依赖
word?: string // 文案
oldImage?: string // 旧图片用于SD的图生图
afterGpt?: string // GPT生成的文案

View File

@ -239,7 +239,7 @@ const book = {
await ipcRenderer.invoke(DEFINE_STRING.BOOK.GENERATE_IMAGE_ALL, bookTaskId, imageCategory),
// 高清图片前检查
CheckImageFileSize: async (id, fileSize, operateBookType) =>
CheckImageFileSize: async (id: string | string[], fileSize: number, operateBookType: OperateBookType) =>
await ipcRenderer.invoke(
DEFINE_STRING.BOOK.CHECK_IMAGE_FILE_SIZE,
id,
@ -248,7 +248,7 @@ const book = {
),
// 高清图片任务
HDImage: async (id, scale, operateBookType) =>
HDImage: async (id: string | string[], scale: number, operateBookType: OperateBookType) =>
await ipcRenderer.invoke(DEFINE_STRING.BOOK.HD_IMAGE, id, scale, operateBookType),
// 将小说视频相关的设置添加到小说任务批次
@ -260,7 +260,7 @@ const book = {
),
// 添加数据到剪映草稿
AddJianyingDraft: async (id, operateBookType) =>
AddJianyingDraft: async (id: string | string[], operateBookType: OperateBookType) =>
await ipcRenderer.invoke(DEFINE_STRING.BOOK.ADD_JIANYING_DRAFT, id, operateBookType)
//#endregion

File diff suppressed because it is too large Load Diff

View File

@ -48,7 +48,6 @@ export default defineComponent({
props: ['bookTask'],
setup(props) {
let message = useMessage()
let dialog = useDialog()
let bookTask = ref(props.bookTask)
@ -63,7 +62,6 @@ export default defineComponent({
* @param e
*/
async function ReSetBookTaskDetail(e) {
e.stopPropagation()
dialog.warning({
title: '重置小说任务',
@ -115,15 +113,28 @@ export default defineComponent({
}
async function hdImageFunc(id, operateBookType) {
softwareStore.spin.spinning = true
softwareStore.spin.tip = '正在高清中。。。'
let res = await window.book.HDImage(id, 4, operateBookType)
softwareStore.spin.spinning = false
if (res.code == 1) {
message.success('高清成功')
} else {
message.error(res.message)
}
let da = dialog.warning({
title: '高清提示',
content: `是否确认高清,当前的高清倍数为 ${softwareStore.globalSetting.hdScale} 倍,是否继续?`,
positiveText: '继续',
negativeText: '取消',
onPositiveClick: async () => {
da.destroy()
softwareStore.spin.spinning = true
softwareStore.spin.tip = '正在高清中。。。'
let res = await window.book.HDImage(
id,
softwareStore.globalSetting.hdScale ?? 2,
operateBookType
)
softwareStore.spin.spinning = false
if (res.code == 1) {
message.success('高清成功')
} else {
message.error(res.message)
}
}
})
}
/**
@ -171,10 +182,10 @@ export default defineComponent({
*/
async function ClipDraft(e) {
e.stopPropagation()
dialog.info({
closeOnEsc: false,
title: '生成草稿前检查',
title: `生成草稿前检查 ${bookTask.value.name}`,
maskClosable: false,
content: () =>
h(ManageBookTaskGenerateInformation, { bookTask: bookTask.value, type: 'bookTask' })

View File

@ -46,15 +46,30 @@
clearable
/>
</n-form-item>
<n-form-item label="选择剪映草稿" path="backgroundMusic">
<div>
<div style="color: red">
注意选择的草稿主轨道的图片数量要和当前的相同会生成新的草稿并且只会替换图片保留其余的数据
</div>
<n-select
style="width: 300px"
v-model:value="bookTask.draftDepend"
filterable
placeholder="选择样式"
:options="draftSelect"
clearable
/>
</div>
</n-form-item>
<n-form-item path="backgroundMusic">
<n-button :disabled="type == 'book'" type="info" @click="UseBookVideoDataToBookTask">{{
type == bookTask ? '应用主小说相关数据' : '当前就是用的主小说的数据'
}}</n-button>
<n-button :disabled="type == 'book'" type="info" @click="UseBookVideoDataToBookTask"
>应用主小说相关数据</n-button
>
<n-button type="info" style="margin-left: 10px" @click="SaveVideoData">保存数据</n-button>
</n-form-item>
<div v-if="type == 'bookTask'" style="color: red">注意在生成草稿前要先保存数据</div>
<div v-else style="color: red">
注意在生成草稿前要先保存数据当前会生成全部的草稿全部会使用上面的参数
注意在生成草稿前要先保存数据当前会生成选择的草稿全部会使用上面的参数
</div>
<n-form-item style="display: flex; justify-content: flex-end">
<n-button type="info" style="margin-left: 10px" @click="AddJianyingDraft">生成草稿</n-button>
@ -62,136 +77,166 @@
</n-form>
</template>
<script>
import { ref, onMounted, defineComponent, onUnmounted, toRaw, watch } from 'vue'
<script setup>
import { ref, onMounted } from 'vue'
import { useMessage, NForm, NFormItem, NButton, NIcon, NInput, NSelect } from 'naive-ui'
import { FolderOpen } from '@vicons/ionicons5'
import { OperateBookType } from '../../../../../../define/enum/bookEnum'
import { useReverseManageStore } from '../../../../../../stores/reverseManage'
export default defineComponent({
components: { NForm, NFormItem, NButton, NIcon, NInput, NSelect, FolderOpen },
props: ['bookTask', 'type'],
setup(props) {
let bookTask = ref(props.bookTask)
let type = ref(props.type)
let backgroundMusicOptions = ref([])
let message = useMessage()
onMounted(async () => {
//
await window.api.GetBackgroundMusicConfigList((value) => {
if (value.code == 0) {
message.error(value.message)
return
}
for (let i = 0; i < value.value.length; i++) {
const element = value.value[i]
let obj = {
label: element.name,
value: element.id
}
backgroundMusicOptions.value.push(obj)
}
})
let props = defineProps({
bookTask: undefined,
type: undefined,
selectBookTask: []
})
let bookTask = ref(props.bookTask)
let type = ref(props.type)
let backgroundMusicOptions = ref([])
let message = useMessage()
let draftSelect = ref([])
let reverseManageStore = useReverseManageStore()
onMounted(async () => {
// 稿
window.api.getDraftFileList((value) => {
value.forEach((element) => {
let obj = {
label: element,
value: element
}
draftSelect.value.push(obj)
})
/**
* 选择对应的字幕文件
*/
async function SelectSrtFile() {
window.api.SelectFile(['srt'], (value) => {
if (value.code == 0) {
message.error(value.message)
return
}
bookTask.value.srtPath = value.value
})
})
//
await window.api.GetBackgroundMusicConfigList((value) => {
if (value.code == 0) {
message.error(value.message)
return
}
/**
* 选择对应的配音文件
*/
async function SelectMusicFile() {
window.api.SelectFile(['mp3', 'wav'], (value) => {
if (value.code == 0) {
message.error(value.message)
return
}
bookTask.value.audioPath = value.value
})
}
/**
* 应用主小说相关数据
*/
async function UseBookVideoDataToBookTask() {
if (type.value == 'book') {
message.error('当前状态这个按钮不可用')
return
}
let res = await window.book.UseBookVideoDataToBookTask(
bookTask.value.id,
OperateBookType.BOOKTASK
)
if (res.code == 0) {
message.error(res.message)
} else {
//
bookTask.value.backgroundMusic = res.data.backgroundMusic
bookTask.value.friendlyReminder = res.data.friendlyReminder
bookTask.value.draftSrtStyle = res.data.draftSrtStyle
bookTask.value.srtPath = res.data.srtPath
bookTask.value.audioPath = res.data.audioPath
message.success('应用主小说相关数据成功')
for (let i = 0; i < value.value.length; i++) {
const element = value.value[i]
let obj = {
label: element.name,
value: element.id
}
backgroundMusicOptions.value.push(obj)
}
})
})
/**
* 保存数据
*/
async function SaveVideoData() {
let res = await window.db.UpdateBookTaskData(bookTask.value.id, {
srtPath: bookTask.value.srtPath,
audioPath: bookTask.value.audioPath,
backgroundMusic: bookTask.value.backgroundMusic,
friendlyReminder: bookTask.value.friendlyReminder,
draftSrtStyle: bookTask.value.draftSrtStyle
})
if (res.code == 0) {
message.error('保存小说批次数据失败')
} else {
message.success('保存小说批次数据成功')
}
/**
* 选择对应的字幕文件
*/
async function SelectSrtFile() {
window.api.SelectFile(['srt'], (value) => {
if (value.code == 0) {
message.error(value.message)
return
}
bookTask.value.srtPath = value.value
})
}
/**
* 添加草稿
*/
async function AddJianyingDraft() {
let res = await window.book.AddJianyingDraft(bookTask.value.id, OperateBookType.BOOKTASK)
window.api.showGlobalMessageDialog(res)
/**
* 选择对应的配音文件
*/
async function SelectMusicFile() {
window.api.SelectFile(['mp3', 'wav'], (value) => {
if (value.code == 0) {
message.error(value.message)
return
}
bookTask.value.audioPath = value.value
})
}
return {
bookTask,
type,
AddJianyingDraft,
SelectSrtFile,
SelectMusicFile,
SaveVideoData,
UseBookVideoDataToBookTask,
backgroundMusicOptions,
rules: {
srtPath: [
{ required: true, message: '请选择背景音乐', trigger: ['input', 'blur', 'change'] }
],
audioPath: [
{ required: true, message: '请选择音频文件', trigger: ['input', 'blur', 'change'] }
]
}
}
/**
* 应用主小说相关数据
*/
async function UseBookVideoDataToBookTask() {
if (type.value == 'book') {
message.error('当前状态这个按钮不可用')
return
}
let res = await window.book.UseBookVideoDataToBookTask(
bookTask.value.id,
OperateBookType.BOOKTASK
)
if (res.code == 0) {
message.error(res.message)
} else {
//
bookTask.value.backgroundMusic = res.data.backgroundMusic
bookTask.value.friendlyReminder = res.data.friendlyReminder
bookTask.value.draftSrtStyle = res.data.draftSrtStyle
bookTask.value.srtPath = res.data.srtPath
bookTask.value.audioPath = res.data.audioPath
message.success('应用主小说相关数据成功')
}
}
/**
* 保存数据
*/
async function SaveVideoData() {
console.log('SaveVideoData', bookTask.value)
if (props.type == 'book') {
let res = await window.db.UpdateBookData(bookTask.value.id, {
srtPath: bookTask.value.srtPath,
audioPath: bookTask.value.audioPath,
backgroundMusic: bookTask.value.backgroundMusic,
friendlyReminder: bookTask.value.friendlyReminder,
draftSrtStyle: bookTask.value.draftSrtStyle,
draftDepend: bookTask.value.draftDepend
})
if (res.code == 0) {
message.error(res.message)
} else {
message.success('保存小说数据成功')
}
} else if (props.type == 'bookTask') {
let res = await window.db.UpdateBookTaskData(bookTask.value.id, {
srtPath: bookTask.value.srtPath,
audioPath: bookTask.value.audioPath,
backgroundMusic: bookTask.value.backgroundMusic,
friendlyReminder: bookTask.value.friendlyReminder,
draftSrtStyle: bookTask.value.draftSrtStyle,
draftDepend: bookTask.value.draftDepend
})
if (res.code == 0) {
message.error(res.message)
} else {
message.success('保存小说批次数据成功')
}
} else {
message.error('未知的操作类型,请检查')
}
}
/**
* 添加草稿
*/
async function AddJianyingDraft() {
if (props.type == 'book') {
let res = await window.book.AddJianyingDraft(
props.selectBookTask,
OperateBookType.ASSIGNBOOKTASK
)
window.api.showGlobalMessageDialog(res)
} else if (props.type == 'bookTask') {
let res = await window.book.AddJianyingDraft(bookTask.value.id, OperateBookType.BOOKTASK)
window.api.showGlobalMessageDialog(res)
} else {
message.error('未知的操作类型,请检查')
return
}
}
let rules = ref({
srtPath: [{ required: true, message: '请选择背景音乐', trigger: ['input', 'blur', 'change'] }],
audioPath: [{ required: true, message: '请选择音频文件', trigger: ['input', 'blur', 'change'] }]
})
</script>

View File

@ -1,21 +1,21 @@
<template>
<div style="margin-bottom: 5px; margin-left: 5px; display: flex; align-items: center">
<n-button :render-icon="renderIcon" size="small" type="info" @click="AddBookDialog"
<n-button :render-icon="renderIcon" size="small" type="info" @click="AddBookDialog()"
>新增</n-button
>
<n-button style="margin-left: 5px" size="small" type="info" @click="HDImageAll"
<n-button style="margin-left: 5px" size="small" type="info" @click="HDImageAll()"
>一键高清</n-button
>
<n-button style="margin-left: 5px" type="info" size="small" @click="DraftAll"
<n-button style="margin-left: 5px" type="info" size="small" @click="DraftAll()"
>一键草稿</n-button
>
<n-button style="margin-left: 5px" type="info" size="small" @click="VideoAll"
<n-button style="margin-left: 5px" type="info" size="small" @click="VideoAll()"
>一键视频</n-button
>
<n-button style="margin-left: 5px" type="info" size="small" @click="ResetAll"
<n-button style="margin-left: 5px" type="info" size="small" @click="ResetAll()"
>一键重置</n-button
>
<n-button style="margin-left: 5px" type="info" size="small" @click="DeleteAll"
<n-button style="margin-left: 5px" type="info" size="small" @click="DeleteAll()"
>一键删除</n-button
>
</div>
@ -26,11 +26,13 @@
:data="reverseManageStore.bookTaskData"
:scroll-x="800"
:row-props="rowProps"
:row-key="rowKey"
@update:checked-row-keys="handleCheck"
/>
</div>
</template>
<script>
<script setup>
import { ref, onMounted, defineComponent, onUnmounted, toRaw, watch, h } from 'vue'
import { useMessage, useDialog, NButton, NDataTable, NIcon, NWatermark } from 'naive-ui'
import { useReverseManageStore } from '../../../../stores/reverseManage'
@ -42,198 +44,208 @@ import { OperateBookType } from '../../../../define/enum/bookEnum'
import { DEFINE_STRING } from '../../../../define/define_string'
import { ResponseMessageType } from '../../../../define/enum/softwareEnum'
import AddBookTask from './Components/ManageBook/AddBookTask.vue'
import ManageBookTaskGenerateInformation from './Components/ManageBook/ManageBookTaskGenerateInformation.vue'
export default defineComponent({
components: {
NButton,
NDataTable,
BookTaskListAction,
NWatermark
},
let reverseManageStore = useReverseManageStore()
let softwareStore = useSoftwareStore()
let dialog = useDialog()
const router = useRouter()
let message = useMessage()
let columns = createColumns()
const checkedRowKeysRef = ref([])
onMounted(async () => {
window.api.setEventListen([DEFINE_STRING.BOOK.MAIN_DATA_RETURN], (value) => {
if (value.code == 0) {
message.error(value.message)
return
}
if (value.type == ResponseMessageType.HD_IMAGE) {
softwareStore.spin.tip = `正在高清中 ${value.data.current} / ${value.data.total}`
}
})
})
setup() {
let reverseManageStore = useReverseManageStore()
let softwareStore = useSoftwareStore()
let dialog = useDialog()
const router = useRouter()
let message = useMessage()
onMounted(async () => {
window.api.setEventListen([DEFINE_STRING.BOOK.MAIN_DATA_RETURN], (value) => {
if (value.code == 0) {
message.error(value.message)
return
}
if (value.type == ResponseMessageType.HD_IMAGE) {
softwareStore.spin.tip = `正在高清中 ${value.data.current} / ${value.data.total}`
}
})
onUnmounted(() => {
window.api.removeEventListen([DEFINE_STRING.BOOK.MAIN_DATA_RETURN])
})
function rowKey(row) {
return row.id
}
function handleCheck(rowKeys) {
checkedRowKeysRef.value = rowKeys
}
function createColumns() {
return [
{
type: 'selection'
},
{
title: 'No.',
key: 'no',
width: 80
},
{
title: '名称',
key: 'name',
width: 130
},
{
title: '风格',
key: 'styleList'
},
{
title: '前缀',
key: 'prefix',
width: 130
},
{
title: '状态',
key: 'status',
width: 100
},
{
title: '操作',
key: 'action',
width: 215,
fixed: 'right',
render(row, index) {
return h(BookTaskListAction, {
bookTask: row
})
}
}
]
}
/**
* 高清数据
*/
async function hdImageFunc(id, operateBookType) {
softwareStore.spin.spinning = true
softwareStore.spin.tip = '正在高清中。。。'
let res = await window.book.HDImage(id, 4, operateBookType)
softwareStore.spin.spinning = false
if (res.code == 1) {
message.success('高清成功')
} else {
message.error(res.message)
}
}
/**
* 一键高清全部
*/
async function HDImageAll() {
console.log('一键高清', checkedRowKeysRef.value)
if (checkedRowKeysRef.value.length == 0) {
message.error('请选择要高清的数据')
return
}
softwareStore.spin.spinning = true
softwareStore.spin.tip = '正在进行高清检查。。。'
let checkImage = await window.book.CheckImageFileSize(
[...checkedRowKeysRef.value],
10240,
OperateBookType.ASSIGNBOOKTASK
)
softwareStore.spin.spinning = false
if (checkImage.code == 0) {
message.error(checkImage.message)
return
}
//
if (checkImage.data && checkImage.data.length > 0) {
//
let msg = checkImage.data.map((item, index) => {
return item.outImagePath + '\n'
})
onUnmounted(() => {
window.api.removeEventListen([DEFINE_STRING.BOOK.MAIN_DATA_RETURN])
dialog.warning({
title: '高清警告',
content: '下面的图片大于10MB是否继续高清' + '\n' + msg,
positiveText: '确定',
negativeText: '取消',
onPositiveClick: async () => {
alert('确认高清')
await hdImageFunc([...checkedRowKeysRef.value], OperateBookType.ASSIGNBOOKTASK)
}
})
} else {
//
await hdImageFunc([...checkedRowKeysRef.value], OperateBookType.ASSIGNBOOKTASK)
}
}
function createColumns() {
return [
{
title: 'No.',
key: 'no',
width: 80
},
{
title: '名称',
key: 'name',
width: 130
},
{
title: '风格',
key: 'styleList'
},
{
title: '前缀',
key: 'prefix',
width: 130
},
{
title: '状态',
key: 'status',
width: 100
},
{
title: '操作',
key: 'action',
width: 215,
fixed: 'right',
render(row, index) {
return h(BookTaskListAction, {
bookTask: row
})
}
}
]
}
async function AddBookDialog() {
message.info('新增' + reverseManageStore.selectBook.id)
dialog.create({
title: '新增小说批次任务',
showIcon: false,
content: () => h(AddBookTask),
style: { width: '600px' }
})
}
/**
* 高清数据
*/
async function hdImageFunc(id, operateBookType) {
softwareStore.spin.spinning = true
softwareStore.spin.tip = '正在高清中。。。'
let res = await window.book.HDImage(id, 4, operateBookType)
softwareStore.spin.spinning = false
if (res.code == 1) {
message.success('高清成功')
} else {
message.error(res.message)
}
}
/**
* 一键高清全部
*/
async function HDImageAll() {
alert('高清全部')
softwareStore.spin.spinning = true
softwareStore.spin.tip = '正在进行高清检查。。。'
let checkImage = await window.book.CheckImageFileSize(
reverseManageStore.selectBook.id,
10240,
OperateBookType.BOOK
)
softwareStore.spin.spinning = false
if (checkImage.code == 0) {
message.error(checkImage.message)
return
}
//
if (checkImage.data && checkImage.data.length > 0) {
//
let msg = checkImage.data.map((item, index) => {
return item.outImagePath + '\n'
})
dialog.warning({
title: '高清警告',
content: '下面的图片大于10MB是否继续高清' + '\n' + msg,
positiveText: '确定',
negativeText: '取消',
onPositiveClick: async () => {
alert('确认高清')
await hdImageFunc(reverseManageStore.selectBook.id, OperateBookType.BOOK)
}
})
} else {
//
await hdImageFunc(reverseManageStore.selectBook.id, OperateBookType.BOOK)
}
}
async function AddBookDialog() {
message.info('新增' + reverseManageStore.selectBook.id)
dialog.create({
title: '新增小说批次任务',
showIcon: false,
content: () => h(AddBookTask),
style: { width: '600px' }
/**
* 一键生成草稿
*/
async function DraftAll() {
if (checkedRowKeysRef.value.length == 0) {
message.error('请选择要生成草稿的任务')
return
}
// 稿
dialog.info({
closeOnEsc: false,
title: `生成草稿前检查 ${reverseManageStore.selectBook.name}`,
maskClosable: false,
content: () =>
h(ManageBookTaskGenerateInformation, {
bookTask: reverseManageStore.selectBook,
type: 'book',
selectBookTask: [...checkedRowKeysRef.value]
})
}
})
}
/**
* 一键生成草稿
*/
async function DraftAll() {
alert('草稿全部')
}
/**
* 一键生成视频
*/
async function VideoAll() {
alert('视频全部')
}
/**
* 一键生成视频
*/
async function VideoAll() {
alert('视频全部')
}
/**
* 一键重置
*/
async function ResetAll() {
alert('重置全部')
}
/**
* 一键重置
*/
async function ResetAll() {
alert('重置全部')
}
/**
* 一键删除
*/
async function DeleteAll() {
alert('删除全部')
}
return {
reverseManageStore,
AddBookDialog,
HDImageAll,
DraftAll,
VideoAll,
ResetAll,
DeleteAll,
columns: createColumns(),
renderIcon() {
return h(NIcon, null, {
default: () => h(AddSharp, { size: '40', color: 'white' }, {})
})
},
rowProps: (row) => {
return {
style: 'cursor: pointer;',
onClick: async () => {
// message.info(row.id)
reverseManageStore.selectBookTask = row
softwareStore.spin.spinning = true
softwareStore.spin.tip = '正在加载数据'
//
router.push({ name: 'manage_book', params: { id: row.id } })
}
}
}
/**
* 一键删除
*/
async function DeleteAll() {
alert('删除全部')
}
const renderIcon = () => {
return h(NIcon, null, {
default: () => h(AddSharp, { size: '40', color: 'white' }, {})
})
}
const rowProps = (row) => {
return {
style: 'cursor: pointer;',
ondblclick: async () => {
// message.info(row.id)
reverseManageStore.selectBookTask = row
softwareStore.spin.spinning = true
softwareStore.spin.tip = '正在加载数据'
//
router.push({ name: 'manage_book', params: { id: row.id } })
}
}
})
}
</script>

View File

@ -1,7 +1,7 @@
<template>
<div style="height: 100%">
<ManageBook style="height: 100%" v-if="softwareStore.softWare.reverse_display_show" />
<n-split v-else direction="horizontal" :min="0.4" :max="0.8" :default-size="0.6" height="100%">
<n-split v-else direction="horizontal" :min="0.4" :max="0.8" :default-size="0.4" height="100%">
<template #1> <ManageBook style="height: 100%" /> </template>
<template #2>
<ManageBookTask style="height: 100%" />

View File

@ -46,14 +46,24 @@
<template #unchecked> </template>
</n-switch>
</n-form-item>
<n-form-item label="人物场景选择模式" style="margin-left: 20px">
<n-form-item label="选择高清倍率" style="margin-left: 20px">
<n-select
size="small"
placeholder="请选择"
:options="hdSelectOptions"
v-model:value="formValue.hdScale"
style="width: 120px"
/>
</n-form-item>
<n-form-item style="margin-left: 20px" label="人物场景选择模式">
<n-select
size="small"
placeholder="请选择"
:options="character_select_model_options"
v-model:value="formValue.character_select_model"
style="width: 120px"
/>
>
</n-select>
</n-form-item>
<n-form-item style="margin-left: 20px"
><n-checkbox v-model:checked="formValue.window_wh_bm_remember"
@ -212,12 +222,27 @@ export default defineComponent({
theme: softwareStore.globalSetting.theme,
character_select_model: window.config.character_select_model,
window_wh_bm_remember: window.config.window_wh_bm_remember,
laiApiSelect: window.config.laiApiSelect ? window.config.laiApiSelect : LaiAPIType.MAIN
laiApiSelect: window.config.laiApiSelect ? window.config.laiApiSelect : LaiAPIType.MAIN,
hdScale: window.config.hdScale ?? 2
})
let show = ref(false)
let gpt_options = ref([])
let gpt_model_options = ref([])
let character_select_model_options = ref([])
let hdSelectOptions = ref([
{
label: '2倍',
value: 2
},
{
label: '3倍',
value: 3
},
{
label: '4倍',
value: 4
}
])
let dialog = useDialog()
let loading = ref(false)
/**
@ -472,6 +497,7 @@ export default defineComponent({
AddGptPrompt,
character_select_model_options,
softwareStore,
hdSelectOptions,
ModifyTranslateSetting,
laiApiOptions: [
{