1
0
mirror of https://github.com/upscayl/upscayl.git synced 2024-11-30 18:24:27 +01:00

Add custom width

This commit is contained in:
Nayam Amarshe 2024-02-08 20:27:35 +05:30
parent f240cca433
commit bece033f4d
11 changed files with 220 additions and 125 deletions

View File

@ -3,12 +3,14 @@ import { getMainWindow } from "../main-window";
import { import {
childProcesses, childProcesses,
customModelsFolderPath, customModelsFolderPath,
customWidth,
noImageProcessing, noImageProcessing,
saveOutputFolder, saveOutputFolder,
setCompression, setCompression,
setNoImageProcessing, setNoImageProcessing,
setStopped, setStopped,
stopped, stopped,
useCustomWidth,
} from "../utils/config-variables"; } from "../utils/config-variables";
import logit from "../utils/logit"; import logit from "../utils/logit";
import { spawnUpscayl } from "../utils/spawn-upscayl"; import { spawnUpscayl } from "../utils/spawn-upscayl";
@ -47,11 +49,11 @@ const batchUpscayl = async (event, payload: BatchUpscaylPayload) => {
let initialScale = getModelScale(model); let initialScale = getModelScale(model);
const desiredScale = payload.scale as string; const desiredScale = useCustomWidth
? customWidth || payload.scale
: payload.scale;
const outputFolderName = `upscayl_${model}_x${ const outputFolderName = `upscayl_${model}_${noImageProcessing ? initialScale : desiredScale}${useCustomWidth ? "px_" : "x_"}`;
noImageProcessing ? initialScale : desiredScale
}`;
outputFolderPath += slash + outputFolderName; outputFolderPath += slash + outputFolderName;
if (!fs.existsSync(outputFolderPath)) { if (!fs.existsSync(outputFolderPath)) {
fs.mkdirSync(outputFolderPath, { recursive: true }); fs.mkdirSync(outputFolderPath, { recursive: true });
@ -78,9 +80,9 @@ const batchUpscayl = async (event, payload: BatchUpscaylPayload) => {
model, model,
gpuId, gpuId,
saveImageAs, saveImageAs,
initialScale initialScale,
), ),
logit logit,
); );
childProcesses.push(upscayl); childProcesses.push(upscayl);
@ -94,7 +96,7 @@ const batchUpscayl = async (event, payload: BatchUpscaylPayload) => {
data = data.toString(); data = data.toString();
mainWindow.webContents.send( mainWindow.webContents.send(
COMMAND.FOLDER_UPSCAYL_PROGRESS, COMMAND.FOLDER_UPSCAYL_PROGRESS,
data.toString() data.toString(),
); );
if (data.includes("invalid") || data.includes("failed")) { if (data.includes("invalid") || data.includes("failed")) {
logit("❌ INVALID GPU OR INVALID FILES IN FOLDER - FAILED"); logit("❌ INVALID GPU OR INVALID FILES IN FOLDER - FAILED");
@ -110,14 +112,14 @@ const batchUpscayl = async (event, payload: BatchUpscaylPayload) => {
mainWindow.setProgressBar(-1); mainWindow.setProgressBar(-1);
mainWindow.webContents.send( mainWindow.webContents.send(
COMMAND.FOLDER_UPSCAYL_PROGRESS, COMMAND.FOLDER_UPSCAYL_PROGRESS,
data.toString() data.toString(),
); );
failed = true; failed = true;
upscayl.kill(); upscayl.kill();
mainWindow && mainWindow &&
mainWindow.webContents.send( mainWindow.webContents.send(
COMMAND.UPSCAYL_ERROR, COMMAND.UPSCAYL_ERROR,
"Error upscaling image. Error: " + data "Error upscaling image. Error: " + data,
); );
return; return;
}; };
@ -133,7 +135,7 @@ const batchUpscayl = async (event, payload: BatchUpscaylPayload) => {
mainWindow.setProgressBar(-1); mainWindow.setProgressBar(-1);
mainWindow.webContents.send( mainWindow.webContents.send(
COMMAND.FOLDER_UPSCAYL_DONE, COMMAND.FOLDER_UPSCAYL_DONE,
outputFolderPath outputFolderPath,
); );
return; return;
} }
@ -148,19 +150,19 @@ const batchUpscayl = async (event, payload: BatchUpscaylPayload) => {
isAlpha isAlpha
? `${outputFolderPath}${slash}${removeFileExtension(file)}.png` ? `${outputFolderPath}${slash}${removeFileExtension(file)}.png`
: `${outputFolderPath}${slash}${removeFileExtension( : `${outputFolderPath}${slash}${removeFileExtension(
file file,
)}.${saveImageAs}`, )}.${saveImageAs}`,
`${outputFolderPath}${slash}${removeFileExtension( `${outputFolderPath}${slash}${removeFileExtension(
file file,
)}.${saveImageAs}`, )}.${saveImageAs}`,
desiredScale, desiredScale,
saveImageAs, saveImageAs,
isAlpha isAlpha,
); );
}); });
mainWindow.webContents.send( mainWindow.webContents.send(
COMMAND.FOLDER_UPSCAYL_DONE, COMMAND.FOLDER_UPSCAYL_DONE,
outputFolderPath outputFolderPath,
); );
showNotification("Upscayled", "Image upscayled successfully!"); showNotification("Upscayled", "Image upscayled successfully!");
} catch (error) { } catch (error) {
@ -170,7 +172,7 @@ const batchUpscayl = async (event, payload: BatchUpscaylPayload) => {
mainWindow.webContents.send( mainWindow.webContents.send(
COMMAND.UPSCAYL_ERROR, COMMAND.UPSCAYL_ERROR,
"Error processing (scaling and converting) the image. Please report this error on Upscayl GitHub Issues page.\n" + "Error processing (scaling and converting) the image. Please report this error on Upscayl GitHub Issues page.\n" +
error error,
); );
showNotification("Upscayl Failure", "Failed to upscale image!"); showNotification("Upscayl Failure", "Failed to upscale image!");
} }

