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

Modified handlePaste functionality to save in temp folder

This commit is contained in:
abhishek-gaonkar 2024-10-05 15:23:35 +05:30
parent a0af4fe02a
commit bf06416134
19 changed files with 197 additions and 57 deletions

View File

@ -25,6 +25,9 @@ const ELECTRON_COMMANDS = {
OS: "Get OS", OS: "Get OS",
SCALING_AND_CONVERTING: "Adding some finishing touches", SCALING_AND_CONVERTING: "Adding some finishing touches",
UPSCAYL_ERROR: "Upscaling Error", UPSCAYL_ERROR: "Upscaling Error",
PASTE_IMAGE: "Paste Image from clipboard",
PASTE_IMAGE_SAVE_SUCCESS: "Clipboard Image saved successfully",
PASTE_IMAGE_SAVE_ERROR: "Clipboard Image save failed",
} as const; } as const;
export { ELECTRON_COMMANDS }; export { ELECTRON_COMMANDS };

View File

@ -1,12 +1,15 @@
export default function getDirectoryFromPath(filePath: string): string { export default function getDirectoryFromPath(
filePath: string,
popFileName: boolean = true,
): string {
// Define the path separator based on the operating system // Define the path separator based on the operating system
const separator = filePath.includes("/") ? "/" : "\\"; const separator = filePath.includes("/") ? "/" : "\\";
// Split the file path by the path separator // Split the file path by the path separator
const pathParts = filePath.split(separator); const pathParts = filePath.split(separator);
// Remove the last element to get the directory // Remove the last element to get the directory if popFileName is true
pathParts.pop(); if (popFileName) pathParts.pop();
// Join the remaining parts back together to form the directory path // Join the remaining parts back together to form the directory path
const directoryPath = pathParts.join(separator); const directoryPath = pathParts.join(separator);

View File

@ -11,7 +11,7 @@ import { spawnUpscayl } from "../utils/spawn-upscayl";
import { getBatchArguments } from "../utils/get-arguments"; import { getBatchArguments } from "../utils/get-arguments";
import slash from "../utils/slash"; import slash from "../utils/slash";
import { modelsPath } from "../utils/get-resource-paths"; import { modelsPath } from "../utils/get-resource-paths";
import { ELECTRON_COMMANDS } from "@common/electron-commands"; import { ELECTRON_COMMANDS } from "../../common/electron-commands";
import { BatchUpscaylPayload } from "../../common/types/types"; import { BatchUpscaylPayload } from "../../common/types/types";
import showNotification from "../utils/show-notification"; import showNotification from "../utils/show-notification";
import { DEFAULT_MODELS } from "../../common/models-list"; import { DEFAULT_MODELS } from "../../common/models-list";

View File

@ -5,7 +5,7 @@ import {
} from "../utils/config-variables"; } from "../utils/config-variables";
import logit from "../utils/logit"; import logit from "../utils/logit";
import slash from "../utils/slash"; import slash from "../utils/slash";
import { ELECTRON_COMMANDS } from "@common/electron-commands"; import { ELECTRON_COMMANDS } from "../../common/electron-commands";
import getModels from "../utils/get-models"; import getModels from "../utils/get-models";
import { getMainWindow } from "../main-window"; import { getMainWindow } from "../main-window";
import settings from "electron-settings"; import settings from "electron-settings";

View File

@ -14,7 +14,7 @@ import {
} from "../utils/get-arguments"; } from "../utils/get-arguments";
import { modelsPath } from "../utils/get-resource-paths"; import { modelsPath } from "../utils/get-resource-paths";
import logit from "../utils/logit"; import logit from "../utils/logit";
import { ELECTRON_COMMANDS } from "@common/electron-commands"; import { ELECTRON_COMMANDS } from "../../common/electron-commands";
import { DoubleUpscaylPayload } from "../../common/types/types"; import { DoubleUpscaylPayload } from "../../common/types/types";
import { ImageFormat } from "../types/types"; import { ImageFormat } from "../types/types";
import showNotification from "../utils/show-notification"; import showNotification from "../utils/show-notification";

View File

@ -1,4 +1,4 @@
import { ELECTRON_COMMANDS } from "@common/electron-commands"; import { ELECTRON_COMMANDS } from "../../common/electron-commands";
import { getMainWindow } from "../main-window"; import { getMainWindow } from "../main-window";
import { import {
savedCustomModelsPath, savedCustomModelsPath,

View File

@ -1,6 +1,6 @@
import fs from "fs"; import fs from "fs";
import { modelsPath } from "../utils/get-resource-paths"; import { modelsPath } from "../utils/get-resource-paths";
import { ELECTRON_COMMANDS } from "@common/electron-commands"; import { ELECTRON_COMMANDS } from "../../common/electron-commands";
import { import {
savedCustomModelsPath, savedCustomModelsPath,
setChildProcesses, setChildProcesses,

View File

@ -0,0 +1,63 @@
import { getMainWindow } from "../main-window";
import logit from "../utils/logit";
import fs from "fs";
import { tmpdir, homedir } from "os";
import path from "path";
import { ELECTRON_COMMANDS } from "../../common/electron-commands";
interface IClipboardFileParameters {
name: string;
extension: string;
size: number;
type: string;
encodedBuffer: string;
}
const createTempFileFromClipboard = (
appFolderPrefix: string,
inputFileParams: IClipboardFileParameters,
onSuccessCallback: (inputFilePath: string, outputFilePath: string) => void,
onErrorCallback: (error: Error) => void,
) => {
let tempDirectory = fs.mkdtempSync(path.join(tmpdir(), appFolderPrefix));
let tempFilePath = path.join(tempDirectory, inputFileParams.name);
fs.writeFile(
tempFilePath,
Buffer.from(inputFileParams.encodedBuffer, "base64"),
(err) => {
if (err) {
onErrorCallback(new Error("No permission to temp folder"));
return;
}
let homeDirectoryAsOutputFolder = homedir();
onSuccessCallback(tempFilePath, homeDirectoryAsOutputFolder);
},
);
};
const pasteImage = (event, file: IClipboardFileParameters) => {
const mainWindow = getMainWindow();
if (!mainWindow) return;
if (!file || !file.name || !file.encodedBuffer) return;
const appFolderPrefix = "upscayl";
createTempFileFromClipboard(
appFolderPrefix,
file,
(imageFilePath, homeDirectory) => {
mainWindow.webContents.send(ELECTRON_COMMANDS.PASTE_IMAGE_SAVE_SUCCESS, [
imageFilePath,
homeDirectory,
]);
},
(error) => {
logit(error.message);
mainWindow.webContents.send(
ELECTRON_COMMANDS.PASTE_IMAGE_SAVE_ERROR,
error.message,
);
},
);
};
export default pasteImage;

View File

@ -19,6 +19,7 @@ import doubleUpscayl from "./commands/double-upscayl";
import autoUpdate from "./commands/auto-update"; import autoUpdate from "./commands/auto-update";
import { FEATURE_FLAGS } from "../common/feature-flags"; import { FEATURE_FLAGS } from "../common/feature-flags";
import settings from "electron-settings"; import settings from "electron-settings";
import pasteImage from "./commands/paste-image";
// INITIALIZATION // INITIALIZATION
log.initialize({ preload: true }); log.initialize({ preload: true });
@ -94,6 +95,8 @@ ipcMain.on(ELECTRON_COMMANDS.FOLDER_UPSCAYL, batchUpscayl);
ipcMain.on(ELECTRON_COMMANDS.DOUBLE_UPSCAYL, doubleUpscayl); ipcMain.on(ELECTRON_COMMANDS.DOUBLE_UPSCAYL, doubleUpscayl);
ipcMain.on(ELECTRON_COMMANDS.PASTE_IMAGE, pasteImage);
if (!FEATURE_FLAGS.APP_STORE_BUILD) { if (!FEATURE_FLAGS.APP_STORE_BUILD) {
autoUpdater.on("update-downloaded", autoUpdate); autoUpdater.on("update-downloaded", autoUpdate);
} }

View File

@ -1,5 +1,5 @@
import log from "electron-log"; import log from "electron-log";
import { ELECTRON_COMMANDS } from "@common/electron-commands"; import { ELECTRON_COMMANDS } from "../../common/electron-commands";
import { getMainWindow } from "../main-window"; import { getMainWindow } from "../main-window";
const logit = (...args: any) => { const logit = (...args: any) => {

View File

@ -1,6 +1,6 @@
"use client"; "use client";
import useLogger from "../hooks/use-logger"; import useLogger from "../hooks/use-logger";
import { useState, useMemo } from "react"; import { useState, useMemo, useEffect } from "react";
import { ELECTRON_COMMANDS } from "@common/electron-commands"; import { ELECTRON_COMMANDS } from "@common/electron-commands";
import { useAtomValue, useSetAtom } from "jotai"; import { useAtomValue, useSetAtom } from "jotai";
import { import {
@ -165,39 +165,102 @@ const MainContent = ({
const handlePaste = (e: React.ClipboardEvent<HTMLDivElement>) => { const handlePaste = (e: React.ClipboardEvent<HTMLDivElement>) => {
resetImagePaths(); resetImagePaths();
e.preventDefault(); e.preventDefault();
const items = e.clipboardData.items; if (e.clipboardData.files.length) {
const files = e.clipboardData.files; const fileObject = e.clipboardData.files[0];
if (items.length === 0 || files.length === 0) { const currentDate = new Date(Date.now());
toast({ const currentTime = `${currentDate.getHours()}-${currentDate.getMinutes()}-${currentDate.getSeconds()}`;
title: t("ERRORS.INVALID_IMAGE_ERROR.TITLE"), const fileName = `${currentTime}-${fileObject.name}`;
description: t("ERRORS.INVALID_IMAGE_ERROR.ADDITIONAL_DESCRIPTION"), const file = {
}); name: fileName,
return; extension: fileName.split(".").pop(),
} size: fileObject.size,
const type = items[0].type; type: fileObject.type.split("/")[0],
const filePath = files[0].path; encodedBuffer: "",
const extension = files[0].name.split(".").at(-1); };
logit("📋 Pasted file: ", JSON.stringify({ type, filePath, extension }));
logit(
"📋 Pasted file: ",
JSON.stringify({
type: file.type,
name: file.name,
extension: file.extension,
}),
);
if ( if (
!type.includes("image") && file.type === "image" &&
!VALID_IMAGE_FORMATS.includes(extension.toLowerCase()) VALID_IMAGE_FORMATS.includes(file.extension)
) { ) {
const reader = new FileReader();
reader.onload = async (event) => {
const result = event.target?.result;
if (typeof result === "string") {
file.encodedBuffer = Buffer.from(result, "utf-8").toString(
"base64",
);
} else if (result instanceof ArrayBuffer) {
file.encodedBuffer = Buffer.from(new Uint8Array(result)).toString(
"base64",
);
} else {
logit("🚫 Invalid file pasted");
toast({ toast({
title: t("ERRORS.INVALID_IMAGE_ERROR.TITLE"), title: t("ERRORS.INVALID_IMAGE_ERROR.TITLE"),
description: t("ERRORS.INVALID_IMAGE_ERROR.ADDITIONAL_DESCRIPTION"), description: t(
"ERRORS.INVALID_IMAGE_ERROR.CLIPBOARD_DESCRIPTION",
),
}); });
}
window.electron.send(ELECTRON_COMMANDS.PASTE_IMAGE, file);
};
reader.readAsArrayBuffer(fileObject);
} else { } else {
setImagePath(filePath); logit("🚫 Invalid file pasted");
const dirname = getDirectoryFromPath(filePath); toast({
title: t("ERRORS.INVALID_IMAGE_ERROR.TITLE"),
description: t("ERRORS.INVALID_IMAGE_ERROR.CLIPBOARD_DESCRIPTION"),
});
}
} else {
logit("🚫 Invalid file pasted");
toast({
title: t("ERRORS.INVALID_IMAGE_ERROR.TITLE"),
description: t("ERRORS.INVALID_IMAGE_ERROR.CLIPBOARD_DESCRIPTION"),
});
}
};
useEffect(() => {
const handlePasteEvent = (e) => handlePaste(e);
window.addEventListener("paste", handlePasteEvent);
window.electron.on(
ELECTRON_COMMANDS.PASTE_IMAGE_SAVE_SUCCESS,
(_: any, output: string[]) => {
let [imageFilePath, homeDirectory] = output;
setImagePath(imageFilePath);
var dirname = getDirectoryFromPath(homeDirectory, false);
logit("🗂 Setting output path: ", dirname); logit("🗂 Setting output path: ", dirname);
if (!FEATURE_FLAGS.APP_STORE_BUILD) { if (!FEATURE_FLAGS.APP_STORE_BUILD) {
if (!rememberOutputFolder) { if (!rememberOutputFolder) {
setOutputPath(dirname); setOutputPath(dirname);
} }
} }
validateImagePath(filePath); validateImagePath(imageFilePath);
} },
);
window.electron.on(
ELECTRON_COMMANDS.PASTE_IMAGE_SAVE_ERROR,
(_: any, error: string) => {
toast({
title: t("ERRORS.NO_IMAGE_ERROR.TITLE"),
description: error,
});
},
);
return () => {
window.removeEventListener("paste", handlePasteEvent);
}; };
}, []);
return ( return (
<div <div
@ -207,7 +270,6 @@ const MainContent = ({
onDragEnter={handleDragEnter} onDragEnter={handleDragEnter}
onDragLeave={handleDragLeave} onDragLeave={handleDragLeave}
onDoubleClick={batchMode ? selectFolderHandler : selectImageHandler} onDoubleClick={batchMode ? selectFolderHandler : selectImageHandler}
onPaste={handlePaste}
> >
<MacTitlebarDragRegion /> <MacTitlebarDragRegion />

View File

@ -24,10 +24,10 @@ import {
import { useToast } from "@/components/ui/use-toast"; import { useToast } from "@/components/ui/use-toast";
import UpscaylSteps from "./upscayl-tab/upscayl-steps"; import UpscaylSteps from "./upscayl-tab/upscayl-steps";
import SettingsTab from "./settings-tab"; import SettingsTab from "./settings-tab";
import Footer from "../footer"; import Footer from "../Footer";
import { NewsModal } from "../news-modal"; import { NewsModal } from "../news-modal";
import Tabs from "../tabs"; import Tabs from "../Tabs";
import Header from "../header"; import Header from "../Header";
import { ChevronLeftIcon } from "lucide-react"; import { ChevronLeftIcon } from "lucide-react";
import { logAtom } from "@/atoms/log-atom"; import { logAtom } from "@/atoms/log-atom";
import { ELECTRON_COMMANDS } from "@common/electron-commands"; import { ELECTRON_COMMANDS } from "@common/electron-commands";

View File

@ -194,8 +194,9 @@
}, },
"INVALID_IMAGE_ERROR": { "INVALID_IMAGE_ERROR": {
"TITLE": "Invalid Image", "TITLE": "Invalid Image",
"DESCRIPTION": "Please select an image with a valid extension like PNG, JPG, JPEG, JFIF or WEBP.", "DESCRIPTION": "Please select/paste an image with a valid extension like PNG, JPG, JPEG, JFIF or WEBP.",
"ADDITIONAL_DESCRIPTION": "Please drag and drop an image" "ADDITIONAL_DESCRIPTION": "Please drag and drop an image",
"CLIPBOARD_DESCRIPTION": "No Image file found in Clipboard to paste!"
}, },
"NO_IMAGE_ERROR": { "NO_IMAGE_ERROR": {
"TITLE": "No image selected", "TITLE": "No image selected",

View File

@ -194,8 +194,9 @@
}, },
"INVALID_IMAGE_ERROR": { "INVALID_IMAGE_ERROR": {
"TITLE": "Imagen inválida", "TITLE": "Imagen inválida",
"DESCRIPTION": "Por favor, selecciona una imagen con una extensión válida como PNG, JPG, JPEG, JFIF o WEBP.", "DESCRIPTION": "Por favor, selecciona/pega una imagen con una extensión válida como PNG, JPG, JPEG, JFIF o WEBP.",
"ADDITIONAL_DESCRIPTION": "Por favor, arrastra y suelta una imagen" "ADDITIONAL_DESCRIPTION": "Por favor, arrastra y suelta una imagen",
"CLIPBOARD_DESCRIPTION": "¡No se encontró ningún archivo de imagen en el portapapeles para pegar!"
}, },
"NO_IMAGE_ERROR": { "NO_IMAGE_ERROR": {
"TITLE": "No se ha seleccionado imagen", "TITLE": "No se ha seleccionado imagen",

View File

@ -194,8 +194,9 @@
}, },
"INVALID_IMAGE_ERROR": { "INVALID_IMAGE_ERROR": {
"TITLE": "Image invalide", "TITLE": "Image invalide",
"DESCRIPTION": "Veuillez sélectionner une image avec une extension valide comme PNG, JPG, JPEG, JFIF ou WEBP.", "DESCRIPTION": "Veuillez sélectionner/coller une image avec une extension valide comme PNG, JPG, JPEG, JFIF ou WEBP.",
"ADDITIONAL_DESCRIPTION": "Veuillez glisser-déposer une image" "ADDITIONAL_DESCRIPTION": "Veuillez faire glisser et déposer une image",
"CLIPBOARD_DESCRIPTION": "Aucun fichier image trouvé dans le presse-papiers à coller !"
}, },
"NO_IMAGE_ERROR": { "NO_IMAGE_ERROR": {
"TITLE": "Aucune image sélectionnée", "TITLE": "Aucune image sélectionnée",

View File

@ -194,8 +194,9 @@
}, },
"INVALID_IMAGE_ERROR": { "INVALID_IMAGE_ERROR": {
"TITLE": "無効な画像", "TITLE": "無効な画像",
"DESCRIPTION": "PNG、JPG、JPEG、JFIF、またはWEBPなどの有効な拡張子を持つ画像を選択してください。", "DESCRIPTION": "PNG、JPG、JPEG、JFIF、または WEBP のような有効な拡張子の画像を選択/貼り付けてください。",
"ADDITIONAL_DESCRIPTION": "画像をドラッグアンドドロップしてください" "ADDITIONAL_DESCRIPTION": "画像をドラッグアンドドロップしてください",
"CLIPBOARD_DESCRIPTION": "クリップボードに貼り付ける画像ファイルが見つかりません!"
}, },
"NO_IMAGE_ERROR": { "NO_IMAGE_ERROR": {
"TITLE": "画像が選択されていません", "TITLE": "画像が選択されていません",

View File

@ -194,8 +194,9 @@
}, },
"INVALID_IMAGE_ERROR": { "INVALID_IMAGE_ERROR": {
"TITLE": "Неверное изображение", "TITLE": "Неверное изображение",
"DESCRIPTION": "Пожалуйста, выберите изображение с правильным расширением, таким как PNG, JPG, JPEG, JFIF или WEBP.", "DESCRIPTION": "Пожалуйста, выберите/вставьте изображение с допустимым расширением, таким как PNG, JPG, JPEG, JFIF или WEBP.",
"ADDITIONAL_DESCRIPTION": "Пожалуйста, перетащите изображение" "ADDITIONAL_DESCRIPTION": "Пожалуйста, перетащите и отпустите изображение",
"CLIPBOARD_DESCRIPTION": "Файл изображения не найден в буфере обмена для вставки!"
}, },
"NO_IMAGE_ERROR": { "NO_IMAGE_ERROR": {
"TITLE": "Изображение не выбрано", "TITLE": "Изображение не выбрано",

View File

@ -194,8 +194,9 @@
}, },
"INVALID_IMAGE_ERROR": { "INVALID_IMAGE_ERROR": {
"TITLE": "图片无效", "TITLE": "图片无效",
"DESCRIPTION": "请选择一个扩展名为 PNG、JPG、JPEG、JFIF 或 WEBP 的有效图片", "DESCRIPTION": "请选择/粘貼一个扩展名为 PNG、JPG、JPEG、JFIF 或 WEBP 的有效图片",
"ADDITIONAL_DESCRIPTION": "请拖放图片" "ADDITIONAL_DESCRIPTION": "請拖放一個圖像",
"CLIPBOARD_DESCRIPTION": "剪貼板中未找到可粘貼的圖像文件!"
}, },
"NO_IMAGE_ERROR": { "NO_IMAGE_ERROR": {
"TITLE": "未选择图片", "TITLE": "未选择图片",

View File

@ -2,4 +2,4 @@
/// <reference types="next/image-types/global" /> /// <reference types="next/image-types/global" />
// NOTE: This file should not be edited // NOTE: This file should not be edited
// see https://nextjs.org/docs/basic-features/typescript for more information. // see https://nextjs.org/docs/pages/building-your-application/configuring/typescript for more information.