Merge pull request #434 from ente-io/fix-heic-convert-fail

Fix heic conversion failing
This commit is contained in:
Abhinav Kumar 2022-03-25 22:16:06 +05:30 committed by GitHub
commit c580ac60c8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 175 additions and 73 deletions

View file

@ -28,6 +28,7 @@ module.exports = (phase) =>
withBundleAnalyzer({
env: {
SENTRY_RELEASE: GIT_SHA,
NEXT_PUBLIC_LATEST_COMMIT_HASH: GIT_SHA,
},
workbox: WORKBOX_CONFIG,

View file

@ -36,7 +36,7 @@ import { ARCHIVE_SECTION, TRASH_SECTION } from 'constants/collection';
import FixLargeThumbnails from './FixLargeThumbnail';
import { SetLoading } from 'types/gallery';
import { downloadAsFile } from 'utils/file';
import { getUploadLogs } from 'utils/upload';
import { getUploadLogs, logUploadInfo } from 'utils/upload';
import styled from 'styled-components';
interface Props {
collections: Collection[];
@ -113,6 +113,7 @@ export default function Sidebar(props: Props) {
}
const downloadUploadLogs = () => {
logUploadInfo('exporting logs');
const logs = getUploadLogs();
const logString = logs.join('\n');
downloadAsFile(`upload_logs_${Date.now()}.txt`, logString);

View file

@ -16,6 +16,7 @@ import FlashMessageBar from 'components/FlashMessageBar';
import Head from 'next/head';
import { getAlbumSiteHost } from 'constants/pages';
import GoToEnte from 'components/pages/sharedAlbum/GoToEnte';
import { logUploadInfo } from 'utils/upload';
const GlobalStyles = createGlobalStyle`
/* ubuntu-regular - latin */
@ -657,6 +658,10 @@ export default function App({ Component, err }) {
}, [redirectName]);
useEffect(() => {
logUploadInfo(`app started`);
logUploadInfo(
`latest commit id :${process.env.NEXT_PUBLIC_LATEST_COMMIT_HASH}`
);
const currentURL = new URL(window.location.href);
if (currentURL.host === getAlbumSiteHost()) {
setIsAlbumsDomain(true);

View file

@ -1,31 +0,0 @@
import QueueProcessor from 'services/queueProcessor';
import { ConvertWorker } from 'utils/comlink';
import { CustomError } from 'utils/error';
import { logError } from 'utils/sentry';
class HEICConverter {
private convertProcessor = new QueueProcessor<Blob>(5);
private worker = null;
async convert(fileBlob: Blob, format = 'JPEG'): Promise<Blob> {
if (!this.worker) {
this.worker = await new ConvertWorker();
}
const response = this.convertProcessor.queueUpRequest(
async () => await this.worker.convertHEIC(fileBlob, format)
);
try {
return await response.promise;
} catch (e) {
if (e.message === CustomError.REQUEST_CANCELLED) {
// ignore
return null;
} else {
logError(e, 'heic conversion failed');
throw e;
}
}
}
}
export default new HEICConverter();

View file

@ -0,0 +1,86 @@
import QueueProcessor from 'services/queueProcessor';
import { CustomError } from 'utils/error';
import { createNewConvertWorker } from 'utils/heicConverter';
import { retryAsyncFunction } from 'utils/network';
import { logError } from 'utils/sentry';
import { logUploadInfo } from 'utils/upload';
const WORKER_POOL_SIZE = 2;
const MAX_CONVERSION_IN_PARALLEL = 1;
const WAIT_TIME_BEFORE_NEXT_ATTEMPT_IN_MICROSECONDS = [100, 100];
const WAIT_TIME_IN_MICROSECONDS = 10 * 1000;
const BREATH_TIME_IN_MICROSECONDS = 1000;
class HEICConverter {
private convertProcessor = new QueueProcessor<Blob>(
MAX_CONVERSION_IN_PARALLEL
);
private workerPool: { comlink: any; worker: Worker }[];
private ready: Promise<void>;
constructor() {
this.ready = this.init();
}
async init() {
this.workerPool = [];
for (let i = 0; i < WORKER_POOL_SIZE; i++) {
this.workerPool.push(await createNewConvertWorker());
}
}
async convert(fileBlob: Blob, format = 'JPEG'): Promise<Blob> {
await this.ready;
const response = this.convertProcessor.queueUpRequest(() =>
retryAsyncFunction<Blob>(async () => {
const { comlink, worker } = this.workerPool.shift();
try {
const convertedHEIC = await new Promise<Blob>(
(resolve, reject) => {
const main = async () => {
try {
const timeout = setTimeout(() => {
reject(Error('wait time exceeded'));
}, WAIT_TIME_IN_MICROSECONDS);
const convertedHEIC =
await comlink.convertHEIC(
fileBlob,
format
);
clearTimeout(timeout);
resolve(convertedHEIC);
} catch (e) {
reject(e);
}
};
main();
}
);
await new Promise((resolve) => {
setTimeout(
() => resolve(null),
BREATH_TIME_IN_MICROSECONDS
);
});
this.workerPool.push({ comlink, worker });
return convertedHEIC;
} catch (e) {
logUploadInfo('heic conversion failed-' + e.message);
logError(e, 'heic conversion failed');
worker.terminate();
this.workerPool.push(await createNewConvertWorker());
throw e;
}
}, WAIT_TIME_BEFORE_NEXT_ATTEMPT_IN_MICROSECONDS)
);
try {
return await response.promise;
} catch (e) {
if (e.message === CustomError.REQUEST_CANCELLED) {
// ignore
return null;
}
throw e;
}
}
}
export default new HEICConverter();

View file

@ -7,7 +7,7 @@ import { convertToHumanReadable } from 'utils/billing';
import { isFileHEIC } from 'utils/file';
import { FileTypeInfo } from 'types/upload';
import { getUint8ArrayView } from '../readerService';
import HEICConverter from 'services/HEICConverter';
import HEICConverter from 'services/heicConverter/heicConverterService';
import { getFileNameSize, logUploadInfo } from 'utils/upload';
const MAX_THUMBNAIL_DIMENSION = 720;

View file

@ -4,8 +4,8 @@ import { getToken } from 'utils/common/key';
import { logError } from 'utils/sentry';
import { EnteFile } from 'types/file';
import { CustomError, handleUploadError } from 'utils/error';
import { retryAsyncFunction } from 'utils/network';
import { UploadFile, UploadURL, MultipartUploadURLs } from 'types/upload';
import { retryHTTPCall } from 'utils/upload/uploadRetrier';
const ENDPOINT = getEndpoint();
const MAX_URL_REQUESTS = 50;
@ -19,7 +19,7 @@ class UploadHttpClient {
if (!token) {
return;
}
const response = await retryAsyncFunction(
const response = await retryHTTPCall(
() =>
HTTPService.post(`${ENDPOINT}/files`, uploadFile, null, {
'X-Auth-Token': token,
@ -90,7 +90,7 @@ class UploadHttpClient {
progressTracker
): Promise<string> {
try {
await retryAsyncFunction(() =>
await retryHTTPCall(() =>
HTTPService.put(
fileUploadURL.url,
file,
@ -112,7 +112,7 @@ class UploadHttpClient {
progressTracker
) {
try {
const response = await retryAsyncFunction(async () => {
const response = await retryHTTPCall(async () => {
const resp = await HTTPService.put(
partUploadURL,
filePart,
@ -136,7 +136,7 @@ class UploadHttpClient {
async completeMultipartUpload(completeURL: string, reqBody: any) {
try {
await retryAsyncFunction(() =>
await retryHTTPCall(() =>
HTTPService.post(completeURL, reqBody, null, {
'content-type': 'text/xml',
})

View file

@ -6,17 +6,6 @@ export interface ComlinkWorker {
worker: Worker;
}
export const getDedicatedConvertWorker = (): ComlinkWorker => {
if (runningInBrowser()) {
const worker = new Worker(
new URL('worker/convert.worker.js', import.meta.url),
{ name: 'ente-convert-worker' }
);
const comlink = Comlink.wrap(worker);
return { comlink, worker };
}
};
const getDedicatedFFmpegWorker = (): ComlinkWorker => {
if (runningInBrowser()) {
const worker = new Worker(
@ -27,6 +16,5 @@ const getDedicatedFFmpegWorker = (): ComlinkWorker => {
return { comlink, worker };
}
};
export const ConvertWorker: any = getDedicatedConvertWorker()?.comlink;
export const FFmpegWorker: any = getDedicatedFFmpegWorker()?.comlink;

View file

@ -23,7 +23,7 @@ import {
VISIBILITY_STATE,
} from 'constants/file';
import PublicCollectionDownloadManager from 'services/publicCollectionDownloadManager';
import HEICConverter from 'services/HEICConverter';
import HEICConverter from 'services/heicConverter/heicConverterService';
import ffmpegService from 'services/ffmpeg/ffmpegService';
export function downloadAsFile(filename: string, content: string) {

View file

@ -0,0 +1,24 @@
import { ComlinkWorker } from 'utils/comlink';
import { runningInBrowser } from 'utils/common';
import * as Comlink from 'comlink';
const getDedicatedConvertWorker = (): ComlinkWorker => {
if (runningInBrowser()) {
const worker = new Worker(
new URL('worker/convert.worker.js', import.meta.url),
{ name: 'ente-convert-worker' }
);
const comlink = Comlink.wrap(worker);
return { comlink, worker };
}
};
export const createNewConvertWorker = async () => {
const comlinkWrapperWorker = getDedicatedConvertWorker();
if (comlinkWrapperWorker) {
return {
comlink: await new comlinkWrapperWorker.comlink(),
worker: comlinkWrapperWorker.worker,
};
}
};

View file

@ -1,29 +1,28 @@
import { sleep } from 'utils/common';
const retrySleepTimeInMilliSeconds = [2000, 5000, 10000];
const waitTimeBeforeNextAttemptInMilliSeconds = [2000, 5000, 10000];
export async function retryAsyncFunction(
func: () => Promise<any>,
checkForBreakingError?: (error) => void
) {
const retrier = async (
func: () => Promise<any>,
attemptNumber: number = 0
) => {
export async function retryAsyncFunction<T>(
request: (abort?: () => void) => Promise<T>,
waitTimeBeforeNextTry?: number[]
): Promise<T> {
if (!waitTimeBeforeNextTry) {
waitTimeBeforeNextTry = waitTimeBeforeNextAttemptInMilliSeconds;
}
for (
let attemptNumber = 0;
attemptNumber <= waitTimeBeforeNextTry.length;
attemptNumber++
) {
try {
const resp = await func();
const resp = await request();
return resp;
} catch (e) {
if (checkForBreakingError) {
checkForBreakingError(e);
}
if (attemptNumber < retrySleepTimeInMilliSeconds.length) {
await sleep(retrySleepTimeInMilliSeconds[attemptNumber]);
return await retrier(func, attemptNumber + 1);
} else {
if (attemptNumber === waitTimeBeforeNextTry.length) {
throw e;
}
await sleep(waitTimeBeforeNextTry[attemptNumber]);
}
};
return await retrier(func);
}
}

View file

@ -0,0 +1,29 @@
import { sleep } from 'utils/common';
const retrySleepTimeInMilliSeconds = [2000, 5000, 10000];
export async function retryHTTPCall(
func: () => Promise<any>,
checkForBreakingError?: (error) => void
): Promise<any> {
const retrier = async (
func: () => Promise<any>,
attemptNumber: number = 0
) => {
try {
const resp = await func();
return resp;
} catch (e) {
if (checkForBreakingError) {
checkForBreakingError(e);
}
if (attemptNumber < retrySleepTimeInMilliSeconds.length) {
await sleep(retrySleepTimeInMilliSeconds[attemptNumber]);
return await retrier(func, attemptNumber + 1);
} else {
throw e;
}
}
};
return await retrier(func);
}

View file

@ -1,9 +1,9 @@
import * as Comlink from 'comlink';
import { convertHEIC } from 'utils/file/convertHEIC';
import { convertHEIC } from 'services/heicConverter/heicConverterClient';
export class Convert {
async convertHEIC(format, file) {
return convertHEIC(format, file);
async convertHEIC(fileBlob, format) {
return convertHEIC(fileBlob, format);
}
}