diff --git a/web/apps/photos/src/services/ffmpeg.ts b/web/apps/photos/src/services/ffmpeg.ts index dbdc53a3c..4dfdb3f64 100644 --- a/web/apps/photos/src/services/ffmpeg.ts +++ b/web/apps/photos/src/services/ffmpeg.ts @@ -10,7 +10,11 @@ import { import { NULL_LOCATION } from "constants/upload"; import type { ParsedExtractedMetadata } from "types/metadata"; 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 @@ -59,12 +63,12 @@ const _generateVideoThumbnail = async ( */ export const generateVideoThumbnailNative = async ( electron: Electron, - dataOrPath: Uint8Array | string, + desktopUploadItem: DesktopUploadItem, ) => _generateVideoThumbnail((seekTime: number) => electron.ffmpegExec( makeGenThumbnailCommand(seekTime), - dataOrPath, + toDataOrPathOrZipEntry(desktopUploadItem), "jpeg", 0, ), @@ -104,21 +108,16 @@ export const extractVideoMetadata = async ( const outputData = uploadItem instanceof File ? 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); }; -/** - * 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) => - typeof desktopUploadItem == "string" || Array.isArray(desktopUploadItem) - ? desktopUploadItem - : desktopUploadItem.path; - // Options: // // - `-c [short for codex] copy` diff --git a/web/apps/photos/src/services/upload/thumbnail.ts b/web/apps/photos/src/services/upload/thumbnail.ts index 9cd9a339c..1dd448376 100644 --- a/web/apps/photos/src/services/upload/thumbnail.ts +++ b/web/apps/photos/src/services/upload/thumbnail.ts @@ -4,6 +4,7 @@ import { type Electron } from "@/next/types/ipc"; import { withTimeout } from "@ente/shared/utils"; import * as ffmpeg from "services/ffmpeg"; import { heicToJPEG } from "services/heic-convert"; +import { toDataOrPathOrZipEntry, type DesktopUploadItem } from "./types"; /** Maximum width or height of the generated thumbnail */ const maxThumbnailDimension = 720; @@ -189,16 +190,16 @@ const percentageSizeDiff = ( */ export const generateThumbnailNative = async ( electron: Electron, - dataOrPath: Uint8Array | string, + desktopUploadItem: DesktopUploadItem, fileTypeInfo: FileTypeInfo, ): Promise => fileTypeInfo.fileType === FILE_TYPE.IMAGE ? await electron.generateImageThumbnail( - dataOrPath, + toDataOrPathOrZipEntry(desktopUploadItem), maxThumbnailDimension, maxThumbnailSize, ) - : ffmpeg.generateVideoThumbnailNative(electron, dataOrPath); + : ffmpeg.generateVideoThumbnailNative(electron, desktopUploadItem); /** * A fallback, black, thumbnail for use in cases where thumbnail generation diff --git a/web/apps/photos/src/services/upload/types.ts b/web/apps/photos/src/services/upload/types.ts index 25a79de1a..05ad332d4 100644 --- a/web/apps/photos/src/services/upload/types.ts +++ b/web/apps/photos/src/services/upload/types.ts @@ -29,3 +29,19 @@ import type { ZipItem } from "@/next/types/ipc"; * Also see: [Note: Reading a UploadItem]. */ 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; + +/** + * 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; diff --git a/web/apps/photos/src/services/upload/uploadService.ts b/web/apps/photos/src/services/upload/uploadService.ts index 8c042ccaf..6dc1bbd49 100644 --- a/web/apps/photos/src/services/upload/uploadService.ts +++ b/web/apps/photos/src/services/upload/uploadService.ts @@ -1012,14 +1012,12 @@ const withThumbnail = async ( fileTypeInfo.fileType == FILE_TYPE.IMAGE && moduleState.isNativeImageThumbnailGenerationNotAvailable; - // 1. Native thumbnail generation using file's path. - if (electron && !notAvailable) { + // 1. Native thumbnail generation using items's (effective) path. + if (electron && !notAvailable && !(uploadItem instanceof File)) { 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( electron, - uploadItem instanceof File ? uploadItem["path"] : uploadItem, + uploadItem, fileTypeInfo, ); } catch (e) { @@ -1051,12 +1049,14 @@ const withThumbnail = async ( // The fallback in this case involves reading the entire stream into // 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 - // unbounded sizes, plus that isn't the expected scenario. So - // instead of trying to cater for arbitrary exceptions, we only run - // this fallback to cover for the case where thumbnail 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. + // unbounded sizes, plus we shouldn't even be getting here unless + // something went wrong. + // + // So instead of trying to cater for arbitrary exceptions, we only + // run this fallback to cover for the case where thumbnail + // 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) { const data = await readEntireStream(fileStream.stream);