1
0
mirror of https://github.com/upscayl/upscayl.git synced 2024-11-12 01:40:53 +01:00

Added video preview and scaling

This commit is contained in:
Feenix 2022-11-25 13:00:03 +05:30
parent 8593e3812b
commit a579f52eab
6 changed files with 319 additions and 81 deletions

View File

@ -327,6 +327,112 @@ ipcMain.on(commands.UPSCAYL, async (event, payload) => {
} }
}); });
//------------------------Video Upscayl-----------------------------//
ipcMain.on(commands.UPSCAYL_VIDEO, async (event, payload) => {
const model = payload.model;
const scale = payload.scaleFactor;
let inputDir = payload.videoPath.match(/(.*)[\/\\]/)[1] || "";
let outputDir = payload.outputPath;
// COPY IMAGE TO TMP FOLDER
const platform = getPlatform();
const fullfileName =
platform === "win"
? payload.videoPath.split("\\").slice(-1)[0]
: payload.videoPath.split("/").slice(-1)[0];
console.log(fullfileName);
const fileName = parse(fullfileName).name;
const fileExt = parse(fullfileName).ext;
const outFile = model.includes("realesrgan")
? outputDir + "/" + fileName + "_upscayl_" + scale + "x_" + model + fileExt
: outputDir +
"/" +
fileName +
"_upscayl_sharpened_" +
scale +
"x_" +
model +
fileExt;
// UPSCALE
if (fs.existsSync(outFile)) {
// If already upscayled, just output that file
mainWindow.webContents.send(commands.UPSCAYL_DONE, outFile);
} else {
let upscayl: ChildProcessWithoutNullStreams | null = null;
switch (model) {
case "realesrgan-x4plus":
case "realesrgan-x4plus-anime":
upscayl = spawn(
execPath("realesrgan"),
[
"-i",
inputDir + "/" + fullfileName,
"-o",
outFile,
"-s",
scale === 2 ? 4 : scale,
"-m",
modelsPath,
"-n",
model,
],
{
cwd: undefined,
detached: false,
}
);
break;
case "models-DF2K":
upscayl = spawn(
execPath("realsr"),
[
"-i",
inputDir + "/" + fullfileName,
"-o",
outFile,
"-s",
scale,
"-x",
"-m",
modelsPath + "/" + model,
],
{
cwd: undefined,
detached: false,
}
);
break;
}
let failed = false;
upscayl?.stderr.on("data", (data) => {
console.log(
"🚀 => upscayl.stderr.on => stderr.toString()",
data.toString()
);
data = data.toString();
mainWindow.webContents.send(commands.UPSCAYL_PROGRESS, data.toString());
if (data.includes("invalid gpu") || data.includes("failed")) {
failed = true;
}
});
upscayl?.on("error", (data) => {
mainWindow.webContents.send(commands.UPSCAYL_PROGRESS, data.toString());
failed = true;
return;
});
// Send done comamnd when
upscayl?.on("close", (code) => {
if (failed !== true) {
console.log("Done upscaling");
mainWindow.webContents.send(commands.UPSCAYL_DONE, outFile);
}
});
}
});
//------------------------Upscayl Folder-----------------------------// //------------------------Upscayl Folder-----------------------------//
ipcMain.on(commands.FOLDER_UPSCAYL, async (event, payload) => { ipcMain.on(commands.FOLDER_UPSCAYL, async (event, payload) => {
const model = payload.model; const model = payload.model;

View File

@ -13,5 +13,8 @@ const commands = {
FOLDER_UPSCAYL_DONE: "Folder upscaling successful", FOLDER_UPSCAYL_DONE: "Folder upscaling successful",
FOLDER_UPSCAYL_PROGRESS: "Send Folder Upscaling Progress from Main to Renderer", FOLDER_UPSCAYL_PROGRESS: "Send Folder Upscaling Progress from Main to Renderer",
OPEN_FOLDER: "Open Folder", OPEN_FOLDER: "Open Folder",
UPSCAYL_VIDEO: "Upscale the Video",
UPSCAYL_VIDEO_DONE: "Video Upscaling Done",
UPSCAYL_VIDEO_PROGRESS: "Send Video Upscale Progress from Main to Renderer",
}; };
exports.default = commands; exports.default = commands;

View File

@ -285,6 +285,96 @@ electron_1.ipcMain.on(commands_1.default.UPSCAYL, (event, payload) => __awaiter(
}); });
} }
})); }));
//------------------------Video Upscayl-----------------------------//
electron_1.ipcMain.on(commands_1.default.UPSCAYL_VIDEO, (event, payload) => __awaiter(void 0, void 0, void 0, function* () {
const model = payload.model;
const scale = payload.scaleFactor;
let inputDir = payload.videoPath.match(/(.*)[\/\\]/)[1] || "";
let outputDir = payload.outputPath;
// COPY IMAGE TO TMP FOLDER
const platform = (0, getPlatform_1.default)();
const fullfileName = platform === "win"
? payload.videoPath.split("\\").slice(-1)[0]
: payload.videoPath.split("/").slice(-1)[0];
console.log(fullfileName);
const fileName = (0, path_1.parse)(fullfileName).name;
const fileExt = (0, path_1.parse)(fullfileName).ext;
const outFile = model.includes("realesrgan")
? outputDir + "/" + fileName + "_upscayl_" + scale + "x_" + model + fileExt
: outputDir +
"/" +
fileName +
"_upscayl_sharpened_" +
scale +
"x_" +
model +
fileExt;
// UPSCALE
if (fs_1.default.existsSync(outFile)) {
// If already upscayled, just output that file
mainWindow.webContents.send(commands_1.default.UPSCAYL_DONE, outFile);
}
else {
let upscayl = null;
switch (model) {
case "realesrgan-x4plus":
case "realesrgan-x4plus-anime":
upscayl = (0, child_process_1.spawn)((0, binaries_1.execPath)("realesrgan"), [
"-i",
inputDir + "/" + fullfileName,
"-o",
outFile,
"-s",
scale === 2 ? 4 : scale,
"-m",
binaries_1.modelsPath,
"-n",
model,
], {
cwd: undefined,
detached: false,
});
break;
case "models-DF2K":
upscayl = (0, child_process_1.spawn)((0, binaries_1.execPath)("realsr"), [
"-i",
inputDir + "/" + fullfileName,
"-o",
outFile,
"-s",
scale,
"-x",
"-m",
binaries_1.modelsPath + "/" + model,
], {
cwd: undefined,
detached: false,
});
break;
}
let failed = false;
upscayl === null || upscayl === void 0 ? void 0 : upscayl.stderr.on("data", (data) => {
console.log("🚀 => upscayl.stderr.on => stderr.toString()", data.toString());
data = data.toString();
mainWindow.webContents.send(commands_1.default.UPSCAYL_PROGRESS, data.toString());
if (data.includes("invalid gpu") || data.includes("failed")) {
failed = true;
}
});
upscayl === null || upscayl === void 0 ? void 0 : upscayl.on("error", (data) => {
mainWindow.webContents.send(commands_1.default.UPSCAYL_PROGRESS, data.toString());
failed = true;
return;
});
// Send done comamnd when
upscayl === null || upscayl === void 0 ? void 0 : upscayl.on("close", (code) => {
if (failed !== true) {
console.log("Done upscaling");
mainWindow.webContents.send(commands_1.default.UPSCAYL_DONE, outFile);
}
});
}
}));
//------------------------Upscayl Folder-----------------------------// //------------------------Upscayl Folder-----------------------------//
electron_1.ipcMain.on(commands_1.default.FOLDER_UPSCAYL, (event, payload) => __awaiter(void 0, void 0, void 0, function* () { electron_1.ipcMain.on(commands_1.default.FOLDER_UPSCAYL, (event, payload) => __awaiter(void 0, void 0, void 0, function* () {
const model = payload.model; const model = payload.model;

View File

@ -4,18 +4,13 @@ import ReactTooltip from "react-tooltip";
interface IProps { interface IProps {
progress: string; progress: string;
selectImageHandler: () => Promise<void>; selectVideoHandler: () => Promise<void>;
selectFolderHandler: () => Promise<void>;
handleModelChange: (e: any) => void; handleModelChange: (e: any) => void;
handleDrop: (e: any) => void; handleDrop: (e: any) => void;
outputHandler: () => Promise<void>; outputHandler: () => Promise<void>;
upscaylHandler: () => Promise<void>; upscaylHandler: () => Promise<void>;
batchMode: boolean; videoPath: string;
setBatchMode: (arg: any) => void;
imagePath: string;
outputPath: string; outputPath: string;
doubleUpscayl: boolean;
setDoubleUpscayl: (arg: boolean) => void;
model: string; model: string;
isVideo: boolean; isVideo: boolean;
setIsVideo: (arg: boolean) => void; setIsVideo: (arg: boolean) => void;
@ -23,26 +18,17 @@ interface IProps {
function LeftPaneVideoSteps({ function LeftPaneVideoSteps({
progress, progress,
selectImageHandler, selectVideoHandler,
selectFolderHandler,
handleModelChange, handleModelChange,
handleDrop, handleDrop,
outputHandler, outputHandler,
upscaylHandler, upscaylHandler,
batchMode, videoPath,
setBatchMode,
imagePath,
outputPath, outputPath,
doubleUpscayl,
setDoubleUpscayl,
model, model,
isVideo, isVideo,
setIsVideo, setIsVideo,
}: IProps) { }: IProps) {
const handleBatchMode = () => {
setBatchMode((oldValue) => !oldValue);
};
const customStyles = { const customStyles = {
option: (provided, state) => ({ option: (provided, state) => ({
...provided, ...provided,
@ -63,26 +49,42 @@ function LeftPaneVideoSteps({
}; };
const modelOptions = [ const modelOptions = [
{ label: "General Photo", value: "realesrgan-x4plus" }, { label: "2x Digital Art", value: "realesr-animevideov3-x2" },
{ label: "Digital Art", value: "realesrgan-x4plus-anime" }, { label: "3x Digital Art", value: "realesr-animevideov3-x3" },
{ label: "Sharpen Image", value: "models-DF2K" }, { label: "4x Digital Art", value: "realesr-animevideov3-x4" },
]; ];
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 p-5 overflow-x-hidden">
{/* STEP 1 */} {/* STEP 1 */}
<div data-tip={imagePath}> <div data-tip={videoPath}>
<p className="step-heading">Step 1</p> <p className="step-heading">Step 1</p>
<button <button className="btn-primary btn" onClick={selectVideoHandler}>
className="btn-primary btn"
onClick={!batchMode ? selectImageHandler : selectFolderHandler}>
Select Video Select Video
</button> </button>
</div> </div>
{/* STEP 2 */} {/* STEP 2 */}
<div className="animate-step-in" data-tip={outputPath}> <div className="animate-step-in">
<p className="step-heading">Step 2</p> <p className="step-heading">Step 2</p>
<p className="mb-2 text-sm">Select Scaling</p>
<Select
options={modelOptions}
components={{
IndicatorSeparator: () => null,
DropdownIndicator: () => null,
}}
onChange={handleModelChange}
className="react-select-container"
classNamePrefix="react-select"
defaultValue={modelOptions[0]}
/>
</div>
{/* STEP 3 */}
<div className="animate-step-in" data-tip={outputPath}>
<p className="step-heading">Step 3</p>
<p className="mb-2 text-sm">Defaults to Video's path</p> <p className="mb-2 text-sm">Defaults to Video's path</p>
<button className="btn-primary btn" onClick={outputHandler}> <button className="btn-primary btn" onClick={outputHandler}>
Set Output Folder Set Output Folder

View File

@ -1,7 +1,12 @@
import React from "react"; import React from "react";
function RightPaneInfo({ version, batchMode }) { function RightPaneInfo({ version, batchMode, isVideo }) {
return ( return isVideo ? (
<>
<p className="p-5 pb-1 text-lg font-semibold">Select Video to Upscale</p>
<p className="text-sm">Upscayl v{version}</p>
</>
) : (
<> <>
<p className="p-5 pb-1 text-lg font-semibold"> <p className="p-5 pb-1 text-lg font-semibold">
Select {batchMode ? "a Folder" : "an Image"} to Upscale Select {batchMode ? "a Folder" : "an Image"} to Upscale

View File

@ -123,7 +123,7 @@ const Home = () => {
}, [batchMode]); }, [batchMode]);
useEffect(() => { useEffect(() => {
if (imagePath.length > 0) { if (imagePath.length > 0 && !isVideo) {
const filePath = imagePath; const filePath = imagePath;
console.log( console.log(
"🚀 => file: index.jsx => line 109 => useEffect => filePath", "🚀 => file: index.jsx => line 109 => useEffect => filePath",
@ -140,15 +140,21 @@ const Home = () => {
alert("Please select an image"); alert("Please select an image");
resetImagePaths(); resetImagePaths();
} }
} else if (videoPath.length > 0) { } else if (videoPath.length > 0 && isVideo) {
const filePath = videoPath; const filePath = videoPath;
console.log("🚀 => file: index.tsx => line 146 => filePath", filePath);
const extension = videoPath.toLocaleLowerCase().split(".").pop(); const extension = videoPath.toLocaleLowerCase().split(".").pop();
console.log("🚀 => file: index.tsx => line 150 => extension", extension);
if (!allowedVideoFileTypes.includes(extension.toLowerCase())) { if (!allowedVideoFileTypes.includes(extension.toLowerCase())) {
alert("Please select an MP4, WebM or MKV video"); alert("Please select an MP4, WebM or MKV video");
resetImagePaths(); resetImagePaths();
} }
} else {
resetImagePaths();
} }
}, [imagePath, videoPath]); }, [imagePath, videoPath]);
@ -334,14 +340,20 @@ const Home = () => {
name="radio-1" name="radio-1"
className="radio" className="radio"
checked={!isVideo} checked={!isVideo}
onChange={() => setIsVideo(false)} onChange={() => {
setIsVideo(false);
console.log("isImage");
}}
/> />
<input <input
type="radio" type="radio"
name="radio-1" name="radio-1"
className="radio" className="radio"
checked={isVideo} checked={isVideo}
onChange={() => setIsVideo(true)} onChange={() => {
setIsVideo(true);
console.log("isVideo");
}}
/> />
<p>Video</p> <p>Video</p>
</div> </div>
@ -349,18 +361,13 @@ const Home = () => {
{isVideo ? ( {isVideo ? (
<LeftPaneVideoSteps <LeftPaneVideoSteps
progress={progress} progress={progress}
selectImageHandler={selectImageHandler} selectVideoHandler={selectVideoHandler}
selectFolderHandler={selectFolderHandler}
handleModelChange={handleModelChange} handleModelChange={handleModelChange}
handleDrop={handleDrop} handleDrop={handleDrop}
outputHandler={outputHandler} outputHandler={outputHandler}
upscaylHandler={upscaylHandler} upscaylHandler={upscaylHandler}
batchMode={batchMode}
setBatchMode={setBatchMode}
imagePath={imagePath}
outputPath={outputPath} outputPath={outputPath}
doubleUpscayl={doubleUpscayl} videoPath={videoPath}
setDoubleUpscayl={setDoubleUpscayl}
model={model} model={model}
isVideo={isVideo} isVideo={isVideo}
setIsVideo={setIsVideo} setIsVideo={setIsVideo}
@ -398,20 +405,33 @@ const Home = () => {
onPaste={(e) => handlePaste(e)}> onPaste={(e) => handlePaste(e)}>
{progress.length > 0 && {progress.length > 0 &&
upscaledImagePath.length === 0 && upscaledImagePath.length === 0 &&
upscaledBatchFolderPath.length === 0 ? ( upscaledBatchFolderPath.length === 0 &&
upscaledVideoPath.length === 0 ? (
<ProgressBar progress={progress} /> <ProgressBar progress={progress} />
) : null} ) : null}
{((!batchMode && {/* DEFAULT PANE INFO */}
{((!isVideo &&
!batchMode &&
imagePath.length === 0 && imagePath.length === 0 &&
upscaledImagePath.length === 0) || upscaledImagePath.length === 0) ||
(batchMode && (!isVideo &&
batchMode &&
batchFolderPath.length === 0 && batchFolderPath.length === 0 &&
upscaledBatchFolderPath.length === 0)) && ( upscaledBatchFolderPath.length === 0) ||
<RightPaneInfo version={version} batchMode={batchMode} /> (isVideo &&
videoPath.length === 0 &&
upscaledVideoPath.length === 0)) && (
<RightPaneInfo
version={version}
batchMode={batchMode}
isVideo={isVideo}
/>
)} )}
{/* SHOW SELECTED IMAGE */}
{!batchMode && {!batchMode &&
!isVideo &&
upscaledImagePath.length === 0 && upscaledImagePath.length === 0 &&
imagePath.length > 0 && ( imagePath.length > 0 && (
<img <img
@ -425,6 +445,7 @@ const Home = () => {
/> />
)} )}
{/* BATCH UPSCALE SHOW SELECTED FOLDER */}
{batchMode && {batchMode &&
upscaledBatchFolderPath.length === 0 && upscaledBatchFolderPath.length === 0 &&
batchFolderPath.length > 0 && ( batchFolderPath.length > 0 && (
@ -433,6 +454,7 @@ const Home = () => {
</p> </p>
)} )}
{/* BATCH UPSCALE DONE INFO */}
{batchMode && upscaledBatchFolderPath.length > 0 && ( {batchMode && upscaledBatchFolderPath.length > 0 && (
<> <>
<p className="text-neutral-50 select-none py-4 font-bold"> <p className="text-neutral-50 select-none py-4 font-bold">
@ -446,43 +468,53 @@ const Home = () => {
</> </>
)} )}
{!batchMode && imagePath.length > 0 && upscaledImagePath.length > 0 && ( {/* COMPARISON SLIDER */}
<> {!batchMode &&
<ImageOptions /> !isVideo &&
<ReactCompareSlider imagePath.length > 0 &&
itemOne={ upscaledImagePath.length > 0 && (
<> <>
<p className="absolute bottom-1 left-1 rounded-md bg-black p-1 text-sm font-medium text-white opacity-30"> <ImageOptions />
Original <ReactCompareSlider
</p> itemOne={
<ReactCompareSliderImage <>
src={"file://" + imagePath} <p className="absolute bottom-1 left-1 rounded-md bg-black p-1 text-sm font-medium text-white opacity-30">
alt="Original" Original
style={{ </p>
objectFit: "contain", <ReactCompareSliderImage
}} src={"file://" + imagePath}
className="bg-[#1d1c23]" alt="Original"
/> style={{
</> objectFit: "contain",
} }}
itemTwo={ className="bg-[#1d1c23]"
<> />
<p className="absolute bottom-1 right-1 rounded-md bg-black p-1 text-sm font-medium text-white opacity-30"> </>
Upscayled }
</p> itemTwo={
<ReactCompareSliderImage <>
src={"file://" + upscaledImagePath} <p className="absolute bottom-1 right-1 rounded-md bg-black p-1 text-sm font-medium text-white opacity-30">
alt="Upscayl" Upscayled
style={{ </p>
objectFit: "contain", <ReactCompareSliderImage
}} src={"file://" + upscaledImagePath}
className="origin-bottom scale-[200%] bg-[#1d1c23]" alt="Upscayl"
/> style={{
</> objectFit: "contain",
} }}
className="h-screen" className="origin-bottom scale-[200%] bg-[#1d1c23]"
/> />
</> </>
}
className="h-screen"
/>
</>
)}
{isVideo && videoPath.length > 0 && upscaledVideoPath.length === 0 && (
<video autoPlay controls className="m-10 w-11/12 rounded-2xl">
<source src={"file://" + videoPath} type="video/mp4" />
</video>
)} )}
</div> </div>
</div> </div>