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", "name": "laitool",
"version": "3.1.7", "version": "3.1.8",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "laitool", "name": "laitool",
"version": "3.1.7", "version": "3.1.8",
"hasInstallScript": true, "hasInstallScript": true,
"dependencies": { "dependencies": {
"@alicloud/alimt20181012": "^1.2.0", "@alicloud/alimt20181012": "^1.2.0",

View File

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

Binary file not shown.

View File

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

View File

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

View File

@ -176,6 +176,27 @@ const migration = (oldRealm: Realm, newRealm: Realm) => {
newBookTask[i].messageName = '' 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 { export class BaseRealmService extends BaseService {
@ -217,7 +238,7 @@ export class BaseRealmService extends BaseService {
BookTaskDetailModel BookTaskDetailModel
], ],
path: this.dbpath, path: this.dbpath,
schemaVersion: 24, schemaVersion: 27,
migration: migration migration: migration
} }
this.realm = await Realm.open(config) this.realm = await Realm.open(config)

View File

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

View File

@ -241,14 +241,14 @@ export function BookIpc() {
// 高清图片前检查,主要是检查图片大小 // 高清图片前检查,主要是检查图片大小
ipcMain.handle( ipcMain.handle(
DEFINE_STRING.BOOK.CHECK_IMAGE_FILE_SIZE, 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) await bookImage.CheckImageFileSize(id, fileSize, operateBookType)
) )
// 开始执行高清图片 // 开始执行高清图片
ipcMain.handle( ipcMain.handle(
DEFINE_STRING.BOOK.HD_IMAGE, 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( ipcMain.handle(
DEFINE_STRING.BOOK.ADD_JIANYING_DRAFT, 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 //#endregion
} }

View File

@ -1,17 +1,17 @@
import path from "path"; import path from 'path'
import { define } from "../../define/define"; import { define } from '../../define/define'
import { Tools } from "../tools"; import { Tools } from '../tools'
import { DEFINE_STRING } from "../../define/define_string"; import { DEFINE_STRING } from '../../define/define_string'
import { get, has } from "lodash"; import { get, has } from 'lodash'
const util = require('util'); const util = require('util')
const { spawn, exec } = require('child_process'); const { spawn, exec } = require('child_process')
const execAsync = util.promisify(exec); const execAsync = util.promisify(exec)
const fspromises = require("fs").promises; const fspromises = require('fs').promises
export class PublicMethod { export class PublicMethod {
constructor(global) { constructor(global) {
this.global = global; this.global = global
this.tools = new Tools(); this.tools = new Tools()
} }
/** /**
@ -21,35 +21,34 @@ export class PublicMethod {
*/ */
async SaveConfigJsonProperty(value) { async SaveConfigJsonProperty(value) {
try { try {
let data = value[0]; let data = value[0]
let property = value[1]; let property = value[1]
let parse = value[2]; let parse = value[2]
if (parse) { if (parse) {
data = JSON.parse(value[0]) data = JSON.parse(value[0])
} }
let json_path = path.join(global.config.project_path, "scripts/config.json"); let json_path = path.join(global.config.project_path, 'scripts/config.json')
// 判断文件是不是存在 // 判断文件是不是存在
let isExit = await this.tools.checkExists(json_path); let isExit = await this.tools.checkExists(json_path)
let json_data = {}; let json_data = {}
if (!isExit) { if (!isExit) {
const dirPath = path.dirname(json_path); const dirPath = path.dirname(json_path)
await fspromises.mkdir(dirPath, { recursive: true }); await fspromises.mkdir(dirPath, { recursive: true })
await fspromises.writeFile(json_path, '{}'); await fspromises.writeFile(json_path, '{}')
} } else {
else { const o_data = await fspromises.readFile(json_path, 'utf8')
const o_data = await fspromises.readFile(json_path, 'utf8');
// 将读取的 JSON 字符串转换为 JavaScript 对象 // 将读取的 JSON 字符串转换为 JavaScript 对象
let obj = JSON.parse(o_data); let obj = JSON.parse(o_data)
json_data = obj; json_data = obj
} }
json_data[property] = data; json_data[property] = data
await fspromises.writeFile(json_path, JSON.stringify(json_data)); await fspromises.writeFile(json_path, JSON.stringify(json_data))
return { return {
code: 1 code: 1
} }
} catch (error) { } catch (error) {
throw error; throw error
} }
} }
@ -62,14 +61,19 @@ export class PublicMethod {
async GetConfigJson(value, ckeck = true) { async GetConfigJson(value, ckeck = true) {
try { try {
value = JSON.parse(value) value = JSON.parse(value)
let srt_config_path = path.join(global.config.project_path, "scripts/config.json"); 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); let data = await this.tools.getJsonFilePropertyValue(
srt_config_path,
value[0],
value[1],
ckeck
)
return { return {
code: 1, code: 1,
data: data data: data
} }
} catch (error) { } catch (error) {
throw error; throw error
} }
} }
@ -78,33 +82,41 @@ export class PublicMethod {
* @param {传入的要修改的数据数组} value * @param {传入的要修改的数据数组} value
*/ */
async ModifyImageTaskList(value) { async ModifyImageTaskList(value) {
this.global.fileQueue.enqueue(async () => { this.global.fileQueue.enqueue(
async () => {
try { try {
let task_list_path = path.join(this.global.config.project_path, "scripts/task_list.json"); let task_list_path = path.join(this.global.config.project_path, 'scripts/task_list.json')
let isE = await this.tools.checkExists(task_list_path); let isE = await this.tools.checkExists(task_list_path)
if (!isE) { if (!isE) {
throw new Error("任务队列文件不存在。请先添加 批次任务"); throw new Error('任务队列文件不存在。请先添加 批次任务')
} }
let task_list_json = JSON.parse(await fspromises.readFile(task_list_path, "utf-8")); let task_list_json = JSON.parse(await fspromises.readFile(task_list_path, 'utf-8'))
// 循环循环数据。修改 // 循环循环数据。修改
for (let i = 0; i < value.length; i++) { for (let i = 0; i < value.length; i++) {
const element = value[i]; const element = value[i]
let index = task_list_json.task_list.findIndex(item => item.id == element.id); let index = task_list_json.task_list.findIndex((item) => item.id == element.id)
task_list_json.task_list[index] = element; task_list_json.task_list[index] = element
} }
await fspromises.writeFile(task_list_path, JSON.stringify(task_list_json)); await fspromises.writeFile(task_list_path, JSON.stringify(task_list_json))
} catch (error) { } catch (error) {
throw error; throw error
} }
}, "modifyFile", "modifyFile", "task_list") },
this.global.fileQueue.setSubBatchCompletionCallback("modifyFile", "task_list", async (failedTasks) => { 'modifyFile',
'modifyFile',
'task_list'
)
this.global.fileQueue.setSubBatchCompletionCallback(
'modifyFile',
'task_list',
async (failedTasks) => {
// 报错 // 报错
if (failedTasks.length > 0) { if (failedTasks.length > 0) {
let message = ""; let message = ''
failedTasks.forEach(({ taskId, error }) => { failedTasks.forEach(({ taskId, error }) => {
message += `${taskId}-, \n 错误信息: ${error}` + '\n'; message += `${taskId}-, \n 错误信息: ${error}` + '\n'
}); })
throw new Error(message); throw new Error(message)
// this.global.newWindow[0].win.webContents.send(DEFINE_STRING.SHOW_MESSAGE_DIALOG, { // this.global.newWindow[0].win.webContents.send(DEFINE_STRING.SHOW_MESSAGE_DIALOG, {
// code: 0, // code: 0,
// message: message // message: message
@ -116,10 +128,10 @@ export class PublicMethod {
// message: "修改成功" // message: "修改成功"
// }) // })
// } // }
return true; return true
}) }
)
} }
/** /**
* 高清指定的文件夹 * 高清指定的文件夹
@ -127,41 +139,43 @@ export class PublicMethod {
*/ */
async ImproveFolder(folder) { async ImproveFolder(folder) {
try { try {
let bakPath = path.join(this.global.config.project_path, "tmp/bak"); let bakPath = path.join(this.global.config.project_path, 'tmp/bak')
let oldInput = path.join(this.global.config.project_path, "tmp/" + folder); let oldInput = path.join(this.global.config.project_path, 'tmp/' + folder)
let newInput = path.join(this.global.config.project_path, "tmp/bak/" + folder) let newInput = path.join(this.global.config.project_path, 'tmp/bak/' + folder)
// 创建文件夹 // 创建文件夹
let existFolder = await this.tools.checkExists(bakPath); let existFolder = await this.tools.checkExists(bakPath)
if (!existFolder) { if (!existFolder) {
await fspromises.mkdir(bakPath, { recursive: true }); await fspromises.mkdir(bakPath, { recursive: true })
} }
let isExistNewFolder = await this.tools.checkExists(newInput); let isExistNewFolder = await this.tools.checkExists(newInput)
if (isExistNewFolder) { if (isExistNewFolder) {
await fspromises.rm(newInput, { recursive: true, force: true }); await fspromises.rm(newInput, { recursive: true, force: true })
} }
// 备份文件 // 备份文件
await fspromises.rename(oldInput, newInput); await fspromises.rename(oldInput, newInput)
//创建同名的文件,用作输出 //创建同名的文件,用作输出
await fspromises.mkdir(oldInput, { recursive: true }); await fspromises.mkdir(oldInput, { recursive: true })
// 开始高清 // 开始高清
let command = `"${path.join(define.package_path, "Improve/rnv.exe")}" -i "${newInput}" -o "${oldInput}"`; let command = `"${path.join(
let out = await execAsync(command, { maxBuffer: 1024 * 1024 * 10, encoding: 'utf-8' }); define.package_path,
console.log(out); 'Improve/rnv.exe'
await this.ModifyTaskStatus('out_folder', folder, "video_improvied"); )}" -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, { this.global.newWindow[0].win.webContents.send(DEFINE_STRING.VIDEO_GENERATE_STATUS_REFRESH, {
out_folder: folder, out_folder: folder,
status: "video_improvied" status: 'video_improvied'
}) })
} catch (error) { } catch (error) {
await this.ModifyTaskStatus('out_folder', folder, "video_improvie_error"); await this.ModifyTaskStatus('out_folder', folder, 'video_improvie_error')
this.global.newWindow[0].win.webContents.send(DEFINE_STRING.VIDEO_GENERATE_STATUS_REFRESH, { this.global.newWindow[0].win.webContents.send(DEFINE_STRING.VIDEO_GENERATE_STATUS_REFRESH, {
out_folder: folder, out_folder: folder,
status: "video_improvie_error" status: 'video_improvie_error'
}) })
throw error; throw error
} }
} }
@ -171,30 +185,39 @@ export class PublicMethod {
async AddWebuiJson() { async AddWebuiJson() {
try { try {
// 读取所有txt文件 // 读取所有txt文件
let txtfile = await this.tools.getFilesWithExtensions(path.join(this.global.config.project_path, 'tmp/input_crop'), '.txt'); let txtfile = await this.tools.getFilesWithExtensions(
let image = await this.tools.getFilesWithExtensions(path.join(this.global.config.project_path, 'tmp/input_crop'), '.png'); path.join(this.global.config.project_path, 'tmp/input_crop'),
let promptJson = await this.tools.getFilesWithExtensions(path.join(this.global.config.project_path, 'tmp/input_crop'), '.json'); '.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'
)
// json 已经存在,不做后续处理 // json 已经存在,不做后续处理
if (image.length == promptJson.length) { if (image.length == promptJson.length) {
return { return {
code: 1, code: 1
} }
} }
if (txtfile.length != image.length) { if (txtfile.length != image.length) {
throw new Error("关键词文件和图片数量对不上,请检查!!") throw new Error('关键词文件和图片数量对不上,请检查!!')
} }
let sd_config = JSON.parse(await fspromises.readFile(define.sd_setting, 'utf-8')); let sd_config = JSON.parse(await fspromises.readFile(define.sd_setting, 'utf-8'))
for (let i = 0; i < image.length; i++) { for (let i = 0; i < image.length; i++) {
const element = image[i]; const element = image[i]
let txtpath = element.split('.png')[0] + '.txt'; let txtpath = element.split('.png')[0] + '.txt'
let prompt = await fspromises.readFile(txtpath, 'utf-8'); let prompt = await fspromises.readFile(txtpath, 'utf-8')
// console.log(txtpath) // console.log(txtpath)
let obj = {} let obj = {}
obj.model = sd_config.setting.type; obj.model = sd_config.setting.type
obj.api = sd_config.setting.webui_api_url + 'sdapi/v1/img2img'; obj.api = sd_config.setting.webui_api_url + 'sdapi/v1/img2img'
obj.webui_config = { obj.webui_config = {
sampler_name: sd_config.webui.sampler_name, sampler_name: sd_config.webui.sampler_name,
prompt: prompt + ',' + sd_config.webui.prompt, prompt: prompt + ',' + sd_config.webui.prompt,
@ -206,22 +229,21 @@ export class PublicMethod {
width: sd_config.webui.width, width: sd_config.webui.width,
height: sd_config.webui.height, height: sd_config.webui.height,
seed: sd_config.setting.seed, seed: sd_config.setting.seed,
init_images: element, init_images: element
} }
obj.adetailer = sd_config.webui.adetailer; obj.adetailer = sd_config.webui.adetailer
// 写入 // 写入
await fspromises.writeFile(element + '.json', JSON.stringify(obj)); await fspromises.writeFile(element + '.json', JSON.stringify(obj))
// 删除对应的txt文件 // 删除对应的txt文件
await fspromises.unlink(txtpath); await fspromises.unlink(txtpath)
} }
return { return {
code: 1, code: 1
} }
} catch (error) { } catch (error) {
throw error; throw error
} }
} }
@ -230,15 +252,15 @@ export class PublicMethod {
*/ */
async GetImageTask() { async GetImageTask() {
try { try {
let json_path = path.join(this.global.config.project_path, "scripts/task_list.json"); let json_path = path.join(this.global.config.project_path, 'scripts/task_list.json')
let isExit = await this.tools.checkExists(json_path); let isExit = await this.tools.checkExists(json_path)
if (!isExit) { if (!isExit) {
return { return {
code: 1, code: 1,
data: null data: null
} }
} }
let json_data = JSON.parse(await fspromises.readFile(json_path)); let json_data = JSON.parse(await fspromises.readFile(json_path))
return { return {
code: 1, code: 1,
data: json_data data: json_data
@ -251,7 +273,6 @@ export class PublicMethod {
} }
} }
/** /**
* 修改任务的状态 * 修改任务的状态
* @param {查找的类型 id out_folder} type * @param {查找的类型 id out_folder} type
@ -259,48 +280,63 @@ export class PublicMethod {
* @param {新的状态} newStatus * @param {新的状态} newStatus
*/ */
async ModifyTaskStatus(type, id, newStatus) { async ModifyTaskStatus(type, id, newStatus) {
this.global.fileQueue.enqueue(async () => { this.global.fileQueue.enqueue(
async () => {
try { try {
// 将修改数据写入到一个并发为 1 的队列中 // 将修改数据写入到一个并发为 1 的队列中
let tmp_task = await fspromises.readFile(path.join(this.global.config.project_path, 'scripts/task_list.json'), 'utf-8'); let tmp_task = await fspromises.readFile(
path.join(this.global.config.project_path, 'scripts/task_list.json'),
'utf-8'
)
console.log(tmp_task) console.log(tmp_task)
let task = JSON.parse(tmp_task); let task = JSON.parse(tmp_task)
if (type == "id") { if (type == 'id') {
let index = task.task_list.findIndex(item => item.id == id); let index = task.task_list.findIndex((item) => item.id == id)
if (index < 0) { if (index < 0) {
throw new Error("传入的数据未找到"); throw new Error('传入的数据未找到')
} else { } else {
task.task_list[index].status = newStatus; task.task_list[index].status = newStatus
} }
} else if (type == "out_folder") { } else if (type == 'out_folder') {
let index = task.task_list.findIndex(item => item.out_folder == id); let index = task.task_list.findIndex((item) => item.out_folder == id)
if (index < 0) { if (index < 0) {
throw new Error("传入的数据未找到"); throw new Error('传入的数据未找到')
} else { } else {
task.task_list[index].status = newStatus; task.task_list[index].status = newStatus
} }
} else { } else {
throw new Error("输入类型错误") throw new Error('输入类型错误')
} }
await fspromises.writeFile(path.join(this.global.config.project_path, 'scripts/task_list.json'), JSON.stringify(task)); await fspromises.writeFile(
path.join(this.global.config.project_path, 'scripts/task_list.json'),
JSON.stringify(task)
)
} catch (error) { } catch (error) {
throw error; throw error
} }
}, "modifyFile", "modifyFile", "task_list") },
this.global.fileQueue.setSubBatchCompletionCallback("modifyFile", "task_list", async (failedTasks) => { 'modifyFile',
'modifyFile',
'task_list'
)
this.global.fileQueue.setSubBatchCompletionCallback(
'modifyFile',
'task_list',
async (failedTasks) => {
// 报错 // 报错
if (failedTasks.length > 0) { if (failedTasks.length > 0) {
let message = ""; let message = ''
failedTasks.forEach(({ taskId, error }) => { failedTasks.forEach(({ taskId, error }) => {
message += `${taskId}-, \n 错误信息: ${error}` + '\n'; message += `${taskId}-, \n 错误信息: ${error}` + '\n'
}); })
this.global.newWindow[0].win.webContents.send(DEFINE_STRING.SHOW_MESSAGE_DIALOG, { this.global.newWindow[0].win.webContents.send(DEFINE_STRING.SHOW_MESSAGE_DIALOG, {
code: 0, code: 0,
message: message message: message
}) })
} }
}) }
)
} }
/** /**
@ -313,23 +349,22 @@ export class PublicMethod {
async getSubFolderList(parentFolder, condition, value) { async getSubFolderList(parentFolder, condition, value) {
try { try {
// console.log(value); // console.log(value);
let folders = await fspromises.readdir(parentFolder, { withFileTypes: true }); let folders = await fspromises.readdir(parentFolder, { withFileTypes: true })
folders = folders.filter(item => item.isDirectory()) folders = folders.filter((item) => item.isDirectory()).map((item) => item.name)
.map(item => item.name)
if (condition == "start") { if (condition == 'start') {
folders = folders.filter(item => item.startsWith(value)); folders = folders.filter((item) => item.startsWith(value))
} else if (condition == "end") { } else if (condition == 'end') {
folders = folders.filter(item => item.endsWith(value)); folders = folders.filter((item) => item.endsWith(value))
} else if (condition == "include") { } else if (condition == 'include') {
//包含过滤 //包含过滤
folders = folders.filter(item => item.includes(value)); folders = folders.filter((item) => item.includes(value))
} else { } else {
throw new Error("条件参数错误"); throw new Error('条件参数错误')
} }
return folders; return folders
} catch (error) { } catch (error) {
throw error; throw error
} }
} }
} }

View File

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

View File

@ -1,38 +1,24 @@
import { OperateBookType } from "../../../define/enum/bookEnum"; import { OperateBookType } from "../../../define/enum/bookEnum";
import { Book } from "../../../model/book"; import { Book } from "../../../model/book";
import { errorMessage, successMessage } from "../../Public/generalTools"; 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 { GeneralResponse } from "../../../model/generalResponse";
import { Setting } from '../../../main/setting/setting' import { Setting } from '../../../main/setting/setting'
import path from 'path' import path from 'path'
import { CheckFolderExistsOrCreate } from "../../../define/Tools/file"; import { CheckFolderExistsOrCreate } from "../../../define/Tools/file";
import { BookTaskDetailService } from "../../../define/db/service/Book/bookTaskDetailService";
import fs from 'fs' import fs from 'fs'
import { ClipDraft } from '../../Public/clipDraft' import { ClipDraft } from '../../Public/clipDraft'
import { BookServiceBasic } from "../ServiceBasic/bookServiceBasic"; import { BookServiceBasic } from "../ServiceBasic/bookServiceBasic";
import { isEmpty } from "lodash";
import JianyingService from "../jianying/jianyingService";
export class BookVideo { export class BookVideo {
bookService: BookService
bookTaskService: BookTaskService
setting: Setting setting: Setting
bookTaskDetailService: BookTaskDetailService
bookServiceBasic: BookServiceBasic bookServiceBasic: BookServiceBasic
jianyingService: JianyingService
constructor() { constructor() {
this.setting = new Setting(global) this.setting = new Setting(global)
this.bookServiceBasic = new BookServiceBasic() this.bookServiceBasic = new BookServiceBasic()
} this.jianyingService = new JianyingService()
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()
}
} }
@ -44,23 +30,22 @@ export class BookVideo {
async UseBookVideoDataToBookTask(id: string, operateBookType: OperateBookType) { async UseBookVideoDataToBookTask(id: string, operateBookType: OperateBookType) {
try { try {
console.log(id, operateBookType) console.log(id, operateBookType)
await this.InitService();
let book = undefined as Book.SelectBook; let book = undefined as Book.SelectBook;
let bookTasks = undefined as Book.SelectBookTask[]; let bookTasks = undefined as Book.SelectBookTask[];
if (operateBookType == OperateBookType.BOOK) { if (operateBookType == OperateBookType.BOOK) {
book = this.bookService.GetBookDataById(id); book = await this.bookServiceBasic.GetBookDataById(id);
if (book == null) { if (book == null) {
throw new Error('未找到对应的小说') throw new Error('未找到对应的小说')
} }
bookTasks = this.bookTaskService.GetBookTaskData({ bookTasks = (await this.bookServiceBasic.GetBookTaskData({
bookId: id, bookId: id,
}).data; })).bookTasks;
} else if (operateBookType == OperateBookType.BOOKTASK) { } else if (operateBookType == OperateBookType.BOOKTASK) {
let bookTask = this.bookTaskService.GetBookTaskDataById(id); let bookTask = await this.bookServiceBasic.GetBookTaskDataById(id);
if (bookTask == null) { if (bookTask == null) {
throw new Error('未找到对应的小说任务') throw new Error('未找到对应的小说任务')
} }
book = this.bookService.GetBookDataById(bookTask.bookId); book = await this.bookServiceBasic.GetBookDataById(bookTask.bookId);
if (book == null) { if (book == null) {
throw new Error('未找到对应的小说') throw new Error('未找到对应的小说')
} }
@ -117,9 +102,9 @@ export class BookVideo {
await CheckFolderExistsOrCreate(path.dirname(configPath)); await CheckFolderExistsOrCreate(path.dirname(configPath));
// 开始配置 // 开始配置
let bookTaskDetail = this.bookTaskDetailService.GetBookTaskData({ let bookTaskDetail = await this.bookServiceBasic.GetBookTaskDetailData({
bookTaskId: bookTask.id bookTaskId: bookTask.id
}).data; });
let configData = { let configData = {
srt_time_information: [], srt_time_information: [],
@ -144,14 +129,7 @@ export class BookVideo {
start_time: element.startTime, start_time: element.startTime,
end_time: element.endTime, end_time: element.endTime,
timeLimit: `${element.startTime} -- ${element.endTime}`, timeLimit: `${element.startTime} -- ${element.endTime}`,
subValue: element.subValue?.map(item => { subValue: element.subValue,
return {
start_time: item.startTime,
end_time: item.endTime,
srt_value: item.srtValue,
id: item.id
}
}),
character_tags: [], character_tags: [],
gpt_prompt: element.gptPrompt, gpt_prompt: element.gptPrompt,
mjMessage: element.mjMessage, mjMessage: element.mjMessage,
@ -179,28 +157,29 @@ export class BookVideo {
* @param operateBookType * @param operateBookType
* @returns * @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 { try {
await this.InitService();
console.log(id, operateBookType) console.log(id, operateBookType)
let book = undefined as Book.SelectBook let book = undefined as Book.SelectBook
let bookTasks = undefined as Book.SelectBookTask[] let bookTasks = [] as Book.SelectBookTask[]
if (operateBookType == OperateBookType.BOOK) { if (operateBookType == OperateBookType.ASSIGNBOOKTASK) {
book = this.bookService.GetBookDataById(id); if (id.length <= 0) {
if (book == null) { throw new Error("没有找到小说批次任务,请检查");
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) { } else if (operateBookType == OperateBookType.BOOKTASK) {
// 直接获取对应的数据 // 直接获取对应的数据
let tempBookTask = this.bookTaskService.GetBookTaskDataById(id); let tempBookTask = await this.bookServiceBasic.GetBookTaskDataById(id as string);
if (tempBookTask == null) { if (tempBookTask == null) {
throw new Error("没有找到小说批次任务,请检查"); throw new Error("没有找到小说批次任务,请检查");
} }
bookTasks = [tempBookTask] bookTasks = [tempBookTask]
book = this.bookService.GetBookDataById(tempBookTask.bookId); book = await this.bookServiceBasic.GetBookDataById(tempBookTask.bookId);
if (book == null) { if (book == null) {
throw new Error throw new Error
} }
@ -213,20 +192,31 @@ export class BookVideo {
} }
// 判断是不是生成单个,每次生成都要修改一下配置文件 // 判断是不是生成单个,每次生成都要修改一下配置文件
// TODO 后面这个地方要修改
// 调用生成草稿的方法 // 调用生成草稿的方法
let result = [] let result = []
for (let i = 0; i < bookTasks.length; i++) { for (let i = 0; i < bookTasks.length; i++) {
const element = bookTasks[i]; const element = bookTasks[i];
// 判断是不是又依赖的草稿,又依赖的草稿,对于依赖的草稿进行操作
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); await this.GenerateConfigFile(book, element);
// 数据处理完毕,开始输出 // 数据处理完毕,开始输出
let clipDraft = new ClipDraft(global, [element.name, { let clipDraft = new ClipDraft(global, [element.name, {
srt_path: element.srtPath, srt_path: operateBookType == OperateBookType.ASSIGNBOOKTASK ? book.srtPath : element.srtPath,
audio_path: element.audioPath, audio_path: operateBookType == OperateBookType.ASSIGNBOOKTASK ? book.audioPath : element.audioPath,
draft_srt_style: element.draftSrtStyle ? element.draftSrtStyle : "0", draft_srt_style: operateBookType == OperateBookType.ASSIGNBOOKTASK ? (book.draftSrtStyle ? book.draftSrtStyle : '0') : (element.draftSrtStyle ? element.draftSrtStyle : "0"),
background_music: element.backgroundMusic, background_music: operateBookType == OperateBookType.ASSIGNBOOKTASK ? book.backgroundMusic : element.backgroundMusic,
friendly_reminder: element.friendlyReminder ? element.friendlyReminder : "0", friendly_reminder: operateBookType == OperateBookType.ASSIGNBOOKTASK ? (book.friendlyReminder ? book.bookFolderPath : '0') : (element.friendlyReminder ? element.friendlyReminder : "0"),
}]) }])
let res = await clipDraft.addDraft(); let res = await clipDraft.addDraft();
if (res.code == 0) { if (res.code == 0) {
@ -234,6 +224,7 @@ export class BookVideo {
} }
result.push(res.draft_name); result.push(res.draft_name);
} }
}
return successMessage(result, `${result.join('\n')} ${'\n'} 剪映草稿添加成功`, "BookTask_AddJianyingDraft") return successMessage(result, `${result.join('\n')} ${'\n'} 剪映草稿添加成功`, "BookTask_AddJianyingDraft")
} catch (error) { } catch (error) {

View File

@ -1,5 +1,5 @@
import path from 'path'; 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 fs from "fs";
import { ValidateJson } from '../../../define/Tools/validate'; import { ValidateJson } from '../../../define/Tools/validate';
import { FfmpegOptions } from '../ffmpegOptions'; import { FfmpegOptions } from '../ffmpegOptions';
@ -38,7 +38,7 @@ class JianyingService {
* @param projectDir * @param projectDir
* @param packagePath * @param packagePath
*/ */
async GetDraftFrameAndText(draftDir: string, projectDir: string, packagePath: string) { public async GetDraftFrameAndText(draftDir: string, projectDir: string, packagePath: string) {
try { try {
// 获取草稿文件路径 // 获取草稿文件路径
let draftJsonPath = path.resolve(draftDir, "draft_content.json"); 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 * @param nodes

View File

@ -39,5 +39,6 @@ declare namespace SoftwareSettingModel {
space_image: string = undefined // 空白图片 space_image: string = undefined // 空白图片
gpt_key: string = undefined // GPT KEY, gpt_key: string = undefined // GPT KEY,
laiApiSelect: string = undefined // LaiAPI选择 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, oldVideoPath?: string,
srtPath?: string, srtPath?: string,
audioPath?: string, audioPath?: string,
draftDepend?: string, // 草稿依赖
imageFolder?: string, imageFolder?: string,
draftSrtStyle?: string | null // 草稿字幕样式 draftSrtStyle?: string | null // 草稿字幕样式
backgroundMusic?: string | null // 背景音乐ID backgroundMusic?: string | null // 背景音乐ID
@ -54,6 +55,7 @@ declare namespace Book {
generateVideoPath?: string, generateVideoPath?: string,
srtPath?: string, srtPath?: string,
audioPath?: string, audioPath?: string,
draftDepend?: string,
draftSrtStyle?: string | null // 草稿字幕样式 draftSrtStyle?: string | null // 草稿字幕样式
backgroundMusic?: string | null // 背景音乐ID backgroundMusic?: string | null // 背景音乐ID
friendlyReminder?: string | null // 友情提示 friendlyReminder?: string | null // 友情提示
@ -147,6 +149,7 @@ declare namespace Book {
bookTaskId?: string bookTaskId?: string
videoPath?: string // 视频地址 videoPath?: string // 视频地址
audioPath?: string // 音频地址 audioPath?: string // 音频地址
draftDepend?: string // 草稿依赖
word?: string // 文案 word?: string // 文案
oldImage?: string // 旧图片用于SD的图生图 oldImage?: string // 旧图片用于SD的图生图
afterGpt?: string // GPT生成的文案 afterGpt?: string // GPT生成的文案

View File

@ -239,7 +239,7 @@ const book = {
await ipcRenderer.invoke(DEFINE_STRING.BOOK.GENERATE_IMAGE_ALL, bookTaskId, imageCategory), 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( await ipcRenderer.invoke(
DEFINE_STRING.BOOK.CHECK_IMAGE_FILE_SIZE, DEFINE_STRING.BOOK.CHECK_IMAGE_FILE_SIZE,
id, 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), 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) await ipcRenderer.invoke(DEFINE_STRING.BOOK.ADD_JIANYING_DRAFT, id, operateBookType)
//#endregion //#endregion

View File

@ -70,8 +70,8 @@
<n-data-table :columns="columns" :data="data" :pagination="pagination" :bordered="false" /> <n-data-table :columns="columns" :data="data" :pagination="pagination" :bordered="false" />
</template> </template>
<script> <script setup>
import { defineComponent, ref, onMounted, onUnmounted, h, toRaw } from 'vue' import { ref, onMounted, onUnmounted, h, toRaw } from 'vue'
import { import {
NButton, NButton,
useMessage, useMessage,
@ -83,13 +83,12 @@ import {
useDialog, useDialog,
NPopover, NPopover,
NTag, NTag,
NTabPane,
NTabs,
NIcon NIcon
} from 'naive-ui' } from 'naive-ui'
import { DEFINE_STRING } from '../../../../define/define_string' import { DEFINE_STRING } from '../../../../define/define_string'
import OneKeyMatrix from '../Backstep/OneKeyMatrix.vue'
import { FolderOpenSharp as FolderOpen } from '@vicons/ionicons5' import { FolderOpenSharp as FolderOpen } from '@vicons/ionicons5'
import { useSoftwareStore } from '../../../../stores/software'
const softwareStore = useSoftwareStore()
const buttonArr = [ const buttonArr = [
{ {
@ -167,30 +166,10 @@ function statusAndMessage(row) {
} }
} }
export default defineComponent({
components: {
NButton,
NDataTable,
NInput,
NSelect,
NForm,
NFormItem,
NPopover,
NTag,
NTabPane,
NTabs,
OneKeyMatrix,
NIcon,
FolderOpen
},
setup() {
let image_folder = ref('')
let message = useMessage() let message = useMessage()
let dialog = useDialog() let dialog = useDialog()
let data = ref([]) let data = ref([])
let options = ref([])
let backgroundMusicOptions = ref([]) let backgroundMusicOptions = ref([])
let friendlyReminderOptions = ref([])
let formValue = ref({ let formValue = ref({
srt_path: null, srt_path: null,
@ -376,6 +355,13 @@ export default defineComponent({
* @param {行数据} row * @param {行数据} row
*/ */
async function ImproveResolution(row) { async function ImproveResolution(row) {
let da = dialog.warning({
title: '高清确认',
content: `确认是不是要高清!当前的高清倍数为 ${softwareStore.globalSetting.hdScale} 倍,是否继续?`,
positiveText: '确定',
negativeText: '取消',
maskClosable: false,
onPositiveClick: async () => {
await window.api.ImproveResolution([row.folder], (value) => { await window.api.ImproveResolution([row.folder], (value) => {
if (value.code == 1) { if (value.code == 1) {
message.success('高清任务加入队列成功,请勿关闭软件') message.success('高清任务加入队列成功,请勿关闭软件')
@ -384,6 +370,8 @@ export default defineComponent({
} }
}) })
} }
})
}
/** /**
* 将生成视频加入到队列中目前一次最多三个 * 将生成视频加入到队列中目前一次最多三个
@ -440,7 +428,7 @@ export default defineComponent({
async function HdAllImages() { async function HdAllImages() {
dialog.warning({ dialog.warning({
title: '确认是不是要全部高清', title: '确认是不是要全部高清',
content: '确认是不是全部高清!分辨率太高,电脑会卡到爆的哦', content: `确认是不是全部高清!当前的高清倍数为 ${softwareStore.globalSetting.hdScale} 倍,是否继续?`,
positiveText: '确定', positiveText: '确定',
negativeText: '取消', negativeText: '取消',
maskClosable: false, maskClosable: false,
@ -672,39 +660,20 @@ export default defineComponent({
* 保存视频的基础信息 * 保存视频的基础信息
*/ */
async function SaveVideoSrtAndAudioMessage() { async function SaveVideoSrtAndAudioMessage() {
await window.api.SaveVideoSrtAndAudioMessage( await window.api.SaveVideoSrtAndAudioMessage(JSON.stringify(toRaw(formValue.value)), (value) => {
JSON.stringify(toRaw(formValue.value)),
(value) => {
if (value.code == 0) { if (value.code == 0) {
message.error(value.message) message.error(value.message)
return return
} }
message.success('保存成功') message.success('保存成功')
} })
)
} }
return { let columns = createColumns({
image_folder,
columns: createColumns({
showRegenerateWindow, showRegenerateWindow,
AddClipDraft, AddClipDraft,
ImproveResolution, ImproveResolution,
GenerateVideo, GenerateVideo,
OpenFolder OpenFolder
}),
data,
options,
backgroundMusicOptions,
friendlyReminderOptions,
formValue,
SelectSrtFile,
SelectMusicFile,
HdAllImages,
ExportAllDraft,
VideoGenerate,
SaveVideoSrtAndAudioMessage
}
}
}) })
</script> </script>

View File

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

View File

@ -46,15 +46,30 @@
clearable clearable
/> />
</n-form-item> </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-form-item path="backgroundMusic">
<n-button :disabled="type == 'book'" type="info" @click="UseBookVideoDataToBookTask">{{ <n-button :disabled="type == 'book'" type="info" @click="UseBookVideoDataToBookTask"
type == bookTask ? '应用主小说相关数据' : '当前就是用的主小说的数据' >应用主小说相关数据</n-button
}}</n-button> >
<n-button type="info" style="margin-left: 10px" @click="SaveVideoData">保存数据</n-button> <n-button type="info" style="margin-left: 10px" @click="SaveVideoData">保存数据</n-button>
</n-form-item> </n-form-item>
<div v-if="type == 'bookTask'" style="color: red">注意在生成草稿前要先保存数据</div> <div v-if="type == 'bookTask'" style="color: red">注意在生成草稿前要先保存数据</div>
<div v-else style="color: red"> <div v-else style="color: red">
注意在生成草稿前要先保存数据当前会生成全部的草稿全部会使用上面的参数 注意在生成草稿前要先保存数据当前会生成选择的草稿全部会使用上面的参数
</div> </div>
<n-form-item style="display: flex; justify-content: flex-end"> <n-form-item style="display: flex; justify-content: flex-end">
<n-button type="info" style="margin-left: 10px" @click="AddJianyingDraft">生成草稿</n-button> <n-button type="info" style="margin-left: 10px" @click="AddJianyingDraft">生成草稿</n-button>
@ -62,22 +77,38 @@
</n-form> </n-form>
</template> </template>
<script> <script setup>
import { ref, onMounted, defineComponent, onUnmounted, toRaw, watch } from 'vue' import { ref, onMounted } from 'vue'
import { useMessage, NForm, NFormItem, NButton, NIcon, NInput, NSelect } from 'naive-ui' import { useMessage, NForm, NFormItem, NButton, NIcon, NInput, NSelect } from 'naive-ui'
import { FolderOpen } from '@vicons/ionicons5' import { FolderOpen } from '@vicons/ionicons5'
import { OperateBookType } from '../../../../../../define/enum/bookEnum' import { OperateBookType } from '../../../../../../define/enum/bookEnum'
import { useReverseManageStore } from '../../../../../../stores/reverseManage'
let props = defineProps({
bookTask: undefined,
type: undefined,
selectBookTask: []
})
export default defineComponent({
components: { NForm, NFormItem, NButton, NIcon, NInput, NSelect, FolderOpen },
props: ['bookTask', 'type'],
setup(props) {
let bookTask = ref(props.bookTask) let bookTask = ref(props.bookTask)
let type = ref(props.type) let type = ref(props.type)
let backgroundMusicOptions = ref([]) let backgroundMusicOptions = ref([])
let message = useMessage() let message = useMessage()
let draftSelect = ref([])
let reverseManageStore = useReverseManageStore()
onMounted(async () => { onMounted(async () => {
// 稿
window.api.getDraftFileList((value) => {
value.forEach((element) => {
let obj = {
label: element,
value: element
}
draftSelect.value.push(obj)
})
})
// //
await window.api.GetBackgroundMusicConfigList((value) => { await window.api.GetBackgroundMusicConfigList((value) => {
if (value.code == 0) { if (value.code == 0) {
@ -152,46 +183,60 @@ export default defineComponent({
* 保存数据 * 保存数据
*/ */
async function SaveVideoData() { 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, { let res = await window.db.UpdateBookTaskData(bookTask.value.id, {
srtPath: bookTask.value.srtPath, srtPath: bookTask.value.srtPath,
audioPath: bookTask.value.audioPath, audioPath: bookTask.value.audioPath,
backgroundMusic: bookTask.value.backgroundMusic, backgroundMusic: bookTask.value.backgroundMusic,
friendlyReminder: bookTask.value.friendlyReminder, friendlyReminder: bookTask.value.friendlyReminder,
draftSrtStyle: bookTask.value.draftSrtStyle draftSrtStyle: bookTask.value.draftSrtStyle,
draftDepend: bookTask.value.draftDepend
}) })
if (res.code == 0) { if (res.code == 0) {
message.error('保存小说批次数据失败') message.error(res.message)
} else { } else {
message.success('保存小说批次数据成功') message.success('保存小说批次数据成功')
} }
} else {
message.error('未知的操作类型,请检查')
}
} }
/** /**
* 添加草稿 * 添加草稿
*/ */
async function AddJianyingDraft() { 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) let res = await window.book.AddJianyingDraft(bookTask.value.id, OperateBookType.BOOKTASK)
window.api.showGlobalMessageDialog(res) window.api.showGlobalMessageDialog(res)
} } else {
message.error('未知的操作类型,请检查')
return { 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'] }
]
}
} }
} }
let rules = ref({
srtPath: [{ required: true, message: '请选择背景音乐', trigger: ['input', 'blur', 'change'] }],
audioPath: [{ required: true, message: '请选择音频文件', trigger: ['input', 'blur', 'change'] }]
}) })
</script> </script>

View File

@ -1,21 +1,21 @@
<template> <template>
<div style="margin-bottom: 5px; margin-left: 5px; display: flex; align-items: center"> <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
> >
<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
> >
<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
> >
<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
> >
<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
> >
<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 >一键删除</n-button
> >
</div> </div>
@ -26,11 +26,13 @@
:data="reverseManageStore.bookTaskData" :data="reverseManageStore.bookTaskData"
:scroll-x="800" :scroll-x="800"
:row-props="rowProps" :row-props="rowProps"
:row-key="rowKey"
@update:checked-row-keys="handleCheck"
/> />
</div> </div>
</template> </template>
<script> <script setup>
import { ref, onMounted, defineComponent, onUnmounted, toRaw, watch, h } from 'vue' import { ref, onMounted, defineComponent, onUnmounted, toRaw, watch, h } from 'vue'
import { useMessage, useDialog, NButton, NDataTable, NIcon, NWatermark } from 'naive-ui' import { useMessage, useDialog, NButton, NDataTable, NIcon, NWatermark } from 'naive-ui'
import { useReverseManageStore } from '../../../../stores/reverseManage' import { useReverseManageStore } from '../../../../stores/reverseManage'
@ -42,21 +44,15 @@ import { OperateBookType } from '../../../../define/enum/bookEnum'
import { DEFINE_STRING } from '../../../../define/define_string' import { DEFINE_STRING } from '../../../../define/define_string'
import { ResponseMessageType } from '../../../../define/enum/softwareEnum' import { ResponseMessageType } from '../../../../define/enum/softwareEnum'
import AddBookTask from './Components/ManageBook/AddBookTask.vue' import AddBookTask from './Components/ManageBook/AddBookTask.vue'
import ManageBookTaskGenerateInformation from './Components/ManageBook/ManageBookTaskGenerateInformation.vue'
export default defineComponent({
components: {
NButton,
NDataTable,
BookTaskListAction,
NWatermark
},
setup() {
let reverseManageStore = useReverseManageStore() let reverseManageStore = useReverseManageStore()
let softwareStore = useSoftwareStore() let softwareStore = useSoftwareStore()
let dialog = useDialog() let dialog = useDialog()
const router = useRouter() const router = useRouter()
let message = useMessage() let message = useMessage()
let columns = createColumns()
const checkedRowKeysRef = ref([])
onMounted(async () => { onMounted(async () => {
window.api.setEventListen([DEFINE_STRING.BOOK.MAIN_DATA_RETURN], (value) => { window.api.setEventListen([DEFINE_STRING.BOOK.MAIN_DATA_RETURN], (value) => {
if (value.code == 0) { if (value.code == 0) {
@ -73,8 +69,19 @@ export default defineComponent({
window.api.removeEventListen([DEFINE_STRING.BOOK.MAIN_DATA_RETURN]) window.api.removeEventListen([DEFINE_STRING.BOOK.MAIN_DATA_RETURN])
}) })
function rowKey(row) {
return row.id
}
function handleCheck(rowKeys) {
checkedRowKeysRef.value = rowKeys
}
function createColumns() { function createColumns() {
return [ return [
{
type: 'selection'
},
{ {
title: 'No.', title: 'No.',
key: 'no', key: 'no',
@ -105,7 +112,6 @@ export default defineComponent({
width: 215, width: 215,
fixed: 'right', fixed: 'right',
render(row, index) { render(row, index) {
return h(BookTaskListAction, { return h(BookTaskListAction, {
bookTask: row bookTask: row
}) })
@ -133,13 +139,17 @@ export default defineComponent({
* 一键高清全部 * 一键高清全部
*/ */
async function HDImageAll() { async function HDImageAll() {
alert('高清全部') console.log('一键高清', checkedRowKeysRef.value)
if (checkedRowKeysRef.value.length == 0) {
message.error('请选择要高清的数据')
return
}
softwareStore.spin.spinning = true softwareStore.spin.spinning = true
softwareStore.spin.tip = '正在进行高清检查。。。' softwareStore.spin.tip = '正在进行高清检查。。。'
let checkImage = await window.book.CheckImageFileSize( let checkImage = await window.book.CheckImageFileSize(
reverseManageStore.selectBook.id, [...checkedRowKeysRef.value],
10240, 10240,
OperateBookType.BOOK OperateBookType.ASSIGNBOOKTASK
) )
softwareStore.spin.spinning = false softwareStore.spin.spinning = false
if (checkImage.code == 0) { if (checkImage.code == 0) {
@ -159,12 +169,12 @@ export default defineComponent({
negativeText: '取消', negativeText: '取消',
onPositiveClick: async () => { onPositiveClick: async () => {
alert('确认高清') alert('确认高清')
await hdImageFunc(reverseManageStore.selectBook.id, OperateBookType.BOOK) await hdImageFunc([...checkedRowKeysRef.value], OperateBookType.ASSIGNBOOKTASK)
} }
}) })
} else { } else {
// //
await hdImageFunc(reverseManageStore.selectBook.id, OperateBookType.BOOK) await hdImageFunc([...checkedRowKeysRef.value], OperateBookType.ASSIGNBOOKTASK)
} }
} }
@ -182,7 +192,22 @@ export default defineComponent({
* 一键生成草稿 * 一键生成草稿
*/ */
async function DraftAll() { async function DraftAll() {
alert('草稿全部') 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]
})
})
} }
/** /**
@ -205,25 +230,15 @@ export default defineComponent({
async function DeleteAll() { async function DeleteAll() {
alert('删除全部') alert('删除全部')
} }
const renderIcon = () => {
return {
reverseManageStore,
AddBookDialog,
HDImageAll,
DraftAll,
VideoAll,
ResetAll,
DeleteAll,
columns: createColumns(),
renderIcon() {
return h(NIcon, null, { return h(NIcon, null, {
default: () => h(AddSharp, { size: '40', color: 'white' }, {}) default: () => h(AddSharp, { size: '40', color: 'white' }, {})
}) })
}, }
rowProps: (row) => { const rowProps = (row) => {
return { return {
style: 'cursor: pointer;', style: 'cursor: pointer;',
onClick: async () => { ondblclick: async () => {
// message.info(row.id) // message.info(row.id)
reverseManageStore.selectBookTask = row reverseManageStore.selectBookTask = row
softwareStore.spin.spinning = true softwareStore.spin.spinning = true
@ -233,7 +248,4 @@ export default defineComponent({
} }
} }
} }
}
}
})
</script> </script>

View File

@ -1,7 +1,7 @@
<template> <template>
<div style="height: 100%"> <div style="height: 100%">
<ManageBook style="height: 100%" v-if="softwareStore.softWare.reverse_display_show" /> <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 #1> <ManageBook style="height: 100%" /> </template>
<template #2> <template #2>
<ManageBookTask style="height: 100%" /> <ManageBookTask style="height: 100%" />

View File

@ -46,14 +46,24 @@
<template #unchecked> </template> <template #unchecked> </template>
</n-switch> </n-switch>
</n-form-item> </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 <n-select
size="small" size="small"
placeholder="请选择" placeholder="请选择"
:options="character_select_model_options" :options="character_select_model_options"
v-model:value="formValue.character_select_model" v-model:value="formValue.character_select_model"
style="width: 120px" style="width: 120px"
/> >
</n-select>
</n-form-item> </n-form-item>
<n-form-item style="margin-left: 20px" <n-form-item style="margin-left: 20px"
><n-checkbox v-model:checked="formValue.window_wh_bm_remember" ><n-checkbox v-model:checked="formValue.window_wh_bm_remember"
@ -212,12 +222,27 @@ export default defineComponent({
theme: softwareStore.globalSetting.theme, theme: softwareStore.globalSetting.theme,
character_select_model: window.config.character_select_model, character_select_model: window.config.character_select_model,
window_wh_bm_remember: window.config.window_wh_bm_remember, 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 show = ref(false)
let gpt_options = ref([]) let gpt_options = ref([])
let gpt_model_options = ref([]) let gpt_model_options = ref([])
let character_select_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 dialog = useDialog()
let loading = ref(false) let loading = ref(false)
/** /**
@ -472,6 +497,7 @@ export default defineComponent({
AddGptPrompt, AddGptPrompt,
character_select_model_options, character_select_model_options,
softwareStore, softwareStore,
hdSelectOptions,
ModifyTranslateSetting, ModifyTranslateSetting,
laiApiOptions: [ laiApiOptions: [
{ {