Add error handling for the web

This commit is contained in:
2025-10-19 16:47:15 +02:00
parent ccf9497313
commit c49b2f17f2
3 changed files with 33 additions and 24 deletions

View File

@@ -4,10 +4,6 @@ import type { MixAudioMode } from "./types/MixAudioMode";
import type { TextTrack } from "./types/TextTrack"; import type { TextTrack } from "./types/TextTrack";
import type { NoAutocomplete } from "./types/Utils"; import type { NoAutocomplete } from "./types/Utils";
import type { VideoConfig, VideoSource } from "./types/VideoConfig"; import type { VideoConfig, VideoSource } from "./types/VideoConfig";
import {
tryParseNativeVideoError,
VideoRuntimeError,
} from "./types/VideoError";
import type { VideoPlayerBase } from "./types/VideoPlayerBase"; import type { VideoPlayerBase } from "./types/VideoPlayerBase";
import type { VideoPlayerStatus } from "./types/VideoPlayerStatus"; import type { VideoPlayerStatus } from "./types/VideoPlayerStatus";
import { VideoPlayerEvents } from "./VideoPlayerEvents"; import { VideoPlayerEvents } from "./VideoPlayerEvents";
@@ -62,24 +58,6 @@ class VideoPlayer extends VideoPlayerEvents implements VideoPlayerBase {
return this.video; return this.video;
} }
/**
* Handles parsing native errors to VideoRuntimeError and calling onError if provided
* @internal
*/
private throwError(error: unknown) {
const parsedError = tryParseNativeVideoError(error);
if (
parsedError instanceof VideoRuntimeError &&
this.triggerEvent("onError", parsedError)
) {
// We don't throw errors if onError is provided
return;
}
throw parsedError;
}
// Source // Source
get source(): VideoPlayerSource { get source(): VideoPlayerSource {
// TODO: properly implement this // TODO: properly implement this
@@ -211,7 +189,8 @@ class VideoPlayer extends VideoPlayerEvents implements VideoPlayerBase {
} }
play(): void { play(): void {
this.player.play()?.catch(this.throwError); // error are already handled by the `onError` callback, no need to catch it here.
this.player.play()?.catch();
} }
pause(): void { pause(): void {

View File

@@ -39,6 +39,7 @@ export class MediaSessionHandler {
[ [
"seekbackward", "seekbackward",
(details: MediaSessionActionDetails) => { (details: MediaSessionActionDetails) => {
// @ts-expect-error ads is in an optional plugin that isn't typed.
if (this.player.usingPlugin("ads") && this.player.ads.inAdBreak()) { if (this.player.usingPlugin("ads") && this.player.ads.inAdBreak()) {
return; return;
} }
@@ -54,6 +55,7 @@ export class MediaSessionHandler {
[ [
"seekforward", "seekforward",
(details: MediaSessionActionDetails) => { (details: MediaSessionActionDetails) => {
// @ts-expect-error ads is in an optional plugin that isn't typed.
if (this.player.usingPlugin("ads") && this.player.ads.inAdBreak()) { if (this.player.usingPlugin("ads") && this.player.ads.inAdBreak()) {
return; return;
} }
@@ -69,6 +71,7 @@ export class MediaSessionHandler {
[ [
"seekto", "seekto",
(details: MediaSessionActionDetails) => { (details: MediaSessionActionDetails) => {
// @ts-expect-error ads is in an optional plugin that isn't typed.
if (this.player.usingPlugin("ads") && this.player.ads.inAdBreak()) { if (this.player.usingPlugin("ads") && this.player.ads.inAdBreak()) {
return; return;
} }

View File

@@ -10,7 +10,14 @@ import type {
TimedMetadata, TimedMetadata,
} from "../types/Events"; } from "../types/Events";
import type { TextTrack } from "../types/TextTrack"; import type { TextTrack } from "../types/TextTrack";
import type { VideoRuntimeError } from "../types/VideoError"; import {
type LibraryError,
type PlayerError,
type SourceError,
type UnknownError,
VideoError,
type VideoRuntimeError,
} from "../types/VideoError";
import type { VideoPlayerStatus } from "../types/VideoPlayerStatus"; import type { VideoPlayerStatus } from "../types/VideoPlayerStatus";
type VideoJsPlayer = ReturnType<typeof videojs>; type VideoJsPlayer = ReturnType<typeof videojs>;
@@ -187,6 +194,26 @@ export class WebEventEmiter implements PlayerEvents {
_onError() { _onError() {
this.onStatusChange("error"); this.onStatusChange("error");
const err = this.player.error();
if (!err) {
console.error("Unknown error occured in player");
return;
}
const codeMap = {
// @ts-expect-error Code added to html5 MediaError by videojs
[MediaError.MEDIA_ERR_CUSTOM]: "unknown/unknown",
[MediaError.MEDIA_ERR_ABORTED]: "player/asset-not-initialized",
[MediaError.MEDIA_ERR_NETWORK]: "player/network",
[MediaError.MEDIA_ERR_DECODE]: "player/invalid-source",
[MediaError.MEDIA_ERR_SRC_NOT_SUPPORTED]:
"source/unsupported-content-type",
// @ts-expect-error Code added to html5 MediaError by videojs
[MediaError.MEDIA_ERR_ENCRYPTED]: "source/failed-to-initialize-asset",
} as Record<
number,
LibraryError | PlayerError | SourceError | UnknownError
>;
this.onError(new VideoError(codeMap[err.code]!, err.message));
} }
NOOP = () => {}; NOOP = () => {};