Merge pull request #434 from ente-io/fix-heic-convert-fail
Fix heic conversion failing
This commit is contained in:
commit
c580ac60c8
|
@ -28,6 +28,7 @@ module.exports = (phase) =>
|
|||
withBundleAnalyzer({
|
||||
env: {
|
||||
SENTRY_RELEASE: GIT_SHA,
|
||||
NEXT_PUBLIC_LATEST_COMMIT_HASH: GIT_SHA,
|
||||
},
|
||||
workbox: WORKBOX_CONFIG,
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
86
src/services/heicConverter/heicConverterService.ts
Normal file
86
src/services/heicConverter/heicConverterService.ts
Normal 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();
|
|
@ -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;
|
||||
|
|
|
@ -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',
|
||||
})
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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) {
|
||||
|
|
24
src/utils/heicConverter/index.ts
Normal file
24
src/utils/heicConverter/index.ts
Normal 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,
|
||||
};
|
||||
}
|
||||
};
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
29
src/utils/upload/uploadRetrier.ts
Normal file
29
src/utils/upload/uploadRetrier.ts
Normal 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);
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue