commit
8d1be4682c
|
@ -301,6 +301,7 @@ const PhotoFrame = ({
|
|||
|
||||
const getSlideData = async (instance: any, index: number, item: File) => {
|
||||
if (!item.msrc) {
|
||||
try {
|
||||
let url: string;
|
||||
if (galleryContext.thumbs.has(item.id)) {
|
||||
url = galleryContext.thumbs.get(item.id);
|
||||
|
@ -321,8 +322,12 @@ const PhotoFrame = ({
|
|||
} catch (e) {
|
||||
// ignore
|
||||
}
|
||||
} catch (e) {
|
||||
// no-op
|
||||
}
|
||||
}
|
||||
if (!fetching[item.dataIndex]) {
|
||||
try {
|
||||
fetching[item.dataIndex] = true;
|
||||
let url: string;
|
||||
if (galleryContext.files.has(item.id)) {
|
||||
|
@ -342,6 +347,11 @@ const PhotoFrame = ({
|
|||
} catch (e) {
|
||||
// ignore
|
||||
}
|
||||
} catch (e) {
|
||||
// no-op
|
||||
} finally {
|
||||
fetching[item.dataIndex] = false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -128,6 +128,7 @@ export default function PreviewCard(props: IProps) {
|
|||
useLayoutEffect(() => {
|
||||
if (file && !file.msrc) {
|
||||
const main = async () => {
|
||||
try {
|
||||
const url = await DownloadManager.getPreview(file);
|
||||
if (isMounted.current) {
|
||||
setImgSrc(url);
|
||||
|
@ -138,6 +139,9 @@ export default function PreviewCard(props: IProps) {
|
|||
}
|
||||
updateUrl(url);
|
||||
}
|
||||
} catch (e) {
|
||||
// no-op
|
||||
}
|
||||
};
|
||||
|
||||
if (thumbs.has(file.id)) {
|
||||
|
|
|
@ -24,7 +24,7 @@ class DownloadManager {
|
|||
return URL.createObjectURL(await cacheResp.blob());
|
||||
}
|
||||
if (!this.thumbnailObjectUrlPromise.get(file.id)) {
|
||||
const downloadPromise = this._downloadThumb(
|
||||
const downloadPromise = this.downloadThumb(
|
||||
token,
|
||||
thumbnailCache,
|
||||
file
|
||||
|
@ -35,10 +35,11 @@ class DownloadManager {
|
|||
} catch (e) {
|
||||
this.thumbnailObjectUrlPromise.delete(file.id);
|
||||
logError(e, 'get preview Failed');
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
_downloadThumb = async (
|
||||
private downloadThumb = async (
|
||||
token: string,
|
||||
thumbnailCache: Cache,
|
||||
file: File
|
||||
|
@ -67,26 +68,29 @@ class DownloadManager {
|
|||
};
|
||||
|
||||
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}`;
|
||||
}
|
||||
try {
|
||||
const getFilePromise = (async () => {
|
||||
const getFilePromise = async () => {
|
||||
const fileStream = await this.downloadFile(file);
|
||||
let fileBlob = await new Response(fileStream).blob();
|
||||
if (forPreview) {
|
||||
fileBlob = await convertForPreview(file, fileBlob);
|
||||
}
|
||||
return URL.createObjectURL(fileBlob);
|
||||
})();
|
||||
if (!this.fileObjectUrlPromise.get(`${file.id}_${forPreview}`)) {
|
||||
this.fileObjectUrlPromise.set(
|
||||
`${file.id}_${forPreview}`,
|
||||
getFilePromise
|
||||
);
|
||||
};
|
||||
if (!this.fileObjectUrlPromise.get(fileUID)) {
|
||||
this.fileObjectUrlPromise.set(fileUID, getFilePromise());
|
||||
}
|
||||
return await this.fileObjectUrlPromise.get(
|
||||
`${file.id}_${forPreview}`
|
||||
);
|
||||
return await this.fileObjectUrlPromise.get(fileUID);
|
||||
} catch (e) {
|
||||
this.fileObjectUrlPromise.delete(fileUID);
|
||||
logError(e, 'Failed to get File');
|
||||
throw e;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -5,8 +5,10 @@ import { BLACK_THUMBNAIL_BASE64 } from '../../../public/images/black-thumbnail-b
|
|||
import FFmpegService from 'services/ffmpegService';
|
||||
|
||||
const THUMBNAIL_HEIGHT = 720;
|
||||
const MAX_ATTEMPTS = 3;
|
||||
const MIN_THUMBNAIL_SIZE = 50000;
|
||||
const MIN_COMPRESSION_PERCENTAGE_SIZE_DIFF = 10;
|
||||
export const MAX_THUMBNAIL_SIZE = 100 * 1024;
|
||||
const MIN_QUALITY = 0.5;
|
||||
const MAX_QUALITY = 0.7;
|
||||
|
||||
const WAIT_TIME_THUMBNAIL_GENERATION = 10 * 1000;
|
||||
|
||||
|
@ -171,11 +173,14 @@ export async function generateVideoThumbnail(file: globalThis.File) {
|
|||
}
|
||||
|
||||
async function thumbnailCanvasToBlob(canvas: HTMLCanvasElement) {
|
||||
let thumbnailBlob = null;
|
||||
let attempts = 0;
|
||||
let quality = 1;
|
||||
let thumbnailBlob: Blob = null;
|
||||
let prevSize = Number.MAX_SAFE_INTEGER;
|
||||
let quality = MAX_QUALITY;
|
||||
|
||||
do {
|
||||
if (thumbnailBlob) {
|
||||
prevSize = thumbnailBlob.size;
|
||||
}
|
||||
thumbnailBlob = await new Promise((resolve) => {
|
||||
canvas.toBlob(
|
||||
function (blob) {
|
||||
|
@ -186,12 +191,26 @@ async function thumbnailCanvasToBlob(canvas: HTMLCanvasElement) {
|
|||
);
|
||||
});
|
||||
thumbnailBlob = thumbnailBlob ?? new Blob([]);
|
||||
attempts++;
|
||||
quality /= 2;
|
||||
quality -= 0.1;
|
||||
} while (
|
||||
thumbnailBlob.size > MIN_THUMBNAIL_SIZE &&
|
||||
attempts <= MAX_ATTEMPTS
|
||||
quality >= MIN_QUALITY &&
|
||||
thumbnailBlob.size > MAX_THUMBNAIL_SIZE &&
|
||||
percentageSizeDiff(thumbnailBlob.size, prevSize) >=
|
||||
MIN_COMPRESSION_PERCENTAGE_SIZE_DIFF
|
||||
);
|
||||
if (thumbnailBlob.size > MAX_THUMBNAIL_SIZE) {
|
||||
logError(
|
||||
Error('thumbnail_too_large'),
|
||||
'thumbnail greater than max limit'
|
||||
);
|
||||
}
|
||||
|
||||
return thumbnailBlob;
|
||||
}
|
||||
|
||||
function percentageSizeDiff(
|
||||
newThumbnailSize: number,
|
||||
oldThumbnailSize: number
|
||||
) {
|
||||
return ((oldThumbnailSize - newThumbnailSize) * 100) / oldThumbnailSize;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue