Desktop side
This commit is contained in:
parent
4750caf156
commit
4461775283
8 changed files with 42 additions and 58 deletions
|
@ -156,10 +156,9 @@ export const attachIPCHandlers = () => {
|
|||
(
|
||||
_,
|
||||
command: string[],
|
||||
inputDataOrPath: Uint8Array | string,
|
||||
outputFileName: string,
|
||||
dataOrPath: Uint8Array | string,
|
||||
timeoutMS: number,
|
||||
) => ffmpegExec(command, inputDataOrPath, outputFileName, timeoutMS),
|
||||
) => ffmpegExec(command, dataOrPath, timeoutMS),
|
||||
);
|
||||
|
||||
// - ML
|
||||
|
|
|
@ -14,7 +14,7 @@ export const convertToJPEG = async (
|
|||
imageData: Uint8Array,
|
||||
): Promise<Uint8Array> => {
|
||||
const inputFilePath = await makeTempFilePath(fileName);
|
||||
const outputFilePath = await makeTempFilePath("output.jpeg");
|
||||
const outputFilePath = await makeTempFilePath(".jpeg");
|
||||
|
||||
// Construct the command first, it may throw on NotAvailable on win32.
|
||||
const command = convertToJPEGCommand(inputFilePath, outputFilePath);
|
||||
|
@ -150,7 +150,7 @@ async function generateImageThumbnail_(
|
|||
let tempOutputFilePath: string;
|
||||
let quality = MAX_QUALITY;
|
||||
try {
|
||||
tempOutputFilePath = await makeTempFilePath("thumb.jpeg");
|
||||
tempOutputFilePath = await makeTempFilePath(".jpeg");
|
||||
let thumbnail: Uint8Array;
|
||||
do {
|
||||
await execAsync(
|
||||
|
|
|
@ -37,8 +37,7 @@ const outputPathPlaceholder = "OUTPUT";
|
|||
*/
|
||||
export const ffmpegExec = async (
|
||||
command: string[],
|
||||
inputDataOrPath: Uint8Array | string,
|
||||
outputFileName: string,
|
||||
dataOrPath: Uint8Array | string,
|
||||
timeoutMS: number,
|
||||
): Promise<Uint8Array> => {
|
||||
// TODO (MR): This currently copies files for both input and output. This
|
||||
|
@ -47,18 +46,18 @@ export const ffmpegExec = async (
|
|||
|
||||
let inputFilePath: string;
|
||||
let isInputFileTemporary: boolean;
|
||||
if (typeof inputDataOrPath == "string") {
|
||||
inputFilePath = inputDataOrPath;
|
||||
if (typeof dataOrPath == "string") {
|
||||
inputFilePath = dataOrPath;
|
||||
isInputFileTemporary = false;
|
||||
} else {
|
||||
inputFilePath = await makeTempFilePath("input" /* arbitrary */);
|
||||
inputFilePath = await makeTempFilePath(".in");
|
||||
isInputFileTemporary = true;
|
||||
await fs.writeFile(inputFilePath, inputDataOrPath);
|
||||
await fs.writeFile(inputFilePath, dataOrPath);
|
||||
}
|
||||
|
||||
let outputFilePath: string | undefined;
|
||||
try {
|
||||
outputFilePath = await makeTempFilePath(outputFileName);
|
||||
outputFilePath = await makeTempFilePath(".out");
|
||||
|
||||
const cmd = substitutePlaceholders(
|
||||
command,
|
||||
|
|
|
@ -20,7 +20,7 @@ const cachedCLIPImageSession = makeCachedInferenceSession(
|
|||
);
|
||||
|
||||
export const clipImageEmbedding = async (jpegImageData: Uint8Array) => {
|
||||
const tempFilePath = await makeTempFilePath("");
|
||||
const tempFilePath = await makeTempFilePath();
|
||||
const imageStream = new Response(jpegImageData.buffer).body;
|
||||
await writeStream(tempFilePath, imageStream);
|
||||
try {
|
||||
|
|
|
@ -25,20 +25,21 @@ const randomPrefix = () => {
|
|||
};
|
||||
|
||||
/**
|
||||
* Return the path to a temporary file with the given {@link formatSuffix}.
|
||||
* Return the path to a temporary file with the given {@link suffix}.
|
||||
*
|
||||
* The function returns the path to a file in the system temp directory (in an
|
||||
* Ente specific folder therin) with a random prefix and the given
|
||||
* {@link formatSuffix}. It ensures that there is no existing file with the same
|
||||
* name already.
|
||||
* Ente specific folder therin) with a random prefix and an (optional)
|
||||
* {@link suffix}.
|
||||
*
|
||||
* It ensures that there is no existing file with the same name already.
|
||||
*
|
||||
* Use {@link deleteTempFile} to remove this file when you're done.
|
||||
*/
|
||||
export const makeTempFilePath = async (formatSuffix: string) => {
|
||||
export const makeTempFilePath = async (suffix?: string) => {
|
||||
const tempDir = await enteTempDirPath();
|
||||
let result: string;
|
||||
do {
|
||||
result = path.join(tempDir, randomPrefix() + "-" + formatSuffix);
|
||||
result = path.join(tempDir, `${randomPrefix()}${suffix ?? ""}`);
|
||||
} while (existsSync(result));
|
||||
return result;
|
||||
};
|
||||
|
|
|
@ -144,17 +144,10 @@ const generateImageThumbnail = (
|
|||
|
||||
const ffmpegExec = (
|
||||
command: string[],
|
||||
inputDataOrPath: Uint8Array | string,
|
||||
outputFileName: string,
|
||||
dataOrPath: Uint8Array | string,
|
||||
timeoutMS: number,
|
||||
): Promise<Uint8Array> =>
|
||||
ipcRenderer.invoke(
|
||||
"ffmpegExec",
|
||||
command,
|
||||
inputDataOrPath,
|
||||
outputFileName,
|
||||
timeoutMS,
|
||||
);
|
||||
ipcRenderer.invoke("ffmpegExec", command, dataOrPath, timeoutMS);
|
||||
|
||||
// - ML
|
||||
|
||||
|
|
|
@ -36,7 +36,6 @@ export const generateVideoThumbnail = async (blob: Blob) => {
|
|||
outputPathPlaceholder,
|
||||
],
|
||||
blob,
|
||||
"thumb.jpeg",
|
||||
);
|
||||
|
||||
try {
|
||||
|
@ -161,27 +160,25 @@ export async function convertToMP4(file: File) {
|
|||
* Run the given FFmpeg command.
|
||||
*
|
||||
* If we're running in the context of our desktop app, use the FFmpeg binary we
|
||||
* bundle with our desktop app to run the command. Otherwise fallback to using
|
||||
* the wasm FFmpeg we link to from our web app in a web worker.
|
||||
* bundle with our desktop app to run the command. Otherwise fallback to using a
|
||||
* wasm FFmpeg in a web worker.
|
||||
*
|
||||
* As a rough ballpark, the native FFmpeg integration in the desktop app is
|
||||
* 10-20x faster than the wasm one currently. See: [Note: FFmpeg in Electron].
|
||||
* As a rough ballpark, currently the native FFmpeg integration in the desktop
|
||||
* app is 10-20x faster than the wasm one. See: [Note: FFmpeg in Electron].
|
||||
*/
|
||||
const ffmpegExec = async (
|
||||
command: string[],
|
||||
blob: Blob,
|
||||
outputFileName: string,
|
||||
timeoutMs: number = 0,
|
||||
): Promise<Uint8Array> => {
|
||||
) => {
|
||||
const electron = globalThis.electron;
|
||||
if (electron)
|
||||
return electron.ffmpegExec(command, blob, outputFileName, timeoutMs);
|
||||
else
|
||||
return workerFactory
|
||||
.lazy()
|
||||
.then((worker) =>
|
||||
worker.exec(command, blob, outputFileName, timeoutMs),
|
||||
);
|
||||
if (electron) {
|
||||
const data = new Uint8Array(await blob.arrayBuffer());
|
||||
return await electron.ffmpegExec(command, data, timeoutMs);
|
||||
} else {
|
||||
const worker = await workerFactory.lazy()
|
||||
return await worker.exec(command, blob, timeoutMs);
|
||||
}
|
||||
};
|
||||
|
||||
const ffmpegExec2 = async (
|
||||
|
|
|
@ -237,11 +237,11 @@ export interface Electron {
|
|||
) => Promise<Uint8Array>;
|
||||
|
||||
/**
|
||||
* Execute a ffmpeg {@link command}.
|
||||
* Execute a FFmpeg {@link command} on the given {@link dataOrPath}.
|
||||
*
|
||||
* This executes the command using the ffmpeg executable we bundle with our
|
||||
* desktop app. There is also a ffmpeg wasm implementation that we use when
|
||||
* running on the web, it also has a sibling function with the same
|
||||
* This executes the command using a FFmpeg executable we bundle with our
|
||||
* desktop app. We also have a wasm FFmpeg wasm implementation that we use
|
||||
* when running on the web, which has a sibling function with the same
|
||||
* parameters. See [Note: ffmpeg in Electron].
|
||||
*
|
||||
* @param command An array of strings, each representing one positional
|
||||
|
@ -250,25 +250,20 @@ export interface Electron {
|
|||
* (respectively {@link inputPathPlaceholder},
|
||||
* {@link outputPathPlaceholder}, {@link ffmpegPathPlaceholder}).
|
||||
*
|
||||
* @param inputDataOrPath The bytes of the input file, or the path to the
|
||||
* input file on the user's local disk. In both cases, the data gets
|
||||
* serialized to a temporary file, and then that path gets substituted in
|
||||
* the ffmpeg {@link command} by {@link inputPathPlaceholder}.
|
||||
*
|
||||
* @param outputFileName The name of the file we instruct ffmpeg to produce
|
||||
* when giving it the given {@link command}. The contents of this file get
|
||||
* returned as the result.
|
||||
* @param dataOrPath The bytes of the input file, or the path to the input
|
||||
* file on the user's local disk. In both cases, the data gets serialized to
|
||||
* a temporary file, and then that path gets substituted in the FFmpeg
|
||||
* {@link command} in lieu of {@link inputPathPlaceholder}.
|
||||
*
|
||||
* @param timeoutMS If non-zero, then abort and throw a timeout error if the
|
||||
* ffmpeg command takes more than the given number of milliseconds.
|
||||
*
|
||||
* @returns The contents of the output file produced by the ffmpeg command
|
||||
* at {@link outputFileName}.
|
||||
* (specified as {@link outputPathPlaceholder} in {@link command}).
|
||||
*/
|
||||
ffmpegExec: (
|
||||
command: string[],
|
||||
inputDataOrPath: Uint8Array | string,
|
||||
outputFileName: string,
|
||||
dataOrPath: Uint8Array | string,
|
||||
timeoutMS: number,
|
||||
) => Promise<Uint8Array>;
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue