diff --git a/web/apps/cast/src/constants/upload.ts b/web/apps/cast/src/constants/upload.ts index 801d8a6ab..2ae1c4383 100644 --- a/web/apps/cast/src/constants/upload.ts +++ b/web/apps/cast/src/constants/upload.ts @@ -1,6 +1,3 @@ -import { FILE_TYPE } from "@/media/file"; -import { FileTypeInfo } from "types/upload"; - export const RAW_FORMATS = [ "heic", "rw2", @@ -14,42 +11,3 @@ export const RAW_FORMATS = [ "dng", "tif", ]; - -// list of format that were missed by type-detection for some files. -export const WHITELISTED_FILE_FORMATS: FileTypeInfo[] = [ - { fileType: FILE_TYPE.IMAGE, exactType: "jpeg", mimeType: "image/jpeg" }, - { fileType: FILE_TYPE.IMAGE, exactType: "jpg", mimeType: "image/jpeg" }, - { fileType: FILE_TYPE.VIDEO, exactType: "webm", mimeType: "video/webm" }, - { fileType: FILE_TYPE.VIDEO, exactType: "mod", mimeType: "video/mpeg" }, - { fileType: FILE_TYPE.VIDEO, exactType: "mp4", mimeType: "video/mp4" }, - { fileType: FILE_TYPE.IMAGE, exactType: "gif", mimeType: "image/gif" }, - { fileType: FILE_TYPE.VIDEO, exactType: "dv", mimeType: "video/x-dv" }, - { - fileType: FILE_TYPE.VIDEO, - exactType: "wmv", - mimeType: "video/x-ms-asf", - }, - { - fileType: FILE_TYPE.VIDEO, - exactType: "hevc", - mimeType: "video/hevc", - }, - { - fileType: FILE_TYPE.IMAGE, - exactType: "raf", - mimeType: "image/x-fuji-raf", - }, - { - fileType: FILE_TYPE.IMAGE, - exactType: "orf", - mimeType: "image/x-olympus-orf", - }, - - { - fileType: FILE_TYPE.IMAGE, - exactType: "crw", - mimeType: "image/x-canon-crw", - }, -]; - -export const KNOWN_NON_MEDIA_FORMATS = ["xmp", "html", "txt"]; diff --git a/web/apps/cast/src/pages/slideshow.tsx b/web/apps/cast/src/pages/slideshow.tsx index d13a26346..99b2209de 100644 --- a/web/apps/cast/src/pages/slideshow.tsx +++ b/web/apps/cast/src/pages/slideshow.tsx @@ -1,4 +1,4 @@ -import { FILE_TYPE } from "@/media/file"; +import { FILE_TYPE } from "@/media/file-type"; import log from "@/next/log"; import PairedSuccessfullyOverlay from "components/PairedSuccessfullyOverlay"; import { PhotoAuditorium } from "components/PhotoAuditorium"; diff --git a/web/apps/cast/src/services/castDownloadManager.ts b/web/apps/cast/src/services/castDownloadManager.ts index 40a5f25af..2314ed54e 100644 --- a/web/apps/cast/src/services/castDownloadManager.ts +++ b/web/apps/cast/src/services/castDownloadManager.ts @@ -1,4 +1,4 @@ -import { FILE_TYPE } from "@/media/file"; +import { FILE_TYPE } from "@/media/file-type"; import ComlinkCryptoWorker from "@ente/shared/crypto"; import { CustomError } from "@ente/shared/error"; import HTTPService from "@ente/shared/network/HTTPService"; diff --git a/web/apps/cast/src/services/detect-type.ts b/web/apps/cast/src/services/detect-type.ts new file mode 100644 index 000000000..ebe0bc2df --- /dev/null +++ b/web/apps/cast/src/services/detect-type.ts @@ -0,0 +1,39 @@ +import { KnownFileTypeInfos } from "@/media/file-type"; +import { nameAndExtension } from "@/next/file"; +import log from "@/next/log"; +import FileType from "file-type"; + +/** + * Try to deduce the MIME type for the given {@link file}. Return the MIME type + * string if successful _and_ if it is an image or a video, otherwise return + * `undefined`. + * + * It first peeks into the file's initial contents to detect the MIME type. If + * that doesn't give any results, it tries to deduce it from the file's name. + */ +export const tryDetectMediaMIMEType = async (file: File): Promise => { + const chunkSizeForTypeDetection = 4100; + + let mime: string | undefined; + try { + const fileChunk = file.slice(0, chunkSizeForTypeDetection); + const chunkData = new Uint8Array(await fileChunk.arrayBuffer()); + const result = await FileType.fromBuffer(chunkData); + mime = result?.mime; + } catch (e) { + log.error( + `Failed to deduce MIME type from file contents for ${file}, will try with the file name instead`, + e, + ); + } + + if (mime) { + if (mime.startsWith("image/") || mime.startsWith("video/")) return mime; + else throw new Error(`Detected MIME type ${mime} is not a media file`); + } + + let [, ext] = nameAndExtension(file.name); + if (!ext) return undefined; + ext = ext.toLowerCase(); + return KnownFileTypeInfos.find((f) => f.exactType == ext)?.mimeType; +}; diff --git a/web/apps/cast/src/services/typeDetectionService.ts b/web/apps/cast/src/services/typeDetectionService.ts deleted file mode 100644 index 883d96b0e..000000000 --- a/web/apps/cast/src/services/typeDetectionService.ts +++ /dev/null @@ -1,92 +0,0 @@ -import { FILE_TYPE } from "@/media/file"; -import { convertBytesToHumanReadable, nameAndExtension } from "@/next/file"; -import log from "@/next/log"; -import { CustomError } from "@ente/shared/error"; -import { - KNOWN_NON_MEDIA_FORMATS, - WHITELISTED_FILE_FORMATS, -} from "constants/upload"; -import FileType from "file-type"; -import { FileTypeInfo } from "types/upload"; - -const TYPE_VIDEO = "video"; -const TYPE_IMAGE = "image"; -const CHUNK_SIZE_FOR_TYPE_DETECTION = 4100; - -export async function getFileType(receivedFile: File): Promise { - try { - let fileType: FILE_TYPE; - - const typeResult = await extractFileType(receivedFile); - const mimTypeParts: string[] = typeResult.mime?.split("/"); - if (mimTypeParts?.length !== 2) { - throw Error(CustomError.INVALID_MIME_TYPE(typeResult.mime)); - } - - switch (mimTypeParts[0]) { - case TYPE_IMAGE: - fileType = FILE_TYPE.IMAGE; - break; - case TYPE_VIDEO: - fileType = FILE_TYPE.VIDEO; - break; - default: - throw Error(CustomError.NON_MEDIA_FILE); - } - return { - fileType, - exactType: typeResult.ext, - mimeType: typeResult.mime, - }; - } catch (e) { - const ne = nameAndExtension(receivedFile.name); - const fileFormat = ne[1].toLowerCase(); - const whiteListedFormat = WHITELISTED_FILE_FORMATS.find( - (a) => a.exactType === fileFormat, - ); - if (whiteListedFormat) { - return whiteListedFormat; - } - if (KNOWN_NON_MEDIA_FORMATS.includes(fileFormat)) { - throw Error(CustomError.UNSUPPORTED_FILE_FORMAT); - } - if (e.message === CustomError.NON_MEDIA_FILE) { - log.error(`unsupported file format ${fileFormat}`, e); - throw Error(CustomError.UNSUPPORTED_FILE_FORMAT); - } - log.error(`type detection failed for format ${fileFormat}`, e); - throw Error(CustomError.TYPE_DETECTION_FAILED(fileFormat)); - } -} - -async function extractFileType(file: File) { - const fileBlobChunk = file.slice(0, CHUNK_SIZE_FOR_TYPE_DETECTION); - const fileDataChunk = await getUint8ArrayView(fileBlobChunk); - return getFileTypeFromBuffer(fileDataChunk); -} - -export async function getUint8ArrayView(file: Blob): Promise { - try { - return new Uint8Array(await file.arrayBuffer()); - } catch (e) { - log.error( - `Failed to read file blob of size ${convertBytesToHumanReadable(file.size)}`, - e, - ); - throw e; - } -} - -async function getFileTypeFromBuffer(buffer: Uint8Array) { - const result = await FileType.fromBuffer(buffer); - if (!result?.mime) { - let logableInfo = ""; - try { - logableInfo = `result: ${JSON.stringify(result)}`; - } catch (e) { - logableInfo = "failed to stringify result"; - } - throw Error(`mimetype missing from file type result - ${logableInfo}`); - } - return result; -} diff --git a/web/apps/cast/src/types/upload.ts b/web/apps/cast/src/types/upload.ts index 91b5c3385..b74621fad 100644 --- a/web/apps/cast/src/types/upload.ts +++ b/web/apps/cast/src/types/upload.ts @@ -1,4 +1,4 @@ -import { FILE_TYPE } from "@/media/file"; +import { FILE_TYPE } from "@/media/file-type"; export interface Metadata { title: string; diff --git a/web/apps/cast/src/utils/file.ts b/web/apps/cast/src/utils/file.ts index 1f04a916d..b81a8630b 100644 --- a/web/apps/cast/src/utils/file.ts +++ b/web/apps/cast/src/utils/file.ts @@ -1,10 +1,10 @@ -import { FILE_TYPE } from "@/media/file"; +import { FILE_TYPE } from "@/media/file-type"; import { decodeLivePhoto } from "@/media/live-photo"; import log from "@/next/log"; import ComlinkCryptoWorker from "@ente/shared/crypto"; import { RAW_FORMATS } from "constants/upload"; import CastDownloadManager from "services/castDownloadManager"; -import { getFileType } from "services/typeDetectionService"; +import { tryDetectMediaMIMEType } from "services/detect-type"; import { EncryptedEnteFile, EnteFile, @@ -132,10 +132,11 @@ export const getPreviewableImage = async ( ); fileBlob = new Blob([imageData]); } - const fileType = await getFileType( + const mimeType = await tryDetectMediaMIMEType( new File([fileBlob], file.metadata.title), ); - fileBlob = new Blob([fileBlob], { type: fileType.mimeType }); + if (!mimeType) return undefined; + fileBlob = new Blob([fileBlob], { type: mimeType }); return fileBlob; } catch (e) { log.error("failed to download file", e); diff --git a/web/apps/photos/src/components/PhotoFrame.tsx b/web/apps/photos/src/components/PhotoFrame.tsx index 5ff70b3d4..8c935ee27 100644 --- a/web/apps/photos/src/components/PhotoFrame.tsx +++ b/web/apps/photos/src/components/PhotoFrame.tsx @@ -1,4 +1,4 @@ -import { FILE_TYPE } from "@/media/file"; +import { FILE_TYPE } from "@/media/file-type"; import log from "@/next/log"; import { PHOTOS_PAGES } from "@ente/shared/constants/pages"; import { CustomError } from "@ente/shared/error"; diff --git a/web/apps/photos/src/components/PhotoViewer/FileInfo/RenderFileName.tsx b/web/apps/photos/src/components/PhotoViewer/FileInfo/RenderFileName.tsx index c1b25e850..399051185 100644 --- a/web/apps/photos/src/components/PhotoViewer/FileInfo/RenderFileName.tsx +++ b/web/apps/photos/src/components/PhotoViewer/FileInfo/RenderFileName.tsx @@ -1,4 +1,4 @@ -import { FILE_TYPE } from "@/media/file"; +import { FILE_TYPE } from "@/media/file-type"; import { nameAndExtension } from "@/next/file"; import log from "@/next/log"; import { FlexWrapper } from "@ente/shared/components/Container"; diff --git a/web/apps/photos/src/components/PhotoViewer/index.tsx b/web/apps/photos/src/components/PhotoViewer/index.tsx index a19469a6f..451801113 100644 --- a/web/apps/photos/src/components/PhotoViewer/index.tsx +++ b/web/apps/photos/src/components/PhotoViewer/index.tsx @@ -16,7 +16,7 @@ import { isSupportedRawFormat, } from "utils/file"; -import { FILE_TYPE } from "@/media/file"; +import { FILE_TYPE } from "@/media/file-type"; import { FlexWrapper } from "@ente/shared/components/Container"; import EnteSpinner from "@ente/shared/components/EnteSpinner"; import AlbumOutlined from "@mui/icons-material/AlbumOutlined"; diff --git a/web/apps/photos/src/components/PlaceholderThumbnails.tsx b/web/apps/photos/src/components/PlaceholderThumbnails.tsx index 7266bf178..662e42287 100644 --- a/web/apps/photos/src/components/PlaceholderThumbnails.tsx +++ b/web/apps/photos/src/components/PlaceholderThumbnails.tsx @@ -1,4 +1,4 @@ -import { FILE_TYPE } from "@/media/file"; +import { FILE_TYPE } from "@/media/file-type"; import { Overlay } from "@ente/shared/components/Container"; import PhotoOutlined from "@mui/icons-material/PhotoOutlined"; import PlayCircleOutlineOutlined from "@mui/icons-material/PlayCircleOutlineOutlined"; diff --git a/web/apps/photos/src/components/Search/SearchBar/searchInput/index.tsx b/web/apps/photos/src/components/Search/SearchBar/searchInput/index.tsx index 87c45b59f..3f737b3e0 100644 --- a/web/apps/photos/src/components/Search/SearchBar/searchInput/index.tsx +++ b/web/apps/photos/src/components/Search/SearchBar/searchInput/index.tsx @@ -1,4 +1,4 @@ -import { FILE_TYPE } from "@/media/file"; +import { FILE_TYPE } from "@/media/file-type"; import CloseIcon from "@mui/icons-material/Close"; import { IconButton } from "@mui/material"; import { t } from "i18next"; diff --git a/web/apps/photos/src/components/pages/gallery/PreviewCard.tsx b/web/apps/photos/src/components/pages/gallery/PreviewCard.tsx index b9288c59b..8091618a1 100644 --- a/web/apps/photos/src/components/pages/gallery/PreviewCard.tsx +++ b/web/apps/photos/src/components/pages/gallery/PreviewCard.tsx @@ -1,4 +1,4 @@ -import { FILE_TYPE } from "@/media/file"; +import { FILE_TYPE } from "@/media/file-type"; import log from "@/next/log"; import { Overlay } from "@ente/shared/components/Container"; import { CustomError } from "@ente/shared/error"; diff --git a/web/apps/photos/src/constants/upload.ts b/web/apps/photos/src/constants/upload.ts index b188626df..faa8e586f 100644 --- a/web/apps/photos/src/constants/upload.ts +++ b/web/apps/photos/src/constants/upload.ts @@ -1,50 +1,5 @@ -import { FILE_TYPE } from "@/media/file"; import { ENCRYPTION_CHUNK_SIZE } from "@ente/shared/crypto/constants"; -import { FileTypeInfo, Location } from "types/upload"; - -// list of format that were missed by type-detection for some files. -export const WHITELISTED_FILE_FORMATS: FileTypeInfo[] = [ - { fileType: FILE_TYPE.IMAGE, exactType: "jpeg", mimeType: "image/jpeg" }, - { fileType: FILE_TYPE.IMAGE, exactType: "jpg", mimeType: "image/jpeg" }, - { fileType: FILE_TYPE.VIDEO, exactType: "webm", mimeType: "video/webm" }, - { fileType: FILE_TYPE.VIDEO, exactType: "mod", mimeType: "video/mpeg" }, - { fileType: FILE_TYPE.VIDEO, exactType: "mp4", mimeType: "video/mp4" }, - { fileType: FILE_TYPE.IMAGE, exactType: "gif", mimeType: "image/gif" }, - { fileType: FILE_TYPE.VIDEO, exactType: "dv", mimeType: "video/x-dv" }, - { - fileType: FILE_TYPE.VIDEO, - exactType: "wmv", - mimeType: "video/x-ms-asf", - }, - { - fileType: FILE_TYPE.VIDEO, - exactType: "hevc", - mimeType: "video/hevc", - }, - { - fileType: FILE_TYPE.IMAGE, - exactType: "raf", - mimeType: "image/x-fuji-raf", - }, - { - fileType: FILE_TYPE.IMAGE, - exactType: "orf", - mimeType: "image/x-olympus-orf", - }, - - { - fileType: FILE_TYPE.IMAGE, - exactType: "crw", - mimeType: "image/x-canon-crw", - }, - { - fileType: FILE_TYPE.VIDEO, - exactType: "mov", - mimeType: "video/quicktime", - }, -]; - -export const KNOWN_NON_MEDIA_FORMATS = ["xmp", "html", "txt"]; +import { Location } from "types/upload"; // this is the chunk size of the un-encrypted file which is read and encrypted before uploading it as a single part. export const MULTIPART_PART_SIZE = 20 * 1024 * 1024; diff --git a/web/apps/photos/src/services/clip-service.ts b/web/apps/photos/src/services/clip-service.ts index ab1ce928c..703c89cf4 100644 --- a/web/apps/photos/src/services/clip-service.ts +++ b/web/apps/photos/src/services/clip-service.ts @@ -1,4 +1,4 @@ -import { FILE_TYPE } from "@/media/file"; +import { FILE_TYPE } from "@/media/file-type"; import { ensureElectron } from "@/next/electron"; import log from "@/next/log"; import ComlinkCryptoWorker from "@ente/shared/crypto"; diff --git a/web/apps/photos/src/services/deduplicationService.ts b/web/apps/photos/src/services/deduplicationService.ts index f9416396f..d591e89ff 100644 --- a/web/apps/photos/src/services/deduplicationService.ts +++ b/web/apps/photos/src/services/deduplicationService.ts @@ -1,4 +1,4 @@ -import { FILE_TYPE } from "@/media/file"; +import { FILE_TYPE } from "@/media/file-type"; import log from "@/next/log"; import HTTPService from "@ente/shared/network/HTTPService"; import { getEndpoint } from "@ente/shared/network/api"; diff --git a/web/apps/photos/src/services/download/index.ts b/web/apps/photos/src/services/download/index.ts index 7a2c35932..37eeac440 100644 --- a/web/apps/photos/src/services/download/index.ts +++ b/web/apps/photos/src/services/download/index.ts @@ -1,4 +1,4 @@ -import { FILE_TYPE } from "@/media/file"; +import { FILE_TYPE } from "@/media/file-type"; import { decodeLivePhoto } from "@/media/live-photo"; import { openCache, type BlobCache } from "@/next/blob-cache"; import log from "@/next/log"; diff --git a/web/apps/photos/src/services/exif.ts b/web/apps/photos/src/services/exif.ts index acc6a0b08..aea44dedc 100644 --- a/web/apps/photos/src/services/exif.ts +++ b/web/apps/photos/src/services/exif.ts @@ -1,9 +1,10 @@ +import { type FileTypeInfo } from "@/media/file-type"; import log from "@/next/log"; import { validateAndGetCreationUnixTimeInMicroSeconds } from "@ente/shared/time"; import { NULL_LOCATION } from "constants/upload"; import exifr from "exifr"; import piexif from "piexifjs"; -import { FileTypeInfo, Location } from "types/upload"; +import { Location } from "types/upload"; type ParsedEXIFData = Record & Partial<{ diff --git a/web/apps/photos/src/services/export/index.ts b/web/apps/photos/src/services/export/index.ts index c949b4636..3ec98a8d0 100644 --- a/web/apps/photos/src/services/export/index.ts +++ b/web/apps/photos/src/services/export/index.ts @@ -1,4 +1,4 @@ -import { FILE_TYPE } from "@/media/file"; +import { FILE_TYPE } from "@/media/file-type"; import { decodeLivePhoto } from "@/media/live-photo"; import { ensureElectron } from "@/next/electron"; import log from "@/next/log"; diff --git a/web/apps/photos/src/services/export/migration.ts b/web/apps/photos/src/services/export/migration.ts index 6c13ce7fb..a6d0af29f 100644 --- a/web/apps/photos/src/services/export/migration.ts +++ b/web/apps/photos/src/services/export/migration.ts @@ -1,4 +1,4 @@ -import { FILE_TYPE } from "@/media/file"; +import { FILE_TYPE } from "@/media/file-type"; import { decodeLivePhoto } from "@/media/live-photo"; import { ensureElectron } from "@/next/electron"; import log from "@/next/log"; diff --git a/web/apps/photos/src/services/fix-exif.ts b/web/apps/photos/src/services/fix-exif.ts index 55f3dce12..81c3611f0 100644 --- a/web/apps/photos/src/services/fix-exif.ts +++ b/web/apps/photos/src/services/fix-exif.ts @@ -1,4 +1,4 @@ -import { FILE_TYPE } from "@/media/file"; +import { FILE_TYPE } from "@/media/file-type"; import log from "@/next/log"; import { validateAndGetCreationUnixTimeInMicroSeconds } from "@ente/shared/time"; import type { FixOption } from "components/FixCreationTime"; diff --git a/web/apps/photos/src/services/machineLearning/mlWorkManager.ts b/web/apps/photos/src/services/machineLearning/mlWorkManager.ts index a00c24ad3..d1c5e9db5 100644 --- a/web/apps/photos/src/services/machineLearning/mlWorkManager.ts +++ b/web/apps/photos/src/services/machineLearning/mlWorkManager.ts @@ -1,4 +1,4 @@ -import { FILE_TYPE } from "@/media/file"; +import { FILE_TYPE } from "@/media/file-type"; import log from "@/next/log"; import { ComlinkWorker } from "@/next/worker/comlink-worker"; import { eventBus, Events } from "@ente/shared/events"; diff --git a/web/apps/photos/src/services/machineLearning/readerService.ts b/web/apps/photos/src/services/machineLearning/readerService.ts index d45380641..62aebdbd1 100644 --- a/web/apps/photos/src/services/machineLearning/readerService.ts +++ b/web/apps/photos/src/services/machineLearning/readerService.ts @@ -1,4 +1,4 @@ -import { FILE_TYPE } from "@/media/file"; +import { FILE_TYPE } from "@/media/file-type"; import log from "@/next/log"; import { MLSyncContext, MLSyncFileContext } from "types/machineLearning"; import { diff --git a/web/apps/photos/src/services/searchService.ts b/web/apps/photos/src/services/searchService.ts index 362e76fbe..96c574b9d 100644 --- a/web/apps/photos/src/services/searchService.ts +++ b/web/apps/photos/src/services/searchService.ts @@ -1,4 +1,4 @@ -import { FILE_TYPE } from "@/media/file"; +import { FILE_TYPE } from "@/media/file-type"; import log from "@/next/log"; import * as chrono from "chrono-node"; import { t } from "i18next"; diff --git a/web/apps/photos/src/services/typeDetectionService.ts b/web/apps/photos/src/services/typeDetectionService.ts index 598b10046..3ca50ca02 100644 --- a/web/apps/photos/src/services/typeDetectionService.ts +++ b/web/apps/photos/src/services/typeDetectionService.ts @@ -1,13 +1,13 @@ -import { FILE_TYPE } from "@/media/file"; +import { + FILE_TYPE, + KnownFileTypeInfos, + KnownNonMediaFileExtensions, + type FileTypeInfo, +} from "@/media/file-type"; import log from "@/next/log"; import { ElectronFile } from "@/next/types/file"; import { CustomError } from "@ente/shared/error"; -import { - KNOWN_NON_MEDIA_FORMATS, - WHITELISTED_FILE_FORMATS, -} from "constants/upload"; import FileType, { FileTypeResult } from "file-type"; -import { FileTypeInfo } from "types/upload"; import { getFileExtension } from "utils/file"; import { getUint8ArrayView } from "./readerService"; @@ -50,13 +50,13 @@ export async function getFileType( }; } catch (e) { const fileFormat = getFileExtension(receivedFile.name); - const whiteListedFormat = WHITELISTED_FILE_FORMATS.find( + const whiteListedFormat = KnownFileTypeInfos.find( (a) => a.exactType === fileFormat, ); if (whiteListedFormat) { return whiteListedFormat; } - if (KNOWN_NON_MEDIA_FORMATS.includes(fileFormat)) { + if (KnownNonMediaFileExtensions.includes(fileFormat)) { throw Error(CustomError.UNSUPPORTED_FILE_FORMAT); } if (e.message === CustomError.NON_MEDIA_FILE) { diff --git a/web/apps/photos/src/services/upload/metadata.ts b/web/apps/photos/src/services/upload/metadata.ts index 4d7b06e33..b5716e4de 100644 --- a/web/apps/photos/src/services/upload/metadata.ts +++ b/web/apps/photos/src/services/upload/metadata.ts @@ -1,4 +1,4 @@ -import { FILE_TYPE } from "@/media/file"; +import { FILE_TYPE, type FileTypeInfo } from "@/media/file-type"; import { getFileNameSize } from "@/next/file"; import log from "@/next/log"; import { ElectronFile } from "@/next/types/file"; @@ -17,7 +17,6 @@ import * as ffmpegService from "services/ffmpeg"; import { getElectronFileStream, getFileStream } from "services/readerService"; import { FilePublicMagicMetadataProps } from "types/file"; import { - FileTypeInfo, Metadata, ParsedExtractedMetadata, type LivePhotoAssets2, diff --git a/web/apps/photos/src/services/upload/thumbnail.ts b/web/apps/photos/src/services/upload/thumbnail.ts index d8ba3a201..b89bc2f79 100644 --- a/web/apps/photos/src/services/upload/thumbnail.ts +++ b/web/apps/photos/src/services/upload/thumbnail.ts @@ -1,11 +1,10 @@ -import { FILE_TYPE } from "@/media/file"; +import { FILE_TYPE, type FileTypeInfo } from "@/media/file-type"; import log from "@/next/log"; import { type Electron } from "@/next/types/ipc"; import { withTimeout } from "@ente/shared/utils"; import { BLACK_THUMBNAIL_BASE64 } from "constants/upload"; import * as ffmpeg from "services/ffmpeg"; import { heicToJPEG } from "services/heic-convert"; -import { FileTypeInfo } from "types/upload"; import { isFileHEIC } from "utils/file"; /** Maximum width or height of the generated thumbnail */ diff --git a/web/apps/photos/src/services/upload/uploadManager.ts b/web/apps/photos/src/services/upload/uploadManager.ts index 9917ee892..065414158 100644 --- a/web/apps/photos/src/services/upload/uploadManager.ts +++ b/web/apps/photos/src/services/upload/uploadManager.ts @@ -1,4 +1,4 @@ -import { FILE_TYPE } from "@/media/file"; +import { FILE_TYPE } from "@/media/file-type"; import { potentialFileTypeFromExtension } from "@/media/live-photo"; import { ensureElectron } from "@/next/electron"; import { nameAndExtension } from "@/next/file"; diff --git a/web/apps/photos/src/services/upload/uploadService.ts b/web/apps/photos/src/services/upload/uploadService.ts index 1df51a5ac..4e2292445 100644 --- a/web/apps/photos/src/services/upload/uploadService.ts +++ b/web/apps/photos/src/services/upload/uploadService.ts @@ -1,4 +1,4 @@ -import { FILE_TYPE } from "@/media/file"; +import { FILE_TYPE, type FileTypeInfo } from "@/media/file-type"; import { encodeLivePhoto } from "@/media/live-photo"; import { ensureElectron } from "@/next/electron"; import { basename } from "@/next/file"; @@ -28,7 +28,6 @@ import { BackupedFile, EncryptedFile, FileInMemory, - FileTypeInfo, FileWithMetadata, ProcessedFile, PublicUploadProps, diff --git a/web/apps/photos/src/types/search/index.ts b/web/apps/photos/src/types/search/index.ts index 5918bb9dd..cf50f4a06 100644 --- a/web/apps/photos/src/types/search/index.ts +++ b/web/apps/photos/src/types/search/index.ts @@ -1,4 +1,4 @@ -import { FILE_TYPE } from "@/media/file"; +import { FILE_TYPE } from "@/media/file-type"; import { City } from "services/locationSearchService"; import { LocationTagData } from "types/entity"; import { EnteFile } from "types/file"; diff --git a/web/apps/photos/src/types/upload/index.ts b/web/apps/photos/src/types/upload/index.ts index bdcef330b..2111617bd 100644 --- a/web/apps/photos/src/types/upload/index.ts +++ b/web/apps/photos/src/types/upload/index.ts @@ -1,4 +1,4 @@ -import { FILE_TYPE } from "@/media/file"; +import { FILE_TYPE } from "@/media/file-type"; import type { ElectronFile } from "@/next/types/file"; import { B64EncryptionResult, @@ -46,14 +46,6 @@ export interface MultipartUploadURLs { completeURL: string; } -export interface FileTypeInfo { - fileType: FILE_TYPE; - exactType: string; - mimeType?: string; - imageType?: string; - videoType?: string; -} - export interface UploadAsset { isLivePhoto?: boolean; file?: File | ElectronFile; diff --git a/web/apps/photos/src/utils/file/index.ts b/web/apps/photos/src/utils/file/index.ts index 1ae6804bb..c6e5d5356 100644 --- a/web/apps/photos/src/utils/file/index.ts +++ b/web/apps/photos/src/utils/file/index.ts @@ -1,4 +1,4 @@ -import { FILE_TYPE } from "@/media/file"; +import { FILE_TYPE, type FileTypeInfo } from "@/media/file-type"; import { decodeLivePhoto } from "@/media/live-photo"; import log from "@/next/log"; import { CustomErrorMessage, type Electron } from "@/next/types/ipc"; @@ -35,7 +35,6 @@ import { SetFilesDownloadProgressAttributesCreator, } from "types/gallery"; import { VISIBILITY_STATE } from "types/magicMetadata"; -import { FileTypeInfo } from "types/upload"; import { isArchivedFile, updateMagicMetadata } from "utils/magicMetadata"; import { safeFileName } from "utils/native-fs"; import { writeStream } from "utils/native-stream"; diff --git a/web/apps/photos/src/utils/machineLearning/index.ts b/web/apps/photos/src/utils/machineLearning/index.ts index 4e9b795ec..bc9ae3974 100644 --- a/web/apps/photos/src/utils/machineLearning/index.ts +++ b/web/apps/photos/src/utils/machineLearning/index.ts @@ -1,4 +1,4 @@ -import { FILE_TYPE } from "@/media/file"; +import { FILE_TYPE } from "@/media/file-type"; import { decodeLivePhoto } from "@/media/live-photo"; import log from "@/next/log"; import PQueue from "p-queue"; diff --git a/web/apps/photos/src/utils/photoFrame/index.ts b/web/apps/photos/src/utils/photoFrame/index.ts index ccd47b604..93b680149 100644 --- a/web/apps/photos/src/utils/photoFrame/index.ts +++ b/web/apps/photos/src/utils/photoFrame/index.ts @@ -1,4 +1,4 @@ -import { FILE_TYPE } from "@/media/file"; +import { FILE_TYPE } from "@/media/file-type"; import log from "@/next/log"; import { LivePhotoSourceURL, SourceURLs } from "services/download"; import { EnteFile } from "types/file"; diff --git a/web/apps/photos/tests/upload.test.ts b/web/apps/photos/tests/upload.test.ts index 3fc7e8541..17143da09 100644 --- a/web/apps/photos/tests/upload.test.ts +++ b/web/apps/photos/tests/upload.test.ts @@ -1,4 +1,4 @@ -import { FILE_TYPE } from "@/media/file"; +import { FILE_TYPE } from "@/media/file-type"; import { tryToParseDateTime } from "@ente/shared/time"; import { getLocalCollections } from "services/collectionService"; import { getLocalFiles } from "services/fileService"; diff --git a/web/packages/media/file-type.ts b/web/packages/media/file-type.ts new file mode 100644 index 000000000..e7eb90275 --- /dev/null +++ b/web/packages/media/file-type.ts @@ -0,0 +1,58 @@ +export enum FILE_TYPE { + IMAGE, + VIDEO, + LIVE_PHOTO, + OTHERS, +} + +export interface FileTypeInfo { + fileType: FILE_TYPE; + exactType: string; + mimeType?: string; + imageType?: string; + videoType?: string; +} + +// list of format that were missed by type-detection for some files. +export const KnownFileTypeInfos: FileTypeInfo[] = [ + { fileType: FILE_TYPE.IMAGE, exactType: "jpeg", mimeType: "image/jpeg" }, + { fileType: FILE_TYPE.IMAGE, exactType: "jpg", mimeType: "image/jpeg" }, + { fileType: FILE_TYPE.VIDEO, exactType: "webm", mimeType: "video/webm" }, + { fileType: FILE_TYPE.VIDEO, exactType: "mod", mimeType: "video/mpeg" }, + { fileType: FILE_TYPE.VIDEO, exactType: "mp4", mimeType: "video/mp4" }, + { fileType: FILE_TYPE.IMAGE, exactType: "gif", mimeType: "image/gif" }, + { fileType: FILE_TYPE.VIDEO, exactType: "dv", mimeType: "video/x-dv" }, + { + fileType: FILE_TYPE.VIDEO, + exactType: "wmv", + mimeType: "video/x-ms-asf", + }, + { + fileType: FILE_TYPE.VIDEO, + exactType: "hevc", + mimeType: "video/hevc", + }, + { + fileType: FILE_TYPE.IMAGE, + exactType: "raf", + mimeType: "image/x-fuji-raf", + }, + { + fileType: FILE_TYPE.IMAGE, + exactType: "orf", + mimeType: "image/x-olympus-orf", + }, + + { + fileType: FILE_TYPE.IMAGE, + exactType: "crw", + mimeType: "image/x-canon-crw", + }, + { + fileType: FILE_TYPE.VIDEO, + exactType: "mov", + mimeType: "video/quicktime", + }, +]; + +export const KnownNonMediaFileExtensions = ["xmp", "html", "txt"]; diff --git a/web/packages/media/file.ts b/web/packages/media/file.ts deleted file mode 100644 index 8917db802..000000000 --- a/web/packages/media/file.ts +++ /dev/null @@ -1,6 +0,0 @@ -export enum FILE_TYPE { - IMAGE, - VIDEO, - LIVE_PHOTO, - OTHERS, -} diff --git a/web/packages/media/live-photo.ts b/web/packages/media/live-photo.ts index c4a631467..0e48d7120 100644 --- a/web/packages/media/live-photo.ts +++ b/web/packages/media/live-photo.ts @@ -1,6 +1,6 @@ import { fileNameFromComponents, nameAndExtension } from "@/next/file"; import JSZip from "jszip"; -import { FILE_TYPE } from "./file"; +import { FILE_TYPE } from "./file-type"; const potentialImageExtensions = [ "heic",