1
0
mirror of https://github.com/upscayl/upscayl.git synced 2024-11-23 23:21:05 +01:00

Fix image, add quality slider and folder upscale

This commit is contained in:
Nayam Amarshe 2023-07-23 14:37:18 +05:30
parent 269d5d9603
commit 93c426a5b4
11 changed files with 1362 additions and 1479 deletions

View File

@ -49,6 +49,7 @@ let folderPath: string | undefined = undefined;
let customModelsFolderPath: string | undefined = undefined;
let outputFolderPath: string | undefined = undefined;
let saveOutputFolder = false;
let quality = 100;
let stopped = false;
@ -157,6 +158,15 @@ app.on("ready", async () => {
saveOutputFolder = lastSaveOutputFolder;
}
});
// GET IMAGE QUALITY (NUMBER) TO LOCAL STORAGE
mainWindow.webContents
.executeJavaScript('localStorage.getItem("quality");', true)
.then((lastSavedQuality: string | null) => {
if (lastSavedQuality !== null) {
quality = parseInt(lastSavedQuality);
}
});
});
// Quit the app once all windows are closed
@ -441,13 +451,34 @@ ipcMain.on(commands.DOUBLE_UPSCAYL, async (event, payload) => {
return;
};
const onClose2 = (code) => {
const onClose2 = async (code) => {
if (!failed2 && !stopped) {
logit("💯 Done upscaling");
mainWindow.webContents.send(
commands.DOUBLE_UPSCAYL_DONE,
isAlpha ? outFile + ".png" : outFile
);
logit("♻ Scaling and converting now...");
const originalImage = await Jimp.read(inputDir + slash + fullfileName);
try {
const newImage = await Jimp.read(isAlpha ? outFile + ".png" : outFile);
try {
newImage
.scaleToFit(
originalImage.getWidth() * parseInt(payload.scale),
originalImage.getHeight() * parseInt(payload.scale)
)
.quality(100 - quality)
.write(isAlpha ? outFile + ".png" : outFile);
mainWindow.setProgressBar(-1);
mainWindow.webContents.send(
commands.DOUBLE_UPSCAYL_DONE,
isAlpha ? outFile + ".png" : outFile
);
} catch (error) {
logit("❌ Error converting to PNG: ", error);
onError(error);
}
} catch (error) {
logit("❌ Error reading original image metadata", error);
onError(error);
}
}
};
@ -580,6 +611,7 @@ ipcMain.on(commands.UPSCAYL, async (event, payload) => {
originalImage.getWidth() * parseInt(payload.scale),
originalImage.getHeight() * parseInt(payload.scale)
)
.quality(100 - quality)
.write(isAlpha ? outFile + ".png" : outFile);
mainWindow.setProgressBar(-1);
mainWindow.webContents.send(
@ -653,8 +685,9 @@ ipcMain.on(commands.FOLDER_UPSCAYL, async (event, payload) => {
data.toString()
);
if (data.includes("invalid gpu") || data.includes("failed")) {
logit("❌ INVALID GPU OR FAILED");
logit("❌ INVALID GPU OR INVALID FILES IN FOLDER - FAILED");
failed = true;
upscayl.kill();
}
};
const onError = (data: any) => {
@ -663,12 +696,15 @@ ipcMain.on(commands.FOLDER_UPSCAYL, async (event, payload) => {
data.toString()
);
failed = true;
upscayl.kill();
return;
};
const onClose = () => {
if (!failed && !stopped) {
logit("💯 Done upscaling");
mainWindow.webContents.send(commands.FOLDER_UPSCAYL_DONE, outputDir);
} else {
upscayl.kill();
}
};
@ -680,24 +716,6 @@ ipcMain.on(commands.FOLDER_UPSCAYL, async (event, payload) => {
//------------------------Auto-Update Code-----------------------------//
autoUpdater.autoInstallOnAppQuit = false;
// ! AUTO UPDATE STUFF
// autoUpdater.on("update-available", ({ version, releaseNotes, releaseName }) => {
// autoUpdater.autoInstallOnAppQuit = false;
// const dialogOpts = {
// type: "info",
// buttons: ["Sweet!"],
// title: "New Upscayl Update!",
// message: releaseName as string,
// detail: `Upscayl ${version} is available! It is being downloaded in the background. Please check GitHub for more details.`,
// };
// logit("📲 Update Available", releaseName, releaseNotes);
// dialog.showMessageBox(dialogOpts).then((returnValue) => {
// if (returnValue.response === 0) {
// logit("📲 Update Downloading");
// }
// });
// });
autoUpdater.on("update-downloaded", (event) => {
autoUpdater.autoInstallOnAppQuit = false;
const dialogOpts: MessageBoxOptions = {
@ -717,110 +735,3 @@ autoUpdater.on("update-downloaded", (event) => {
}
});
});
//------------------------Video Upscayl-----------------------------//
// ipcMain.on(commands.UPSCAYL_VIDEO, async (event, payload) => {
// // Extract the model
// const model = payload.model;
// // Extract the Video Directory
// let videoFileName = payload.videoPath.replace(/^.*[\\\/]/, "");
// const justFileName = parse(videoFileName).name;
// let inputDir = payload.videoPath.match(/(.*)[\/\\]/)[1] || "";
// log.log("🚀 => file: index.ts => line 337 => inputDir", inputDir);
// // Set the output directory
// let outputDir = payload.outputPath + "_frames";
// log.log("🚀 => file: index.ts => line 340 => outputDir", outputDir);
// let frameExtractionPath = join(inputDir, justFileName + "_f");
// let frameUpscalePath = join(inputDir, justFileName + "_u");
// log.log(
// "🚀 => file: index.ts => line 342 => frameExtractionPath",
// frameExtractionPath,
// frameUpscalePath
// );
// if (!fs.existsSync(frameExtractionPath)) {
// fs.mkdirSync(frameExtractionPath, { recursive: true });
// }
// if (!fs.existsSync(frameUpscalePath)) {
// fs.mkdirSync(frameUpscalePath, { recursive: true });
// }
// let ffmpegProcess: ChildProcessWithoutNullStreams | null = null;
// ffmpegProcess = spawn(
// ffmpeg.path,
// [
// "-i",
// inputDir + slash + videoFileName,
// frameExtractionPath + slash + "out%d.png",
// ],
// {
// cwd: undefined,
// detached: false,
// }
// );
// let failed = false;
// ffmpegProcess?.stderr.on("data", (data: string) => {
// log.log("🚀 => file: index.ts:420 => data", data.toString());
// data = data.toString();
// mainWindow.webContents.send(
// commands.FFMPEG_VIDEO_PROGRESS,
// data.toString()
// );
// });
// ffmpegProcess?.on("error", (data: string) => {
// mainWindow.webContents.send(
// commands.FFMPEG_VIDEO_PROGRESS,
// data.toString()
// );
// failed = true;
// return;
// });
// // Send done comamnd when
// ffmpegProcess?.on("close", (code: number) => {
// if (failed !== true) {
// log.log("Frame extraction successful!");
// mainWindow.webContents.send(commands.FFMPEG_VIDEO_DONE, outputDir);
// // UPSCALE
// let upscayl: ChildProcessWithoutNullStreams | null = null;
// upscayl = spawn(
// execPath("realesrgan"),
// [
// "-i",
// frameExtractionPath,
// "-o",
// frameUpscalePath,
// "-s",
// 4,
// "-m",
// modelsPath,
// "-n",
// model,
// ],
// {
// cwd: undefined,
// detached: false,
// }
// );
// upscayl?.stderr.on("data", (data) => {
// log.log(
// "🚀 => upscayl.stderr.on => stderr.toString()",
// data.toString()
// );
// data = data.toString();
// mainWindow.webContents.send(
// commands.FFMPEG_VIDEO_PROGRESS,
// data.toString()
// );
// });
// }
// });
// });

2511
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +1,7 @@
{
"name": "upscayl",
"private": true,
"version": "2.5.5",
"version": "2.7.5",
"productName": "Upscayl",
"homepage": "https://github.com/TGS963/upscayl",
"contributors": [
@ -160,34 +160,30 @@
"@types/react": "^18.0.37",
"@types/react-dom": "^18.0.11",
"autoprefixer": "^10.4.14",
"electron": "^23.2.4",
"electron-builder": "^24.2.1",
"electron": "^25.3.1",
"electron-builder": "^24.4.0",
"next": "^13.3.0",
"postcss": "^8.4.23",
"prettier": "^2.8.7",
"prettier-plugin-tailwindcss": "^0.1.13",
"prettier": "^3.0.0",
"prettier-plugin-tailwindcss": "^0.4.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"tailwindcss": "^3.3.1",
"typescript": "^4.8.4"
},
"dependencies": {
"app-root-dir": "^1.0.2",
"daisyui": "^2.51.4",
"daisyui": "^3.3.1",
"electron-is-dev": "^2.0.0",
"electron-log": "^5.0.0-beta.16",
"electron-next": "^3.1.5",
"electron-updater": "^5.3.0",
"image-size": "^1.0.2",
"electron-updater": "^6.1.1",
"jimp": "^0.22.8",
"jotai": "^2.0.4",
"jotai": "^2.2.2",
"react-compare-slider": "^2.2.0",
"react-dropzone": "^14.2.3",
"react-image-zoom": "^1.3.1",
"react-select": "^5.6.0",
"react-tooltip": "^4.5.0",
"tailwind-scrollbar": "^2.0.1",
"theme-change": "^2.2.0"
"react-select": "^5.7.4",
"react-tooltip": "^5.18.1",
"tailwind-scrollbar": "^3.0.4",
"theme-change": "^2.5.0"
},
"volta": {
"node": "16.17.0"

View File

@ -10,13 +10,10 @@ export function ImageScaleSelect({ scale, setScale }: ImageScaleSelectProps) {
<div>
<div className="flex flex-row gap-1">
<p className="text-sm font-medium">IMAGE SCALE</p>
<a
href="https://github.com/upscayl/upscayl/wiki/Guide#scale-option"
target="_blank">
<p className="badge-primary badge text-[10px] font-medium">
EXPERIMENTAL
</p>
</a>
<p className="badge-primary badge text-[10px] font-medium">
EXPERIMENTAL
</p>
</div>
<input
type="range"

View File

@ -0,0 +1,28 @@
import React from "react";
type QualityInputProps = {
quality: number;
handleQualityChange: (arg: any) => void;
};
export function QualityInput({
quality,
handleQualityChange,
}: QualityInputProps) {
return (
<div className="flex flex-col gap-2">
<p className="text-sm font-medium uppercase">
Image Compression ({quality}%)
</p>
<input
type="range"
placeholder="Type here"
className="range range-primary w-full max-w-xs"
min={0}
max={100}
value={quality}
onChange={handleQualityChange}
/>
</div>
);
}

View File

@ -12,12 +12,15 @@ import { useAtom, useAtomValue } from "jotai";
import { customModelsPathAtom, scaleAtom } from "../../atoms/userSettingsAtom";
import { modelsListAtom } from "../../atoms/modelsListAtom";
import useLog from "../hooks/useLog";
import { QualityInput } from "./QualityInput";
interface IProps {
batchMode: boolean;
setModel: React.Dispatch<React.SetStateAction<string>>;
saveImageAs: string;
setSaveImageAs: React.Dispatch<React.SetStateAction<string>>;
quality: number;
setQuality: React.Dispatch<React.SetStateAction<number>>;
gpuId: string;
setGpuId: React.Dispatch<React.SetStateAction<string>>;
logData: string[];
@ -26,6 +29,8 @@ interface IProps {
function SettingsTab({
batchMode,
setModel,
quality,
setQuality,
gpuId,
setGpuId,
saveImageAs,
@ -113,6 +118,11 @@ function SettingsTab({
localStorage.setItem("saveImageAs", format);
};
const handleQualityChange = (e) => {
setQuality(e.target.value);
localStorage.setItem("quality", e.target.value);
};
const handleGpuIdChange = (e) => {
setGpuId(e.target.value);
localStorage.setItem("gpuId", e.target.value);
@ -139,9 +149,30 @@ function SettingsTab({
<DonateButton />
</div>
<LogArea
copyOnClickHandler={copyOnClickHandler}
isCopied={isCopied}
logData={logData}
/>
{/* THEME SELECTOR */}
<ThemeSelect />
{/* IMAGE FORMAT BUTTONS */}
<ImageFormatSelect
batchMode={batchMode}
saveImageAs={saveImageAs}
setExportType={setExportType}
/>
{/* IMAGE SCALE */}
<ImageScaleSelect scale={scale} setScale={setScale} />
<QualityInput
quality={quality}
handleQualityChange={handleQualityChange}
/>
<SaveOutputFolderToggle
rememberOutputFolder={rememberOutputFolder}
setRememberOutputFolder={setRememberOutputFolder}
@ -155,22 +186,6 @@ function SettingsTab({
customModelsPath={customModelsPath}
setCustomModelsPath={setCustomModelsPath}
/>
{/* IMAGE FORMAT BUTTONS */}
<ImageFormatSelect
batchMode={batchMode}
saveImageAs={saveImageAs}
setExportType={setExportType}
/>
{/* IMAGE SCALE */}
<ImageScaleSelect scale={scale} setScale={setScale} />
<LogArea
copyOnClickHandler={copyOnClickHandler}
isCopied={isCopied}
logData={logData}
/>
</div>
);
}

View File

@ -1,7 +1,7 @@
import { useAtomValue } from "jotai";
import React, { useEffect, useState } from "react";
import Select from "react-select";
import ReactTooltip from "react-tooltip";
import { Tooltip } from "react-tooltip";
import { themeChange } from "theme-change";
import { modelsListAtom } from "../../../atoms/modelsListAtom";
import useLog from "../../hooks/useLog";
@ -140,13 +140,14 @@ function LeftPaneImageSteps({
onClick={() => setBatchMode((oldValue) => !oldValue)}></input>
<p
className="mr-1 inline-block cursor-help text-sm"
data-tip="This will let you Upscayl all files in a folder at once">
data-tooltip-id="tooltip"
data-tooltip-content="This will let you Upscayl all files in a folder at once">
Batch Upscayl
</p>
</div>
{/* STEP 1 */}
<div data-tip={imagePath}>
<div data-tooltip-id="tooltip" data-tooltip-content={imagePath}>
<p className="step-heading">Step 1</p>
<button
className="btn-primary btn"
@ -198,7 +199,8 @@ function LeftPaneImageSteps({
</p>
<button
className="badge-info badge cursor-help"
data-tip="Enable this option to get a 16x upscayl (we just run upscayl twice). Note that this may not always work properly with all images, for example, images with really large resolutions.">
data-tooltip-id="tooltip"
data-tooltip-content="Enable this option to get a 16x upscayl (we just run upscayl twice). Note that this may not always work properly with all images, for example, images with really large resolutions.">
i
</button>
</div>
@ -206,7 +208,10 @@ function LeftPaneImageSteps({
</div>
{/* STEP 3 */}
<div className="animate-step-in" data-tip={outputPath}>
<div
className="animate-step-in"
data-tooltip-content={outputPath}
data-tooltip-id="tooltip">
<p className="step-heading">Step 3</p>
<p className="mb-2 text-sm">
Defaults to {!batchMode ? "Image's" : "Folder's"} path
@ -239,7 +244,7 @@ function LeftPaneImageSteps({
</button>
</div>
<ReactTooltip class="max-w-sm" />
<Tooltip className="max-w-sm" id="tooltip" />
</div>
);
}

View File

@ -1,6 +1,5 @@
import React from "react";
import Select from "react-select";
import ReactTooltip from "react-tooltip";
interface IProps {
progress: string;
@ -101,8 +100,6 @@ function LeftPaneVideoSteps({
{progress.length > 0 ? "Upscayling⏳" : "Upscayl"}
</button>
</div>
<ReactTooltip class="max-w-sm" />
</div>
);
}

View File

@ -1,15 +1,22 @@
import React from "react";
import Spinner from "../../icons/Spinner";
function ProgressBar({ progress, doubleUpscaylCounter, stopHandler }) {
function ProgressBar({
progress,
doubleUpscaylCounter,
stopHandler,
batchMode,
}) {
return (
<div className="absolute flex h-full w-full flex-col items-center justify-center bg-base-300/50 backdrop-blur-lg">
<div className="flex flex-col items-center gap-2">
<Spinner />
<p className="rounded-full bg-base-300 px-2 py-1 font-bold">
{doubleUpscaylCounter > 0
? `${progress}\nPass ${doubleUpscaylCounter}`
: `${progress}`}
{batchMode && "In Progress"}
{!batchMode &&
(doubleUpscaylCounter > 0
? `${progress}\nPass ${doubleUpscaylCounter}`
: `${progress}`)}
</p>
<p className="rounded-full bg-base-300 px-2 py-1 text-sm font-medium">
Doing the Upscayl magic...

View File

@ -1,10 +1,8 @@
import "../styles/globals.css";
import Head from "next/head";
import { AppProps } from "next/app";
import { useEffect } from "react";
import { themeChange } from "theme-change";
import log from "electron-log/renderer";
import { Provider } from "jotai";
import "react-tooltip/dist/react-tooltip.css";
const MyApp = ({ Component, pageProps }: AppProps) => {
return (

View File

@ -33,6 +33,7 @@ const Home = () => {
const [videoPath, setVideoPath] = useState("");
const [upscaledVideoPath, setUpscaledVideoPath] = useState("");
const [doubleUpscaylCounter, setDoubleUpscaylCounter] = useState(0);
const [quality, setQuality] = useState(0);
const [gpuId, setGpuId] = useState("");
const [saveImageAs, setSaveImageAs] = useState("png");
const [zoomAmount, setZoomAmount] = useState("100%");
@ -556,6 +557,8 @@ const Home = () => {
<SettingsTab
batchMode={batchMode}
setModel={setModel}
quality={quality}
setQuality={setQuality}
gpuId={gpuId}
setGpuId={setGpuId}
saveImageAs={saveImageAs}
@ -580,6 +583,7 @@ const Home = () => {
upscaledBatchFolderPath.length === 0 &&
upscaledVideoPath.length === 0 ? (
<ProgressBar
batchMode={batchMode}
progress={progress}
doubleUpscaylCounter={doubleUpscaylCounter}
stopHandler={stopHandler}