Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: better error handling #301

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions packages/2d/src/lib/components/Audio.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@ export class Audio extends Media {
}

DependencyContext.collectPromise(
new Promise<void>(resolve => {
this.waitForCanPlay(audio, resolve);
new Promise<void>((resolve, reject) => {
this.waitForCanPlay(audio, resolve, reject);
}),
);

Expand Down
16 changes: 3 additions & 13 deletions packages/2d/src/lib/components/Img.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import {
BBox,
Color,
DependencyContext,
DetailedError,
PossibleVector2,
SerializedVector2,
SignalValue,
Expand Down Expand Up @@ -159,18 +158,9 @@ export class Img extends Asset {
image.addEventListener('load', resolve);
image.addEventListener('error', () =>
reject(
new DetailedError({
message: `Failed to load an image`,
remarks: `\
The <code>src</code> property was set to:
<pre><code>${rawSrc}</code></pre>
...which resolved to the following url:
<pre><code>${src}</code></pre>
Make sure that source is correct and that the image exists.<br/>
<a target='_blank' href='https://motioncanvas.io/docs/media#images'>Learn more</a>
about working with images.`,
inspect: this.key,
}),
new Error(
`Failed to load the image: ${src} for node ${this.key}. If you are sure that the url is correct, this might be a CORS error.`,
),
),
);
}),
Expand Down
29 changes: 20 additions & 9 deletions packages/2d/src/lib/components/Media.ts
Original file line number Diff line number Diff line change
Expand Up @@ -218,12 +218,18 @@ export abstract class Media extends Asset {
}

protected scheduleSeek(time: number) {
this.waitForCanPlay(this.mediaElement(), () => {
const media = this.mediaElement();
// Wait until the media is ready to seek again as
// setting the time before the video doesn't work reliably.
media.currentTime = time;
});
this.waitForCanPlay(
this.mediaElement(),
() => {
const media = this.mediaElement();
// Wait until the media is ready to seek again as
// setting the time before the video doesn't work reliably.
media.currentTime = time;
},
(error: string) => {
throw new Error(error);
},
);
}

/**
Expand All @@ -233,7 +239,11 @@ export abstract class Media extends Asset {
* @param onCanPlay - The function to call when the media is ready to play.
* @returns
*/
protected waitForCanPlay(media: HTMLMediaElement, onCanPlay: () => void) {
protected waitForCanPlay(
media: HTMLMediaElement,
onCanPlay: () => void,
onErrorMessage: (message: string) => void,
) {
if (media.readyState >= 2) {
onCanPlay();
return;
Expand All @@ -246,8 +256,9 @@ export abstract class Media extends Asset {

const onError = () => {
const reason = this.getErrorReason(media.error?.code);
console.log(`ERROR: Error loading video: ${this.src()}, ${reason}`);
media.removeEventListener('error', onError);
onErrorMessage(
`Failed to load media: ${this.src()} for node ${this.key}: ${reason}. If you are sure that the url is correct, this might be a CORS error.`,
);
};

media.addEventListener('canplay', onCanPlayWrapper);
Expand Down
4 changes: 2 additions & 2 deletions packages/2d/src/lib/components/Video.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,8 +136,8 @@ export class Video extends Media {
}

DependencyContext.collectPromise(
new Promise<void>(resolve => {
this.waitForCanPlay(video, resolve);
new Promise<void>((resolve, reject) => {
this.waitForCanPlay(video, resolve, reject);
}),
);

Expand Down
9 changes: 6 additions & 3 deletions packages/2d/src/lib/utils/video/ffmpeg-client.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import {verifyFetchResponse} from '@revideo/core';

export class ImageCommunication {
public constructor() {
if (!import.meta.hot) {
Expand Down Expand Up @@ -27,9 +29,10 @@ export class ImageCommunication {
}),
});

if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
await verifyFetchResponse(
response,
"Didn't get frame from /revideo-ffmpeg-decoder/video-frame",
);

const width = parseInt(response.headers.get('X-Frame-Width') || '1080', 10);
const height = parseInt(
Expand Down
2 changes: 2 additions & 0 deletions packages/core/src/app/Renderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,8 @@ export class Renderer {
this.finished.dispatch(result);
this.sharedWebGLContext.dispose();
this.lock.release();

return result;
}

/**
Expand Down
13 changes: 10 additions & 3 deletions packages/core/src/exporter/FFmpegExporter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {EventDispatcher} from '../events';
import {BoolMetaField, MetaField, ObjectMetaField, ValueOf} from '../meta';
import {Exporter} from './Exporter';
import {download} from './download-videos';
import {verifyFetchResponse} from './utils';

type ServerResponse =
| {
Expand Down Expand Up @@ -113,14 +114,16 @@ export class FFmpegExporterClient implements Exporter {

public async stop(result: RendererResult): Promise<void> {
await this.invoke('end', result);
await fetch('/revideo-ffmpeg-decoder/finished', {
const response = await fetch('/revideo-ffmpeg-decoder/finished', {
method: 'POST',
headers: {
// eslint-disable-next-line
'Content-Type': 'application/json',
},
body: JSON.stringify({}),
});

await verifyFetchResponse(response, '/revideo-ffmpeg-decoder/finished');
}

public async kill(): Promise<void> {
Expand All @@ -136,7 +139,7 @@ export class FFmpegExporterClient implements Exporter {
startFrame: number,
endFrame: number,
): Promise<void> {
await fetch('/audio-processing/generate-audio', {
const response = await fetch('/audio-processing/generate-audio', {
method: 'POST',
body: JSON.stringify({
tempDir: `revideo-${this.settings.name}-${this.settings.hiddenFolderId}`,
Expand All @@ -146,19 +149,23 @@ export class FFmpegExporterClient implements Exporter {
fps: this.settings.fps,
}),
});

await verifyFetchResponse(response, '/audio-processing/generate-audio');
}

public async mergeMedia(): Promise<void> {
const outputFilename = this.settings.name;
const tempDir = `revideo-${this.settings.name}-${this.settings.hiddenFolderId}`;

await fetch('/audio-processing/merge-media', {
const response = await fetch('/audio-processing/merge-media', {
method: 'POST',
body: JSON.stringify({
outputFilename,
tempDir,
}),
});

await verifyFetchResponse(response, '/audio-processing/merge-media');
}

/**
Expand Down
23 changes: 19 additions & 4 deletions packages/core/src/exporter/WasmExporter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import type {AssetInfo, RendererSettings} from '../app/Renderer';
import {MetaField, ObjectMetaField} from '../meta';
import {Exporter} from './Exporter';
import {download} from './download-videos';
import {verifyFetchResponse} from './utils';

export class WasmExporter implements Exporter {
public static readonly id = '@revideo/core/wasm';
Expand Down Expand Up @@ -42,6 +43,7 @@ export class WasmExporter implements Exporter {
public async handleFrame(canvas: HTMLCanvasElement): Promise<void> {
const frame = new VideoFrame(canvas, {timestamp: 0});
await this.encoder.addFrame(frame);

frame.close();
}

Expand All @@ -55,7 +57,7 @@ export class WasmExporter implements Exporter {
`revideo-${this.settings.name}-${this.settings.hiddenFolderId}`,
);

await fetch('/revideo-ffmpeg-decoder/finished', {
const finishResponse = await fetch('/revideo-ffmpeg-decoder/finished', {
method: 'POST',
headers: {
// eslint-disable-next-line
Expand All @@ -64,18 +66,25 @@ export class WasmExporter implements Exporter {
body: JSON.stringify({}),
});

await fetch('/uploadVideoFile', {
await verifyFetchResponse(
finishResponse,
'/revideo-ffmpeg-decoder/finished',
);

const uploadResponse = await fetch('/uploadVideoFile', {
method: 'POST',
body: formData,
});

await verifyFetchResponse(uploadResponse, '/uploadVideoFile');
}

public async generateAudio(
assets: AssetInfo[][],
startFrame: number,
endFrame: number,
): Promise<void> {
await fetch('/audio-processing/generate-audio', {
const response = await fetch('/audio-processing/generate-audio', {
method: 'POST',
body: JSON.stringify({
tempDir: `revideo-${this.settings.name}-${this.settings.hiddenFolderId}`,
Expand All @@ -85,19 +94,25 @@ export class WasmExporter implements Exporter {
fps: this.settings.fps,
}),
});

console.log('generate audio response aahhh OK????', response.ok);

await verifyFetchResponse(response, '/audio-processing/generate-audio');
}

public async mergeMedia(): Promise<void> {
const outputFilename = this.settings.name;
const tempDir = `revideo-${this.settings.name}-${this.settings.hiddenFolderId}`;

await fetch('/audio-processing/merge-media', {
const response = await fetch('/audio-processing/merge-media', {
method: 'POST',
body: JSON.stringify({
outputFilename,
tempDir,
}),
});

await verifyFetchResponse(response, '/audio-processing/merge-media');
}

public async downloadVideos(assets: AssetInfo[][]): Promise<void> {
Expand Down
8 changes: 5 additions & 3 deletions packages/core/src/exporter/download-videos.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {AssetInfo} from '../app';
import {verifyFetchResponse} from './utils';

export async function download(assets: AssetInfo[][]): Promise<void> {
const videoRanges: Map<string, {start: number; end: number}> = new Map();
Expand Down Expand Up @@ -47,9 +48,10 @@ export async function download(assets: AssetInfo[][]): Promise<void> {
},
);

if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
await verifyFetchResponse(
response,
'/revideo-ffmpeg-decoder/download-video-chunks',
);

const result = await response.json();

Expand Down
1 change: 1 addition & 0 deletions packages/core/src/exporter/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ export * from './Exporter';
export * from './FFmpegExporter';
export * from './ImageExporter';
export * from './WasmExporter';
export * from './utils';
9 changes: 9 additions & 0 deletions packages/core/src/exporter/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export async function verifyFetchResponse(response: Response, remarks: string) {
if (response.ok) {
return;
}

throw new Error(
`ERROR: ${remarks}. response returned status code ${response.status}.`,
);
}
13 changes: 9 additions & 4 deletions packages/renderer/client/render.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {Project, Renderer, Vector2} from '@revideo/core';
import {Project, Renderer, RendererResult, Vector2} from '@revideo/core';

declare global {
interface Window {
Expand Down Expand Up @@ -54,10 +54,15 @@ export const render = async (
renderSettings.size = new Vector2({x: videoWidth, y: videoHeight});
}

await renderer.render(renderSettings);
window.onRenderComplete();
const result = await renderer.render(renderSettings);
if (result === RendererResult.Success) {
window.onRenderComplete();
} else {
await new Promise(resolve => setTimeout(resolve, 3000)); // wait for two seconds for error to be handled
window.onRenderFailed('Render process failed');
}
} catch (e: any) {
window.onRenderFailed(e.message);
window.onRenderFailed(e.message ?? e);
}
};

Expand Down
4 changes: 0 additions & 4 deletions packages/renderer/server/render-video.ts
Original file line number Diff line number Diff line change
Expand Up @@ -207,10 +207,6 @@ async function renderVideoOnPage(
clearInterval(interval);
reject(new Error(errorMessage));
});

page.exposeFunction('browserError', (message: string) => {
reject(new Error(message));
});
});

await page.goto(url);
Expand Down
Loading