From 67eabd5d027825eb3832a2e60ba7033fd795eccb Mon Sep 17 00:00:00 2001 From: Abhinav Date: Mon, 5 Jun 2023 22:55:02 +0530 Subject: [PATCH] add support to show conversion failed message --- apps/photos/src/components/PhotoFrame.tsx | 57 ++++++++++--- .../src/components/PhotoViewer/index.tsx | 14 +++- .../styledComponents/ConversionFailedBtn.tsx | 15 ++++ apps/photos/src/types/file/index.ts | 1 + apps/photos/src/utils/file/index.ts | 80 ++++++++++--------- 5 files changed, 119 insertions(+), 48 deletions(-) create mode 100644 apps/photos/src/components/PhotoViewer/styledComponents/ConversionFailedBtn.tsx diff --git a/apps/photos/src/components/PhotoFrame.tsx b/apps/photos/src/components/PhotoFrame.tsx index 5b85af82f..31730fe1f 100644 --- a/apps/photos/src/components/PhotoFrame.tsx +++ b/apps/photos/src/components/PhotoFrame.tsx @@ -80,6 +80,7 @@ const PhotoFrame = ({ const [isShiftKeyPressed, setIsShiftKeyPressed] = useState(false); const router = useRouter(); const [isSourceLoaded, setIsSourceLoaded] = useState(false); + const [conversionFailed, setConversionFailed] = useState(false); const displayFiles = useMemoSingleThreaded(() => { return files.map((item) => { @@ -196,7 +197,8 @@ const PhotoFrame = ({ const updateSrcURL = async ( index: number, id: number, - mergedSrcURL: MergedSourceURL + mergedSrcURL: MergedSourceURL, + conversionFailed: boolean ) => { const file = displayFiles[index]; // this is to prevent outdate updateSrcURL call from updating the wrong file @@ -217,6 +219,21 @@ const PhotoFrame = ({ 'PhotoSwipe updateSrcURL called when source already loaded' ); return; + } else if (file.conversionFailed) { + addLogLine( + `PhotoSwipe: updateSrcURL: conversion failed: ${file.id}` + ); + logError( + new Error( + `PhotoSwipe: updateSrcURL: conversion failed: ${file.id}` + ), + 'PhotoSwipe updateSrcURL called when conversion failed' + ); + return; + } + if (conversionFailed) { + setConversionFailed(true); + return; } await updateFileSrcProps(file, mergedSrcURL); setIsSourceLoaded(true); @@ -406,18 +423,28 @@ const PhotoFrame = ({ logError(e, 'getSlideData failed get msrc url failed'); } } + if (item.isSourceLoaded) { + setIsSourceLoaded(true); addLogLine(`[${item.id}] source already loaded`); return; } + if (item.conversionFailed) { + setConversionFailed(true); + addLogLine(`[${item.id}] conversion failed`); + return; + } if (fetching[item.id]) { addLogLine(`[${item.id}] file download already in progress`); return; } + setIsSourceLoaded(false); + setConversionFailed(false); try { addLogLine(`[${item.id}] new file src request`); fetching[item.id] = true; let srcURL: MergedSourceURL; + let conversionFailed: boolean = false; if (galleryContext.files.has(item.id)) { addLogLine( `[${item.id}] gallery context cache hit, using cached file` @@ -428,7 +455,10 @@ const PhotoFrame = ({ `[${item.id}] gallery context cache miss, calling downloadManager to get file` ); appContext.startLoading(); - let downloadedURL; + let downloadedURL: { + original: string[]; + converted: string[]; + }; if (publicCollectionGalleryContext.accessedThroughSharedURL) { downloadedURL = await PublicCollectionDownloadManager.getFile( @@ -441,15 +471,21 @@ const PhotoFrame = ({ downloadedURL = await DownloadManager.getFile(item, true); } appContext.finishLoading(); - const mergedURL: MergedSourceURL = { - original: downloadedURL.original.join(','), - converted: downloadedURL.converted.join(','), - }; - galleryContext.files.set(item.id, mergedURL); - srcURL = mergedURL; + if ( + downloadedURL.converted.filter((url) => !!url).length !== + downloadedURL.converted.length + ) { + conversionFailed = true; + } else { + const mergedURL: MergedSourceURL = { + original: downloadedURL.original.join(','), + converted: downloadedURL.converted.join(','), + }; + galleryContext.files.set(item.id, mergedURL); + srcURL = mergedURL; + } } - setIsSourceLoaded(false); - await updateSrcURL(index, item.id, srcURL); + await updateSrcURL(index, item.id, srcURL, conversionFailed); try { addLogLine( @@ -498,6 +534,7 @@ const PhotoFrame = ({ isHiddenCollection={activeCollection === HIDDEN_SECTION} enableDownload={enableDownload} isSourceLoaded={isSourceLoaded} + conversionFailed={conversionFailed} fileToCollectionsMap={fileToCollectionsMap} collectionNameMap={collectionNameMap} /> diff --git a/apps/photos/src/components/PhotoViewer/index.tsx b/apps/photos/src/components/PhotoViewer/index.tsx index f825c672b..3819d7313 100644 --- a/apps/photos/src/components/PhotoViewer/index.tsx +++ b/apps/photos/src/components/PhotoViewer/index.tsx @@ -42,6 +42,7 @@ import ChevronLeft from '@mui/icons-material/ChevronLeft'; import { t } from 'i18next'; import { getParsedExifData } from 'services/upload/exifService'; import { getFileType } from 'services/typeDetectionService'; +import { ConversionFailedBtn } from './styledComponents/ConversionFailedBtn'; interface PhotoswipeFullscreenAPI { enter: () => void; @@ -75,6 +76,7 @@ interface Iprops { isHiddenCollection: boolean; enableDownload: boolean; isSourceLoaded: boolean; + conversionFailed: boolean; fileToCollectionsMap: Map; collectionNameMap: Map; } @@ -84,7 +86,7 @@ function PhotoViewer(props: Iprops) { const [photoSwipe, setPhotoSwipe] = useState>(); - const { isOpen, items, isSourceLoaded } = props; + const { isOpen, items, isSourceLoaded, conversionFailed } = props; const [isFav, setIsFav] = useState(false); const [showInfo, setShowInfo] = useState(false); const [exif, setExif] = @@ -559,6 +561,16 @@ function PhotoViewer(props: Iprops) { {livePhotoBtnHTML} {t('LIVE')} )} + {conversionFailed && ( + + downloadFileHelper(photoSwipe.currItem) + }> + {' '} + DOWNLOAD{' '} + + )} +
diff --git a/apps/photos/src/components/PhotoViewer/styledComponents/ConversionFailedBtn.tsx b/apps/photos/src/components/PhotoViewer/styledComponents/ConversionFailedBtn.tsx new file mode 100644 index 000000000..546acd96e --- /dev/null +++ b/apps/photos/src/components/PhotoViewer/styledComponents/ConversionFailedBtn.tsx @@ -0,0 +1,15 @@ +import { styled } from '@mui/material'; +export const ConversionFailedBtn = styled('button')` + position: absolute; + bottom: 6vh; + left: 6vh; + height: 40px; + width: 80px; + background: #d7d7d7; + outline: none; + border: none; + border-radius: 10%; + z-index: 10; + cursor: ${(props) => (props.disabled ? 'not-allowed' : 'pointer')}; + } +`; diff --git a/apps/photos/src/types/file/index.ts b/apps/photos/src/types/file/index.ts index 5b4e3c37f..249901c71 100644 --- a/apps/photos/src/types/file/index.ts +++ b/apps/photos/src/types/file/index.ts @@ -60,6 +60,7 @@ export interface EnteFile originalVideoURL?: string; originalImageURL?: string; dataIndex?: number; + conversionFailed?: boolean; } export interface TrashRequest { diff --git a/apps/photos/src/utils/file/index.ts b/apps/photos/src/utils/file/index.ts index 6626222b1..8dcb37673 100644 --- a/apps/photos/src/utils/file/index.ts +++ b/apps/photos/src/utils/file/index.ts @@ -286,14 +286,18 @@ export async function getRenderableFileURL(file: EnteFile, fileBlob: Blob) { fileBlob ); return { - converted: [URL.createObjectURL(convertedBlob)], + converted: [ + convertedBlob ? URL.createObjectURL(convertedBlob) : null, + ], original: [URL.createObjectURL(fileBlob)], }; } case FILE_TYPE.LIVE_PHOTO: { const livePhoto = await getRenderableLivePhoto(file, fileBlob); return { - converted: livePhoto.map((asset) => URL.createObjectURL(asset)), + converted: livePhoto.map((asset) => + asset ? URL.createObjectURL(asset) : null + ), original: [URL.createObjectURL(fileBlob)], }; } @@ -330,52 +334,54 @@ async function getPlayableVideo(videoNameTitle: string, video: Uint8Array) { } export async function getRenderableImage(fileName: string, imageBlob: Blob) { - const tempFile = new File([imageBlob], fileName); - const { exactType } = await getFileType(tempFile); - let convertedImageBlob: Blob; - if (isRawFile(exactType)) { - try { - if (!isSupportedRawFormat(exactType)) { - throw Error(CustomError.UNSUPPORTED_RAW_FORMAT); - } - - if (!isElectron()) { - throw Error(CustomError.NOT_AVAILABLE_ON_WEB); - } - addLogLine( - `RawConverter called for ${fileName}-${convertBytesToHumanReadable( - imageBlob.size - )}` - ); - convertedImageBlob = await imageProcessor.convertToJPEG( - imageBlob, - fileName - ); - addLogLine(`${fileName} successfully converted`); - } catch (e) { + try { + const tempFile = new File([imageBlob], fileName); + const { exactType } = await getFileType(tempFile); + let convertedImageBlob: Blob; + if (isRawFile(exactType)) { try { - if (!isFileHEIC(exactType)) { - throw e; + if (!isSupportedRawFormat(exactType)) { + throw Error(CustomError.UNSUPPORTED_RAW_FORMAT); + } + + if (!isElectron()) { + throw Error(CustomError.NOT_AVAILABLE_ON_WEB); } addLogLine( - `HEICConverter called for ${fileName}-${convertBytesToHumanReadable( + `RawConverter called for ${fileName}-${convertBytesToHumanReadable( imageBlob.size )}` ); - convertedImageBlob = await heicConversionService.convert( - imageBlob + convertedImageBlob = await imageProcessor.convertToJPEG( + imageBlob, + fileName ); addLogLine(`${fileName} successfully converted`); } catch (e) { - logError(e, 'get Renderable Image failed', { - fileType: exactType, - }); - throw Error(CustomError.NON_PREVIEWABLE_FILE); + try { + if (!isFileHEIC(exactType)) { + throw e; + } + addLogLine( + `HEICConverter called for ${fileName}-${convertBytesToHumanReadable( + imageBlob.size + )}` + ); + convertedImageBlob = await heicConversionService.convert( + imageBlob + ); + addLogLine(`${fileName} successfully converted`); + } catch (e) { + throw Error(CustomError.NON_PREVIEWABLE_FILE); + } } + return convertedImageBlob; + } else { + return imageBlob; } - return convertedImageBlob; - } else { - return imageBlob; + } catch (e) { + logError(e, 'get Renderable Image failed'); + return null; } }