This commit is contained in:
Manav Rathi 2024-04-22 16:11:08 +05:30
parent bb094743f3
commit 4750caf156
No known key found for this signature in database
5 changed files with 49 additions and 88 deletions

View file

@ -13,17 +13,14 @@ const enteTempDirPath = async () => {
return result;
};
const randomPrefix = (length: number) => {
const CHARACTERS =
/** Generate a random string suitable for being used as a file name prefix */
const randomPrefix = () => {
const alphabet =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
let result = "";
const charactersLength = CHARACTERS.length;
for (let i = 0; i < length; i++) {
result += CHARACTERS.charAt(
Math.floor(Math.random() * charactersLength),
);
}
for (let i = 0; i < 10; i++)
result += alphabet[Math.floor(Math.random() * alphabet.length)];
return result;
};
@ -41,7 +38,7 @@ export const makeTempFilePath = async (formatSuffix: string) => {
const tempDir = await enteTempDirPath();
let result: string;
do {
result = path.join(tempDir, randomPrefix(10) + "-" + formatSuffix);
result = path.join(tempDir, randomPrefix() + "-" + formatSuffix);
} while (existsSync(result));
return result;
};

View file

@ -1,4 +1,4 @@
import { ElectronFile, type DesktopFilePath } from "@/next/types/file";
import { ElectronFile } from "@/next/types/file";
import { ComlinkWorker } from "@/next/worker/comlink-worker";
import { validateAndGetCreationUnixTimeInMicroSeconds } from "@ente/shared/time";
import { Remote } from "comlink";
@ -17,12 +17,10 @@ import { type DedicatedFFmpegWorker } from "worker/ffmpeg.worker";
* This function is called during upload, when we need to generate thumbnails
* for the new files that the user is adding.
*
* @param fileOrPath The input video file or a path to it.
* @returns JPEG data for the generated thumbnail.
* @param blob The input video blob.
* @returns JPEG data of the generated thumbnail.
*/
export const generateVideoThumbnail = async (
fileOrPath: File | DesktopFilePath,
): Promise<Uint8Array> => {
export const generateVideoThumbnail = async (blob: Blob) => {
const thumbnailAtTime = (seekTime: number) =>
ffmpegExec(
[
@ -37,7 +35,7 @@ export const generateVideoThumbnail = async (
"scale=-1:720",
outputPathPlaceholder,
],
fileOrPath,
blob,
"thumb.jpeg",
);
@ -171,20 +169,19 @@ export async function convertToMP4(file: File) {
*/
const ffmpegExec = async (
command: string[],
fileOrPath: File | DesktopFilePath,
blob: Blob,
outputFileName: string,
timeoutMs: number = 0,
): Promise<Uint8Array> => {
if (fileOrPath instanceof File) {
const electron = globalThis.electron;
if (electron)
return electron.ffmpegExec(command, blob, outputFileName, timeoutMs);
else
return workerFactory
.lazy()
.then((worker) =>
worker.exec(command, fileOrPath, outputFileName, timeoutMs),
worker.exec(command, blob, outputFileName, timeoutMs),
);
} else {
const { path, electron } = fileOrPath;
return electron.ffmpegExec(command, path, outputFileName, timeoutMs);
}
};
const ffmpegExec2 = async (

View file

@ -1,5 +1,4 @@
import log from "@/next/log";
import { ElectronFile } from "@/next/types/file";
import { CustomErrorMessage, type Electron } from "@/next/types/ipc";
import { withTimeout } from "@ente/shared/utils";
import { FILE_TYPE } from "constants/file";
@ -84,16 +83,11 @@ const generateImageThumbnail = async (
blob: Blob,
fileTypeInfo: FileTypeInfo,
) => {
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 {
jpegData = await generateImageThumbnailInElectron(electron, file);
return await generateImageThumbnailInElectron(electron, file);
} catch (e) {
if (e.message == CustomErrorMessage.NotAvailable) {
moduleState.isNativeThumbnailCreationNotAvailable = true;
@ -103,15 +97,12 @@ const generateImageThumbnail = async (
}
}
if (!jpegData) {
jpegData = await generateImageThumbnailUsingCanvas(blob, fileTypeInfo);
}
return jpegData;
return await generateImageThumbnailUsingCanvas(blob, fileTypeInfo);
};
const generateImageThumbnailInElectron = async (
electron: Electron,
inputFile: File | ElectronFile,
blob: Blob,
): Promise<Uint8Array> => {
const startTime = Date.now();
const jpegData = await electron.generateImageThumbnail(
@ -255,7 +246,7 @@ const compressedJPEGData = async (canvas: HTMLCanvasElement) => {
percentageSizeDiff(blob.size, prevSize) >= 10
);
return blob;
return new Uint8Array(await blob.arrayBuffer());
};
const percentageSizeDiff = (

View file

@ -1,8 +1,6 @@
import { nameAndExtension } from "@/next/file";
import log from "@/next/log";
import { withTimeout } from "@ente/shared/utils";
import QueueProcessor from "@ente/shared/utils/queueProcessor";
import { generateTempName } from "@ente/shared/utils/temp";
import { expose } from "comlink";
import {
ffmpegPathPlaceholder,
@ -23,21 +21,15 @@ export class DedicatedFFmpegWorker {
}
/**
* Execute a FFmpeg {@link command}.
* Execute a FFmpeg {@link command} on {@link blob}.
*
* This is a sibling of {@link ffmpegExec} in ipc.ts exposed by the desktop
* app. See [Note: FFmpeg in Electron].
* This is a sibling of {@link ffmpegExec} exposed by the desktop app in
* `ipc.ts`. See [Note: FFmpeg in Electron].
*/
async exec(
command: string[],
inputFile: File,
outputFileName: string,
timeoutMs,
): Promise<Uint8Array> {
async exec(command: string[], blob: Blob, timeoutMs): Promise<Uint8Array> {
if (!this.ffmpeg.isLoaded()) await this.ffmpeg.load();
const go = () =>
ffmpegExec(this.ffmpeg, command, inputFile, outputFileName);
const go = () => ffmpegExec(this.ffmpeg, command, blob);
const request = this.ffmpegTaskQueue.queueUpRequest(() =>
timeoutMs ? withTimeout(go(), timeoutMs) : go(),
@ -49,48 +41,46 @@ export class DedicatedFFmpegWorker {
expose(DedicatedFFmpegWorker, self);
const ffmpegExec = async (
ffmpeg: FFmpeg,
command: string[],
inputFile: File,
outputFileName: string,
) => {
const [, extension] = nameAndExtension(inputFile.name);
const tempNameSuffix = extension ? `input.${extension}` : "input";
const tempInputFilePath = `${generateTempName(10, tempNameSuffix)}`;
const tempOutputFilePath = `${generateTempName(10, outputFileName)}`;
const ffmpegExec = async (ffmpeg: FFmpeg, command: string[], blob: Blob) => {
const inputPath = `${randomPrefix()}.in`;
const outputPath = `${randomPrefix()}.out`;
const cmd = substitutePlaceholders(
command,
tempInputFilePath,
tempOutputFilePath,
);
const cmd = substitutePlaceholders(command, inputPath, outputPath);
const inputData = new Uint8Array(await blob.arrayBuffer());
try {
ffmpeg.FS(
"writeFile",
tempInputFilePath,
new Uint8Array(await inputFile.arrayBuffer()),
);
ffmpeg.FS("writeFile", inputPath, inputData);
log.info(`Running FFmpeg (wasm) command ${cmd}`);
await ffmpeg.run(...cmd);
return ffmpeg.FS("readFile", tempOutputFilePath);
return ffmpeg.FS("readFile", outputPath);
} finally {
try {
ffmpeg.FS("unlink", tempInputFilePath);
ffmpeg.FS("unlink", inputPath);
} catch (e) {
log.error("Failed to remove input ${tempInputFilePath}", e);
log.error(`Failed to remove input ${inputPath}`, e);
}
try {
ffmpeg.FS("unlink", tempOutputFilePath);
ffmpeg.FS("unlink", outputPath);
} catch (e) {
log.error("Failed to remove output ${tempOutputFilePath}", e);
log.error(`Failed to remove output ${outputPath}`, e);
}
}
};
/** Generate a random string suitable for being used as a file name prefix */
const randomPrefix = () => {
const alphabet =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
let result = "";
for (let i = 0; i < 10; i++)
result += alphabet[Math.floor(Math.random() * alphabet.length)];
return result;
};
const substitutePlaceholders = (
command: string[],
inputFilePath: string,

View file

@ -1,14 +0,0 @@
const CHARACTERS =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
export function generateTempName(length: number, suffix: string) {
let tempName = "";
const charactersLength = CHARACTERS.length;
for (let i = 0; i < length; i++) {
tempName += CHARACTERS.charAt(
Math.floor(Math.random() * charactersLength),
);
}
return `${tempName}-${suffix}`;
}