mirror of
https://github.com/upscayl/upscayl.git
synced 2024-11-27 17:00:52 +01:00
Fix image, add quality slider and folder upscale
This commit is contained in:
parent
269d5d9603
commit
93c426a5b4
@ -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");
|
||||
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()
|
||||
// );
|
||||
// });
|
||||
// }
|
||||
// });
|
||||
// });
|
||||
|
2479
package-lock.json
generated
2479
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
28
package.json
28
package.json
@ -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"
|
||||
|
@ -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>
|
||||
</div>
|
||||
<input
|
||||
type="range"
|
||||
|
28
renderer/components/settings-tab/QualityInput.tsx
Normal file
28
renderer/components/settings-tab/QualityInput.tsx
Normal 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>
|
||||
);
|
||||
}
|
@ -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>
|
||||
);
|
||||
}
|
||||
|
@ -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>
|
||||
);
|
||||
}
|
||||
|
@ -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>
|
||||
);
|
||||
}
|
||||
|
@ -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
|
||||
{batchMode && "In Progress"}
|
||||
{!batchMode &&
|
||||
(doubleUpscaylCounter > 0
|
||||
? `${progress}\nPass ${doubleUpscaylCounter}`
|
||||
: `${progress}`}
|
||||
: `${progress}`)}
|
||||
</p>
|
||||
<p className="rounded-full bg-base-300 px-2 py-1 text-sm font-medium">
|
||||
Doing the Upscayl magic...
|
||||
|
@ -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 (
|
||||
|
@ -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}
|
||||
|
Loading…
Reference in New Issue
Block a user