View File

@ -3,6 +3,7 @@ import { getMainWindow } from "../main-window";
import { import {
childProcesses, childProcesses,
customModelsFolderPath, customModelsFolderPath,
customWidth,
noImageProcessing, noImageProcessing,
outputFolderPath, outputFolderPath,
saveOutputFolder, saveOutputFolder,
@ -10,6 +11,7 @@ import {
setNoImageProcessing, setNoImageProcessing,
setStopped, setStopped,
stopped, stopped,
useCustomWidth,
} from "../utils/config-variables"; } from "../utils/config-variables";
import slash from "../utils/slash"; import slash from "../utils/slash";
import { spawnUpscayl } from "../utils/spawn-upscayl"; import { spawnUpscayl } from "../utils/spawn-upscayl";
@ -55,7 +57,9 @@ const doubleUpscayl = async (event, payload: DoubleUpscaylPayload) => {
let initialScale = getModelScale(model); let initialScale = getModelScale(model);
const desiredScale = parseInt(payload.scale) * parseInt(payload.scale); const desiredScale = useCustomWidth
? customWidth || parseInt(payload.scale) * parseInt(payload.scale)
: parseInt(payload.scale) * parseInt(payload.scale);
const outFile = const outFile =
outputDir + outputDir +
@ -65,7 +69,7 @@ const doubleUpscayl = async (event, payload: DoubleUpscaylPayload) => {
(noImageProcessing (noImageProcessing
? parseInt(initialScale) * parseInt(initialScale) ? parseInt(initialScale) * parseInt(initialScale)
: desiredScale) + : desiredScale) +
"x_" + (useCustomWidth ? "px_" : "x_") +
model + model +
"." + "." +
saveImageAs; saveImageAs;
@ -80,9 +84,9 @@ const doubleUpscayl = async (event, payload: DoubleUpscaylPayload) => {
model, model,
gpuId, gpuId,
saveImageAs, saveImageAs,
initialScale initialScale,
), ),
logit logit,
); );
childProcesses.push(upscayl); childProcesses.push(upscayl);
@ -119,7 +123,7 @@ const doubleUpscayl = async (event, payload: DoubleUpscaylPayload) => {
mainWindow && mainWindow &&
mainWindow.webContents.send( mainWindow.webContents.send(
COMMAND.UPSCAYL_ERROR, COMMAND.UPSCAYL_ERROR,
"Error upscaling image. Error: " + data "Error upscaling image. Error: " + data,
); );
showNotification("Upscayl Failure", "Failed to upscale image!"); showNotification("Upscayl Failure", "Failed to upscale image!");
upscayl.kill(); upscayl.kill();
@ -140,12 +144,12 @@ const doubleUpscayl = async (event, payload: DoubleUpscaylPayload) => {
isAlpha isAlpha
? (outFile + ".png").replace( ? (outFile + ".png").replace(
/([^/\\]+)$/i, /([^/\\]+)$/i,
encodeURIComponent((outFile + ".png").match(/[^/\\]+$/i)![0]) encodeURIComponent((outFile + ".png").match(/[^/\\]+$/i)![0]),
) )
: outFile.replace( : outFile.replace(
/([^/\\]+)$/i, /([^/\\]+)$/i,
encodeURIComponent(outFile.match(/[^/\\]+$/i)![0]) encodeURIComponent(outFile.match(/[^/\\]+$/i)![0]),
) ),
); );
return; return;
} }
@ -157,15 +161,15 @@ const doubleUpscayl = async (event, payload: DoubleUpscaylPayload) => {
outFile, outFile,
desiredScale.toString(), desiredScale.toString(),
saveImageAs, saveImageAs,
isAlpha isAlpha,
); );
mainWindow.setProgressBar(-1); mainWindow.setProgressBar(-1);
mainWindow.webContents.send( mainWindow.webContents.send(
COMMAND.DOUBLE_UPSCAYL_DONE, COMMAND.DOUBLE_UPSCAYL_DONE,
outFile.replace( outFile.replace(
/([^/\\]+)$/i, /([^/\\]+)$/i,
encodeURIComponent(outFile.match(/[^/\\]+$/i)![0]) encodeURIComponent(outFile.match(/[^/\\]+$/i)![0]),
) ),
); );
if (isAlpha && saveImageAs === "jpg") { if (isAlpha && saveImageAs === "jpg") {
unlinkSync(outFile + ".png"); unlinkSync(outFile + ".png");
@ -177,7 +181,7 @@ const doubleUpscayl = async (event, payload: DoubleUpscaylPayload) => {
mainWindow.webContents.send( mainWindow.webContents.send(
COMMAND.UPSCAYL_ERROR, COMMAND.UPSCAYL_ERROR,
"Error processing (scaling and converting) the image. Please report this error on Upscayl GitHub Issues page.\n" + "Error processing (scaling and converting) the image. Please report this error on Upscayl GitHub Issues page.\n" +
error error,
); );
showNotification("Upscayl Failure", "Failed to upscale image!"); showNotification("Upscayl Failure", "Failed to upscale image!");
upscayl.kill(); upscayl.kill();
@ -199,9 +203,9 @@ const doubleUpscayl = async (event, payload: DoubleUpscaylPayload) => {
model, model,
gpuId, gpuId,
saveImageAs, saveImageAs,
initialScale initialScale,
), ),
logit logit,
); );
childProcesses.push(upscayl2); childProcesses.push(upscayl2);
@ -228,7 +232,7 @@ const doubleUpscayl = async (event, payload: DoubleUpscaylPayload) => {
mainWindow && mainWindow &&
mainWindow.webContents.send( mainWindow.webContents.send(
COMMAND.UPSCAYL_ERROR, COMMAND.UPSCAYL_ERROR,
"Error upscaling image. Error: " + data "Error upscaling image. Error: " + data,
); );
showNotification("Upscayl Failure", "Failed to upscale image!"); showNotification("Upscayl Failure", "Failed to upscale image!");
upscayl2.kill(); upscayl2.kill();

View File

@ -4,6 +4,7 @@ import COMMAND from "../../common/commands";
import { import {
compression, compression,
customModelsFolderPath, customModelsFolderPath,
customWidth,
folderPath, folderPath,
noImageProcessing, noImageProcessing,
outputFolderPath, outputFolderPath,
@ -13,6 +14,7 @@ import {
setNoImageProcessing, setNoImageProcessing,
setStopped, setStopped,
stopped, stopped,
useCustomWidth,
} from "../utils/config-variables"; } from "../utils/config-variables";
import convertAndScale from "../utils/convert-and-scale"; import convertAndScale from "../utils/convert-and-scale";
import { getSingleImageArguments } from "../utils/get-arguments"; import { getSingleImageArguments } from "../utils/get-arguments";
@ -62,7 +64,9 @@ const imageUpscayl = async (event, payload: ImageUpscaylPayload) => {
let initialScale = getModelScale(model); let initialScale = getModelScale(model);
const desiredScale = payload.scale; const desiredScale = useCustomWidth
? customWidth || payload.scale
: payload.scale;
const outFile = const outFile =
outputDir + outputDir +
@ -70,7 +74,7 @@ const imageUpscayl = async (event, payload: ImageUpscaylPayload) => {
fileName + fileName +
"_upscayl_" + "_upscayl_" +
(noImageProcessing ? initialScale : desiredScale) + (noImageProcessing ? initialScale : desiredScale) +
"x_" + (useCustomWidth ? "px_" : "x_") +
model + model +
"." + "." +
saveImageAs; saveImageAs;
@ -83,8 +87,8 @@ const imageUpscayl = async (event, payload: ImageUpscaylPayload) => {
COMMAND.UPSCAYL_DONE, COMMAND.UPSCAYL_DONE,
outFile.replace( outFile.replace(
/([^/\\]+)$/i, /([^/\\]+)$/i,
encodeURIComponent(outFile.match(/[^/\\]+$/i)![0]) encodeURIComponent(outFile.match(/[^/\\]+$/i)![0]),
) ),
); );
} else { } else {
logit( logit(
@ -101,7 +105,7 @@ const imageUpscayl = async (event, payload: ImageUpscaylPayload) => {
desiredScale, desiredScale,
outFile, outFile,
compression, compression,
}) }),
); );
const upscayl = spawnUpscayl( const upscayl = spawnUpscayl(
getSingleImageArguments( getSingleImageArguments(
@ -112,9 +116,9 @@ const imageUpscayl = async (event, payload: ImageUpscaylPayload) => {
model, model,
initialScale, initialScale,
gpuId, gpuId,
saveImageAs saveImageAs,
), ),
logit logit,
); );
setChildProcesses(upscayl); setChildProcesses(upscayl);
@ -157,8 +161,8 @@ const imageUpscayl = async (event, payload: ImageUpscaylPayload) => {
COMMAND.UPSCAYL_DONE, COMMAND.UPSCAYL_DONE,
outFile.replace( outFile.replace(
/([^/\\]+)$/i, /([^/\\]+)$/i,
encodeURIComponent(outFile.match(/[^/\\]+$/i)![0]) encodeURIComponent(outFile.match(/[^/\\]+$/i)![0]),
) ),
); );
return; return;
} }
@ -172,7 +176,7 @@ const imageUpscayl = async (event, payload: ImageUpscaylPayload) => {
outFile, outFile,
desiredScale, desiredScale,
saveImageAs, saveImageAs,
isAlpha isAlpha,
); );
// Remove the png file (default) if the saveImageAs is not png // Remove the png file (default) if the saveImageAs is not png
// fs.access( // fs.access(
@ -190,20 +194,20 @@ const imageUpscayl = async (event, payload: ImageUpscaylPayload) => {
COMMAND.UPSCAYL_DONE, COMMAND.UPSCAYL_DONE,
outFile.replace( outFile.replace(
/([^/\\]+)$/i, /([^/\\]+)$/i,
encodeURIComponent(outFile.match(/[^/\\]+$/i)![0]) encodeURIComponent(outFile.match(/[^/\\]+$/i)![0]),
) ),
); );
showNotification("Upscayl", "Image upscayled successfully!"); showNotification("Upscayl", "Image upscayled successfully!");
} catch (error) { } catch (error) {
logit( logit(
"❌ Error processing (scaling and converting) the image. Please report this error on GitHub.", "❌ Error processing (scaling and converting) the image. Please report this error on GitHub.",
error error,
); );
upscayl.kill(); upscayl.kill();
mainWindow.webContents.send( mainWindow.webContents.send(
COMMAND.UPSCAYL_ERROR, COMMAND.UPSCAYL_ERROR,
"Error processing (scaling and converting) the image. Please report this error on Upscayl GitHub Issues page.\n" + "Error processing (scaling and converting) the image. Please report this error on Upscayl GitHub Issues page.\n" +
error error,
); );
showNotification("Upscayl Failure", "Failed to upscale image!"); showNotification("Upscayl Failure", "Failed to upscale image!");
} }

