This commit is contained in:
Manav Rathi 2024-04-30 11:01:50 +05:30
parent a5177a3742
commit 68f3f1e714
No known key found for this signature in database
4 changed files with 44 additions and 28 deletions

View file

@ -10,7 +10,11 @@ import {
import { NULL_LOCATION } from "constants/upload"; import { NULL_LOCATION } from "constants/upload";
import type { ParsedExtractedMetadata } from "types/metadata"; import type { ParsedExtractedMetadata } from "types/metadata";
import type { DedicatedFFmpegWorker } from "worker/ffmpeg.worker"; import type { DedicatedFFmpegWorker } from "worker/ffmpeg.worker";
import type { UploadItem } from "./upload/types"; import {
toDataOrPathOrZipEntry,
type DesktopUploadItem,
type UploadItem,
} from "./upload/types";
/** /**
* Generate a thumbnail for the given video using a wasm FFmpeg running in a web * Generate a thumbnail for the given video using a wasm FFmpeg running in a web
@ -59,12 +63,12 @@ const _generateVideoThumbnail = async (
*/ */
export const generateVideoThumbnailNative = async ( export const generateVideoThumbnailNative = async (
electron: Electron, electron: Electron,
dataOrPath: Uint8Array | string, desktopUploadItem: DesktopUploadItem,
) => ) =>
_generateVideoThumbnail((seekTime: number) => _generateVideoThumbnail((seekTime: number) =>
electron.ffmpegExec( electron.ffmpegExec(
makeGenThumbnailCommand(seekTime), makeGenThumbnailCommand(seekTime),
dataOrPath, toDataOrPathOrZipEntry(desktopUploadItem),
"jpeg", "jpeg",
0, 0,
), ),
@ -104,21 +108,16 @@ export const extractVideoMetadata = async (
const outputData = const outputData =
uploadItem instanceof File uploadItem instanceof File
? await ffmpegExecWeb(command, uploadItem, "txt", 0) ? await ffmpegExecWeb(command, uploadItem, "txt", 0)
: await electron.ffmpegExec(command, forE(uploadItem), "txt", 0); : await electron.ffmpegExec(
command,
toDataOrPathOrZipEntry(uploadItem),
"txt",
0,
);
return parseFFmpegExtractedMetadata(outputData); return parseFFmpegExtractedMetadata(outputData);
}; };
/**
* For each of cases of {@link UploadItem} that apply when we're running in the
* context of our desktop app, return a value that can be passed to
* {@link Electron}'s {@link ffmpegExec} over IPC.
*/
const forE = (desktopUploadItem: Exclude<UploadItem, File>) =>
typeof desktopUploadItem == "string" || Array.isArray(desktopUploadItem)
? desktopUploadItem
: desktopUploadItem.path;
// Options: // Options:
// //
// - `-c [short for codex] copy` // - `-c [short for codex] copy`

View file

@ -4,6 +4,7 @@ import { type Electron } from "@/next/types/ipc";
import { withTimeout } from "@ente/shared/utils"; import { withTimeout } from "@ente/shared/utils";
import * as ffmpeg from "services/ffmpeg"; import * as ffmpeg from "services/ffmpeg";
import { heicToJPEG } from "services/heic-convert"; import { heicToJPEG } from "services/heic-convert";
import { toDataOrPathOrZipEntry, type DesktopUploadItem } from "./types";
/** Maximum width or height of the generated thumbnail */ /** Maximum width or height of the generated thumbnail */
const maxThumbnailDimension = 720; const maxThumbnailDimension = 720;
@ -189,16 +190,16 @@ const percentageSizeDiff = (
*/ */
export const generateThumbnailNative = async ( export const generateThumbnailNative = async (
electron: Electron, electron: Electron,
dataOrPath: Uint8Array | string, desktopUploadItem: DesktopUploadItem,
fileTypeInfo: FileTypeInfo, fileTypeInfo: FileTypeInfo,
): Promise<Uint8Array> => ): Promise<Uint8Array> =>
fileTypeInfo.fileType === FILE_TYPE.IMAGE fileTypeInfo.fileType === FILE_TYPE.IMAGE
? await electron.generateImageThumbnail( ? await electron.generateImageThumbnail(
dataOrPath, toDataOrPathOrZipEntry(desktopUploadItem),
maxThumbnailDimension, maxThumbnailDimension,
maxThumbnailSize, maxThumbnailSize,
) )
: ffmpeg.generateVideoThumbnailNative(electron, dataOrPath); : ffmpeg.generateVideoThumbnailNative(electron, desktopUploadItem);
/** /**
* A fallback, black, thumbnail for use in cases where thumbnail generation * A fallback, black, thumbnail for use in cases where thumbnail generation

View file

@ -29,3 +29,19 @@ import type { ZipItem } from "@/next/types/ipc";
* Also see: [Note: Reading a UploadItem]. * Also see: [Note: Reading a UploadItem].
*/ */
export type UploadItem = File | FileAndPath | string | ZipItem; export type UploadItem = File | FileAndPath | string | ZipItem;
/**
* The of cases of {@link UploadItem} that apply when we're running in the
* context of our desktop app.
*/
export type DesktopUploadItem = Exclude<UploadItem, File>;
/**
* For each of cases of {@link UploadItem} that apply when we're running in the
* context of our desktop app, return a value that can be passed to
* {@link Electron} functions over IPC.
*/
export const toDataOrPathOrZipEntry = (desktopUploadItem: DesktopUploadItem) =>
typeof desktopUploadItem == "string" || Array.isArray(desktopUploadItem)
? desktopUploadItem
: desktopUploadItem.path;

View file

@ -1012,14 +1012,12 @@ const withThumbnail = async (
fileTypeInfo.fileType == FILE_TYPE.IMAGE && fileTypeInfo.fileType == FILE_TYPE.IMAGE &&
moduleState.isNativeImageThumbnailGenerationNotAvailable; moduleState.isNativeImageThumbnailGenerationNotAvailable;
// 1. Native thumbnail generation using file's path. // 1. Native thumbnail generation using items's (effective) path.
if (electron && !notAvailable) { if (electron && !notAvailable && !(uploadItem instanceof File)) {
try { try {
// When running in the context of our desktop app, File paths will
// be absolute. See: [Note: File paths when running under Electron].
thumbnail = await generateThumbnailNative( thumbnail = await generateThumbnailNative(
electron, electron,
uploadItem instanceof File ? uploadItem["path"] : uploadItem, uploadItem,
fileTypeInfo, fileTypeInfo,
); );
} catch (e) { } catch (e) {
@ -1051,12 +1049,14 @@ const withThumbnail = async (
// The fallback in this case involves reading the entire stream into // The fallback in this case involves reading the entire stream into
// memory, and passing that data across the IPC boundary in a single // memory, and passing that data across the IPC boundary in a single
// go (i.e. not in a streaming manner). This is risky for videos of // go (i.e. not in a streaming manner). This is risky for videos of
// unbounded sizes, plus that isn't the expected scenario. So // unbounded sizes, plus we shouldn't even be getting here unless
// instead of trying to cater for arbitrary exceptions, we only run // something went wrong.
// this fallback to cover for the case where thumbnail generation //
// was not available for an image file on Windows. If/when we add // So instead of trying to cater for arbitrary exceptions, we only
// support of native thumbnailing on Windows too, this entire branch // run this fallback to cover for the case where thumbnail
// can be removed. // generation was not available for an image file on Windows.
// If/when we add support of native thumbnailing on Windows too,
// this entire branch can be removed.
if (fileTypeInfo.fileType == FILE_TYPE.IMAGE) { if (fileTypeInfo.fileType == FILE_TYPE.IMAGE) {
const data = await readEntireStream(fileStream.stream); const data = await readEntireStream(fileStream.stream);