[photos-desktop] Disable node integration - Part x/x (#1176)
This continues the refactoring to disable node integration in our rendered process. The code is still in a WIP state, and more PRs in this series will come.
This commit is contained in:
commit
22e57669fb
21 changed files with 191 additions and 348 deletions
|
@ -42,13 +42,6 @@ This spins up a server for serving files using a protocol handler inside our
|
|||
Electron process. This allows us to directly use the output produced by
|
||||
`next build` for loading into our renderer process.
|
||||
|
||||
### electron-reload
|
||||
|
||||
Reloads contents of the BrowserWindow (renderer process) when source files are
|
||||
changed.
|
||||
|
||||
* TODO (MR): Do we need this? Isn't the next-electron-server HMR covering this?
|
||||
|
||||
## DX
|
||||
|
||||
See [web/docs/dependencies#DX](../../web/docs/dependencies.md#dx) for the
|
||||
|
|
|
@ -25,7 +25,6 @@
|
|||
"chokidar": "^3.5.3",
|
||||
"compare-versions": "^6.1.0",
|
||||
"electron-log": "^4.3.5",
|
||||
"electron-reload": "^2.0.0-alpha.1",
|
||||
"electron-store": "^8.0.1",
|
||||
"electron-updater": "^4.3.8",
|
||||
"ffmpeg-static": "^5.1.0",
|
||||
|
@ -33,17 +32,13 @@
|
|||
"html-entities": "^2.4.0",
|
||||
"jpeg-js": "^0.4.4",
|
||||
"next-electron-server": "^1",
|
||||
"node-fetch": "^2.6.7",
|
||||
"node-stream-zip": "^1.15.0",
|
||||
"onnxruntime-node": "^1.16.3",
|
||||
"promise-fs": "^2.1.1"
|
||||
"onnxruntime-node": "^1.16.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/auto-launch": "^5.0.2",
|
||||
"@types/ffmpeg-static": "^3.0.1",
|
||||
"@types/get-folder-size": "^2.0.0",
|
||||
"@types/node-fetch": "^2.6.2",
|
||||
"@types/promise-fs": "^2.1.1",
|
||||
"@typescript-eslint/eslint-plugin": "^7",
|
||||
"@typescript-eslint/parser": "^7",
|
||||
"concurrently": "^8",
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { ipcRenderer } from "electron/renderer";
|
||||
import { existsSync } from "node:fs";
|
||||
import * as fs from "node:fs/promises";
|
||||
import path from "path";
|
||||
import { existsSync, mkdir, rmSync } from "promise-fs";
|
||||
import { DiskCache } from "../services/diskCache";
|
||||
|
||||
const ENTE_CACHE_DIR_NAME = "ente";
|
||||
|
@ -21,16 +22,14 @@ export async function openDiskCache(
|
|||
cacheLimitInBytes?: number,
|
||||
) {
|
||||
const cacheBucketDir = await getCacheBucketDir(cacheName);
|
||||
if (!existsSync(cacheBucketDir)) {
|
||||
await mkdir(cacheBucketDir, { recursive: true });
|
||||
}
|
||||
await fs.mkdir(cacheBucketDir, { recursive: true });
|
||||
return new DiskCache(cacheBucketDir, cacheLimitInBytes);
|
||||
}
|
||||
|
||||
export async function deleteDiskCache(cacheName: string) {
|
||||
const cacheBucketDir = await getCacheBucketDir(cacheName);
|
||||
if (existsSync(cacheBucketDir)) {
|
||||
rmSync(cacheBucketDir, { recursive: true, force: true });
|
||||
await fs.rm(cacheBucketDir, { recursive: true, force: true });
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
|
|
|
@ -1,23 +0,0 @@
|
|||
import * as fs from "promise-fs";
|
||||
import { writeStream } from "./../services/fs";
|
||||
|
||||
export const exists = (path: string) => {
|
||||
return fs.existsSync(path);
|
||||
};
|
||||
|
||||
export const checkExistsAndCreateDir = async (dirPath: string) => {
|
||||
if (!fs.existsSync(dirPath)) {
|
||||
await fs.mkdir(dirPath);
|
||||
}
|
||||
};
|
||||
|
||||
export const saveStreamToDisk = async (
|
||||
filePath: string,
|
||||
fileStream: ReadableStream<Uint8Array>,
|
||||
) => {
|
||||
await writeStream(filePath, fileStream);
|
||||
};
|
||||
|
||||
export const saveFileToDisk = async (path: string, fileData: string) => {
|
||||
await fs.writeFile(path, fileData);
|
||||
};
|
|
@ -1,5 +1,4 @@
|
|||
import { app, BrowserWindow } from "electron";
|
||||
import electronReload from "electron-reload";
|
||||
import serveNextAt from "next-electron-server";
|
||||
import { initWatcher } from "./services/chokidar";
|
||||
import { isDev } from "./utils/common";
|
||||
|
@ -42,19 +41,6 @@ export const setIsUpdateAvailable = (value: boolean): void => {
|
|||
updateIsAvailable = value;
|
||||
};
|
||||
|
||||
/**
|
||||
* Hot reload the main process if anything changes in the source directory that
|
||||
* we're running from.
|
||||
*
|
||||
* In particular, this gets triggered when the `tsc -w` rebuilds JS files in the
|
||||
* `app/` directory when we change the TS files in the `src/` directory.
|
||||
*/
|
||||
const setupMainHotReload = () => {
|
||||
if (isDev) {
|
||||
electronReload(__dirname, {});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* The URL where the renderer HTML is being served from.
|
||||
*/
|
||||
|
@ -77,7 +63,6 @@ const setupRendererServer = () => {
|
|||
serveNextAt(rendererURL);
|
||||
};
|
||||
|
||||
setupMainHotReload();
|
||||
setupRendererServer();
|
||||
setupLogging(isDev);
|
||||
|
||||
|
|
|
@ -28,18 +28,12 @@
|
|||
*/
|
||||
|
||||
import { contextBridge, ipcRenderer } from "electron";
|
||||
import { existsSync } from "fs";
|
||||
import { createWriteStream, existsSync } from "node:fs";
|
||||
import * as fs from "node:fs/promises";
|
||||
import { Readable } from "node:stream";
|
||||
import path from "path";
|
||||
import * as fs from "promise-fs";
|
||||
import { Readable } from "stream";
|
||||
import { deleteDiskCache, openDiskCache } from "./api/cache";
|
||||
import { logToDisk, openLogDirectory } from "./api/common";
|
||||
import {
|
||||
checkExistsAndCreateDir,
|
||||
exists,
|
||||
saveFileToDisk,
|
||||
saveStreamToDisk,
|
||||
} from "./api/export";
|
||||
import { runFFmpegCmd } from "./api/ffmpeg";
|
||||
import { getDirFiles } from "./api/fs";
|
||||
import { convertToJPEG, generateImageThumbnail } from "./api/imageProcessor";
|
||||
|
@ -67,8 +61,12 @@ import {
|
|||
} from "./api/watch";
|
||||
import { setupLogging } from "./utils/logging";
|
||||
|
||||
/* Some of the code below has been duplicated to make this file self contained.
|
||||
Enhancement: consider alternatives */
|
||||
/*
|
||||
Some of the code below has been duplicated to make this file self contained
|
||||
(see the documentation at the top of why it needs to be a single file).
|
||||
|
||||
Enhancement: consider alternatives
|
||||
*/
|
||||
|
||||
/* preload: duplicated logError */
|
||||
export function logError(error: Error, message: string, info?: string): void {
|
||||
|
@ -77,10 +75,27 @@ export function logError(error: Error, message: string, info?: string): void {
|
|||
|
||||
// -
|
||||
|
||||
export const convertBrowserStreamToNode = (
|
||||
fileStream: ReadableStream<Uint8Array>,
|
||||
) => {
|
||||
const reader = fileStream.getReader();
|
||||
/* preload: duplicated writeStream */
|
||||
/**
|
||||
* Write a (web) ReadableStream to a file at the given {@link filePath}.
|
||||
*
|
||||
* The returned promise resolves when the write completes.
|
||||
*
|
||||
* @param filePath The local filesystem path where the file should be written.
|
||||
* @param readableStream A [web
|
||||
* ReadableStream](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream)
|
||||
*/
|
||||
const writeStream = (filePath: string, readableStream: ReadableStream) =>
|
||||
writeNodeStream(filePath, convertWebReadableStreamToNode(readableStream));
|
||||
|
||||
/**
|
||||
* Convert a Web ReadableStream into a Node.js ReadableStream
|
||||
*
|
||||
* This can be used to, for example, write a ReadableStream obtained via
|
||||
* `net.fetch` into a file using the Node.js `fs` APIs
|
||||
*/
|
||||
const convertWebReadableStreamToNode = (readableStream: ReadableStream) => {
|
||||
const reader = readableStream.getReader();
|
||||
const rs = new Readable();
|
||||
|
||||
rs._read = async () => {
|
||||
|
@ -101,11 +116,11 @@ export const convertBrowserStreamToNode = (
|
|||
return rs;
|
||||
};
|
||||
|
||||
export async function writeNodeStream(
|
||||
const writeNodeStream = async (
|
||||
filePath: string,
|
||||
fileStream: NodeJS.ReadableStream,
|
||||
) {
|
||||
const writeable = fs.createWriteStream(filePath);
|
||||
) => {
|
||||
const writeable = createWriteStream(filePath);
|
||||
|
||||
fileStream.on("error", (error) => {
|
||||
writeable.destroy(error); // Close the writable stream with an error
|
||||
|
@ -115,23 +130,26 @@ export async function writeNodeStream(
|
|||
|
||||
await new Promise((resolve, reject) => {
|
||||
writeable.on("finish", resolve);
|
||||
writeable.on("error", async (e) => {
|
||||
writeable.on("error", async (e: unknown) => {
|
||||
if (existsSync(filePath)) {
|
||||
await fs.unlink(filePath);
|
||||
}
|
||||
reject(e);
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/* preload: duplicated writeStream */
|
||||
export async function writeStream(
|
||||
filePath: string,
|
||||
fileStream: ReadableStream<Uint8Array>,
|
||||
) {
|
||||
const readable = convertBrowserStreamToNode(fileStream);
|
||||
await writeNodeStream(filePath, readable);
|
||||
}
|
||||
// - Export
|
||||
|
||||
const exists = (path: string) => existsSync(path);
|
||||
|
||||
const checkExistsAndCreateDir = (dirPath: string) =>
|
||||
fs.mkdir(dirPath, { recursive: true });
|
||||
|
||||
const saveStreamToDisk = writeStream;
|
||||
|
||||
const saveFileToDisk = (path: string, contents: string) =>
|
||||
fs.writeFile(path, contents);
|
||||
|
||||
// -
|
||||
|
||||
|
@ -154,9 +172,7 @@ async function moveFile(
|
|||
}
|
||||
// check if destination folder exists
|
||||
const destinationFolder = path.dirname(destinationPath);
|
||||
if (!existsSync(destinationFolder)) {
|
||||
await fs.mkdir(destinationFolder, { recursive: true });
|
||||
}
|
||||
await fs.mkdir(destinationFolder, { recursive: true });
|
||||
await fs.rename(sourcePath, destinationPath);
|
||||
}
|
||||
|
||||
|
@ -183,7 +199,8 @@ async function deleteFolder(folderPath: string): Promise<void> {
|
|||
if (!existsSync(folderPath)) {
|
||||
return;
|
||||
}
|
||||
if (!fs.statSync(folderPath).isDirectory()) {
|
||||
const stat = await fs.stat(folderPath);
|
||||
if (!stat.isDirectory()) {
|
||||
throw new Error("Path is not a folder");
|
||||
}
|
||||
// check if folder is empty
|
||||
|
@ -201,17 +218,18 @@ async function rename(oldPath: string, newPath: string) {
|
|||
await fs.rename(oldPath, newPath);
|
||||
}
|
||||
|
||||
function deleteFile(filePath: string): void {
|
||||
const deleteFile = async (filePath: string) => {
|
||||
if (!existsSync(filePath)) {
|
||||
return;
|
||||
}
|
||||
if (!fs.statSync(filePath).isFile()) {
|
||||
const stat = await fs.stat(filePath);
|
||||
if (!stat.isFile()) {
|
||||
throw new Error("Path is not a file");
|
||||
}
|
||||
fs.rmSync(filePath);
|
||||
}
|
||||
return fs.rm(filePath);
|
||||
};
|
||||
|
||||
// -
|
||||
// - ML
|
||||
|
||||
/* preload: duplicated Model */
|
||||
export enum Model {
|
||||
|
@ -315,7 +333,7 @@ const parseExecError = (err: any) => {
|
|||
}
|
||||
};
|
||||
|
||||
// -
|
||||
// - General
|
||||
|
||||
const selectDirectory = async (): Promise<string> => {
|
||||
try {
|
||||
|
@ -349,7 +367,7 @@ const clearElectronStore = () => {
|
|||
ipcRenderer.send("clear-electron-store");
|
||||
};
|
||||
|
||||
// -
|
||||
// - App update
|
||||
|
||||
const updateAndRestart = () => {
|
||||
ipcRenderer.send("update-and-restart");
|
||||
|
@ -410,10 +428,12 @@ setupLogging();
|
|||
// running out of memory, causing the app to crash as it copies it over across
|
||||
// the processes.
|
||||
contextBridge.exposeInMainWorld("ElectronAPIs", {
|
||||
// - Export
|
||||
exists,
|
||||
checkExistsAndCreateDir,
|
||||
saveStreamToDisk,
|
||||
saveFileToDisk,
|
||||
|
||||
selectDirectory,
|
||||
clearElectronStore,
|
||||
readTextFile,
|
||||
|
@ -438,20 +458,29 @@ contextBridge.exposeInMainWorld("ElectronAPIs", {
|
|||
updateWatchMappingIgnoredFiles,
|
||||
logToDisk,
|
||||
convertToJPEG,
|
||||
openLogDirectory,
|
||||
registerUpdateEventListener,
|
||||
updateAndRestart,
|
||||
skipAppUpdate,
|
||||
getAppVersion,
|
||||
|
||||
runFFmpegCmd,
|
||||
muteUpdateNotification,
|
||||
generateImageThumbnail,
|
||||
registerForegroundEventListener,
|
||||
openDirectory,
|
||||
moveFile,
|
||||
deleteFolder,
|
||||
rename,
|
||||
deleteFile,
|
||||
|
||||
// General
|
||||
getAppVersion,
|
||||
openDirectory,
|
||||
|
||||
// Logging
|
||||
openLogDirectory,
|
||||
|
||||
// - App update
|
||||
updateAndRestart,
|
||||
skipAppUpdate,
|
||||
muteUpdateNotification,
|
||||
|
||||
// - ML
|
||||
computeImageEmbedding,
|
||||
computeTextEmbedding,
|
||||
});
|
||||
|
|
|
@ -2,10 +2,8 @@ import { compareVersions } from "compare-versions";
|
|||
import { app, BrowserWindow } from "electron";
|
||||
import { default as ElectronLog, default as log } from "electron-log";
|
||||
import { autoUpdater } from "electron-updater";
|
||||
import fetch from "node-fetch";
|
||||
import { setIsAppQuitting, setIsUpdateAvailable } from "../main";
|
||||
import { AppUpdateInfo, GetFeatureFlagResponse } from "../types";
|
||||
import { isPlatform } from "../utils/common/platform";
|
||||
import { AppUpdateInfo } from "../types";
|
||||
import { logErrorSentry } from "./sentry";
|
||||
import {
|
||||
clearMuteUpdateNotificationVersion,
|
||||
|
@ -64,56 +62,42 @@ async function checkForUpdateAndNotify(mainWindow: BrowserWindow) {
|
|||
);
|
||||
return;
|
||||
}
|
||||
const desktopCutoffVersion = await getDesktopCutoffVersion();
|
||||
|
||||
let timeout: NodeJS.Timeout;
|
||||
log.debug("attempting auto update");
|
||||
autoUpdater.downloadUpdate();
|
||||
const muteUpdateNotificationVersion =
|
||||
getMuteUpdateNotificationVersion();
|
||||
if (
|
||||
desktopCutoffVersion &&
|
||||
isPlatform("mac") &&
|
||||
compareVersions(
|
||||
updateCheckResult.updateInfo.version,
|
||||
desktopCutoffVersion,
|
||||
) > 0
|
||||
muteUpdateNotificationVersion &&
|
||||
updateCheckResult.updateInfo.version ===
|
||||
muteUpdateNotificationVersion
|
||||
) {
|
||||
log.debug("auto update not possible due to key change");
|
||||
log.info(
|
||||
"user chose to mute update notification for version ",
|
||||
updateCheckResult.updateInfo.version,
|
||||
);
|
||||
return;
|
||||
}
|
||||
autoUpdater.on("update-downloaded", () => {
|
||||
timeout = setTimeout(
|
||||
() =>
|
||||
showUpdateDialog(mainWindow, {
|
||||
autoUpdatable: true,
|
||||
version: updateCheckResult.updateInfo.version,
|
||||
}),
|
||||
FIVE_MIN_IN_MICROSECOND,
|
||||
);
|
||||
});
|
||||
autoUpdater.on("error", (error) => {
|
||||
clearTimeout(timeout);
|
||||
logErrorSentry(error, "auto update failed");
|
||||
showUpdateDialog(mainWindow, {
|
||||
autoUpdatable: false,
|
||||
version: updateCheckResult.updateInfo.version,
|
||||
});
|
||||
} else {
|
||||
let timeout: NodeJS.Timeout;
|
||||
log.debug("attempting auto update");
|
||||
autoUpdater.downloadUpdate();
|
||||
const muteUpdateNotificationVersion =
|
||||
getMuteUpdateNotificationVersion();
|
||||
if (
|
||||
muteUpdateNotificationVersion &&
|
||||
updateCheckResult.updateInfo.version ===
|
||||
muteUpdateNotificationVersion
|
||||
) {
|
||||
log.info(
|
||||
"user chose to mute update notification for version ",
|
||||
updateCheckResult.updateInfo.version,
|
||||
);
|
||||
return;
|
||||
}
|
||||
autoUpdater.on("update-downloaded", () => {
|
||||
timeout = setTimeout(
|
||||
() =>
|
||||
showUpdateDialog(mainWindow, {
|
||||
autoUpdatable: true,
|
||||
version: updateCheckResult.updateInfo.version,
|
||||
}),
|
||||
FIVE_MIN_IN_MICROSECOND,
|
||||
);
|
||||
});
|
||||
autoUpdater.on("error", (error) => {
|
||||
clearTimeout(timeout);
|
||||
logErrorSentry(error, "auto update failed");
|
||||
showUpdateDialog(mainWindow, {
|
||||
autoUpdatable: false,
|
||||
version: updateCheckResult.updateInfo.version,
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
setIsUpdateAvailable(true);
|
||||
} catch (e) {
|
||||
logErrorSentry(e, "checkForUpdateAndNotify failed");
|
||||
|
@ -138,18 +122,6 @@ export function muteUpdateNotification(version: string) {
|
|||
setMuteUpdateNotificationVersion(version);
|
||||
}
|
||||
|
||||
async function getDesktopCutoffVersion() {
|
||||
try {
|
||||
const featureFlags = (
|
||||
await fetch("https://static.ente.io/feature_flags.json")
|
||||
).json() as GetFeatureFlagResponse;
|
||||
return featureFlags.desktopCutoffVersion;
|
||||
} catch (e) {
|
||||
logErrorSentry(e, "failed to get feature flags");
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
function showUpdateDialog(
|
||||
mainWindow: BrowserWindow,
|
||||
updateInfo: AppUpdateInfo,
|
||||
|
|
|
@ -1,17 +1,15 @@
|
|||
import { app } from "electron";
|
||||
import * as log from "electron-log";
|
||||
import { app, net } from "electron/main";
|
||||
import { existsSync } from "fs";
|
||||
import fs from "fs/promises";
|
||||
import fetch from "node-fetch";
|
||||
import path from "path";
|
||||
import { readFile } from "promise-fs";
|
||||
import * as fs from "node:fs/promises";
|
||||
import * as path from "node:path";
|
||||
import util from "util";
|
||||
import { CustomErrors } from "../constants/errors";
|
||||
import { Model } from "../types";
|
||||
import Tokenizer from "../utils/clip-bpe-ts/mod";
|
||||
import { isDev } from "../utils/common";
|
||||
import { getPlatform } from "../utils/common/platform";
|
||||
import { writeNodeStream } from "./fs";
|
||||
import { writeStream } from "./fs";
|
||||
import { logErrorSentry } from "./sentry";
|
||||
const shellescape = require("any-shell-escape");
|
||||
const execAsync = util.promisify(require("child_process").exec);
|
||||
|
@ -80,13 +78,11 @@ function getModelSavePath(modelName: string) {
|
|||
async function downloadModel(saveLocation: string, url: string) {
|
||||
// confirm that the save location exists
|
||||
const saveDir = path.dirname(saveLocation);
|
||||
if (!existsSync(saveDir)) {
|
||||
log.info("creating model save dir");
|
||||
await fs.mkdir(saveDir, { recursive: true });
|
||||
}
|
||||
await fs.mkdir(saveDir, { recursive: true });
|
||||
log.info("downloading clip model");
|
||||
const resp = await fetch(url);
|
||||
await writeNodeStream(saveLocation, resp.body);
|
||||
const res = await net.fetch(url);
|
||||
if (!res.ok) throw new Error(`Failed to fetch ${url}: HTTP ${res.status}`);
|
||||
await writeStream(saveLocation, res.body);
|
||||
log.info("clip model downloaded");
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import path from "path";
|
||||
import { existsSync, stat, unlink } from "promise-fs";
|
||||
import { existsSync } from "node:fs";
|
||||
import * as fs from "node:fs/promises";
|
||||
import * as path from "node:path";
|
||||
import DiskLRUService from "../services/diskLRU";
|
||||
import { LimitedCache } from "../types/cache";
|
||||
import { getFileStream, writeStream } from "./fs";
|
||||
|
@ -28,19 +29,19 @@ export class DiskCache implements LimitedCache {
|
|||
): Promise<Response> {
|
||||
const cachePath = path.join(this.cacheBucketDir, cacheKey);
|
||||
if (existsSync(cachePath)) {
|
||||
const fileStats = await stat(cachePath);
|
||||
const fileStats = await fs.stat(cachePath);
|
||||
if (sizeInBytes && fileStats.size !== sizeInBytes) {
|
||||
logError(
|
||||
Error(),
|
||||
"Cache key exists but size does not match. Deleting cache key.",
|
||||
);
|
||||
unlink(cachePath).catch((e) => {
|
||||
fs.unlink(cachePath).catch((e) => {
|
||||
if (e.code === "ENOENT") return;
|
||||
logError(e, "Failed to delete cache key");
|
||||
});
|
||||
return undefined;
|
||||
}
|
||||
DiskLRUService.touch(cachePath);
|
||||
DiskLRUService.markUse(cachePath);
|
||||
return new Response(await getFileStream(cachePath));
|
||||
} else {
|
||||
return undefined;
|
||||
|
@ -49,7 +50,7 @@ export class DiskCache implements LimitedCache {
|
|||
async delete(cacheKey: string): Promise<boolean> {
|
||||
const cachePath = path.join(this.cacheBucketDir, cacheKey);
|
||||
if (existsSync(cachePath)) {
|
||||
await unlink(cachePath);
|
||||
await fs.unlink(cachePath);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import getFolderSize from "get-folder-size";
|
||||
import path from "path";
|
||||
import { close, open, readdir, stat, unlink, utimes } from "promise-fs";
|
||||
import * as fs from "node:fs/promises";
|
||||
import * as path from "node:path";
|
||||
import { logError } from "../services/logging";
|
||||
|
||||
export interface LeastRecentlyUsedResult {
|
||||
|
@ -12,19 +12,10 @@ class DiskLRUService {
|
|||
private isRunning: Promise<any> = null;
|
||||
private reRun: boolean = false;
|
||||
|
||||
async touch(path: string) {
|
||||
try {
|
||||
const time = new Date();
|
||||
await utimes(path, time, time);
|
||||
} catch (err) {
|
||||
logError(err, "utimes method touch failed");
|
||||
try {
|
||||
await close(await open(path, "w"));
|
||||
} catch (e) {
|
||||
logError(e, "open-close method touch failed");
|
||||
}
|
||||
// log and ignore
|
||||
}
|
||||
/** Mark "use" of a given file by updating its modified time */
|
||||
async markUse(path: string) {
|
||||
const now = new Date();
|
||||
await fs.utimes(path, now, now);
|
||||
}
|
||||
|
||||
enforceCacheSizeLimit(cacheDir: string, maxSize: number) {
|
||||
|
@ -53,7 +44,7 @@ class DiskLRUService {
|
|||
const leastRecentlyUsed =
|
||||
await this.findLeastRecentlyUsed(cacheDir);
|
||||
try {
|
||||
await unlink(leastRecentlyUsed.path);
|
||||
await fs.unlink(leastRecentlyUsed.path);
|
||||
} catch (e) {
|
||||
// ENOENT: File not found
|
||||
// which can be ignored as we are trying to delete the file anyway
|
||||
|
@ -81,16 +72,15 @@ class DiskLRUService {
|
|||
): Promise<LeastRecentlyUsedResult> {
|
||||
result = result || { atime: new Date(), path: "" };
|
||||
|
||||
const files = await readdir(dir);
|
||||
const files = await fs.readdir(dir);
|
||||
for (const file of files) {
|
||||
const newBase = path.join(dir, file);
|
||||
const stats = await stat(newBase);
|
||||
if (stats.isDirectory()) {
|
||||
const st = await fs.stat(newBase);
|
||||
if (st.isDirectory()) {
|
||||
result = await this.findLeastRecentlyUsed(newBase, result);
|
||||
} else {
|
||||
const { atime } = await stat(newBase);
|
||||
|
||||
if (atime.getTime() < result.atime.getTime()) {
|
||||
const { atime } = st;
|
||||
if (st.atime.getTime() < result.atime.getTime()) {
|
||||
result = {
|
||||
atime,
|
||||
path: newBase,
|
||||
|
|
|
@ -1,17 +1,15 @@
|
|||
import log from "electron-log";
|
||||
import pathToFfmpeg from "ffmpeg-static";
|
||||
import { existsSync } from "fs";
|
||||
import { readFile, rmSync, writeFile } from "promise-fs";
|
||||
import { existsSync } from "node:fs";
|
||||
import * as fs from "node:fs/promises";
|
||||
import util from "util";
|
||||
import { promiseWithTimeout } from "../utils/common";
|
||||
import { CustomErrors } from "../constants/errors";
|
||||
import { generateTempFilePath, getTempDirPath } from "../utils/temp";
|
||||
import { logErrorSentry } from "./sentry";
|
||||
const shellescape = require("any-shell-escape");
|
||||
|
||||
const execAsync = util.promisify(require("child_process").exec);
|
||||
|
||||
const FFMPEG_EXECUTION_WAIT_TIME = 30 * 1000;
|
||||
|
||||
const INPUT_PATH_PLACEHOLDER = "INPUT";
|
||||
const FFMPEG_PLACEHOLDER = "FFMPEG";
|
||||
const OUTPUT_PATH_PLACEHOLDER = "OUTPUT";
|
||||
|
@ -70,10 +68,7 @@ export async function runFFmpegCmd(
|
|||
if (dontTimeout) {
|
||||
await execAsync(escapedCmd);
|
||||
} else {
|
||||
await promiseWithTimeout(
|
||||
execAsync(escapedCmd),
|
||||
FFMPEG_EXECUTION_WAIT_TIME,
|
||||
);
|
||||
await promiseWithTimeout(execAsync(escapedCmd), 30 * 1000);
|
||||
}
|
||||
if (!existsSync(tempOutputFilePath)) {
|
||||
throw new Error("ffmpeg output file not found");
|
||||
|
@ -85,14 +80,14 @@ export async function runFFmpegCmd(
|
|||
"ms",
|
||||
);
|
||||
|
||||
const outputFile = await readFile(tempOutputFilePath);
|
||||
const outputFile = await fs.readFile(tempOutputFilePath);
|
||||
return new Uint8Array(outputFile);
|
||||
} catch (e) {
|
||||
logErrorSentry(e, "ffmpeg run command error");
|
||||
throw e;
|
||||
} finally {
|
||||
try {
|
||||
rmSync(tempOutputFilePath, { force: true });
|
||||
await fs.rm(tempOutputFilePath, { force: true });
|
||||
} catch (e) {
|
||||
logErrorSentry(e, "failed to remove tempOutputFile");
|
||||
}
|
||||
|
@ -114,7 +109,7 @@ const ffmpegBinaryPath = () => {
|
|||
|
||||
export async function writeTempFile(fileStream: Uint8Array, fileName: string) {
|
||||
const tempFilePath = await generateTempFilePath(fileName);
|
||||
await writeFile(tempFilePath, fileStream);
|
||||
await fs.writeFile(tempFilePath, fileStream);
|
||||
return tempFilePath;
|
||||
}
|
||||
|
||||
|
@ -126,5 +121,29 @@ export async function deleteTempFile(tempFilePath: string) {
|
|||
"tried to delete a non temp file",
|
||||
);
|
||||
}
|
||||
rmSync(tempFilePath, { force: true });
|
||||
await fs.rm(tempFilePath, { force: true });
|
||||
}
|
||||
|
||||
export const promiseWithTimeout = async <T>(
|
||||
request: Promise<T>,
|
||||
timeout: number,
|
||||
): Promise<T> => {
|
||||
const timeoutRef: {
|
||||
current: NodeJS.Timeout;
|
||||
} = { current: null };
|
||||
const rejectOnTimeout = new Promise<null>((_, reject) => {
|
||||
timeoutRef.current = setTimeout(
|
||||
() => reject(Error(CustomErrors.WAIT_TIME_EXCEEDED)),
|
||||
timeout,
|
||||
);
|
||||
});
|
||||
const requestWithTimeOutCancellation = async () => {
|
||||
const resp = await request;
|
||||
clearTimeout(timeoutRef.current);
|
||||
return resp;
|
||||
};
|
||||
return await Promise.race([
|
||||
requestWithTimeOutCancellation(),
|
||||
rejectOnTimeout,
|
||||
]);
|
||||
};
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { existsSync } from "fs";
|
||||
import StreamZip from "node-stream-zip";
|
||||
import path from "path";
|
||||
import * as fs from "promise-fs";
|
||||
import { createWriteStream, existsSync } from "node:fs";
|
||||
import * as fs from "node:fs/promises";
|
||||
import * as path from "node:path";
|
||||
import { Readable } from "stream";
|
||||
import { ElectronFile } from "../types";
|
||||
import { logError } from "./logging";
|
||||
|
@ -212,7 +212,7 @@ export async function writeNodeStream(
|
|||
filePath: string,
|
||||
fileStream: NodeJS.ReadableStream,
|
||||
) {
|
||||
const writeable = fs.createWriteStream(filePath);
|
||||
const writeable = createWriteStream(filePath);
|
||||
|
||||
fileStream.on("error", (error) => {
|
||||
writeable.destroy(error); // Close the writable stream with an error
|
||||
|
@ -222,7 +222,7 @@ export async function writeNodeStream(
|
|||
|
||||
await new Promise((resolve, reject) => {
|
||||
writeable.on("finish", resolve);
|
||||
writeable.on("error", async (e) => {
|
||||
writeable.on("error", async (e: unknown) => {
|
||||
if (existsSync(filePath)) {
|
||||
await fs.unlink(filePath);
|
||||
}
|
||||
|
|
|
@ -2,9 +2,8 @@ import { exec } from "child_process";
|
|||
import util from "util";
|
||||
|
||||
import log from "electron-log";
|
||||
import { existsSync, rmSync } from "fs";
|
||||
import * as fs from "node:fs/promises";
|
||||
import path from "path";
|
||||
import { readFile, writeFile } from "promise-fs";
|
||||
import { CustomErrors } from "../constants/errors";
|
||||
import { isDev } from "../utils/common";
|
||||
import { isPlatform } from "../utils/common/platform";
|
||||
|
@ -88,28 +87,22 @@ export async function convertToJPEG(
|
|||
tempInputFilePath = await generateTempFilePath(filename);
|
||||
tempOutputFilePath = await generateTempFilePath("output.jpeg");
|
||||
|
||||
await writeFile(tempInputFilePath, fileData);
|
||||
await fs.writeFile(tempInputFilePath, fileData);
|
||||
|
||||
await runConvertCommand(tempInputFilePath, tempOutputFilePath);
|
||||
|
||||
if (!existsSync(tempOutputFilePath)) {
|
||||
throw new Error("heic convert output file not found");
|
||||
}
|
||||
const convertedFileData = new Uint8Array(
|
||||
await readFile(tempOutputFilePath),
|
||||
);
|
||||
return convertedFileData;
|
||||
return new Uint8Array(await fs.readFile(tempOutputFilePath));
|
||||
} catch (e) {
|
||||
logErrorSentry(e, "failed to convert heic");
|
||||
throw e;
|
||||
} finally {
|
||||
try {
|
||||
rmSync(tempInputFilePath, { force: true });
|
||||
await fs.rm(tempInputFilePath, { force: true });
|
||||
} catch (e) {
|
||||
logErrorSentry(e, "failed to remove tempInputFile");
|
||||
}
|
||||
try {
|
||||
rmSync(tempOutputFilePath, { force: true });
|
||||
await fs.rm(tempOutputFilePath, { force: true });
|
||||
} catch (e) {
|
||||
logErrorSentry(e, "failed to remove tempOutputFile");
|
||||
}
|
||||
|
@ -183,10 +176,7 @@ export async function generateImageThumbnail(
|
|||
quality,
|
||||
);
|
||||
|
||||
if (!existsSync(tempOutputFilePath)) {
|
||||
throw new Error("output thumbnail file not found");
|
||||
}
|
||||
thumbnail = new Uint8Array(await readFile(tempOutputFilePath));
|
||||
thumbnail = new Uint8Array(await fs.readFile(tempOutputFilePath));
|
||||
quality -= 10;
|
||||
} while (thumbnail.length > maxSize && quality > MIN_QUALITY);
|
||||
return thumbnail;
|
||||
|
@ -195,7 +185,7 @@ export async function generateImageThumbnail(
|
|||
throw e;
|
||||
} finally {
|
||||
try {
|
||||
rmSync(tempOutputFilePath, { force: true });
|
||||
await fs.rm(tempOutputFilePath, { force: true });
|
||||
} catch (e) {
|
||||
logErrorSentry(e, "failed to remove tempOutputFile");
|
||||
}
|
||||
|
|
|
@ -77,10 +77,6 @@ export interface AppUpdateInfo {
|
|||
version: string;
|
||||
}
|
||||
|
||||
export interface GetFeatureFlagResponse {
|
||||
desktopCutoffVersion?: string;
|
||||
}
|
||||
|
||||
export enum Model {
|
||||
GGML_CLIP = "ggml-clip",
|
||||
ONNX_CLIP = "onnx-clip",
|
||||
|
|
|
@ -1,27 +1,2 @@
|
|||
import { app } from "electron";
|
||||
import { CustomErrors } from "../../constants/errors";
|
||||
export const isDev = !app.isPackaged;
|
||||
|
||||
export const promiseWithTimeout = async <T>(
|
||||
request: Promise<T>,
|
||||
timeout: number,
|
||||
): Promise<T> => {
|
||||
const timeoutRef: {
|
||||
current: NodeJS.Timeout;
|
||||
} = { current: null };
|
||||
const rejectOnTimeout = new Promise<null>((_, reject) => {
|
||||
timeoutRef.current = setTimeout(
|
||||
() => reject(Error(CustomErrors.WAIT_TIME_EXCEEDED)),
|
||||
timeout,
|
||||
);
|
||||
});
|
||||
const requestWithTimeOutCancellation = async () => {
|
||||
const resp = await request;
|
||||
clearTimeout(timeoutRef.current);
|
||||
return resp;
|
||||
};
|
||||
return await Promise.race([
|
||||
requestWithTimeOutCancellation(),
|
||||
rejectOnTimeout,
|
||||
]);
|
||||
};
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import { app, BrowserWindow, Menu, nativeImage, Tray } from "electron";
|
||||
import ElectronLog from "electron-log";
|
||||
import { existsSync } from "node:fs";
|
||||
import os from "os";
|
||||
import path from "path";
|
||||
import { existsSync } from "promise-fs";
|
||||
import util from "util";
|
||||
import { rendererURL } from "../main";
|
||||
import { setupAutoUpdater } from "../services/appUpdater";
|
||||
|
|
|
@ -1,17 +1,14 @@
|
|||
import { app } from "electron";
|
||||
import { existsSync } from "node:fs";
|
||||
import * as fs from "node:fs/promises";
|
||||
import path from "path";
|
||||
import { existsSync, mkdir } from "promise-fs";
|
||||
|
||||
const ENTE_TEMP_DIRECTORY = "ente";
|
||||
|
||||
const CHARACTERS =
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
||||
|
||||
export async function getTempDirPath() {
|
||||
const tempDirPath = path.join(app.getPath("temp"), ENTE_TEMP_DIRECTORY);
|
||||
if (!existsSync(tempDirPath)) {
|
||||
await mkdir(tempDirPath);
|
||||
}
|
||||
const tempDirPath = path.join(app.getPath("temp"), "ente");
|
||||
await fs.mkdir(tempDirPath, { recursive: true });
|
||||
return tempDirPath;
|
||||
}
|
||||
|
||||
|
|
|
@ -205,11 +205,6 @@
|
|||
"@nodelib/fs.scandir" "2.1.5"
|
||||
fastq "^1.6.0"
|
||||
|
||||
"@octetstream/promisify@2.0.2":
|
||||
version "2.0.2"
|
||||
resolved "https://registry.yarnpkg.com/@octetstream/promisify/-/promisify-2.0.2.tgz#29ac3bd7aefba646db670227f895d812c1a19615"
|
||||
integrity sha512-7XHoRB61hxsz8lBQrjC1tq/3OEIgpvGWg6DKAdwi7WRzruwkmsdwmOoUXbU4Dtd4RSOMDwed0SkP3y8UlMt1Bg==
|
||||
|
||||
"@pkgr/core@^0.1.0":
|
||||
version "0.1.1"
|
||||
resolved "https://registry.yarnpkg.com/@pkgr/core/-/core-0.1.1.tgz#1ec17e2edbec25c8306d424ecfbf13c7de1aaa31"
|
||||
|
@ -293,14 +288,6 @@
|
|||
resolved "https://registry.yarnpkg.com/@types/ms/-/ms-0.7.31.tgz#31b7ca6407128a3d2bbc27fe2d21b345397f6197"
|
||||
integrity sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA==
|
||||
|
||||
"@types/node-fetch@^2.6.2":
|
||||
version "2.6.2"
|
||||
resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.6.2.tgz#d1a9c5fd049d9415dce61571557104dec3ec81da"
|
||||
integrity sha512-DHqhlq5jeESLy19TYhLakJ07kNumXWjcDdxXsLUMJZ6ue8VZJj4kLPQVE/2mdHh3xZziNF1xppu5lwmS53HR+A==
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
form-data "^3.0.0"
|
||||
|
||||
"@types/node@*":
|
||||
version "18.0.3"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-18.0.3.tgz#463fc47f13ec0688a33aec75d078a0541a447199"
|
||||
|
@ -329,13 +316,6 @@
|
|||
"@types/node" "*"
|
||||
xmlbuilder ">=11.0.1"
|
||||
|
||||
"@types/promise-fs@^2.1.1":
|
||||
version "2.1.2"
|
||||
resolved "https://registry.yarnpkg.com/@types/promise-fs/-/promise-fs-2.1.2.tgz#7ef6ab00c7fbc68081e34e560d2f008d3dd27fd2"
|
||||
integrity sha512-s3YON1LmplAUVrvTT2d1I0m2Rk0hSgc/1l5/krnU96YpP4NG9VEN/qopaFv8yk5a2Z+AgYzafS1LCP+kQH0MYw==
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/responselike@^1.0.0":
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/responselike/-/responselike-1.0.0.tgz#251f4fe7d154d2bad125abe1b429b23afd262e29"
|
||||
|
@ -809,7 +789,7 @@ chalk@^4.0.0, chalk@^4.0.2, chalk@^4.1.2:
|
|||
ansi-styles "^4.1.0"
|
||||
supports-color "^7.1.0"
|
||||
|
||||
chokidar@^3.5.2, chokidar@^3.5.3:
|
||||
chokidar@^3.5.3:
|
||||
version "3.5.3"
|
||||
resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd"
|
||||
integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==
|
||||
|
@ -1182,13 +1162,6 @@ electron-publish@24.5.0:
|
|||
lazy-val "^1.0.5"
|
||||
mime "^2.5.2"
|
||||
|
||||
electron-reload@^2.0.0-alpha.1:
|
||||
version "2.0.0-alpha.1"
|
||||
resolved "https://registry.yarnpkg.com/electron-reload/-/electron-reload-2.0.0-alpha.1.tgz#6cad98df96695ca1d5462dc9407f7c620028ce99"
|
||||
integrity sha512-hTde7gv0TEqxbxlB3pj2CwoyCQ9sdiQrcP8GkpzhosxyVeYM3mZbMEVKCZK3L0fED7Mz5A9IWmK7zEvi4H3P1g==
|
||||
dependencies:
|
||||
chokidar "^3.5.2"
|
||||
|
||||
electron-store@^8.0.1:
|
||||
version "8.0.2"
|
||||
resolved "https://registry.yarnpkg.com/electron-store/-/electron-store-8.0.2.tgz#95c8cf81c1e1cf48b24f3ceeea24b921c1ff62d7"
|
||||
|
@ -1503,15 +1476,6 @@ flatted@^3.1.0:
|
|||
resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.6.tgz#022e9218c637f9f3fc9c35ab9c9193f05add60b2"
|
||||
integrity sha512-0sQoMh9s0BYsm+12Huy/rkKxVu4R1+r96YX5cG44rHV0pQ6iC3Q+mkoMFaGWObMFYQxCVT+ssG1ksneA2MI9KQ==
|
||||
|
||||
form-data@^3.0.0:
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/form-data/-/form-data-3.0.1.tgz#ebd53791b78356a99af9a300d4282c4d5eb9755f"
|
||||
integrity sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==
|
||||
dependencies:
|
||||
asynckit "^0.4.0"
|
||||
combined-stream "^1.0.8"
|
||||
mime-types "^2.1.12"
|
||||
|
||||
form-data@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452"
|
||||
|
@ -2246,13 +2210,6 @@ node-addon-api@^1.6.3:
|
|||
resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-1.7.2.tgz#3df30b95720b53c24e59948b49532b662444f54d"
|
||||
integrity sha512-ibPK3iA+vaY1eEjESkQkM0BbCqFOaZMiXRTtdB0u7b4djtY6JnsjvPdUHVMg6xQt3B8fpTTWHI9A+ADjM9frzg==
|
||||
|
||||
node-fetch@^2.6.7:
|
||||
version "2.6.7"
|
||||
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad"
|
||||
integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==
|
||||
dependencies:
|
||||
whatwg-url "^5.0.0"
|
||||
|
||||
node-stream-zip@^1.15.0:
|
||||
version "1.15.0"
|
||||
resolved "https://registry.yarnpkg.com/node-stream-zip/-/node-stream-zip-1.15.0.tgz#158adb88ed8004c6c49a396b50a6a5de3bca33ea"
|
||||
|
@ -2485,13 +2442,6 @@ progress@^2.0.3:
|
|||
resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8"
|
||||
integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==
|
||||
|
||||
promise-fs@^2.1.1:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/promise-fs/-/promise-fs-2.1.1.tgz#0b725a592c165ff16157d1f13640ba390637e557"
|
||||
integrity sha512-43p7e4QzAQ3w6eyN0+gbBL7jXiZFWLWYITg9wIObqkBySu/a5K1EDcQ/S6UyB/bmiZWDA4NjTbcopKLTaKcGSw==
|
||||
dependencies:
|
||||
"@octetstream/promisify" "2.0.2"
|
||||
|
||||
promise-retry@^2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/promise-retry/-/promise-retry-2.0.1.tgz#ff747a13620ab57ba688f5fc67855410c370da22"
|
||||
|
@ -2964,11 +2914,6 @@ to-regex-range@^5.0.1:
|
|||
dependencies:
|
||||
is-number "^7.0.0"
|
||||
|
||||
tr46@~0.0.3:
|
||||
version "0.0.3"
|
||||
resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a"
|
||||
integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==
|
||||
|
||||
tree-kill@^1.2.2:
|
||||
version "1.2.2"
|
||||
resolved "https://registry.yarnpkg.com/tree-kill/-/tree-kill-1.2.2.tgz#4ca09a9092c88b73a7cdc5e8a01b507b0790a0cc"
|
||||
|
@ -3092,19 +3037,6 @@ verror@^1.10.0:
|
|||
core-util-is "1.0.2"
|
||||
extsprintf "^1.2.0"
|
||||
|
||||
webidl-conversions@^3.0.0:
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871"
|
||||
integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==
|
||||
|
||||
whatwg-url@^5.0.0:
|
||||
version "5.0.0"
|
||||
resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d"
|
||||
integrity sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==
|
||||
dependencies:
|
||||
tr46 "~0.0.3"
|
||||
webidl-conversions "^3.0.0"
|
||||
|
||||
which@^2.0.1:
|
||||
version "2.0.2"
|
||||
resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1"
|
||||
|
|
|
@ -1150,7 +1150,7 @@ class ExportService {
|
|||
videoStream,
|
||||
);
|
||||
} catch (e) {
|
||||
ElectronAPIs.deleteFile(
|
||||
await ElectronAPIs.deleteFile(
|
||||
getFileExportPath(collectionExportPath, imageExportName),
|
||||
);
|
||||
throw e;
|
||||
|
|
|
@ -51,10 +51,7 @@ import {
|
|||
import { FileTypeInfo } from "types/upload";
|
||||
import { isPlaybackPossible } from "utils/photoFrame";
|
||||
|
||||
import {
|
||||
default as ElectronAPIs,
|
||||
default as ElectronFSService,
|
||||
} from "@ente/shared/electron";
|
||||
import { default as ElectronAPIs } from "@ente/shared/electron";
|
||||
import { downloadUsingAnchor } from "@ente/shared/utils";
|
||||
import { t } from "i18next";
|
||||
import imageProcessor from "services/imageProcessor";
|
||||
|
@ -801,7 +798,7 @@ export async function downloadFileDesktop(
|
|||
videoStream,
|
||||
);
|
||||
} catch (e) {
|
||||
ElectronFSService.deleteFile(
|
||||
await ElectronAPIs.deleteFile(
|
||||
getFileExportPath(downloadPath, imageExportName),
|
||||
);
|
||||
throw e;
|
||||
|
|
|
@ -96,7 +96,7 @@ export interface ElectronAPIsType {
|
|||
openDirectory: (dirPath: string) => Promise<void>;
|
||||
moveFile: (oldPath: string, newPath: string) => Promise<void>;
|
||||
deleteFolder: (path: string) => Promise<void>;
|
||||
deleteFile: (path: string) => void;
|
||||
deleteFile: (path: string) => Promise<void>;
|
||||
rename: (oldPath: string, newPath: string) => Promise<void>;
|
||||
computeImageEmbedding: (
|
||||
model: Model,
|
||||
|
|
Loading…
Add table
Reference in a new issue