mirror of
https://github.com/upscayl/upscayl.git
synced 2025-02-17 19:19:23 +01:00
Merge pull request #464 from upscayl/nayam/refactor-codebase
Refactor Upscayl Codebase
This commit is contained in:
commit
c952a330cc
25
electron/commands/auto-update.ts
Normal file
25
electron/commands/auto-update.ts
Normal file
@ -0,0 +1,25 @@
|
||||
import { MessageBoxOptions, dialog } from "electron";
|
||||
import { autoUpdater } from "electron-updater";
|
||||
import logit from "../utils/logit";
|
||||
|
||||
const autoUpdate = (event) => {
|
||||
autoUpdater.autoInstallOnAppQuit = false;
|
||||
const dialogOpts: MessageBoxOptions = {
|
||||
type: "info",
|
||||
buttons: ["Install update", "No Thanks"],
|
||||
title: "New Upscayl Update",
|
||||
message: event.releaseName as string,
|
||||
detail:
|
||||
"A new version has been downloaded. Restart the application to apply the updates.",
|
||||
};
|
||||
logit("✅ Update Downloaded");
|
||||
dialog.showMessageBox(dialogOpts).then((returnValue) => {
|
||||
if (returnValue.response === 0) {
|
||||
autoUpdater.quitAndInstall();
|
||||
} else {
|
||||
logit("🚫 Update Installation Cancelled");
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export default autoUpdate;
|
156
electron/commands/batch-upscayl.ts
Normal file
156
electron/commands/batch-upscayl.ts
Normal file
@ -0,0 +1,156 @@
|
||||
import fs from "fs";
|
||||
import { getMainWindow } from "../main-window";
|
||||
import {
|
||||
childProcesses,
|
||||
customModelsFolderPath,
|
||||
outputFolderPath,
|
||||
saveOutputFolder,
|
||||
setStopped,
|
||||
stopped,
|
||||
} from "../utils/config-variables";
|
||||
import logit from "../utils/logit";
|
||||
import { spawnUpscayl } from "../utils/spawn-upscayl";
|
||||
import { getBatchArguments } from "../utils/get-arguments";
|
||||
import slash from "../utils/slash";
|
||||
import { modelsPath } from "../utils/get-resource-paths";
|
||||
import COMMAND from "../constants/commands";
|
||||
import convertAndScale from "../utils/convert-and-scale";
|
||||
import DEFAULT_MODELS from "../constants/models";
|
||||
|
||||
const batchUpscayl = async (event, payload) => {
|
||||
const mainWindow = getMainWindow();
|
||||
if (!mainWindow) return;
|
||||
// GET THE MODEL
|
||||
const model = payload.model;
|
||||
const gpuId = payload.gpuId;
|
||||
const saveImageAs = payload.saveImageAs;
|
||||
// const scale = payload.scale as string;
|
||||
|
||||
// GET THE IMAGE DIRECTORY
|
||||
let inputDir = payload.batchFolderPath;
|
||||
// GET THE OUTPUT DIRECTORY
|
||||
let outputDir = payload.outputPath;
|
||||
|
||||
if (saveOutputFolder === true && outputFolderPath) {
|
||||
outputDir = outputFolderPath;
|
||||
}
|
||||
|
||||
const isDefaultModel = DEFAULT_MODELS.includes(model);
|
||||
|
||||
let scale = "4";
|
||||
if (model.includes("x2")) {
|
||||
scale = "2";
|
||||
} else if (model.includes("x3")) {
|
||||
scale = "3";
|
||||
} else {
|
||||
scale = "4";
|
||||
}
|
||||
|
||||
outputDir += `_${model}_x${payload.scale}`;
|
||||
if (!fs.existsSync(outputDir)) {
|
||||
fs.mkdirSync(outputDir, { recursive: true });
|
||||
}
|
||||
|
||||
// Delete .DS_Store files
|
||||
fs.readdirSync(inputDir).forEach((file) => {
|
||||
if (file === ".DS_Store") {
|
||||
logit("🗑️ Deleting .DS_Store file");
|
||||
fs.unlinkSync(inputDir + slash + file);
|
||||
}
|
||||
});
|
||||
|
||||
// UPSCALE
|
||||
const upscayl = spawnUpscayl(
|
||||
"realesrgan",
|
||||
getBatchArguments(
|
||||
inputDir,
|
||||
outputDir,
|
||||
isDefaultModel ? modelsPath : customModelsFolderPath ?? modelsPath,
|
||||
model,
|
||||
gpuId,
|
||||
"png",
|
||||
scale
|
||||
),
|
||||
logit
|
||||
);
|
||||
|
||||
childProcesses.push(upscayl);
|
||||
|
||||
setStopped(false);
|
||||
let failed = false;
|
||||
|
||||
const onData = (data: any) => {
|
||||
if (!mainWindow) return;
|
||||
data = data.toString();
|
||||
mainWindow.webContents.send(
|
||||
COMMAND.FOLDER_UPSCAYL_PROGRESS,
|
||||
data.toString()
|
||||
);
|
||||
if (data.includes("invalid") || data.includes("failed")) {
|
||||
logit("❌ INVALID GPU OR INVALID FILES IN FOLDER - FAILED");
|
||||
failed = true;
|
||||
upscayl.kill();
|
||||
}
|
||||
};
|
||||
const onError = (data: any) => {
|
||||
if (!mainWindow) return;
|
||||
mainWindow.setProgressBar(-1);
|
||||
mainWindow.webContents.send(
|
||||
COMMAND.FOLDER_UPSCAYL_PROGRESS,
|
||||
data.toString()
|
||||
);
|
||||
failed = true;
|
||||
upscayl.kill();
|
||||
mainWindow &&
|
||||
mainWindow.webContents.send(
|
||||
COMMAND.UPSCAYL_ERROR,
|
||||
"Error upscaling image. Error: " + data
|
||||
);
|
||||
return;
|
||||
};
|
||||
const onClose = () => {
|
||||
if (!mainWindow) return;
|
||||
if (!failed && !stopped) {
|
||||
logit("💯 Done upscaling");
|
||||
logit("♻ Scaling and converting now...");
|
||||
upscayl.kill();
|
||||
mainWindow && mainWindow.webContents.send(COMMAND.SCALING_AND_CONVERTING);
|
||||
// Get number of files in output folder
|
||||
const files = fs.readdirSync(inputDir);
|
||||
try {
|
||||
files.forEach(async (file) => {
|
||||
console.log("Filename: ", file.slice(0, -3));
|
||||
await convertAndScale(
|
||||
inputDir + slash + file,
|
||||
outputDir + slash + file.slice(0, -3) + "png",
|
||||
outputDir + slash + file.slice(0, -3) + saveImageAs,
|
||||
payload.scale,
|
||||
saveImageAs,
|
||||
onError
|
||||
);
|
||||
// Remove the png file (default) if the saveImageAs is not png
|
||||
if (saveImageAs !== "png") {
|
||||
fs.unlinkSync(outputDir + slash + file.slice(0, -3) + "png");
|
||||
}
|
||||
});
|
||||
mainWindow.webContents.send(COMMAND.FOLDER_UPSCAYL_DONE, outputDir);
|
||||
} catch (error) {
|
||||
logit("❌ Error processing (scaling and converting) the image.", error);
|
||||
upscayl.kill();
|
||||
mainWindow &&
|
||||
mainWindow.webContents.send(
|
||||
COMMAND.UPSCAYL_ERROR,
|
||||
"Error processing (scaling and converting) the image. Please report this error on Upscayl GitHub Issues page."
|
||||
);
|
||||
}
|
||||
} else {
|
||||
upscayl.kill();
|
||||
}
|
||||
};
|
||||
|
||||
upscayl.process.stderr.on("data", onData);
|
||||
upscayl.process.on("error", onError);
|
||||
upscayl.process.on("close", onClose);
|
||||
};
|
||||
|
||||
export default batchUpscayl;
|
53
electron/commands/custom-models-select.ts
Normal file
53
electron/commands/custom-models-select.ts
Normal file
@ -0,0 +1,53 @@
|
||||
import { MessageBoxOptions, dialog } from "electron";
|
||||
import {
|
||||
customModelsFolderPath,
|
||||
setCustomModelsFolderPath,
|
||||
} from "../utils/config-variables";
|
||||
import logit from "../utils/logit";
|
||||
import slash from "../utils/slash";
|
||||
import COMMAND from "../constants/commands";
|
||||
import getModels from "../utils/get-models";
|
||||
import { getMainWindow } from "../main-window";
|
||||
|
||||
const customModelsSelect = async (event, message) => {
|
||||
const mainWindow = getMainWindow();
|
||||
|
||||
if (!mainWindow) return;
|
||||
const { canceled, filePaths: folderPaths } = await dialog.showOpenDialog({
|
||||
properties: ["openDirectory"],
|
||||
title: "Select Custom Models Folder",
|
||||
defaultPath: customModelsFolderPath,
|
||||
});
|
||||
if (canceled) {
|
||||
logit("🚫 Select Custom Models Folder Operation Cancelled");
|
||||
return null;
|
||||
} else {
|
||||
setCustomModelsFolderPath(folderPaths[0]);
|
||||
|
||||
if (
|
||||
!folderPaths[0].endsWith(slash + "models") &&
|
||||
!folderPaths[0].endsWith(slash + "models" + slash)
|
||||
) {
|
||||
logit("❌ Invalid Custom Models Folder Detected: Not a 'models' folder");
|
||||
const options: MessageBoxOptions = {
|
||||
type: "error",
|
||||
title: "Invalid Folder",
|
||||
message:
|
||||
"Please make sure that the folder name is 'models' and nothing else.",
|
||||
buttons: ["OK"],
|
||||
};
|
||||
dialog.showMessageBoxSync(options);
|
||||
return null;
|
||||
}
|
||||
|
||||
mainWindow.webContents.send(
|
||||
COMMAND.CUSTOM_MODEL_FILES_LIST,
|
||||
getModels(customModelsFolderPath)
|
||||
);
|
||||
|
||||
logit("📁 Custom Folder Path: ", customModelsFolderPath);
|
||||
return customModelsFolderPath;
|
||||
}
|
||||
};
|
||||
|
||||
export default customModelsSelect;
|
215
electron/commands/double-upscayl.ts
Normal file
215
electron/commands/double-upscayl.ts
Normal file
@ -0,0 +1,215 @@
|
||||
import path, { parse } from "path";
|
||||
import { getMainWindow } from "../main-window";
|
||||
import {
|
||||
childProcesses,
|
||||
customModelsFolderPath,
|
||||
outputFolderPath,
|
||||
saveOutputFolder,
|
||||
setStopped,
|
||||
stopped,
|
||||
} from "../utils/config-variables";
|
||||
import DEFAULT_MODELS from "../constants/models";
|
||||
import slash from "../utils/slash";
|
||||
import { spawnUpscayl } from "../utils/spawn-upscayl";
|
||||
import {
|
||||
getDoubleUpscaleArguments,
|
||||
getDoubleUpscaleSecondPassArguments,
|
||||
} from "../utils/get-arguments";
|
||||
import { modelsPath } from "../utils/get-resource-paths";
|
||||
import logit from "../utils/logit";
|
||||
import COMMAND from "../constants/commands";
|
||||
import convertAndScale from "../utils/convert-and-scale";
|
||||
|
||||
const doubleUpscayl = async (event, payload) => {
|
||||
const mainWindow = getMainWindow();
|
||||
if (!mainWindow) return;
|
||||
|
||||
const model = payload.model as string;
|
||||
const imagePath = payload.imagePath;
|
||||
let inputDir = (imagePath.match(/(.*)[\/\\]/) || [""])[1];
|
||||
let outputDir = path.normalize(payload.outputPath);
|
||||
|
||||
if (saveOutputFolder === true && outputFolderPath) {
|
||||
outputDir = outputFolderPath;
|
||||
}
|
||||
const gpuId = payload.gpuId as string;
|
||||
const saveImageAs = payload.saveImageAs as string;
|
||||
|
||||
const isDefaultModel = DEFAULT_MODELS.includes(model);
|
||||
|
||||
// COPY IMAGE TO TMP FOLDER
|
||||
|
||||
const fullfileName = imagePath.split(slash).slice(-1)[0] as string;
|
||||
const fileName = parse(fullfileName).name;
|
||||
|
||||
let scale = "4";
|
||||
if (model.includes("x2")) {
|
||||
scale = "2";
|
||||
} else if (model.includes("x3")) {
|
||||
scale = "3";
|
||||
} else {
|
||||
scale = "4";
|
||||
}
|
||||
|
||||
const outFile =
|
||||
outputDir +
|
||||
slash +
|
||||
fileName +
|
||||
"_upscayl_" +
|
||||
parseInt(payload.scale) * parseInt(payload.scale) +
|
||||
"x_" +
|
||||
model +
|
||||
"." +
|
||||
saveImageAs;
|
||||
|
||||
// UPSCALE
|
||||
let upscayl = spawnUpscayl(
|
||||
"realesrgan",
|
||||
getDoubleUpscaleArguments(
|
||||
inputDir,
|
||||
fullfileName,
|
||||
outFile,
|
||||
isDefaultModel ? modelsPath : customModelsFolderPath ?? modelsPath,
|
||||
model,
|
||||
gpuId,
|
||||
saveImageAs,
|
||||
scale
|
||||
),
|
||||
logit
|
||||
);
|
||||
|
||||
childProcesses.push(upscayl);
|
||||
|
||||
setStopped(false);
|
||||
let failed = false;
|
||||
let isAlpha = false;
|
||||
let failed2 = false;
|
||||
|
||||
const onData = (data) => {
|
||||
if (!mainWindow) return;
|
||||
// CONVERT DATA TO STRING
|
||||
data = data.toString();
|
||||
// SEND UPSCAYL PROGRESS TO RENDERER
|
||||
mainWindow.webContents.send(COMMAND.DOUBLE_UPSCAYL_PROGRESS, data);
|
||||
// IF PROGRESS HAS ERROR, UPSCAYL FAILED
|
||||
if (data.includes("invalid gpu") || data.includes("failed")) {
|
||||
upscayl.kill();
|
||||
failed = true;
|
||||
}
|
||||
if (data.includes("has alpha channel")) {
|
||||
isAlpha = true;
|
||||
}
|
||||
};
|
||||
|
||||
const onError = (data) => {
|
||||
if (!mainWindow) return;
|
||||
mainWindow.setProgressBar(-1);
|
||||
data.toString();
|
||||
// SEND UPSCAYL PROGRESS TO RENDERER
|
||||
mainWindow.webContents.send(COMMAND.DOUBLE_UPSCAYL_PROGRESS, data);
|
||||
// SET FAILED TO TRUE
|
||||
failed = true;
|
||||
mainWindow &&
|
||||
mainWindow.webContents.send(
|
||||
COMMAND.UPSCAYL_ERROR,
|
||||
"Error upscaling image. Error: " + data
|
||||
);
|
||||
upscayl.kill();
|
||||
return;
|
||||
};
|
||||
|
||||
const onClose2 = async (code) => {
|
||||
if (!mainWindow) return;
|
||||
if (!failed2 && !stopped) {
|
||||
logit("💯 Done upscaling");
|
||||
logit("♻ Scaling and converting now...");
|
||||
mainWindow.webContents.send(COMMAND.SCALING_AND_CONVERTING);
|
||||
try {
|
||||
await convertAndScale(
|
||||
inputDir + slash + fullfileName,
|
||||
isAlpha ? outFile + ".png" : outFile,
|
||||
outFile,
|
||||
payload.scale,
|
||||
saveImageAs,
|
||||
onError
|
||||
);
|
||||
mainWindow.setProgressBar(-1);
|
||||
mainWindow.webContents.send(
|
||||
COMMAND.DOUBLE_UPSCAYL_DONE,
|
||||
isAlpha
|
||||
? (outFile + ".png").replace(
|
||||
/([^/\\]+)$/i,
|
||||
encodeURIComponent((outFile + ".png").match(/[^/\\]+$/i)![0])
|
||||
)
|
||||
: outFile.replace(
|
||||
/([^/\\]+)$/i,
|
||||
encodeURIComponent(outFile.match(/[^/\\]+$/i)![0])
|
||||
)
|
||||
);
|
||||
} catch (error) {
|
||||
logit("❌ Error reading original image metadata", error);
|
||||
mainWindow &&
|
||||
mainWindow.webContents.send(
|
||||
COMMAND.UPSCAYL_ERROR,
|
||||
"Error processing (scaling and converting) the image. Please report this error on Upscayl GitHub Issues page."
|
||||
);
|
||||
upscayl.kill();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
upscayl.process.stderr.on("data", onData);
|
||||
upscayl.process.on("error", onError);
|
||||
upscayl.process.on("close", (code) => {
|
||||
// IF NOT FAILED
|
||||
if (!failed && !stopped) {
|
||||
// UPSCALE
|
||||
let upscayl2 = spawnUpscayl(
|
||||
"realesrgan",
|
||||
getDoubleUpscaleSecondPassArguments(
|
||||
isAlpha,
|
||||
outFile,
|
||||
isDefaultModel ? modelsPath : customModelsFolderPath ?? modelsPath,
|
||||
model,
|
||||
gpuId,
|
||||
saveImageAs,
|
||||
scale
|
||||
),
|
||||
logit
|
||||
);
|
||||
|
||||
childProcesses.push(upscayl2);
|
||||
|
||||
upscayl2.process.stderr.on("data", (data) => {
|
||||
if (!mainWindow) return;
|
||||
// CONVERT DATA TO STRING
|
||||
data = data.toString();
|
||||
// SEND UPSCAYL PROGRESS TO RENDERER
|
||||
mainWindow.webContents.send(COMMAND.DOUBLE_UPSCAYL_PROGRESS, data);
|
||||
// IF PROGRESS HAS ERROR, UPSCAYL FAILED
|
||||
if (data.includes("invalid gpu") || data.includes("failed")) {
|
||||
upscayl2.kill();
|
||||
failed2 = true;
|
||||
}
|
||||
});
|
||||
upscayl2.process.on("error", (data) => {
|
||||
if (!mainWindow) return;
|
||||
data.toString();
|
||||
// SEND UPSCAYL PROGRESS TO RENDERER
|
||||
mainWindow.webContents.send(COMMAND.DOUBLE_UPSCAYL_PROGRESS, data);
|
||||
// SET FAILED TO TRUE
|
||||
failed2 = true;
|
||||
mainWindow &&
|
||||
mainWindow.webContents.send(
|
||||
COMMAND.UPSCAYL_ERROR,
|
||||
"Error upscaling image. Error: " + data
|
||||
);
|
||||
upscayl2.kill();
|
||||
return;
|
||||
});
|
||||
upscayl2.process.on("close", onClose2);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export default doubleUpscayl;
|
26
electron/commands/get-models-list.ts
Normal file
26
electron/commands/get-models-list.ts
Normal file
@ -0,0 +1,26 @@
|
||||
import COMMAND from "../constants/commands";
|
||||
import { getMainWindow } from "../main-window";
|
||||
import {
|
||||
customModelsFolderPath,
|
||||
setCustomModelsFolderPath,
|
||||
} from "../utils/config-variables";
|
||||
import getModels from "../utils/get-models";
|
||||
import logit from "../utils/logit";
|
||||
|
||||
const getModelsList = async (event, payload) => {
|
||||
const mainWindow = getMainWindow();
|
||||
|
||||
if (!mainWindow) return;
|
||||
if (payload) {
|
||||
setCustomModelsFolderPath(payload);
|
||||
|
||||
logit("📁 Custom Models Folder Path: ", customModelsFolderPath);
|
||||
|
||||
mainWindow.webContents.send(
|
||||
COMMAND.CUSTOM_MODEL_FILES_LIST,
|
||||
getModels(payload)
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export default getModelsList;
|
172
electron/commands/image-upscayl.ts
Normal file
172
electron/commands/image-upscayl.ts
Normal file
@ -0,0 +1,172 @@
|
||||
import fs from "fs";
|
||||
import { modelsPath } from "../utils/get-resource-paths";
|
||||
import COMMAND from "../constants/commands";
|
||||
import {
|
||||
customModelsFolderPath,
|
||||
folderPath,
|
||||
outputFolderPath,
|
||||
overwrite,
|
||||
saveOutputFolder,
|
||||
setChildProcesses,
|
||||
setOverwrite,
|
||||
setStopped,
|
||||
stopped,
|
||||
} from "../utils/config-variables";
|
||||
import convertAndScale from "../utils/convert-and-scale";
|
||||
import { getSingleImageArguments } from "../utils/get-arguments";
|
||||
import logit from "../utils/logit";
|
||||
import slash from "../utils/slash";
|
||||
import { spawnUpscayl } from "../utils/spawn-upscayl";
|
||||
import { parse } from "path";
|
||||
import DEFAULT_MODELS from "../constants/models";
|
||||
import { getMainWindow } from "../main-window";
|
||||
|
||||
const imageUpscayl = async (event, payload) => {
|
||||
const mainWindow = getMainWindow();
|
||||
|
||||
if (!mainWindow) {
|
||||
logit("No main window found");
|
||||
return;
|
||||
}
|
||||
|
||||
setOverwrite(payload.overwrite);
|
||||
|
||||
const model = payload.model as string;
|
||||
const gpuId = payload.gpuId as string;
|
||||
const saveImageAs = payload.saveImageAs as string;
|
||||
|
||||
let inputDir = (payload.imagePath.match(/(.*)[\/\\]/)[1] || "") as string;
|
||||
let outputDir: string | undefined =
|
||||
folderPath || (payload.outputPath as string);
|
||||
|
||||
if (saveOutputFolder === true && outputFolderPath) {
|
||||
outputDir = outputFolderPath;
|
||||
}
|
||||
|
||||
const isDefaultModel = DEFAULT_MODELS.includes(model);
|
||||
|
||||
const fullfileName = payload.imagePath.replace(/^.*[\\\/]/, "") as string;
|
||||
const fileName = parse(fullfileName).name;
|
||||
const fileExt = parse(fullfileName).ext;
|
||||
|
||||
let scale = "4";
|
||||
if (model.includes("x2")) {
|
||||
scale = "2";
|
||||
} else if (model.includes("x3")) {
|
||||
scale = "3";
|
||||
} else {
|
||||
scale = "4";
|
||||
}
|
||||
|
||||
const outFile =
|
||||
outputDir +
|
||||
slash +
|
||||
fileName +
|
||||
"_upscayl_" +
|
||||
payload.scale +
|
||||
"x_" +
|
||||
model +
|
||||
"." +
|
||||
saveImageAs;
|
||||
|
||||
// UPSCALE
|
||||
if (fs.existsSync(outFile) && !overwrite) {
|
||||
// If already upscayled, just output that file
|
||||
logit("✅ Already upscayled at: ", outFile);
|
||||
mainWindow.webContents.send(
|
||||
COMMAND.UPSCAYL_DONE,
|
||||
outFile.replace(
|
||||
/([^/\\]+)$/i,
|
||||
encodeURIComponent(outFile.match(/[^/\\]+$/i)![0])
|
||||
)
|
||||
);
|
||||
} else {
|
||||
const upscayl = spawnUpscayl(
|
||||
"realesrgan",
|
||||
getSingleImageArguments(
|
||||
inputDir,
|
||||
fullfileName,
|
||||
outFile,
|
||||
isDefaultModel ? modelsPath : customModelsFolderPath ?? modelsPath,
|
||||
model,
|
||||
scale,
|
||||
gpuId,
|
||||
"png"
|
||||
),
|
||||
logit
|
||||
);
|
||||
|
||||
setChildProcesses(upscayl);
|
||||
|
||||
setStopped(false);
|
||||
let isAlpha = false;
|
||||
let failed = false;
|
||||
|
||||
const onData = (data: string) => {
|
||||
logit("image upscayl: ", data.toString());
|
||||
mainWindow.setProgressBar(parseFloat(data.slice(0, data.length)) / 100);
|
||||
data = data.toString();
|
||||
mainWindow.webContents.send(COMMAND.UPSCAYL_PROGRESS, data.toString());
|
||||
if (data.includes("invalid gpu") || data.includes("failed")) {
|
||||
logit("❌ INVALID GPU OR FAILED");
|
||||
upscayl.kill();
|
||||
failed = true;
|
||||
}
|
||||
if (data.includes("has alpha channel")) {
|
||||
logit("📢 INCLUDES ALPHA CHANNEL, CHANGING OUTFILE NAME!");
|
||||
isAlpha = true;
|
||||
}
|
||||
};
|
||||
const onError = (data) => {
|
||||
if (!mainWindow) return;
|
||||
mainWindow.setProgressBar(-1);
|
||||
mainWindow.webContents.send(COMMAND.UPSCAYL_PROGRESS, data.toString());
|
||||
failed = true;
|
||||
upscayl.kill();
|
||||
return;
|
||||
};
|
||||
const onClose = async () => {
|
||||
if (!failed && !stopped) {
|
||||
logit("💯 Done upscaling");
|
||||
logit("♻ Scaling and converting now...");
|
||||
mainWindow.webContents.send(COMMAND.SCALING_AND_CONVERTING);
|
||||
// Free up memory
|
||||
upscayl.kill();
|
||||
try {
|
||||
await convertAndScale(
|
||||
inputDir + slash + fullfileName,
|
||||
isAlpha ? outFile + ".png" : outFile,
|
||||
outFile,
|
||||
payload.scale,
|
||||
saveImageAs,
|
||||
onError
|
||||
);
|
||||
mainWindow.setProgressBar(-1);
|
||||
mainWindow.webContents.send(
|
||||
COMMAND.UPSCAYL_DONE,
|
||||
outFile.replace(
|
||||
/([^/\\]+)$/i,
|
||||
encodeURIComponent(outFile.match(/[^/\\]+$/i)![0])
|
||||
)
|
||||
);
|
||||
} catch (error) {
|
||||
logit(
|
||||
"❌ Error processing (scaling and converting) the image. Please report this error on GitHub.",
|
||||
error
|
||||
);
|
||||
upscayl.kill();
|
||||
mainWindow.webContents.send(
|
||||
COMMAND.UPSCAYL_ERROR,
|
||||
"Error processing (scaling and converting) the image. Please report this error on Upscayl GitHub Issues page."
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
upscayl.process.stderr.on("data", onData);
|
||||
upscayl.process.on("error", onError);
|
||||
upscayl.process.on("close", onClose);
|
||||
}
|
||||
};
|
||||
|
||||
export default imageUpscayl;
|
9
electron/commands/open-folder.ts
Normal file
9
electron/commands/open-folder.ts
Normal file
@ -0,0 +1,9 @@
|
||||
import { shell } from "electron";
|
||||
import logit from "../utils/logit";
|
||||
|
||||
const openFolder = async (event, payload) => {
|
||||
logit("📂 Opening Folder: ", payload);
|
||||
shell.openPath(payload);
|
||||
};
|
||||
|
||||
export default openFolder;
|
58
electron/commands/select-file.ts
Normal file
58
electron/commands/select-file.ts
Normal file
@ -0,0 +1,58 @@
|
||||
import { MessageBoxOptions, dialog } from "electron";
|
||||
import { getMainWindow } from "../main-window";
|
||||
import { imagePath, setImagePath } from "../utils/config-variables";
|
||||
import logit from "../utils/logit";
|
||||
|
||||
const selectFile = async () => {
|
||||
const mainWindow = getMainWindow();
|
||||
|
||||
const { canceled, filePaths } = await dialog.showOpenDialog({
|
||||
properties: ["openFile", "multiSelections"],
|
||||
title: "Select Image",
|
||||
defaultPath: imagePath,
|
||||
});
|
||||
|
||||
if (canceled) {
|
||||
logit("🚫 File Operation Cancelled");
|
||||
return null;
|
||||
} else {
|
||||
setImagePath(filePaths[0]);
|
||||
|
||||
let isValid = false;
|
||||
// READ SELECTED FILES
|
||||
filePaths.forEach((file) => {
|
||||
// log.log("Files in Folder: ", file);
|
||||
if (
|
||||
file.endsWith(".png") ||
|
||||
file.endsWith(".jpg") ||
|
||||
file.endsWith(".jpeg") ||
|
||||
file.endsWith(".webp") ||
|
||||
file.endsWith(".JPG") ||
|
||||
file.endsWith(".PNG") ||
|
||||
file.endsWith(".JPEG") ||
|
||||
file.endsWith(".WEBP")
|
||||
) {
|
||||
isValid = true;
|
||||
}
|
||||
});
|
||||
|
||||
if (!isValid) {
|
||||
logit("❌ Invalid File Detected");
|
||||
const options: MessageBoxOptions = {
|
||||
type: "error",
|
||||
title: "Invalid File",
|
||||
message:
|
||||
"The selected file is not a valid image. Make sure you select a '.png', '.jpg', or '.webp' file.",
|
||||
};
|
||||
if (!mainWindow) return null;
|
||||
dialog.showMessageBoxSync(mainWindow, options);
|
||||
return null;
|
||||
}
|
||||
|
||||
logit("📄 Selected File Path: ", filePaths[0]);
|
||||
// CREATE input AND upscaled FOLDER
|
||||
return filePaths[0];
|
||||
}
|
||||
};
|
||||
|
||||
export default selectFile;
|
21
electron/commands/select-folder.ts
Normal file
21
electron/commands/select-folder.ts
Normal file
@ -0,0 +1,21 @@
|
||||
import { dialog } from "electron";
|
||||
import { folderPath, setFolderPath } from "../utils/config-variables";
|
||||
import logit from "../utils/logit";
|
||||
|
||||
const selectFolder = async (event, message) => {
|
||||
const { canceled, filePaths: folderPaths } = await dialog.showOpenDialog({
|
||||
properties: ["openDirectory"],
|
||||
defaultPath: folderPath,
|
||||
});
|
||||
|
||||
if (canceled) {
|
||||
logit("🚫 Select Folder Operation Cancelled");
|
||||
return null;
|
||||
} else {
|
||||
setFolderPath(folderPaths[0]);
|
||||
logit("📁 Selected Folder Path: ", folderPath);
|
||||
return folderPaths[0];
|
||||
}
|
||||
};
|
||||
|
||||
export default selectFolder;
|
16
electron/commands/stop.ts
Normal file
16
electron/commands/stop.ts
Normal file
@ -0,0 +1,16 @@
|
||||
import { getMainWindow } from "../main-window";
|
||||
import { childProcesses, setStopped } from "../utils/config-variables";
|
||||
import logit from "../utils/logit";
|
||||
|
||||
const stop = async (event, payload) => {
|
||||
const mainWindow = getMainWindow();
|
||||
|
||||
setStopped(true);
|
||||
mainWindow && mainWindow.setProgressBar(-1);
|
||||
childProcesses.forEach((child) => {
|
||||
logit("🛑 Stopping Upscaling Process", child.process.pid);
|
||||
child.kill();
|
||||
});
|
||||
};
|
||||
|
||||
export default stop;
|
@ -1,4 +1,4 @@
|
||||
const commands = {
|
||||
const COMMAND = {
|
||||
SELECT_FILE: "Select a File",
|
||||
SELECT_FOLDER: "Select a Folder",
|
||||
UPSCAYL: "Upscale the Image",
|
||||
@ -27,4 +27,4 @@ const commands = {
|
||||
UPSCAYL_ERROR: "Upscaling Error",
|
||||
};
|
||||
|
||||
export default commands;
|
||||
export default COMMAND;
|
9
electron/constants/models.ts
Normal file
9
electron/constants/models.ts
Normal file
@ -0,0 +1,9 @@
|
||||
const DEFAULT_MODELS = [
|
||||
"realesrgan-x4plus",
|
||||
"remacri",
|
||||
"ultramix_balanced",
|
||||
"ultrasharp",
|
||||
"realesrgan-x4plus-anime",
|
||||
];
|
||||
|
||||
export default DEFAULT_MODELS;
|
@ -1,922 +1,69 @@
|
||||
// Native
|
||||
import prepareNext from "electron-next";
|
||||
import { autoUpdater } from "electron-updater";
|
||||
import { getPlatform } from "./getDeviceSpecs";
|
||||
import path, { join, parse } from "path";
|
||||
import log from "electron-log";
|
||||
import { format } from "url";
|
||||
import fs from "fs";
|
||||
import { app, ipcMain, protocol, net } from "electron";
|
||||
import COMMAND from "./constants/commands";
|
||||
import logit from "./utils/logit";
|
||||
import openFolder from "./commands/open-folder";
|
||||
import stop from "./commands/stop";
|
||||
import selectFolder from "./commands/select-folder";
|
||||
import selectFile from "./commands/select-file";
|
||||
import getModelsList from "./commands/get-models-list";
|
||||
import customModelsSelect from "./commands/custom-models-select";
|
||||
import imageUpscayl from "./commands/image-upscayl";
|
||||
import { createMainWindow } from "./main-window";
|
||||
import electronIsDev from "electron-is-dev";
|
||||
import { execPath, modelsPath } from "./utils/get-resource-paths";
|
||||
import batchUpscayl from "./commands/batch-upscayl";
|
||||
import doubleUpscayl from "./commands/double-upscayl";
|
||||
import autoUpdate from "./commands/auto-update";
|
||||
import sharp from "sharp";
|
||||
|
||||
import { execPath, modelsPath } from "./binaries";
|
||||
// Packages
|
||||
import {
|
||||
BrowserWindow,
|
||||
app,
|
||||
ipcMain,
|
||||
dialog,
|
||||
shell,
|
||||
MessageBoxOptions,
|
||||
protocol,
|
||||
} from "electron";
|
||||
|
||||
import prepareNext from "electron-next";
|
||||
import isDev from "electron-is-dev";
|
||||
import commands from "./commands";
|
||||
import { ChildProcessWithoutNullStreams } from "child_process";
|
||||
import {
|
||||
getBatchArguments,
|
||||
getDoubleUpscaleArguments,
|
||||
getDoubleUpscaleSecondPassArguments,
|
||||
getSingleImageArguments,
|
||||
} from "./utils/getArguments";
|
||||
import { spawnUpscayl } from "./upscayl";
|
||||
|
||||
let childProcesses: {
|
||||
process: ChildProcessWithoutNullStreams;
|
||||
kill: () => boolean;
|
||||
}[] = [];
|
||||
|
||||
// INITIALIZATION
|
||||
log.initialize({ preload: true });
|
||||
sharp.cache(false);
|
||||
logit("🚃 App Path: ", app.getAppPath());
|
||||
|
||||
function escapeRegExp(string) {
|
||||
return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); // $& means the whole matched string
|
||||
}
|
||||
|
||||
// Path variables for file and folder selection
|
||||
let imagePath: string | undefined = undefined;
|
||||
let folderPath: string | undefined = undefined;
|
||||
let customModelsFolderPath: string | undefined = undefined;
|
||||
let outputFolderPath: string | undefined = undefined;
|
||||
let saveOutputFolder = false;
|
||||
let quality = 0;
|
||||
let overwrite = false;
|
||||
|
||||
let stopped = false;
|
||||
|
||||
// Slashes for use in directory names
|
||||
const slash: string = getPlatform() === "win" ? "\\" : "/";
|
||||
|
||||
// Prepare the renderer once the app is ready
|
||||
let mainWindow: BrowserWindow | null = null;
|
||||
app.on("ready", async () => {
|
||||
app.whenReady().then(async () => {
|
||||
await prepareNext("./renderer");
|
||||
createMainWindow();
|
||||
|
||||
log.info("🚀 UPSCAYL EXEC PATH: ", execPath("realesrgan"));
|
||||
log.info("🚀 MODELS PATH: ", modelsPath);
|
||||
|
||||
mainWindow = new BrowserWindow({
|
||||
icon: join(__dirname, "build", "icon.png"),
|
||||
width: 1300,
|
||||
height: 940,
|
||||
minHeight: 500,
|
||||
minWidth: 500,
|
||||
show: false,
|
||||
backgroundColor: "#171717",
|
||||
webPreferences: {
|
||||
nodeIntegration: true,
|
||||
nodeIntegrationInWorker: true,
|
||||
webSecurity: false,
|
||||
preload: join(__dirname, "preload.js"),
|
||||
},
|
||||
titleBarStyle: getPlatform() === "mac" ? "hiddenInset" : "default",
|
||||
});
|
||||
const url = isDev
|
||||
? "http://localhost:8000"
|
||||
: format({
|
||||
pathname: join(__dirname, "../renderer/out/index.html"),
|
||||
protocol: "file:",
|
||||
slashes: true,
|
||||
});
|
||||
|
||||
mainWindow.setMenuBarVisibility(false);
|
||||
mainWindow.loadURL(url);
|
||||
|
||||
mainWindow.webContents.setWindowOpenHandler(({ url }) => {
|
||||
shell.openExternal(url);
|
||||
return { action: "deny" };
|
||||
protocol.handle("file:", (request) => {
|
||||
const pathname = decodeURI(request.url);
|
||||
return net.fetch(pathname);
|
||||
});
|
||||
|
||||
mainWindow.once("ready-to-show", () => {
|
||||
if (!mainWindow) return;
|
||||
mainWindow.show();
|
||||
mainWindow.webContents.setZoomFactor(1);
|
||||
});
|
||||
|
||||
app.whenReady().then(() => {
|
||||
protocol.registerFileProtocol("file", (request, callback) => {
|
||||
const pathname = decodeURI(request.url.replace("file:///", ""));
|
||||
callback(pathname);
|
||||
});
|
||||
});
|
||||
|
||||
if (!isDev) {
|
||||
if (!electronIsDev) {
|
||||
autoUpdater.checkForUpdates();
|
||||
}
|
||||
|
||||
// <------------------------Save Last Paths----------------------------->
|
||||
// GET LAST IMAGE PATH TO LOCAL STORAGE
|
||||
mainWindow.webContents
|
||||
.executeJavaScript('localStorage.getItem("lastImagePath");', true)
|
||||
.then((lastImagePath: string | null) => {
|
||||
if (lastImagePath && lastImagePath.length > 0) {
|
||||
imagePath = lastImagePath;
|
||||
}
|
||||
});
|
||||
// GET LAST FOLDER PATH TO LOCAL STORAGE
|
||||
mainWindow.webContents
|
||||
.executeJavaScript('localStorage.getItem("lastFolderPath");', true)
|
||||
.then((lastFolderPath: string | null) => {
|
||||
if (lastFolderPath && lastFolderPath.length > 0) {
|
||||
folderPath = lastFolderPath;
|
||||
}
|
||||
});
|
||||
// GET LAST CUSTOM MODELS FOLDER PATH TO LOCAL STORAGE
|
||||
mainWindow.webContents
|
||||
.executeJavaScript(
|
||||
'localStorage.getItem("lastCustomModelsFolderPath");',
|
||||
true
|
||||
)
|
||||
.then((lastCustomModelsFolderPath: string | null) => {
|
||||
if (lastCustomModelsFolderPath && lastCustomModelsFolderPath.length > 0) {
|
||||
customModelsFolderPath = lastCustomModelsFolderPath;
|
||||
}
|
||||
});
|
||||
// GET LAST CUSTOM MODELS FOLDER PATH TO LOCAL STORAGE
|
||||
mainWindow.webContents
|
||||
.executeJavaScript('localStorage.getItem("lastOutputFolderPath");', true)
|
||||
.then((lastOutputFolderPath: string | null) => {
|
||||
if (lastOutputFolderPath && lastOutputFolderPath.length > 0) {
|
||||
outputFolderPath = lastOutputFolderPath;
|
||||
}
|
||||
});
|
||||
// GET LAST SAVE OUTPUT FOLDER (BOOLEAN) TO LOCAL STORAGE
|
||||
mainWindow.webContents
|
||||
.executeJavaScript('localStorage.getItem("rememberOutputFolder");', true)
|
||||
.then((lastSaveOutputFolder: boolean | null) => {
|
||||
if (lastSaveOutputFolder !== null) {
|
||||
saveOutputFolder = lastSaveOutputFolder;
|
||||
}
|
||||
});
|
||||
// GET IMAGE QUALITY (NUMBER) TO LOCAL STORAGE
|
||||
mainWindow.webContents
|
||||
.executeJavaScript('localStorage.getItem("quality");', true)
|
||||
.then((lastSavedQuality: string | null) => {
|
||||
if (lastSavedQuality !== null) {
|
||||
if (parseInt(lastSavedQuality) === 100) {
|
||||
quality = 99;
|
||||
} else {
|
||||
quality = parseInt(lastSavedQuality);
|
||||
}
|
||||
}
|
||||
});
|
||||
mainWindow.webContents.send(commands.OS, getPlatform());
|
||||
});
|
||||
|
||||
// Quit the app once all windows are closed
|
||||
app.on("window-all-closed", app.quit);
|
||||
|
||||
log.log("🚃 App Path: ", app.getAppPath());
|
||||
|
||||
const logit = (...args: any) => {
|
||||
log.log(...args);
|
||||
if (!mainWindow) return;
|
||||
mainWindow.webContents.send(commands.LOG, args.join(" "));
|
||||
};
|
||||
|
||||
// Default models
|
||||
const defaultModels = [
|
||||
"realesrgan-x4plus",
|
||||
"remacri",
|
||||
"ultramix_balanced",
|
||||
"ultrasharp",
|
||||
"realesrgan-x4plus-anime",
|
||||
];
|
||||
|
||||
// ! DONT FORGET TO RESTART THE APP WHEN YOU CHANGE CODE HERE
|
||||
|
||||
//------------------------Get Model Names-----------------------------//
|
||||
const getModels = (folderPath: string) => {
|
||||
let models: string[] = [];
|
||||
let isValid = false;
|
||||
|
||||
// READ CUSTOM MODELS FOLDER
|
||||
fs.readdirSync(folderPath).forEach((file) => {
|
||||
// log.log("Files in Folder: ", file);
|
||||
if (
|
||||
file.endsWith(".param") ||
|
||||
file.endsWith(".PARAM") ||
|
||||
file.endsWith(".bin") ||
|
||||
file.endsWith(".BIN")
|
||||
) {
|
||||
isValid = true;
|
||||
const modelName = file.substring(0, file.lastIndexOf(".")) || file;
|
||||
if (!models.includes(modelName)) {
|
||||
models.push(modelName);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (!isValid) {
|
||||
logit("❌ Invalid Custom Model Folder Detected");
|
||||
const options: MessageBoxOptions = {
|
||||
type: "error",
|
||||
title: "Invalid Folder",
|
||||
message:
|
||||
"The selected folder does not contain valid model files. Make sure you select the folder that ONLY contains '.param' and '.bin' files.",
|
||||
buttons: ["OK"],
|
||||
};
|
||||
dialog.showMessageBoxSync(options);
|
||||
return null;
|
||||
}
|
||||
|
||||
logit("🔎 Detected Custom Models: ", models);
|
||||
return models;
|
||||
};
|
||||
|
||||
//------------------------Save Last Paths-----------------------------//
|
||||
const convertAndScale = async (
|
||||
originalImagePath: string,
|
||||
upscaledImagePath: string,
|
||||
processedImagePath: string,
|
||||
scale: string,
|
||||
saveImageAs: string,
|
||||
onError: (error: any) => void
|
||||
) => {
|
||||
const originalImage = await sharp(originalImagePath).metadata();
|
||||
if (!mainWindow || !originalImage) {
|
||||
throw new Error("Could not grab the original image!");
|
||||
}
|
||||
// Resize the image to the scale
|
||||
const newImage = sharp(upscaledImagePath)
|
||||
.resize(
|
||||
originalImage.width && originalImage.width * parseInt(scale),
|
||||
originalImage.height && originalImage.height * parseInt(scale)
|
||||
)
|
||||
.withMetadata(); // Keep metadata
|
||||
// Change the output according to the saveImageAs
|
||||
if (saveImageAs === "png") {
|
||||
newImage.png({ quality: 100 - quality });
|
||||
} else if (saveImageAs === "jpg") {
|
||||
console.log("Quality: ", quality);
|
||||
newImage.jpeg({ quality: 100 - quality });
|
||||
}
|
||||
// Save the image
|
||||
const buffer = await newImage.toBuffer();
|
||||
sharp(buffer)
|
||||
.toFile(processedImagePath)
|
||||
.then(() => {
|
||||
logit("✅ Done converting to: ", upscaledImagePath);
|
||||
})
|
||||
.catch((error) => {
|
||||
logit("❌ Error converting to: ", saveImageAs, error);
|
||||
onError(error);
|
||||
});
|
||||
};
|
||||
|
||||
//------------------------Open Folder-----------------------------//
|
||||
ipcMain.on(commands.OPEN_FOLDER, async (event, payload) => {
|
||||
logit("📂 Opening Folder: ", payload);
|
||||
shell.openPath(payload);
|
||||
});
|
||||
|
||||
//------------------------Stop Command-----------------------------//
|
||||
ipcMain.on(commands.STOP, async (event, payload) => {
|
||||
stopped = true;
|
||||
mainWindow && mainWindow.setProgressBar(-1);
|
||||
childProcesses.forEach((child) => {
|
||||
logit("🛑 Stopping Upscaling Process", child.process.pid);
|
||||
child.kill();
|
||||
});
|
||||
});
|
||||
|
||||
//------------------------Select Folder-----------------------------//
|
||||
ipcMain.handle(commands.SELECT_FOLDER, async (event, message) => {
|
||||
const { canceled, filePaths: folderPaths } = await dialog.showOpenDialog({
|
||||
properties: ["openDirectory"],
|
||||
defaultPath: folderPath,
|
||||
});
|
||||
|
||||
if (canceled) {
|
||||
logit("🚫 Select Folder Operation Cancelled");
|
||||
return null;
|
||||
} else {
|
||||
folderPath = folderPaths[0];
|
||||
logit("📁 Selected Folder Path: ", folderPath);
|
||||
return folderPaths[0];
|
||||
app.on("window-all-closed", () => {
|
||||
if (process.platform !== "darwin") {
|
||||
app.quit();
|
||||
}
|
||||
});
|
||||
|
||||
//------------------------Select File-----------------------------//
|
||||
ipcMain.handle(commands.SELECT_FILE, async () => {
|
||||
if (!mainWindow) return;
|
||||
const { canceled, filePaths } = await dialog.showOpenDialog({
|
||||
properties: ["openFile", "multiSelections"],
|
||||
title: "Select Image",
|
||||
defaultPath: imagePath,
|
||||
});
|
||||
ipcMain.on(COMMAND.STOP, stop);
|
||||
|
||||
if (canceled) {
|
||||
logit("🚫 File Operation Cancelled");
|
||||
return null;
|
||||
} else {
|
||||
imagePath = filePaths[0];
|
||||
ipcMain.on(COMMAND.OPEN_FOLDER, openFolder);
|
||||
|
||||
let isValid = false;
|
||||
// READ SELECTED FILES
|
||||
filePaths.forEach((file) => {
|
||||
// log.log("Files in Folder: ", file);
|
||||
if (
|
||||
file.endsWith(".png") ||
|
||||
file.endsWith(".jpg") ||
|
||||
file.endsWith(".jpeg") ||
|
||||
file.endsWith(".webp") ||
|
||||
file.endsWith(".JPG") ||
|
||||
file.endsWith(".PNG") ||
|
||||
file.endsWith(".JPEG") ||
|
||||
file.endsWith(".WEBP")
|
||||
) {
|
||||
isValid = true;
|
||||
}
|
||||
});
|
||||
ipcMain.handle(COMMAND.SELECT_FOLDER, selectFolder);
|
||||
|
||||
if (!isValid) {
|
||||
logit("❌ Invalid File Detected");
|
||||
const options: MessageBoxOptions = {
|
||||
type: "error",
|
||||
title: "Invalid File",
|
||||
message:
|
||||
"The selected file is not a valid image. Make sure you select a '.png', '.jpg', or '.webp' file.",
|
||||
};
|
||||
dialog.showMessageBoxSync(mainWindow, options);
|
||||
return null;
|
||||
}
|
||||
ipcMain.handle(COMMAND.SELECT_FILE, selectFile);
|
||||
|
||||
logit("📄 Selected File Path: ", filePaths[0]);
|
||||
// CREATE input AND upscaled FOLDER
|
||||
return filePaths[0];
|
||||
}
|
||||
});
|
||||
ipcMain.on(COMMAND.GET_MODELS_LIST, getModelsList);
|
||||
|
||||
//------------------------Get Models List-----------------------------//
|
||||
ipcMain.on(commands.GET_MODELS_LIST, async (event, payload) => {
|
||||
if (!mainWindow) return;
|
||||
if (payload) {
|
||||
customModelsFolderPath = payload;
|
||||
ipcMain.handle(COMMAND.SELECT_CUSTOM_MODEL_FOLDER, customModelsSelect);
|
||||
|
||||
logit("📁 Custom Models Folder Path: ", customModelsFolderPath);
|
||||
ipcMain.on(COMMAND.UPSCAYL, imageUpscayl);
|
||||
|
||||
mainWindow.webContents.send(
|
||||
commands.CUSTOM_MODEL_FILES_LIST,
|
||||
getModels(payload)
|
||||
);
|
||||
}
|
||||
});
|
||||
ipcMain.on(COMMAND.FOLDER_UPSCAYL, batchUpscayl);
|
||||
|
||||
//------------------------Custom Models Select-----------------------------//
|
||||
ipcMain.handle(commands.SELECT_CUSTOM_MODEL_FOLDER, async (event, message) => {
|
||||
if (!mainWindow) return;
|
||||
const { canceled, filePaths: folderPaths } = await dialog.showOpenDialog({
|
||||
properties: ["openDirectory"],
|
||||
title: "Select Custom Models Folder",
|
||||
defaultPath: customModelsFolderPath,
|
||||
});
|
||||
if (canceled) {
|
||||
logit("🚫 Select Custom Models Folder Operation Cancelled");
|
||||
return null;
|
||||
} else {
|
||||
customModelsFolderPath = folderPaths[0];
|
||||
ipcMain.on(COMMAND.DOUBLE_UPSCAYL, doubleUpscayl);
|
||||
|
||||
if (
|
||||
!folderPaths[0].endsWith(slash + "models") &&
|
||||
!folderPaths[0].endsWith(slash + "models" + slash)
|
||||
) {
|
||||
logit("❌ Invalid Custom Models Folder Detected: Not a 'models' folder");
|
||||
const options: MessageBoxOptions = {
|
||||
type: "error",
|
||||
title: "Invalid Folder",
|
||||
message:
|
||||
"Please make sure that the folder name is 'models' and nothing else.",
|
||||
buttons: ["OK"],
|
||||
};
|
||||
dialog.showMessageBoxSync(options);
|
||||
return null;
|
||||
}
|
||||
|
||||
mainWindow.webContents.send(
|
||||
commands.CUSTOM_MODEL_FILES_LIST,
|
||||
getModels(customModelsFolderPath)
|
||||
);
|
||||
|
||||
logit("📁 Custom Folder Path: ", customModelsFolderPath);
|
||||
return customModelsFolderPath;
|
||||
}
|
||||
});
|
||||
|
||||
//------------------------Image Upscayl-----------------------------//
|
||||
ipcMain.on(commands.UPSCAYL, async (event, payload) => {
|
||||
if (!mainWindow) return;
|
||||
overwrite = payload.overwrite;
|
||||
const model = payload.model as string;
|
||||
const gpuId = payload.gpuId as string;
|
||||
const saveImageAs = payload.saveImageAs as string;
|
||||
|
||||
let inputDir = (payload.imagePath.match(/(.*)[\/\\]/)[1] || "") as string;
|
||||
let outputDir = folderPath || (payload.outputPath as string);
|
||||
|
||||
if (saveOutputFolder === true && outputFolderPath) {
|
||||
outputDir = outputFolderPath;
|
||||
}
|
||||
|
||||
const isDefaultModel = defaultModels.includes(model);
|
||||
|
||||
const fullfileName = payload.imagePath.replace(/^.*[\\\/]/, "") as string;
|
||||
const fileName = parse(fullfileName).name;
|
||||
const fileExt = parse(fullfileName).ext;
|
||||
|
||||
let scale = "4";
|
||||
if (model.includes("x2")) {
|
||||
scale = "2";
|
||||
} else if (model.includes("x3")) {
|
||||
scale = "3";
|
||||
} else {
|
||||
scale = "4";
|
||||
}
|
||||
|
||||
const outFile =
|
||||
outputDir +
|
||||
slash +
|
||||
fileName +
|
||||
"_upscayl_" +
|
||||
payload.scale +
|
||||
"x_" +
|
||||
model +
|
||||
"." +
|
||||
saveImageAs;
|
||||
|
||||
// GET OVERWRITE SETTINGS FROM LOCAL STORAGE
|
||||
mainWindow.webContents
|
||||
.executeJavaScript('localStorage.getItem("overwrite");', true)
|
||||
.then((lastSavedOverwrite: boolean | null) => {
|
||||
if (lastSavedOverwrite !== null) {
|
||||
console.log("Overwrite: ", lastSavedOverwrite);
|
||||
overwrite = lastSavedOverwrite;
|
||||
}
|
||||
});
|
||||
|
||||
// UPSCALE
|
||||
if (fs.existsSync(outFile) && overwrite === false) {
|
||||
// If already upscayled, just output that file
|
||||
logit("✅ Already upscayled at: ", outFile);
|
||||
mainWindow.webContents.send(
|
||||
commands.UPSCAYL_DONE,
|
||||
outFile.replace(
|
||||
/([^/\\]+)$/i,
|
||||
encodeURIComponent(outFile.match(/[^/\\]+$/i)![0])
|
||||
)
|
||||
);
|
||||
} else {
|
||||
const upscayl = spawnUpscayl(
|
||||
"realesrgan",
|
||||
getSingleImageArguments(
|
||||
inputDir,
|
||||
fullfileName,
|
||||
outFile,
|
||||
isDefaultModel ? modelsPath : customModelsFolderPath ?? modelsPath,
|
||||
model,
|
||||
scale,
|
||||
gpuId,
|
||||
"png"
|
||||
),
|
||||
logit
|
||||
);
|
||||
|
||||
childProcesses.push(upscayl);
|
||||
|
||||
stopped = false;
|
||||
let isAlpha = false;
|
||||
let failed = false;
|
||||
|
||||
const onData = (data: string) => {
|
||||
if (!mainWindow) return;
|
||||
logit("image upscayl: ", data.toString());
|
||||
mainWindow.setProgressBar(parseFloat(data.slice(0, data.length)) / 100);
|
||||
data = data.toString();
|
||||
mainWindow.webContents.send(commands.UPSCAYL_PROGRESS, data.toString());
|
||||
if (data.includes("invalid gpu") || data.includes("failed")) {
|
||||
logit("❌ INVALID GPU OR FAILED");
|
||||
upscayl.kill();
|
||||
failed = true;
|
||||
}
|
||||
if (data.includes("has alpha channel")) {
|
||||
logit("📢 INCLUDES ALPHA CHANNEL, CHANGING OUTFILE NAME!");
|
||||
isAlpha = true;
|
||||
}
|
||||
};
|
||||
const onError = (data) => {
|
||||
if (!mainWindow) return;
|
||||
mainWindow.webContents.send(commands.UPSCAYL_PROGRESS, data.toString());
|
||||
failed = true;
|
||||
upscayl.kill();
|
||||
return;
|
||||
};
|
||||
const onClose = async () => {
|
||||
if (!failed && !stopped) {
|
||||
logit("💯 Done upscaling");
|
||||
logit("♻ Scaling and converting now...");
|
||||
mainWindow &&
|
||||
mainWindow.webContents.send(commands.SCALING_AND_CONVERTING);
|
||||
// Free up memory
|
||||
upscayl.kill();
|
||||
try {
|
||||
if (!mainWindow) return;
|
||||
await convertAndScale(
|
||||
inputDir + slash + fullfileName,
|
||||
isAlpha ? outFile + ".png" : outFile,
|
||||
isAlpha ? outFile + ".png" : outFile,
|
||||
payload.scale,
|
||||
saveImageAs,
|
||||
onError
|
||||
);
|
||||
mainWindow.setProgressBar(-1);
|
||||
mainWindow.webContents.send(
|
||||
commands.UPSCAYL_DONE,
|
||||
outFile.replace(
|
||||
/([^/\\]+)$/i,
|
||||
encodeURIComponent(outFile.match(/[^/\\]+$/i)![0])
|
||||
)
|
||||
);
|
||||
} catch (error) {
|
||||
logit(
|
||||
"❌ Error processing (scaling and converting) the image. Please report this error on GitHub.",
|
||||
error
|
||||
);
|
||||
upscayl.kill();
|
||||
mainWindow &&
|
||||
mainWindow.webContents.send(
|
||||
commands.UPSCAYL_ERROR,
|
||||
"Error processing (scaling and converting) the image. Please report this error on GitHub."
|
||||
);
|
||||
onError(error);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
upscayl.process.stderr.on("data", onData);
|
||||
upscayl.process.on("error", onError);
|
||||
upscayl.process.on("close", onClose);
|
||||
}
|
||||
});
|
||||
|
||||
//------------------------Batch Upscayl-----------------------------//
|
||||
ipcMain.on(commands.FOLDER_UPSCAYL, async (event, payload) => {
|
||||
if (!mainWindow) return;
|
||||
// GET THE MODEL
|
||||
const model = payload.model;
|
||||
const gpuId = payload.gpuId;
|
||||
const saveImageAs = payload.saveImageAs;
|
||||
// const scale = payload.scale as string;
|
||||
|
||||
// GET THE IMAGE DIRECTORY
|
||||
let inputDir = payload.batchFolderPath;
|
||||
// GET THE OUTPUT DIRECTORY
|
||||
let outputDir = payload.outputPath;
|
||||
|
||||
if (saveOutputFolder === true && outputFolderPath) {
|
||||
outputDir = outputFolderPath;
|
||||
}
|
||||
|
||||
const isDefaultModel = defaultModels.includes(model);
|
||||
|
||||
let scale = "4";
|
||||
if (model.includes("x2")) {
|
||||
scale = "2";
|
||||
} else if (model.includes("x3")) {
|
||||
scale = "3";
|
||||
} else {
|
||||
scale = "4";
|
||||
}
|
||||
|
||||
outputDir += `_${model}_x${payload.scale}`;
|
||||
if (!fs.existsSync(outputDir)) {
|
||||
fs.mkdirSync(outputDir, { recursive: true });
|
||||
}
|
||||
|
||||
// Delete .DS_Store files
|
||||
fs.readdirSync(inputDir).forEach((file) => {
|
||||
if (file === ".DS_Store") {
|
||||
logit("🗑️ Deleting .DS_Store file");
|
||||
fs.unlinkSync(inputDir + slash + file);
|
||||
}
|
||||
});
|
||||
|
||||
// UPSCALE
|
||||
const upscayl = spawnUpscayl(
|
||||
"realesrgan",
|
||||
getBatchArguments(
|
||||
inputDir,
|
||||
outputDir,
|
||||
isDefaultModel ? modelsPath : customModelsFolderPath ?? modelsPath,
|
||||
model,
|
||||
gpuId,
|
||||
"png",
|
||||
scale
|
||||
),
|
||||
logit
|
||||
);
|
||||
|
||||
childProcesses.push(upscayl);
|
||||
|
||||
stopped = false;
|
||||
let failed = false;
|
||||
|
||||
const onData = (data: any) => {
|
||||
if (!mainWindow) return;
|
||||
data = data.toString();
|
||||
mainWindow.webContents.send(
|
||||
commands.FOLDER_UPSCAYL_PROGRESS,
|
||||
data.toString()
|
||||
);
|
||||
if (data.includes("invalid") || data.includes("failed")) {
|
||||
logit("❌ INVALID GPU OR INVALID FILES IN FOLDER - FAILED");
|
||||
failed = true;
|
||||
upscayl.kill();
|
||||
}
|
||||
};
|
||||
const onError = (data: any) => {
|
||||
if (!mainWindow) return;
|
||||
mainWindow.webContents.send(
|
||||
commands.FOLDER_UPSCAYL_PROGRESS,
|
||||
data.toString()
|
||||
);
|
||||
failed = true;
|
||||
upscayl.kill();
|
||||
return;
|
||||
};
|
||||
const onClose = () => {
|
||||
if (!mainWindow) return;
|
||||
if (!failed && !stopped) {
|
||||
logit("💯 Done upscaling");
|
||||
logit("♻ Scaling and converting now...");
|
||||
upscayl.kill();
|
||||
mainWindow &&
|
||||
mainWindow.webContents.send(commands.SCALING_AND_CONVERTING);
|
||||
// Get number of files in output folder
|
||||
const files = fs.readdirSync(inputDir);
|
||||
try {
|
||||
files.forEach(async (file) => {
|
||||
console.log("Filename: ", file.slice(0, -3));
|
||||
await convertAndScale(
|
||||
inputDir + slash + file,
|
||||
outputDir + slash + file.slice(0, -3) + "png",
|
||||
outputDir + slash + file.slice(0, -3) + saveImageAs,
|
||||
payload.scale,
|
||||
saveImageAs,
|
||||
onError
|
||||
);
|
||||
// Remove the png file (default) if the saveImageAs is not png
|
||||
if (saveImageAs !== "png") {
|
||||
fs.unlinkSync(outputDir + slash + file.slice(0, -3) + "png");
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
logit(
|
||||
"❌ Error processing (scaling and converting) the image. Please report this error on GitHub.",
|
||||
error
|
||||
);
|
||||
upscayl.kill();
|
||||
mainWindow &&
|
||||
mainWindow.webContents.send(
|
||||
commands.UPSCAYL_ERROR,
|
||||
"Error processing (scaling and converting) the image. Please report this error on GitHub."
|
||||
);
|
||||
onError(error);
|
||||
}
|
||||
|
||||
mainWindow.webContents.send(commands.FOLDER_UPSCAYL_DONE, outputDir);
|
||||
} else {
|
||||
upscayl.kill();
|
||||
}
|
||||
};
|
||||
|
||||
upscayl.process.stderr.on("data", onData);
|
||||
upscayl.process.on("error", onError);
|
||||
upscayl.process.on("close", onClose);
|
||||
});
|
||||
|
||||
//------------------------Double Upscayl-----------------------------//
|
||||
ipcMain.on(commands.DOUBLE_UPSCAYL, async (event, payload) => {
|
||||
if (!mainWindow) return;
|
||||
|
||||
const model = payload.model as string;
|
||||
const imagePath = payload.imagePath;
|
||||
let inputDir = (imagePath.match(/(.*)[\/\\]/) || [""])[1];
|
||||
let outputDir = path.normalize(payload.outputPath);
|
||||
|
||||
if (saveOutputFolder === true && outputFolderPath) {
|
||||
outputDir = outputFolderPath;
|
||||
}
|
||||
const gpuId = payload.gpuId as string;
|
||||
const saveImageAs = payload.saveImageAs as string;
|
||||
|
||||
const isDefaultModel = defaultModels.includes(model);
|
||||
|
||||
// COPY IMAGE TO TMP FOLDER
|
||||
|
||||
const fullfileName = imagePath.split(slash).slice(-1)[0] as string;
|
||||
const fileName = parse(fullfileName).name;
|
||||
const outFile =
|
||||
outputDir + slash + fileName + "_upscayl_16x_" + model + "." + saveImageAs;
|
||||
|
||||
let scale = "4";
|
||||
if (model.includes("x2")) {
|
||||
scale = "2";
|
||||
} else if (model.includes("x3")) {
|
||||
scale = "3";
|
||||
} else {
|
||||
scale = "4";
|
||||
}
|
||||
|
||||
// UPSCALE
|
||||
let upscayl = spawnUpscayl(
|
||||
"realesrgan",
|
||||
getDoubleUpscaleArguments(
|
||||
inputDir,
|
||||
fullfileName,
|
||||
outFile,
|
||||
isDefaultModel ? modelsPath : customModelsFolderPath ?? modelsPath,
|
||||
model,
|
||||
gpuId,
|
||||
saveImageAs,
|
||||
scale
|
||||
),
|
||||
logit
|
||||
);
|
||||
|
||||
childProcesses.push(upscayl);
|
||||
|
||||
stopped = false;
|
||||
let failed = false;
|
||||
let isAlpha = false;
|
||||
let failed2 = false;
|
||||
|
||||
const onData = (data) => {
|
||||
if (!mainWindow) return;
|
||||
// CONVERT DATA TO STRING
|
||||
data = data.toString();
|
||||
// SEND UPSCAYL PROGRESS TO RENDERER
|
||||
mainWindow.webContents.send(commands.DOUBLE_UPSCAYL_PROGRESS, data);
|
||||
// IF PROGRESS HAS ERROR, UPSCAYL FAILED
|
||||
if (data.includes("invalid gpu") || data.includes("failed")) {
|
||||
upscayl.kill();
|
||||
failed = true;
|
||||
}
|
||||
if (data.includes("has alpha channel")) {
|
||||
isAlpha = true;
|
||||
}
|
||||
};
|
||||
|
||||
const onError = (data) => {
|
||||
if (!mainWindow) return;
|
||||
data.toString();
|
||||
// SEND UPSCAYL PROGRESS TO RENDERER
|
||||
mainWindow.webContents.send(commands.DOUBLE_UPSCAYL_PROGRESS, data);
|
||||
// SET FAILED TO TRUE
|
||||
failed = true;
|
||||
upscayl.kill();
|
||||
return;
|
||||
};
|
||||
|
||||
const onClose2 = async (code) => {
|
||||
if (!mainWindow) return;
|
||||
if (!failed2 && !stopped) {
|
||||
logit("💯 Done upscaling");
|
||||
logit("♻ Scaling and converting now...");
|
||||
mainWindow.webContents.send(commands.SCALING_AND_CONVERTING);
|
||||
try {
|
||||
const originalImage = await sharp(
|
||||
inputDir + slash + fullfileName
|
||||
).metadata();
|
||||
if (!mainWindow || !originalImage) {
|
||||
throw new Error("Could not grab the original image!");
|
||||
}
|
||||
// Resize the image to the scale
|
||||
const newImage = sharp(isAlpha ? outFile + ".png" : outFile)
|
||||
.resize(
|
||||
originalImage.width &&
|
||||
originalImage.width * parseInt(payload.scale),
|
||||
originalImage.height &&
|
||||
originalImage.height * parseInt(payload.scale)
|
||||
)
|
||||
.withMetadata(); // Keep metadata
|
||||
// Change the output according to the saveImageAs
|
||||
if (saveImageAs === "png") {
|
||||
newImage.png({ quality: 100 - quality });
|
||||
} else if (saveImageAs === "jpg") {
|
||||
newImage.jpeg({ quality: 100 - quality });
|
||||
}
|
||||
// Save the image
|
||||
await newImage
|
||||
.toFile(isAlpha ? outFile + ".png" : outFile)
|
||||
.then(() => {
|
||||
logit(
|
||||
"✅ Done converting to: ",
|
||||
isAlpha ? outFile + ".png" : outFile
|
||||
);
|
||||
})
|
||||
.catch((error) => {
|
||||
logit("❌ Error converting to: ", saveImageAs, error);
|
||||
upscayl.kill();
|
||||
onError(error);
|
||||
});
|
||||
mainWindow.setProgressBar(-1);
|
||||
mainWindow.webContents.send(
|
||||
commands.DOUBLE_UPSCAYL_DONE,
|
||||
isAlpha
|
||||
? (outFile + ".png").replace(
|
||||
/([^/\\]+)$/i,
|
||||
encodeURIComponent((outFile + ".png").match(/[^/\\]+$/i)![0])
|
||||
)
|
||||
: outFile.replace(
|
||||
/([^/\\]+)$/i,
|
||||
encodeURIComponent(outFile.match(/[^/\\]+$/i)![0])
|
||||
)
|
||||
);
|
||||
} catch (error) {
|
||||
logit("❌ Error reading original image metadata", error);
|
||||
upscayl.kill();
|
||||
onError(error);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
upscayl.process.stderr.on("data", onData);
|
||||
upscayl.process.on("error", onError);
|
||||
upscayl.process.on("close", (code) => {
|
||||
// IF NOT FAILED
|
||||
if (!failed && !stopped) {
|
||||
// UPSCALE
|
||||
let upscayl2 = spawnUpscayl(
|
||||
"realesrgan",
|
||||
getDoubleUpscaleSecondPassArguments(
|
||||
isAlpha,
|
||||
outFile,
|
||||
isDefaultModel ? modelsPath : customModelsFolderPath ?? modelsPath,
|
||||
model,
|
||||
gpuId,
|
||||
saveImageAs,
|
||||
scale
|
||||
),
|
||||
logit
|
||||
);
|
||||
|
||||
childProcesses.push(upscayl2);
|
||||
|
||||
upscayl2.process.stderr.on("data", (data) => {
|
||||
if (!mainWindow) return;
|
||||
// CONVERT DATA TO STRING
|
||||
data = data.toString();
|
||||
// SEND UPSCAYL PROGRESS TO RENDERER
|
||||
mainWindow.webContents.send(commands.DOUBLE_UPSCAYL_PROGRESS, data);
|
||||
// IF PROGRESS HAS ERROR, UPSCAYL FAILED
|
||||
if (data.includes("invalid gpu") || data.includes("failed")) {
|
||||
upscayl2.kill();
|
||||
failed2 = true;
|
||||
}
|
||||
});
|
||||
upscayl2.process.on("error", (data) => {
|
||||
if (!mainWindow) return;
|
||||
data.toString();
|
||||
// SEND UPSCAYL PROGRESS TO RENDERER
|
||||
mainWindow.webContents.send(commands.DOUBLE_UPSCAYL_PROGRESS, data);
|
||||
// SET FAILED TO TRUE
|
||||
failed2 = true;
|
||||
upscayl2.kill();
|
||||
return;
|
||||
});
|
||||
upscayl2.process.on("close", onClose2);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
//------------------------Auto-Update Code-----------------------------//
|
||||
autoUpdater.autoInstallOnAppQuit = false;
|
||||
|
||||
autoUpdater.on("update-downloaded", (event) => {
|
||||
autoUpdater.autoInstallOnAppQuit = false;
|
||||
const dialogOpts: MessageBoxOptions = {
|
||||
type: "info",
|
||||
buttons: ["Install update", "No Thanks"],
|
||||
title: "New Upscayl Update",
|
||||
message: event.releaseName as string,
|
||||
detail:
|
||||
"A new version has been downloaded. Restart the application to apply the updates.",
|
||||
};
|
||||
logit("✅ Update Downloaded");
|
||||
dialog.showMessageBox(dialogOpts).then((returnValue) => {
|
||||
if (returnValue.response === 0) {
|
||||
autoUpdater.quitAndInstall();
|
||||
} else {
|
||||
logit("🚫 Update Installation Cancelled");
|
||||
}
|
||||
});
|
||||
});
|
||||
autoUpdater.on("update-downloaded", autoUpdate);
|
||||
|
128
electron/main-window.ts
Normal file
128
electron/main-window.ts
Normal file
@ -0,0 +1,128 @@
|
||||
import { BrowserWindow, shell } from "electron";
|
||||
import { getPlatform } from "./utils/get-device-specs";
|
||||
import { join } from "path";
|
||||
import COMMAND from "./constants/commands";
|
||||
import {
|
||||
overwrite,
|
||||
setCustomModelsFolderPath,
|
||||
setFolderPath,
|
||||
setImagePath,
|
||||
setOutputFolderPath,
|
||||
setOverwrite,
|
||||
setQuality,
|
||||
setSaveOutputFolder,
|
||||
} from "./utils/config-variables";
|
||||
import electronIsDev from "electron-is-dev";
|
||||
|
||||
let mainWindow: BrowserWindow | null;
|
||||
|
||||
const createMainWindow = () => {
|
||||
mainWindow = new BrowserWindow({
|
||||
icon: join(__dirname, "build", "icon.png"),
|
||||
width: 1300,
|
||||
height: 940,
|
||||
minHeight: 500,
|
||||
minWidth: 500,
|
||||
show: false,
|
||||
backgroundColor: "#171717",
|
||||
webPreferences: {
|
||||
nodeIntegration: true,
|
||||
nodeIntegrationInWorker: true,
|
||||
webSecurity: false,
|
||||
preload: join(__dirname, "preload.js"),
|
||||
},
|
||||
titleBarStyle: getPlatform() === "mac" ? "hiddenInset" : "default",
|
||||
});
|
||||
|
||||
const url = electronIsDev
|
||||
? "http://localhost:8000"
|
||||
: (new URL("file:///").pathname = join(
|
||||
__dirname,
|
||||
"../renderer/out/index.html"
|
||||
)).toString();
|
||||
mainWindow.loadURL(url);
|
||||
|
||||
mainWindow.webContents.setWindowOpenHandler(({ url }) => {
|
||||
shell.openExternal(url);
|
||||
return { action: "deny" };
|
||||
});
|
||||
|
||||
mainWindow.once("ready-to-show", () => {
|
||||
if (!mainWindow) return;
|
||||
mainWindow.show();
|
||||
});
|
||||
|
||||
// GET LAST IMAGE PATH TO LOCAL STORAGE
|
||||
mainWindow.webContents
|
||||
.executeJavaScript('localStorage.getItem("lastImagePath");', true)
|
||||
.then((lastImagePath: string | null) => {
|
||||
if (lastImagePath && lastImagePath.length > 0) {
|
||||
setImagePath(lastImagePath);
|
||||
}
|
||||
});
|
||||
// GET LAST FOLDER PATH TO LOCAL STORAGE
|
||||
mainWindow.webContents
|
||||
.executeJavaScript('localStorage.getItem("lastFolderPath");', true)
|
||||
.then((lastFolderPath: string | null) => {
|
||||
if (lastFolderPath && lastFolderPath.length > 0) {
|
||||
setFolderPath(lastFolderPath);
|
||||
}
|
||||
});
|
||||
// GET LAST CUSTOM MODELS FOLDER PATH TO LOCAL STORAGE
|
||||
mainWindow.webContents
|
||||
.executeJavaScript(
|
||||
'localStorage.getItem("lastCustomModelsFolderPath");',
|
||||
true
|
||||
)
|
||||
.then((lastCustomModelsFolderPath: string | null) => {
|
||||
if (lastCustomModelsFolderPath && lastCustomModelsFolderPath.length > 0) {
|
||||
setCustomModelsFolderPath(lastCustomModelsFolderPath);
|
||||
}
|
||||
});
|
||||
// GET LAST CUSTOM MODELS FOLDER PATH TO LOCAL STORAGE
|
||||
mainWindow.webContents
|
||||
.executeJavaScript('localStorage.getItem("lastOutputFolderPath");', true)
|
||||
.then((lastOutputFolderPath: string | null) => {
|
||||
if (lastOutputFolderPath && lastOutputFolderPath.length > 0) {
|
||||
setOutputFolderPath(lastOutputFolderPath);
|
||||
}
|
||||
});
|
||||
// GET LAST SAVE OUTPUT FOLDER (BOOLEAN) TO LOCAL STORAGE
|
||||
mainWindow.webContents
|
||||
.executeJavaScript('localStorage.getItem("rememberOutputFolder");', true)
|
||||
.then((lastSaveOutputFolder: boolean | null) => {
|
||||
if (lastSaveOutputFolder !== null) {
|
||||
setSaveOutputFolder(lastSaveOutputFolder);
|
||||
}
|
||||
});
|
||||
// GET IMAGE QUALITY (NUMBER) TO LOCAL STORAGE
|
||||
mainWindow.webContents
|
||||
.executeJavaScript('localStorage.getItem("quality");', true)
|
||||
.then((lastSavedQuality: string | null) => {
|
||||
if (lastSavedQuality !== null) {
|
||||
if (parseInt(lastSavedQuality) === 100) {
|
||||
setQuality(99);
|
||||
} else {
|
||||
setQuality(parseInt(lastSavedQuality));
|
||||
}
|
||||
}
|
||||
});
|
||||
// GET IMAGE QUALITY (NUMBER) TO LOCAL STORAGE
|
||||
mainWindow.webContents
|
||||
.executeJavaScript('localStorage.getItem("overwrite");', true)
|
||||
.then((lastSavedOverwrite: string | null) => {
|
||||
if (lastSavedOverwrite !== null) {
|
||||
setOverwrite(lastSavedOverwrite === "true");
|
||||
}
|
||||
});
|
||||
|
||||
mainWindow.webContents.send(COMMAND.OS, getPlatform());
|
||||
|
||||
mainWindow.setMenuBarVisibility(false);
|
||||
};
|
||||
|
||||
const getMainWindow = () => {
|
||||
return mainWindow;
|
||||
};
|
||||
|
||||
export { createMainWindow, getMainWindow };
|
@ -1,5 +1,5 @@
|
||||
import { ipcRenderer, contextBridge } from "electron";
|
||||
import { getPlatform } from "./getDeviceSpecs";
|
||||
import { getPlatform } from "./utils/get-device-specs";
|
||||
|
||||
// 'ipcRenderer' will be available in index.js with the method 'window.electron'
|
||||
contextBridge.exposeInMainWorld("electron", {
|
||||
|
@ -1,23 +0,0 @@
|
||||
import { spawn } from "child_process";
|
||||
import { execPath } from "./binaries";
|
||||
|
||||
function upscaylImage(
|
||||
inputFile: string,
|
||||
outFile: string,
|
||||
modelsPath: string,
|
||||
model: string
|
||||
) {
|
||||
// UPSCALE
|
||||
let upscayl = spawn(
|
||||
execPath("realesrgan"),
|
||||
["-i", inputFile, "-o", outFile, "-s", "4", "-m", modelsPath, "-n", model],
|
||||
{
|
||||
cwd: undefined,
|
||||
detached: false,
|
||||
}
|
||||
);
|
||||
|
||||
return upscayl;
|
||||
}
|
||||
|
||||
module.exports = { upscaylImage };
|
54
electron/utils/config-variables.ts
Normal file
54
electron/utils/config-variables.ts
Normal file
@ -0,0 +1,54 @@
|
||||
import { ChildProcessWithoutNullStreams } from "child_process";
|
||||
|
||||
export let imagePath: string | undefined = "";
|
||||
export let folderPath: string | undefined = undefined;
|
||||
export let customModelsFolderPath: string | undefined = undefined;
|
||||
export let outputFolderPath: string | undefined = undefined;
|
||||
export let saveOutputFolder = false;
|
||||
export let quality = 0;
|
||||
export let overwrite = false;
|
||||
export let stopped = false;
|
||||
export let childProcesses: {
|
||||
process: ChildProcessWithoutNullStreams;
|
||||
kill: () => boolean;
|
||||
}[] = [];
|
||||
|
||||
export function setImagePath(value: string | undefined): void {
|
||||
imagePath = value;
|
||||
}
|
||||
|
||||
export function setFolderPath(value: string | undefined): void {
|
||||
folderPath = value;
|
||||
}
|
||||
|
||||
export function setCustomModelsFolderPath(value: string | undefined): void {
|
||||
customModelsFolderPath = value;
|
||||
}
|
||||
|
||||
// SETTERS
|
||||
export function setOutputFolderPath(value: string | undefined): void {
|
||||
outputFolderPath = value;
|
||||
}
|
||||
|
||||
export function setSaveOutputFolder(value: boolean): void {
|
||||
saveOutputFolder = value;
|
||||
}
|
||||
|
||||
export function setQuality(value: number): void {
|
||||
quality = value;
|
||||
}
|
||||
|
||||
export function setOverwrite(value: boolean): void {
|
||||
overwrite = value;
|
||||
}
|
||||
|
||||
export function setStopped(value: boolean): void {
|
||||
stopped = value;
|
||||
}
|
||||
|
||||
export function setChildProcesses(value: {
|
||||
process: ChildProcessWithoutNullStreams;
|
||||
kill: () => boolean;
|
||||
}): void {
|
||||
childProcesses.push(value);
|
||||
}
|
47
electron/utils/convert-and-scale.ts
Normal file
47
electron/utils/convert-and-scale.ts
Normal file
@ -0,0 +1,47 @@
|
||||
import sharp from "sharp";
|
||||
import logit from "./logit";
|
||||
import { getMainWindow } from "../main-window";
|
||||
import { quality } from "./config-variables";
|
||||
|
||||
const convertAndScale = async (
|
||||
originalImagePath: string,
|
||||
upscaledImagePath: string,
|
||||
processedImagePath: string,
|
||||
scale: string,
|
||||
saveImageAs: string,
|
||||
onError: (error: any) => void
|
||||
) => {
|
||||
const mainWindow = getMainWindow();
|
||||
|
||||
const originalImage = await sharp(originalImagePath).metadata();
|
||||
if (!mainWindow || !originalImage) {
|
||||
throw new Error("Could not grab the original image!");
|
||||
}
|
||||
// Resize the image to the scale
|
||||
const newImage = sharp(upscaledImagePath)
|
||||
.resize(
|
||||
originalImage.width && originalImage.width * parseInt(scale),
|
||||
originalImage.height && originalImage.height * parseInt(scale)
|
||||
)
|
||||
.withMetadata(); // Keep metadata
|
||||
// Change the output according to the saveImageAs
|
||||
if (saveImageAs === "png") {
|
||||
newImage.png({ quality: 100 - quality });
|
||||
} else if (saveImageAs === "jpg") {
|
||||
console.log("Quality: ", quality);
|
||||
newImage.jpeg({ quality: 100 - quality });
|
||||
}
|
||||
// Save the image
|
||||
const buffer = await newImage.toBuffer();
|
||||
sharp(buffer)
|
||||
.toFile(processedImagePath)
|
||||
.then(() => {
|
||||
logit("✅ Done converting to: ", upscaledImagePath);
|
||||
})
|
||||
.catch((error) => {
|
||||
logit("❌ Error converting to: ", saveImageAs, error);
|
||||
onError(error);
|
||||
});
|
||||
};
|
||||
|
||||
export default convertAndScale;
|
@ -1,4 +1,4 @@
|
||||
import { getPlatform } from "../getDeviceSpecs";
|
||||
import { getPlatform } from "./get-device-specs";
|
||||
const slash: string = getPlatform() === "win" ? "\\" : "/";
|
||||
|
||||
export const getSingleImageArguments = (
|
||||
@ -137,30 +137,3 @@ export const getBatchArguments = (
|
||||
saveImageAs,
|
||||
];
|
||||
};
|
||||
|
||||
// ! REDUNDANT
|
||||
export const getBatchSharpenArguments = (
|
||||
inputDir: string,
|
||||
outputDir: string,
|
||||
modelsPath: string,
|
||||
model: string,
|
||||
gpuId: string,
|
||||
saveImageAs: string,
|
||||
scale: string
|
||||
) => {
|
||||
return [
|
||||
"-i",
|
||||
inputDir,
|
||||
"-o",
|
||||
outputDir,
|
||||
"-s",
|
||||
scale,
|
||||
"-x",
|
||||
"-m",
|
||||
modelsPath + slash + model,
|
||||
gpuId ? "-g" : "",
|
||||
gpuId ? gpuId : "",
|
||||
"-f",
|
||||
saveImageAs,
|
||||
];
|
||||
};
|
56
electron/utils/get-models.ts
Normal file
56
electron/utils/get-models.ts
Normal file
@ -0,0 +1,56 @@
|
||||
import fs from "fs";
|
||||
import logit from "./logit";
|
||||
import { MessageBoxOptions, dialog } from "electron";
|
||||
|
||||
const getModels = (folderPath: string | undefined) => {
|
||||
let models: string[] = [];
|
||||
let isValid = false;
|
||||
|
||||
if (!folderPath) {
|
||||
logit("❌ Invalid Custom Model Folder Detected");
|
||||
const options: MessageBoxOptions = {
|
||||
type: "error",
|
||||
title: "Invalid Folder",
|
||||
message:
|
||||
"The selected folder does not contain valid model files. Make sure you select the folder that ONLY contains '.param' and '.bin' files.",
|
||||
buttons: ["OK"],
|
||||
};
|
||||
dialog.showMessageBoxSync(options);
|
||||
return null;
|
||||
}
|
||||
|
||||
// READ CUSTOM MODELS FOLDER
|
||||
fs.readdirSync(folderPath).forEach((file) => {
|
||||
// log.log("Files in Folder: ", file);
|
||||
if (
|
||||
file.endsWith(".param") ||
|
||||
file.endsWith(".PARAM") ||
|
||||
file.endsWith(".bin") ||
|
||||
file.endsWith(".BIN")
|
||||
) {
|
||||
isValid = true;
|
||||
const modelName = file.substring(0, file.lastIndexOf(".")) || file;
|
||||
if (!models.includes(modelName)) {
|
||||
models.push(modelName);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (!isValid) {
|
||||
logit("❌ Invalid Custom Model Folder Detected");
|
||||
const options: MessageBoxOptions = {
|
||||
type: "error",
|
||||
title: "Invalid Folder",
|
||||
message:
|
||||
"The selected folder does not contain valid model files. Make sure you select the folder that ONLY contains '.param' and '.bin' files.",
|
||||
buttons: ["OK"],
|
||||
};
|
||||
dialog.showMessageBoxSync(options);
|
||||
return null;
|
||||
}
|
||||
|
||||
logit("🔎 Detected Custom Models: ", models);
|
||||
return models;
|
||||
};
|
||||
|
||||
export default getModels;
|
@ -1,14 +1,14 @@
|
||||
/*
|
||||
appRootDir is the resources directory inside the unpacked electron app temp directory.
|
||||
resources contains app.asar file, that contains the main and renderer files.
|
||||
We're putting resources/{os}/bin from project inside resources/bin of electron. Same for the models directory as well.
|
||||
*/
|
||||
|
||||
import { join, dirname, resolve } from "path";
|
||||
import { getPlatform } from "./getDeviceSpecs";
|
||||
import { getPlatform } from "./get-device-specs";
|
||||
import isDev from "electron-is-dev";
|
||||
import { app } from "electron";
|
||||
|
||||
/**
|
||||
* appRootDir is the resources directory inside the unpacked electron app temp directory.
|
||||
* resources contains app.asar file, that contains the main and renderer files.
|
||||
* We're putting resources/{os}/bin from project inside resources/bin of electron.
|
||||
* Same for the models directory as well.
|
||||
*/
|
||||
const appRootDir = app.getAppPath();
|
||||
|
||||
const binariesPath = isDev
|
12
electron/utils/logit.ts
Normal file
12
electron/utils/logit.ts
Normal file
@ -0,0 +1,12 @@
|
||||
import log from "electron-log";
|
||||
import COMMAND from "../constants/commands";
|
||||
import { getMainWindow } from "../main-window";
|
||||
|
||||
const logit = (...args: any) => {
|
||||
const mainWindow = getMainWindow();
|
||||
if (!mainWindow) return;
|
||||
log.log(...args);
|
||||
mainWindow.webContents.send(COMMAND.LOG, args.join(" "));
|
||||
};
|
||||
|
||||
export default logit;
|
4
electron/utils/slash.ts
Normal file
4
electron/utils/slash.ts
Normal file
@ -0,0 +1,4 @@
|
||||
import { getPlatform } from "./get-device-specs";
|
||||
|
||||
const slash: string = getPlatform() === "win" ? "\\" : "/";
|
||||
export default slash;
|
@ -1,5 +1,5 @@
|
||||
import { spawn } from "child_process";
|
||||
import { execPath } from "./binaries";
|
||||
import { execPath } from "./get-resource-paths";
|
||||
|
||||
export const spawnUpscayl = (
|
||||
binaryName: string,
|
@ -1,5 +1,5 @@
|
||||
import React from "react";
|
||||
import commands from "../../../electron/commands";
|
||||
import commands from "../../../electron/constants/commands";
|
||||
|
||||
type CustomModelsFolderSelectProps = {
|
||||
customModelsPath: string;
|
||||
|
@ -12,7 +12,7 @@ const ToggleOverwrite = ({ overwrite, setOverwrite }: ToggleOverwriteProps) => {
|
||||
} else {
|
||||
const currentlySavedOverwrite = localStorage.getItem("overwrite");
|
||||
if (currentlySavedOverwrite) {
|
||||
setOverwrite(JSON.parse(currentlySavedOverwrite));
|
||||
setOverwrite(currentlySavedOverwrite === "true");
|
||||
}
|
||||
}
|
||||
}, []);
|
||||
|
@ -176,7 +176,7 @@ function LeftPaneImageSteps({
|
||||
handleModelChange(e);
|
||||
setCurrentModel({ label: e.label, value: e.value });
|
||||
}}
|
||||
className="react-select-container"
|
||||
className="react-select-container active:w-full focus:w-full hover:w-full transition-all"
|
||||
classNamePrefix="react-select"
|
||||
value={currentModel}
|
||||
/>
|
||||
|
@ -20,7 +20,6 @@ const firebaseConfig = {
|
||||
// Initialize Firebase
|
||||
const app = initializeApp(firebaseConfig);
|
||||
export const db = getFirestore(app);
|
||||
console.log("🚀 => file: firebase.ts:23 => db:", db);
|
||||
|
||||
const createCollection = <T = DocumentData>(collectionName: string) => {
|
||||
return collection(db, collectionName) as CollectionReference<T>;
|
||||
|
@ -1,6 +1,6 @@
|
||||
"use client";
|
||||
import { useState, useEffect, useCallback } from "react";
|
||||
import commands from "../../electron/commands";
|
||||
import COMMAND from "../../electron/constants/commands";
|
||||
import { ReactCompareSlider } from "react-compare-slider";
|
||||
import Header from "../components/Header";
|
||||
import Footer from "../components/Footer";
|
||||
@ -89,7 +89,7 @@ const Home = () => {
|
||||
};
|
||||
|
||||
window.electron.on(
|
||||
commands.OS,
|
||||
COMMAND.OS,
|
||||
(_, data: "linux" | "mac" | "win" | undefined) => {
|
||||
if (data) {
|
||||
setOs(data);
|
||||
@ -98,21 +98,21 @@ const Home = () => {
|
||||
);
|
||||
|
||||
// LOG
|
||||
window.electron.on(commands.LOG, (_, data: string) => {
|
||||
window.electron.on(COMMAND.LOG, (_, data: string) => {
|
||||
logit(`🐞 BACKEND REPORTED: `, data);
|
||||
});
|
||||
|
||||
window.electron.on(commands.SCALING_AND_CONVERTING, (_, data: string) => {
|
||||
window.electron.on(COMMAND.SCALING_AND_CONVERTING, (_, data: string) => {
|
||||
setProgress("Processing the image...");
|
||||
});
|
||||
|
||||
window.electron.on(commands.UPSCAYL_ERROR, (_, data: string) => {
|
||||
window.electron.on(COMMAND.UPSCAYL_ERROR, (_, data: string) => {
|
||||
alert(data);
|
||||
resetImagePaths();
|
||||
});
|
||||
|
||||
// UPSCAYL PROGRESS
|
||||
window.electron.on(commands.UPSCAYL_PROGRESS, (_, data: string) => {
|
||||
window.electron.on(COMMAND.UPSCAYL_PROGRESS, (_, data: string) => {
|
||||
if (data.length > 0 && data.length < 10) {
|
||||
setProgress(data);
|
||||
} else if (data.includes("converting")) {
|
||||
@ -123,7 +123,7 @@ const Home = () => {
|
||||
});
|
||||
|
||||
// FOLDER UPSCAYL PROGRESS
|
||||
window.electron.on(commands.FOLDER_UPSCAYL_PROGRESS, (_, data: string) => {
|
||||
window.electron.on(COMMAND.FOLDER_UPSCAYL_PROGRESS, (_, data: string) => {
|
||||
if (data.length > 0 && data.length < 10) {
|
||||
setProgress(data);
|
||||
}
|
||||
@ -132,7 +132,7 @@ const Home = () => {
|
||||
});
|
||||
|
||||
// DOUBLE UPSCAYL PROGRESS
|
||||
window.electron.on(commands.DOUBLE_UPSCAYL_PROGRESS, (_, data: string) => {
|
||||
window.electron.on(COMMAND.DOUBLE_UPSCAYL_PROGRESS, (_, data: string) => {
|
||||
if (data.length > 0 && data.length < 10) {
|
||||
if (data === "0.00%") {
|
||||
setDoubleUpscaylCounter(doubleUpscaylCounter + 1);
|
||||
@ -144,7 +144,7 @@ const Home = () => {
|
||||
});
|
||||
|
||||
// UPSCAYL DONE
|
||||
window.electron.on(commands.UPSCAYL_DONE, (_, data: string) => {
|
||||
window.electron.on(COMMAND.UPSCAYL_DONE, (_, data: string) => {
|
||||
setProgress("");
|
||||
setTimeout(() => setUpscaledImagePath(data), 500);
|
||||
logit("upscaledImagePath: ", data);
|
||||
@ -152,42 +152,39 @@ const Home = () => {
|
||||
});
|
||||
|
||||
// FOLDER UPSCAYL DONE
|
||||
window.electron.on(commands.FOLDER_UPSCAYL_DONE, (_, data: string) => {
|
||||
window.electron.on(COMMAND.FOLDER_UPSCAYL_DONE, (_, data: string) => {
|
||||
setProgress("");
|
||||
setUpscaledBatchFolderPath(data);
|
||||
logit(`💯 FOLDER_UPSCAYL_DONE: `, data);
|
||||
});
|
||||
|
||||
// DOUBLE UPSCAYL DONE
|
||||
window.electron.on(commands.DOUBLE_UPSCAYL_DONE, (_, data: string) => {
|
||||
window.electron.on(COMMAND.DOUBLE_UPSCAYL_DONE, (_, data: string) => {
|
||||
setProgress("");
|
||||
setTimeout(() => setUpscaledImagePath(data), 500);
|
||||
setDoubleUpscaylCounter(0);
|
||||
setUpscaledImagePath(data);
|
||||
logit(`💯 DOUBLE_UPSCAYL_DONE: `, data);
|
||||
});
|
||||
|
||||
// CUSTOM FOLDER LISTENER
|
||||
window.electron.on(
|
||||
commands.CUSTOM_MODEL_FILES_LIST,
|
||||
(_, data: string[]) => {
|
||||
logit(`📜 CUSTOM_MODEL_FILES_LIST: `, data);
|
||||
const newModelOptions = data.map((model) => {
|
||||
return {
|
||||
value: model,
|
||||
label: model,
|
||||
};
|
||||
});
|
||||
window.electron.on(COMMAND.CUSTOM_MODEL_FILES_LIST, (_, data: string[]) => {
|
||||
logit(`📜 CUSTOM_MODEL_FILES_LIST: `, data);
|
||||
const newModelOptions = data.map((model) => {
|
||||
return {
|
||||
value: model,
|
||||
label: model,
|
||||
};
|
||||
});
|
||||
|
||||
// Add newModelsList to modelOptions and remove duplicates
|
||||
const combinedModelOptions = [...modelOptions, ...newModelOptions];
|
||||
const uniqueModelOptions = combinedModelOptions.filter(
|
||||
// Check if any model in the array appears more than once
|
||||
(model, index, array) =>
|
||||
array.findIndex((t) => t.value === model.value) === index
|
||||
);
|
||||
setModelOptions(uniqueModelOptions);
|
||||
}
|
||||
);
|
||||
// Add newModelsList to modelOptions and remove duplicates
|
||||
const combinedModelOptions = [...modelOptions, ...newModelOptions];
|
||||
const uniqueModelOptions = combinedModelOptions.filter(
|
||||
// Check if any model in the array appears more than once
|
||||
(model, index, array) =>
|
||||
array.findIndex((t) => t.value === model.value) === index
|
||||
);
|
||||
setModelOptions(uniqueModelOptions);
|
||||
});
|
||||
if (!localStorage.getItem("upscaylCloudModalShown")) {
|
||||
logit("⚙️ upscayl cloud show to true");
|
||||
localStorage.setItem("upscaylCloudModalShown", "true");
|
||||
@ -201,7 +198,7 @@ const Home = () => {
|
||||
);
|
||||
|
||||
if (customModelsPath !== null) {
|
||||
window.electron.send(commands.GET_MODELS_LIST, customModelsPath);
|
||||
window.electron.send(COMMAND.GET_MODELS_LIST, customModelsPath);
|
||||
logit("🎯 GET_MODELS_LIST: ", customModelsPath);
|
||||
}
|
||||
}, []);
|
||||
@ -210,6 +207,16 @@ const Home = () => {
|
||||
const rememberOutputFolder = localStorage.getItem("rememberOutputFolder");
|
||||
const lastOutputFolderPath = localStorage.getItem("lastOutputFolderPath");
|
||||
|
||||
// GET OVERWRITE
|
||||
if (!localStorage.getItem("overwrite")) {
|
||||
localStorage.setItem("overwrite", JSON.stringify(overwrite));
|
||||
} else {
|
||||
const currentlySavedOverwrite = localStorage.getItem("overwrite");
|
||||
if (currentlySavedOverwrite) {
|
||||
setOverwrite(currentlySavedOverwrite === "true");
|
||||
}
|
||||
}
|
||||
|
||||
if (rememberOutputFolder === "true") {
|
||||
setOutputPath(lastOutputFolderPath);
|
||||
} else {
|
||||
@ -267,7 +274,7 @@ const Home = () => {
|
||||
const selectImageHandler = async () => {
|
||||
resetImagePaths();
|
||||
|
||||
var path = await window.electron.invoke(commands.SELECT_FILE);
|
||||
var path = await window.electron.invoke(COMMAND.SELECT_FILE);
|
||||
|
||||
if (path !== null) {
|
||||
logit("🖼 Selected Image Path: ", path);
|
||||
@ -281,7 +288,7 @@ const Home = () => {
|
||||
const selectFolderHandler = async () => {
|
||||
resetImagePaths();
|
||||
|
||||
var path = await window.electron.invoke(commands.SELECT_FOLDER);
|
||||
var path = await window.electron.invoke(COMMAND.SELECT_FOLDER);
|
||||
|
||||
if (path !== null) {
|
||||
logit("🖼 Selected Folder Path: ", path);
|
||||
@ -319,7 +326,7 @@ const Home = () => {
|
||||
|
||||
const openFolderHandler = (e) => {
|
||||
logit("📂 OPEN_FOLDER: ", upscaledBatchFolderPath);
|
||||
window.electron.send(commands.OPEN_FOLDER, upscaledBatchFolderPath);
|
||||
window.electron.send(COMMAND.OPEN_FOLDER, upscaledBatchFolderPath);
|
||||
};
|
||||
|
||||
const handleDrop = (e) => {
|
||||
@ -380,7 +387,7 @@ const Home = () => {
|
||||
};
|
||||
|
||||
const outputHandler = async () => {
|
||||
var path = await window.electron.invoke(commands.SELECT_FOLDER);
|
||||
var path = await window.electron.invoke(COMMAND.SELECT_FOLDER);
|
||||
if (path !== null) {
|
||||
logit("🗂 Setting Output Path: ", path);
|
||||
setOutputPath(path);
|
||||
@ -405,7 +412,7 @@ const Home = () => {
|
||||
setProgress("Hold on...");
|
||||
|
||||
if (doubleUpscayl) {
|
||||
window.electron.send(commands.DOUBLE_UPSCAYL, {
|
||||
window.electron.send(COMMAND.DOUBLE_UPSCAYL, {
|
||||
imagePath,
|
||||
outputPath,
|
||||
model,
|
||||
@ -416,7 +423,7 @@ const Home = () => {
|
||||
logit("🏁 DOUBLE_UPSCAYL");
|
||||
} else if (batchMode) {
|
||||
setDoubleUpscayl(false);
|
||||
window.electron.send(commands.FOLDER_UPSCAYL, {
|
||||
window.electron.send(COMMAND.FOLDER_UPSCAYL, {
|
||||
scaleFactor,
|
||||
batchFolderPath,
|
||||
outputPath,
|
||||
@ -427,7 +434,7 @@ const Home = () => {
|
||||
});
|
||||
logit("🏁 FOLDER_UPSCAYL");
|
||||
} else {
|
||||
window.electron.send(commands.UPSCAYL, {
|
||||
window.electron.send(COMMAND.UPSCAYL, {
|
||||
scaleFactor,
|
||||
imagePath,
|
||||
outputPath,
|
||||
@ -457,7 +464,7 @@ const Home = () => {
|
||||
};
|
||||
|
||||
const stopHandler = () => {
|
||||
window.electron.send(commands.STOP);
|
||||
window.electron.send(COMMAND.STOP);
|
||||
logit("🛑 Stopping Upscayl");
|
||||
resetImagePaths();
|
||||
};
|
||||
@ -588,10 +595,7 @@ const Home = () => {
|
||||
hideZoomOptions={true}
|
||||
/>
|
||||
<img
|
||||
src={
|
||||
"file://" +
|
||||
`${upscaledImagePath ? upscaledImagePath : imagePath}`
|
||||
}
|
||||
src={"file:///" + imagePath}
|
||||
onLoad={(e: any) => {
|
||||
setDimensions({
|
||||
width: e.target.naturalWidth,
|
||||
|
@ -91,6 +91,9 @@
|
||||
.react-select-container {
|
||||
@apply w-40;
|
||||
}
|
||||
.full-width {
|
||||
@apply w-full;
|
||||
}
|
||||
.react-select-container .react-select__control {
|
||||
@apply rounded-btn h-12 cursor-pointer !border-0 !border-none !border-transparent bg-primary shadow-none;
|
||||
}
|
||||
@ -158,7 +161,19 @@
|
||||
}
|
||||
|
||||
[data-theme="upscayl"] .react-select-container .react-select__control {
|
||||
@apply rounded-btn h-10 ring-1 ring-slate-500 cursor-pointer !border-0 !border-none !border-transparent bg-primary shadow-none;
|
||||
@apply ring-1 ring-slate-500 rounded-btn h-10 cursor-pointer !border-0 !border-none !border-transparent bg-primary shadow-none;
|
||||
}
|
||||
|
||||
[data-theme="upscayl"] .react-select-container .react-select__single-value {
|
||||
@apply text-primary-content normal-case font-medium;
|
||||
}
|
||||
|
||||
[data-theme="upscayl"] .react-select-container .react-select__input-container {
|
||||
@apply text-xs text-primary-content normal-case font-medium;
|
||||
}
|
||||
|
||||
[data-theme="upscayl"] .react-select-container .react-select__menu {
|
||||
@apply rounded-lg bg-primary p-1 normal-case font-medium;
|
||||
}
|
||||
|
||||
.mac-titlebar {
|
||||
|
Loading…
x
Reference in New Issue
Block a user