Initial commit 添加MJ功能

This commit is contained in:
2024-05-15 12:57:15 +08:00
commit 74009113fa
602 changed files with 43708 additions and 0 deletions
+108
View File
@@ -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
}
+45
View File
@@ -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
}
+47
View File
@@ -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
}
+35
View File
@@ -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
}
+78
View File
@@ -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
}
+18
View File
@@ -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
}
+72
View File
@@ -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
}
+18
View File
@@ -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
}
+27
View File
@@ -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
}
+28
View File
@@ -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
}
}
}
}
+355
View File
@@ -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()
}
}
}
}
+33
View File
@@ -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);
}
}
+448
View File
@@ -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()
}
}
}
}
+274
View File
@@ -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
+850
View File
@@ -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}`
}
}
}
}
+335
View File
@@ -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;
}
}
}
+493
View File
@@ -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()
}
}
}
}
+556
View File
@@ -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()
}
}
}
/**
* 保存合成视频的基础信息(Srtaudiobackground
* @param {*} value 保存合成视频的基础信息(Srtaudiobackground
*/
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,
}
}
}
+301
View File
@@ -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()
}
}
}
}
+490
View File
@@ -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);
});
}
}
+52
View File
@@ -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 });
});
}
}
+406
View File
@@ -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();
}
}
+780
View File
@@ -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()
})
}
}
}
+85
View File
@@ -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;
}
}
}
+199
View File
@@ -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
View File
File diff suppressed because it is too large Load Diff
+434
View File
@@ -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)
})
+299
View File
@@ -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)
}
}
+332
View File
@@ -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
}
+326
View File
@@ -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();
});
}
}