LaiTool/src/main/index.js
lq1405 82ec437b5d v 3.4.2
1. 新增 图/文转视频 菜单界面,专注实现图/文转视频(目前只集成了 MJ VIDEO)
  1. 全新的界面排列,小说列表和批次任务更加分明
  2. 添加转视频进度,在主界面即可看到转视频的比例
  3. 单独的界面去处理图转视频,避免表格数据过多繁琐
  4. 新增分页显示,界面加载更快,也可切换不分页,需要更多的事件等待加载
  5. 单独操作面板,参数修改处理更加清晰,支持多种模式显示,右侧固定或抽屉模式
  6. 批量设置转视频配置,可以批量修改分类
  7. 友好的选择视频界面
2. 重写 软件导出剪映,修复若干草稿导出问题
  1. 修复导出剪映文案和图片对齐会有些许对不上,时长越长越明显
  2. 修复导出草稿关键帧部分问题
  3. 导出的文案通过分镜自动导入,不再需要手动选择SRT
3. 美化 生成草稿界面 弹窗,优化部分逻辑
  1. 删除选择SRT文件,SRT根据聚合推文中导入的SRT自动生成草稿
  2. 只需选择配音文件即可,配音文件和导入的SRT请自行对应
  3. 背景音乐不在内部设置,自行选择文件夹或者是MP3、WAV文件
  4. 背景音乐选择文件夹则读取文件夹,随机获取一个
  5. 背景音乐选择指定的音乐文件则使用选择的
2025-08-09 18:46:07 +08:00

