Initial commit 添加MJ功能
This commit is contained in:
@@ -0,0 +1,108 @@
|
||||
import { ipcMain } from "electron";
|
||||
import { DEFINE_STRING } from "../../define/define_string";
|
||||
import { DiscordSimple } from "../discord/discordSimple";
|
||||
import { DiscordRequestListener } from "../discord/discordRequestLister";
|
||||
|
||||
/**
|
||||
* 添加discord的ipc事件
|
||||
* @param {*} createWindow
|
||||
*/
|
||||
function DiscordIpc(global) {
|
||||
let windows = global.newWindow.filter(item => item.hash === 'discord');
|
||||
if (windows.length <= 0) {
|
||||
throw new Error("Discord窗口未找到");
|
||||
}
|
||||
let thisWindow = windows[0];
|
||||
|
||||
// 创建网络请求人物监听器
|
||||
if (!thisWindow.win.RequestListener) {
|
||||
thisWindow.win.RequestListener = new DiscordRequestListener(thisWindow.win);
|
||||
thisWindow.win.RequestListener.start();
|
||||
}
|
||||
|
||||
WinddowUrlRefresh(thisWindow);
|
||||
GetSessionAuthorization(thisWindow);
|
||||
|
||||
// 加载内部执行的方法
|
||||
let discordSimple = new DiscordSimple(thisWindow.win);
|
||||
|
||||
// 获取discord窗口的url
|
||||
ipcMain.handle(DEFINE_STRING.DISCORD.GET_DISCORD_WINDOW_URL, async (event, value) => {
|
||||
// 调用指定的窗口,获取数据(相关的数据)
|
||||
return thisWindow.win.webContents.getURL();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除所有的DiscordIpc事件
|
||||
*/
|
||||
function RemoveDiscordIpc() {
|
||||
for (const key in DEFINE_STRING.DISCORD) {
|
||||
if (Object.hasOwnProperty.call(DEFINE_STRING.DISCORD, key)) {
|
||||
const element = DEFINE_STRING.DISCORD[key];
|
||||
ipcMain.removeHandler(element);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 添加一个初始化的方式,获取数据或者时写入数据
|
||||
|
||||
// 监听新增的url刷新事件
|
||||
function WinddowUrlRefresh(thisWindow) {
|
||||
thisWindow.win.webContents.on('did-navigate-in-page', (event, url) => {
|
||||
// 将当前的数据写道指定的界面中,对返回的数据进行分类
|
||||
// 判断是不是有channels。有的话就是获取到了数据
|
||||
let serviceID = null;
|
||||
let channelID = null;
|
||||
if (url.indexOf("/channels/") > 0) {
|
||||
const regex = /\/channels\/(\d+)\/(\d+)/;
|
||||
const match = url.match(regex);
|
||||
if (match && match[1] && match[2]) {
|
||||
serviceID = match[1];
|
||||
channelID = match[2];
|
||||
}
|
||||
|
||||
global.newWindow[0].win.webContents.send(DEFINE_STRING.DISCORD.OPERATE_REFRASH_DISCORD_URL, {
|
||||
code: 1,
|
||||
type: DEFINE_STRING.DISCORD_SIMPLE_DATA_TYPE.URL,
|
||||
data: {
|
||||
url: url,
|
||||
serviceID: serviceID,
|
||||
channelID: channelID
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
// 判断是不是需要登录(登录提示)
|
||||
if (url.indexOf('/login') > 0) {
|
||||
global.newWindow[0].win.webContents.send(DEFINE_STRING.SHOW_MAIN_NOTIFICATION, {
|
||||
code: 0,
|
||||
message: "请先登录Discord账号"
|
||||
});
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 获取session。Authorization,以及当当登录的User-Agent
|
||||
function GetSessionAuthorization(thisWindow) {
|
||||
thisWindow.win.RequestListener.AddRequestListener('GetSessionAuthorization', (details) => {
|
||||
if (details.url.indexOf("channels") > 0) {
|
||||
global.newWindow[0].win.webContents.send(DEFINE_STRING.DISCORD.OPERATE_REFRASH_DISCORD_URL, {
|
||||
code: 1,
|
||||
type: DEFINE_STRING.DISCORD_SIMPLE_DATA_TYPE.TOKEN,
|
||||
data: {
|
||||
authorization: details.requestHeaders.Authorization,
|
||||
userAgent: details.requestHeaders["User-Agent"],
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
export {
|
||||
DiscordIpc,
|
||||
RemoveDiscordIpc
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
import { ipcMain } from "electron";
|
||||
import { GPT } from "../Public/GPT";
|
||||
import { DEFINE_STRING } from '../../define/define_string'
|
||||
let gpt = new GPT(global);
|
||||
|
||||
function GptIpc() {
|
||||
|
||||
// 获取默认或者是自定义的GPT服务商
|
||||
ipcMain.handle(DEFINE_STRING.GET_GPT_BUSINESS_OPTION, async (event, value) => await gpt.GetGPTBusinessOption(value));
|
||||
|
||||
// 获取默认的或者是自定义的GPT推理模型
|
||||
ipcMain.handle(DEFINE_STRING.GET_GPT_MODEL_OPTION, async (event, value) => await gpt.GetGPTModelOption(value));
|
||||
|
||||
// 获取默认的提示词推理模式或者是自定义的提示词推理模式
|
||||
ipcMain.handle(DEFINE_STRING.GET_GPT_AUTO_INFERENCE_OPTIONS, async (event, value) => await gpt.GetGptAutoInferenceOptions(value));
|
||||
|
||||
// 保存自定义的GPT服务商数据
|
||||
ipcMain.handle(DEFINE_STRING.SAVE_DYNAMIC_GPT_OPTION, async (event, value) => await gpt.SaveDynamicGPTOption(value));
|
||||
|
||||
// 删除自定义的GPT服务商数据
|
||||
ipcMain.handle(DEFINE_STRING.DELETE_DYNAMIC_GPT_OPTION, async (event, value) => await gpt.DeleteDynamicGPTOption(value));
|
||||
|
||||
// 测试当前的GPT是不是可以链接成功
|
||||
ipcMain.handle(DEFINE_STRING.TEST_GPT_CONNECTION, async (event, value) => await gpt.TestGPTConnection(value));
|
||||
|
||||
// 自定义GPT推理提示词测试输出
|
||||
ipcMain.handle(DEFINE_STRING.GENERATE_GPT_EXAMPLE_OUT, async (event, value) => await gpt.GenerateGptExampleOut(value));
|
||||
|
||||
// 获取GPT推理词设置
|
||||
ipcMain.handle(DEFINE_STRING.GET_CUSTOMIZE_GPT_PROMPT, async (event, value) => await gpt.GetCustomizeGptPrompt(value));
|
||||
|
||||
// 监听自动分析人物事件
|
||||
ipcMain.handle(DEFINE_STRING.AUTO_ANALYZE_CHARACTER, async (event, value) => await gpt.AutoAnalyzeCharacter(value));
|
||||
|
||||
// GPT推理关键词
|
||||
ipcMain.handle(DEFINE_STRING.GPT_PROMPT, async (event, value) => await gpt.GPTPrompt(value));
|
||||
|
||||
// 监听洗稿任务
|
||||
ipcMain.handle(DEFINE_STRING.AIMODIFY_ONE_WORD, async (event, value) => await gpt.AIModifyOneWord(value));
|
||||
|
||||
}
|
||||
|
||||
export {
|
||||
GptIpc
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
import {
|
||||
ipcMain
|
||||
} from "electron";
|
||||
import { DEFINE_STRING } from '../../define/define_string'
|
||||
import {
|
||||
ImageGenerate
|
||||
} from '../backPrompt/imageGenerate'
|
||||
let imageGenerate = new ImageGenerate(global);
|
||||
|
||||
function ImageGenerateIpc() {
|
||||
// 监听获取任务列表
|
||||
ipcMain.handle(DEFINE_STRING.GET_GENERATE_TASK_LIST, async (event) => await imageGenerate.GetGenerateTaskList());
|
||||
|
||||
// 监听修改任务信息
|
||||
ipcMain.handle(DEFINE_STRING.MODIFY_IMAGE_TASK_LIST, async (event, value) => await imageGenerate.ModifyImageTaskList(value));
|
||||
|
||||
// 监听生成一次图片任务
|
||||
ipcMain.handle(DEFINE_STRING.GENERATE_IMAGWE_IN_SELECT_TASK, async (event, value) => await imageGenerate.GenerateImageInSelectTask(value));
|
||||
// 监听生成sd api json 的任务
|
||||
|
||||
ipcMain.handle(DEFINE_STRING.ADD_WEBUI_JSON, async (event) => await imageGenerate.AddWebuiJson());
|
||||
// 下载文件到指定位置
|
||||
|
||||
ipcMain.handle(DEFINE_STRING.DOWNLOAD_IMAGE_FILE, async (event, value) => await imageGenerate.DownloadImageFile(value));
|
||||
// 获取指定名字的prompt的配置文件
|
||||
|
||||
ipcMain.handle(DEFINE_STRING.GET_PROMPT_JSON, async (event, value) => await imageGenerate.GetPromptJson(value));
|
||||
// 修改后台队列任务的状态
|
||||
|
||||
ipcMain.handle(DEFINE_STRING.MODIFY_GENERATE_TASK_STATUS, async (event, value) => await imageGenerate.ModifyGenerateTaskStatus(value));
|
||||
// 监听获取基础数据
|
||||
|
||||
ipcMain.handle(DEFINE_STRING.GET_IAMGE_PROMPT_LIST, async (event) => {
|
||||
// 获取最新的那个窗口
|
||||
let w = global.newWindow[global.newWindow.length - 1];
|
||||
let res = await imageGenerate.getFolderImageList(w, w.init_folder, true);
|
||||
return res;
|
||||
})
|
||||
|
||||
// 监听修改配置json
|
||||
ipcMain.handle(DEFINE_STRING.MODIFY_INPUT_CROP_JSON, async (event, value) => await imageGenerate.ModifyInpurCropJson(value));
|
||||
|
||||
}
|
||||
|
||||
export {
|
||||
ImageGenerateIpc
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
import { ipcMain } from "electron";
|
||||
const { BrowserWindow, BrowserView } = require('electron')
|
||||
import { DEFINE_STRING } from "../../define/define_string";
|
||||
import { define } from "../../define/define";
|
||||
import { Tools } from "../tools";
|
||||
import { DiscordSimple } from "../discord/discordSimple";
|
||||
let tools = new Tools();
|
||||
|
||||
|
||||
function MainIpc(createWindow) {
|
||||
|
||||
// 添加打开discord窗口的事件
|
||||
ipcMain.handle(DEFINE_STRING.MAIN.OPEN_DISCORD_WINDOW, async (event) => {
|
||||
try {
|
||||
|
||||
let discordSimple = new DiscordSimple(null);
|
||||
discordSimple.CreateDiscordWindow(createWindow);
|
||||
|
||||
return {
|
||||
code: 1,
|
||||
message: "打开discord窗口成功"
|
||||
}
|
||||
} catch (error) {
|
||||
return {
|
||||
code: 0,
|
||||
message: "打开discord窗口失败"
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
export {
|
||||
MainIpc
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
import { ipcMain } from "electron"
|
||||
import { DEFINE_STRING } from "../../define/define_string"
|
||||
import { MjSimple } from "../discord/mjSimple"
|
||||
import { TagCustomize } from "../Original/TagCustomize"
|
||||
import { MJOriginalImageGenerate } from '../Original/MJOriginalImageGenerate'
|
||||
import { PublicMethod } from "../Public/publicMethod"
|
||||
import { DiscordSimple } from "../discord/discordSimple"
|
||||
import { Tools } from "../tools"
|
||||
import path from 'path'
|
||||
|
||||
let mjSimple = new MjSimple(global)
|
||||
let discordSimple = new DiscordSimple(null)
|
||||
let tagCustomize = new TagCustomize(global);
|
||||
let mJOriginalImageGenerate = new MJOriginalImageGenerate(global);
|
||||
let publicMethod = new PublicMethod(global);
|
||||
let tools = new Tools();
|
||||
|
||||
|
||||
function MjIpc() {
|
||||
// 监听保存mj的文案配置信息
|
||||
ipcMain.handle(DEFINE_STRING.MJ.SAVE_WORD_SRT, async (event, value) => await mjSimple.SvaeMJWordSrt(value));
|
||||
|
||||
// 监听获取MJ的文件配置信息
|
||||
ipcMain.handle(DEFINE_STRING.MJ.GET_MJ_CONFIG_SRT_INFORMATION, async (event, value) => await mjSimple.GetMJConfigSrtInformation(value));
|
||||
|
||||
// 监听获取标签数据
|
||||
ipcMain.handle(DEFINE_STRING.MJ.GET_TAG_DATA_BY_TYPE_AND_PROPERTY, async (event, value) => await tagCustomize.GetTagDataByTypeAndProperty(value));
|
||||
|
||||
// 保存指定的标签数据
|
||||
ipcMain.handle(DEFINE_STRING.MJ.SAVE_TAG_PROPERTY_DATA, async (event, value) => await tagCustomize.SaveTagPropertyData(value));
|
||||
|
||||
// 删除指定的标签数据
|
||||
ipcMain.handle(DEFINE_STRING.MJ.DELETE_TAG_PROPERTY_DATA, async (event, value) => await tagCustomize.DeleteTagPropertyData(value));
|
||||
|
||||
// MJ 原创生图
|
||||
ipcMain.handle(DEFINE_STRING.MJ.ORIGINAL_MJ_IMAGE_GENERATE, async (event, value) => await mJOriginalImageGenerate.OriginalMJImageGenerate(value));
|
||||
|
||||
// 获取discord的频道机器人
|
||||
ipcMain.handle(DEFINE_STRING.MJ.GET_CHANNEL_ROBOTS, async (event, value) => await mjSimple.GetChannelRobots(value));
|
||||
|
||||
// 获取MJ生图的方式
|
||||
// GetMJGenerateCategory: async (callback) => callback(await ipcRenderer.invoke(DEFINE_STRING.MJ.GET_MJ_GENERATE_CATEGORY)),
|
||||
ipcMain.handle(DEFINE_STRING.MJ.GET_MJ_GENERATE_CATEGORY, async (event) => await mjSimple.GetMJGenerateCategory());
|
||||
|
||||
// MJ生成的图片分割
|
||||
ipcMain.handle(DEFINE_STRING.MJ.IMAGE_SPLIT, async (event, value) => await mJOriginalImageGenerate.ImageSplit(value));
|
||||
|
||||
// 添加MJ敏感词
|
||||
ipcMain.handle(DEFINE_STRING.MJ.ADD_MJ_BAD_PROMPT, async (event, value) => await mjSimple.AddMJBadPrompt(value));
|
||||
|
||||
// 添加MJ敏感词检查
|
||||
ipcMain.handle(DEFINE_STRING.MJ.MJ_BAD_PROMPT_CHECK, async (event, value) => await mjSimple.MJBadPromptCheck(value));
|
||||
|
||||
// 获取已经生图完成的数据,并获取图片
|
||||
ipcMain.handle(DEFINE_STRING.MJ.GET_GENERATED_MJ_IMAGE_AND_SPLIT, async (event, value) => await mJOriginalImageGenerate.GetGeneratedMJImageAndSplit(value));
|
||||
|
||||
// 给图片链接,下载指定的图片并分割保存
|
||||
ipcMain.handle(DEFINE_STRING.MJ.DOWNLOAD_IMAGE_URL_AND_SPLIT, async (event, value) => await mJOriginalImageGenerate.DownloadImageUrlAndSplit(value));
|
||||
|
||||
/**
|
||||
* 监听DISCORD界面创建消息,并修改数据
|
||||
*/
|
||||
ipcMain.on(DEFINE_STRING.DISCORD.CREATE_MESSAGE, async (event, value) => await discordSimple.DiscordCreateMessage(value));
|
||||
|
||||
/**
|
||||
* 监听DISCORD界面的更新消息,并修改数据
|
||||
*/
|
||||
ipcMain.on(DEFINE_STRING.DISCORD.UPDATE_MESSAGE, async (event, value) => await discordSimple.DiscordUpdateMessage(value));
|
||||
|
||||
/**
|
||||
* 监听DISCORD界面的删除消息,并修改数据
|
||||
*/
|
||||
ipcMain.on(DEFINE_STRING.DISCORD.DELETE_MESSAGE, async (event, value) => await discordSimple.DiscordDeleteMessage(value));
|
||||
}
|
||||
|
||||
export {
|
||||
MjIpc
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
import { ipcMain } from "electron";
|
||||
import { DEFINE_STRING } from '../../define/define_string'
|
||||
import { OriginalImageGenerate } from "../Original/OriginalImageGenerate";
|
||||
let originalImageGenerate = new OriginalImageGenerate(global);
|
||||
|
||||
function OriginalImageGenerateIpc() {
|
||||
// 监听自动生成单个配置的文件
|
||||
ipcMain.handle(DEFINE_STRING.ORIGINAL_ADD_WEBUI_JSON, async (event, value) => await originalImageGenerate.OriginalAddWebuiJson(value));
|
||||
|
||||
// 监听自动保存数据到json文件
|
||||
ipcMain.handle(DEFINE_STRING.AUTO_SAVE_DATA_JSON, async (event, value) => await originalImageGenerate.AutoSaveDataJson(value));
|
||||
|
||||
// SD原创单张图片生成
|
||||
ipcMain.handle(DEFINE_STRING.ORIGINAL_SD_SINGLE_IMAGE_GENERATE, async (event, value) => await originalImageGenerate.OriginalSDImageGenerate(value));
|
||||
}
|
||||
|
||||
export {
|
||||
OriginalImageGenerateIpc
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
import { ipcMain } from "electron";
|
||||
import { DEFINE_STRING } from '../../define/define_string'
|
||||
import { SD } from "../Public/SD";
|
||||
let sd = new SD(global);
|
||||
|
||||
function SdIpc() {
|
||||
// 获取样式图片的子列表
|
||||
ipcMain.handle(DEFINE_STRING.GET_STYLE_IMAGE_SUB_LIST, async (event, value) => await sd.GetStyleImageSubList(value));
|
||||
|
||||
// 获取图片样式信息
|
||||
ipcMain.handle(DEFINE_STRING.GET_IMAGE_STYLE_INFOMATION, async (event, value) => await sd.GetImageStyleInfomation(value));
|
||||
|
||||
// 获取图片样式菜单
|
||||
ipcMain.handle(DEFINE_STRING.GET_IMAGE_STYLE_MENU, async (event) => await sd.GetImageStyleMenu());
|
||||
}
|
||||
export {
|
||||
SdIpc
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
const {
|
||||
ipcMain
|
||||
} = require("electron")
|
||||
import { DEFINE_STRING } from '../../define/define_string'
|
||||
import {
|
||||
Setting
|
||||
} from '../setting/setting'
|
||||
let setting = new Setting(global);
|
||||
|
||||
function SettingIpc() {
|
||||
|
||||
// 获取背景音乐配置列表
|
||||
ipcMain.handle(DEFINE_STRING.GET_BACKGROUND_MUSIC_CONFIG_LIST, async (event) => await setting.GetBackGroundMusicConfigList());
|
||||
|
||||
// 获取剪映关键帧配置列表
|
||||
ipcMain.handle(DEFINE_STRING.GET_KEYFRAME_OPTIONS, async (event) => await setting.GetKeyFrameOptions());
|
||||
// 保存剪映关键帧配置
|
||||
ipcMain.handle(DEFINE_STRING.SAVE_KEY_FRAME_SETTING, async (event, value) => await setting.SaveKeyFrameSetting(value));
|
||||
// 监听添加背景音乐文件
|
||||
ipcMain.handle(DEFINE_STRING.ADD_BACKGROUND_MUSIC_FOLDER, async (event, value) => await setting.AddBackgroundMusicFolder(value))
|
||||
// 删除剪映的样式设置
|
||||
ipcMain.handle(DEFINE_STRING.DELETE_DRAFT_TEXT_STYLE, async (event, value) => await setting.deleteClipSetting("text_style", value));
|
||||
|
||||
// 删除剪映的背景音乐设置
|
||||
ipcMain.handle(DEFINE_STRING.DELETE_CLIP_SETTING, async (event, value) => await setting.deleteClipSetting("background_music_setting", value));
|
||||
// 删除剪映的友情提示设置
|
||||
ipcMain.handle(DEFINE_STRING.DELETE_FRIENDLY_REMINDER, async (event, value) => await setting.deleteClipSetting("friendly_reminder_setting", value));
|
||||
|
||||
// 监听获取SD配置任务
|
||||
ipcMain.handle(DEFINE_STRING.INIT_SD_CONFIG, async (event, value) => await setting.InitSDConfig());
|
||||
|
||||
// 获取主页显示信息
|
||||
ipcMain.handle(DEFINE_STRING.GET_SHOW_MESSAGE, async (event) => await setting.GetShowMessage())
|
||||
// 获取关键帧的配置数据
|
||||
ipcMain.handle(DEFINE_STRING.GET_KEY_FRAME_CONFIG_DATA, async (event) => await setting.GetKeyFrameConfigData());
|
||||
|
||||
// 删除后台队列任务
|
||||
ipcMain.handle(DEFINE_STRING.DELETE_BACK_TASK, async (event, value) => await setting.RemoveTask(value));
|
||||
|
||||
// 获取自动保存图片的分类方式
|
||||
ipcMain.handle(DEFINE_STRING.GET_AUTO_SAVE_IMAGE_CLASSIFY_OPTIONS, async (event) => await setting.GetAutoSaveImageClassifyOptions());
|
||||
|
||||
// 保存图片自动保存的配置
|
||||
ipcMain.handle(DEFINE_STRING.SAVE_IMAGE_AUTO_SAVE_SETTING, async (event, value) => await setting.SaveImageAutoSaveSetting(value));
|
||||
|
||||
// 获取当前的自动保存图片的设置
|
||||
ipcMain.handle(DEFINE_STRING.GET_IMAGE_AUTO_SAVE_SETTING, async (event) => await setting.GetImageAutoSaveSetting());
|
||||
|
||||
// 开始手动保存图片
|
||||
ipcMain.handle(DEFINE_STRING.SAVE_IMAGE_TO_OTHER_FOLDER, async (event, value) => await setting.SaveImageToOtherFolder(value));
|
||||
// 检查机器码是否存在
|
||||
ipcMain.handle(DEFINE_STRING.CHECK_MACHINE_ID, async (event, value) => await setting.CheckMachineId(value));
|
||||
|
||||
//修改剪映草稿配置
|
||||
ipcMain.handle(DEFINE_STRING.MODIFY_SAMPLE_SETTING, async (event, value) => await setting.ModifySampleSetting(value));
|
||||
|
||||
// 获取选择角色场景模式的options
|
||||
ipcMain.handle(DEFINE_STRING.MJ.GET_TAG_SELECT_MODEL, async (event) => await setting.GetRoleSceneModeOptions());
|
||||
|
||||
// 获取当前生成图片的生图方式(sd,mj,d3)
|
||||
ipcMain.handle(DEFINE_STRING.GET_IMAGE_GENERATE_CATEGORY, async (event) => await setting.GetImageGenerateCategory());
|
||||
|
||||
// // 获取指定的配置文件里面指定的属性的数据
|
||||
ipcMain.handle(DEFINE_STRING.GET_DEFINE_CONFIG_JSON_BY_PROPERTY, async (event, value) => await setting.GetDefineConfigJsonByProperty(value))
|
||||
|
||||
// // 保存指定的配置文件里面指定的属性的数据
|
||||
ipcMain.handle(DEFINE_STRING.SAVE_DEFINE_CONFIG_JSON_BY_PROPERTY, async (event, value) => await setting.SaveDefineConfigJsonByProperty(value))
|
||||
}
|
||||
|
||||
export {
|
||||
SettingIpc
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
import { ipcMain } from "electron";
|
||||
import { DEFINE_STRING } from '../../define/define_string'
|
||||
import { Translate } from '../Public/Translate'
|
||||
let translate = new Translate(global);
|
||||
|
||||
function TranslateIpc() {
|
||||
// 监听添加任务到翻译队列的任务
|
||||
ipcMain.handle(DEFINE_STRING.TRANSLATE_PROMPT, async (event, value) => await translate.TranslatePrompt(value));
|
||||
|
||||
// 添加立即返回的翻译任务
|
||||
ipcMain.handle(DEFINE_STRING.TRANSLATE_RETURN_NOW, async (event, value) => await translate.TranslateReturnNow(value));
|
||||
|
||||
// 添加立即返回的翻译任务到队列中
|
||||
ipcMain.handle(DEFINE_STRING.MJ.TRANSLATE_RETURN_NOW_TASK, async (event, value) => await translate.TranslateReturnNowTask(value));
|
||||
}
|
||||
export {
|
||||
TranslateIpc
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
import { ipcMain } from "electron";
|
||||
import { DEFINE_STRING } from '../../define/define_string'
|
||||
import { VideoGenerate } from "../backPrompt/videoGenerate";
|
||||
let videoGenerate = new VideoGenerate(global);
|
||||
|
||||
function VideoGenerateIpc() {
|
||||
// 一键全自动前的条件检查
|
||||
ipcMain.handle(DEFINE_STRING.AUTO_CONDITION_CHECK, async (event, value) => await videoGenerate.CheckVideoGenerattionAllCondition(null, value, null));
|
||||
|
||||
// 开始全自动合成任务
|
||||
ipcMain.handle(DEFINE_STRING.ACTION_AUTO_VIDEO_TASK, async (event, value) => await videoGenerate.ActionAutoVideoTask(value));
|
||||
|
||||
// 获取设置好的配置
|
||||
ipcMain.handle(DEFINE_STRING.GET_VIDEO_GENERATE_CONFIG, async (event) => await videoGenerate.GetVideoGenerateConfig());
|
||||
|
||||
// 保存合成视频的基础配置
|
||||
ipcMain.handle(DEFINE_STRING.SAVE_VIDEO_SRT_AND_AUDIO_MESSAGE, async (event, value) => await videoGenerate.SaveVideoSrtAndAudioMessage(value));
|
||||
|
||||
// 监听自动和成视频任务
|
||||
ipcMain.handle(DEFINE_STRING.AUTO_GENERATION_VIDEO, async (event, value) => await videoGenerate.AutoGeneretionVideo(value));
|
||||
|
||||
// 获取子文件夹列表
|
||||
ipcMain.handle(DEFINE_STRING.GET_SUBFOLDER_LIST, async (event, value) => await videoGenerate.getSubFolderList(value));
|
||||
}
|
||||
export {
|
||||
VideoGenerateIpc
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
import {
|
||||
ipcMain
|
||||
} from "electron";
|
||||
import { DEFINE_STRING } from '../../define/define_string'
|
||||
import {
|
||||
Writing
|
||||
} from '../backPrompt/writing'
|
||||
let writing = new Writing(global);
|
||||
|
||||
function WritingIpc() {
|
||||
// 监听分镜时间的保存
|
||||
ipcMain.handle(DEFINE_STRING.SAVE_COPYWRITING_INFOMATION, async (event, value) => await writing.SaveCopywritingInformation(value));
|
||||
|
||||
// 监听获取当前项目下面的分镜文案
|
||||
ipcMain.handle(DEFINE_STRING.GET_PROJECT_WORD, async (event, value) => await writing.GetProjectWord());
|
||||
|
||||
// 获取config配置文件数据
|
||||
ipcMain.handle(DEFINE_STRING.GET_CONFIG_JSON, async (event, value) => await writing.GetConfigJson(value));
|
||||
|
||||
// 将指定的文案txt数组写入到指定的文件中
|
||||
ipcMain.handle(DEFINE_STRING.SAVE_WORD_TXT, async (event, value) => await writing.SaveWordTxt(value));
|
||||
|
||||
// 监听获取字幕时间
|
||||
ipcMain.handle(DEFINE_STRING.IMPORT_SRT_AND_GET_TIME, async (event, value) => await writing.ImportSrtAndGetTime(value))
|
||||
}
|
||||
export {
|
||||
WritingIpc
|
||||
}
|
||||
@@ -0,0 +1,287 @@
|
||||
import { DEFINE_STRING } from "../../define/define_string";
|
||||
import { AsyncQueue } from '../quene'
|
||||
import { PublicMethod } from '../Public/publicMethod'
|
||||
import { ImageStyleDefine } from '../../define/iamgeStyleDefine'
|
||||
import { DiscordSimple } from "../discord/discordSimple";
|
||||
import { DiscordWorker } from "../discord/discordWorker";
|
||||
import { Tools } from "../tools";
|
||||
import path from 'path'
|
||||
import sharp from 'sharp'
|
||||
import { define } from "../../define/define";
|
||||
import { AwesomeRegx } from "awesome-js";
|
||||
|
||||
/**
|
||||
* MJ原创生图的类
|
||||
*/
|
||||
export class MJOriginalImageGenerate {
|
||||
constructor(global) {
|
||||
this.global = global;
|
||||
this.pm = new PublicMethod(global);
|
||||
this.discordWorker = new DiscordWorker();
|
||||
this.tools = new Tools();
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载指定的图片地址并且分割
|
||||
* @param {*} value
|
||||
*/
|
||||
async DownloadImageUrlAndSplit(value) {
|
||||
try {
|
||||
value = JSON.parse(value);
|
||||
let element = value[0];
|
||||
let iamge_url = value[1];
|
||||
let image_path = "";
|
||||
if (value.length > 2) {
|
||||
image_path = value[2];
|
||||
} else {
|
||||
image_path = path.join(global.config.project_path, `data\\MJOriginalImage\\${element.id}.png`);
|
||||
}
|
||||
// 判断是不是一个链接
|
||||
const urlRegex = /^(http|https):\/\/[^ "]+$/;
|
||||
if (!urlRegex.test(iamge_url)) {
|
||||
throw new Error("指定的图片地址不是一个链接");
|
||||
}
|
||||
|
||||
// 这边开始下载对应的图片
|
||||
await this.tools.downloadFileUrl(iamge_url, image_path);
|
||||
|
||||
// 将下载的图片进行分割
|
||||
let split_res = await this.ImageSplit(JSON.stringify([image_path, element.name]));
|
||||
if (split_res.code == 0) {
|
||||
throw new Error(split_res.message);
|
||||
}
|
||||
|
||||
element.image_click = iamge_url;
|
||||
element.subImagePath = split_res.data.subImagePath;
|
||||
element.outImagePath = split_res.data.outImagePath;
|
||||
element['image_path'] = image_path
|
||||
|
||||
return {
|
||||
code: 1,
|
||||
data: element
|
||||
}
|
||||
} catch (error) {
|
||||
return {
|
||||
code: 0,
|
||||
message: "下载指定的图片地址并且分割错误,错误信息如下:" + error.message
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取已经生图完成的数据,并获取图片
|
||||
* @param {*} value
|
||||
* @returns
|
||||
*/
|
||||
async GetGeneratedMJImageAndSplit(value) {
|
||||
try {
|
||||
value = JSON.parse(value);
|
||||
let param = [];
|
||||
// 循环数据,直传需要的数据
|
||||
for (let i = 0; i < value.length; i++) {
|
||||
const element = value[i];
|
||||
param.push({
|
||||
id: element.id,
|
||||
image_id: element.mj_message.image_id,
|
||||
name: element.name,
|
||||
});
|
||||
}
|
||||
|
||||
// 判断窗口是不是开启
|
||||
let discordWin = await this.discordWorker.CheckDiscordWindowIsOpenAndLoad();
|
||||
// 执行采集图片的脚本
|
||||
// 开始写入
|
||||
let discordSimple = new DiscordSimple(discordWin);
|
||||
// 开始执行脚本
|
||||
let result = await discordSimple.ExecuteScript(define.discordScript, `GetGeneratedMJImageAndSplit(${JSON.stringify(param)})`);
|
||||
|
||||
let res = [];
|
||||
result = JSON.parse(result);
|
||||
// 将返回的数据进行分割
|
||||
for (let i = 0; i < result.length; i++) {
|
||||
const element = result[i];
|
||||
let image_path = path.join(global.config.project_path, `data\\MJOriginalImage\\${element.image_id}.png`);
|
||||
|
||||
|
||||
let ds = this.DownloadImageUrlAndSplit(JSON.stringify[element, element.result, image_path]);
|
||||
if (ds.code == 0) {
|
||||
throw new Error(ds.message);
|
||||
}
|
||||
|
||||
res.push(ds.data);
|
||||
}
|
||||
|
||||
// 全部分割完毕,返回
|
||||
return {
|
||||
code: 1,
|
||||
data: res
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
return {
|
||||
code: 0,
|
||||
message: "获取已经生图完成的数据,并获取图片错误,错误信息如下" + error.message
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MJ生成的图片分割
|
||||
async ImageSplit(value) {
|
||||
try {
|
||||
value = JSON.parse(value);
|
||||
let inputPath = value[0];
|
||||
let r_name = value[1];
|
||||
let outputDir = path.join(this.global.config.project_path, `data\\MJOriginalImage`);
|
||||
const metadata = await sharp(inputPath).metadata();
|
||||
const smallWidth = metadata.width / 2;
|
||||
const smallHeight = metadata.height / 2;
|
||||
let times = new Date().getTime();
|
||||
let imgs = [];
|
||||
let first_p = path.join(this.global.config.project_path, `tmp\\output_crop_00001\\${r_name}`);
|
||||
|
||||
for (let i = 0; i < 4; i++) {
|
||||
const xOffset = i % 2 === 0 ? 0 : smallWidth;
|
||||
const yOffset = Math.floor(i / 2) * smallHeight;
|
||||
let out_file = path.join(outputDir, `/${r_name}_${times}_${i}.png`);
|
||||
await sharp(inputPath)
|
||||
.extract({
|
||||
left: xOffset,
|
||||
top: yOffset,
|
||||
width: smallWidth,
|
||||
height: smallHeight
|
||||
})
|
||||
.resize(smallWidth, smallHeight)
|
||||
.toFile(out_file);
|
||||
|
||||
imgs.push(out_file);
|
||||
|
||||
// 将第一个图片复制一个到指定的位置
|
||||
if (i == 0) {
|
||||
await this.tools.copyFileOrDirectory(out_file, first_p);
|
||||
// 复制一份到input
|
||||
let input_p = path.join(this.global.config.project_path, `tmp\\input_crop\\${r_name}`);
|
||||
await this.tools.copyFileOrDirectory(out_file, input_p);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
code: 1,
|
||||
data: {
|
||||
subImagePath: imgs,
|
||||
outImagePath: first_p
|
||||
}
|
||||
}
|
||||
|
||||
} catch (err) {
|
||||
return {
|
||||
code: 0,
|
||||
message: "MJ图片切割错误,错误信息如下" + err.message
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* MJ 原创生图
|
||||
* @param {*} value
|
||||
*/
|
||||
async OriginalMJImageGenerate(value) {
|
||||
try {
|
||||
let data = value[0];
|
||||
if (value[1]) {
|
||||
data = JSON.parse(data);
|
||||
}
|
||||
let show_global_message = value[2];
|
||||
let batch = DEFINE_STRING.QUEUE_BATCH.MJ_ORIGINAL_GENERATE_IMAGE;
|
||||
|
||||
// 判断存放的文件夹是不是存在,不存在的话创建
|
||||
let outputDir = path.join(this.global.config.project_path, `data\\MJOriginalImage`);
|
||||
|
||||
await this.tools.checkFolderExistsOrCreate(outputDir);
|
||||
|
||||
let fileExist = await this.tools.checkExists(outputDir);
|
||||
if (!fileExist) {
|
||||
await this.tools.createDirectory(outputDir);
|
||||
}
|
||||
|
||||
// 检查this.global中是不是又mj队列,没有的话创建一个
|
||||
if (!this.global.mjGenerateQuene) {
|
||||
this.global.mjGenerateQuene = new AsyncQueue(this.global, 1, true);
|
||||
}
|
||||
|
||||
let style_ids = await this.pm.GetConfigJson(JSON.stringify(["image_style", []]), false);
|
||||
// let image_styles = await ImageStyleDefine.getImageStyleStringByIds(style_ids.data);
|
||||
|
||||
// 替换风格的逻辑
|
||||
let current_task = null;
|
||||
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
const element = data[i];
|
||||
let tasK_id = `${batch}_${element.name}_${element.id}`;
|
||||
|
||||
this.global.mjGenerateQuene.enqueue(async () => {
|
||||
try {
|
||||
this.global.mjGenerateQuene.setCurrentCreateItem(element)
|
||||
// 开始进行mj生图
|
||||
let prompt = element.prompt;
|
||||
current_task = element.name;
|
||||
// 判断窗口是不是开启
|
||||
|
||||
let discordW = await this.discordWorker.CheckDiscordWindowIsOpenAndLoad();
|
||||
|
||||
// 开始写入
|
||||
let discordSimple = new DiscordSimple(discordW);
|
||||
await discordSimple.WritePromptToInput(prompt);
|
||||
|
||||
// 发送命令完成(删除当前正在执行。开始下一个任务)
|
||||
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
|
||||
}, tasK_id, batch);
|
||||
}
|
||||
|
||||
// 判断该当前正在执行的人物队列数(小于设置的数量,开始一个任务)
|
||||
this.global.mjGenerateQuene.startNextTask();
|
||||
|
||||
this.global.requestQuene.setBatchCompletionCallback(batch, (failedTasks) => {
|
||||
if (failedTasks.length > 0) {
|
||||
let message = `
|
||||
MJ生图任务都已完成。
|
||||
但是以下任务执行失败:
|
||||
`
|
||||
failedTasks.forEach(({ taskId, error }) => {
|
||||
message += `${taskId}-, \n 错误信息: ${error}` + '\n';
|
||||
});
|
||||
|
||||
this.global.newWindow[0].win.webContents.send(DEFINE_STRING.SHOW_MESSAGE_DIALOG, {
|
||||
code: 0,
|
||||
message: message
|
||||
})
|
||||
} else {
|
||||
if (show_global_message) {
|
||||
this.global.newWindow[0].win.webContents.send(DEFINE_STRING.SHOW_MESSAGE_DIALOG, {
|
||||
code: 1,
|
||||
message: "所有MJ生图任务完成"
|
||||
})
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
return {
|
||||
code: 1,
|
||||
}
|
||||
|
||||
|
||||
} catch (error) {
|
||||
return {
|
||||
code: 0,
|
||||
message: "MJ生图错误,错误信息如下" + error.message
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,355 @@
|
||||
|
||||
import { Tools } from "../tools";
|
||||
import path from "path";
|
||||
import { DEFINE_STRING } from "../../define/define_string";
|
||||
import { define } from "../../define/define";
|
||||
import { PublicMethod } from "../Public/publicMethod";
|
||||
import { SD } from "../Public/SD"
|
||||
const util = require('util');
|
||||
import axios from "axios";
|
||||
const sharp = require('sharp');
|
||||
const { spawn, exec } = require('child_process');
|
||||
const execAsync = util.promisify(exec);
|
||||
const { v4: uuidv4 } = require('uuid'); // 引入UUID库来生成唯一标识符
|
||||
let fspromises = require("fs").promises;
|
||||
import { MD5 } from "crypto-js";
|
||||
import { ImageStyleDefine } from "../../define/iamgeStyleDefine";
|
||||
|
||||
|
||||
export class OriginalImageGenerate {
|
||||
constructor(global) {
|
||||
this.global = global;
|
||||
this.tools = new Tools();
|
||||
this.pm = new PublicMethod(global);
|
||||
this.sd = new SD(global);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* SD原创单张图片生成
|
||||
* @param {*} value 传入的参数 0 :原创界面的data数据信息,1:是否需要格式化,2:是否需要全局提示
|
||||
*/
|
||||
async OriginalSDImageGenerate(value) {
|
||||
try {
|
||||
let data = value[0];
|
||||
if (value[1]) {
|
||||
data = JSON.parse(data);
|
||||
}
|
||||
let show_global_message = value[2];
|
||||
// 判断输出的文件夹路径是不是存在,不存在创建
|
||||
let output_crop_path = path.join(this.global.config.project_path, "tmp/output_crop_1");
|
||||
// 检查文件是不是存在
|
||||
let isE = await this.tools.checkExists(output_crop_path);
|
||||
if (!isE) {
|
||||
output_crop_path = path.join(this.global.config.project_path, "tmp/output_crop_00001");
|
||||
}
|
||||
await this.tools.checkFolderExistsOrCreate(output_crop_path);
|
||||
let SdOriginalImage = path.join(this.global.config.project_path, 'data/SdOriginalImage');
|
||||
await this.tools.checkFolderExistsOrCreate(SdOriginalImage);
|
||||
|
||||
// 获取当前的同用前缀后缀
|
||||
let config_json = await this.pm.GetConfigJson(JSON.stringify([null, {}]));
|
||||
let prefix_prompt = config_json.data.prefix_prompt;
|
||||
let suffix_prompt = config_json.data.suffix_prompt;
|
||||
let batch = DEFINE_STRING.QUEUE_BATCH.SD_ORIGINAL_GENERATE_IMAGE;
|
||||
let url = this.global.config.webui_api_url + 'sdapi/v1/txt2img';
|
||||
let sd_setting = JSON.parse(await fspromises.readFile(define.sd_setting, 'utf-8'));
|
||||
|
||||
// 判断当前是不是有开修脸修手
|
||||
let ADetailer = {
|
||||
args: sd_setting.adetailer
|
||||
};
|
||||
let seed = sd_setting.setting.seed;
|
||||
|
||||
let style_ids = await this.pm.GetConfigJson(JSON.stringify(["image_style", []]), false);
|
||||
let image_styles = await ImageStyleDefine.getImageStyleStringByIds(style_ids.data);
|
||||
//
|
||||
console.log(image_styles);
|
||||
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
const element = data[i];
|
||||
let adetailer = element.adetailer;
|
||||
|
||||
let imageJson = JSON.parse(await fspromises.readFile(path.normalize(element.prompt_json), 'utf-8'));
|
||||
// let prompt = image_styles + sd_setting.webui.prompt + ',' + element.prompt;
|
||||
// // 添加前缀
|
||||
// if (prefix_prompt) {
|
||||
// prompt = prefix_prompt + ',' + prompt;
|
||||
// }
|
||||
// // 添加后缀
|
||||
// if (suffix_prompt) {
|
||||
// prompt = prompt + ',' + suffix_prompt;
|
||||
// }
|
||||
let prompt = imageJson.webui_config.prompt;
|
||||
|
||||
|
||||
this.global.requestQuene.enqueue(async () => {
|
||||
try {
|
||||
// 开始请求
|
||||
let body = {
|
||||
"prompt": prompt,
|
||||
"negative_prompt": imageJson.webui_config.negative_prompt,
|
||||
"seed": seed,
|
||||
"sampler_name": imageJson.webui_config.sampler_name,
|
||||
// 提示词相关性
|
||||
"cfg_scale": sd_setting.webui.cfg_scale,
|
||||
"width": sd_setting.webui.width,
|
||||
"height": sd_setting.webui.height,
|
||||
"batch_size": sd_setting.setting.batch_size,
|
||||
"n_iter": 1,
|
||||
"steps": imageJson.webui_config.steps,
|
||||
"save_images": false,
|
||||
}
|
||||
|
||||
// 判断是不是开启修脸修手
|
||||
if (adetailer) {
|
||||
let ta = {
|
||||
ADetailer: ADetailer
|
||||
}
|
||||
body.alwayson_scripts = ta;
|
||||
}
|
||||
const response = await axios.post(url, body);
|
||||
let info = JSON.parse(response.data.info);
|
||||
if (seed == -1) {
|
||||
seed = info.seed;
|
||||
}
|
||||
let images = response.data.images;
|
||||
let subImagePath = [];
|
||||
let out_tmp_image_path = path.join(output_crop_path, `tmp_${element.name}`);
|
||||
let out_image_path = path.join(output_crop_path, `${element.name}`);
|
||||
|
||||
let input_image = path.join(this.global.config.project_path, `tmp/input_crop/${element.name}`);
|
||||
|
||||
for (let j = 0; j < images.length; j++) {
|
||||
const image = images[j];
|
||||
let imageData = Buffer.from(image.split(",", 1)[0], 'base64');
|
||||
// 写入数据(写入到当前当前项目文件下面的 data/SdOriginalImage 下面)
|
||||
let image_path = path.join(this.global.config.project_path, `data/SdOriginalImage/${element.name.split('.')[0]}_${j}.png`);
|
||||
let tmp_image_path = path.join(this.global.config.project_path, `data/SdOriginalImage/tmp_${element.name.split('.')[0]}_${j}.png`);
|
||||
subImagePath.push(image_path);
|
||||
await sharp(imageData)
|
||||
.toFile(tmp_image_path)
|
||||
.then(async () => {
|
||||
// 生图成功,删除数据
|
||||
// 判断原本的图片文件是不是存在,存在删除
|
||||
await this.tools.deletePngAndDeleteExifData(tmp_image_path, image_path);
|
||||
}).catch(err => {
|
||||
throw err;
|
||||
});
|
||||
// console.log("文生图成功" + image_path);
|
||||
|
||||
// 将第一个张写出到指定的文件夹中
|
||||
if (j == 0) {
|
||||
await sharp(imageData)
|
||||
.toFile(out_tmp_image_path)
|
||||
.then(async () => {
|
||||
// 生图成功,删除数据
|
||||
await this.tools.deletePngAndDeleteExifData(out_tmp_image_path, out_image_path);
|
||||
await this.tools.copyFileOrDirectory(out_image_path, input_image);
|
||||
})
|
||||
.catch(err => {
|
||||
// console.log(err)
|
||||
throw err;
|
||||
});
|
||||
}
|
||||
}
|
||||
// 将图片的信息写入到config.json文件中
|
||||
let index = config_json.data.srt_time_information.findIndex(item => item.id == element.id);
|
||||
if (index < 0) {
|
||||
throw new Error("没有找到指定的ID,请检查数据");
|
||||
}
|
||||
config_json.data.srt_time_information[index].subImagePath = subImagePath;
|
||||
config_json.data.srt_time_information[index].outImagePath = out_image_path;
|
||||
this.global.fileQueue.enqueue(async () => {
|
||||
await this.pm.SaveConfigJsonProperty([config_json.data.srt_time_information, "srt_time_information", false]);
|
||||
});
|
||||
|
||||
// 返回数据,用于前台刷新,返回图片数据
|
||||
this.global.newWindow[0].win.webContents.send(DEFINE_STRING.SD_ORIGINAL_GENERATE_IMAGE_RETURN, {
|
||||
code: 1,
|
||||
id: element.id,
|
||||
data: {
|
||||
subImagePath: subImagePath,
|
||||
outImagePath: out_image_path
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
}, `${batch}_${element.name}`, batch);
|
||||
}
|
||||
|
||||
this.global.requestQuene.setBatchCompletionCallback(batch, (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
|
||||
})
|
||||
} else {
|
||||
if (show_global_message) {
|
||||
this.global.newWindow[0].win.webContents.send(DEFINE_STRING.SHOW_MESSAGE_DIALOG, {
|
||||
code: 1,
|
||||
message: "所有生图任务完成"
|
||||
})
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
return {
|
||||
code: 1,
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
return {
|
||||
code: 0,
|
||||
message: error.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 自动保存数据到json文件
|
||||
* @param {*} value 自动保存数据到json文件
|
||||
*/
|
||||
async AutoSaveDataJson(value) {
|
||||
try {
|
||||
// 目前自动保存的信息,中文提示词,英文提示词,前缀,后缀
|
||||
value = JSON.parse(value);
|
||||
for (let i = 0; i < value.length; i++) {
|
||||
const element = value[i];
|
||||
// 将修改文件的的方法添加到修改文件队列中
|
||||
this.global.fileQueue.enqueue(async () => {
|
||||
try {
|
||||
if (element.prompt_json) {
|
||||
let old_json = JSON.parse(await fspromises.readFile(path.normalize(element.prompt_json), 'utf-8'));
|
||||
old_json.webui_config.prompt = element.prompt;
|
||||
// old_json.adetailer = element.adetailer;
|
||||
old_json.chinese_prompt = element.chinese_prompt;
|
||||
// 前缀提示词
|
||||
old_json.prefix_prompt = element.prefix_prompt;
|
||||
// 后缀提示词
|
||||
old_json.suffix_prompt = element.suffix_prompt;
|
||||
old_json.adetailer = element.adetailer;
|
||||
old_json.prompt = element.prompt;
|
||||
await fspromises.writeFile(path.normalize(element.prompt_json), JSON.stringify(old_json));
|
||||
}
|
||||
} catch (error) {
|
||||
throw new Error(error);
|
||||
}
|
||||
});
|
||||
|
||||
// 判断是不是有图片。判断图片是不是符合格式(有些格式是file:// 开头的, 以时间结尾(都要删除))
|
||||
// 判断是不是有图片
|
||||
let file_regex = /^file:\/\//;
|
||||
if (element.outImagePath && file_regex.test(element.outImagePath)) {
|
||||
// 删除 "file://" 开头
|
||||
element.outImagePath = decodeURI(element.outImagePath);
|
||||
element.outImagePath = element.outImagePath.replace(/^file:\/\//, '').replace(/\?time=.*$/, '');
|
||||
}
|
||||
if (element.subImagePath && element.subImagePath.length > 0) {
|
||||
for (let j = 0; j < element.subImagePath.length; j++) {
|
||||
if (file_regex.test(element.subImagePath[j])) {
|
||||
element.subImagePath[j] = decodeURI(element.subImagePath[j]);
|
||||
element.subImagePath[j] = element.subImagePath[j].replace(/^file:\/\//, '').replace(/\?time=.*$/, '');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
await this.tools.writeJsonFilePropertyValue(path.join(this.global.config.project_path, "scripts/config.json"), "srt_time_information", value, false);
|
||||
|
||||
return {
|
||||
code: 1
|
||||
}
|
||||
} catch (error) {
|
||||
return {
|
||||
code: 0,
|
||||
message: error.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 将反推的图片的信息添加到一个json文件中
|
||||
*/
|
||||
async OriginalAddWebuiJson(value) {
|
||||
try {
|
||||
let data = JSON.parse(value);
|
||||
// 判断文件夹是不是存在
|
||||
let imput_crop_path = path.join(this.global.config.project_path, "tmp/input_crop");
|
||||
let isExist = await this.tools.checkExists(imput_crop_path);
|
||||
if (!isExist) {
|
||||
await fspromises.mkdir(imput_crop_path, { recursive: true })
|
||||
}
|
||||
|
||||
// 判断当前的数据是不是相同
|
||||
// 读取所有txt文件
|
||||
let promptJson = await this.tools.getFilesWithExtensions(path.join(global.config.project_path, 'tmp/input_crop'), '.json');
|
||||
|
||||
// json 已经存在,不做后续处理
|
||||
if (data.length == promptJson.length) {
|
||||
return {
|
||||
code: 1,
|
||||
data: path.join(this.global.config.project_path, "tmp/input_crop")
|
||||
}
|
||||
}
|
||||
|
||||
let sd_config = JSON.parse(await fspromises.readFile(define.sd_setting, 'utf-8'));
|
||||
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
const element = data[i];
|
||||
let name = String(element.no).padStart(5, '0') + ".png";
|
||||
// console.log(txtpath)
|
||||
let obj = {}
|
||||
obj.model = sd_config.setting.type;
|
||||
obj.api = sd_config.setting.webui_api_url + 'sdapi/v1/txt2img';
|
||||
obj.name = name;
|
||||
obj.webui_config = {
|
||||
sampler_name: sd_config.webui.sampler_name,
|
||||
prompt: "",
|
||||
negative_prompt: sd_config.webui.negative_prompt,
|
||||
batch_size: 1,
|
||||
steps: sd_config.webui.steps,
|
||||
cfg_scale: sd_config.webui.cfg_scale,
|
||||
denoising_strength: sd_config.webui.denoising_strength,
|
||||
width: sd_config.webui.width,
|
||||
height: sd_config.webui.height,
|
||||
seed: sd_config.setting.seed,
|
||||
init_images: path.normalize(path.join(this.global.config.project_path, "tmp/input_crop/" + name)),
|
||||
}
|
||||
obj.adetailer = sd_config.webui.adetailer;
|
||||
|
||||
let file_path = path.join(this.global.config.project_path, "tmp/input_crop/" + name + '.json');
|
||||
// 写入
|
||||
await fspromises.writeFile(file_path, JSON.stringify(obj));
|
||||
}
|
||||
|
||||
return {
|
||||
code: 1,
|
||||
data: path.join(this.global.config.project_path, "tmp/input_crop")
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
return {
|
||||
code: 0,
|
||||
message: error.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
import { TagDefine } from "../../define/tagDefine";
|
||||
export class TagCustomize {
|
||||
constructor(global) {
|
||||
this.global = global;
|
||||
this.tagDefine = new TagDefine(global);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取默认的tag
|
||||
* @param {*} value
|
||||
*/
|
||||
async GetTagDataByTypeAndProperty(value) {
|
||||
return await this.tagDefine.getTagDataByTypeAndProperty(value[0], value[1]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存数据到指定的tag中
|
||||
* @param {*} value
|
||||
* @returns
|
||||
*/
|
||||
async SaveTagPropertyData(value) {
|
||||
return await this.tagDefine.saveTagPropertyData(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除指定的tag数据
|
||||
* @param {*} value
|
||||
* @returns
|
||||
*/
|
||||
async DeleteTagPropertyData(value) {
|
||||
return await this.tagDefine.deleteTagPropertyData(value);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,448 @@
|
||||
import axios from "axios";
|
||||
import path from "path";
|
||||
import { DEFINE_STRING } from "../../define/define_string";
|
||||
import { define } from "../../define/define";
|
||||
let fspromises = require("fs").promises;
|
||||
import { gptDefine } from "../../define/gptDefine";
|
||||
|
||||
export class GPT {
|
||||
constructor(global) {
|
||||
this.global = global;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 输出测试案例
|
||||
* @param {*} value 传入的值(整个数据)
|
||||
*/
|
||||
async GenerateGptExampleOut(value) {
|
||||
try {
|
||||
|
||||
let data = JSON.parse(value);
|
||||
let message = gptDefine.CustomizeGptPrompt(data);
|
||||
let content = await this.FetchGpt(message);
|
||||
console.log(content);
|
||||
return {
|
||||
code: 1,
|
||||
data: content
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
return {
|
||||
code: 0,
|
||||
message: error.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* GPT推理提示词的方法
|
||||
* @param {*} element 当前推理的句子
|
||||
* @param {*} gpt_count 设置的GPT上下文理解数量
|
||||
* @param {*} auto_analyze_character 当前的角色数据
|
||||
* @returns
|
||||
*/
|
||||
async GPTPromptGenerate(element, gpt_count, auto_analyze_character) {
|
||||
try {
|
||||
// 获取当前的推理模式
|
||||
let gpt_auto_inference = this.global.config.gpt_auto_inference;
|
||||
let message = null;
|
||||
if (gpt_auto_inference == "customize") {
|
||||
// 自定义模式
|
||||
// 获取当前自定义的推理提示词
|
||||
let customize_gpt_prompt = (await gptDefine.getGptDataByTypeAndProperty("dynamic", "customize_gpt_prompt", [])).data;
|
||||
let index = customize_gpt_prompt.findIndex(item => item.id == this.global.config.customize_gpt_prompt);
|
||||
if (this.global.config.customize_gpt_prompt && index < 0) {
|
||||
throw new Error("自定义推理默认要选择对应的自定义推理词");
|
||||
}
|
||||
message = gptDefine.CustomizeGptPrompt(customize_gpt_prompt[index], element.after_gpt);
|
||||
message.push({
|
||||
"role": "user",
|
||||
"content": element.after_gpt
|
||||
})
|
||||
} else {
|
||||
// 内置模式
|
||||
// 获取
|
||||
let prefix_word = "";
|
||||
// 拼接一个word
|
||||
let i = element.no - 1;
|
||||
if (i <= gpt_count) {
|
||||
prefix_word = this.all_data.filter((item, index) => index < i).map(item => item.after_gpt).join('\r\n');
|
||||
} else if (i > gpt_count) {
|
||||
prefix_word = this.all_data.filter((item, index) => i - index <= gpt_count && i - index > 0).map(item => item.after_gpt).join('\r\n');
|
||||
}
|
||||
|
||||
let suffix_word = "";
|
||||
let o_i = this.all_data.length - i;
|
||||
if (o_i <= gpt_count) {
|
||||
suffix_word = this.all_data.filter((item, index) => index > i).map(item => item.after_gpt).join('\r\n');
|
||||
} else if (o_i > gpt_count) {
|
||||
suffix_word = this.all_data.filter((item, index) => index - i <= gpt_count && index - i > 0).map(item => item.after_gpt).join('\r\n');
|
||||
}
|
||||
|
||||
let word = `${prefix_word}\r\n${element.after_gpt}\r\n${suffix_word}`;
|
||||
let single_word = element.after_gpt;
|
||||
|
||||
// 判断当前的格式
|
||||
if (["superSinglePrompt", 'onlyPromptMJ'].includes(this.global.config.gpt_auto_inference)) {
|
||||
// 有返回案例的
|
||||
message = gptDefine.GetExamplePromptMessage(this.global.config.gpt_auto_inference);
|
||||
// 加当前提问的
|
||||
message.push({
|
||||
"role": "user",
|
||||
"content": single_word
|
||||
})
|
||||
|
||||
} else {
|
||||
// 直接返回,没有案例的
|
||||
message = [
|
||||
{
|
||||
"role": "system",
|
||||
"content": gptDefine.getSystemContentByType(this.global.config.gpt_auto_inference, {
|
||||
textContent: word,
|
||||
characterContent: auto_analyze_character
|
||||
})
|
||||
},
|
||||
{
|
||||
"role": "user",
|
||||
"content": gptDefine.getUserContentByType(this.global.config.gpt_auto_inference, {
|
||||
textContent: single_word,
|
||||
wordCount: this.global.config.gpt_model && this.global.config.gpt_model.includes("gpt-4") ? '20' : '40'
|
||||
})
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
let res = await this.FetchGpt(message);
|
||||
return res;
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将推理提示词添加到任务
|
||||
*/
|
||||
async GPTPrompt(data) {
|
||||
try {
|
||||
console.log(data)
|
||||
let value = JSON.parse(data[0]);
|
||||
let show_global_message = data[1];
|
||||
this.all_data = JSON.parse(data[2]);
|
||||
// 获取data中的after_gpt,然后使用换行符拼接成一个字符串
|
||||
// let word = value.map(item => item.after_gpt).join('\r\n');
|
||||
let batch = DEFINE_STRING.QUEUE_BATCH.SD_ORIGINAL_GPT_PROMPT;
|
||||
|
||||
// 获取人物角色数据
|
||||
let config_json = JSON.parse(await fspromises.readFile(path.join(this.global.config.project_path, "scripts/config.json"), 'utf-8'));
|
||||
let auto_analyze_character = config_json.auto_analyze_character;
|
||||
let gpt_count = this.global.config.gpt_count ? this.global.config.gpt_count : 10;
|
||||
for (let i = 0; i < value.length; i++) {
|
||||
const element = value[i];
|
||||
this.global.requestQuene.enqueue(async () => {
|
||||
try {
|
||||
|
||||
let content = await this.GPTPromptGenerate(element, gpt_count, auto_analyze_character);
|
||||
|
||||
if (content) {
|
||||
content = content.replace(/\)\s*\(/g, ", ").replace(/^\(/, "").replace(/\)$/, "")
|
||||
}
|
||||
// 获取对应的数据,将数据返回前端事件
|
||||
this.global.newWindow[0].win.webContents.send(DEFINE_STRING.GPT_GENERATE_PROMPT_RETURN, {
|
||||
id: element.id,
|
||||
gpt_prompt: content
|
||||
})
|
||||
|
||||
this.global.fileQueue.enqueue(async () => {
|
||||
// 将推理出来的数据写入执行的文件中
|
||||
let json_config = JSON.parse(await fspromises.readFile(element.prompt_json, 'utf-8'));
|
||||
// 写入
|
||||
json_config.gpt_prompt = content;
|
||||
await fspromises.writeFile(element.prompt_json, JSON.stringify(json_config));
|
||||
})
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
}, `${batch}_${element.id}`, batch);
|
||||
}
|
||||
|
||||
this.global.requestQuene.setBatchCompletionCallback(batch, (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
|
||||
})
|
||||
} else {
|
||||
if (show_global_message) {
|
||||
this.global.newWindow[0].win.webContents.send(DEFINE_STRING.SHOW_MESSAGE_DIALOG, {
|
||||
code: 1,
|
||||
message: "所有推理任务完成"
|
||||
})
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
code: 1,
|
||||
}
|
||||
|
||||
|
||||
} catch (error) {
|
||||
return {
|
||||
code: 0,
|
||||
message: error.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改请求的参数
|
||||
* @param {*} data
|
||||
* @returns
|
||||
*/
|
||||
ModifyData(gpt_url, data) {
|
||||
let res = data;
|
||||
if (gpt_url.includes("dashscope.aliyuncs.com")) {
|
||||
res = {
|
||||
"model": data.model,
|
||||
"input": {
|
||||
"messages": data.messages,
|
||||
},
|
||||
"parameters": {
|
||||
"result_format": "message"
|
||||
}
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取返回的内容
|
||||
* @param {*} gpt_url GPT请求的内容
|
||||
* @param {*} res 请求返回的数据
|
||||
* @returns
|
||||
*/
|
||||
GetResponseContent(gpt_url, res) {
|
||||
let content = "";
|
||||
if (gpt_url.includes("dashscope.aliyuncs.com")) {
|
||||
content = res.data.output.choices[0].message.content;
|
||||
} else {
|
||||
|
||||
content = res.data.choices[0].message.content;
|
||||
}
|
||||
return content;
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送GPT请求
|
||||
* @param {*} message 请求的信息
|
||||
* @param {*} gpt_url gpt的url,默认在global中取
|
||||
* @param {*} gpt_key gpt的key,默认在global中取
|
||||
* @param {*} gpt_model gpt的model,默认在global中取
|
||||
* @returns
|
||||
*/
|
||||
async FetchGpt(message,
|
||||
gpt_url = this.global.config.gpt_business,
|
||||
gpt_key = this.global.config.gpt_key,
|
||||
gpt_model = this.global.config.gpt_model) {
|
||||
try {
|
||||
|
||||
let data = {
|
||||
"model": gpt_model,
|
||||
"messages": message
|
||||
};
|
||||
|
||||
data = this.ModifyData(gpt_url, data);
|
||||
let config = {
|
||||
method: 'post',
|
||||
maxBodyLength: Infinity,
|
||||
url: gpt_url,
|
||||
headers: {
|
||||
'Authorization': `Bearer ${gpt_key}`,
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
data: JSON.stringify(data)
|
||||
};
|
||||
|
||||
let res = await axios.request(config);
|
||||
let content = this.GetResponseContent(gpt_url, res);
|
||||
return content;
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 自动分析文本,返回人物场景。角色。
|
||||
* @param {要分析的文本} value
|
||||
* @returns
|
||||
*/
|
||||
async AutoAnalyzeCharacter(value) {
|
||||
try {
|
||||
let message = [
|
||||
{
|
||||
"role": "system",
|
||||
"content": gptDefine.getSystemContentByType("character", { textContent: value })
|
||||
},
|
||||
{
|
||||
"role": "user",
|
||||
"content": gptDefine.getUserContentByType("character", {})
|
||||
}
|
||||
]
|
||||
let content = await this.FetchGpt(message);
|
||||
|
||||
return {
|
||||
code: 1,
|
||||
data: content
|
||||
}
|
||||
} catch (error) {
|
||||
return {
|
||||
code: 0,
|
||||
message: error.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取GPT的服务商配置,默认的和自定义的
|
||||
* @returns
|
||||
*/
|
||||
async GetGPTBusinessOption(value) {
|
||||
return await gptDefine.getGptDataByTypeAndProperty(value, "gpt_options", []);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取GPT的模型配置,默认的和自定义的
|
||||
* @returns
|
||||
*/
|
||||
async GetGPTModelOption(value) {
|
||||
return await gptDefine.getGptDataByTypeAndProperty(value, "gpt_model_options", []);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取GPT的自动推理模式配置,默认的和自定义的
|
||||
* @returns
|
||||
*/
|
||||
async GetGptAutoInferenceOptions(value) {
|
||||
return await gptDefine.getGptDataByTypeAndProperty(value, "gpt_auto_inference", []);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取GPT的自动推理模式配置,默认的和自定义的
|
||||
* @returns
|
||||
*/
|
||||
async GetCustomizeGptPrompt(value) {
|
||||
return await gptDefine.getGptDataByTypeAndProperty(value, "customize_gpt_prompt", []);
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存自定义的GPT服务商配置
|
||||
* @param {*} value 配置信息 0 : 传入的数据 1: 属性名称
|
||||
* @returns
|
||||
*/
|
||||
async SaveDynamicGPTOption(value) {
|
||||
try {
|
||||
let res = await gptDefine.saveDynamicGPTOption(value);
|
||||
return {
|
||||
code: 1,
|
||||
}
|
||||
} catch (error) {
|
||||
return {
|
||||
code: 0,
|
||||
message: error.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除指定Id的自定义GPT服务商配置
|
||||
* @param {*} value id 0 : 删除的数据 1: 属性名称
|
||||
* @returns
|
||||
*/
|
||||
async DeleteDynamicGPTOption(value) {
|
||||
try {
|
||||
let res = await gptDefine.deleteDynamicGPTOption(value);
|
||||
return {
|
||||
code: 1,
|
||||
data: res
|
||||
}
|
||||
} catch (error) {
|
||||
return {
|
||||
code: 0,
|
||||
message: error.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Stirng} value 传入的GPT网址和key,判断是不是可以链接成功
|
||||
*/
|
||||
async TestGPTConnection(value) {
|
||||
try {
|
||||
value = JSON.parse(value);
|
||||
let message = [
|
||||
{
|
||||
"role": "system",
|
||||
"content": "你好"
|
||||
},
|
||||
{
|
||||
"role": "user",
|
||||
"content": "你好"
|
||||
}
|
||||
];
|
||||
|
||||
let content = await this.FetchGpt(message, value.gpt_business, value.gpt_key, value.gpt_model);
|
||||
return {
|
||||
code: 1,
|
||||
}
|
||||
} catch (error) {
|
||||
return {
|
||||
code: 0,
|
||||
message: error.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 单句洗稿
|
||||
* @param {文案参数} value
|
||||
*/
|
||||
async AIModifyOneWord(value) {
|
||||
try {
|
||||
|
||||
let message = [
|
||||
{
|
||||
"role": "system",
|
||||
"content": "You are ChatGPT, a large language model trained by OpenAI. Answer as concisely as possible."
|
||||
},
|
||||
{
|
||||
"role": "user",
|
||||
"content": `请您扮演一个抖音网文改写专家,我会给你一句文案,请你不要改变文案的结构,不改变原来的意思,仅对文案进行同义转换改写,不要有奇怪的写法,说法通俗一点,不要其他的标点符号,每一小句话之间都是以句号连接,参考抖音网文解说,以下是文案:${value[1]}。`
|
||||
}
|
||||
]
|
||||
let content = await this.FetchGpt(message);
|
||||
|
||||
return {
|
||||
code: 1,
|
||||
data: { no: value[0], content: content }
|
||||
}
|
||||
} catch (error) {
|
||||
return {
|
||||
code: 0,
|
||||
message: error.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,274 @@
|
||||
import axios from "axios";
|
||||
import path from "path";
|
||||
import { DEFINE_STRING } from "../../define/define_string";
|
||||
import { define } from "../../define/define";
|
||||
import { ImageStyleDefine } from "../../define/iamgeStyleDefine";
|
||||
import { cloneDeep } from 'lodash';
|
||||
let fspromises = require("fs").promises;
|
||||
const sharp = require('sharp');
|
||||
// const {
|
||||
// createCanvas,
|
||||
// loadImage
|
||||
// } = require('canvas');
|
||||
import { SdSettingDefine } from "../../define/setting/sdSettingDefine";
|
||||
import { PublicMethod } from "./publicMethod";
|
||||
import { Tools } from "../tools";
|
||||
|
||||
export class SD {
|
||||
constructor(global) {
|
||||
this.global = global;
|
||||
this.pm = new PublicMethod(global);
|
||||
this.tools = new Tools();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取图片风格菜单
|
||||
* @returns 返回图片风格菜单
|
||||
*
|
||||
* */
|
||||
async GetImageStyleMenu() {
|
||||
try {
|
||||
let style = ImageStyleDefine.getImageStyleMenu();
|
||||
return {
|
||||
code: 1,
|
||||
data: style
|
||||
}
|
||||
} catch (error) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定的ID的风格信息,传入的是一个数组
|
||||
* @param {*} value id集合
|
||||
*/
|
||||
async GetImageStyleInfomation(value) {
|
||||
try {
|
||||
if (value) {
|
||||
value = JSON.parse(value);
|
||||
} else {
|
||||
value = [];
|
||||
}
|
||||
value = value ? value : [];
|
||||
let style = ImageStyleDefine.getAllSubStyle();
|
||||
let tmp = [];
|
||||
for (let i = 0; i < value.length; i++) {
|
||||
const element = value[i];
|
||||
for (let j = 0; j < style.length; j++) {
|
||||
const item = style[j];
|
||||
if (item.id == element) {
|
||||
tmp.push(item);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
let newSubStyle = cloneDeep(tmp);
|
||||
for (let i = 0; i < newSubStyle.length; i++) {
|
||||
const element = newSubStyle[i];
|
||||
element.image = path.join(define.image_path, "style/" + element.image);
|
||||
}
|
||||
return {
|
||||
code: 1,
|
||||
data: newSubStyle
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
return {
|
||||
code: 0,
|
||||
message: error.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定ID的分类的子风格信息
|
||||
* @param {*} value ID
|
||||
* @returns 返回ID对应的子风格的详细信息
|
||||
*/
|
||||
async GetStyleImageSubList(value) {
|
||||
try {
|
||||
let subStyle = ImageStyleDefine.getImagePathById(value);
|
||||
let newSubStyle = cloneDeep(subStyle);
|
||||
for (let i = 0; i < newSubStyle.length; i++) {
|
||||
const element = newSubStyle[i];
|
||||
element.image = path.join(define.image_path, "style/" + element.image);
|
||||
}
|
||||
return {
|
||||
code: 1,
|
||||
data: newSubStyle
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
return {
|
||||
code: 0,
|
||||
message: error.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成一次图片的方法。可以区分模式
|
||||
* @param {图片名称 } image
|
||||
* @param {任务队列信息} task_list 301198499
|
||||
*/
|
||||
async OneImageGeneration(image, task_list, seed = -1) {
|
||||
let taskPath = path.join(this.global.config.project_path, "scripts/task_list.json")
|
||||
try {
|
||||
let imageJson = JSON.parse(await fspromises.readFile(image + '.json', 'utf-8'));
|
||||
let sd_setting = JSON.parse(await fspromises.readFile(define.sd_setting, 'utf-8'));
|
||||
let model = imageJson.model;
|
||||
let image_json = JSON.parse(await fspromises.readFile(image + '.json', 'utf-8'));
|
||||
let image_path = "";
|
||||
let target_image_path = "";
|
||||
|
||||
if (image_json.name) {
|
||||
image_path = path.join(this.global.config.project_path, `tmp/${task_list.out_folder}/tmp_${image_json.name}`)
|
||||
target_image_path = path.join(this.global.config.project_path, `tmp/${task_list.out_folder}/${image_json.name}`)
|
||||
} else {
|
||||
image_path = image.replaceAll("input_crop", task_list.out_folder).split(".png")[0] + "_tmp.png";
|
||||
target_image_path = image.replaceAll("input_crop", task_list.out_folder);
|
||||
}
|
||||
|
||||
// let prompt = "";
|
||||
// // 拼接提示词
|
||||
// if (task_list.image_style != null) {
|
||||
// prompt += `((${task_list.image_style})),`;
|
||||
// }
|
||||
// if (task_list.lora != null) {
|
||||
// prompt += `${task_list.lora},`;
|
||||
// }
|
||||
// let image_styles = await ImageStyleDefine.getImageStyleStringByIds(task_list.image_style_list ? task_list.image_style_list : []);
|
||||
|
||||
// prompt = `${prompt}, ${image_styles}, ${imageJson.webui_config.prompt}`;
|
||||
let prompt = imageJson.webui_config.prompt;
|
||||
|
||||
// 判断当前是不是有开修脸修手
|
||||
let ADetailer = {
|
||||
args: sd_setting.adetailer
|
||||
};
|
||||
|
||||
if (model == "img2img") {
|
||||
let web_api = this.global.config.webui_api_url + 'sdapi/v1/img2img'
|
||||
let sd_config = imageJson["webui_config"];
|
||||
sd_config.prompt = prompt;
|
||||
sd_config.seed = seed;
|
||||
let im = await fspromises.readFile(image, 'binary');
|
||||
sd_config.init_images = [new Buffer.from(im, 'binary').toString('base64')];
|
||||
|
||||
if (imageJson.adetailer) {
|
||||
let ta = {
|
||||
ADetailer: ADetailer
|
||||
}
|
||||
sd_config.alwayson_scripts = ta;
|
||||
}
|
||||
sd_config.height = sd_setting.webui.height;
|
||||
sd_config.width = sd_setting.webui.width;
|
||||
|
||||
const response = await axios.post(web_api, sd_config);
|
||||
let info = JSON.parse(response.data.info);
|
||||
if (seed == -1) {
|
||||
seed = info.seed;
|
||||
}
|
||||
|
||||
// 目前是单图出图
|
||||
let images = response.data.images;
|
||||
let imageData = Buffer.from(images[0].split(",", 1)[0], 'base64');
|
||||
await sharp(imageData)
|
||||
.toFile(image_path)
|
||||
.then(async () => {
|
||||
// console.log("图生图成功" + image_path);
|
||||
await this.tools.deletePngAndDeleteExifData(image_path, target_image_path);
|
||||
})
|
||||
.catch(err => {
|
||||
throw new Error(err);
|
||||
});
|
||||
return seed;
|
||||
|
||||
} else if (model == "txt2img") {
|
||||
let body = {
|
||||
"prompt": prompt,
|
||||
"negative_prompt": imageJson.webui_config.negative_prompt,
|
||||
"seed": seed,
|
||||
"sampler_name": imageJson.webui_config.sampler_name,
|
||||
// 提示词相关性
|
||||
"cfg_scale": imageJson.webui_config.cfg_scale,
|
||||
"width": sd_setting.webui.width,
|
||||
"height": sd_setting.webui.height,
|
||||
"batch_size": 1,
|
||||
"n_iter": 1,
|
||||
"steps": imageJson.webui_config.steps,
|
||||
"save_images": false,
|
||||
}
|
||||
let web_api = this.global.config.webui_api_url + 'sdapi/v1/txt2img';
|
||||
|
||||
if (imageJson.adetailer) {
|
||||
let ta = {
|
||||
ADetailer: ADetailer
|
||||
}
|
||||
body.alwayson_scripts = ta;
|
||||
}
|
||||
const response = await axios.post(web_api, body);
|
||||
let info = JSON.parse(response.data.info);
|
||||
if (seed == -1) {
|
||||
seed = info.seed;
|
||||
}
|
||||
// 目前是单图出图
|
||||
let images = response.data.images;
|
||||
let imageData = Buffer.from(images[0].split(",", 1)[0], 'base64');
|
||||
await sharp(imageData)
|
||||
.toFile(image_path)
|
||||
.then(async () => {
|
||||
// console.log("文生图成功" + image_path);
|
||||
await this.tools.deletePngAndDeleteExifData(image_path, target_image_path);
|
||||
})
|
||||
.catch(err => {
|
||||
// console.log(err)
|
||||
throw new Error(err);
|
||||
});
|
||||
return seed;
|
||||
} else {
|
||||
throw new Error("SD 模式错误");
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
// 当前队列执行失败移除整个批次的任务
|
||||
this.global.requestQuene.removeTask(task_list.out_folder, null)
|
||||
this.global.fileQueue.enqueue(async () => {
|
||||
// 记录失败状态
|
||||
let task_list_json = JSON.parse(await fspromises.readFile(taskPath, 'utf-8'));
|
||||
// 修改指定的列表的数据
|
||||
task_list_json.task_list.map(a => {
|
||||
if (a.id == task_list.id) {
|
||||
a.status = "error";
|
||||
a.errorMessage = error.toString();
|
||||
}
|
||||
})
|
||||
// 写入
|
||||
await fspromises.writeFile(taskPath, JSON.stringify(task_list_json));
|
||||
this.global.newWindow[0].win.webContents.send(DEFINE_STRING.IMAGE_TASK_STATUS_REFRESH, {
|
||||
out_folder: task_list.out_folder,
|
||||
status: "error"
|
||||
});
|
||||
})
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*文生图
|
||||
* @param {SD 请求的地址} url
|
||||
* @param {SD请求的body} body
|
||||
*/
|
||||
async txt2img(url, body) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
*图生图
|
||||
* @param {SD 请求的地址} url
|
||||
* @param {SD请求的body} body
|
||||
*/
|
||||
async img2img(url, body) {
|
||||
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,850 @@
|
||||
import { define } from "../../define/define";
|
||||
import path from "path";
|
||||
import { Tools } from "../tools";
|
||||
const { v4: uuidv4 } = require('uuid');
|
||||
const fspromises = require("fs").promises;
|
||||
const mm = require('music-metadata');
|
||||
const wavFileInfo = require('wav-file-info');
|
||||
let tools = new Tools();
|
||||
import { PublicMethod } from './publicMethod'
|
||||
import { cloneDeep } from "lodash";
|
||||
const compressing = require("compressing");
|
||||
|
||||
export class ClipDraft {
|
||||
constructor(global, value) {
|
||||
this.speedId = null;
|
||||
this.canvasesId = null;
|
||||
this.soundChannelId = null;
|
||||
this.vocalSeparationsId = null;
|
||||
this.materialVideoId = null;
|
||||
this.tracksSegmentsId = null;
|
||||
this.trackTypeId = null;
|
||||
this.materialAnimationsId = null;
|
||||
this.materialsTextID = null;
|
||||
this.materialsBeatsID = null;
|
||||
this.textId = null;
|
||||
this.friendlyReminderId = null;
|
||||
this.draft_json = null;
|
||||
this.one_duration_time = 5000000;
|
||||
this.text_end_time = 0;
|
||||
this.iamge_end_time = 0;
|
||||
this.dubbing_emd_time = 0;
|
||||
this.draft_duration_time = 0;
|
||||
this.global = global;
|
||||
this.value = value;
|
||||
this.pm = new PublicMethod(global);
|
||||
}
|
||||
|
||||
async InitData() {
|
||||
this.draft_name = this.global.config.project_name + '_' + this.value[0];
|
||||
let draft_path = path.join(this.global.config.draft_path, this.draft_name);
|
||||
await fspromises.rm(draft_path, { recursive: true, force: true });
|
||||
await compressing.zip.uncompress(define.draft_temp_path, path.join(this.global.config.draft_path, this.global.config.project_name + '_' + this.value[0]));
|
||||
this.draftPath = path.join(draft_path, "draft_content.json");
|
||||
this.image_dir = path.join(this.global.config.project_path, `tmp/${this.value[0]}`);
|
||||
this.srtPath = this.value[1].srt_path;
|
||||
this.style_id = this.value[1].draft_srt_style;
|
||||
this.num = 1;
|
||||
this.mp3_path = this.value[1].audio_path;
|
||||
this.friendlyReminderId = this.value[1].friendly_reminder;
|
||||
this.srt_information = (await this.pm.GetConfigJson(JSON.stringify(["srt_time_information", []]))).data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载默认的草稿
|
||||
* @returns
|
||||
*/
|
||||
async LoadDraftJson() {
|
||||
let draft_json = JSON.parse(await fspromises.readFile(this.draftPath));
|
||||
this.draft_json = draft_json;
|
||||
// console.log(this.draft_json);
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加speed
|
||||
*/
|
||||
async AddSpeeds() {
|
||||
// 获取speed的模板地址
|
||||
let speed_json = JSON.parse(await fspromises.readFile(define.clip_speed_temp_path));
|
||||
// console.log(speed_json)
|
||||
// 设置ID
|
||||
let speedID = uuidv4().toUpperCase();
|
||||
this.speedId = speedID;
|
||||
speed_json.id = speedID;
|
||||
return speed_json;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加Canvases
|
||||
*/
|
||||
async AddCanvases() {
|
||||
let canvases_json = JSON.parse(await fspromises.readFile(define.add_canvases_temp_path));
|
||||
// console.log(canvases_json);
|
||||
// 设置ID
|
||||
let canvasesId = uuidv4().toUpperCase();
|
||||
this.canvasesId = canvasesId;
|
||||
canvases_json.id = canvasesId;
|
||||
return canvases_json;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加soundchannel
|
||||
* @returns
|
||||
*/
|
||||
async AddSoundChannelMapping() {
|
||||
let soundChannelMapping_json = JSON.parse(await fspromises.readFile(define.add_sound_channel_mappings_temp_path));
|
||||
// console.log(soundChannelMapping_json)
|
||||
let sound_channel_mappings_tmp_ID = uuidv4().toUpperCase();
|
||||
this.soundChannelId = sound_channel_mappings_tmp_ID;
|
||||
soundChannelMapping_json.id = sound_channel_mappings_tmp_ID;
|
||||
return soundChannelMapping_json;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加 vocal_separations
|
||||
*/
|
||||
async AddVocalSeparations() {
|
||||
let vocal_separations_json = JSON.parse(await fspromises.readFile(define.add_vocal_separations_temp_path));
|
||||
// console.log(vocal_separations_json);
|
||||
let vocalSeparationId = uuidv4().toUpperCase();
|
||||
this.vocalSeparationsId = vocalSeparationId;
|
||||
vocal_separations_json.id = vocalSeparationId;
|
||||
return vocal_separations_json;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加一个文件到原材料地址
|
||||
* @param {图片文件地址} imagePath
|
||||
*/
|
||||
async AddMaterialVideo(imagePath) {
|
||||
let materialVideoTmpJson = JSON.parse(await fspromises.readFile(define.add_material_video_temp_path));
|
||||
// console.log(materialVideoTmpJson);
|
||||
let materialId = uuidv4().toUpperCase();
|
||||
this.materialVideoId = materialId;
|
||||
materialVideoTmpJson.id = materialId;
|
||||
|
||||
// 获取输入的图片宽高
|
||||
// let image = await Jimp.read(imagePath);
|
||||
// let width = image.bitmap.width;
|
||||
// let height = image.bitmap.height;
|
||||
materialVideoTmpJson.width = 1000;
|
||||
materialVideoTmpJson.height = 1000;
|
||||
|
||||
materialVideoTmpJson.path = imagePath;
|
||||
let image_name = path.basename(imagePath);
|
||||
materialVideoTmpJson.material_name = image_name;
|
||||
|
||||
return materialVideoTmpJson;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加一个轨道
|
||||
* @returns
|
||||
*/
|
||||
async AddTracksSegments() {
|
||||
let tracksJson = JSON.parse(await fspromises.readFile(define.add_tracks_segments_temp_path));
|
||||
// console.log(tracksJson);
|
||||
let tracksSegmentsId = uuidv4().toUpperCase();
|
||||
tracksJson.id = tracksSegmentsId;
|
||||
tracksJson.extra_material_refs = [];
|
||||
tracksJson.extra_material_refs.push(this.speedId);
|
||||
tracksJson.extra_material_refs.push(this.canvasesId);
|
||||
tracksJson.extra_material_refs.push(this.soundChannelId);
|
||||
tracksJson.extra_material_refs.push(this.vocalSeparationsId);
|
||||
tracksJson.material_id = this.materialVideoId;
|
||||
tracksJson.target_timerange.start = this.num * tracksJson.target_timerange.duration;
|
||||
return tracksJson;
|
||||
}
|
||||
|
||||
/**
|
||||
* 新建一个track
|
||||
* @param {track的类型} type
|
||||
*/
|
||||
async AddTracks(type) {
|
||||
let tracks_json = JSON.parse(await fspromises.readFile(define.add_tracks_type_temp_path));
|
||||
let track_type_id = uuidv4();
|
||||
this.trackTypeId = track_type_id;
|
||||
tracks_json.id = this.trackTypeId;
|
||||
tracks_json.type = type;
|
||||
return tracks_json;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加单个图片到轨道
|
||||
*/
|
||||
async AddOneImageToDraft(image_path) {
|
||||
// 添加 canvases
|
||||
let canvanses = await this.AddCanvases();
|
||||
this.draft_json.materials.canvases.push(canvanses)
|
||||
|
||||
// 添加 sound_channel_mappings
|
||||
let sound_channel = await this.AddSoundChannelMapping();
|
||||
this.draft_json.materials.sound_channel_mappings.push(sound_channel)
|
||||
|
||||
// 添加 speeds
|
||||
let speeds = await this.AddSpeeds();
|
||||
this.draft_json.materials.speeds.push(speeds);
|
||||
|
||||
// 添加 vocal_separations
|
||||
let vocal_sep = await this.AddVocalSeparations();
|
||||
this.draft_json.materials.vocal_separations.push(vocal_sep);
|
||||
|
||||
// 添加视频 materials 下面的 Videos
|
||||
let video = await this.AddMaterialVideo(image_path);
|
||||
this.draft_json.materials.videos.push(video);
|
||||
|
||||
// 添加track轨道
|
||||
let segment = await this.AddTracksSegments();
|
||||
return segment;
|
||||
// this.draft_json.tracks.segments.push(segment);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将所有的文件全部都写轨道上面
|
||||
*/
|
||||
async AddAllImageToTracks() {
|
||||
let img_dir = path.normalize(this.image_dir);
|
||||
let files = await fspromises.readdir(img_dir)
|
||||
let imageFiles = files.filter(file => /\.(png)$/i.test(file));
|
||||
imageFiles.sort();
|
||||
imageFiles = imageFiles.map(item => path.join(img_dir, item))
|
||||
// console.log(imageFiles);
|
||||
|
||||
// 创建一个tracks
|
||||
let tracks_json = await this.AddTracks("video");
|
||||
//往tracks里面的segments添加图片数据
|
||||
for (let i = 0; i < imageFiles.length; i++) {
|
||||
const image_path = imageFiles[i];
|
||||
let segment = await this.AddOneImageToDraft(image_path);
|
||||
tracks_json.segments.push(segment);
|
||||
// console.log(tracks_json);
|
||||
}
|
||||
|
||||
this.draft_json.tracks.push(tracks_json);
|
||||
// 修改持续时间
|
||||
let duration_time = imageFiles.length * this.one_duration_time;
|
||||
this.iamge_end_time = duration_time;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加materials中的material_animations
|
||||
*/
|
||||
async AddMaterialAnimations() {
|
||||
let material_animations = JSON.parse(await fspromises.readFile(define.add_material_animations_temp_path));
|
||||
let material_animations_id = uuidv4();
|
||||
this.materialAnimationsId = material_animations_id;
|
||||
material_animations.id = material_animations_id;
|
||||
return material_animations;
|
||||
}
|
||||
|
||||
/**
|
||||
* 为字幕添加样式
|
||||
*/
|
||||
async AddTextStyle(material_text_json) {
|
||||
try {
|
||||
let clip_setting = JSON.parse(await fspromises.readFile(define.clip_setting));
|
||||
let text_style = clip_setting.text_style.filter(item => item.id != "0" && item.id != "1");
|
||||
|
||||
// 添加默认样式
|
||||
let c = JSON.parse(material_text_json.content);
|
||||
let data = JSON.parse(`[{\"size\":7.882736,\"fill\":{\"content\":{\"solid\":{\"color\":[1,1,1]}}},\"range\":[0,5]}]`);
|
||||
data[0].range = [0, c.text.length];
|
||||
c["styles"] = data;
|
||||
material_text_json.content = JSON.stringify(c);
|
||||
return material_text_json;
|
||||
|
||||
// 判断是不是添加样式添加样式
|
||||
if (this.style_id == "0") {
|
||||
return material_text_json;
|
||||
} else if (this.style_id == "1") {
|
||||
// 随机
|
||||
const randomIndex = Math.floor(Math.random() * text_style.length);
|
||||
this.style_id = text_style[randomIndex].id;
|
||||
}
|
||||
let style = text_style.filter(item => item.id == this.style_id)[0];
|
||||
let content = JSON.parse(material_text_json.content);
|
||||
|
||||
// 修改范围
|
||||
let textstring = content.text;
|
||||
let length = textstring.length;
|
||||
style.style[0].range = [0, length];
|
||||
|
||||
content.styles = style.style;
|
||||
material_text_json.content = JSON.stringify(content);
|
||||
let path = style.style[0].font.path;
|
||||
let id = style.style[0].font.id;
|
||||
let font_size = style.font_size;
|
||||
let fonts = style.fonts;
|
||||
let style_name = style.style_name;
|
||||
material_text_json.fonts.id = uuidv4();
|
||||
material_text_json.font_category_id = id;
|
||||
material_text_json.fonts.path = path;
|
||||
material_text_json.fonts.title = fonts;
|
||||
material_text_json.check_flag = font_size;
|
||||
material_text_json.title = fonts;
|
||||
material_text_json.font_size = font_size;
|
||||
material_text_json.style_name = style_name;
|
||||
return material_text_json;
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得一个单的materialsText
|
||||
*/
|
||||
async AddMaterialsText(textString) {
|
||||
let material_text_json = JSON.parse(await fspromises.readFile(define.add_material_text_temp_path));
|
||||
let material_text_id = uuidv4();
|
||||
this.materialsTextID = material_text_id;
|
||||
material_text_json.id = material_text_id;
|
||||
|
||||
// 设置内容
|
||||
let content = JSON.parse(material_text_json.content);
|
||||
content.text = textString;
|
||||
material_text_json.content = JSON.stringify(content);
|
||||
|
||||
material_text_json = await this.AddTextStyle(material_text_json);
|
||||
return material_text_json;
|
||||
}
|
||||
|
||||
async ModifyTextClipTransform(text_segments) {
|
||||
// console.log(text_segments);
|
||||
|
||||
let clip_setting = JSON.parse(await fspromises.readFile(define.clip_setting));
|
||||
let text_style = clip_setting.text_style;
|
||||
if (text_style.length <= 0)
|
||||
return text_segments;
|
||||
let style = text_style.filter(item => item.id == this.style_id)[0];
|
||||
text_segments.clip = style.clip;
|
||||
return text_segments;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加一个字幕到tracks
|
||||
* @returns
|
||||
*/
|
||||
async AddOneTextToDraft(timeObj) {
|
||||
try {
|
||||
|
||||
// 添加 materials 中的 material_animations
|
||||
let material_animattions = await this.AddMaterialAnimations();
|
||||
this.draft_json.materials.material_animations.push(material_animattions);
|
||||
|
||||
// 添加 materials 下面的texts
|
||||
let material_text = await this.AddMaterialsText(timeObj.text);
|
||||
this.draft_json.materials.texts.push(material_text);
|
||||
|
||||
let text_segments = JSON.parse(await fspromises.readFile(define.add_track_text_segments_temp_path));
|
||||
let textId = uuidv4();
|
||||
this.textId = textId;
|
||||
text_segments.id = textId;
|
||||
text_segments.extra_material_refs = [this.materialAnimationsId];
|
||||
text_segments.material_id = this.materialsTextID;
|
||||
text_segments.target_timerange.start = timeObj.start;
|
||||
text_segments.target_timerange.duration = timeObj.end - timeObj.start;
|
||||
|
||||
// 修改样式偏移量
|
||||
text_segments = await this.ModifyTextClipTransform(text_segments);
|
||||
|
||||
return text_segments;
|
||||
|
||||
} catch (error) {
|
||||
return {
|
||||
code: 0,
|
||||
message: `Error Message ${error}`
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加所有的text到tracks里面
|
||||
*/
|
||||
async AddAllTextToTrack() {
|
||||
// 添加一个tracks
|
||||
let new_tracks = await this.AddTracks("text");
|
||||
// 计算时间
|
||||
let srt_data = (await fspromises.readFile(this.srtPath)).toString("utf-8");
|
||||
const entries = srt_data.replace(/\r\n/g, '\n').split('\n\n');
|
||||
let data = entries.map(entry => {
|
||||
const lines = entry.split('\n');
|
||||
if (lines.length >= 3) {
|
||||
const times = lines[1];
|
||||
const text = lines.slice(2).join(' ');
|
||||
const [start, end] = times.split(' --> ').map(time => {
|
||||
const [hours, minutes, seconds] = time.split(':');
|
||||
const [sec, millis] = seconds.split(',');
|
||||
return ((parseInt(hours) * 3600 + parseInt(minutes) * 60 + parseInt(sec)) * 1000 + parseInt(millis)) * 1000;
|
||||
});
|
||||
return { start, end, text };
|
||||
}
|
||||
}).filter(entry => entry);
|
||||
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
const text = data[i];
|
||||
let text_se = await this.AddOneTextToDraft(text);
|
||||
// console.log(text_se);
|
||||
new_tracks.segments.push(text_se);
|
||||
if (i == data.length - 1) {
|
||||
this.text_end_time = text.end;
|
||||
}
|
||||
}
|
||||
// console.log(this.draft_json)
|
||||
this.draft_json.tracks.push(new_tracks);
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加 materials下面的 beats
|
||||
*/
|
||||
async AddMaterialsBeats() {
|
||||
let beats_json = JSON.parse(await fspromises.readFile(define.add_materials_beats_tmp_path));
|
||||
let materialsBeatsID = uuidv4();
|
||||
this.materialsBeatsID = materialsBeatsID;
|
||||
beats_json.id = materialsBeatsID;
|
||||
return beats_json;
|
||||
}
|
||||
|
||||
|
||||
async getAudioDuration(filePath) {
|
||||
const ext = filePath.split('.').pop().toLowerCase();
|
||||
|
||||
switch (ext) {
|
||||
case 'mp3':
|
||||
try {
|
||||
const metadata = await mm.parseFile(filePath);
|
||||
return metadata.format.duration;
|
||||
} catch (error) {
|
||||
throw new Error(error);
|
||||
}
|
||||
|
||||
case 'wav':
|
||||
return new Promise((resolve, reject) => {
|
||||
wavFileInfo.infoByFilename(filePath, (err, info) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
} else {
|
||||
resolve(info.duration);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
default:
|
||||
throw new Error("不支持的文件类型");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加 materials 下面的 audios
|
||||
*/
|
||||
async AddMaterialsAudios(musicPath) {
|
||||
try {
|
||||
let audios_json = JSON.parse(await fspromises.readFile(define.add_materials_audios_tmp_path));
|
||||
let mp3_name = path.basename(musicPath);
|
||||
let time = await this.getAudioDuration(path.normalize(musicPath));
|
||||
let duration_time = time * 1000000;
|
||||
if (this.audios_duration_time == undefined) {
|
||||
this.audios_duration_time = duration_time;
|
||||
}
|
||||
let audiosID = uuidv4();
|
||||
this.materialsAudiosID = audiosID;
|
||||
audios_json.id = audiosID;
|
||||
audios_json.name = mp3_name;
|
||||
audios_json.duration = duration_time;
|
||||
audios_json.path = musicPath;
|
||||
console.log(audios_json)
|
||||
return audios_json;
|
||||
|
||||
} catch (error) {
|
||||
throw new Error(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加tracks下面的audios下面的Segments
|
||||
*/
|
||||
async AddAudioTracksSegments() {
|
||||
try {
|
||||
let audio_segments = JSON.parse(await fspromises.readFile(define.add_tracks_audio_segments_tmp_path));
|
||||
let audioId = uuidv4();
|
||||
this.tracksAudioId = audioId;
|
||||
audio_segments.id = audioId;
|
||||
audio_segments.material_id = this.materialsAudiosID;
|
||||
audio_segments.extra_material_refs = [];
|
||||
audio_segments.extra_material_refs.push(this.speedID);
|
||||
audio_segments.extra_material_refs.push(this.materialsBeatsID);
|
||||
audio_segments.extra_material_refs.push(this.soundChannelId);
|
||||
audio_segments.extra_material_refs.push(this.vocalSeparationsId);
|
||||
audio_segments.source_timerange.duration = this.audios_duration_time;
|
||||
audio_segments.target_timerange.duration = this.audios_duration_time;
|
||||
return audio_segments;
|
||||
} catch (error) {
|
||||
throw new Error(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加配音
|
||||
*/
|
||||
async AddDubbingMusic(musicPath) {
|
||||
// 添加speeds
|
||||
let speeds = await this.AddSpeeds();
|
||||
this.draft_json.materials.speeds.push(speeds);
|
||||
|
||||
// 添加beats
|
||||
let beats = await this.AddMaterialsBeats();
|
||||
this.draft_json.materials.beats.push(beats);
|
||||
|
||||
// 添加 sound_channel_mappings
|
||||
let sound_channel_mappings = await this.AddSoundChannelMapping();
|
||||
this.draft_json.materials.sound_channel_mappings.push(sound_channel_mappings)
|
||||
|
||||
// 添加 materials 下面的 audios
|
||||
let audios = await this.AddMaterialsAudios(musicPath);
|
||||
this.draft_json.materials.audios.push(audios);
|
||||
|
||||
// 添加一个track
|
||||
let tracks_json = await this.AddTracks("audio");
|
||||
|
||||
let audio_segments_json = await this.AddAudioTracksSegments();
|
||||
tracks_json.segments.push(audio_segments_json);
|
||||
this.draft_json.tracks.push(tracks_json);
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加背景音乐
|
||||
* @param {背景音乐的ID} background_music_id
|
||||
*/
|
||||
async AddRandomBackfroundMusic(background_music_id) {
|
||||
try {
|
||||
// 获取背景音乐文件夹
|
||||
let clip_setting_json = JSON.parse(await fspromises.readFile(define.clip_setting));
|
||||
let setting = clip_setting_json.background_music_setting.filter(item => item.id == background_music_id);
|
||||
console.log(setting);
|
||||
let folder_path = setting[0].folder_path;
|
||||
console.log(folder_path);
|
||||
let files = await tools.getFilesWithExtensions(folder_path, [".mp3", ".wav"]);
|
||||
if (files.length == 0) {
|
||||
throw new Error("背景音乐文件夹下面未存在数据");
|
||||
}
|
||||
// 获取随机的数据
|
||||
const randomIndex = Math.floor(Math.random() * files.length);
|
||||
let musicPath = files[randomIndex];
|
||||
|
||||
// 添加speeds
|
||||
let speeds = await this.AddSpeeds();
|
||||
this.draft_json.materials.speeds.push(speeds);
|
||||
|
||||
// 添加beats
|
||||
let beats = await this.AddMaterialsBeats();
|
||||
this.draft_json.materials.beats.push(beats);
|
||||
|
||||
// 添加 sound_channel_mappings
|
||||
let sound_channel_mappings = await this.AddSoundChannelMapping();
|
||||
this.draft_json.materials.sound_channel_mappings.push(sound_channel_mappings)
|
||||
|
||||
// 添加 materials 下面的 audios
|
||||
let audios = await this.AddMaterialsAudios(musicPath);
|
||||
// audios.duration = this.text_end_time;
|
||||
this.draft_json.materials.audios.push(audios);
|
||||
|
||||
// 添加一个track
|
||||
let tracks_json = await this.AddTracks("audio");
|
||||
let audio_segments_json = await this.AddAudioTracksSegments();
|
||||
// 修改
|
||||
audio_segments_json.source_timerange.duration = this.audios_duration_time;
|
||||
audio_segments_json.target_timerange.duration = this.audios_duration_time;
|
||||
tracks_json.segments.push(audio_segments_json);
|
||||
this.draft_json.tracks.push(tracks_json);
|
||||
|
||||
} catch (error) {
|
||||
throw new Error(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加温馨提示
|
||||
*/
|
||||
async AddFriendlyReminder() {
|
||||
// 直接push
|
||||
try {
|
||||
|
||||
return;
|
||||
|
||||
let friendlyReminder = null;
|
||||
let friendlyReminderSetting = JSON.parse(await fspromises.readFile(define.clip_setting)).friendly_reminder_setting;
|
||||
friendlyReminderSetting = friendlyReminderSetting.filter(item => item.id != "0" && item.id != "1");
|
||||
if (friendlyReminderSetting.length <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.friendlyReminderId == "0") {
|
||||
return;
|
||||
} else if (this.friendlyReminderId == "1") {
|
||||
// 获取随机的数据
|
||||
const randomIndex = Math.floor(Math.random() * friendlyReminderSetting.length);
|
||||
friendlyReminder = friendlyReminderSetting[randomIndex];
|
||||
} else {
|
||||
friendlyReminder = friendlyReminderSetting.filter(item => item.id == this.friendlyReminderId);
|
||||
}
|
||||
|
||||
// 添加 materials 下面的 material_animations
|
||||
this.draft_json.materials.material_animations.push(friendlyReminder.material_animations);
|
||||
// 添加 materials下面的texts
|
||||
this.draft_json.materials.texts.push(friendlyReminder.texts);
|
||||
// 添加 tracks
|
||||
let track = friendlyReminder.tracks;
|
||||
// 修改持续时间
|
||||
track.segments[0].target_timerange.duration = this.audios_duration_time;
|
||||
this.draft_json.tracks.push(track);
|
||||
} catch (error) {
|
||||
throw new Error(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改草稿的持续时间
|
||||
*/
|
||||
async ModifyDurationTime() {
|
||||
|
||||
let max_time = Math.max(this.iamge_end_time, this.text_end_time, this.audios_duration_time);
|
||||
this.draft_json.duration = max_time;
|
||||
this.draft_json.canvas_config.height = 1440;
|
||||
this.draft_json.canvas_config.width = 1920;
|
||||
this.draft_json.canvas_config.ratio = "4:3";
|
||||
}
|
||||
|
||||
/**
|
||||
* 将文件写道指定的位置
|
||||
*/
|
||||
async WriteDraftFile() {
|
||||
await fspromises.writeFile(this.draftPath, JSON.stringify(this.draft_json));
|
||||
}
|
||||
|
||||
async find_draft_node(nodes, type, value) {
|
||||
for (let index = 0; index < nodes.length; index++) {
|
||||
let node = nodes[index];
|
||||
if (node[type] == value) {
|
||||
return node
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将草稿图片和文字对齐
|
||||
*/
|
||||
async AlginDraftImgToText() {
|
||||
// 所有的字幕轨道里面的数据,读取出来
|
||||
let img_nodes = (await this.find_draft_node(this.draft_json.tracks, "type", "video")).segments;
|
||||
|
||||
//将最后一个数据修改为背景音乐的最后时间
|
||||
this.srt_information[this.srt_information.length - 1].end_time = this.audios_duration_time / 1000;
|
||||
|
||||
// 开始对齐
|
||||
for (let i = 0; i < this.srt_information.length; i++) {
|
||||
if (img_nodes.length < i) {
|
||||
break;
|
||||
}
|
||||
const element = this.srt_information[i];
|
||||
let duration = 0;
|
||||
if (i + 1 < this.srt_information.length) {
|
||||
duration = (this.srt_information[i + 1].start_time - element.start_time - 1) * 1000;
|
||||
} else {
|
||||
duration = (element.end_time - element.start_time) * 1000;
|
||||
}
|
||||
img_nodes[i].source_timerange.duration = duration;
|
||||
img_nodes[i].target_timerange.duration = duration;
|
||||
img_nodes[i].target_timerange.start = element.start_time * 1000;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过 key_frame 返回关键帧数据
|
||||
* @param {*} key_frame 关键帧配置
|
||||
*/
|
||||
async GetFrameData(key_frame) {
|
||||
if (key_frame.key_frame == "KFTypePositionY") {
|
||||
return key_frame.up_down_key_frame;
|
||||
} else if (key_frame.key_frame == "KFTypePositionX") {
|
||||
return key_frame.left_right_key_frame;
|
||||
} else if (key_frame.key_frame == "KFTypeScale") {
|
||||
return key_frame.scale_key_frame;
|
||||
} else {
|
||||
return {
|
||||
"default_scale": 100,
|
||||
"start_position": 0,
|
||||
"end_position": 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 图片添加关键帧
|
||||
*/
|
||||
async AddKeyFarme() {
|
||||
let img_nodes = (await this.find_draft_node(this.draft_json.tracks, "type", "video")).segments;
|
||||
let key_frame_tmp_data = JSON.parse(await fspromises.readFile(define.add_keyframe_tmp_path, "utf-8"));
|
||||
// 添加关键帧
|
||||
// 将最后一个数据修改为背景音乐的最后时间
|
||||
this.srt_information[this.srt_information.length - 1].end_time = this.audios_duration_time / 1000;
|
||||
|
||||
let key_frame_setting = await tools.getJsonFilePropertyValue(define.clip_setting, "key_frame", null, false);
|
||||
// 判断关键帧配置是不是存在。不存在直接结束
|
||||
if (key_frame_setting == null) {
|
||||
return;
|
||||
}
|
||||
let key_frame_pos = await this.GetFrameData(key_frame_setting);
|
||||
let isFixedSpeed = key_frame_setting.isFixedSpeed;
|
||||
let key_frame_time = key_frame_setting.key_frame_time * 1000000;
|
||||
let isDown = true;
|
||||
let scale_rate = key_frame_pos.default_scale / 100;
|
||||
|
||||
// 获取通用的关键帧配置,然后添加到每一个图片上面(可以设置时间。当前图片的持续实现小于设置的时间。会计算不要过快)
|
||||
for (let i = 0; i < img_nodes.length; i++) {
|
||||
let element = img_nodes[i];
|
||||
let image_duartion = cloneDeep(element.source_timerange.duration);
|
||||
|
||||
let up_pos = Math.abs(key_frame_pos.start_position);
|
||||
let down_pos = Math.abs(key_frame_pos.end_position);
|
||||
|
||||
if (key_frame_setting.key_frame == "KFTypePositionY") {
|
||||
|
||||
|
||||
// 勾选了匀速。需要计算时间(计算比例)
|
||||
|
||||
if (isFixedSpeed && image_duartion < key_frame_time) {
|
||||
let time_rate = image_duartion / key_frame_time;
|
||||
up_pos = up_pos * time_rate;
|
||||
down_pos = down_pos * time_rate;
|
||||
}
|
||||
let key_frame_tmp = cloneDeep(key_frame_tmp_data)
|
||||
let up_pos_rate = isDown ? (up_pos / this.draft_json.canvas_config.height) : (0 - up_pos / this.draft_json.canvas_config.height)
|
||||
key_frame_tmp.id = uuidv4();
|
||||
key_frame_tmp.keyframe_list[0].id == uuidv4();
|
||||
key_frame_tmp.keyframe_list[0].values = [up_pos_rate];
|
||||
|
||||
let dow_pos_rate = isDown ? (0 - down_pos / this.draft_json.canvas_config.height) : (down_pos / this.draft_json.canvas_config.height)
|
||||
key_frame_tmp.keyframe_list[1].id = uuidv4();
|
||||
key_frame_tmp.keyframe_list[1].time_offset = image_duartion;
|
||||
key_frame_tmp.keyframe_list[1].values = [dow_pos_rate];
|
||||
|
||||
key_frame_tmp.property_type = key_frame_setting.key_frame;
|
||||
|
||||
// 修改缩放倍率
|
||||
element.clip.scale.x = scale_rate;
|
||||
element.clip.scale.y = scale_rate;
|
||||
element.clip.transform.y = dow_pos_rate;
|
||||
isDown = !isDown;
|
||||
element.common_keyframes.push(key_frame_tmp);
|
||||
} else if (key_frame_setting.key_frame == "KFTypePositionX") {
|
||||
// 勾选了匀速。需要计算时间(计算比例)
|
||||
if (isFixedSpeed && image_duartion < key_frame_time) {
|
||||
let time_rate = image_duartion / key_frame_time;
|
||||
up_pos = up_pos * time_rate;
|
||||
down_pos = down_pos * time_rate;
|
||||
}
|
||||
let key_frame_tmp = cloneDeep(key_frame_tmp_data)
|
||||
let up_pos_rate = isDown ? (up_pos / this.draft_json.canvas_config.width) : (0 - up_pos / this.draft_json.canvas_config.width)
|
||||
key_frame_tmp.id = uuidv4();
|
||||
key_frame_tmp.keyframe_list[0].id == uuidv4();
|
||||
key_frame_tmp.keyframe_list[0].values = [up_pos_rate];
|
||||
|
||||
let dow_pos_rate = isDown ? (0 - down_pos / this.draft_json.canvas_config.width) : (down_pos / this.draft_json.canvas_config.width)
|
||||
key_frame_tmp.keyframe_list[1].id = uuidv4();
|
||||
key_frame_tmp.keyframe_list[1].time_offset = image_duartion;
|
||||
key_frame_tmp.keyframe_list[1].values = [dow_pos_rate];
|
||||
|
||||
key_frame_tmp.property_type = key_frame_setting.key_frame;
|
||||
|
||||
// 修改缩放倍率
|
||||
element.clip.scale.x = scale_rate;
|
||||
element.clip.scale.y = scale_rate;
|
||||
element.clip.transform.x = dow_pos_rate;
|
||||
isDown = !isDown;
|
||||
element.common_keyframes.push(key_frame_tmp);
|
||||
}
|
||||
else if (key_frame_setting.key_frame == "KFTypeScale") {
|
||||
if (isFixedSpeed && image_duartion < key_frame_time) {
|
||||
let time_rate = image_duartion / key_frame_time;
|
||||
// 计算方式和上面的不同
|
||||
let sub_total = Math.abs(up_pos - down_pos);
|
||||
let currwnt_rate = sub_total * (1 - time_rate);
|
||||
up_pos = up_pos + currwnt_rate / 2;
|
||||
down_pos = down_pos - currwnt_rate / 2;
|
||||
}
|
||||
|
||||
// 修改上面的数据,添加Y轴缩放
|
||||
let key_frame_tmp = cloneDeep(key_frame_tmp_data)
|
||||
let up_pos_rate = isDown ? up_pos / 100 : down_pos / 100;
|
||||
key_frame_tmp.id = uuidv4();
|
||||
key_frame_tmp.keyframe_list[0].id == uuidv4();
|
||||
key_frame_tmp.keyframe_list[0].values = [up_pos_rate];
|
||||
|
||||
let dow_pos_rate = isDown ? down_pos / 100 : up_pos / 100;
|
||||
key_frame_tmp.keyframe_list[1].id = uuidv4();
|
||||
key_frame_tmp.keyframe_list[1].time_offset = image_duartion;
|
||||
key_frame_tmp.keyframe_list[1].values = [dow_pos_rate];
|
||||
|
||||
key_frame_tmp.property_type = key_frame_setting.key_frame + "X";
|
||||
|
||||
// 修改上面的数据,添加Y轴缩放
|
||||
// 修改缩放倍率
|
||||
element.clip.scale.x = isDown ? up_pos : dow_pos_rate;
|
||||
element.clip.scale.y = isDown ? up_pos : dow_pos_rate;
|
||||
element.clip.transform.x = 0;
|
||||
element.common_keyframes.push(key_frame_tmp);
|
||||
|
||||
key_frame_tmp = cloneDeep(key_frame_tmp)
|
||||
key_frame_tmp.id = uuidv4();
|
||||
key_frame_tmp.keyframe_list[0].id == uuidv4();
|
||||
key_frame_tmp.keyframe_list[1].id = uuidv4();
|
||||
key_frame_tmp.property_type = key_frame_setting.key_frame + "Y";
|
||||
element.common_keyframes.push(key_frame_tmp);
|
||||
|
||||
isDown = !isDown;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 添加草稿
|
||||
*/
|
||||
async addDraft() {
|
||||
try {
|
||||
await this.InitData();
|
||||
await this.LoadDraftJson();
|
||||
await this.AddAllImageToTracks();
|
||||
await this.AddAllTextToTrack();
|
||||
await this.AddDubbingMusic(path.normalize(this.value[1].audio_path));
|
||||
if (this.value[1].background_music != "" && this.value[1].background_music != undefined && this.value[1].background_music != null) {
|
||||
await this.AddRandomBackfroundMusic(this.value[1].background_music);
|
||||
}
|
||||
|
||||
// 添加温馨提示
|
||||
// await this.AddFriendlyReminder();
|
||||
|
||||
|
||||
await this.ModifyDurationTime();
|
||||
|
||||
// 对齐草稿数据
|
||||
await this.AlginDraftImgToText();
|
||||
|
||||
// 添加关键帧
|
||||
await this.AddKeyFarme();
|
||||
|
||||
await this.WriteDraftFile();
|
||||
return {
|
||||
code: 1,
|
||||
draft_name: this.draft_name
|
||||
}
|
||||
} catch (error) {
|
||||
return {
|
||||
code: 0,
|
||||
message: `An error occurred: ${error}`
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,335 @@
|
||||
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();
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改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 {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 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;
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 高清指定的文件夹
|
||||
* @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;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成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');
|
||||
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前项目的生图任务列表
|
||||
*/
|
||||
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) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,493 @@
|
||||
|
||||
import { Tools } from "../tools";
|
||||
import path from "path";
|
||||
import { DEFINE_STRING } from "../../define/define_string";
|
||||
import { define } from "../../define/define";
|
||||
import { PublicMethod } from "../Public/publicMethod";
|
||||
import { SD } from "../Public/SD"
|
||||
const util = require('util');
|
||||
const { spawn, exec } = require('child_process');
|
||||
import axios from "axios";
|
||||
const sharp = require('sharp');
|
||||
const execAsync = util.promisify(exec);
|
||||
const { v4: uuidv4 } = require('uuid'); // 引入UUID库来生成唯一标识符
|
||||
let fspromises = require("fs").promises;
|
||||
import { MD5 } from "crypto-js";
|
||||
import { ImageSetting } from "../../define/setting/imageSetting";
|
||||
|
||||
|
||||
export class ImageGenerate {
|
||||
constructor(global) {
|
||||
this.global = global;
|
||||
this.tools = new Tools();
|
||||
this.pm = new PublicMethod(global);
|
||||
this.sd = new SD(global);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定名称的提示词json文件数据
|
||||
* @param {*} value
|
||||
*/
|
||||
async GetPromptJson(value) {
|
||||
try {
|
||||
let json_path = path.join(this.global.config.project_path, "tmp/input_crop/" + value + ".json");
|
||||
let isExist = await this.tools.checkExists(json_path);
|
||||
if (!isExist) {
|
||||
throw new Error("提示词文件不存在");
|
||||
}
|
||||
let res = JSON.parse(await fspromises.readFile(json_path, 'utf-8'));
|
||||
return {
|
||||
value: 1,
|
||||
data: res
|
||||
}
|
||||
} catch (error) {
|
||||
return {
|
||||
code: 0,
|
||||
message: error.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改指定任务队列的状态
|
||||
* @param {*} value 0 使用什么查找(id。out_folder,) 1查找的值 2 状态值
|
||||
* @returns
|
||||
*/
|
||||
async ModifyGenerateTaskStatus(value) {
|
||||
try {
|
||||
await this.pm.ModifyTaskStatus(value[0], value[1], value[2]);
|
||||
return {
|
||||
code: 1
|
||||
}
|
||||
} catch (error) {
|
||||
return {
|
||||
code: 0,
|
||||
message: error.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 修改本地的参数
|
||||
* @param {修改json的参数} value
|
||||
*/
|
||||
async ModifyInpurCropJson(value) {
|
||||
try {
|
||||
value = JSON.parse(value);
|
||||
let prompt = value.prompt;
|
||||
// 重绘幅度
|
||||
let denoising_strength = value.denoising_strength;
|
||||
let json_path = path.join(path.join(global.config.project_path, 'tmp/input_crop'), value.name + ".json");
|
||||
let old_json = JSON.parse(await fspromises.readFile(json_path, 'utf-8'));
|
||||
old_json.webui_config.prompt = prompt;
|
||||
old_json.webui_config.denoising_strength = denoising_strength;
|
||||
old_json.model = value.model;
|
||||
old_json.adetailer = value.adetailer;
|
||||
old_json.chinese_prompt = value.chinese_prompt;
|
||||
|
||||
if (value.modify_old) {
|
||||
old_json.webui_config.init_images = value.modify_old;
|
||||
}
|
||||
|
||||
await fspromises.writeFile(json_path, JSON.stringify(old_json));
|
||||
return {
|
||||
code: 1
|
||||
}
|
||||
} catch (error) {
|
||||
throw new Error(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回文件夹里的所有的图片,以及其他信息
|
||||
* @param {图片文件夹} value
|
||||
* @returns
|
||||
*/
|
||||
async getFolderImageList(mainWindow, value, isReturn) {
|
||||
try {
|
||||
let img_dir = path.normalize(value);
|
||||
let base_name = path.basename(img_dir);
|
||||
let tmp = path.dirname(img_dir);
|
||||
let input_path = path.join(tmp, "input_crop")
|
||||
// console.log(tmp, input_path)
|
||||
let files = await fspromises.readdir(img_dir)
|
||||
let imageFiles = files.filter(file => /\.(png)$/i.test(file));
|
||||
imageFiles.sort();
|
||||
|
||||
let res = [];
|
||||
let task_list_json = JSON.parse(await fspromises.readFile(path.join(this.global.config.project_path, 'scripts/task_list.json'), 'utf-8'));
|
||||
// 处理数据
|
||||
for (let i = 0; i < imageFiles.length; i++) {
|
||||
const img = imageFiles[i];
|
||||
let imput_path = path.join(input_path, img);
|
||||
let img_json = path.join(input_path, img + '.json');
|
||||
// 判断文件是不是存在
|
||||
let prompt_json = JSON.parse(await fspromises.readFile(img_json));
|
||||
|
||||
let output_list = await this.pm.getSubFolderList(path.join(this.global.config.project_path, "tmp"), 'start', 'output_crop');
|
||||
let output_image_list = [];
|
||||
for (let i = 0; i < output_list.length; i++) {
|
||||
const item = output_list[i];
|
||||
// 找到找到文件对应的任务列表的样式和lora
|
||||
let task_list = task_list_json.task_list.filter(a => a.out_folder == item);
|
||||
let output_1 = path.join(tmp, item);
|
||||
let obj = {
|
||||
id: uuidv4(),
|
||||
img_path: path.join(output_1, img),
|
||||
image_style: task_list[0].image_style,
|
||||
lora: task_list[0].lora
|
||||
}
|
||||
output_image_list.push(obj);
|
||||
|
||||
}
|
||||
let main_task_list = task_list_json.task_list.filter(a => a.out_folder == base_name);
|
||||
let obj = {
|
||||
name: img,
|
||||
id: uuidv4(),
|
||||
sampler_name: prompt_json.webui_config.sampler_name,
|
||||
prompt: prompt_json.webui_config.prompt,
|
||||
chinese_prompt: prompt_json.chinese_prompt,
|
||||
negative_prompt: prompt_json.webui_config.negative_prompt,
|
||||
denoising_strength: prompt_json.webui_config.denoising_strength,
|
||||
seed: prompt_json.webui_config.seed,
|
||||
image: path.join(img_dir, img),
|
||||
oldImage: prompt_json.webui_config.init_images,
|
||||
output_list: output_image_list,
|
||||
model: prompt_json.model,
|
||||
image_style: main_task_list[0].image_style,
|
||||
lora: main_task_list[0].lora,
|
||||
image_style_list: main_task_list[0].image_style_list,
|
||||
adetailer: prompt_json.adetailer,
|
||||
width: prompt_json.webui_config.width,
|
||||
height: prompt_json.webui_config.height
|
||||
}
|
||||
res.push(obj)
|
||||
// 获取提示词
|
||||
}
|
||||
if (isReturn) {
|
||||
return {
|
||||
code: 1,
|
||||
windowID: mainWindow.id,
|
||||
value: res
|
||||
}
|
||||
} else {
|
||||
mainWindow.webContents.send(DEFINE_STRING.RETURN_IMAGE_PROMPT, {
|
||||
code: 1,
|
||||
windowID: mainWindow.id,
|
||||
value: res
|
||||
})
|
||||
}
|
||||
} catch (error) {
|
||||
return {
|
||||
code: 0,
|
||||
message: `Error message ${error.toString()}`
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将反推的图片的信息添加到一个json文件中
|
||||
*/
|
||||
async AddWebuiJson() {
|
||||
try {
|
||||
return await this.pm.AddWebuiJson();
|
||||
} catch (error) {
|
||||
return {
|
||||
code: 0,
|
||||
message: error.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改任务队列数据
|
||||
* @param {替换后的值} value
|
||||
*/
|
||||
async ModifyImageTaskList(value) {
|
||||
try {
|
||||
let res = await this.pm.ModifyImageTaskList(value);
|
||||
if (res) {
|
||||
return {
|
||||
code: 1,
|
||||
message: "替换成功"
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
return {
|
||||
code: 0,
|
||||
message: error.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取生成图片的任务队列(可以添加部分判断)
|
||||
*/
|
||||
async GetGenerateTaskList() {
|
||||
try {
|
||||
return await this.pm.GetImageTask()
|
||||
} catch (error) {
|
||||
return {
|
||||
code: 0,
|
||||
message: error.message
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成任务中的队列
|
||||
* @param {传入所有执行生图队列的任务} value
|
||||
*/
|
||||
async GenerateImageInSelectTask(value) {
|
||||
try {
|
||||
let batch = DEFINE_STRING.QUEUE_BATCH.SD_BACKSTEP_GENERATE_IMAGE
|
||||
let taskPath = path.join(this.global.config.project_path, 'scripts/task_list.json');
|
||||
let task_list_json = JSON.parse(await fspromises.readFile(taskPath, 'utf-8'));
|
||||
let auto_save_image = await this.tools.getJsonFilePropertyValue(define.img_base, "auto_save_image", {}, false);
|
||||
let png_files = [];
|
||||
// 读取队列中的任务,然后添加队列
|
||||
for (let i = 0; i < value.length; i++) {
|
||||
const element = value[i];
|
||||
let task_list = task_list_json.task_list.filter(item => item.id == element)[0];
|
||||
let seed = -1;
|
||||
let images = await this.tools.getFilesWithExtensions(path.join(this.global.config.project_path, 'tmp/input_crop'), '.png');
|
||||
await fspromises.mkdir(path.join(this.global.config.project_path, 'tmp/' + task_list.out_folder), { recursive: true });
|
||||
if (images.length <= 0) {
|
||||
throw new Error("未检测到抽帧图片。请检查");
|
||||
}
|
||||
if (images.length > auto_save_image.save_match_count) {
|
||||
png_files = await this.tools.getFilesWithExtensions(auto_save_image.main_save_folder, '.png');
|
||||
}
|
||||
this.global.requestQuene.enqueue(async () => {
|
||||
let res = await this.sd.OneImageGeneration(images[0], task_list, seed);
|
||||
let tmp_seed = -1;
|
||||
if (seed == -1) {
|
||||
tmp_seed = res;
|
||||
}
|
||||
for (let j = 1; j < images.length; j++) {
|
||||
const item = images[j];
|
||||
|
||||
let has_permission = false;
|
||||
// 判断权限
|
||||
let permission = this.global.permission;
|
||||
|
||||
if (permission && permission.length >= 0) {
|
||||
if (permission.indexOf(DEFINE_STRING.PERMISSIONS.AUTO_SAVE_IMAGE_PERMISSION) >= 0) {
|
||||
has_permission = true;
|
||||
}
|
||||
} else {
|
||||
has_permission = true;
|
||||
}
|
||||
|
||||
if (auto_save_image.save_match_count && j >= auto_save_image.save_match_count && has_permission && auto_save_image.auto_match) {
|
||||
// 现在随机匹配视频
|
||||
// 获取指定的文件夹中的图片
|
||||
let randomData = png_files[Math.floor(Math.random() * png_files.length)];
|
||||
let base_name = path.basename(item);
|
||||
let copy_path = path.join(this.global.config.project_path, 'tmp/' + task_list.out_folder, base_name);
|
||||
await this.tools.copyFileOrDirectory(randomData, copy_path);
|
||||
} else {
|
||||
this.global.requestQuene.enqueue(async () => {
|
||||
await this.sd.OneImageGeneration(item, task_list, tmp_seed);
|
||||
}, `${task_list.out_folder}_${images[j]}`, batch, task_list.out_folder)
|
||||
}
|
||||
}
|
||||
|
||||
}, `${task_list.out_folder}_${images[0]}`, batch, task_list.out_folder)
|
||||
// }
|
||||
task_list.status = 'queue';
|
||||
|
||||
// 开始批次执行(全部刷新)
|
||||
this.global.newWindow[0].win.webContents.send(DEFINE_STRING.IMAGE_TASK_STATUS_REFRESH, task_list);
|
||||
|
||||
// 判断是不是还有批次执行的任务
|
||||
// 监听子批次完成(修改当前批次的状态)
|
||||
this.global.requestQuene.setSubBatchCompletionCallback(batch, task_list.out_folder, async (failedTasks) => {
|
||||
console.log(failedTasks)
|
||||
if (failedTasks.length > 0) {
|
||||
|
||||
} else {
|
||||
// 判断是不是有错误。没有错误的话。直接修改状态。有错误直接记录错误(写入一个就行)
|
||||
task_list_json = JSON.parse(await fspromises.readFile(taskPath, 'utf-8'));
|
||||
// 全部执行完毕之后,修改
|
||||
task_list_json.task_list.map(a => {
|
||||
if (a.out_folder == task_list.out_folder) {
|
||||
a.status = "ok";
|
||||
}
|
||||
})
|
||||
// 写入
|
||||
await fspromises.writeFile(taskPath, JSON.stringify(task_list_json));
|
||||
|
||||
this.global.newWindow[0].win.webContents.send(DEFINE_STRING.IMAGE_TASK_STATUS_REFRESH, {
|
||||
out_folder: task_list.out_folder,
|
||||
status: "ok"
|
||||
});
|
||||
|
||||
this.global.newWindow[0].win.webContents.send(DEFINE_STRING.VIDEO_GENERATE_STATUS_REFRESH, {
|
||||
out_folder: task_list.out_folder,
|
||||
status: "ok"
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 监听总批次完成
|
||||
this.global.requestQuene.setBatchCompletionCallback(batch, (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
|
||||
})
|
||||
} else {
|
||||
this.global.newWindow[0].win.webContents.send(DEFINE_STRING.SHOW_MESSAGE_DIALOG, {
|
||||
code: 1,
|
||||
message: "所有生成任务完成"
|
||||
})
|
||||
}
|
||||
})
|
||||
// 写回
|
||||
await fspromises.writeFile(taskPath, JSON.stringify(task_list_json));
|
||||
return {
|
||||
code: 1,
|
||||
}
|
||||
} catch (error) {
|
||||
return {
|
||||
code: 0,
|
||||
message: error.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量高清文件夹
|
||||
* @param {项目下面的文件夹的值} value
|
||||
*/
|
||||
async ImproveResolution(value) {
|
||||
try {
|
||||
let batch = DEFINE_STRING.QUEUE_BATCH.LOCAL_IMAGE_IMPROVE;
|
||||
|
||||
// 加入队列
|
||||
for (let i = 0; i < value.length; i++) {
|
||||
const folder = value[i];
|
||||
this.global.requestQuene.enqueue(async () => {
|
||||
await this.pm.ImproveFolder(folder);
|
||||
}, folder, batch)
|
||||
|
||||
// 添加队列后修改状态
|
||||
await this.pm.ModifyTaskStatus('out_folder', folder, "video_improving");
|
||||
this.global.newWindow[0].win.webContents.send(DEFINE_STRING.VIDEO_GENERATE_STATUS_REFRESH, {
|
||||
out_folder: folder,
|
||||
status: "video_improving"
|
||||
})
|
||||
}
|
||||
|
||||
global.requestQuene.setBatchCompletionCallback(batch, async (failedTasks) => {
|
||||
|
||||
let permission = this.global.permission;
|
||||
// 判断权限
|
||||
if (permission && permission.length >= 0) {
|
||||
if (permission.indexOf(DEFINE_STRING.PERMISSIONS.AUTO_SAVE_IMAGE_PERMISSION) >= 0) {
|
||||
// 在高清前,先将要高清的图片保存
|
||||
let res = await ImageSetting.SaveImageToOtherFolder(value, null);
|
||||
if (res.code == 0) {
|
||||
return res;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 在高清前,先将要高清的图片保存
|
||||
let res = await ImageSetting.SaveImageToOtherFolder(value, null);
|
||||
if (res.code == 0) {
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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
|
||||
})
|
||||
} else {
|
||||
this.global.newWindow[0].win.webContents.send(DEFINE_STRING.SHOW_MESSAGE_DIALOG, {
|
||||
code: 1,
|
||||
message: "所有任务高清完成"
|
||||
})
|
||||
}
|
||||
})
|
||||
return {
|
||||
code: 1
|
||||
}
|
||||
} catch (error) {
|
||||
return {
|
||||
code: 0,
|
||||
message: error.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载文件到指定位置
|
||||
* @param {*} value 0: 源图片地址 1: 类型 2: 目标文件地址
|
||||
*/
|
||||
async DownloadImageFile(value) {
|
||||
try {
|
||||
let type = value[1];
|
||||
let img_file = value[0];
|
||||
let target_file = value[2];
|
||||
if (type == "cover") {
|
||||
// 下载封面
|
||||
// 判断封面是不是存在
|
||||
let cover_path = path.join(this.global.config.project_path, 'data/封面');
|
||||
let isExist = await this.tools.checkExists(cover_path);
|
||||
if (!isExist) {
|
||||
await fspromises.mkdir(cover_path, { recursive: true });
|
||||
}
|
||||
// 复制
|
||||
await fspromises.copyFile(img_file, path.join(cover_path, Date.now() + '.png'));
|
||||
return {
|
||||
code: 1,
|
||||
message: "保存成功!"
|
||||
}
|
||||
} else if (type == "replace") {
|
||||
if (!target_file) {
|
||||
throw new Error("目标文件地址不存在");
|
||||
}
|
||||
await fspromises.copyFile(path.normalize(decodeURIComponent(img_file)), path.normalize(decodeURIComponent(target_file)));
|
||||
await this.tools.copyFileOrDirectory(decodeURIComponent(img_file), decodeURIComponent(target_file).replace("output_crop_1", "input_crop"));
|
||||
return {
|
||||
code: 1,
|
||||
}
|
||||
}
|
||||
|
||||
else {
|
||||
return {
|
||||
code: 0,
|
||||
message: "参数错误"
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
return {
|
||||
code: 0,
|
||||
message: error.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,556 @@
|
||||
import path from "path";
|
||||
import { define } from "../../define/define";
|
||||
import { Tools } from "../tools";
|
||||
import { PublicMethod } from "../Public/publicMethod";
|
||||
import { func } from '../func'
|
||||
import { DEFINE_STRING } from "../../define/define_string";
|
||||
const util = require('util');
|
||||
const { spawn, exec } = require('child_process');
|
||||
const execAsync = util.promisify(exec);
|
||||
const fspromises = require("fs").promises;
|
||||
import { SD } from "../Public/SD"
|
||||
|
||||
export class VideoGenerate {
|
||||
constructor(global) {
|
||||
this.global = global;
|
||||
this.tools = new Tools();
|
||||
this.pm = new PublicMethod(global);
|
||||
this.sd = new SD(global);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取之前的基础配置
|
||||
*/
|
||||
async GetVideoGenerateConfig() {
|
||||
try {
|
||||
let res = await this.tools.getJsonFilePropertyValue(path.join(this.global.config.project_path, "scripts/config.json"), "video_config", {}, false);
|
||||
return {
|
||||
code: 1,
|
||||
data: res
|
||||
}
|
||||
} catch (error) {
|
||||
return {
|
||||
code: 0,
|
||||
message: error.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存合成视频的基础信息(Srt,audio,background)
|
||||
* @param {*} value 保存合成视频的基础信息(Srt,audio,background)
|
||||
*/
|
||||
async SaveVideoSrtAndAudioMessage(value) {
|
||||
try {
|
||||
value = JSON.parse(value);
|
||||
await this.tools.writeJsonFilePropertyValue(path.join(this.global.config.project_path, "scripts/config.json"), "video_config", value);
|
||||
return {
|
||||
code: 1
|
||||
}
|
||||
} catch (error) {
|
||||
return {
|
||||
code: 0,
|
||||
message: error.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*全自动合成视频的任务(出图、高清、合成视频)
|
||||
* @param {传入的需要执行的task_list的任务} value
|
||||
*/
|
||||
async ActionAutoVideoTask(value) {
|
||||
try {
|
||||
// 开始添加队列任务
|
||||
// 生图
|
||||
// 将当前的所有任务添加到队列中
|
||||
await this.pm.AddWebuiJson();
|
||||
let batch = DEFINE_STRING.QUEUE_BATCH.AUTO_VIDEO_GENERATE;
|
||||
let taskPath = path.join(this.global.config.project_path, "scripts/task_list.json");
|
||||
|
||||
// // 修改数据(保存srt字幕文件位置。配音文件位置。背景音乐文件)
|
||||
// this.global.fileQueue.enqueue(async () => {
|
||||
// let config_json = JSON.parse(await fspromises.readFile(path.join(this.global.config.project_path, "scripts/config.json"), 'utf-8'));
|
||||
// config_json.srt_path = value[1].srt_path;
|
||||
// config_json.audio_path = value[1].audio_path;
|
||||
// config_json.background_music = value[1].background_music;
|
||||
// await fspromises.writeFile(path.join(this.global.config.project_path, "scripts/config.json"), JSON.stringify(config_json));
|
||||
// })
|
||||
|
||||
await this.tools.writeJsonFilePropertyValue(path.join(this.global.config.project_path, "scripts/config.json"), "video_config", value[1]);
|
||||
|
||||
// 便利所有的队列任务
|
||||
for (let i = 0; i < value[0].length; i++) {
|
||||
// 将所有的数据天添加到队列(总的大队列,有很多的小队列)
|
||||
// 将所有生图任务添加到队列中
|
||||
const task_list = value[0][i];
|
||||
let seed = -1;
|
||||
let subBatchId = `${task_list.out_folder}_image`
|
||||
let images = await this.tools.getFilesWithExtensions(path.join(this.global.config.project_path, 'tmp/input_crop'), '.png');
|
||||
await fspromises.mkdir(path.join(this.global.config.project_path, 'tmp/' + task_list.out_folder), { recursive: true });
|
||||
if (images.length <= 0) {
|
||||
throw new Error("未检测到抽帧图片。请检查");
|
||||
}
|
||||
this.global.requestQuene.enqueue(async () => {
|
||||
let res = await this.sd.OneImageGeneration(images[0], task_list, seed);
|
||||
let tmp_seed = -1;
|
||||
if (seed == -1) {
|
||||
tmp_seed = res;
|
||||
}
|
||||
for (let j = 1; j < images.length; j++) {
|
||||
const element = images[j];
|
||||
this.global.requestQuene.enqueue(async () => {
|
||||
await this.sd.OneImageGeneration(element, task_list, tmp_seed);
|
||||
}, `${task_list.out_folder}_${images[j]}`, batch, subBatchId)
|
||||
}
|
||||
|
||||
}, `${task_list.out_folder}_${images[0]}`, batch, subBatchId)
|
||||
// }
|
||||
task_list.status = 'queue';
|
||||
task_list["isAuto"] = "true";
|
||||
await this.pm.ModifyImageTaskList([task_list]);
|
||||
// 修改状态
|
||||
// await this.pm.ModifyTaskStatus("id", task_list.id, "queue");
|
||||
// this.global.fileQueue.enqueue(async () => {
|
||||
// let task_json = JSON.parse(await fspromises.readFile(taskPath, 'utf-8'));
|
||||
// let index = task_json.task_list.findIndex(item => item.id == task_list.id);
|
||||
// task_json.task_list[index] = task_list;
|
||||
// await fspromises.writeFile(taskPath, JSON.stringify(task_json));
|
||||
// })
|
||||
this.global.newWindow[0].win.webContents.send(DEFINE_STRING.VIDEO_GENERATE_STATUS_REFRESH, task_list)
|
||||
|
||||
// 判断是不是还有批次执行的任务
|
||||
// 监听子批次完成(修改当前批次的状态)
|
||||
this.global.requestQuene.setSubBatchCompletionCallback(batch, subBatchId, async (failedTasks) => {
|
||||
console.log(failedTasks)
|
||||
if (failedTasks.length > 0) {
|
||||
// 之前的任务出现错误
|
||||
// 执行错误
|
||||
} else {
|
||||
// 判断是不是有错误。没有错误的话。直接修改状态。有错误直接记录错误(写入一个就行)
|
||||
task_list.status = "ok";
|
||||
await this.pm.ModifyTaskStatus("id", task_list.id, 'ok');
|
||||
this.global.newWindow[0].win.webContents.send(DEFINE_STRING.VIDEO_GENERATE_STATUS_REFRESH, task_list)
|
||||
console.log(subBatchId + "生图执行完毕。可以开始执行高清")
|
||||
|
||||
// 添加高清队列
|
||||
// task_list
|
||||
this.global.requestQuene.enqueue(async () => {
|
||||
await this.pm.ImproveFolder(task_list.out_folder);
|
||||
}, `${task_list.out_folder}_improve`, batch, `${task_list.out_folder}_improve`);
|
||||
// 添加队列后修改状态
|
||||
task_list.status = "video_improving"
|
||||
await this.pm.ModifyTaskStatus('out_folder', task_list.out_folder, "video_improving");
|
||||
this.global.newWindow[0].win.webContents.send(DEFINE_STRING.VIDEO_GENERATE_STATUS_REFRESH, task_list)
|
||||
|
||||
// 监听高清任务完成
|
||||
this.global.requestQuene.setSubBatchCompletionCallback(batch, `${task_list.out_folder}_improve`, async (failedTasks) => {
|
||||
console.log(failedTasks)
|
||||
if (failedTasks.length > 0) {
|
||||
// 之前的任务出现错误
|
||||
// 执行错误
|
||||
} else {
|
||||
|
||||
console.log(task_list.out_folder + "高清完成,可以开始合成视频");
|
||||
// 添加生成视频队列
|
||||
this.global.requestQuene.enqueue(async () => {
|
||||
let video_config = JSON.parse(await fspromises.readFile(define.video_config, 'utf-8'));
|
||||
await this.AutoGeneretionOneVide(task_list.out_folder, video_config, value);
|
||||
}, `${task_list.out_folder}_video`, batch, `${task_list.out_folder}_video`);
|
||||
task_list.status = "video_queue";
|
||||
// 添加后修改状态
|
||||
await this.pm.ModifyTaskStatus('out_folder', task_list.out_folder, "video_queue");
|
||||
|
||||
this.global.newWindow[0].win.webContents.send(DEFINE_STRING.VIDEO_GENERATE_STATUS_REFRESH, task_list)
|
||||
|
||||
// 监听生成视频任务完成
|
||||
this.global.requestQuene.setSubBatchCompletionCallback(batch, `${task_list.out_folder}_video`, async (failedTasks) => {
|
||||
console.log(failedTasks)
|
||||
if (failedTasks.length > 0) {
|
||||
// 之前的任务出现错误
|
||||
// 执行错误
|
||||
} else {
|
||||
console.log(task_list.out_folder + "合成视频完成");
|
||||
// 添加生成视频队列
|
||||
task_list.status = "video_ok";
|
||||
// 添加后修改状态
|
||||
await this.pm.ModifyTaskStatus('out_folder', task_list.out_folder, "video_ok");
|
||||
this.global.newWindow[0].win.webContents.send(DEFINE_STRING.VIDEO_GENERATE_STATUS_REFRESH, task_list)
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 监听总批次完成
|
||||
this.global.requestQuene.setBatchCompletionCallback(batch, 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
|
||||
})
|
||||
} else {
|
||||
console.log("所有的自动生成任务完成");
|
||||
this.global.newWindow[0].win.webContents.send(DEFINE_STRING.SHOW_MESSAGE_DIALOG, {
|
||||
code: 1,
|
||||
message: "所有自动任务完成"
|
||||
})
|
||||
}
|
||||
|
||||
this.global.fileQueue.enqueue(async () => {
|
||||
// 读取最新的数据删除
|
||||
// 将自动化标识删除
|
||||
let task_json = JSON.parse(await fspromises.readFile(taskPath, 'utf-8'));
|
||||
for (let i = 0; i < task_json.task_list.length; i++) {
|
||||
let task_list = task_json.task_list[i];
|
||||
task_list["isAuto"] = "false";
|
||||
await this.pm.ModifyImageTaskList([task_list]);
|
||||
}
|
||||
})
|
||||
})
|
||||
return {
|
||||
code: 1,
|
||||
message: "自动任务添加到队列中"
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
return {
|
||||
code: 0,
|
||||
message: error.message
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文件夹下面的子文件夹,添加判断条件
|
||||
* @param {value} value
|
||||
* @returns
|
||||
*/
|
||||
async getSubFolderList(value) {
|
||||
try {
|
||||
let folder = await this.pm.getSubFolderList(path.join(this.global.config.project_path, "tmp"), value[0], value[1]);
|
||||
// 找到所有的数据(查询状态返回)
|
||||
let task_path = path.join(this.global.config.project_path, "scripts/task_list.json");
|
||||
let isExist = await this.tools.checkExists(task_path);
|
||||
let data = [];
|
||||
if (!isExist) {
|
||||
for (let i = 0; i < folder.length; i++) {
|
||||
const element = folder[i];
|
||||
let obj = {
|
||||
folder: element,
|
||||
status: "unkown error"
|
||||
}
|
||||
data.push(obj);
|
||||
}
|
||||
} else {
|
||||
let task_list = JSON.parse(await fspromises.readFile(task_path, 'utf-8'))["task_list"];
|
||||
// 查询状态
|
||||
for (let i = 0; i < folder.length; i++) {
|
||||
const element = folder[i];
|
||||
let index = task_list.findIndex(item => item.out_folder == element);
|
||||
if (index < 0) {
|
||||
let obj = {
|
||||
folder: element,
|
||||
status: "unkown error"
|
||||
}
|
||||
data.push(obj);
|
||||
} else {
|
||||
let status = task_list[index].status;
|
||||
let obj = {
|
||||
folder: element,
|
||||
status: status
|
||||
}
|
||||
data.push(obj);
|
||||
}
|
||||
}
|
||||
}
|
||||
return {
|
||||
code: 1,
|
||||
data: data
|
||||
}
|
||||
} catch (error) {
|
||||
return {
|
||||
code: 0,
|
||||
message: `Error Message ${error}`
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 自动生成一个视频
|
||||
* @param {视频生成图片的文件夹} element
|
||||
* @param {视频配置} video_config
|
||||
* @param {参数。第一个值为数组,为生成的图片文件。第二个参数是配置} value
|
||||
*/
|
||||
async AutoGeneretionOneVide(element, video_config, value) {
|
||||
try {
|
||||
let background_music = "";
|
||||
// 读取背景音乐的路径
|
||||
if (value[1].background_music != "") {
|
||||
let background_music_config = (await func.getClipSetting("background_music_setting")).value;
|
||||
let background_music_filter = background_music_config.filter(item => item.id == value[1].background_music);
|
||||
if (background_music_filter.length <= 0) {
|
||||
throw new Error("背景音乐对应的配置没有找到");
|
||||
} else {
|
||||
background_music = background_music_filter[0].folder_path;
|
||||
}
|
||||
}
|
||||
|
||||
// 随机获取字幕设置
|
||||
let ass_config = video_config.assConfig;
|
||||
let ass_random_index = Math.floor(Math.random() * ass_config.length);
|
||||
let watermark_config = video_config.watermarkConfig;
|
||||
let watermark_random_index = Math.floor(Math.random() * watermark_config.length);
|
||||
|
||||
// 对每个视频生成配置文件并将其添加到配置文件中
|
||||
let obj = {
|
||||
srt_path: value[1].srt_path,
|
||||
// 字幕样式(需要随机)
|
||||
srt_style: ass_config[ass_random_index],
|
||||
audio_path: value[1].audio_path,
|
||||
background_music_folder: background_music,
|
||||
// 水印设置(需要随机)
|
||||
friendly_reminder: watermark_config[watermark_random_index],
|
||||
video_resolution_x: video_config.video_resolution_x,
|
||||
video_resolution_y: video_config.video_resolution_y,
|
||||
outpue_file: path.join(this.global.config.project_path, this.global.config.project_name + element + ".mp4"),
|
||||
image_folder: path.join(this.global.config.project_path, 'tmp/' + element),
|
||||
srt_config: path.join(this.global.config.project_path, 'scripts/config.json'),
|
||||
mp4_file_txt: path.join(this.global.config.project_path, `scripts/${element}.txt`),
|
||||
status: "no",
|
||||
audio_sound_size: video_config.audioSoundSize,
|
||||
background_music_sound_size: video_config.backgroundMusicSoundSize,
|
||||
keyFrame: video_config.keyframe,
|
||||
frameRate: video_config.frameRate,
|
||||
bitRate: video_config.bitRate
|
||||
}
|
||||
// 将配置文件写入
|
||||
let project_config_path = path.join(this.global.config.project_path, `scripts/${element}.json`);
|
||||
await fspromises.writeFile(project_config_path, JSON.stringify(obj));
|
||||
// let task_list = JSON.parse(await fspromises.readFile(path.join(this.global.config.project_path,'scripts/task_')));
|
||||
let scriptPath = path.join(define.scripts_path, 'Lai.exe');
|
||||
// 执行生成图片的脚本
|
||||
let script = `cd "${define.scripts_path}" && "${scriptPath}" -c "${project_config_path.replaceAll('\\', '/')}"`;
|
||||
const output = await execAsync(script, { maxBuffer: 1024 * 1024 * 10, encoding: 'utf-8' });
|
||||
if (output.stderr != '') {
|
||||
obj.status = "video_error";
|
||||
obj.stdout = output.stdout;
|
||||
obj.stderr = output.stderr;
|
||||
await this.pm.ModifyTaskStatus('out_folder', element, "video_error");
|
||||
throw new Error(output.stderr);
|
||||
} else {
|
||||
obj.status = "video_ok";
|
||||
obj.stdout = output.stdout;
|
||||
obj.stderr = output.stderr;
|
||||
await this.pm.ModifyTaskStatus('out_folder', element, "video_ok");
|
||||
// 将写出的视频中的exif数据删除
|
||||
// await this.tools.deletePngAndDeleteExifData(obj.outpue_file, path.join(this.global.config.project_path, this.global.config.project_name + "_" + element.split('_')[element.split('_').length - 1] + ".mp4"));
|
||||
}
|
||||
|
||||
this.global.newWindow[0].win.webContents.send(DEFINE_STRING.VIDEO_GENERATE_STATUS_REFRESH, {
|
||||
out_folder: element,
|
||||
status: obj.status
|
||||
})
|
||||
} catch (error) {
|
||||
//手动修改
|
||||
let task_list_json = JSON.parse(await fspromises.readFile(path.join(this.global.config.project_path, "scripts/task_list.json"), 'utf-8'));
|
||||
let index = task_list_json.task_list.findIndex(item => item.out_folder == element);
|
||||
if (index < 0) {
|
||||
throw new Error("未找到对应的任务");
|
||||
}
|
||||
task_list_json.task_list[index].status = "video_error";
|
||||
task_list_json.task_list[index].errorMessage = error.toString();
|
||||
// 写回
|
||||
await fspromises.writeFile(path.join(this.global.config.project_path, "scripts/task_list.json"), JSON.stringify(task_list_json));
|
||||
this.global.newWindow[0].win.webContents.send(DEFINE_STRING.VIDEO_GENERATE_STATUS_REFRESH, {
|
||||
out_folder: element,
|
||||
status: "video_error"
|
||||
})
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加自动合成任务到队列中去
|
||||
* @param {} value
|
||||
*/
|
||||
async AutoGeneretionVideo(value) {
|
||||
try {
|
||||
// 先检查所有的条件
|
||||
let batch = DEFINE_STRING.QUEUE_BATCH.AUTO_VIDEO_GENERATE_SINGLE;
|
||||
// console.log(value);
|
||||
let video_config = JSON.parse(await fspromises.readFile(define.video_config, 'utf-8'));
|
||||
let res = await this.CheckVideoGenerattionAllCondition(video_config, value[1], value[0]);
|
||||
if (res.code == 0) {
|
||||
this.global.newWindow[0].win.webContents.send(DEFINE_STRING.SHOW_MESSAGE_DIALOG, res);
|
||||
return res;
|
||||
}
|
||||
for (let i = 0; i < value[0].length; i++) {
|
||||
const element = value[0][i];
|
||||
this.global.requestQuene.enqueue(async () => {
|
||||
await this.AutoGeneretionOneVide(element, video_config, value)
|
||||
}, element, batch);
|
||||
|
||||
// 添加队列后修改状态
|
||||
await this.pm.ModifyTaskStatus('out_folder', element, "video_queue");
|
||||
this.global.newWindow[0].win.webContents.send(DEFINE_STRING.VIDEO_GENERATE_STATUS_REFRESH, {
|
||||
out_folder: element,
|
||||
status: "video_queue"
|
||||
})
|
||||
}
|
||||
|
||||
this.global.requestQuene.setBatchCompletionCallback(batch, (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
|
||||
})
|
||||
} else {
|
||||
this.global.newWindow[0].win.webContents.send(DEFINE_STRING.SHOW_MESSAGE_DIALOG, {
|
||||
code: 1,
|
||||
message: "所有生成视频任务已完成"
|
||||
})
|
||||
}
|
||||
})
|
||||
return {
|
||||
code: 1
|
||||
}
|
||||
} catch (error) {
|
||||
return {
|
||||
code: 0,
|
||||
message: error.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测生成视频条件的必要条件
|
||||
* @param {生成视频配置文件} video_config
|
||||
* @param {生成视频的基础配置} simpleData
|
||||
* @param {输出文件夹位置,为null,检查input文件里面的图片文件是不是和配置文件中的图片数量一致} out_folder
|
||||
* @returns
|
||||
*/
|
||||
async CheckVideoGenerattionAllCondition(video_config, simpleData, out_folder) {
|
||||
let message = "";
|
||||
if (video_config == null) {
|
||||
video_config = JSON.parse(await fspromises.readFile(define.video_config, 'utf-8'));
|
||||
}
|
||||
// 基础信息检测
|
||||
if (video_config.audioSoundSize == null) {
|
||||
message += "配音大小不能为空" + '\n';
|
||||
}
|
||||
if (video_config.backgroundMusicSoundSize == null) {
|
||||
message += "背景音乐音量大学不能为空" + '\n';
|
||||
}
|
||||
if (video_config.video_resolution_x == null) {
|
||||
message += "生成视频的宽度不能为空" + '\n';
|
||||
}
|
||||
if (video_config.video_resolution_y == null) {
|
||||
message += "生成视频的高度不能为空" + '\r\n';
|
||||
}
|
||||
if (video_config.offsetValue == null) {
|
||||
message += "视频的上下偏移量不能为空" + '\n';
|
||||
}
|
||||
if (video_config.frameRate == null) {
|
||||
message += "生成视频的帧率不能为空" + '\n';
|
||||
}
|
||||
if (video_config.bitRate == null) {
|
||||
message += "生成视频码率的不能为空" + '\n';
|
||||
}
|
||||
|
||||
// 判断字幕列表中是不是有数据
|
||||
if (video_config.assConfig == null || video_config.assConfig.length <= 0) {
|
||||
message += "字幕设置最少包含一条" + '\n';
|
||||
}
|
||||
if (video_config.watermarkConfig == null || video_config.watermarkConfig.length <= 0) {
|
||||
message += "水印设置最少包含一条" + '\n';
|
||||
}
|
||||
// 判断背景音乐文件夹中是不是存在。并且检查其中是不是有音乐文件
|
||||
if (simpleData.background_music == "") {
|
||||
message += "背景音乐文件夹路径不能为空" + '\n';
|
||||
}
|
||||
// 判断背景音乐文件夹是不是存在
|
||||
if (await this.tools.checkExists(simpleData.background_music)) {
|
||||
message += "背景音乐文件夹不存在" + '\n';
|
||||
}
|
||||
// 判断里面是不是有MP3或者是wav
|
||||
let clip_json = JSON.parse(await fspromises.readFile(define.clip_setting, 'utf-8'))["background_music_setting"];
|
||||
let background_music_obj = clip_json.filter(item => item.id == simpleData.background_music)[0];
|
||||
let mp3_file = await this.tools.getFilesWithExtensions(background_music_obj.folder_path, '.mp3');
|
||||
let wav_file = await this.tools.getFilesWithExtensions(background_music_obj.folder_path, '.wav');
|
||||
if (mp3_file.length <= 0 && wav_file.length <= 0) {
|
||||
message += "背景文件夹中没有 MP3 或 WAV 文件" + '\n';
|
||||
}
|
||||
|
||||
let config_path = path.join(this.global.config.project_path, 'scripts/config.json');
|
||||
let isE = await this.tools.checkExists(config_path);
|
||||
if (!isE) {
|
||||
message += "配置文件不存在。请先导入字幕文件。并调整时间轴。"
|
||||
} else {
|
||||
// 判断文案时间信息和图片信息是不是相同
|
||||
let config_json = JSON.parse(await fspromises.readFile(config_path, 'utf-8'));
|
||||
let len = config_json["srt_time_information"].length;
|
||||
if (out_folder == null) {
|
||||
// 判断输入文件中的配置文件中的数量是不是对上
|
||||
let img_l = await this.tools.getFilesWithExtensions(path.join(this.global.config.project_path, "tmp/input_crop"), '.png');
|
||||
if (img_l.length != len) {
|
||||
message += `input_crop 文件里面图片和文案信息对不上。检查是不是图片数量不对`
|
||||
}
|
||||
// 判断是不是有tag文件
|
||||
let tag_txt = await this.tools.getFilesWithExtensions(path.join(this.global.config.project_path, "tmp/input_crop"), '.txt');
|
||||
let c_j = await this.tools.getFilesWithExtensions(path.join(this.global.config.project_path, "tmp/input_crop"), '.json');
|
||||
if (tag_txt.length > 0) {
|
||||
if (tag_txt.length != img_l.length) {
|
||||
message += "反推的tag文件和图片的数量对不上。" + "\n";
|
||||
}
|
||||
} else if (c_j.length > 0) {
|
||||
if (c_j.length != img_l.length) {
|
||||
message += "已存在的配置文件和图片的数量对不上。" + "\n";
|
||||
}
|
||||
} else {
|
||||
message += "好像没有反推呢" + "\n";
|
||||
}
|
||||
} else {
|
||||
// 判断当前的输入的 out_folder 中图片数量是不是可以和配置文件中能否对上
|
||||
for (let i = 0; i < out_folder.length; i++) {
|
||||
const element = out_folder[i];
|
||||
let image_l = await this.tools.getFilesWithExtensions(path.join(this.global.config.project_path, "tmp/" + element), '.png');
|
||||
if (image_l.length != len) {
|
||||
message += `${element} 文件里面图片和文案信息对不上。检查是不是图片数量不对`
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (message != '') {
|
||||
let res = {
|
||||
code: 0,
|
||||
message: message
|
||||
}
|
||||
this.global.newWindow[0].win.webContents.send(DEFINE_STRING.SHOW_MESSAGE_DIALOG, res)
|
||||
return res;
|
||||
}
|
||||
return {
|
||||
code: 1,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,301 @@
|
||||
let path = require("path");
|
||||
let fspromises = require("fs").promises;
|
||||
import { Tools } from "../tools";
|
||||
import { DEFINE_STRING } from "../../define/define_string";
|
||||
import { PublicMethod } from "../Public/publicMethod";
|
||||
import { define } from "../../define/define";
|
||||
import { get, has } from "lodash";
|
||||
import { ClipSetting } from "../../define/setting/clipSetting";
|
||||
const { v4: uuidv4 } = require('uuid'); // 引入UUID库来生成唯一标识符
|
||||
let tools = new Tools();
|
||||
|
||||
export class Writing {
|
||||
constructor(global) {
|
||||
this.global = global
|
||||
this.pm = new PublicMethod(global);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将文案信息写入到本地的文案文件中
|
||||
* @param {*} value
|
||||
*/
|
||||
async SaveWordTxt(value) {
|
||||
try {
|
||||
let word_path = path.join(global.config.project_path, "文案.txt");
|
||||
await tools.writeArrayToFile(value, word_path);
|
||||
return {
|
||||
code: 1,
|
||||
message: "保存成功"
|
||||
}
|
||||
} catch (error) {
|
||||
throw new Error(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将分镜的时间信息添加道配置文件中
|
||||
* @param {*} value 是一个数组,0 :写入的数据 1:写入的属性 2:是否需要解析
|
||||
*/
|
||||
async SaveCopywritingInformation(value) {
|
||||
try {
|
||||
return await this.pm.SaveConfigJsonProperty(value);
|
||||
} catch (error) {
|
||||
return {
|
||||
code: 0,
|
||||
message: error.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取Config.json文件中指定的属性
|
||||
* @param {Array} value 传入的值 0 : 需要获取的属性 1: 返回的默认值
|
||||
* @returns
|
||||
*/
|
||||
async GetConfigJson(value) {
|
||||
try {
|
||||
return await this.pm.GetConfigJson(value, false);
|
||||
} catch (error) {
|
||||
return {
|
||||
code: 0,
|
||||
message: error.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前项目下面的文案
|
||||
*/
|
||||
async GetProjectWord() {
|
||||
try {
|
||||
// 先判断当前的项目文件下面是不是又配置文件。没有才读取文案
|
||||
let srt_config_path = path.join(global.config.project_path, "scripts/config.json");
|
||||
let isExist = await tools.checkExists(srt_config_path);
|
||||
let data = null;
|
||||
let isImformation = false;
|
||||
if (isExist) {
|
||||
let config_1 = JSON.parse(await fspromises.readFile(srt_config_path));
|
||||
isImformation = has(config_1, 'srt_time_information');
|
||||
if (isImformation) {
|
||||
data = JSON.parse(await fspromises.readFile(srt_config_path)).srt_time_information;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isExist || !isImformation) {
|
||||
let word_path = path.join(global.config.project_path, "文案.txt");
|
||||
let isExistWord = await tools.checkExists(word_path);
|
||||
if (!isExistWord) {
|
||||
return {
|
||||
code: 0,
|
||||
message: "没有文案文件"
|
||||
}
|
||||
}
|
||||
let data = await fspromises.readFile(word_path, { encoding: 'utf-8' });
|
||||
let lines = data.split(/\r?\n/);
|
||||
// 打印或返回这个数组
|
||||
// console.log(lines);
|
||||
// 判断是不是有洗稿后的文件
|
||||
let new_srt_path = path.join(global.config.project_path, "new_word.txt");
|
||||
let isExistAfterGPTWord = await tools.checkExists(new_srt_path);
|
||||
let after_data = null;
|
||||
if (isExistAfterGPTWord) {
|
||||
after_data = (await fspromises.readFile(new_srt_path, { encoding: 'utf-8' })).split(/\r?\n/);
|
||||
}
|
||||
// 判断抽帧文件是不是存在
|
||||
// 返回图片信息
|
||||
let old_image_path_list = await tools.getFilesWithExtensions(path.join(global.config.project_path, "tmp/input_crop"), '.png');
|
||||
let res = [];
|
||||
let lastId = '';
|
||||
// 处理数据
|
||||
for (let i = 0; i < lines.length; i++) {
|
||||
const line = lines[i];
|
||||
let id = uuidv4();
|
||||
let after_gpt = null;
|
||||
if (after_data != null) {
|
||||
after_gpt = after_data[i];
|
||||
}
|
||||
|
||||
let img_path = null;
|
||||
if (old_image_path_list != null) {
|
||||
img_path = old_image_path_list[i];
|
||||
}
|
||||
let obj = {
|
||||
no: i + 1,
|
||||
id: id,
|
||||
lastId: lastId,
|
||||
word: line,
|
||||
old_image: img_path,
|
||||
after_gpt: after_gpt,
|
||||
start_time: null,
|
||||
end_time: null,
|
||||
timeLimit: null,
|
||||
subValue: []
|
||||
}
|
||||
res.push(obj);
|
||||
lastId = id;
|
||||
}
|
||||
return {
|
||||
code: 1,
|
||||
type: 0,
|
||||
data: res
|
||||
}
|
||||
} else {
|
||||
let data = JSON.parse(await fspromises.readFile(srt_config_path)).srt_time_information;
|
||||
return {
|
||||
code: 1,
|
||||
type: 1,
|
||||
data: data
|
||||
}
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
throw new Error(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 搭导入srt。然后加载时间轴。完全匹配失败的将会还是会导入然后手动手动切换
|
||||
* @param {文案洗稿界面信息} textData
|
||||
*/
|
||||
async ImportSrtAndGetTime(data) {
|
||||
let textData = data[0];
|
||||
let init_num = textData.length;
|
||||
let srt_path = data[1];
|
||||
let current_text = "";
|
||||
try {
|
||||
if (!srt_path) {
|
||||
// 获取项目下面的所有的srt
|
||||
let srtfiles = await tools.getFilesWithExtensions(global.config.project_path, '.srt');
|
||||
if (srtfiles.length <= 0) {
|
||||
throw new Error("没有SRT文件");
|
||||
}
|
||||
srt_path = srtfiles[0];
|
||||
}
|
||||
let srt_data = (await fspromises.readFile(srt_path, 'utf-8')).toString("utf-8");
|
||||
const entries = srt_data.replace(/\r\n/g, '\n').split('\n\n');
|
||||
let data = entries.map(entry => {
|
||||
const lines = entry.split('\n');
|
||||
if (lines.length >= 3) {
|
||||
const times = lines[1];
|
||||
const text = lines.slice(2).join(' ');
|
||||
const [start, end] = times.split(' --> ').map(time => {
|
||||
const [hours, minutes, seconds] = time.split(':');
|
||||
const [sec, millis] = seconds.split(',');
|
||||
return ((parseInt(hours) * 3600 + parseInt(minutes) * 60 + parseInt(sec)) * 1000 + parseInt(millis));
|
||||
});
|
||||
return { start, end, text, id: uuidv4() };
|
||||
}
|
||||
}).filter(entry => entry);
|
||||
// 开始匹配(洗稿后的)
|
||||
let srt_list = [];
|
||||
let srt_obj = null
|
||||
let text_count = 0;
|
||||
let tmp_str = "";
|
||||
for (let i = 0; i < data.length;) {
|
||||
let srt_value = data[i].text;
|
||||
current_text = `字幕: “${srt_value}” 和文案第${text_count + 1} 行数据 “${textData[text_count].after_gpt}” 数据不匹配(检查一下上下文)`;
|
||||
let start_time = data[i].start;
|
||||
let end_time = data[i].end;
|
||||
let obj = {
|
||||
start_time,
|
||||
end_time,
|
||||
srt_value,
|
||||
id: data[i].id
|
||||
};
|
||||
// 判断当前字幕是不是在当前句
|
||||
// 不能用简单的包含,而是将数据进行去除特殊符号拼接后判断是不是相同
|
||||
tmp_str += srt_value;
|
||||
if (tools.removePunctuationIncludingEllipsis(textData[text_count].after_gpt).startsWith(tools.removePunctuationIncludingEllipsis(tmp_str))) {
|
||||
if (srt_obj == null) {
|
||||
srt_obj = {}
|
||||
srt_obj.id = uuidv4();
|
||||
srt_obj.start_time = start_time;
|
||||
srt_obj.value = srt_value;
|
||||
srt_obj.subValue = [obj];
|
||||
}
|
||||
else {
|
||||
srt_obj.value = srt_obj.value + srt_value;
|
||||
srt_obj.subValue = [...srt_obj.subValue, obj];
|
||||
}
|
||||
textData[text_count].start_time = srt_obj.start_time;
|
||||
textData[text_count].subValue = srt_obj.subValue
|
||||
srt_list.push(obj);
|
||||
i++;
|
||||
} else {
|
||||
// 判断下一句文件是不是以当当前巨开头。是的话继续。不是的话。直接返回后面的所有信息
|
||||
if (tools.removePunctuationIncludingEllipsis(textData[text_count + 1].after_gpt).startsWith(tools.removePunctuationIncludingEllipsis(srt_value))) {
|
||||
textData[text_count].end_time = srt_list[srt_list.length - 1].end_time;
|
||||
text_count++;
|
||||
srt_obj = null;
|
||||
tmp_str = ""
|
||||
} else {
|
||||
// 将下面的数据直接 添加到textData后面。
|
||||
// 修改当前行数据的结束事件为
|
||||
if (srt_list.length > 0) {
|
||||
textData[text_count].end_time = srt_list[srt_list.length - 1].end_time;
|
||||
text_count++;
|
||||
}
|
||||
// 将后面的数据直接添加
|
||||
let lastId = textData[textData.length - 1].id;
|
||||
for (let j = i; j < data.length; j++) {
|
||||
// 直接修改原有数据
|
||||
if (text_count < init_num) {
|
||||
textData[text_count].subValue = [{
|
||||
start_time: data[j].start,
|
||||
end_time: data[j].end,
|
||||
id: data[j].id,
|
||||
srt_value: data[j].text
|
||||
}]
|
||||
textData[text_count].start_time = data[j].start;
|
||||
textData[text_count].end_time = data[j].end;
|
||||
text_count++;
|
||||
}
|
||||
else {
|
||||
let id = uuidv4();
|
||||
// 添加
|
||||
let obj = {
|
||||
no: j + 1,
|
||||
id: id,
|
||||
word: null,
|
||||
lastId: lastId,
|
||||
old_image: path.normalize(define.zhanwei_image),
|
||||
after_gpt: null,
|
||||
start_time: data[j].start,
|
||||
end_time: data[j].end,
|
||||
subValue: [{
|
||||
start_time: data[j].start,
|
||||
end_time: data[j].end,
|
||||
id: data[j].id,
|
||||
srt_value: data[j].text
|
||||
}]
|
||||
}
|
||||
lastId = id;
|
||||
textData.push(obj);
|
||||
}
|
||||
}
|
||||
this.global.newWindow[0].win.webContents.send(DEFINE_STRING.SHOW_MESSAGE_DIALOG, {
|
||||
code: 0,
|
||||
message: current_text
|
||||
})
|
||||
return {
|
||||
code: 1,
|
||||
data: textData
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// 最后对齐
|
||||
textData[textData.length - 1].end_time = srt_list[srt_list.length - 1].end_time
|
||||
// 返回数据
|
||||
return {
|
||||
code: 1,
|
||||
data: textData
|
||||
}
|
||||
} catch (error) {
|
||||
return {
|
||||
code: 0,
|
||||
message: error.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,490 @@
|
||||
const axios = require('axios');
|
||||
// import { Midjourney as mApi} from "midjourney";
|
||||
// const MJapi2 = require('midjourney');
|
||||
// const { midjourney } = require('midjourney')
|
||||
// const Midjourney = require('midjourney');
|
||||
// ES5 的模块引入方式
|
||||
const fetch = require("node-fetch");
|
||||
|
||||
// ES6 的模块引入方式
|
||||
// import fetch from "node-fetch";
|
||||
|
||||
export class DiscordAPI {
|
||||
constructor(mj_setting) {
|
||||
// https://discord.com/api/v9/channels/1208362852482809939/messages?limit=20
|
||||
this.apiClient = axios.create({
|
||||
baseURL: 'https://discord.com'
|
||||
});
|
||||
this.DiscordBaseUrl = 'https://discord.com';
|
||||
this.ServerId = mj_setting.serviceID;
|
||||
this.ChannelId = mj_setting.channelID;
|
||||
this.userToken = mj_setting.token;
|
||||
this.botId = mj_setting.select_robot?.botId;
|
||||
this.commandId = mj_setting.select_robot?.commandId;
|
||||
this.versionId = mj_setting.select_robot?.versionId;
|
||||
this.versionName = mj_setting.select_robot?.versionName;
|
||||
this.botName = mj_setting.select_robot?.botName;
|
||||
}
|
||||
|
||||
// 提交任务
|
||||
async imagine(data) {
|
||||
|
||||
// let req_data = {
|
||||
// "token": this.userToken,
|
||||
// "method": "post",
|
||||
// "api_url": "/mj/submit/imagine",
|
||||
// "data":data
|
||||
// }
|
||||
|
||||
// const response = await this.apiClient.post('/api/v3/req_mj_api', req_data);
|
||||
// await this.interactions(data.prompt);
|
||||
return await this.interactions(data.prompt)
|
||||
// return {
|
||||
// code:1,
|
||||
// result:'taskid_'+new Date().getTime()
|
||||
// }
|
||||
// return response.data;
|
||||
|
||||
}
|
||||
async channelList() {
|
||||
axios.get(`https://discord.com/api/v9/channels/${this.ChannelId}/messages?limit=20`, {
|
||||
method: 'get',
|
||||
headers: {
|
||||
"Authorization": this.userToken
|
||||
}
|
||||
})
|
||||
.then(response => {
|
||||
// 请求成功处理
|
||||
console.log(typeof response.data);
|
||||
let eList = []
|
||||
let flg = false;
|
||||
let job_id = '';
|
||||
let type = '';
|
||||
if (response && response.data) {
|
||||
try {
|
||||
response.data.forEach(element => {
|
||||
flg = false;
|
||||
type = '';
|
||||
if (element.attachments && element.attachments.length) {
|
||||
const arr = element.attachments[0].filename.split("_");
|
||||
job_id = arr[arr.length - 1].replace(".png", '');
|
||||
if (element.components) {
|
||||
element.components.forEach(e2 => {
|
||||
e2.components.forEach(e3 => {
|
||||
if (e3.label == 'U1') {
|
||||
flg = true;
|
||||
type = 'U1'
|
||||
} else if (e3.label && e3.label.indexOf('Upscale') > -1) {
|
||||
flg = true;
|
||||
type = 'Upscale'
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
let t = eList.find((e) => {
|
||||
return e.filename == element.attachments[0].filename;
|
||||
})
|
||||
|
||||
if (!t) {
|
||||
eList.push(
|
||||
{
|
||||
flg: flg, job_id: job_id, filename: element.attachments[0].filename,
|
||||
url: element.attachments[0].url,
|
||||
proxy_url: element.attachments[0].proxy_url,
|
||||
type: type,
|
||||
timestamp: element.timestamp
|
||||
})
|
||||
}
|
||||
|
||||
// console.log({flg:flg,job_id:job_id,filename:element.attachments[0].filename})
|
||||
} else {
|
||||
console.log({ flg: flg })
|
||||
}
|
||||
|
||||
});
|
||||
} catch (error) {
|
||||
console.log('异常2', error)
|
||||
}
|
||||
// console.log(eList)
|
||||
// 8e7406df-bf0c-4e3d-8e49-b2bb8e2c263d
|
||||
// 5abedc71-ba80-4756-8ddc-489c927d3acd
|
||||
}
|
||||
}).catch(error => {
|
||||
// 请求失败处理
|
||||
console.error(error);
|
||||
});
|
||||
}
|
||||
// 混合
|
||||
async blend(data) {
|
||||
|
||||
let req_data = {
|
||||
"token": this.userToken,
|
||||
"method": "post",
|
||||
"api_url": "/mj/submit/blend",
|
||||
"data": data
|
||||
}
|
||||
|
||||
const response = await this.apiClient.post('/api/v3/req_mj_api', req_data);
|
||||
return response.data;
|
||||
|
||||
}
|
||||
|
||||
|
||||
// 反推
|
||||
async describe(data) {
|
||||
|
||||
let req_data = {
|
||||
"token": this.userToken,
|
||||
"method": "post",
|
||||
"api_url": "/mj/submit/describe",
|
||||
"data": data
|
||||
}
|
||||
|
||||
const response = await this.apiClient.post('/api/v3/req_mj_api', req_data);
|
||||
return response.data;
|
||||
|
||||
}
|
||||
|
||||
// 获取任务
|
||||
async getTaskId(task_id) {
|
||||
let req_data = {
|
||||
"token": this.userToken,
|
||||
"method": "get",
|
||||
"api_url": `/mj/task/${task_id}/fetch`,
|
||||
"data": {}
|
||||
}
|
||||
const response = await this.apiClient.post('/api/v3/req_mj_api', req_data);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
//获取seed
|
||||
async imageSeed(task_id) {
|
||||
let req_data = {
|
||||
"token": this.userToken,
|
||||
"method": "get",
|
||||
"api_url": `/mj/task/${task_id}/image-seed`,
|
||||
"data": {}
|
||||
}
|
||||
const response = await this.apiClient.post('/api/v3/req_mj_api', req_data);
|
||||
return response.data;
|
||||
|
||||
}
|
||||
|
||||
//账号创建
|
||||
async account_create(data) {
|
||||
let req_data = {
|
||||
"token": this.userToken,
|
||||
"method": "post",
|
||||
"api_url": '/mj/account/create',
|
||||
"data": data
|
||||
}
|
||||
const response = await this.apiClient.post('/api/v3/req_mj_api', req_data, { timeout: 20000 });
|
||||
return response.data;
|
||||
}
|
||||
|
||||
//账号创建
|
||||
async account_fetch(cid) {
|
||||
let req_data = {
|
||||
"token": this.userToken,
|
||||
"method": "get",
|
||||
"api_url": `/mj/account/${cid}/fetch`,
|
||||
"data": {}
|
||||
}
|
||||
const response = await this.apiClient.post('/api/v3/req_mj_api', req_data);
|
||||
return response.data;
|
||||
|
||||
}
|
||||
|
||||
//账号同步信息
|
||||
async account_asyn_info(cid) {
|
||||
let req_data = {
|
||||
"token": this.userToken,
|
||||
"method": "post",
|
||||
"api_url": `/mj/account/${cid}/sync-info`,
|
||||
"data": {}
|
||||
}
|
||||
const response = await this.apiClient.post('/api/v3/req_mj_api', req_data, { timeout: 20000 });
|
||||
return response.data;
|
||||
}
|
||||
|
||||
//账号删除信息
|
||||
async account_del_info(cid) {
|
||||
let req_data = {
|
||||
"token": this.userToken,
|
||||
"method": "delete",
|
||||
"api_url": `/mj/account/${cid}/delete`,
|
||||
"data": {}
|
||||
}
|
||||
const response = await this.apiClient.post('/api/v3/req_mj_api', req_data);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
|
||||
//执行动作
|
||||
async action(data) {
|
||||
let req_data = {
|
||||
"token": this.userToken,
|
||||
"method": "post",
|
||||
"api_url": '/mj/submit/action',
|
||||
"data": data
|
||||
}
|
||||
const response = await this.apiClient.post('/api/v3/req_mj_api', req_data);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
// 确认弹窗
|
||||
async modal(data) {
|
||||
let req_data = {
|
||||
"token": this.userToken,
|
||||
"method": "post",
|
||||
"api_url": '/mj/submit/modal',
|
||||
"data": data
|
||||
}
|
||||
const response = await this.apiClient.post('/api/v3/req_mj_api', req_data);
|
||||
return response.data;
|
||||
|
||||
}
|
||||
|
||||
async interactions(prompt) {
|
||||
// 直接自己调用
|
||||
prompt = prompt.trim();
|
||||
// prompt = "4k,8k,best quality, masterpiece, woman, divorced, leaving fast-paced city life, serene expression, walking away from cityscape, bustling streets, entering tranquil countryside, peaceful surroundings, rejuvenating atmosphere, , --niji 5 --ar 4:3"
|
||||
var payload = {};
|
||||
if (this.botName == 'niji') {
|
||||
payload = {
|
||||
"type": 2,
|
||||
"application_id": this.botId,
|
||||
"guild_id": this.ServerId,
|
||||
"channel_id": this.ChannelId,
|
||||
"session_id": this.userToken,
|
||||
"data": {
|
||||
"version": this.versionId,
|
||||
"id": this.commandId,
|
||||
"name": "imagine",
|
||||
"type": 1,
|
||||
"options": [
|
||||
{
|
||||
"type": 3,
|
||||
"name": "prompt",
|
||||
"value": prompt
|
||||
}
|
||||
],
|
||||
"application_command": {
|
||||
"id": this.commandId,
|
||||
"type": 1,
|
||||
"application_id": this.botId,
|
||||
"version": this.versionId,
|
||||
"name": "imagine",
|
||||
"description": "Create images with Midjourney",
|
||||
"options": [
|
||||
{
|
||||
"type": 3,
|
||||
"name": "prompt",
|
||||
"description": "The prompt to imagine",
|
||||
"required": true,
|
||||
"description_localized": "The prompt to imagine",
|
||||
"name_localized": "prompt"
|
||||
}
|
||||
],
|
||||
"integration_types": [
|
||||
0
|
||||
],
|
||||
"global_popularity_rank": 1,
|
||||
"description_localized": "Create images with Midjourney",
|
||||
"name_localized": "imagine"
|
||||
},
|
||||
"attachments": [
|
||||
|
||||
]
|
||||
},
|
||||
// "nonce": "1210857131343872000",
|
||||
"analytics_location": "slash_ui"
|
||||
}
|
||||
} else {
|
||||
payload = {
|
||||
"type": 2,
|
||||
// "application_id":"1022952195194359889",//niji
|
||||
"application_id": this.botId,
|
||||
"guild_id": this.ServerId,
|
||||
"channel_id": this.ChannelId,
|
||||
"session_id": this.userToken,
|
||||
"data": {
|
||||
"version": this.versionId,
|
||||
"id": this.commandId,
|
||||
"name": "imagine", "type": 1,
|
||||
"options":
|
||||
[{
|
||||
"type": 3,
|
||||
"name": "prompt",
|
||||
"value": prompt
|
||||
}],
|
||||
"application_command": {
|
||||
"id": this.commandId,
|
||||
"type": 1,
|
||||
"application_id": this.botId,
|
||||
"version": this.versionId,
|
||||
"name": "imagine",
|
||||
"description": "Create images with Niji journey",
|
||||
"options": [{ "type": 3, "name": "prompt", "description": "The prompt to imagine", "required": true, "description_localized": "The prompt to imagine", "name_localized": "prompt" }],
|
||||
"integration_types": [0], "global_popularity_rank": 1, "description_localized": "Create images with Niji journey", "name_localized": "imagine"
|
||||
},
|
||||
"attachments": []
|
||||
},
|
||||
"analytics_location": "slash_ui"
|
||||
}
|
||||
}
|
||||
|
||||
let response = {
|
||||
status: 200,
|
||||
data: {}
|
||||
};
|
||||
|
||||
|
||||
try {
|
||||
const headers = {
|
||||
"Content-Type": "application/json",
|
||||
Authorization: this.userToken,
|
||||
};
|
||||
await fetch(`${this.DiscordBaseUrl}/api/v9/interactions`, {
|
||||
method: "POST",
|
||||
body: JSON.stringify(payload),
|
||||
headers: headers,
|
||||
}).then(res => res.json()).then(res => {
|
||||
response.data = res;
|
||||
}).catch(e => {
|
||||
console.error("请求失败了,详细信息:" + JSON.stringify(e));
|
||||
response = {
|
||||
status: 500,
|
||||
data: JSON.stringify(e)
|
||||
};
|
||||
});
|
||||
console.log('response结果')
|
||||
console.log(response)
|
||||
// if (response.status == 204) {
|
||||
// //成功
|
||||
|
||||
// }
|
||||
if (response.status >= 400) {
|
||||
console.error("api.error.config", {
|
||||
payload: JSON.stringify(payload)
|
||||
});
|
||||
}
|
||||
return {
|
||||
code: response.status,
|
||||
response: response
|
||||
};
|
||||
}
|
||||
catch (error) {
|
||||
console.error(error);
|
||||
return 500;
|
||||
}
|
||||
return;
|
||||
const client = new Midjourney.Midjourney({
|
||||
ServerId: this.ServerId,
|
||||
ChannelId: this.ChannelId,
|
||||
SalaiToken: this.userToken,
|
||||
Debug: true,
|
||||
fetch: fetch,
|
||||
Ws: true, //enable ws is required for remix mode (and custom zoom)
|
||||
});
|
||||
await client.init();
|
||||
console.log('mjmj_begin2', prompt);
|
||||
// const prompt =
|
||||
// "Christmas dinner with spaghetti with family in a cozy house, we see interior details , simple blue&white illustration";
|
||||
//imagine
|
||||
const Imagine = await client.Imagine(
|
||||
prompt,
|
||||
(uri, progress) => {
|
||||
client.Close();
|
||||
|
||||
console.log("loading", uri, "progress", progress);
|
||||
return
|
||||
}
|
||||
);
|
||||
console.log(Imagine);
|
||||
if (!Imagine) {
|
||||
console.log("no message");
|
||||
console.log('mjmj_end2')
|
||||
return;
|
||||
}
|
||||
console.log('mjmj_end')
|
||||
client.Close();
|
||||
return
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取频道内的机器人
|
||||
* @returns 返回机器人列表
|
||||
*/
|
||||
async getBotList() {
|
||||
try {
|
||||
|
||||
const headers = {
|
||||
'Host': 'discord.com',
|
||||
'Connection': 'keep-alive',
|
||||
'authorization': this.userToken,
|
||||
}
|
||||
|
||||
await fetch('https://discord.com/api/v9/guilds/1182523906855284826/application-command-index', {
|
||||
method: 'GET',
|
||||
headers: headers,
|
||||
})
|
||||
.then(response => {
|
||||
response.json()
|
||||
}
|
||||
)
|
||||
.then(data => {
|
||||
console.log(data)
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error:', error)
|
||||
})
|
||||
|
||||
|
||||
} catch (error) {
|
||||
throw new Error(error);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
async getMjMsgList() {
|
||||
// const headers = {
|
||||
// "Content-Type": "application/json",
|
||||
// Authorization:this.userToken,
|
||||
// };
|
||||
let response = {
|
||||
status: 200,
|
||||
data: {}
|
||||
};
|
||||
// // `https://discord.com/api/v9/channels/${mj_channelId}/messages?limit=10
|
||||
// await fetch(`${this.DiscordBaseUrl}/api/v9/channels/${this.ChannelId}/messages?limit=50`, {
|
||||
// method: "GET",
|
||||
// headers: headers,
|
||||
// }).then(res => res.json()).then(res => {
|
||||
// response.data=res;
|
||||
// }).catch(e => {
|
||||
// console.error("请求失败了,详细信息:" + JSON.stringify(e));
|
||||
// response ={
|
||||
// status:500,
|
||||
// data:JSON.stringify(e)
|
||||
// };
|
||||
// });
|
||||
// console.log('getMjMsgList_response结果')
|
||||
// // console.log(response)
|
||||
|
||||
// return response;
|
||||
axios.get(`https://discord.com/api/v9/channels/${this.ChannelId}/messages?limit=20`, {
|
||||
method: 'get',
|
||||
headers: {
|
||||
"Authorization": this.userToken
|
||||
}
|
||||
}).then(res => {
|
||||
response.data = res;
|
||||
}).catch(error => {
|
||||
// 请求失败处理
|
||||
console.error(error);
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
|
||||
export class DiscordRequestListener {
|
||||
constructor(thisWindow) {
|
||||
this.listeners = {};
|
||||
this.thisWindow = thisWindow;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加网络请求前的监听人物
|
||||
* @param {*} key 存储的属性名称
|
||||
* @param {*} listener 执行的方法
|
||||
* @returns
|
||||
*/
|
||||
async AddRequestListener(key, listener) {
|
||||
// 判断当前key是不是存在
|
||||
if (this.listeners[key]) {
|
||||
return;
|
||||
}
|
||||
this.listeners[key] = listener;
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除指定的key事件
|
||||
* @param {*} key
|
||||
*/
|
||||
async RemoveRequestListener(key) {
|
||||
if (this.listeners[key]) {
|
||||
delete this.listeners[key];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 监听指定的窗口的网络请求
|
||||
*/
|
||||
async start() {
|
||||
// 获取session
|
||||
// 获取Authorization
|
||||
// 获取User-Agent
|
||||
const mySession = this.thisWindow.webContents.session; // 获取 session
|
||||
// 使用 webRequest API 监听请求
|
||||
mySession.webRequest.onBeforeSendHeaders((details, callback) => {
|
||||
|
||||
for (const key in this.listeners) {
|
||||
if (Object.hasOwnProperty.call(this.listeners, key)) {
|
||||
const element = this.listeners[key];
|
||||
element(details);
|
||||
}
|
||||
}
|
||||
callback({ cancel: false, requestHeaders: details.requestHeaders });
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,406 @@
|
||||
// @noBytecode
|
||||
|
||||
/**
|
||||
* 获取Discord的消息输入框,并聚焦
|
||||
* @returns 返回获取的消息输入框
|
||||
*/
|
||||
function getInputFoucs() {
|
||||
let selector = 'div[contenteditable="true"][class^="markup_"]';
|
||||
const inputElement = document.querySelector(selector) // as HTMLInputElement;
|
||||
if (!inputElement) {
|
||||
throw new Error('没有找到对应的消息输入框');
|
||||
}
|
||||
inputElement.focus()
|
||||
return inputElement;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定 image_id 的图片
|
||||
*/
|
||||
function GetGeneratedMJImageAndSplit(str) {
|
||||
let res = [];
|
||||
for (let i = 0; i < str.length; i++) {
|
||||
const element = str[i];
|
||||
let image_id = element.image_id;
|
||||
let links = document.getElementsByTagName('a');
|
||||
// 创建一个空数组来存储包含特定字符串的链接
|
||||
let result = [];
|
||||
// 遍历所有的链接
|
||||
for (let i = 0; i < links.length; i++) {
|
||||
// 如果链接的href属性包含特定的字符串,就把它添加到结果数组中
|
||||
if (links[i].href.includes(image_id)) {
|
||||
result.push(links[i].href);
|
||||
}
|
||||
}
|
||||
if (result.length > 0) {
|
||||
res.push({
|
||||
id: element.id,
|
||||
image_id: element.image_id,
|
||||
result: result[0],
|
||||
name: element.name
|
||||
});
|
||||
}
|
||||
return JSON.stringify(res);
|
||||
}
|
||||
}
|
||||
|
||||
window['updateObserver'] = {};
|
||||
|
||||
/**
|
||||
* 获取Discord的消息输入框的X,Y位置,每次输入前都要检测一下
|
||||
*/
|
||||
function GetMessageInputPosition() {
|
||||
try {
|
||||
let inputElement = getInputFoucs();
|
||||
const rect = inputElement.getBoundingClientRect();
|
||||
// 计算鼠标点击位置(假设点击在元素中心)
|
||||
const mouseX = rect.left + rect.width / 2;
|
||||
const mouseY = rect.top + rect.height / 2;
|
||||
sightingTelescope(mouseX, mouseY)
|
||||
return { mouseX, mouseY };
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 瞄准镜
|
||||
*/
|
||||
function sightingTelescope(x, y) {
|
||||
var div = document.createElement('div');
|
||||
div.style.position = 'absolute';
|
||||
div.style.left = (x - 1) + "px";
|
||||
div.style.top = (y - 15) + "px";
|
||||
div.style.width = '30px';
|
||||
div.style.height = '30px';
|
||||
div.style.zIndex = '9999';
|
||||
div.style.border = '1px dashed green';
|
||||
div.style.color = 'green';
|
||||
div.style.textAlign = 'center';
|
||||
div.style.fontWeight = '600';
|
||||
div.style.borderRadius = '50%';
|
||||
div.style.fontSize = '30px';
|
||||
div.textContent = '+';
|
||||
document.body.appendChild(div);
|
||||
setTimeout(function () {
|
||||
div.parentNode.removeChild(div);
|
||||
}, 3000);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断discord中的输入框中是不是有数据直接返回
|
||||
* 有数据返回true,无数据返回false
|
||||
*/
|
||||
function GetMessageInputValue() {
|
||||
let inputElement = getInputFoucs();
|
||||
// 判断是不是为空
|
||||
let emptyElement = inputElement.querySelector('span[class^="emptyText_"]');
|
||||
if (emptyElement) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 监听指定的元素
|
||||
*/
|
||||
function MutationObserverElement() {
|
||||
// 观察的节点
|
||||
let messageList = document.querySelector('ol[class^="scrollerInner_"][data-list-id^="chat-message"]');
|
||||
|
||||
// 观察器的配置(需要观察什么变动)
|
||||
const config = { childList: true, subtree: true };
|
||||
|
||||
// 当观察到变动时执行的回调函数
|
||||
const callback = function (mutationsList, observer) {
|
||||
for (let mutation of mutationsList) {
|
||||
if (mutation.type === 'childList') {
|
||||
if (mutation.addedNodes.length) {
|
||||
// 判断当前此修改是不是包含发送的消息
|
||||
for (let i = 0; i < mutation.addedNodes.length; i++) {
|
||||
const element = mutation.addedNodes[i];
|
||||
let id = element.id;
|
||||
if (id && id.includes('chat-messages-')) {
|
||||
let data = {};
|
||||
let id_arr = id.split('-');
|
||||
data["message_id"] = id_arr[id_arr.length - 1];
|
||||
data["channel_id"] = id_arr[id_arr.length - 2];
|
||||
data["tiamspan"] = new Date().getTime();
|
||||
|
||||
// 获取消息的ID
|
||||
let data_list = element.querySelector('div[data-list-item-id^="chat-messages"]');
|
||||
if (!data_list) {
|
||||
return;
|
||||
}
|
||||
// 判断下面是不是有数据
|
||||
|
||||
let content_data = data_list.querySelector('div[class^="contents_"]')?.querySelector('div[id^="message-content"]');
|
||||
|
||||
if (!content_data) {
|
||||
return;
|
||||
}
|
||||
// 判断下面是不是有元素
|
||||
let has_data = content_data.hasChildNodes();
|
||||
|
||||
// 找到之后,再重新找节点,创建数据
|
||||
let mainCurrentNode = {
|
||||
"currentNode": data_list,
|
||||
"firstNode": content_data
|
||||
}
|
||||
|
||||
let res = getMessageData(mainCurrentNode);
|
||||
for (let d in res) {
|
||||
data[d] = res[d];
|
||||
}
|
||||
|
||||
// 对应的元素存在,就是一个新消息 返回
|
||||
if (!has_data) {
|
||||
console.log("LAItool 新增消息", element)
|
||||
data.code = 1;
|
||||
window.discord.CreateMessage(JSON.stringify(data))
|
||||
MutationObserverUpdateElement(id)
|
||||
} else {
|
||||
// 判断下面是不是有
|
||||
let wrapper = content_data.querySelector('div[class^="wrapper"]');
|
||||
// 判断下面是不是strong元素
|
||||
let strong = content_data.querySelector('strong');
|
||||
|
||||
if (!wrapper && strong) {
|
||||
console.log("LAItool 新增消息", element);
|
||||
data.code = 1;
|
||||
window.discord.CreateMessage(JSON.stringify(data))
|
||||
MutationObserverUpdateElement(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (mutation.removedNodes.length) {
|
||||
// 判断当前此修改是不是包含发送的消息
|
||||
for (let i = 0; i < mutation.removedNodes.length; i++) {
|
||||
const element = mutation.removedNodes[i];
|
||||
let id = element.id;
|
||||
if (id && id.includes('chat-messages-')) {
|
||||
let data = {};
|
||||
let id_arr = id.split('-');
|
||||
data["message_id"] = id_arr[id_arr.length - 1];
|
||||
data["channel_id"] = id_arr[id_arr.length - 2];
|
||||
data["tiamspan"] = new Date().getTime();
|
||||
|
||||
// 获取消息的ID
|
||||
let data_list = element.querySelector('div[data-list-item-id^="chat-messages"]');
|
||||
if (!data_list) {
|
||||
return;
|
||||
}
|
||||
// 判断下面是不是有数据
|
||||
|
||||
let content_data = data_list.querySelector('div[class^="contents_"]')?.querySelector('div[id^="message-content"]');
|
||||
|
||||
if (!content_data) {
|
||||
return;
|
||||
}
|
||||
// 判断下面是不是有元素
|
||||
let has_data = content_data.hasChildNodes();
|
||||
|
||||
// 找到之后,再重新找节点,创建数据
|
||||
let mainCurrentNode = {
|
||||
"currentNode": data_list,
|
||||
"firstNode": content_data
|
||||
}
|
||||
|
||||
let res = getMessageData(mainCurrentNode);
|
||||
for (let d in res) {
|
||||
data[d] = res[d];
|
||||
}
|
||||
|
||||
// 对应的元素存在,就是一个新消息 返回
|
||||
if (!has_data) {
|
||||
console.log("LAItool 删除消息", element)
|
||||
|
||||
// MutationObserverUpdateElement(id)
|
||||
data.code = 1;
|
||||
window.discord.DeleteMessage(JSON.stringify(data))
|
||||
|
||||
stopUpdateObserver(id)
|
||||
} else {
|
||||
// 判断下面是不是有
|
||||
let wrapper = content_data.querySelector('div[class^="wrapper"]');
|
||||
// 判断下面是不是strong元素
|
||||
let strong = content_data.querySelector('strong');
|
||||
|
||||
if (!wrapper && strong) {
|
||||
console.log("LAItool 删除消息", element)
|
||||
data.code = 1;
|
||||
window.discord.DeleteMessage(JSON.stringify(data))
|
||||
stopUpdateObserver(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 创建一个观察器实例并传入回调函数
|
||||
window.observer = new MutationObserver(callback);
|
||||
|
||||
// 以上述配置开始观察目标节点
|
||||
window.observer.observe(messageList, config);
|
||||
}
|
||||
|
||||
|
||||
// 往上找到指定的标签
|
||||
function findParentNode(startNode) {
|
||||
let data = {};
|
||||
let currentNode = startNode;
|
||||
|
||||
while (currentNode !== null) {
|
||||
|
||||
if (!data["currentNode"] && currentNode.id && currentNode.id.startsWith('message-content')) {
|
||||
data["firstNode"] = currentNode;
|
||||
}
|
||||
|
||||
if (currentNode.getAttribute && currentNode.getAttribute('data-list-item-id') && currentNode.getAttribute('data-list-item-id').startsWith('chat-messages')) {
|
||||
data["currentNode"] = currentNode;
|
||||
return data;
|
||||
}
|
||||
currentNode = currentNode.parentNode;
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
function gatherNodeData(node) {
|
||||
let result = node.textContent || ''; // 获取当前节点的文本内容
|
||||
return result;
|
||||
// 遍历所有子节点
|
||||
}
|
||||
|
||||
// 获取一个数据的当前消息的数据
|
||||
function getMessageData(mainData) {
|
||||
if (!mainData["currentNode"]) {
|
||||
window.discord.UpdateMessage(JSON.stringify({ code: 0, message: "没有找到对应的消息" }))
|
||||
return;
|
||||
}
|
||||
// 获取当前的消息ID
|
||||
|
||||
let res = {};
|
||||
|
||||
let data_arr = mainData["currentNode"].getAttribute('data-list-item-id')?.split('-');
|
||||
let message_id = data_arr[data_arr.length - 1];
|
||||
let channel_id = data_arr[data_arr.length - 2];
|
||||
res["message_id"] = message_id;
|
||||
res["channel_id"] = channel_id;
|
||||
|
||||
// 判断是不是又违规的消息
|
||||
let text = mainData["currentNode"].textContent.toLowerCase().includes("Banned prompt detected");
|
||||
|
||||
|
||||
if (text) {
|
||||
res["error"] = "Banned prompt detected";
|
||||
res.code = 1;
|
||||
window.discord.UpdateMessage(JSON.stringify(res))
|
||||
}
|
||||
|
||||
// 获取提示词相关的数据
|
||||
// 开始拼接提示词数据
|
||||
let message_prompt = null;
|
||||
if (!mainData["firstNode"]) {
|
||||
message_prompt = null;
|
||||
} else {
|
||||
message_prompt = gatherNodeData(mainData["firstNode"]);
|
||||
}
|
||||
res["message_prompt"] = message_prompt;
|
||||
|
||||
// 判断是不是有图片信息
|
||||
let image_content = mainData["currentNode"].querySelector('div[id^="message-accessories-' + message_id + '"]')
|
||||
?.querySelector('div[class^="visualMediaItemContainer"]')
|
||||
?.querySelector('div[class^="imageWrapper"]');
|
||||
|
||||
let image_click = null;
|
||||
let image_show = null;
|
||||
if (image_content) {
|
||||
// 最后的图片信息
|
||||
image_click = image_content.querySelector('a').href;
|
||||
image_show = image_content.querySelector('a').getAttribute("data-safe-src");
|
||||
}
|
||||
res["image_click"] = image_click;
|
||||
res["image_show"] = image_show;
|
||||
return res;
|
||||
|
||||
}
|
||||
|
||||
|
||||
function MutationObserverUpdateElement(elementId) {
|
||||
// 观察的节点
|
||||
let messageList = document.querySelector('li[id^="' + elementId + '"]');
|
||||
|
||||
// 观察器的配置(需要观察什么变动)
|
||||
const config = {
|
||||
characterData: true,
|
||||
subtree: true
|
||||
};
|
||||
|
||||
// 当观察到变动时执行的回调函数
|
||||
const callback = function (mutationsList, observer) {
|
||||
// 判断当前节点是不是有更新
|
||||
for (let mutation of mutationsList) {
|
||||
if (mutation.type === 'characterData') {
|
||||
console.log(mutation)
|
||||
console.log("LAITool 修改消息: " + 'The ' + mutation.attributeName + ' attribute was modified.', mutation.target);
|
||||
|
||||
|
||||
// 往上找数据,开始获取数据
|
||||
let mainData = findParentNode(mutation.target);
|
||||
|
||||
let res = getMessageData(mainData);
|
||||
|
||||
console.log("LAITool 修改消息", res);
|
||||
res.code = 1;
|
||||
window.discord.UpdateMessage(JSON.stringify(res));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
if (!window['updateObserver'][elementId]) {
|
||||
window['updateObserver'][elementId] = new MutationObserver(callback)
|
||||
}
|
||||
|
||||
// 以上述配置开始观察目标节点
|
||||
window['updateObserver'][elementId].observe(messageList, config);
|
||||
}
|
||||
|
||||
// 开始监听
|
||||
function startObserving() {
|
||||
// 判断当前是不是有观察器
|
||||
if (window.observer) {
|
||||
return;
|
||||
}
|
||||
debugger
|
||||
// 没有开始监听
|
||||
MutationObserverElement();
|
||||
console.log("开始监听")
|
||||
return "开始监听";
|
||||
}
|
||||
|
||||
/**
|
||||
* 停止监听
|
||||
*/
|
||||
function stopObserver() {
|
||||
window.observer.disconnect();
|
||||
}
|
||||
|
||||
/**
|
||||
* 停止监听
|
||||
*/
|
||||
function stopUpdateObserver(elementId) {
|
||||
if (window['updateObserver'][elementId]) {
|
||||
window['updateObserver'][elementId].disconnect();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,780 @@
|
||||
let fspromises = require("fs").promises;
|
||||
import path from "path";
|
||||
import { define } from "../../define/define";
|
||||
import { Tools } from "../tools";
|
||||
import { once } from "events";
|
||||
import { DiscordRequestListener } from "./discordRequestLister";
|
||||
import { DEFINE_STRING } from "../../define/define_string";
|
||||
|
||||
/**
|
||||
* 对DisCord窗口进行操作的方法
|
||||
*/
|
||||
export class DiscordSimple {
|
||||
constructor(win) {
|
||||
this.win = win;
|
||||
this.tools = new Tools();
|
||||
this.script = define.discordScript;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 创建discord窗口
|
||||
* @param {*} createWindow 创建的函数(其实没有必要穿)
|
||||
* @returns 返回创建的窗体的示例
|
||||
*/
|
||||
async CreateDiscordWindow(createWindow) {
|
||||
try {
|
||||
// 判断是不是打开了discord窗口
|
||||
let index = global.newWindow.findIndex((item) => item.hash == "discord");
|
||||
if (index != -1) {
|
||||
return {
|
||||
code: 0,
|
||||
message: "已经打开了discord窗口"
|
||||
}
|
||||
}
|
||||
// 要增加判断(可以加载到指定的界面)
|
||||
// 判断是不是有MJ的对应的配置,没有的话加载到默认的界面,有的话加载到指定的界面
|
||||
let mj_config = await this.tools.getJsonFilePropertyValue(define.img_base, "mj_config", null, false);
|
||||
let url = 'https://discord.com/app';
|
||||
if (mj_config && mj_config.serverID && mj_config.channelID) {
|
||||
url = `https://discord.com/channels/${mj_config.serverID}/${mj_config.channelID}`;
|
||||
}
|
||||
let w = await createWindow("discord", null, url);
|
||||
return w;
|
||||
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 监听界面刷新完毕事件
|
||||
*/
|
||||
async WaitWindowFinishLoad(discordWindow) {
|
||||
try {
|
||||
async function waitForEvent(emitter, eventName, timeout) {
|
||||
const eventPromise = once(emitter, eventName);
|
||||
|
||||
const timeoutPromise = new Promise((_, reject) => {
|
||||
const timer = setTimeout(() => {
|
||||
clearTimeout(timer);
|
||||
reject(new Error('Timeout Open Discord Dialog'));
|
||||
}, timeout);
|
||||
});
|
||||
|
||||
await Promise.race([eventPromise, timeoutPromise]);
|
||||
}
|
||||
|
||||
// 使用方法(监听窗口启动,如果10秒没有启动,使用代码关闭,移除队列)
|
||||
await waitForEvent(discordWindow.webContents, 'did-finish-load', 10000);
|
||||
return {
|
||||
code: 1
|
||||
}
|
||||
} catch (error) {
|
||||
// 关闭窗口
|
||||
discordWindow.win.close();
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化DisCord的配置文件
|
||||
*/
|
||||
async InitData() {
|
||||
if (this.mj_config) return;
|
||||
// 初始化配置文件,若是没有配置文件,则创建一个配置文件
|
||||
let mj_config_default = {
|
||||
serviceID: null,
|
||||
channelID: null,
|
||||
authorization: null,
|
||||
userAgent: null
|
||||
}
|
||||
// 判断是不是有对应的mj的配置,没有的话就返回默认值
|
||||
let mj_config = await this.tools.getJsonFilePropertyValue(define.img_base, "mj_config", mj_config_default, false);
|
||||
this.mj_config = mj_config;
|
||||
}
|
||||
|
||||
// 在每次输入之前获取指定的位置
|
||||
// 获取当前界面中Input的位置
|
||||
/**
|
||||
* 获取当前界面中Input的位置
|
||||
*/
|
||||
async GetInputPosition() {
|
||||
try {
|
||||
await this.InitData();
|
||||
await this.tools.delay(10000)
|
||||
let result = await this.ExecuteScript(this.script, 'GetMessageInputPosition()');
|
||||
this.x = result.mouseX;
|
||||
this.y = result.mouseY;
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 封装一个执行脚本的方法
|
||||
* @param {*} scriptpath 要执行的脚本的路径
|
||||
* @param {*} funcname 执行脚本的方法名(要暴露出来)
|
||||
*/
|
||||
async ExecuteScript(scriptpath, funcname = "") {
|
||||
try {
|
||||
await this.InitData();
|
||||
// let script = await fspromises.readFile(scriptpath, 'utf-8');
|
||||
let script = `
|
||||
// @noBytecode
|
||||
|
||||
/**
|
||||
* 获取Discord的消息输入框,并聚焦
|
||||
* @returns 返回获取的消息输入框
|
||||
*/
|
||||
function getInputFoucs() {
|
||||
let selector = 'div[contenteditable="true"][class^="markup_"]';
|
||||
const inputElement = document.querySelector(selector) // as HTMLInputElement;
|
||||
if (!inputElement) {
|
||||
throw new Error('没有找到对应的消息输入框');
|
||||
}
|
||||
inputElement.focus()
|
||||
return inputElement;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定 image_id 的图片
|
||||
*/
|
||||
function GetGeneratedMJImageAndSplit(str) {
|
||||
let res = [];
|
||||
for (let i = 0; i < str.length; i++) {
|
||||
const element = str[i];
|
||||
let image_id = element.image_id;
|
||||
let links = document.getElementsByTagName('a');
|
||||
// 创建一个空数组来存储包含特定字符串的链接
|
||||
let result = [];
|
||||
// 遍历所有的链接
|
||||
for (let i = 0; i < links.length; i++) {
|
||||
// 如果链接的href属性包含特定的字符串,就把它添加到结果数组中
|
||||
if (links[i].href.includes(image_id)) {
|
||||
result.push(links[i].href);
|
||||
}
|
||||
}
|
||||
if (result.length > 0) {
|
||||
res.push({
|
||||
id: element.id,
|
||||
image_id: element.image_id,
|
||||
result: result[0],
|
||||
name: element.name
|
||||
});
|
||||
}
|
||||
return JSON.stringify(res);
|
||||
}
|
||||
}
|
||||
|
||||
window['updateObserver'] = {};
|
||||
|
||||
/**
|
||||
* 获取Discord的消息输入框的X,Y位置,每次输入前都要检测一下
|
||||
*/
|
||||
function GetMessageInputPosition() {
|
||||
try {
|
||||
let inputElement = getInputFoucs();
|
||||
const rect = inputElement.getBoundingClientRect();
|
||||
// 计算鼠标点击位置(假设点击在元素中心)
|
||||
const mouseX = rect.left + rect.width / 2;
|
||||
const mouseY = rect.top + rect.height / 2;
|
||||
sightingTelescope(mouseX, mouseY)
|
||||
return { mouseX, mouseY };
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 瞄准镜
|
||||
*/
|
||||
function sightingTelescope(x, y) {
|
||||
var div = document.createElement('div');
|
||||
div.style.position = 'absolute';
|
||||
div.style.left = (x - 1) + "px";
|
||||
div.style.top = (y - 15) + "px";
|
||||
div.style.width = '30px';
|
||||
div.style.height = '30px';
|
||||
div.style.zIndex = '9999';
|
||||
div.style.border = '1px dashed green';
|
||||
div.style.color = 'green';
|
||||
div.style.textAlign = 'center';
|
||||
div.style.fontWeight = '600';
|
||||
div.style.borderRadius = '50%';
|
||||
div.style.fontSize = '30px';
|
||||
div.textContent = '+';
|
||||
document.body.appendChild(div);
|
||||
setTimeout(function () {
|
||||
div.parentNode.removeChild(div);
|
||||
}, 3000);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断discord中的输入框中是不是有数据直接返回
|
||||
* 有数据返回true,无数据返回false
|
||||
*/
|
||||
function GetMessageInputValue() {
|
||||
let inputElement = getInputFoucs();
|
||||
// 判断是不是为空
|
||||
let emptyElement = inputElement.querySelector('span[class^="emptyText_"]');
|
||||
if (emptyElement) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 监听指定的元素
|
||||
*/
|
||||
function MutationObserverElement() {
|
||||
// 观察的节点
|
||||
let messageList = document.querySelector('ol[class^="scrollerInner_"][data-list-id^="chat-message"]');
|
||||
|
||||
// 观察器的配置(需要观察什么变动)
|
||||
const config = { childList: true, subtree: true };
|
||||
|
||||
// 当观察到变动时执行的回调函数
|
||||
const callback = function (mutationsList, observer) {
|
||||
for (let mutation of mutationsList) {
|
||||
if (mutation.type === 'childList') {
|
||||
if (mutation.addedNodes.length) {
|
||||
// 判断当前此修改是不是包含发送的消息
|
||||
for (let i = 0; i < mutation.addedNodes.length; i++) {
|
||||
const element = mutation.addedNodes[i];
|
||||
let id = element.id;
|
||||
if (id && id.includes('chat-messages-')) {
|
||||
let data = {};
|
||||
let id_arr = id.split('-');
|
||||
data["message_id"] = id_arr[id_arr.length - 1];
|
||||
data["channel_id"] = id_arr[id_arr.length - 2];
|
||||
data["tiamspan"] = new Date().getTime();
|
||||
|
||||
// 获取消息的ID
|
||||
let data_list = element.querySelector('div[data-list-item-id^="chat-messages"]');
|
||||
if (!data_list) {
|
||||
return;
|
||||
}
|
||||
// 判断下面是不是有数据
|
||||
|
||||
let content_data = data_list.querySelector('div[class^="contents_"]')?.querySelector('div[id^="message-content"]');
|
||||
|
||||
if (!content_data) {
|
||||
return;
|
||||
}
|
||||
// 判断下面是不是有元素
|
||||
let has_data = content_data.hasChildNodes();
|
||||
|
||||
// 找到之后,再重新找节点,创建数据
|
||||
let mainCurrentNode = {
|
||||
"currentNode": data_list,
|
||||
"firstNode": content_data
|
||||
}
|
||||
|
||||
let res = getMessageData(mainCurrentNode);
|
||||
for (let d in res) {
|
||||
data[d] = res[d];
|
||||
}
|
||||
|
||||
// 对应的元素存在,就是一个新消息 返回
|
||||
if (!has_data) {
|
||||
console.log("LAItool 新增消息", element)
|
||||
data.code = 1;
|
||||
window.discord.CreateMessage(JSON.stringify(data))
|
||||
MutationObserverUpdateElement(id)
|
||||
} else {
|
||||
// 判断下面是不是有
|
||||
let wrapper = content_data.querySelector('div[class^="wrapper"]');
|
||||
// 判断下面是不是strong元素
|
||||
let strong = content_data.querySelector('strong');
|
||||
|
||||
if (!wrapper && strong) {
|
||||
console.log("LAItool 新增消息", element);
|
||||
data.code = 1;
|
||||
window.discord.CreateMessage(JSON.stringify(data))
|
||||
MutationObserverUpdateElement(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (mutation.removedNodes.length) {
|
||||
// 判断当前此修改是不是包含发送的消息
|
||||
for (let i = 0; i < mutation.removedNodes.length; i++) {
|
||||
const element = mutation.removedNodes[i];
|
||||
let id = element.id;
|
||||
if (id && id.includes('chat-messages-')) {
|
||||
let data = {};
|
||||
let id_arr = id.split('-');
|
||||
data["message_id"] = id_arr[id_arr.length - 1];
|
||||
data["channel_id"] = id_arr[id_arr.length - 2];
|
||||
data["tiamspan"] = new Date().getTime();
|
||||
|
||||
// 获取消息的ID
|
||||
let data_list = element.querySelector('div[data-list-item-id^="chat-messages"]');
|
||||
if (!data_list) {
|
||||
return;
|
||||
}
|
||||
// 判断下面是不是有数据
|
||||
|
||||
let content_data = data_list.querySelector('div[class^="contents_"]')?.querySelector('div[id^="message-content"]');
|
||||
|
||||
if (!content_data) {
|
||||
return;
|
||||
}
|
||||
// 判断下面是不是有元素
|
||||
let has_data = content_data.hasChildNodes();
|
||||
|
||||
// 找到之后,再重新找节点,创建数据
|
||||
let mainCurrentNode = {
|
||||
"currentNode": data_list,
|
||||
"firstNode": content_data
|
||||
}
|
||||
|
||||
let res = getMessageData(mainCurrentNode);
|
||||
for (let d in res) {
|
||||
data[d] = res[d];
|
||||
}
|
||||
|
||||
// 对应的元素存在,就是一个新消息 返回
|
||||
if (!has_data) {
|
||||
console.log("LAItool 删除消息", element)
|
||||
|
||||
// MutationObserverUpdateElement(id)
|
||||
data.code = 1;
|
||||
window.discord.DeleteMessage(JSON.stringify(data))
|
||||
|
||||
stopUpdateObserver(id)
|
||||
} else {
|
||||
// 判断下面是不是有
|
||||
let wrapper = content_data.querySelector('div[class^="wrapper"]');
|
||||
// 判断下面是不是strong元素
|
||||
let strong = content_data.querySelector('strong');
|
||||
|
||||
if (!wrapper && strong) {
|
||||
console.log("LAItool 删除消息", element)
|
||||
data.code = 1;
|
||||
window.discord.DeleteMessage(JSON.stringify(data))
|
||||
stopUpdateObserver(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 创建一个观察器实例并传入回调函数
|
||||
window.observer = new MutationObserver(callback);
|
||||
|
||||
// 以上述配置开始观察目标节点
|
||||
window.observer.observe(messageList, config);
|
||||
}
|
||||
|
||||
|
||||
// 往上找到指定的标签
|
||||
function findParentNode(startNode) {
|
||||
let data = {};
|
||||
let currentNode = startNode;
|
||||
|
||||
while (currentNode !== null) {
|
||||
|
||||
if (!data["currentNode"] && currentNode.id && currentNode.id.startsWith('message-content')) {
|
||||
data["firstNode"] = currentNode;
|
||||
}
|
||||
|
||||
if (currentNode.getAttribute && currentNode.getAttribute('data-list-item-id') && currentNode.getAttribute('data-list-item-id').startsWith('chat-messages')) {
|
||||
data["currentNode"] = currentNode;
|
||||
return data;
|
||||
}
|
||||
currentNode = currentNode.parentNode;
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
function gatherNodeData(node) {
|
||||
let result = node.textContent || ''; // 获取当前节点的文本内容
|
||||
return result;
|
||||
// 遍历所有子节点
|
||||
}
|
||||
|
||||
// 获取一个数据的当前消息的数据
|
||||
function getMessageData(mainData) {
|
||||
if (!mainData["currentNode"]) {
|
||||
window.discord.UpdateMessage(JSON.stringify({ code: 0, message: "没有找到对应的消息" }))
|
||||
return;
|
||||
}
|
||||
// 获取当前的消息ID
|
||||
|
||||
let res = {};
|
||||
|
||||
let data_arr = mainData["currentNode"].getAttribute('data-list-item-id')?.split('-');
|
||||
let message_id = data_arr[data_arr.length - 1];
|
||||
let channel_id = data_arr[data_arr.length - 2];
|
||||
res["message_id"] = message_id;
|
||||
res["channel_id"] = channel_id;
|
||||
|
||||
// 判断是不是又违规的消息
|
||||
let text = mainData["currentNode"].textContent.toLowerCase().includes("Banned prompt detected");
|
||||
|
||||
|
||||
if (text) {
|
||||
res["error"] = "Banned prompt detected";
|
||||
res.code = 1;
|
||||
window.discord.UpdateMessage(JSON.stringify(res))
|
||||
}
|
||||
|
||||
// 获取提示词相关的数据
|
||||
// 开始拼接提示词数据
|
||||
let message_prompt = null;
|
||||
if (!mainData["firstNode"]) {
|
||||
message_prompt = null;
|
||||
} else {
|
||||
message_prompt = gatherNodeData(mainData["firstNode"]);
|
||||
}
|
||||
res["message_prompt"] = message_prompt;
|
||||
|
||||
// 判断是不是有图片信息
|
||||
let image_content = mainData["currentNode"].querySelector('div[id^="message-accessories-' + message_id + '"]')
|
||||
?.querySelector('div[class^="visualMediaItemContainer"]')
|
||||
?.querySelector('div[class^="imageWrapper"]');
|
||||
|
||||
let image_click = null;
|
||||
let image_show = null;
|
||||
if (image_content) {
|
||||
// 最后的图片信息
|
||||
image_click = image_content.querySelector('a').href;
|
||||
image_show = image_content.querySelector('a').getAttribute("data-safe-src");
|
||||
}
|
||||
res["image_click"] = image_click;
|
||||
res["image_show"] = image_show;
|
||||
return res;
|
||||
|
||||
}
|
||||
|
||||
|
||||
function MutationObserverUpdateElement(elementId) {
|
||||
// 观察的节点
|
||||
let messageList = document.querySelector('li[id^="' + elementId + '"]');
|
||||
|
||||
// 观察器的配置(需要观察什么变动)
|
||||
const config = {
|
||||
characterData: true,
|
||||
subtree: true
|
||||
};
|
||||
|
||||
// 当观察到变动时执行的回调函数
|
||||
const callback = function (mutationsList, observer) {
|
||||
// 判断当前节点是不是有更新
|
||||
for (let mutation of mutationsList) {
|
||||
if (mutation.type === 'characterData') {
|
||||
console.log(mutation)
|
||||
console.log("LAITool 修改消息: " + 'The ' + mutation.attributeName + ' attribute was modified.', mutation.target);
|
||||
|
||||
|
||||
// 往上找数据,开始获取数据
|
||||
let mainData = findParentNode(mutation.target);
|
||||
|
||||
let res = getMessageData(mainData);
|
||||
|
||||
console.log("LAITool 修改消息", res);
|
||||
res.code = 1;
|
||||
window.discord.UpdateMessage(JSON.stringify(res));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
if (!window['updateObserver'][elementId]) {
|
||||
window['updateObserver'][elementId] = new MutationObserver(callback)
|
||||
}
|
||||
|
||||
// 以上述配置开始观察目标节点
|
||||
window['updateObserver'][elementId].observe(messageList, config);
|
||||
}
|
||||
|
||||
// 开始监听
|
||||
function startObserving() {
|
||||
// 判断当前是不是有观察器
|
||||
if (window.observer) {
|
||||
return;
|
||||
}
|
||||
debugger
|
||||
// 没有开始监听
|
||||
MutationObserverElement();
|
||||
console.log("开始监听")
|
||||
return "开始监听";
|
||||
}
|
||||
|
||||
/**
|
||||
* 停止监听
|
||||
*/
|
||||
function stopObserver() {
|
||||
window.observer.disconnect();
|
||||
}
|
||||
|
||||
/**
|
||||
* 停止监听
|
||||
*/
|
||||
function stopUpdateObserver(elementId) {
|
||||
if (window['updateObserver'][elementId]) {
|
||||
window['updateObserver'][elementId].disconnect();
|
||||
}
|
||||
}
|
||||
`;
|
||||
this.win.webContents.focus();
|
||||
let result = await this.win.webContents.executeJavaScript(`${script}\n${funcname}`);
|
||||
return result;
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 每句提示词输入之前,删除现在的数据
|
||||
*/
|
||||
async onDelete() {
|
||||
await this.InitData();
|
||||
// 执行脚本判断是不是又数据,有的话删除数据
|
||||
let res = await this.ExecuteScript(this.script, "GetMessageInputValue()")
|
||||
// 有数据,删除
|
||||
if (res) {
|
||||
// 发送全选事件
|
||||
this.win.webContents.sendInputEvent({ type: 'keyDown', keyCode: 'A', modifiers: ['Control'] });
|
||||
this.win.webContents.sendInputEvent({ type: 'keyUp', keyCode: 'A', modifiers: ['Control'] });
|
||||
await this.tools.delay(300) // 等待全选完成
|
||||
// 发送删除事件
|
||||
this.win.webContents.sendInputEvent({ type: 'keyDown', keyCode: 'Delete' });
|
||||
this.win.webContents.sendInputEvent({ type: 'keyUp', keyCode: 'Delete' });
|
||||
// 判断是不是被删除
|
||||
do {
|
||||
await this.tools.delay(300);
|
||||
res = await this.ExecuteScript(this.script, "GetMessageInputValue()")
|
||||
}
|
||||
while (res);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 将提示词写入到指定的input中(一句)
|
||||
* @param {*} prompt
|
||||
*/
|
||||
async WritePromptToInput(prompt) {
|
||||
try {
|
||||
await this.InitData();
|
||||
|
||||
await this.GetInputPosition();
|
||||
|
||||
// 判断当前的输入框中是不是又数据,又的话删除数据
|
||||
await this.onDelete();
|
||||
|
||||
// 当前选择的机器人命令
|
||||
let command = "/imagine";
|
||||
// 循环写入数据
|
||||
for (let i = 0; i < command.length; i++) {
|
||||
const key = command[i];
|
||||
let simpleWait = Math.floor(Math.random() * (150));
|
||||
await this.tools.delay(simpleWait)
|
||||
|
||||
this.win.webContents.sendInputEvent({ type: 'char', keyCode: key });
|
||||
}
|
||||
let simpleWait = Math.floor(Math.random() * (5000)) + 1000;
|
||||
await this.tools.delay(simpleWait)
|
||||
// 写完之后,执行enter
|
||||
this.win.webContents.sendInputEvent({ type: 'keyDown', keyCode: 'Enter' });;
|
||||
|
||||
|
||||
// 循环写入数据
|
||||
for (let i = 0; i < prompt.length; i++) {
|
||||
const key = prompt[i];
|
||||
if (key == "") {
|
||||
let spaceTime = Math.floor(Math.random() * (500 - 100)) + 100;
|
||||
await this.tools.delay(spaceTime)
|
||||
} else {
|
||||
let simpleWait = Math.floor(Math.random() * (150));
|
||||
await this.tools.delay(simpleWait)
|
||||
}
|
||||
|
||||
this.win.webContents.sendInputEvent({ type: 'char', keyCode: key });
|
||||
|
||||
}
|
||||
// 执行命令
|
||||
let res = await this.ExecuteScript(this.script, 'startObserving()');
|
||||
console.log(res);
|
||||
|
||||
simpleWait = Math.floor(Math.random() * (1000));
|
||||
await this.tools.delay(simpleWait)
|
||||
|
||||
//
|
||||
// 写完之后,执行enter
|
||||
this.win.webContents.sendInputEvent({ type: 'keyDown', keyCode: 'Enter' });
|
||||
this.win.webContents.sendInputEvent({ type: 'keyUp', keyCode: 'Enter' });
|
||||
|
||||
// 开始监听网络请求
|
||||
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
sendChangeMessage(data) {
|
||||
global.newWindow[0].win.webContents.send(DEFINE_STRING.DISCORD.MAIN_DISCORD_MESSAGE_CHANGE, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* discord创建消息,并更新数据
|
||||
* @param {*} Value
|
||||
*/
|
||||
async DiscordCreateMessage(value) {
|
||||
try {
|
||||
value = JSON.parse(value);
|
||||
|
||||
let currentCreateItem = global.mjGenerateQuene.getCurrentCreateItem();
|
||||
console.log("LAITOOL 创建数据: ", value);
|
||||
value.type = "created"
|
||||
|
||||
// 判断是不是是不是错误数据
|
||||
if (value.error) {
|
||||
// 先移除任务
|
||||
global.mjGenerateQuene.removeTaskProgress((taskProgress) => {
|
||||
return taskProgress.filter(item => item?.message_id != value.message_id);
|
||||
})
|
||||
|
||||
// 在将当前任务设置为空
|
||||
global.mjGenerateQuene.setCurrentCreateItem(null);
|
||||
// 开始下一个任务
|
||||
global.mjGenerateQuene.startNextTask();
|
||||
}
|
||||
|
||||
|
||||
// 判断当前是不是有提示词
|
||||
if ((value.image_click == null || value.image_click == "") && (value.image_show == null || value.image_show == "") && value.message_prompt == "") {
|
||||
return;
|
||||
}
|
||||
|
||||
// 这是一个生成完成了(通过图片ID判断)
|
||||
if ((value.image_click != null && value.image_click != '') && value.message_prompt != "") {
|
||||
// 获取图片的ID
|
||||
let image_regex = /([a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12})/i;
|
||||
let image_match = value.image_click.match(image_regex);
|
||||
|
||||
if (image_match) {
|
||||
value["image_id"] = image_match[1];
|
||||
}
|
||||
|
||||
let image_path = path.join(global.config.project_path, `data\\MJOriginalImage\\${value.image_id}.png`);
|
||||
|
||||
// 这边开始下载对应的图片
|
||||
await this.tools.downloadFileUrl(value.image_click, image_path);
|
||||
|
||||
value["image_path"] = image_path;
|
||||
// 注意这边要移除掉id
|
||||
value.id = null;
|
||||
value.type = "finished"
|
||||
} else {
|
||||
if (currentCreateItem) {
|
||||
currentCreateItem["message_id"] = value.message_id;
|
||||
// 修改数据
|
||||
global.mjGenerateQuene.setCurrentCreateItem(currentCreateItem);
|
||||
// let currentCreateItem = { id: "6075f067-d824-45a4-9213-86e8f254cfd9" };
|
||||
value.id = currentCreateItem.id
|
||||
}
|
||||
}
|
||||
|
||||
// 获取获取
|
||||
this.sendChangeMessage(value)
|
||||
|
||||
// 创建或者是完成之后,开始下一个任务。
|
||||
|
||||
// 判断是不是创建(创建的话直接判断)
|
||||
// 获取正在执行的任务,自己保存的,不是设置的。现在正在后台执行的mj任务
|
||||
// 如果是创建。创建成功,直接删除当前数据
|
||||
if (value.type == "created") {
|
||||
global.mjGenerateQuene.setCurrentCreateItem(null);
|
||||
}
|
||||
|
||||
global.mjGenerateQuene.startNextTask();
|
||||
|
||||
} catch (error) {
|
||||
this.sendChangeMessage({
|
||||
code: 0,
|
||||
message: "MJ创建消息保存失败: " + error.toString()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 监听discord的更新消息。并修改数据
|
||||
* @param {*} value
|
||||
*/
|
||||
async DiscordUpdateMessage(value) {
|
||||
try {
|
||||
value = JSON.parse(value);
|
||||
// 接收到discord的消息
|
||||
console.log("LAITOOL 更新数据: ", value);
|
||||
value.type = "updated";
|
||||
|
||||
// 更新的时候,修改数据(判断是不是有进度)
|
||||
let regex = /\((\d+)%\)/;
|
||||
let match = value["message_prompt"].match(regex);
|
||||
if (match) {
|
||||
value.progress = parseInt(match[1]);
|
||||
}
|
||||
// 判断是不是又图片,有的话。固定图片ID,并且只固定一次(前端固定吧)
|
||||
if (value.image_click) {
|
||||
let image_regex = /([a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12})/i;
|
||||
let image_match = value.image_click.match(image_regex);
|
||||
|
||||
if (image_match) {
|
||||
value["image_id"] = image_match[1];
|
||||
}
|
||||
}
|
||||
this.sendChangeMessage(value)
|
||||
|
||||
} catch (error) {
|
||||
this.sendChangeMessage({
|
||||
code: 0,
|
||||
message: "MJ更新消息保存失败: " + error.toString()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 监听discord的删除消息。并修改数据
|
||||
* @param {*} value
|
||||
*/
|
||||
async DiscordDeleteMessage(value) {
|
||||
try {
|
||||
value = JSON.parse(value)
|
||||
if (value['image_click'] == null && value['image_show'] == null && value['message_prompt'] == "") {
|
||||
return;
|
||||
}
|
||||
console.log("LAITOOL 删除数据: ", value)
|
||||
value.type = "delete"
|
||||
this.sendChangeMessage(value)
|
||||
|
||||
// 这边可能要做判断(判断是不是开启下一个)
|
||||
|
||||
// 删除当前消息ID对应的历史任务(传入一个构造函数,函数的参数是当前所有的历史任务)
|
||||
global.mjGenerateQuene.removeTaskProgress((taskProgress) => {
|
||||
return taskProgress.filter(item => item?.message_id != value.message_id);
|
||||
})
|
||||
|
||||
|
||||
} catch (error) {
|
||||
this.sendChangeMessage({
|
||||
code: 0,
|
||||
message: 'MJ删除数据保存失败 :' + error.toString()
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
import { DiscordSimple } from "./discordSimple";
|
||||
import { Tools } from "../tools";
|
||||
import { define } from "../../define/define";
|
||||
|
||||
/**
|
||||
* discord中的一些公用的方法
|
||||
*/
|
||||
export class DiscordWorker {
|
||||
constructor() {
|
||||
this.tools = new Tools();
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化DisCord的配置文件
|
||||
*/
|
||||
async InitData() {
|
||||
if (this.mj_config) return;
|
||||
// 初始化配置文件,若是没有配置文件,则创建一个配置文件
|
||||
let mj_config_default = {
|
||||
serviceID: null,
|
||||
channelID: null,
|
||||
authorization: null,
|
||||
userAgent: null
|
||||
}
|
||||
// 判断是不是有对应的mj的配置,没有的话就返回默认值
|
||||
let mj_config = await this.tools.getJsonFilePropertyValue(define.img_base, "mj_config", mj_config_default, false);
|
||||
this.mj_config = mj_config;
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过hash判断指定的窗口是不是开启
|
||||
* @param {*} hash 指定界面的hash
|
||||
* @returns true/false
|
||||
*/
|
||||
CheckDiscordWindowIsOpen(hash) {
|
||||
try {
|
||||
let index = global.newWindow.findIndex(item => item.hash === hash);
|
||||
if (index == -1) {
|
||||
return null;
|
||||
} else {
|
||||
return global.newWindow[index].win
|
||||
}
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
// 判断当前的Doscord界面是不是开启,没有开启的话开启并加载到设置指定的界面
|
||||
async CheckDiscordWindowIsOpenAndLoad() {
|
||||
try {
|
||||
let discordW = this.CheckDiscordWindowIsOpen('discord');
|
||||
if (!discordW) {
|
||||
// 自动打开窗口(调用方法)
|
||||
let mainSimple = new DiscordSimple(global.newWindow[0].win);
|
||||
discordW = await mainSimple.CreateDiscordWindow(global.createWindow);
|
||||
//等待窗口加载完毕(接收消息-一直阻塞)
|
||||
await mainSimple.WaitWindowFinishLoad(discordW);
|
||||
}
|
||||
// 切换到指定的界面
|
||||
// 获取当前配置serverID和channelID是不是当前界面是一样的
|
||||
|
||||
await this.InitData();
|
||||
|
||||
let url = discordW.webContents.getURL();
|
||||
const regex = /\/channels\/(\d+)\/(\d+)/;
|
||||
const match = url.match(regex);
|
||||
let serviceID = null;
|
||||
let channelID = null;
|
||||
if (match && match[1] && match[2]) {
|
||||
serviceID = match[1];
|
||||
channelID = match[2];
|
||||
}
|
||||
if (!(serviceID && channelID && this.mj_config.serviceID === serviceID && this.mj_config.channelID === channelID)) {
|
||||
// 重新加载url
|
||||
// 重新拼接url
|
||||
await discordW.webContents.loadURL(`https://discord.com/channels/${this.mj_config.serviceID}/${this.mj_config.channelID}`)
|
||||
// 等待界面加载完毕
|
||||
}
|
||||
return discordW;
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,199 @@
|
||||
let path = require('path');
|
||||
import { Tools } from "../tools";
|
||||
import { DiscordAPI } from "./discordApi";
|
||||
import { MjSetting } from "../../define/setting/mjSetting";
|
||||
import { DynamicSetting } from "../../define/setting/dynamicSetting";
|
||||
import { AwesomeHelp } from "awesome-js"
|
||||
|
||||
export class MjSimple {
|
||||
constructor(global) {
|
||||
this.global = global;
|
||||
this.tools = new Tools();
|
||||
this.mjSetting = new MjSetting(global);
|
||||
this.dynamicSetting = new DynamicSetting(global);
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存文案的基础信息
|
||||
* @param {*} value 参数信息
|
||||
* @returns
|
||||
*/
|
||||
async SvaeMJWordSrt(value) {
|
||||
try {
|
||||
let mj_config = path.join(this.global.config.project_path, "scripts/mj_config.json");
|
||||
await this.tools.checkJsonFileExistsOrCreate(mj_config);
|
||||
this.tools.writeJsonFilePropertyValue(path.join(this.global.config.project_path, "scripts/mj_config.json"), value[1], value[0], false);
|
||||
return {
|
||||
code: 1,
|
||||
}
|
||||
} catch (error) {
|
||||
return {
|
||||
code: 0,
|
||||
message: error.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 获取MJ配置的字幕信息
|
||||
async GetMJConfigSrtInformation() {
|
||||
try {
|
||||
let mj_config = path.join(this.global.config.project_path, "scripts/mj_config.json");
|
||||
let res = await this.tools.getJsonFilePropertyValue(mj_config, "srt_time_information", [], false);
|
||||
return {
|
||||
code: 1,
|
||||
data: res
|
||||
}
|
||||
} catch (error) {
|
||||
return {
|
||||
code: 0,
|
||||
message: error.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 获取频道机器人
|
||||
async GetChannelRobots(value) {
|
||||
// let
|
||||
try {
|
||||
value = JSON.parse(value);
|
||||
let discordAPI = new DiscordAPI(value);
|
||||
let res = await discordAPI.getBotList();
|
||||
|
||||
|
||||
} catch (error) {
|
||||
return {
|
||||
code: 0,
|
||||
message: "获取频道机器人失败, 错误信息如下:" + error.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 获取MJ生图的方式
|
||||
async GetMJGenerateCategory() {
|
||||
return this.mjSetting.GetMJGenerateCategory();
|
||||
}
|
||||
|
||||
// 保存MJ敏感词
|
||||
async AddMJBadPrompt(value) {
|
||||
try {
|
||||
await this.dynamicSetting.saveDataByTypeAndProperty(value, "mj", "bad_prompt", (data) => {
|
||||
// 判断当前数据在默认的敏感词数据中是不是存在。
|
||||
let default_bad_prompt = this.mjSetting.GetMJBadPrompt().data;
|
||||
let index = default_bad_prompt.findIndex(item => item.value.toLowerCase() == value.value.toLowerCase());
|
||||
if (index >= 0) {
|
||||
throw new Error("当前敏感词已存在默认的敏感词中");
|
||||
}
|
||||
// 在判断当前数据在自定义的数据中是不是存在
|
||||
index = data.findIndex(item => item.value.toLowerCase() == value.value.toLowerCase());
|
||||
if (index >= 0) {
|
||||
throw new Error("当前敏感词已存在自定义的敏感词中");
|
||||
}
|
||||
|
||||
});
|
||||
return {
|
||||
code: 1,
|
||||
message: "添加铭感词成功"
|
||||
}
|
||||
} catch (error) {
|
||||
return {
|
||||
code: 0,
|
||||
message: error.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 获取MJ所有的敏感词
|
||||
async GetMJBadPrompt() {
|
||||
try {
|
||||
let default_bad_prompt = this.mjSetting.GetMJBadPrompt().data;
|
||||
let data = await this.dynamicSetting.getDataByTypeAndProperty("all", 'mj', 'bad_prompt', default_bad_prompt, []);
|
||||
return {
|
||||
code: 1,
|
||||
data: data
|
||||
}
|
||||
} catch (error) {
|
||||
return {
|
||||
code: 0,
|
||||
message: "获取敏感词失败, 错误信息如下:" + error.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查当前出入数据所有的敏感词
|
||||
* @param {*} data
|
||||
* @returns
|
||||
*/
|
||||
async MJBadPromptCheck(data) {
|
||||
try {
|
||||
data = JSON.parse(data);
|
||||
// 获取所有的敏感词
|
||||
let bad_prompts = await this.GetMJBadPrompt();
|
||||
if (bad_prompts.code == 0) {
|
||||
throw new Error(bad_prompts.message);
|
||||
}
|
||||
bad_prompts = bad_prompts.data.data;
|
||||
let bad_prompts_string = [];
|
||||
for (let i = 0; i < bad_prompts.length; i++) {
|
||||
const element = bad_prompts[i];
|
||||
bad_prompts_string.push(element.value);
|
||||
}
|
||||
let bad_prompt_ids = [];
|
||||
|
||||
let badPromptMap = AwesomeHelp.makeSensitiveMap(bad_prompts_string);
|
||||
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
const element = data[i];
|
||||
let prompt = element.prompt;
|
||||
if (!prompt || prompt == "") {
|
||||
continue;
|
||||
}
|
||||
// 判断是不是存在敏感词
|
||||
let res = AwesomeHelp.checkSensitiveWord(prompt.toLowerCase(), false, badPromptMap);
|
||||
|
||||
// 输出Map中的所有的key字符串
|
||||
console.log(badPromptMap.keys());
|
||||
// 判断当前数据的位置
|
||||
for (let [key, value] of res.entries()) {
|
||||
let ket_length = key.length;
|
||||
|
||||
for (let j = 0; j < value.length; j++) {
|
||||
const item = value[j];
|
||||
let location = item.location;
|
||||
|
||||
// 判断执行位置之前是不是空格或者是,
|
||||
let start = location - 1;
|
||||
let end = location + ket_length;
|
||||
let start_check = prompt.toLowerCase()[start] == ' ' || prompt.toLowerCase()[start] == ',';
|
||||
let end_check = prompt.toLowerCase()[end] == ' ' || prompt.toLowerCase()[end] == ',';
|
||||
if (start_check && end_check) {
|
||||
// 这个位置是敏感词
|
||||
bad_prompt_ids.push({
|
||||
id: element.id,
|
||||
no: element.no
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
console.log(res);
|
||||
|
||||
|
||||
}
|
||||
|
||||
console.log(bad_prompt_ids)
|
||||
return {
|
||||
code: 1,
|
||||
data: bad_prompt_ids
|
||||
}
|
||||
|
||||
|
||||
} catch (error) {
|
||||
return {
|
||||
code: 0,
|
||||
message: "检查敏感词失败, 错误信息如下:" + error.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+1122
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,434 @@
|
||||
import { app, shell, BrowserWindow, ipcMain, dialog, nativeTheme } 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.js'
|
||||
import { func } from './func.js'
|
||||
import { AsyncQueue } from "./quene.js"
|
||||
import { DEFINE_STRING } from '../define/define_string.js'
|
||||
const fspromises = require("fs").promises;
|
||||
const { v4: uuidv4 } = require('uuid');
|
||||
const { version } = require('../../package.json')
|
||||
|
||||
import { Tools } from './tools.js'
|
||||
import { ImageGenerate } from './backPrompt/imageGenerate.js'
|
||||
import { Setting } from './setting/setting.js'
|
||||
import { has, isEmpty } from 'lodash'
|
||||
|
||||
// ipc
|
||||
import { SettingIpc } from './IPCEvent/settingIpc.js'
|
||||
import { ImageGenerateIpc } from './IPCEvent/imageGenerateIpc.js'
|
||||
import { WritingIpc } from './IPCEvent/writingIpc.js'
|
||||
import { VideoGenerateIpc } from './IPCEvent/videoGenerateIpc'
|
||||
import { TranslateIpc } from './IPCEvent/translateIpc.js'
|
||||
import { GptIpc } from './IPCEvent/gptIpc.js'
|
||||
import { MjIpc } from './IPCEvent/mjIpc.js'
|
||||
import { OriginalImageGenerateIpc } from './IPCEvent/originalImageGenerateIpc'
|
||||
import { SdIpc } from './IPCEvent/sdIpc.js'
|
||||
import { DiscordIpc, RemoveDiscordIpc } from './IPCEvent/discordIpc.js'
|
||||
import { MainIpc } from './IPCEvent/mainIpc.js'
|
||||
|
||||
let tools = new Tools();
|
||||
let imageGenerate = new ImageGenerate(global);
|
||||
let setting = new Setting(global);
|
||||
|
||||
|
||||
async function InitData(gl) {
|
||||
let res = await setting.getSettingDafultData();
|
||||
gl.config = res;
|
||||
gl.requestQuene = new AsyncQueue(gl, res.task_number);
|
||||
gl.fileQueue = new AsyncQueue(gl, 1);
|
||||
return res;
|
||||
}
|
||||
|
||||
function setIpcHandler(hash) {
|
||||
if (hash == "discord") {
|
||||
DiscordIpc(global);
|
||||
}
|
||||
}
|
||||
|
||||
function removeIpcHandler(hash) {
|
||||
if (hash == "discord") {
|
||||
RemoveDiscordIpc();
|
||||
}
|
||||
}
|
||||
|
||||
async function createWindow(hash = "ShowMessage", data, url = null) {
|
||||
// Create the browser window.
|
||||
global.currentHash = hash;
|
||||
|
||||
let mainWindow = new BrowserWindow({
|
||||
width: 900,
|
||||
height: 670,
|
||||
title: 'LAITool',
|
||||
icon: '../../resources/icon.ico',
|
||||
show: false,
|
||||
autoHideMenuBar: true,
|
||||
...(process.platform === 'linux' ? { icon } : {}),
|
||||
webPreferences: {
|
||||
preload: join(__dirname, '../preload/index.js'),
|
||||
sandbox: false,
|
||||
nodeIntegration: (hash == 'discord' ? false : true), // 在网页中集成Node
|
||||
nodeIntegrationInWorker: true,
|
||||
webSecurity: false,
|
||||
partition: "persist:my-partition",
|
||||
}
|
||||
})
|
||||
|
||||
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();
|
||||
} else {
|
||||
if (hash != "") {
|
||||
mainWindow.loadURL(`file://${path.join(__dirname, '../renderer/index.html')}#/${hash}`)
|
||||
} else {
|
||||
mainWindow.loadFile(join(__dirname, '../renderer/index.html'))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mainWindow.on("closed", () => {
|
||||
// 判断指定的窗口,移除指定的监听
|
||||
removeIpcHandler(hash);
|
||||
global.newWindow = global.newWindow.filter(item => item.id != mainWindow.id)
|
||||
})
|
||||
|
||||
// 创建一个新的窗口,添加对应的监听
|
||||
|
||||
global.newWindow.push({
|
||||
hash,
|
||||
id: mainWindow.id,
|
||||
win: mainWindow,
|
||||
init_folder: data
|
||||
})
|
||||
|
||||
setIpcHandler(hash);
|
||||
await InitData(global);
|
||||
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)
|
||||
})
|
||||
|
||||
global.newWindow = [];
|
||||
mainWindow = createWindow('ShowMessage', null)
|
||||
|
||||
//判断是不是又配置文件,没有的话,将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.join(define.dynamic_setting));
|
||||
// 判断标签文件是不是存在
|
||||
tools.checkJsonFileExistsOrCreate(path.join(define.tag_setting));
|
||||
|
||||
app.on('activate', 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('ShowMessage', null)
|
||||
|
||||
})
|
||||
})
|
||||
|
||||
// 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()
|
||||
}
|
||||
})
|
||||
|
||||
SettingIpc();
|
||||
ImageGenerateIpc();
|
||||
WritingIpc();
|
||||
VideoGenerateIpc();
|
||||
TranslateIpc();
|
||||
GptIpc();
|
||||
SdIpc();
|
||||
MjIpc();
|
||||
MainIpc(createWindow);
|
||||
OriginalImageGenerateIpc();
|
||||
|
||||
|
||||
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;
|
||||
})
|
||||
|
||||
// 获取当前版本
|
||||
ipcMain.handle(DEFINE_STRING.GET_VERSION, async (event) => version);
|
||||
|
||||
// 监听保存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_MACHINE_ID, async (event, value) => await func.GetMachineId());
|
||||
|
||||
// 监听获取不想要的提示词任务
|
||||
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.on(DEFINE_STRING.OPEN_FOLDER, (event, value) => shell.openPath(path.join(global.config.project_path, "tmp/" + value)));
|
||||
|
||||
// 监听字幕的保存
|
||||
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_GLOABAL_MESSAGE_DIALOG, (event, value) => {
|
||||
global.newWindow[0].win.webContents.send(DEFINE_STRING.SHOW_MESSAGE_DIALOG, value)
|
||||
})
|
||||
|
||||
/**
|
||||
* 监听一个窗口,返回窗口创建成功后需要的基础数据
|
||||
*/
|
||||
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)
|
||||
})
|
||||
@@ -0,0 +1,299 @@
|
||||
import { DEFINE_STRING } from "../define/define_string";
|
||||
import { Tools } from "./tools";
|
||||
export class AsyncQueue {
|
||||
constructor(global, concurrencyLimit = 1, manualMode = false) {
|
||||
this.global = global;
|
||||
this.tasks = [];
|
||||
this.concurrencyLimit = concurrencyLimit;
|
||||
this.manualMode = manualMode;
|
||||
this.currentConcurrency = 0;
|
||||
// 扩展批次完成状态对象以支持子批次
|
||||
this.batchCompletion = {};
|
||||
this.taskDeadline = global.endTime;
|
||||
this.showEndTime = true;
|
||||
this.tools = new Tools();
|
||||
this.currentCreateItem = null;
|
||||
|
||||
// 只有在手动模式下才会使用。要设置当前正在执行的任务,将正在执行的任务添加到里面,需要在任务完成之后手动移除
|
||||
this.taskProgress = [];
|
||||
}
|
||||
|
||||
async enqueue(task, taskId, batchId, subBatchId = 'default') {
|
||||
if (!this.batchCompletion[batchId]) {
|
||||
this.batchCompletion[batchId] = { remaining: 0, subBatches: {}, callback: null, failedTasks: [] };
|
||||
}
|
||||
if (!this.batchCompletion[batchId].subBatches[subBatchId]) {
|
||||
this.batchCompletion[batchId].subBatches[subBatchId] = { remaining: 0, failedTasks: [] };
|
||||
}
|
||||
|
||||
this.batchCompletion[batchId].remaining++;
|
||||
this.batchCompletion[batchId].subBatches[subBatchId].remaining++;
|
||||
this.tasks.push({ task, taskId, batchId, subBatchId });
|
||||
if (!this.manualMode) {
|
||||
this.process();
|
||||
}
|
||||
}
|
||||
|
||||
setBatchCompletionCallback(batchId, callback) {
|
||||
if (this.batchCompletion[batchId]) {
|
||||
this.batchCompletion[batchId].callback = callback;
|
||||
}
|
||||
}
|
||||
|
||||
setSubBatchCompletionCallback(batchId, subBatchId, callback) {
|
||||
if (this.batchCompletion[batchId]) {
|
||||
this.batchCompletion[batchId].subBatches[subBatchId].callback = callback;
|
||||
}
|
||||
}
|
||||
|
||||
setTaskDeadline(deadline) {
|
||||
this.taskDeadline = deadline;
|
||||
}
|
||||
|
||||
async process() {
|
||||
|
||||
// 判断是不是有机器码检测的标识
|
||||
if (!this.global.CheckMachineId) {
|
||||
// throw new Error("Machine ID not detected, please check the machine ID.");
|
||||
this.global.newWindow[0].win.webContents.send(DEFINE_STRING.SHOW_MESSAGE_DIALOG, { code: 0, message: "请联系管理员激活" });
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.global.endTime && new Date(Date.now()).toISOString() > this.global.endTime) {
|
||||
this.tasks = [];
|
||||
if (this.showEndTime) {
|
||||
this.global.newWindow[0].win.webContents.send(DEFINE_STRING.SHOW_MESSAGE_DIALOG, { code: 0, message: "试用时间已到,请联系客服" });
|
||||
}
|
||||
this.showEndTime = false;
|
||||
return;
|
||||
}
|
||||
|
||||
while (this.tasks.length > 0 && this.currentConcurrency < this.concurrencyLimit) {
|
||||
const { task, taskId, batchId, subBatchId } = this.tasks.shift();
|
||||
this.currentConcurrency++;
|
||||
task().then(() => {
|
||||
this.currentConcurrency--;
|
||||
this.handleTaskCompletion(batchId, subBatchId, null);
|
||||
if (!this.manualMode) {
|
||||
this.process();
|
||||
}
|
||||
}).catch((error) => {
|
||||
// 判断是不是网络请求,不是网络请求直接报错
|
||||
let retryCount = 0;
|
||||
const maxRetryCount = 5;
|
||||
let retryTask = async () => {
|
||||
if (retryCount < maxRetryCount) {
|
||||
retryCount++;
|
||||
await this.tools.delay(2000);
|
||||
this.global.newWindow[0].win.webContents.send(DEFINE_STRING.SHOW_MAIN_NOTIFICATION,
|
||||
{ code: 0, message: `生图失败,共5次尝试,目前第${retryCount}次` });
|
||||
task().then(() => {
|
||||
this.currentConcurrency--;
|
||||
this.handleTaskCompletion(batchId, subBatchId, null);
|
||||
if (!this.manualMode) {
|
||||
this.process();
|
||||
}
|
||||
}).catch((error) => {
|
||||
retryTask();
|
||||
});
|
||||
} else {
|
||||
// console.error(`Task ${taskId} failed after ${maxRetryCount} retries`);
|
||||
this.currentConcurrency--;
|
||||
this.handleTaskCompletion(batchId, subBatchId, { taskId, error });
|
||||
if (!this.manualMode) {
|
||||
this.process();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ([DEFINE_STRING.QUEUE_BATCH.SD_BACKSTEP_GENERATE_IMAGE, DEFINE_STRING.QUEUE_BATCH.SD_ORIGINAL_GENERATE_IMAGE].includes(batchId)) {
|
||||
retryTask();
|
||||
} else {
|
||||
console.error('An error occurred in task ' + taskId + ':', error);
|
||||
// 出现报错。直接停掉当前对应的的子批次
|
||||
this.currentConcurrency--;
|
||||
this.handleTaskCompletion(batchId, subBatchId, { taskId, error });
|
||||
if (!this.manualMode) {
|
||||
this.process();
|
||||
} else {
|
||||
// 将当前的设置删除,将历史人物删除
|
||||
this.setCurrentCreateItem(null);
|
||||
this.taskProgress = [];
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 任务完成执行的函数,用于处理任务完成后的状态更新等
|
||||
handleTaskCompletion(batchId, subBatchId, taskError) {
|
||||
const batch = this.batchCompletion[batchId];
|
||||
const subBatch = this.batchCompletion[batchId].subBatches[subBatchId];
|
||||
|
||||
if (taskError) {
|
||||
batch.failedTasks.push(taskError);
|
||||
subBatch.failedTasks.push(taskError);
|
||||
// 删除整个子批次
|
||||
this.removeTask(batchId, null, subBatchId)
|
||||
}
|
||||
this.batchCompletion[batchId].remaining--;
|
||||
this.batchCompletion[batchId].subBatches[subBatchId].remaining--;
|
||||
|
||||
// 子批次完成
|
||||
if (this.batchCompletion[batchId].subBatches[subBatchId].remaining === 0) {
|
||||
console.log(`Sub-batch ${subBatchId} in batch ${batchId} completed.`);
|
||||
const callback = this.batchCompletion[batchId].subBatches[subBatchId].callback;
|
||||
if (callback) {
|
||||
callback(this.batchCompletion[batchId].subBatches[subBatchId].failedTasks);
|
||||
}
|
||||
this.batchCompletion[batchId].subBatches[subBatchId] = { remaining: 0, callback: null, failedTasks: [] };
|
||||
}
|
||||
|
||||
if (this.batchCompletion[batchId].remaining === 0) {
|
||||
const callback = this.batchCompletion[batchId].callback;
|
||||
if (callback) {
|
||||
callback(this.batchCompletion[batchId].failedTasks);
|
||||
}
|
||||
this.batchCompletion[batchId] = { remaining: 0, subBatches: {}, callback: null, failedTasks: [] };
|
||||
}
|
||||
}
|
||||
|
||||
// 移除任务
|
||||
removeTask(batchId, taskId, subBatchId = 'default') {
|
||||
try {
|
||||
|
||||
if (!this.batchCompletion[batchId]) {
|
||||
throw new Error(`Batch ${batchId} does not exist.`);
|
||||
}
|
||||
|
||||
// 获取当前批次的所有的任务
|
||||
let initialCount = this.tasks.filter(item => item.batchId === batchId).length;
|
||||
let finalCount = initialCount;
|
||||
|
||||
// 首先,判断是否指定了taskId来决定是移除单个任务还是整个批次
|
||||
if (taskId == null) {
|
||||
// 删除整个批次
|
||||
this.tasks = this.tasks.filter(item => item.batchId !== batchId);
|
||||
finalCount = this.tasks.filter(item => item.batchId === batchId).length;
|
||||
} else if (taskId == 'all') {
|
||||
// 删除所有的指定的子批次
|
||||
this.tasks = this.tasks.filter(item => !(item.subBatchId == subBatchId && item.batchId == batchId));
|
||||
finalCount = this.tasks.filter(item => item.subBatchId == subBatchId && item.batchId == batchId).length;
|
||||
} else {
|
||||
// 删除指定的任务,需要考虑subBatchId
|
||||
this.tasks = this.tasks.filter(item => !(item.taskId === taskId && item.batchId === batchId && item.subBatchId === subBatchId));
|
||||
finalCount = this.tasks.filter(item => item.taskId === taskId && item.batchId === batchId && item.subBatchId === subBatchId).length;
|
||||
}
|
||||
// 在移除后再次计算该批次的任务数量
|
||||
// const finalCount = this.tasks.length;
|
||||
// 使用两次计数的差值更新 remaining
|
||||
const removedTasks = initialCount - finalCount;
|
||||
if (removedTasks > 0) {
|
||||
this.batchCompletion[batchId].remaining -= removedTasks;
|
||||
if (this.batchCompletion[batchId].subBatches[subBatchId]) {
|
||||
this.batchCompletion[batchId].subBatches[subBatchId].remaining -= removedTasks;
|
||||
this.batchCompletion[batchId].subBatches[subBatchId].callback = null;
|
||||
}
|
||||
}
|
||||
|
||||
// 检查并处理完整批次和小批次的完成状态
|
||||
const batch = this.batchCompletion[batchId];
|
||||
// 判断子批次的数量
|
||||
const subBatch = this.batchCompletion[batchId].subBatches[subBatchId];
|
||||
// 子批次完成
|
||||
if (subBatch && subBatch.remaining == 0 && subBatch.callback) {
|
||||
subBatch.callback(subBatch.failedTasks)
|
||||
delete this.batchCompletion[batchId].subBatches[subBatchId]; // 清理批次完成状态
|
||||
}
|
||||
// 总批次完成
|
||||
if (batch && batch.remaining === 0 && batch.callback) {
|
||||
const callback = batch.callback;
|
||||
callback(batch.failedTasks);
|
||||
delete this.batchCompletion[batchId]; // 清理批次完成状态
|
||||
} else {
|
||||
// 遍历小批次,看是否有需要清理的
|
||||
for (let subBatchId in batch.subBatches) {
|
||||
const subBatch = batch.subBatches[subBatchId];
|
||||
if (subBatch.remaining === 0) {
|
||||
// 这里可以进行小批次完成后的处理,比如调用小批次的特定回调
|
||||
console.log(`Sub-batch ${subBatchId} in batch ${batchId} completed.`);
|
||||
// 可以选择删除空的小批次状态,如果不需要保留它的完成记录
|
||||
delete batch.subBatches[subBatchId];
|
||||
}
|
||||
}
|
||||
}
|
||||
return {
|
||||
code: 1
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
return {
|
||||
code: 0,
|
||||
message: error.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 获取失败的任务
|
||||
getFailedTasks(batchId, subBatchId = 'default') {
|
||||
if (this.batchCompletion[batchId] && this.batchCompletion[batchId].subBatches[subBatchId]) {
|
||||
return this.batchCompletion[batchId].subBatches[subBatchId].failedTasks;
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
// 手动开启下一个任务
|
||||
async startNextTask() {
|
||||
// 判断当前是不是有任务正在执行
|
||||
if (this.currentCreateItem) {
|
||||
return;
|
||||
}
|
||||
console.log("调用开始下一个任务", this.taskProgress)
|
||||
if (this.manualMode && this.tasks.length > 0 && this.currentConcurrency < this.concurrencyLimit && this.taskProgress.length < 3) {
|
||||
this.process();
|
||||
}
|
||||
}
|
||||
|
||||
// 修改currentItem
|
||||
setCurrentCreateItem(item) {
|
||||
this.currentCreateItem = item;
|
||||
if (item) {
|
||||
// 判断相同的ID的任务是否存在,存在则不添加
|
||||
let index = this.taskProgress.findIndex(task => task.id === item.id);
|
||||
if (index == -1) {
|
||||
this.taskProgress.push(item);
|
||||
} else {
|
||||
// 直接修改
|
||||
this.taskProgress[index] = item;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 获取currentItem
|
||||
getCurrentCreateItem() {
|
||||
return this.currentCreateItem;
|
||||
}
|
||||
|
||||
// 获取当前正在执行的任务数量
|
||||
getCurrentConcurrency() {
|
||||
return this.currentConcurrency;
|
||||
}
|
||||
|
||||
// 获取当前任务的限制任务数量
|
||||
getConcurrencyLimit() {
|
||||
return this.concurrencyLimit
|
||||
}
|
||||
|
||||
// 获取当前正在执行的任务对象()
|
||||
getTaskProgress() {
|
||||
return this.taskProgress;
|
||||
}
|
||||
|
||||
// 删除自动设置的任务
|
||||
removeTaskProgress(callback) {
|
||||
this.taskProgress = callback(this.taskProgress);
|
||||
console.log("删除对应消息的任务", this.taskProgress)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,332 @@
|
||||
const fspromises = require("fs").promises;
|
||||
import path from "path";
|
||||
import axios from "axios";
|
||||
const { JSDOM } = require('jsdom');
|
||||
import { define } from "../../define/define";
|
||||
import { Tools } from "../tools";
|
||||
import { ClipSetting } from "../../define/setting/clipSetting";
|
||||
import { ImageSetting } from "../../define/setting/imageSetting";
|
||||
import { DEFINE_STRING } from "../../define/define_string";
|
||||
import { TagDefine } from "../../define/tagDefine";
|
||||
|
||||
let tagDefine = new TagDefine(global);
|
||||
export class Setting {
|
||||
constructor(global) {
|
||||
this.global = global;
|
||||
this.tools = new Tools();
|
||||
}
|
||||
|
||||
//#region 剪映设置
|
||||
|
||||
/**
|
||||
* 添加背景音乐文件夹设置
|
||||
* @param {*} value
|
||||
*/
|
||||
async AddBackgroundMusicFolder(value) {
|
||||
return await ClipSetting.AddBackgroundMusicFolder(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取剪映背景音乐配置列表
|
||||
*/
|
||||
async GetBackGroundMusicConfigList() {
|
||||
try {
|
||||
let res = await this.tools.getJsonFilePropertyValue(define.clip_setting, "background_music_setting", []);
|
||||
return {
|
||||
code: 1,
|
||||
value: res
|
||||
}
|
||||
} catch (error) {
|
||||
return {
|
||||
code: 0,
|
||||
error: error.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除剪辑配置里面对应ID的数据
|
||||
* @param {要删除的样式ID} value
|
||||
*/
|
||||
async deleteClipSetting(property, value) {
|
||||
return ClipSetting.deleteClipSetting(property, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取剪映的关键帧配置
|
||||
* @returns
|
||||
*/
|
||||
async GetKeyFrameConfigData() {
|
||||
try {
|
||||
let key_frame = await this.tools.getJsonFilePropertyValue(define.clip_setting, "key_frame", null, false);
|
||||
return {
|
||||
code: 1,
|
||||
data: key_frame
|
||||
}
|
||||
} catch (error) {
|
||||
return {
|
||||
code: 0,
|
||||
message: error.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取剪映关键帧配置
|
||||
* @returns
|
||||
*/
|
||||
async GetKeyFrameOptions() {
|
||||
return await ClipSetting.GetKeyFrameOptions();
|
||||
}
|
||||
|
||||
async SaveKeyFrameSetting(value) {
|
||||
return await ClipSetting.SaveKeyFrameSetting(value);
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
|
||||
//#region 图片设置
|
||||
/**
|
||||
* 获取自动保存图片的存储文件的方式
|
||||
* @returns
|
||||
*/
|
||||
async GetAutoSaveImageClassifyOptions() {
|
||||
return await ImageSetting.GetAutoSaveImageClassifyOptions();
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存自动保存图片的设置
|
||||
* @param {*} value
|
||||
* @returns
|
||||
*/
|
||||
async SaveImageAutoSaveSetting(value) {
|
||||
return await ImageSetting.SaveImageAutoSaveSetting(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取自动保存图片的设置
|
||||
* @param {*} value
|
||||
* @returns
|
||||
*/
|
||||
async GetImageAutoSaveSetting(value) {
|
||||
return await ImageSetting.GetImageAutoSaveSetting(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 手动另存文件夹
|
||||
* @returns
|
||||
*/
|
||||
async SaveImageToOtherFolder(value) {
|
||||
return await ImageSetting.SaveImageToOtherFolder([], value);
|
||||
}
|
||||
|
||||
|
||||
//#endregion
|
||||
|
||||
|
||||
/**
|
||||
* 移除任务
|
||||
* @param {*} value
|
||||
*/
|
||||
async RemoveTask(value) {
|
||||
if (value[0] == DEFINE_STRING.QUEUE_BATCH.IMAGE_SAVE_TO_OTHER_FOLDER) {
|
||||
this.global.fileQueue.removeTask(value[0], value[1], value[2])
|
||||
} else {
|
||||
this.global.requestQuene.removeTask(value[0], value[1], value[2])
|
||||
if (this.global.mjGenerateQuene) {
|
||||
this.global.mjGenerateQuene.removeTask(value[0], value[1], value[2])
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 加载SD配置文件
|
||||
*/
|
||||
async InitSDConfig() {
|
||||
try {
|
||||
let sd_config = JSON.parse(await fspromises.readFile(define.sd_setting, 'utf-8'));
|
||||
return {
|
||||
code: 1,
|
||||
data: {
|
||||
webui_api_url: sd_config.setting.webui_api_url,
|
||||
type: sd_config.setting.type,
|
||||
sampler_name: sd_config.webui.sampler_name,
|
||||
prompt: sd_config.webui.prompt,
|
||||
negative_prompt: sd_config.webui.negative_prompt,
|
||||
denoising_strength: sd_config.webui.denoising_strength,
|
||||
steps: sd_config.webui.steps,
|
||||
width: sd_config.webui.width,
|
||||
height: sd_config.webui.height,
|
||||
adetailer: sd_config.webui.adetailer,
|
||||
batch_size: sd_config.setting.batch_size,
|
||||
seed: sd_config.setting.seed,
|
||||
style_weight: sd_config.setting.style_weight,
|
||||
cfg_scale: sd_config.webui.cfg_scale,
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
return {
|
||||
code: 0,
|
||||
message: error
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取主页显示的信息
|
||||
*/
|
||||
async GetShowMessage() {
|
||||
try {
|
||||
let res = await axios.get('https://share.weiyun.com/1EPtoGg8');
|
||||
const dom = new JSDOM(res.data, {
|
||||
runScripts: "dangerously",
|
||||
resources: "usable",
|
||||
});
|
||||
|
||||
// 创建一个函数来异步获取syncData
|
||||
async function getSyncData() {
|
||||
// 创建一个Promise来等待onload事件
|
||||
await new Promise((resolve, reject) => {
|
||||
dom.window.onload = () => {
|
||||
resolve();
|
||||
};
|
||||
|
||||
// 设置一个超时,以防onload事件永远不触发
|
||||
setTimeout(() => {
|
||||
reject(new Error('Loading timed out'));
|
||||
}, 5000); // 10秒超时
|
||||
});
|
||||
|
||||
// 返回syncData对象
|
||||
return dom.window.syncData;
|
||||
}
|
||||
|
||||
// 使用异步函数并处理结果
|
||||
let re = await getSyncData();
|
||||
return {
|
||||
code: 1,
|
||||
data: re
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
return {
|
||||
code: 0,
|
||||
message: error.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化配置
|
||||
*/
|
||||
async getSettingDafultData() {
|
||||
// 加载通用配置
|
||||
let data = await fspromises.readFile(define.config_path, "utf-8");
|
||||
let sd_data = await fspromises.readFile(define.sd_setting, 'utf-8');
|
||||
let config_json_date = JSON.parse(data);
|
||||
config_json_date.webui_api_url = JSON.parse(sd_data).setting.webui_api_url;
|
||||
return config_json_date;
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改配置文件中的剪映的草稿位置
|
||||
* @param {剪映的草稿位置} value
|
||||
* @returns
|
||||
*/
|
||||
async ModifySampleSetting(value) {
|
||||
try {
|
||||
value = JSON.parse(value);
|
||||
// 当前的配置文件的内容就是global.config的内容
|
||||
// 直接修改 global.config的内容即可
|
||||
// 传入的value是一个对象,需要遍历其中的所有属性,并将属性值进行修改
|
||||
for (let key in value) {
|
||||
this.global.config[key] = value[key];
|
||||
}
|
||||
await fspromises.writeFile(define.config_path, JSON.stringify(this.global.config));
|
||||
return {
|
||||
code: 1,
|
||||
message: "保存成功"
|
||||
}
|
||||
} catch (error) {
|
||||
return {
|
||||
cdoe: "0",
|
||||
message: "保存失败,错误信息如下:" + '\n' + error.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查机器码是不是存在
|
||||
* @param {*} value
|
||||
* @returns
|
||||
*/
|
||||
async CheckMachineId(value) {
|
||||
try {
|
||||
// 判断机器码是不是存在
|
||||
let res = await axios.post('http://api.yu-zhile.com/GetMachineStatus', {
|
||||
machineId: value
|
||||
})
|
||||
if (res.status != 200) {
|
||||
throw new Error("请求错误");
|
||||
}
|
||||
if (res.data.code == 0) {
|
||||
throw new Error(res.data.message);
|
||||
}
|
||||
|
||||
this.global.endTime = res.data.endTime;
|
||||
this.global.permissions = res.data.permissions;
|
||||
this.global.CheckMachineId = true;
|
||||
return {
|
||||
code: 1,
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
return {
|
||||
code: 0,
|
||||
message: error.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取选择角色场景模式的options
|
||||
* @returns
|
||||
*/
|
||||
async GetRoleSceneModeOptions() {
|
||||
return tagDefine.getTagSelectModel();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取生图的类别(sd,mj,d3)
|
||||
* @returns
|
||||
*/
|
||||
async GetImageGenerateCategory() {
|
||||
return ImageSetting.GetImageGenerateCategory();
|
||||
}
|
||||
|
||||
//#region SD设置
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region MJ设置
|
||||
|
||||
/**
|
||||
* 获取指定的配置文件里面指定的属性的数据
|
||||
* @param {*} value
|
||||
*/
|
||||
async GetDefineConfigJsonByProperty(value) {
|
||||
return ImageSetting.GetDefineConfigJsonByProperty(value);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 保存指定的配置文件里面指定的属性的数据
|
||||
*/
|
||||
async SaveDefineConfigJsonByProperty(value) {
|
||||
return ImageSetting.SaveDefineConfigJsonByProperty(value);
|
||||
}
|
||||
|
||||
|
||||
//#endregion
|
||||
}
|
||||
@@ -0,0 +1,326 @@
|
||||
const fspromises = require("fs").promises;
|
||||
const { net } = require('electron');
|
||||
import path from "path";
|
||||
const util = require('util');
|
||||
const { spawn, exec } = require('child_process');
|
||||
const execAsync = util.promisify(exec);
|
||||
import { define } from "../define/define";
|
||||
import { get, has, set } from "lodash";
|
||||
import axios from "axios";
|
||||
|
||||
export class Tools {
|
||||
constructor() { }
|
||||
|
||||
/**
|
||||
* 判断判断文件夹或者是文件是不是存在
|
||||
* @param {文件或者是文件夹的路径} filePath
|
||||
* @returns
|
||||
*/
|
||||
async checkExists(filePath) {
|
||||
try {
|
||||
await fspromises.access(filePath);
|
||||
return true; // 文件或目录存在
|
||||
} catch (error) {
|
||||
return false; // 文件或目录不存在
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 判断json文件是不是存在,不存在的话,协议一个空的json文件
|
||||
* @param {*} filePath 文件地址
|
||||
*/
|
||||
async checkJsonFileExistsOrCreate(filePath) {
|
||||
try {
|
||||
if (!(await this.checkExists(filePath))) {
|
||||
// 判断传入的json文件的父文件夹是不是存在,不存在的话,创建
|
||||
let parentFolder = path.dirname(filePath);
|
||||
if (!(await this.checkExists(parentFolder))) {
|
||||
// 创建文件夹
|
||||
await fspromises.mkdir(parentFolder, { recursive: true });
|
||||
}
|
||||
await fspromises.writeFile(filePath, "{}");
|
||||
}
|
||||
} catch (error) {
|
||||
throw new Error(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除目标图片,然后将原图片的exif信息删除,然后将原图片复制到目标图片地址
|
||||
* @param {*} source 原图片地址
|
||||
* @param {*} target 目标图片地址
|
||||
*/
|
||||
async deletePngAndDeleteExifData(source, target) {
|
||||
try {
|
||||
let exiftool = path.join(define.package_path, 'exittool/exiftool.exe');
|
||||
await this.deleteFileOrDirectory(target);
|
||||
let script = `"${exiftool}" -all= -overwrite_original "${source}" -o "${target}"`;
|
||||
const output = await execAsync(script, { maxBuffer: 1024 * 1024 * 10, encoding: 'utf-8' });
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 复制文件或者是文件夹到指定的位置
|
||||
* @param {*} src 源文件或者是文件夹
|
||||
* @param {*} dest 目标文件或者是文件夹
|
||||
*/
|
||||
async copyFileOrDirectory(src, dest) {
|
||||
try {
|
||||
let stats = await fspromises.stat(src);
|
||||
if (stats.isDirectory()) {
|
||||
await fspromises.mkdir(dest, { recursive: true });
|
||||
let items = await fspromises.readdir(src);
|
||||
let promises = items.map(item => this.copyFileOrDirectory(path.join(src, item), path.join(dest, item)));
|
||||
await Promise.all(promises);
|
||||
} else if (stats.isFile()) {
|
||||
await fspromises.copyFile(src, dest);
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
throw new Error(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 延时多少秒
|
||||
* @param {等待时间} time
|
||||
* @returns
|
||||
*/
|
||||
async delay(time) {
|
||||
return new Promise(resolve => setTimeout(resolve, time));
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断指定路径的文件夹是不是存在,如果不存在则创建
|
||||
*/
|
||||
async checkFolderExistsOrCreate(folderPath) {
|
||||
try {
|
||||
if (!(await this.checkExists(folderPath))) {
|
||||
await fspromises.mkdir(folderPath, { recursive: true });
|
||||
}
|
||||
} catch (error) {
|
||||
throw new Error(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定路径下面的指定的拓展的文件
|
||||
* @param {指定的文件夹路径} folderPath
|
||||
* @param {后缀名} extensions
|
||||
* @returns
|
||||
*/
|
||||
async getFilesWithExtensions(folderPath, extensions) {
|
||||
try {
|
||||
let entries = await fspromises.readdir(folderPath, { withFileTypes: true });
|
||||
let files = [];
|
||||
// 使用Promise.all来并行处理所有的stat调用
|
||||
const fileStats = await Promise.all(entries.map(async (entry) => {
|
||||
const entryPath = path.join(folderPath, entry.name);
|
||||
if (entry.isFile()) {
|
||||
return {
|
||||
name: entry.name,
|
||||
path: entryPath,
|
||||
isFile: true,
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
isFile: false,
|
||||
};
|
||||
}
|
||||
}));
|
||||
|
||||
// 过滤出文件并且满足扩展名要求的文件
|
||||
files = fileStats.filter(fileStat => fileStat.isFile && extensions.includes(path.extname(fileStat.name).toLowerCase()));
|
||||
|
||||
// 对files数组进行排序,基于文件名
|
||||
files.sort((a, b) => a.name.localeCompare(b.name));
|
||||
|
||||
// 返回文件名数组
|
||||
return files.map(fileStat => path.join(folderPath, fileStat.name));
|
||||
} catch (error) {
|
||||
throw new Error(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 去除字符串中的所有标点符号,包括英文和中文的标点,以及省略号。
|
||||
*
|
||||
* @param {string} sentence 待处理的字符串。
|
||||
* @return {string} 去除标点后的字符串。
|
||||
*/
|
||||
removePunctuationIncludingEllipsis(sentence) {
|
||||
// 扩展正则表达式以包含中文标点符号和省略号
|
||||
// 注意英文省略号可能由三个连续点表示,也可能直接使用特殊的省略号字符
|
||||
const punctuationRegExp = /[., \/#!$%\^&\*;:{}=\-_`~()\[\],。、;:?!‘’“”()【】《》…]+/g;
|
||||
|
||||
// 使用正则表达式的replace方法替换掉所有匹配到的标点符号为空字符串
|
||||
return sentence.replace(punctuationRegExp, '');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 将一个数组写到一个txt文件中
|
||||
* @param {数据数组} dataArray
|
||||
* @param {txt文件地址} filePath
|
||||
*/
|
||||
async writeArrayToFile(dataArray, filePath) {
|
||||
try {
|
||||
// 将数组转换为字符串,每个元素后面加上换行符
|
||||
const dataString = dataArray.join('\n');
|
||||
// 使用fs.writeFile异步写入文件
|
||||
await fspromises.writeFile(filePath, dataString);
|
||||
return dataString
|
||||
} catch (error) {
|
||||
throw new Error(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除指定的文件夹和文件地址(删除文件夹会删除文件夹里面的所有的文件)
|
||||
* @param {文件或者文件夹的地址} dir
|
||||
*/
|
||||
async deleteFileOrDirectory(dir) {
|
||||
let isExist = await this.checkExists(dir);
|
||||
if (isExist) {
|
||||
let stats = await fspromises.stat(dir);
|
||||
if (stats.isDirectory()) {
|
||||
let items = await fspromises.readdir(dir);
|
||||
let promises = items.map(item => this.deleteFileOrDirectory(path.join(dir, item)));
|
||||
await Promise.all(promises);
|
||||
await fspromises.rmdir(dir);
|
||||
} else if (stats.isFile()) {
|
||||
await fspromises.unlink(dir);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定的json文件,并获取指定的属性的值(传入的属性值为null,返回全部。)
|
||||
* 传入的property不为null,返回全部的json文件,检查是不是要判断传入的属性是不是存在。
|
||||
* 要检查属性是不是存在,传入的checkProperty为true,不检查传入的checkProperty为false
|
||||
* checkProperty为true。若值不存在会抛出错误。为false。不存在直接返回null
|
||||
* @param {*} filePath json文件地址
|
||||
* @param {*} property 属性名称
|
||||
* @param {*} defaultValue 默认值(默认为null)
|
||||
* @param {*} checkProperty 是否检查属性是不是存在(默认为true,若为false,不存在直接返回设置的默认值)
|
||||
* @returns
|
||||
*/
|
||||
async getJsonFilePropertyValue(filePath, property, defaultValue = null, checkProperty = true) {
|
||||
try {
|
||||
let isExist = await this.checkExists(filePath);
|
||||
if (!isExist) {
|
||||
throw new Error("文件不存在,请先添加");
|
||||
}
|
||||
let config = JSON.parse(await fspromises.readFile(filePath, 'utf-8'));
|
||||
let d = null;
|
||||
if (property) {
|
||||
if (checkProperty) {
|
||||
if (!has(config, property)) {
|
||||
throw new Error(`${property} 属性不存在,请检查是不是少了什么步骤`);
|
||||
}
|
||||
}
|
||||
d = get(config, property, defaultValue)
|
||||
|
||||
} else {
|
||||
d = config;
|
||||
}
|
||||
return d;
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过地址获取指定地址的json文件,然后写入指定的属性的值(判断是不是要检查属性是不是存在)
|
||||
* @param {*} filePath 要写入的文件地址
|
||||
* @param {*} property 写入的属性名
|
||||
* @param {*} value 写入的属性的值
|
||||
* @param {*} checkProperty 是否检查属性是不是存在(默认为false,不检查直接写入-覆盖或新增。若需要检查。值不存在,会抛出错误)
|
||||
*/
|
||||
async writeJsonFilePropertyValue(filePath, property, value, checkProperty = false) {
|
||||
try {
|
||||
let isExist = await this.checkExists(filePath);
|
||||
if (!isExist) {
|
||||
throw new Error("文件不存在,请先添加");
|
||||
}
|
||||
let config = JSON.parse(await fspromises.readFile(filePath, 'utf-8'));
|
||||
if (checkProperty) {
|
||||
if (!has(config, property)) {
|
||||
throw new Error(`${property} 属性不存在,添加失败`);
|
||||
}
|
||||
}
|
||||
set(config, property, value);
|
||||
await fspromises.writeFile(filePath, JSON.stringify(config));
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定文件夹下面特定条件的文件夹
|
||||
* @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;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用chromium下载文件
|
||||
* @param {*} url
|
||||
* @param {*} filePath
|
||||
* @returns
|
||||
*/
|
||||
async downloadFileUrl(url, filePath) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const request = net.request({
|
||||
method: 'GET',
|
||||
url: url
|
||||
});
|
||||
|
||||
request.on('response', (response) => {
|
||||
const chunks = [];
|
||||
response.on('data', (chunk) => chunks.push(chunk));
|
||||
response.on('end', async () => {
|
||||
try {
|
||||
await fspromises.writeFile(filePath, Buffer.concat(chunks));
|
||||
console.log('File downloaded successfully');
|
||||
resolve();
|
||||
} catch (err) {
|
||||
reject(err);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
request.on('error', (error) => {
|
||||
reject(error);
|
||||
});
|
||||
|
||||
request.end();
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user