Przeglądaj źródła

Merge pull request #255 from ente-io/download-manager-imporvements

Download manager improvements
Vishnu Mohandas 3 lat temu
rodzic
commit
24fcbb3723

+ 1 - 1
src/components/PhotoFrame.tsx

@@ -354,7 +354,7 @@ const PhotoFrame = ({
                 if (galleryContext.thumbs.has(item.id)) {
                 if (galleryContext.thumbs.has(item.id)) {
                     url = galleryContext.thumbs.get(item.id);
                     url = galleryContext.thumbs.get(item.id);
                 } else {
                 } else {
-                    url = await DownloadManager.getPreview(item);
+                    url = await DownloadManager.getThumbnail(item);
                     galleryContext.thumbs.set(item.id, url);
                     galleryContext.thumbs.set(item.id, url);
                 }
                 }
                 updateUrl(item.dataIndex)(url);
                 updateUrl(item.dataIndex)(url);

+ 1 - 1
src/components/pages/gallery/PreviewCard.tsx

@@ -177,7 +177,7 @@ export default function PreviewCard(props: IProps) {
         if (file && !file.msrc) {
         if (file && !file.msrc) {
             const main = async () => {
             const main = async () => {
                 try {
                 try {
-                    const url = await DownloadManager.getPreview(file);
+                    const url = await DownloadManager.getThumbnail(file);
                     if (isMounted.current) {
                     if (isMounted.current) {
                         setImgSrc(url);
                         setImgSrc(url);
                         thumbs.set(file.id, url);
                         thumbs.set(file.id, url);

+ 45 - 49
src/services/downloadManager.ts

@@ -1,7 +1,11 @@
 import { getToken } from 'utils/common/key';
 import { getToken } from 'utils/common/key';
 import { getFileUrl, getThumbnailUrl } from 'utils/common/apiUtil';
 import { getFileUrl, getThumbnailUrl } from 'utils/common/apiUtil';
 import CryptoWorker from 'utils/crypto';
 import CryptoWorker from 'utils/crypto';
-import { generateStreamFromArrayBuffer, convertForPreview } from 'utils/file';
+import {
+    generateStreamFromArrayBuffer,
+    convertForPreview,
+    needsConversionForPreview,
+} from 'utils/file';
 import HTTPService from './HTTPService';
 import HTTPService from './HTTPService';
 import { File, FILE_TYPE } from './fileService';
 import { File, FILE_TYPE } from './fileService';
 import { logError } from 'utils/sentry';
 import { logError } from 'utils/sentry';
@@ -10,27 +14,36 @@ class DownloadManager {
     private fileObjectUrlPromise = new Map<string, Promise<string>>();
     private fileObjectUrlPromise = new Map<string, Promise<string>>();
     private thumbnailObjectUrlPromise = new Map<number, Promise<string>>();
     private thumbnailObjectUrlPromise = new Map<number, Promise<string>>();
 
 
-    public async getPreview(file: File) {
+    public async getThumbnail(file: File) {
         try {
         try {
             const token = getToken();
             const token = getToken();
             if (!token) {
             if (!token) {
                 return null;
                 return null;
             }
             }
-            const thumbnailCache = await caches.open('thumbs');
-            const cacheResp: Response = await thumbnailCache.match(
-                file.id.toString()
-            );
-            if (cacheResp) {
-                return URL.createObjectURL(await cacheResp.blob());
-            }
             if (!this.thumbnailObjectUrlPromise.get(file.id)) {
             if (!this.thumbnailObjectUrlPromise.get(file.id)) {
-                const downloadPromise = this.downloadThumb(
-                    token,
-                    thumbnailCache,
-                    file
-                );
-                this.thumbnailObjectUrlPromise.set(file.id, downloadPromise);
+                const downloadPromise = async () => {
+                    const thumbnailCache = await caches.open('thumbs');
+                    const cacheResp: Response = await thumbnailCache.match(
+                        file.id.toString()
+                    );
+                    if (cacheResp) {
+                        return URL.createObjectURL(await cacheResp.blob());
+                    }
+                    const thumb = await this.downloadThumb(token, file);
+                    const thumbBlob = new Blob([thumb]);
+                    try {
+                        await thumbnailCache.put(
+                            file.id.toString(),
+                            new Response(thumbBlob)
+                        );
+                    } catch (e) {
+                        // TODO: handle storage full exception.
+                    }
+                    return URL.createObjectURL(thumbBlob);
+                };
+                this.thumbnailObjectUrlPromise.set(file.id, downloadPromise());
             }
             }
+
             return await this.thumbnailObjectUrlPromise.get(file.id);
             return await this.thumbnailObjectUrlPromise.get(file.id);
         } catch (e) {
         } catch (e) {
             this.thumbnailObjectUrlPromise.delete(file.id);
             this.thumbnailObjectUrlPromise.delete(file.id);
@@ -39,24 +52,7 @@ class DownloadManager {
         }
         }
     }
     }
 
 
-    private downloadThumb = async (
-        token: string,
-        thumbnailCache: Cache,
-        file: File
-    ) => {
-        const thumb = await this.getThumbnail(token, file);
-        try {
-            await thumbnailCache.put(
-                file.id.toString(),
-                new Response(new Blob([thumb]))
-            );
-        } catch (e) {
-            // TODO: handle storage full exception.
-        }
-        return URL.createObjectURL(new Blob([thumb]));
-    };
-
-    getThumbnail = async (token: string, file: File) => {
+    downloadThumb = async (token: string, file: File) => {
         const resp = await HTTPService.get(
         const resp = await HTTPService.get(
             getThumbnailUrl(file.id),
             getThumbnailUrl(file.id),
             null,
             null,
@@ -73,36 +69,36 @@ class DownloadManager {
     };
     };
 
 
     getFile = async (file: File, forPreview = false) => {
     getFile = async (file: File, forPreview = false) => {
-        let fileUID: string;
-        if (file.metadata.fileType === FILE_TYPE.VIDEO) {
-            fileUID = file.id.toString();
-        } else {
-            fileUID = `${file.id}_forPreview=${forPreview}`;
-        }
+        const shouldBeConverted = forPreview && needsConversionForPreview(file);
+        const fileKey = shouldBeConverted
+            ? `${file.id}_converted`
+            : `${file.id}`;
         try {
         try {
-            const getFilePromise = async () => {
+            const getFilePromise = async (convert: boolean) => {
                 const fileStream = await this.downloadFile(file);
                 const fileStream = await this.downloadFile(file);
                 let fileBlob = await new Response(fileStream).blob();
                 let fileBlob = await new Response(fileStream).blob();
-                if (forPreview) {
+                if (convert) {
                     fileBlob = await convertForPreview(file, fileBlob);
                     fileBlob = await convertForPreview(file, fileBlob);
                 }
                 }
                 return URL.createObjectURL(fileBlob);
                 return URL.createObjectURL(fileBlob);
             };
             };
-            if (!this.fileObjectUrlPromise.get(fileUID)) {
-                this.fileObjectUrlPromise.set(fileUID, getFilePromise());
+            if (!this.fileObjectUrlPromise.get(fileKey)) {
+                this.fileObjectUrlPromise.set(
+                    fileKey,
+                    getFilePromise(shouldBeConverted)
+                );
             }
             }
-            return await this.fileObjectUrlPromise.get(fileUID);
+            const fileURL = await this.fileObjectUrlPromise.get(fileKey);
+            return fileURL;
         } catch (e) {
         } catch (e) {
-            this.fileObjectUrlPromise.delete(fileUID);
+            this.fileObjectUrlPromise.delete(fileKey);
             logError(e, 'Failed to get File');
             logError(e, 'Failed to get File');
             throw e;
             throw e;
         }
         }
     };
     };
 
 
-    public async getCachedFile(file: File) {
-        return await this.fileObjectUrlPromise.get(
-            `${file.id}_forPreview=false`
-        );
+    public async getCachedOriginalFile(file: File) {
+        return await this.fileObjectUrlPromise.get(file.id.toString());
     }
     }
 
 
     async downloadFile(file: File) {
     async downloadFile(file: File) {

+ 1 - 1
src/services/migrateThumbnailService.ts

@@ -67,7 +67,7 @@ export async function replaceThumbnail(
                     current: idx,
                     current: idx,
                     total: largeThumbnailFiles.length,
                     total: largeThumbnailFiles.length,
                 });
                 });
-                const originalThumbnail = await downloadManager.getThumbnail(
+                const originalThumbnail = await downloadManager.downloadThumb(
                     token,
                     token,
                     file
                     file
                 );
                 );

+ 2 - 2
src/services/upload/thumbnailService.ts

@@ -4,7 +4,7 @@ import { logError } from 'utils/sentry';
 import { BLACK_THUMBNAIL_BASE64 } from '../../../public/images/black-thumbnail-b64';
 import { BLACK_THUMBNAIL_BASE64 } from '../../../public/images/black-thumbnail-b64';
 import FFmpegService from 'services/ffmpegService';
 import FFmpegService from 'services/ffmpegService';
 import { convertToHumanReadable } from 'utils/billingUtil';
 import { convertToHumanReadable } from 'utils/billingUtil';
-import { fileIsHEIC } from 'utils/file';
+import { isFileHEIC } from 'utils/file';
 import { FileTypeInfo } from './readFileService';
 import { FileTypeInfo } from './readFileService';
 
 
 const MAX_THUMBNAIL_DIMENSION = 720;
 const MAX_THUMBNAIL_DIMENSION = 720;
@@ -31,7 +31,7 @@ export async function generateThumbnail(
         let thumbnail: Uint8Array;
         let thumbnail: Uint8Array;
         try {
         try {
             if (fileTypeInfo.fileType === FILE_TYPE.IMAGE) {
             if (fileTypeInfo.fileType === FILE_TYPE.IMAGE) {
-                const isHEIC = fileIsHEIC(fileTypeInfo.exactType);
+                const isHEIC = isFileHEIC(fileTypeInfo.exactType);
                 canvas = await generateImageThumbnail(worker, file, isHEIC);
                 canvas = await generateImageThumbnail(worker, file, isHEIC);
             } else {
             } else {
                 try {
                 try {

+ 19 - 5
src/utils/file/index.ts

@@ -40,7 +40,7 @@ export function downloadAsFile(filename: string, content: string) {
 export async function downloadFile(file: File) {
 export async function downloadFile(file: File) {
     const a = document.createElement('a');
     const a = document.createElement('a');
     a.style.display = 'none';
     a.style.display = 'none';
-    const cachedFileUrl = await DownloadManager.getCachedFile(file);
+    const cachedFileUrl = await DownloadManager.getCachedOriginalFile(file);
     const fileURL =
     const fileURL =
         cachedFileUrl ??
         cachedFileUrl ??
         URL.createObjectURL(
         URL.createObjectURL(
@@ -57,10 +57,11 @@ export async function downloadFile(file: File) {
     a.remove();
     a.remove();
 }
 }
 
 
-export function fileIsHEIC(mimeType: string) {
+export function isFileHEIC(mimeType: string) {
     return (
     return (
-        mimeType.toLowerCase().endsWith(TYPE_HEIC) ||
-        mimeType.toLowerCase().endsWith(TYPE_HEIF)
+        mimeType &&
+        (mimeType.toLowerCase().endsWith(TYPE_HEIC) ||
+            mimeType.toLowerCase().endsWith(TYPE_HEIF))
     );
     );
 }
 }
 
 
@@ -277,7 +278,7 @@ export async function convertForPreview(file: File, fileBlob: Blob) {
 
 
     const mimeType =
     const mimeType =
         (await getMimeTypeFromBlob(worker, fileBlob)) ?? typeFromExtension;
         (await getMimeTypeFromBlob(worker, fileBlob)) ?? typeFromExtension;
-    if (fileIsHEIC(mimeType)) {
+    if (isFileHEIC(mimeType)) {
         fileBlob = await worker.convertHEIC2JPEG(fileBlob);
         fileBlob = await worker.convertHEIC2JPEG(fileBlob);
     }
     }
     return fileBlob;
     return fileBlob;
@@ -482,3 +483,16 @@ export async function downloadFiles(files: File[]) {
         }
         }
     }
     }
 }
 }
+
+export function needsConversionForPreview(file: File) {
+    const fileExtension = splitFilenameAndExtension(file.metadata.title)[1];
+    if (
+        file.metadata.fileType === FILE_TYPE.LIVE_PHOTO ||
+        (file.metadata.fileType === FILE_TYPE.IMAGE &&
+            isFileHEIC(fileExtension))
+    ) {
+        return true;
+    } else {
+        return false;
+    }
+}