463 lines
14 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import fspromises from 'fs/promises'
import { v4 as uuidv4 } from 'uuid'
import { app, shell, BrowserWindow, ipcMain, dialog, nativeTheme, session } from 'electron'
import path, { join } from 'path'
import { electronApp, optimizer, is } from '@electron-toolkit/utils'
import icon from '../../resources/icon.ico?asset'
import { define } from '../define/define'
import { func } from './func.js'
import { AsyncQueue } from './quene'
import { DEFINE_STRING } from '../define/define_string'
import { Tools } from './tools.js'
import { ImageGenerate } from './ReverseManage/imageGenerate.js'
import { Setting } from './setting/setting'
import { has, isEmpty } from 'lodash'
import { AutoSync } from './setting/autoSync.js'
import { SoftWareServiceBasic } from './Service/ServiceBasic/softwareServiceBasic'
// ipc
import { DiscordIpc, RemoveDiscordIpc } from './IPCEvent/discordIpc.js'
import { Logger } from './logger.js'
import { RegisterIpc } from './IPCEvent/index'
import { InitRemoteMjSettingType, InitData as a } from './initFunc'
let tools = new Tools()
let imageGenerate = new ImageGenerate(global)
let setting = new Setting(global)
let softWareServiceBasic = new SoftWareServiceBasic()
async function InitData(gl) {
await InitRemoteMjSettingType()
await a()
let res = await setting.getSettingDafultData()
gl.config = res
return res
}
function setIpcHandler(hash) {
if (hash == 'discord') {
DiscordIpc(global)
}
}
function removeIpcHandler(hash) {
if (hash == 'discord') {
RemoveDiscordIpc()
}
}
async function createWindow(hash = 'mainHome', data, url = null) {
// Create the browser window.
await InitData(global)
global.currentHash = hash
// 判断当前是不是有设置的宽高,用的话记忆
let isRe = global.config.window_wh_bm_remember && hash == 'mainHome' && global.config.window_wh_bm
const ses = session.fromPartition('persist:my-session')
let mainWindow = new BrowserWindow({
width: isRe ? global.config.window_wh_bm.width : 900,
height: isRe ? global.config.window_wh_bm.height : 675,
x: isRe ? global.config.window_wh_bm.x : 100,
y: isRe ? global.config.window_wh_bm.y : 100,
title: 'LAITool',
icon: '../../resources/icon.ico',
show: false,
autoHideMenuBar: true,
...(process.platform === 'linux' ? { icon } : {}),
webPreferences: {
preload: join(__dirname, '../preload/index.js'),
sandbox: false,
nodeIntegration: true, // 在网页中集成Node
nodeIntegrationInWorker: true,
webSecurity: false,
partition: 'persist:my-partition',
session: ses,
webviewTag: true
}
})
mainWindow.myID = uuidv4()
mainWindow.on('ready-to-show', () => {
mainWindow.show()
})
mainWindow.webContents.setWindowOpenHandler((details) => {
shell.openExternal(details.url)
return { action: 'deny' }
})
// HMR for renderer base on electron-vite cli.
// Load the remote URL for development or the local html file for production.
// 判断是不是加载外部网页
if (url) {
mainWindow.loadURL(url)
} else {
if (is.dev && process.env['ELECTRON_RENDERER_URL']) {
mainWindow.loadURL(process.env['ELECTRON_RENDERER_URL'] + '/#/' + hash)
// mainWindow.webContents.openDevTools()
process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'
} else {
if (hash != '') {
mainWindow.loadURL(`file://${path.join(__dirname, '../renderer/index.html')}#/${hash}`)
} else {
mainWindow.loadFile(join(__dirname, '../renderer/index.html'))
}
}
}
mainWindow.on('close', async () => {
// 判断指定的窗口,移除指定的监听
removeIpcHandler(hash)
global.newWindow = global.newWindow.filter((item) => item.id != mainWindow.id)
// 判断当前的是不是开启了记录功能
if (global.config.window_wh_bm_remember && hash == 'mainHome') {
let window_wh_bm = mainWindow.getBounds()
// 记录到文件中
await setting.ModifySampleSetting(JSON.stringify({ window_wh_bm: window_wh_bm }))
await softWareServiceBasic.UpdateSoftware({
window_wh_bm: JSON.stringify(window_wh_bm)
})
}
})
// 创建一个新的窗口,添加对应的监听
global.newWindow.push({
hash,
id: mainWindow.id,
win: mainWindow,
init_folder: data
})
setIpcHandler(hash)
return mainWindow
}
let mainWindow
global.createWindow = createWindow
// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.whenReady().then(async () => {
// Set app user model id for windows
electronApp.setAppUserModelId('com.electron')
// Default open or close DevTools by F12 in development
// and ignore CommandOrControl + R in production.
// see https://github.com/alex8088/electron-toolkit/tree/master/packages/utils
app.on('browser-window-created', (_, window) => {
optimizer.watchWindowShortcuts(window)
})
//判断是不是又配置文件没有的话将temp中的基础配置加载
// 判断文件夹是不是存在
let config_p = path.dirname(define.config_path)
let isE = await tools.checkExists(config_p)
// 文件夹存在判断json文件数量
let ex_json = []
if (isE) {
let ex_json_path = await tools.getFilesWithExtensions(config_p, '.json')
for (let i = 0; i < ex_json_path.length; i++) {
const element = ex_json_path[i]
ex_json.push(path.basename(element))
}
} else {
await fspromises.mkdir(config_p, { recursive: true })
}
// 判断文件是不是存在,不存在添加
if (ex_json.length != 5) {
let temp_path = path.join(path.dirname(define.draft_temp_path), 'config')
let tmp_json_path = await tools.getFilesWithExtensions(temp_path, '.json')
for (let i = 0; i < tmp_json_path.length; i++) {
const element = tmp_json_path[i]
if (!ex_json.includes(path.basename(element))) {
await fspromises.copyFile(element, path.join(config_p, path.basename(element)))
}
}
}
// 判断动态文件是不是存在
tools.checkJsonFileExistsOrCreate(path.normalize(define.dynamic_setting))
// 判断标签文件是不是存在
tools.checkJsonFileExistsOrCreate(
path.normalize(define.tag_setting),
JSON.stringify({
character_tags: [],
style_tags: [],
scene_tags: [],
prefix_tags: [],
suffix_tags: []
})
)
// 判断SD图片缓存文件是不是存在不存在创建
tools.checkFolderExistsOrCreate(path.normalize(define.temp_sd_image))
tools.checkFolderExistsOrCreate(path.normalize(path.join(define.image_path, 'c_s')))
app.on('activate', async function () {
// On macOS it's common to re-create a window in the app when the
// dock icon is clicked and there are no other windows open.
if (BrowserWindow.getAllWindows().length === 0) {
mainWindow = createWindow('mainHome', null)
}
})
global.logger = new Logger(define.logger_path)
// 同步数据
await AutoSync()
global.newWindow = []
mainWindow = await createWindow('mainHome', null)
global.requestQuene = new AsyncQueue(global, global.config.task_number)
global.fileQueue = new AsyncQueue(global, 1)
})
// Quit when all windows are closed, except on macOS. There, it's common
// for applications and their menu bar to stay active until the user quits
// explicitly with Cmd + Q.
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit()
}
})
RegisterIpc(createWindow)
ipcMain.handle('dark-mode:toggle', (event, value) => {
if (value) {
nativeTheme.themeSource = value
} else {
nativeTheme.themeSource = 'system'
}
return nativeTheme.shouldUseDarkColors
})
// In this file you can include the rest of your app"s specific main process
// code. You can also put them in separate files and require them here.
ipcMain.handle(DEFINE_STRING.GET_SETTING_Dafault_DATA, async (event) => {
return await InitData(global)
})
ipcMain.handle(DEFINE_STRING.GET_DRAFT_FILE_LIST, async (event) => {
let res = await func.getDraftFileList()
return res
})
ipcMain.handle(DEFINE_STRING.SELECT_FOLDER, async (event, value = null) => {
let po = ['openDirectory']
if (value && !isEmpty(value.multi)) {
po.push('multiSelections')
}
let { filePaths } = await dialog.showOpenDialog({
properties: po,
defaultPath: value && !isEmpty(value.defaultPath) ? value.defaultPath : ''
})
return filePaths
})
ipcMain.handle(DEFINE_STRING.SELECT_FILE, async (event, value) => {
try {
let { filePaths } = await dialog.showOpenDialog({
properties: ['openFile'],
filters: [{ name: 'fileName', extensions: value }]
})
return {
code: 1,
value: filePaths[0]
}
} catch (error) {
return {
code: 0,
message: `Error Message ${error}`
}
}
})
ipcMain.handle(DEFINE_STRING.GET_DRAFT_TEXT_STYLE, async (event, value) => {
let res = await func.getDraftTextStyle(value)
return res
})
ipcMain.handle(DEFINE_STRING.GET_TEXT_STYLE_LIST, async (event) => {
let res = await func.getClipSetting('text_style')
return res
})
ipcMain.handle(DEFINE_STRING.GET_FRIENDLY_REMINDER_LIST, async (event) => {
let res = await func.getClipSetting('friendly_reminder_setting')
return res
})
ipcMain.handle(DEFINE_STRING.GET_FRIENDLY_REMINDER_DRAFT, async (event, value) => {
let res = await func.GetDraftFriendlyReminder(value)
return res
})
ipcMain.handle(DEFINE_STRING.ADD_DRAFT, async (event, value) => {
let res = await func.addDraft(value)
return res
})
// 监听保存SD配置
ipcMain.handle(DEFINE_STRING.SAVE_SD_CONFIG, async (event, value) => await func.SaveSDConfig(value))
// 监听保存生成视频的简单配置
ipcMain.handle(
DEFINE_STRING.SAVE_GENERAL_SETTING,
async (event, value) => await func.SaveGeneralSetting(value)
)
// 获取当前的视频合成配置信息
ipcMain.handle(
DEFINE_STRING.GET_VIDEO_CONFIG_MESSAGE,
async (event) => await func.GetVideoConfigMessage()
)
// 监听保存字幕的是指信息
ipcMain.handle(
DEFINE_STRING.SAVE_ASS_CONFIG,
async (event, value) => await func.SaveAssConfig(value)
)
// 监听获取当前系统安装的字体
ipcMain.handle(
DEFINE_STRING.GET_SYSTEM_INSTALL_FONTNAME,
async (event) => await func.GetSystemInstallFontName()
)
// 监听删除视频配置任务删除指定ID的值
ipcMain.handle(
DEFINE_STRING.DELETE_VIDEO_CONFIG,
async (event, value) => await func.DeleteVideoConfig(value)
)
// 监听添加生图任务信息
ipcMain.handle(
DEFINE_STRING.ADD_IMAGE_TASK_LIST,
async (event, value) => await func.AddImageTask(value)
)
// 监听删除生成图片列表中的信息
ipcMain.handle(
DEFINE_STRING.DELETE_IMAGE_TASK_LIST,
async (event, value) => await func.DeleteImageTaskList(value)
)
// 监听获取不想要的提示词任务
ipcMain.handle(DEFINE_STRING.GET_BAD_PROMPT, async (event) => await func.GetBadPrompt())
// 保存不想要的提示词
ipcMain.handle(
DEFINE_STRING.SAVE_BAD_PROMPT,
async (event, value) => await func.SaveBadPrompt(value)
)
// 一键删除不想要的值
ipcMain.handle(DEFINE_STRING.DELETE_BAD_PROMPT, async (event) => await func.DeleteBadPrompt())
// 监听反推任务
ipcMain.handle(DEFINE_STRING.PUSH_BACK_PROMPT, async (event) => await func.PushBackPrompt())
// 打开GPT购买界面
ipcMain.on(DEFINE_STRING.OPEN_GPT_BUY_URL, async (event, value) => await func.openGptBuyUrl(value))
// 监听打开任意网站的任务
ipcMain.on(DEFINE_STRING.OPEN_URL, async (event, value) => await func.OpenUrl(value))
// 监听抽帧任务
ipcMain.handle(DEFINE_STRING.GET_FRAME, async (event, value) => await func.getFrame(value))
// 监听获取ADtailer列表的任务
ipcMain.handle(DEFINE_STRING.GET_ADETAILER_LIST, async (event) => await func.GetADetailerList())
// 监听保存ADtailer数据
ipcMain.handle(
DEFINE_STRING.SAVE_DETAILER_CONFIG,
async (event, value) => await func.SaveADetailerConfig(value)
)
// 分镜识别。语音识别
ipcMain.handle(
DEFINE_STRING.START_STORY_BOARDING,
async (event, value) => await func.StartStoryboarding(value)
)
// 保存试用结束时间
ipcMain.on(DEFINE_STRING.SAVE_TRIAL_END_TIME, (event, value) => {
global.endTime = value.endTime
global.permissions = value.permissions
})
// 获取当前机器的权限
ipcMain.handle(DEFINE_STRING.GET_PERMISSION, async (event) => {
return {
code: 1,
endTime: global.endTime,
permissions: global.permissions
}
})
// 监听字幕的保存
ipcMain.handle(DEFINE_STRING.SAVE_NEW_WORD, async (event, value) => {
return await func.SaveNewWord(value)
})
/**
* 监听字幕对齐任务,通过前端传过来的数据
*/
ipcMain.handle(DEFINE_STRING.ALIGN_DRAFT_IMG_TO_TEXT, async (event, value) => {
return await func.alginDraftImgToText(value)
})
/**
* 监听单张重绘任务
*/
ipcMain.handle(DEFINE_STRING.RE_GENERATE_IAMGE_ONE, async (event, value) => {
console.log(value[0])
let newWindow = global.newWindow.filter((item) => item.id == value[0])
let res = await func.ReGenerateImageOne(newWindow, value)
return res
})
/**
* 监听文件夹高清任务
*/
ipcMain.handle(DEFINE_STRING.IMPROVE_IMAGE_RESOULTION, async (event, value) => {
let res = await imageGenerate.ImproveResolution(value)
return res
})
/**
* 监听刷新窗口的事件
*/
ipcMain.handle(DEFINE_STRING.REFRASH_IMAGWE_DATA, async (event, value) => {
console.log(value)
let newWindow = global.newWindow.filter((item) => item.id == value[0])
let img_dir = path.join(global.config.project_path, 'tmp/' + value[1])
let res = await imageGenerate.getFolderImageList(newWindow, img_dir, true)
return res
})
/**
* 监听一个窗口,返回窗口创建成功后需要的基础数据
*/
ipcMain.on(DEFINE_STRING.SHOW_NEW_WINDOW, async (event, value) => {
let newW = createWindow(value[0], value[1])
})
// 监听程序关闭
ipcMain.on(DEFINE_STRING.QUIT_APP, (event) => {
app.quit()
})
/**
* 监听图片对齐任务
*/
ipcMain.on(DEFINE_STRING.ALIGN_DRAFT_IMG, async (event, value) => {
func.alignDraftImage(mainWindow, value)
})