Mirrorize
This commit is contained in:
parent
f820be9652
commit
9c63bccddb
2 changed files with 63 additions and 47 deletions
|
@ -1,10 +1,9 @@
|
|||
import { ensureElectron } from "@/next/electron";
|
||||
import { convertBytesToHumanReadable, getFileNameSize } from "@/next/file";
|
||||
import { getFileNameSize } from "@/next/file";
|
||||
import log from "@/next/log";
|
||||
import { CustomErrorMessage, type Electron } from "@/next/types/ipc";
|
||||
import { CustomError } from "@ente/shared/error";
|
||||
import { FILE_TYPE } from "constants/file";
|
||||
import { BLACK_THUMBNAIL_BASE64 } from "constants/upload";
|
||||
import isElectron from "is-electron";
|
||||
import * as FFmpegService from "services/ffmpeg/ffmpegService";
|
||||
import HeicConversionService from "services/heicConversionService";
|
||||
import { ElectronFile, FileTypeInfo } from "types/upload";
|
||||
|
@ -22,6 +21,30 @@ const MAX_QUALITY = 0.7;
|
|||
|
||||
const WAIT_TIME_THUMBNAIL_GENERATION = 30 * 1000;
|
||||
|
||||
class ModuleState {
|
||||
/**
|
||||
* This will be set to true if we get an error from the Node.js side of our
|
||||
* desktop app telling us that native JPEG conversion is not available for
|
||||
* the current OS/arch combination. That way, we can stop pestering it again
|
||||
* and again (saving an IPC round-trip).
|
||||
*
|
||||
* Note the double negative when it is used.
|
||||
*/
|
||||
isNativeThumbnailCreationNotAvailable = false;
|
||||
}
|
||||
|
||||
const moduleState = new ModuleState();
|
||||
|
||||
interface GeneratedThumbnail {
|
||||
/** The JPEG data of the generated thumbnail */
|
||||
thumbnail: Uint8Array;
|
||||
/**
|
||||
* `true` if this is a fallback (all black) thumbnail we're returning since
|
||||
* thumbnail generation failed for some reason.
|
||||
*/
|
||||
hasStaticThumbnail: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a JPEG thumbnail for the given {@link file}.
|
||||
*
|
||||
|
@ -33,7 +56,7 @@ const WAIT_TIME_THUMBNAIL_GENERATION = 30 * 1000;
|
|||
export const generateThumbnail = async (
|
||||
file: File | ElectronFile,
|
||||
fileTypeInfo: FileTypeInfo,
|
||||
): Promise<{ thumbnail: Uint8Array; hasStaticThumbnail: boolean }> => {
|
||||
): Promise<GeneratedThumbnail> => {
|
||||
try {
|
||||
const thumbnail =
|
||||
fileTypeInfo.fileType === FILE_TYPE.IMAGE
|
||||
|
@ -53,61 +76,55 @@ export const generateThumbnail = async (
|
|||
};
|
||||
|
||||
/**
|
||||
* A fallback, black, thumbnail to use in cases where thumbnail generation fails
|
||||
* A fallback, black, thumbnail for use in cases where thumbnail generation
|
||||
* fails.
|
||||
*/
|
||||
const fallbackThumbnail = () =>
|
||||
Uint8Array.from(atob(BLACK_THUMBNAIL_BASE64), (c) => c.charCodeAt(0));
|
||||
|
||||
async function generateImageThumbnail(
|
||||
const generateImageThumbnail = async (
|
||||
file: File | ElectronFile,
|
||||
fileTypeInfo: FileTypeInfo,
|
||||
) {
|
||||
if (isElectron()) {
|
||||
) => {
|
||||
let jpegData: Uint8Array | undefined;
|
||||
|
||||
const electron = globalThis.electron;
|
||||
const available = !moduleState.isNativeThumbnailCreationNotAvailable;
|
||||
if (electron && available) {
|
||||
// If we're running in our desktop app, try to make the thumbnail using
|
||||
// the native tools available there-in, it'll be faster than doing it on
|
||||
// the web layer.
|
||||
try {
|
||||
return await generateImageThumbnailInElectron(
|
||||
file,
|
||||
maxThumbnailDimension,
|
||||
maxThumbnailSize,
|
||||
);
|
||||
jpegData = await generateImageThumbnailInElectron(electron, file);
|
||||
} catch (e) {
|
||||
return await generateImageThumbnailUsingCanvas(file, fileTypeInfo);
|
||||
if (e.message == CustomErrorMessage.NotAvailable) {
|
||||
moduleState.isNativeThumbnailCreationNotAvailable = true;
|
||||
} else {
|
||||
log.error("Native thumbnail creation failed", e);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return await generateImageThumbnailUsingCanvas(file, fileTypeInfo);
|
||||
}
|
||||
}
|
||||
|
||||
if (!jpegData) {
|
||||
jpegData = await generateImageThumbnailUsingCanvas(file, fileTypeInfo);
|
||||
}
|
||||
return jpegData;
|
||||
};
|
||||
|
||||
const generateImageThumbnailInElectron = async (
|
||||
electron: Electron,
|
||||
inputFile: File | ElectronFile,
|
||||
maxDimension: number,
|
||||
maxSize: number,
|
||||
): Promise<Uint8Array> => {
|
||||
try {
|
||||
const startTime = Date.now();
|
||||
const thumb = await ensureElectron().generateImageThumbnail(
|
||||
inputFile,
|
||||
maxDimension,
|
||||
maxSize,
|
||||
);
|
||||
log.info(
|
||||
`originalFileSize:${convertBytesToHumanReadable(
|
||||
inputFile?.size,
|
||||
)},thumbFileSize:${convertBytesToHumanReadable(
|
||||
thumb?.length,
|
||||
)}, native thumbnail generation time: ${
|
||||
Date.now() - startTime
|
||||
}ms `,
|
||||
);
|
||||
return thumb;
|
||||
} catch (e) {
|
||||
if (
|
||||
e.message !==
|
||||
CustomError.WINDOWS_NATIVE_IMAGE_PROCESSING_NOT_SUPPORTED
|
||||
) {
|
||||
log.error("failed to generate image thumbnail natively", e);
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
const startTime = Date.now();
|
||||
const jpegData = await electron.generateImageThumbnail(
|
||||
inputFile,
|
||||
maxThumbnailDimension,
|
||||
maxThumbnailSize,
|
||||
);
|
||||
log.debug(
|
||||
() => `Native thumbnail generation took ${Date.now() - startTime} ms`,
|
||||
);
|
||||
return jpegData;
|
||||
};
|
||||
|
||||
async function generateImageThumbnailUsingCanvas(
|
||||
|
|
|
@ -305,7 +305,6 @@ export const getRenderableImage = async (fileName: string, imageBlob: Blob) => {
|
|||
moduleState.isNativeJPEGConversionNotAvailable = true;
|
||||
} else {
|
||||
log.error("Native conversion to JPEG failed", e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -335,7 +334,7 @@ const nativeConvertToJPEG = async (fileName: string, imageBlob: Blob) => {
|
|||
const jpegData = electron
|
||||
? await electron.convertToJPEG(fileName, imageData)
|
||||
: await workerBridge.convertToJPEG(fileName, imageData);
|
||||
log.info(`Native JPEG conversion took ${Date.now() - startTime} ms`);
|
||||
log.debug(() => `Native JPEG conversion took ${Date.now() - startTime} ms`);
|
||||
return new Blob([jpegData]);
|
||||
};
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue