From 5feabea516645e4f9ae277e415da824a8f44c8cb Mon Sep 17 00:00:00 2001
From: Nayam Amarshe <25067102+NayamAmarshe@users.noreply.github.com>
Date: Thu, 25 Apr 2024 00:59:51 +0530
Subject: [PATCH] Refactor code and remove config vars to fix ASCII path
decoding
---
common/decode-path.ts | 5 ++
common/get-directory-from-path.ts | 15 ++++
common/get-file-name.ts | 8 +++
common/sanitize-path.ts | 24 +++++++
common/types/types.d.ts | 13 ++--
electron/commands/batch-upscayl.ts | 43 +++---------
electron/commands/double-upscayl.ts | 50 +++++---------
electron/commands/image-upscayl.ts | 69 ++++++-------------
electron/utils/config-variables.ts | 15 +---
electron/utils/get-arguments.ts | 7 +-
package.json | 2 +-
renderer/atoms/userSettingsAtom.ts | 4 ++
renderer/components/settings-tab/index.tsx | 7 +-
.../upscayl-tab/config/LeftPaneImageSteps.tsx | 2 +-
renderer/pages/index.tsx | 37 ++++++----
15 files changed, 142 insertions(+), 159 deletions(-)
create mode 100644 common/decode-path.ts
create mode 100644 common/get-directory-from-path.ts
create mode 100644 common/get-file-name.ts
create mode 100644 common/sanitize-path.ts
diff --git a/common/decode-path.ts b/common/decode-path.ts
new file mode 100644
index 0000000..de29c8e
--- /dev/null
+++ b/common/decode-path.ts
@@ -0,0 +1,5 @@
+import path from "path";
+
+export default function decodePath(filePath: string): string {
+ return path.normalize(decodeURIComponent(filePath));
+}
diff --git a/common/get-directory-from-path.ts b/common/get-directory-from-path.ts
new file mode 100644
index 0000000..bc27318
--- /dev/null
+++ b/common/get-directory-from-path.ts
@@ -0,0 +1,15 @@
+export default function getDirectoryFromPath(filePath: string): string {
+ // Define the path separator based on the operating system
+ const separator = filePath.includes("/") ? "/" : "\\";
+
+ // Split the file path by the path separator
+ const pathParts = filePath.split(separator);
+
+ // Remove the last element to get the directory
+ pathParts.pop();
+
+ // Join the remaining parts back together to form the directory path
+ const directoryPath = pathParts.join(separator);
+
+ return directoryPath || "";
+}
diff --git a/common/get-file-name.ts b/common/get-file-name.ts
new file mode 100644
index 0000000..4a826f5
--- /dev/null
+++ b/common/get-file-name.ts
@@ -0,0 +1,8 @@
+export default function getFilenameFromPath(
+ path: string,
+ withExtension: boolean = true,
+) {
+ if (!path) return "";
+ if (withExtension) return path.split("/").slice(-1)[0];
+ return path.split("/").slice(-1)[0].split(".").slice(0, -1).join(".");
+}
diff --git a/common/sanitize-path.ts b/common/sanitize-path.ts
new file mode 100644
index 0000000..73cf1f1
--- /dev/null
+++ b/common/sanitize-path.ts
@@ -0,0 +1,24 @@
+export function sanitizePath(filePath: string) {
+ // const protocolPrefix = "file://";
+
+ // Normalize the file path to use forward slashes (for Windows)
+ const normalizedFilePath = filePath.replace(/\\/g, "/");
+
+ // Split the file path into segments based on forward slashes
+ const pathSegments = normalizedFilePath.split("/");
+
+ // Encode each segment separately using encodeURIComponent
+ const encodedPathSegments = pathSegments.map((segment) =>
+ encodeURIComponent(segment),
+ );
+
+ // Join the encoded segments back together with forward slashes
+ const encodedFilePath = encodedPathSegments.join("/");
+
+ // Combine the protocol prefix with the encoded file path to create the final file URL
+ const fileUrl = encodedFilePath;
+ console.log("🚀 => fileUrl:", fileUrl);
+
+ // Return the final Electron file URL
+ return fileUrl;
+}
diff --git a/common/types/types.d.ts b/common/types/types.d.ts
index 464d0d9..cf4c3eb 100644
--- a/common/types/types.d.ts
+++ b/common/types/types.d.ts
@@ -1,10 +1,12 @@
+import { ImageFormat } from "@electron/types/types";
+
export type ImageUpscaylPayload = {
imagePath: string;
- outputPath?: string;
+ outputPath: string;
scale: string;
model: string;
gpuId: string;
- saveImageAs: string;
+ saveImageAs: ImageFormat;
overwrite: boolean;
compression: string;
noImageProcessing: boolean;
@@ -14,11 +16,14 @@ export type ImageUpscaylPayload = {
export type DoubleUpscaylPayload = {
model: string;
+ /**
+ * The path to the image to upscale.
+ */
imagePath: string;
outputPath: string;
scale: string;
gpuId: string;
- saveImageAs: string;
+ saveImageAs: ImageFormat;
compression: string;
noImageProcessing: boolean;
customWidth: string;
@@ -30,7 +35,7 @@ export type BatchUpscaylPayload = {
outputPath: string;
model: string;
gpuId: string;
- saveImageAs: string;
+ saveImageAs: ImageFormat;
scale: string;
compression: string;
noImageProcessing: boolean;
diff --git a/electron/commands/batch-upscayl.ts b/electron/commands/batch-upscayl.ts
index 4d65342..9384ac6 100644
--- a/electron/commands/batch-upscayl.ts
+++ b/electron/commands/batch-upscayl.ts
@@ -3,9 +3,6 @@ import { getMainWindow } from "../main-window";
import {
childProcesses,
savedCustomModelsPath,
- rememberOutputFolder,
- setCompression,
- setNoImageProcessing,
setStopped,
stopped,
} from "../utils/config-variables";
@@ -16,55 +13,33 @@ import slash from "../utils/slash";
import { modelsPath } from "../utils/get-resource-paths";
import COMMAND from "../../common/commands";
import { BatchUpscaylPayload } from "../../common/types/types";
-import { ImageFormat } from "../types/types";
import showNotification from "../utils/show-notification";
import { DEFAULT_MODELS } from "../../common/models-list";
const batchUpscayl = async (event, payload: BatchUpscaylPayload) => {
const mainWindow = getMainWindow();
if (!mainWindow) return;
- // GET THE MODEL
- const model = payload.model;
- const gpuId = payload.gpuId;
- const saveImageAs = payload.saveImageAs as ImageFormat;
- console.log("PAYLOAD: ", payload);
- // GET THE IMAGE DIRECTORY
- let inputDir = payload.batchFolderPath;
- // GET THE OUTPUT DIRECTORY
- let outputFolderPath = payload.outputPath;
- if (rememberOutputFolder === true && outputFolderPath) {
- outputFolderPath = outputFolderPath;
- }
- // ! Don't do fetchLocalStorage() again, it causes the values to be reset
- setNoImageProcessing(payload.noImageProcessing);
- setCompression(parseInt(payload.compression));
-
- const isDefaultModel = DEFAULT_MODELS.includes(model);
const scale = payload.scale;
const useCustomWidth = payload.useCustomWidth;
const customWidth = useCustomWidth ? payload.customWidth : "";
-
+ const model = payload.model;
+ const gpuId = payload.gpuId;
+ const saveImageAs = payload.saveImageAs;
+ // GET THE IMAGE DIRECTORY
+ let inputDir = decodeURIComponent(payload.batchFolderPath);
+ // GET THE OUTPUT DIRECTORY
+ let outputFolderPath = decodeURIComponent(payload.outputPath);
const outputFolderName = `upscayl_${saveImageAs}_${model}_${
useCustomWidth ? `${customWidth}px` : `${scale}x`
}`;
-
outputFolderPath += slash + outputFolderName;
+ // CREATE THE OUTPUT DIRECTORY
if (!fs.existsSync(outputFolderPath)) {
fs.mkdirSync(outputFolderPath, { recursive: true });
}
- // Delete .DS_Store files
- fs.readdirSync(inputDir).forEach((file) => {
- if (
- file === ".DS_Store" ||
- file.toLowerCase() === "desktop.ini" ||
- file.startsWith(".")
- ) {
- logit("🗑️ Deleting .DS_Store file");
- fs.unlinkSync(inputDir + slash + file);
- }
- });
+ const isDefaultModel = DEFAULT_MODELS.includes(model);
// UPSCALE
const upscayl = spawnUpscayl(
diff --git a/electron/commands/double-upscayl.ts b/electron/commands/double-upscayl.ts
index 0696895..f196642 100644
--- a/electron/commands/double-upscayl.ts
+++ b/electron/commands/double-upscayl.ts
@@ -1,12 +1,8 @@
-import path, { parse } from "path";
+import { parse } from "path";
import { getMainWindow } from "../main-window";
import {
childProcesses,
savedCustomModelsPath,
- savedOutputPath,
- rememberOutputFolder,
- setCompression,
- setNoImageProcessing,
setStopped,
stopped,
} from "../utils/config-variables";
@@ -23,38 +19,31 @@ import { DoubleUpscaylPayload } from "../../common/types/types";
import { ImageFormat } from "../types/types";
import showNotification from "../utils/show-notification";
import { DEFAULT_MODELS } from "../../common/models-list";
-import getModelScale from "../../common/check-model-scale";
+import getFilenameFromPath from "../../common/get-file-name";
+import decodePath from "../../common/decode-path";
+import getDirectoryFromPath from "../../common/get-directory-from-path";
const doubleUpscayl = async (event, payload: DoubleUpscaylPayload) => {
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 (rememberOutputFolder === true && savedOutputPath) {
- outputDir = savedOutputPath;
- }
+ const compression = payload.compression;
+ const scale = parseInt(payload.scale) ** 2;
+ const useCustomWidth = payload.useCustomWidth;
+ const customWidth = useCustomWidth ? payload.customWidth : "";
+ const model = payload.model;
const gpuId = payload.gpuId as string;
const saveImageAs = payload.saveImageAs as ImageFormat;
-
- setNoImageProcessing(payload.noImageProcessing);
- setCompression(parseInt(payload.compression));
+ const imagePath = decodePath(payload.imagePath);
+ let inputDir = getDirectoryFromPath(imagePath);
+ let outputDir = decodePath(payload.outputPath);
+ const fullfileName = getFilenameFromPath(imagePath);
+ const fileName = parse(fullfileName).name;
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;
-
- const modelScale = getModelScale(model);
- const scale = parseInt(payload.scale) ** 2;
- const useCustomWidth = payload.useCustomWidth;
- const customWidth = useCustomWidth ? payload.customWidth : "";
-
const outFile =
outputDir +
slash +
@@ -69,7 +58,7 @@ const doubleUpscayl = async (event, payload: DoubleUpscaylPayload) => {
let upscayl = spawnUpscayl(
getDoubleUpscaleArguments({
inputDir,
- fullfileName,
+ fullfileName: decodeURIComponent(fullfileName),
outFile,
modelsPath: isDefaultModel
? modelsPath
@@ -128,13 +117,7 @@ const doubleUpscayl = async (event, payload: DoubleUpscaylPayload) => {
logit("💯 Done upscaling");
mainWindow.setProgressBar(-1);
- mainWindow.webContents.send(
- COMMAND.DOUBLE_UPSCAYL_DONE,
- outFile.replace(
- /([^/\\]+)$/i,
- encodeURIComponent(outFile.match(/[^/\\]+$/i)![0]),
- ),
- );
+ mainWindow.webContents.send(COMMAND.DOUBLE_UPSCAYL_DONE, outFile);
showNotification("Upscayled", "Image upscayled successfully!");
}
};
@@ -188,6 +171,7 @@ const doubleUpscayl = async (event, payload: DoubleUpscaylPayload) => {
saveImageAs,
scale: scale.toString(),
customWidth,
+ compression,
}),
logit,
);
diff --git a/electron/commands/image-upscayl.ts b/electron/commands/image-upscayl.ts
index ea7e3e2..9373877 100644
--- a/electron/commands/image-upscayl.ts
+++ b/electron/commands/image-upscayl.ts
@@ -2,14 +2,8 @@ import fs from "fs";
import { modelsPath } from "../utils/get-resource-paths";
import COMMAND from "../../common/commands";
import {
- savedCompression,
savedCustomModelsPath,
- savedBatchUpscaylFolderPath,
- savedOutputPath,
- rememberOutputFolder,
setChildProcesses,
- setCompression,
- setNoImageProcessing,
setStopped,
stopped,
} from "../utils/config-variables";
@@ -23,6 +17,9 @@ import { ImageUpscaylPayload } from "../../common/types/types";
import { ImageFormat } from "../types/types";
import showNotification from "../utils/show-notification";
import { DEFAULT_MODELS } from "../../common/models-list";
+import getFilenameFromPath from "../../common/get-file-name";
+import decodePath from "../../common/decode-path";
+import getDirectoryFromPath from "../../common/get-directory-from-path";
const imageUpscayl = async (event, payload: ImageUpscaylPayload) => {
const mainWindow = getMainWindow();
@@ -32,34 +29,20 @@ const imageUpscayl = async (event, payload: ImageUpscaylPayload) => {
return;
}
- setNoImageProcessing(payload.noImageProcessing);
- setCompression(parseInt(payload.compression));
-
// GET VARIABLES
+ const compression = payload.compression;
+ const scale = payload.scale;
+ const useCustomWidth = payload.useCustomWidth;
+ const customWidth = useCustomWidth ? payload.customWidth : "";
const model = payload.model as string;
const gpuId = payload.gpuId as string;
const saveImageAs = payload.saveImageAs as ImageFormat;
const overwrite = payload.overwrite as boolean;
- let inputDir = (payload.imagePath.match(/(.*)[\/\\]/)?.[1] || "") as string;
- let outputDir: string | undefined =
- savedBatchUpscaylFolderPath || (payload.outputPath as string);
- if (
- rememberOutputFolder === true &&
- savedOutputPath &&
- savedOutputPath?.length > 0
- ) {
- logit("🧠 Using saved output path");
- outputDir = savedOutputPath;
- }
- const isDefaultModel = DEFAULT_MODELS.includes(model);
- logit("Is Default Model? : ", isDefaultModel);
- const fullfileName = payload.imagePath.replace(/^.*[\\\/]/, "") as string;
- const fileName = parse(fullfileName).name;
- const fileExt = parse(fullfileName).ext;
- const useCustomWidth = payload.useCustomWidth;
- const customWidth = useCustomWidth ? payload.customWidth : "";
-
- const scale = payload.scale;
+ const imagePath = decodePath(payload.imagePath);
+ let inputDir = getDirectoryFromPath(imagePath);
+ let outputDir = decodePath(payload.outputPath);
+ const fileNameWithExt = getFilenameFromPath(imagePath);
+ const fileName = parse(fileNameWithExt).name;
const outFile =
outputDir +
@@ -71,17 +54,13 @@ const imageUpscayl = async (event, payload: ImageUpscaylPayload) => {
"." +
saveImageAs;
+ const isDefaultModel = DEFAULT_MODELS.includes(model);
+
// 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]),
- ),
- );
+ mainWindow.webContents.send(COMMAND.UPSCAYL_DONE, outFile);
} else {
logit(
"✅ Upscayl Variables: ",
@@ -90,18 +69,18 @@ const imageUpscayl = async (event, payload: ImageUpscaylPayload) => {
gpuId,
saveImageAs,
inputDir,
+ fileNameWithExt,
outputDir,
- fullfileName,
+ outFile,
fileName,
scale,
- outFile,
- compression: savedCompression,
+ compression,
}),
);
const upscayl = spawnUpscayl(
getSingleImageArguments({
- inputDir,
- fullfileName,
+ inputDir: decodeURIComponent(inputDir),
+ fileNameWithExt: decodeURIComponent(fileNameWithExt),
outFile,
modelsPath: isDefaultModel
? modelsPath
@@ -146,13 +125,7 @@ const imageUpscayl = async (event, payload: ImageUpscaylPayload) => {
// Free up memory
upscayl.kill();
mainWindow.setProgressBar(-1);
- mainWindow.webContents.send(
- COMMAND.UPSCAYL_DONE,
- outFile.replace(
- /([^/\\]+)$/i,
- encodeURIComponent(outFile.match(/[^/\\]+$/i)![0]),
- ),
- );
+ mainWindow.webContents.send(COMMAND.UPSCAYL_DONE, outFile);
showNotification("Upscayl", "Image upscayled successfully!");
}
};
diff --git a/electron/utils/config-variables.ts b/electron/utils/config-variables.ts
index a6c74e6..7fbef85 100644
--- a/electron/utils/config-variables.ts
+++ b/electron/utils/config-variables.ts
@@ -40,12 +40,6 @@ export function setRememberOutputFolder(value: boolean): void {
logit("💾 Updating Remember Output Folder: ", rememberOutputFolder);
}
-export let savedCompression = 0;
-export function setCompression(value: number): void {
- savedCompression = value;
- logit("📐 Updating Compression: ", savedCompression);
-}
-
export let stopped = false;
export let childProcesses: {
process: ChildProcessWithoutNullStreams;
@@ -148,14 +142,7 @@ export function fetchLocalStorage(): void {
setRememberOutputFolder(lastSaveOutputFolder);
}
});
- // GET IMAGE COMPRESSION (NUMBER) FROM LOCAL STORAGE
- mainWindow.webContents
- .executeJavaScript('localStorage.getItem("compression");', true)
- .then((lastSavedCompression: string | null) => {
- if (lastSavedCompression !== null) {
- setCompression(parseInt(lastSavedCompression));
- }
- });
+
// GET PROCESS IMAGE (BOOLEAN) FROM LOCAL STORAGE
mainWindow.webContents
.executeJavaScript('localStorage.getItem("noImageProcessing");', true)
diff --git a/electron/utils/get-arguments.ts b/electron/utils/get-arguments.ts
index 03bb572..6d3408e 100644
--- a/electron/utils/get-arguments.ts
+++ b/electron/utils/get-arguments.ts
@@ -5,7 +5,7 @@ const slash: string = getPlatform() === "win" ? "\\" : "/";
export const getSingleImageArguments = ({
inputDir,
- fullfileName,
+ fileNameWithExt,
outFile,
modelsPath,
model,
@@ -15,7 +15,7 @@ export const getSingleImageArguments = ({
customWidth,
}: {
inputDir: string;
- fullfileName: string;
+ fileNameWithExt: string;
outFile: string;
modelsPath: string;
model: string;
@@ -29,7 +29,7 @@ export const getSingleImageArguments = ({
return [
// INPUT IMAGE
"-i",
- inputDir + slash + fullfileName,
+ inputDir + slash + fileNameWithExt,
// OUTPUT IMAGE
"-o",
outFile,
@@ -109,6 +109,7 @@ export const getDoubleUpscaleSecondPassArguments = ({
saveImageAs: ImageFormat;
scale: string;
customWidth: string;
+ compression: string;
}) => {
const modelScale = (parseInt(getModelScale(model)) ** 2).toString();
let includeScale = modelScale !== scale && !customWidth;
diff --git a/package.json b/package.json
index a07ed71..bf876c6 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
{
"name": "upscayl",
"private": true,
- "version": "2.10.9",
+ "version": "2.11.0",
"productName": "Upscayl",
"author": {
"name": "Nayam Amarshe",
diff --git a/renderer/atoms/userSettingsAtom.ts b/renderer/atoms/userSettingsAtom.ts
index e3aacd9..f704eb9 100644
--- a/renderer/atoms/userSettingsAtom.ts
+++ b/renderer/atoms/userSettingsAtom.ts
@@ -10,6 +10,10 @@ export const scaleAtom = atomWithStorage