View File

@ -15,6 +15,8 @@ export let childProcesses: {
}[] = []; }[] = [];
export let noImageProcessing: boolean = false; export let noImageProcessing: boolean = false;
export let turnOffNotifications: boolean = false; export let turnOffNotifications: boolean = false;
export let customWidth: string | null = null;
export let useCustomWidth: boolean = false;
export function setImagePath(value: string | undefined): void { export function setImagePath(value: string | undefined): void {
imagePath = value; imagePath = value;
@ -62,7 +64,7 @@ export function setChildProcesses(value: {
JSON.stringify({ JSON.stringify({
binary: childProcesses[0].process.spawnfile, binary: childProcesses[0].process.spawnfile,
args: childProcesses[0].process.spawnargs, args: childProcesses[0].process.spawnargs,
}) }),
); );
} }
@ -76,6 +78,16 @@ export function setTurnOffNotifications(value: boolean): void {
logit("🔕 Updating Turn Off Notifications: ", turnOffNotifications); logit("🔕 Updating Turn Off Notifications: ", turnOffNotifications);
} }
export function setCustomWidth(value: string | null): void {
customWidth = value;
logit("📏 Updating Custom Width: ", customWidth);
}
export function setUseCustomWidth(value: boolean): void {
useCustomWidth = value;
logit("📏 Updating Use Custom Width: ", useCustomWidth);
}
// LOCAL STORAGE // LOCAL STORAGE
export function fetchLocalStorage(): void { export function fetchLocalStorage(): void {
const mainWindow = getMainWindow(); const mainWindow = getMainWindow();
@ -101,7 +113,7 @@ export function fetchLocalStorage(): void {
mainWindow.webContents mainWindow.webContents
.executeJavaScript( .executeJavaScript(
'localStorage.getItem("lastCustomModelsFolderPath");', 'localStorage.getItem("lastCustomModelsFolderPath");',
true true,
) )
.then((lastCustomModelsFolderPath: string | null) => { .then((lastCustomModelsFolderPath: string | null) => {
if (lastCustomModelsFolderPath && lastCustomModelsFolderPath.length > 0) { if (lastCustomModelsFolderPath && lastCustomModelsFolderPath.length > 0) {
@ -149,4 +161,22 @@ export function fetchLocalStorage(): void {
setTurnOffNotifications(lastSaved === "true"); setTurnOffNotifications(lastSaved === "true");
} }
}); });
// GET CUSTOM WIDTH (STRING) FROM LOCAL STORAGE
mainWindow.webContents
.executeJavaScript('localStorage.getItem("customWidth");', true)
.then((lastSaved: string | null) => {
if (lastSaved !== null) {
setCustomWidth(lastSaved);
}
});
// GET USE CUSTOM WIDTH (BOOLEAN) FROM LOCAL STORAGE
mainWindow.webContents
.executeJavaScript('localStorage.getItem("useCustomWidth");', true)
.then((lastSaved: string | null) => {
if (lastSaved !== null) {
setUseCustomWidth(lastSaved === "true");
}
});
} }

View File

@ -1,7 +1,7 @@
import fs from "fs"; import fs from "fs";
import sharp, { FormatEnum, Metadata } from "sharp"; import sharp, { FormatEnum, Metadata } from "sharp";
import logit from "./logit"; import logit from "./logit";
import { compression } from "./config-variables"; import { compression, customWidth } from "./config-variables";
import { ImageFormat } from "./types"; import { ImageFormat } from "./types";
const convertAndScale = async ( const convertAndScale = async (
@ -10,9 +10,9 @@ const convertAndScale = async (
processedImagePath: string, processedImagePath: string,
scale: string, scale: string,
saveImageAs: ImageFormat, saveImageAs: ImageFormat,
isAlpha: boolean isAlpha: boolean,
) => { ) => {
if (!isAlpha && scale === "4" && compression === 0) { if (!isAlpha && !customWidth && scale === "4" && compression === 0) {
logit("Skipping compression for 4x scale and 0% compression"); logit("Skipping compression for 4x scale and 0% compression");
return; return;
} }
@ -28,7 +28,7 @@ const convertAndScale = async (
logit("🖼️ Checking if original image exists: ", originalImagePath); logit("🖼️ Checking if original image exists: ", originalImagePath);
if (err) { if (err) {
throw new Error( throw new Error(
"Could not grab the original image from the path provided! - " + err "Could not grab the original image from the path provided! - " + err,
); );
} }
}); });
@ -38,23 +38,6 @@ const convertAndScale = async (
} }
console.log("🚀 => originalImage:", originalImage); console.log("🚀 => originalImage:", originalImage);
// Resize the image to the scale
const newImage = sharp(upscaledImagePath, {
limitInputPixels: false,
})
.resize(
originalImage.width && originalImage.width * parseInt(scale),
originalImage.height && originalImage.height * parseInt(scale),
{
fit: "outside",
}
)
.withMetadata({
density: originalImage.density,
orientation: originalImage.orientation,
});
console.log("🚀 => newImage:", newImage);
// Convert compression percentage (0-100) to compressionLevel (0-9) // Convert compression percentage (0-100) to compressionLevel (0-9)
const compressionLevel = Math.round((compression / 100) * 9); const compressionLevel = Math.round((compression / 100) * 9);
@ -63,13 +46,33 @@ const convertAndScale = async (
JSON.stringify({ JSON.stringify({
originalWidth: originalImage.width, originalWidth: originalImage.width,
originalHeight: originalImage.height, originalHeight: originalImage.height,
customWidth,
scale, scale,
saveImageAs, saveImageAs,
compressionPercentage: compression, compressionPercentage: compression,
compressionLevel, compressionLevel,
}) }),
); );
// Resize the image to the scale
const newImage = sharp(upscaledImagePath, {
limitInputPixels: false,
})
.resize(
customWidth
? parseInt(customWidth)
: originalImage.width && originalImage.width * parseInt(scale),
customWidth
? null
: originalImage.height && originalImage.height * parseInt(scale),
)
.withMetadata({
density: originalImage.density,
orientation: originalImage.orientation,
});
console.log("🚀 => newImage:", newImage);
const buffer = await newImage const buffer = await newImage
.withMetadata({ .withMetadata({
density: originalImage.density, density: originalImage.density,

View File

@ -3,7 +3,7 @@ import { atomWithStorage } from "jotai/utils";
export const customModelsPathAtom = atomWithStorage<string | null>( export const customModelsPathAtom = atomWithStorage<string | null>(
"customModelsPath", "customModelsPath",
null null,
); );
export const scaleAtom = atomWithStorage<"2" | "3" | "4">("scale", "4"); export const scaleAtom = atomWithStorage<"2" | "3" | "4">("scale", "4");
export const batchModeAtom = atom<boolean>(false); export const batchModeAtom = atom<boolean>(false);
@ -12,17 +12,17 @@ export const progressAtom = atom<string>("");
export const rememberOutputFolderAtom = atomWithStorage<boolean>( export const rememberOutputFolderAtom = atomWithStorage<boolean>(
"rememberOutputFolder", "rememberOutputFolder",
false false,
); );
export const dontShowCloudModalAtom = atomWithStorage<boolean>( export const dontShowCloudModalAtom = atomWithStorage<boolean>(
"dontShowCloudModal", "dontShowCloudModal",
false false,
); );
export const noImageProcessingAtom = atomWithStorage<boolean>( export const noImageProcessingAtom = atomWithStorage<boolean>(
"noImageProcessing", "noImageProcessing",
false false,
); );
export const compressionAtom = atomWithStorage<number>("compression", 0); export const compressionAtom = atomWithStorage<number>("compression", 0);
@ -31,12 +31,22 @@ export const overwriteAtom = atomWithStorage("overwrite", false);
export const turnOffNotificationsAtom = atomWithStorage( export const turnOffNotificationsAtom = atomWithStorage(
"turnOffNotifications", "turnOffNotifications",
false false,
); );
export const viewTypeAtom = atomWithStorage<"slider" | "lens">( export const viewTypeAtom = atomWithStorage<"slider" | "lens">(
"viewType", "viewType",
"lens" "lens",
); );
export const lensSizeAtom = atomWithStorage<number>("lensSize", 100); export const lensSizeAtom = atomWithStorage<number>("lensSize", 100);
export const customWidthAtom = atomWithStorage<number | null>(
"customWidth",
null,
);
export const useCustomWidthAtom = atomWithStorage<boolean>(
"useCustomWidth",
false,
);

View File

@ -0,0 +1,51 @@
import { customWidthAtom, useCustomWidthAtom } from "@/atoms/userSettingsAtom";
import { useAtom } from "jotai";
import React, { useState } from "react";
export function CustomResolutionInput() {
const [useCustomWidth, setUseCustomWidth] = useAtom(useCustomWidthAtom);
const [customWidth, setCustomWidth] = useAtom(customWidthAtom);
return (
<div>
<div className="flex flex-col gap-1">
<p className="text-sm font-medium">CUSTOM OUTPUT WIDTH</p>
<p className="text-xs text-base-content/80">
<b>REQUIRES RESTART</b>
<br />
Use a custom width for the output images. The height will be adjusted
automatically. Enabling this will override the scale setting.
</p>
</div>
<div className="flex items-center gap-2">
<input
type="checkbox"
className="toggle"
checked={useCustomWidth}
onClick={(e) => {
if (!e.currentTarget.checked) {
localStorage.removeItem("customWidth");
}
setUseCustomWidth(!useCustomWidth);
}}
/>
<input
type="number"
value={customWidth}
disabled={!useCustomWidth}
onChange={(e) => {
if (e.currentTarget.value === "") {
setUseCustomWidth(false);
setCustomWidth(null);
localStorage.removeItem("customWidth");
return;
}
setCustomWidth(parseInt(e.currentTarget.value));
}}
step="1"
className="input input-primary mt-2 w-full"
/>
</div>
</div>
);
}

View File

@ -1,3 +1,5 @@
import { useCustomWidthAtom } from "@/atoms/userSettingsAtom";
import { useAtom } from "jotai";
import React from "react"; import React from "react";
type ImageScaleSelectProps = { type ImageScaleSelectProps = {
@ -6,16 +8,21 @@ type ImageScaleSelectProps = {
}; };
export function ImageScaleSelect({ scale, setScale }: ImageScaleSelectProps) { export function ImageScaleSelect({ scale, setScale }: ImageScaleSelectProps) {
const [useCustomWidth, setUseCustomWidth] = useAtom(useCustomWidthAtom);
return ( return (
<div> <div className={`${useCustomWidth && "opacity-50"}`}>
<div className="flex flex-row gap-1"> <div className="flex flex-row gap-1">
<p className="text-sm font-medium">IMAGE SCALE</p> <p className="text-sm font-medium">
IMAGE SCALE {useCustomWidth && "DISABLED"}
</p>
{/* {/*
<p className="badge-primary badge text-[10px] font-medium"> <p className="badge-primary badge text-[10px] font-medium">
EXPERIMENTAL EXPERIMENTAL
</p> */} </p> */}
</div> </div>
<input <input
disabled={useCustomWidth}
type="range" type="range"
min="1" min="1"
max="4" max="4"

View File

@ -24,6 +24,7 @@ import { ResetSettings } from "./ResetSettings";
import ProcessImageToggle from "./ProcessImageToggle"; import ProcessImageToggle from "./ProcessImageToggle";
import { featureFlags } from "@common/feature-flags"; import { featureFlags } from "@common/feature-flags";
import TurnOffNotificationsToggle from "./TurnOffNotificationsToggle"; import TurnOffNotificationsToggle from "./TurnOffNotificationsToggle";
import { CustomResolutionInput } from "./CustomResolutionInput";
interface IProps { interface IProps {
batchMode: boolean; batchMode: boolean;
@ -71,7 +72,7 @@ function SettingsTab({
const modelOptions = useAtomValue(modelsListAtom); const modelOptions = useAtomValue(modelsListAtom);
const [scale, setScale] = useAtom(scaleAtom); const [scale, setScale] = useAtom(scaleAtom);
const [noImageProcessing, setNoImageProcessing] = useAtom( const [noImageProcessing, setNoImageProcessing] = useAtom(
noImageProcessingAtom noImageProcessingAtom,
); );
const { logit } = useLog(); const { logit } = useLog();
@ -86,7 +87,7 @@ function SettingsTab({
const currentlySavedImageFormat = localStorage.getItem("saveImageAs"); const currentlySavedImageFormat = localStorage.getItem("saveImageAs");
logit( logit(
"⚙️ Getting saveImageAs from localStorage: ", "⚙️ Getting saveImageAs from localStorage: ",
currentlySavedImageFormat currentlySavedImageFormat,
); );
setSaveImageAs(currentlySavedImageFormat); setSaveImageAs(currentlySavedImageFormat);
} }
@ -98,11 +99,11 @@ function SettingsTab({
logit("🔀 Setting model to", modelOptions[0].value); logit("🔀 Setting model to", modelOptions[0].value);
} else { } else {
let currentlySavedModel = JSON.parse( let currentlySavedModel = JSON.parse(
localStorage.getItem("model") localStorage.getItem("model"),
) as (typeof modelOptions)[0]; ) as (typeof modelOptions)[0];
if ( if (
modelOptions.find( modelOptions.find(
(model) => model.value === currentlySavedModel.value (model) => model.value === currentlySavedModel.value,
) === undefined ) === undefined
) { ) {
localStorage.setItem("model", JSON.stringify(modelOptions[0])); localStorage.setItem("model", JSON.stringify(modelOptions[0]));
@ -113,7 +114,7 @@ function SettingsTab({
setModel(currentlySavedModel.value); setModel(currentlySavedModel.value);
logit( logit(
"⚙️ Getting model from localStorage: ", "⚙️ Getting model from localStorage: ",
JSON.stringify(currentlySavedModel) JSON.stringify(currentlySavedModel),
); );
} }
@ -131,14 +132,14 @@ function SettingsTab({
localStorage.setItem("rememberOutputFolder", "false"); localStorage.setItem("rememberOutputFolder", "false");
} else { } else {
const currentlySavedRememberOutputFolder = localStorage.getItem( const currentlySavedRememberOutputFolder = localStorage.getItem(
"rememberOutputFolder" "rememberOutputFolder",
); );
logit( logit(
"⚙️ Getting rememberOutputFolder from localStorage: ", "⚙️ Getting rememberOutputFolder from localStorage: ",
currentlySavedRememberOutputFolder currentlySavedRememberOutputFolder,
); );
setRememberOutputFolder( setRememberOutputFolder(
currentlySavedRememberOutputFolder === "true" ? true : false currentlySavedRememberOutputFolder === "true" ? true : false,
); );
} }
}, []); }, []);
@ -167,24 +168,26 @@ function SettingsTab({
}; };
const upscaylVersion = navigator?.userAgent?.match( const upscaylVersion = navigator?.userAgent?.match(
/Upscayl\/([\d\.]+\d+)/ /Upscayl\/([\d\.]+\d+)/,
)[1]; )[1];
return ( return (
<div className="animate-step-in animate flex h-screen flex-col gap-7 overflow-y-auto p-5 overflow-x-hidden"> <div className="animate-step-in animate flex h-screen flex-col gap-7 overflow-y-auto overflow-x-hidden p-5">
<div className="flex flex-col gap-2 text-sm font-medium uppercase"> <div className="flex flex-col gap-2 text-sm font-medium uppercase">
<p>Having issues?</p> <p>Having issues?</p>
<a <a
className="btn-primary btn" className="btn btn-primary"
href="https://github.com/upscayl/upscayl/wiki/" href="https://github.com/upscayl/upscayl/wiki/"
target="_blank"> target="_blank"
>
HELP ME! HELP ME!
</a> </a>
{featureFlags.APP_STORE_BUILD && ( {featureFlags.APP_STORE_BUILD && (
<a <a
className="btn-primary btn" className="btn btn-primary"
href={`mailto:upscayl@gmail.com?subject=Upscayl%20Issue%3A%20%3CIssue%20name%20here%3E&body=Device%20Name%3A%20%3CYOUR%20DEVICE%20MODEL%3E%0AOperating%20System%3A%20%3CYOUR%20OPERATING%20SYSTEM%20VERSION%3E%0AUpscayl%20Version%3A%20${upscaylVersion}%0A%0AHi%2C%20I'm%20having%20an%20issue%20with%20Upscayl.%20%3CDESCRIBE%20ISSUE%20HERE%3E`} href={`mailto:upscayl@gmail.com?subject=Upscayl%20Issue%3A%20%3CIssue%20name%20here%3E&body=Device%20Name%3A%20%3CYOUR%20DEVICE%20MODEL%3E%0AOperating%20System%3A%20%3CYOUR%20OPERATING%20SYSTEM%20VERSION%3E%0AUpscayl%20Version%3A%20${upscaylVersion}%0A%0AHi%2C%20I'm%20having%20an%20issue%20with%20Upscayl.%20%3CDESCRIBE%20ISSUE%20HERE%3E`}
target="_blank"> target="_blank"
>
EMAIL DEVELOPER EMAIL DEVELOPER
</a> </a>
)} )}
@ -217,6 +220,8 @@ function SettingsTab({
{/* IMAGE SCALE */} {/* IMAGE SCALE */}
<ImageScaleSelect scale={scale} setScale={setScale} /> <ImageScaleSelect scale={scale} setScale={setScale} />
<CustomResolutionInput />
<CompressionInput <CompressionInput
compression={compression} compression={compression}
handleCompressionChange={handleCompressionChange} handleCompressionChange={handleCompressionChange}
@ -247,10 +252,11 @@ function SettingsTab({
{featureFlags.SHOW_UPSCAYL_CLOUD_INFO && ( {featureFlags.SHOW_UPSCAYL_CLOUD_INFO && (
<> <>
<button <button
className="mb-5 rounded-btn p-1 mx-5 bg-success shadow-lg shadow-success/40 text-slate-50 animate-pulse text-sm" className="rounded-btn mx-5 mb-5 animate-pulse bg-success p-1 text-sm text-slate-50 shadow-lg shadow-success/40"
onClick={() => { onClick={() => {
setShow(true); setShow(true);
}}> }}
>
Introducing Upscayl Cloud Introducing Upscayl Cloud
</button> </button>

View File

@ -289,42 +289,14 @@ function LeftPaneImageSteps({
<p className="step-heading">Step 4</p> <p className="step-heading">Step 4</p>
{dimensions.width && dimensions.height && ( {dimensions.width && dimensions.height && (
<p className="mb-2 text-sm"> <p className="mb-2 text-sm">
Upscayl from <br /> Upscayl from{" "}
<span className="font-bold"> <span className="font-bold">
{dimensions.width}x{dimensions.height} {dimensions.width}x{dimensions.height}
</span>{" "} </span>{" "}
to to{" "}
<div className="flex items-center gap-1"> <span className="font-bold">
<input
className="input input-primary h-8 w-16 px-2 py-0 text-sm font-bold"
defaultValue={getUpscaleResolution().width}
value={targetWidth}
onChange={(e) => {
if (parseInt(e.target.value) > 32768) {
setTargetWidth(32768);
} else if (e.target.value === "") {
setTargetWidth(1);
}
setTargetWidth(parseInt(e.target.value));
}}
/>{" "}
x{" "}
<input
className="input input-primary h-8 w-16 px-2 py-0 text-sm font-bold"
defaultValue={getUpscaleResolution().width}
value={targetHeight}
onChange={(e) => {
if (parseInt(e.target.value) > 32768) {
setTargetHeight(32768);
} else if (e.target.value === "") {
setTargetHeight(1);
} else setTargetHeight(parseInt(e.target.value));
}}
/>
</div>
{/* <span className="font-bold">
{getUpscaleResolution().width}x{getUpscaleResolution().height} {getUpscaleResolution().width}x{getUpscaleResolution().height}
</span> */} </span>
</p> </p>
)} )}
<button <button

View File

@ -138,7 +138,13 @@ const Home = () => {
); );
// LOG // LOG
window.electron.on(COMMAND.LOG, (_, data: string) => { window.electron.on(COMMAND.LOG, (_, data: string) => {
logit(`🐞 BACKEND REPORTED: `, data); if (
!data.includes("Updating") &&
!data.includes("Custom Models Folder Path") &&
!data.includes("Detected Custom Models")
) {
logit(`🐞 BACKEND REPORTED: `, data);
}
}); });
// SCALING AND CONVERTING // SCALING AND CONVERTING
window.electron.on(COMMAND.SCALING_AND_CONVERTING, (_, data: string) => { window.electron.on(COMMAND.SCALING_AND_CONVERTING, (_, data: string) => {