Skip to content

Commit 1331d69

Browse files
authored
fix(npm/player): improve component lifecycle and null safety (#1606)
- Add null safety checks in ShadowPlayer to prevent accessing undefined video element before component initialization - Add pause() and dispose() methods for proper cleanup - Add disconnectedCallback() for automatic cleanup
1 parent 488942f commit 1331d69

2 files changed

Lines changed: 44 additions & 7 deletions

File tree

webapp/packages/multi-video-player/src/video-player/player.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,11 +113,41 @@ export class MultiVideoPlayer extends HTMLElement {
113113
}
114114
}
115115

116+
pause() {
117+
if (this.player) {
118+
try {
119+
this.player.pause();
120+
} catch (error) {
121+
// ignore pause errors
122+
}
123+
}
124+
}
125+
116126
private progressControl(): MultiVideoProgressControl | null {
117127
return this.player
118128
?.getChild('controlBar')
119129
?.getChild('MultiVideoProgressControl') as MultiVideoProgressControl | null;
120130
}
131+
132+
async dispose() {
133+
if (this.player) {
134+
try {
135+
// pause the player first to avoid AbortError
136+
this.player.pause();
137+
await new Promise(resolve => setTimeout(resolve, 0));
138+
this.player.dispose();
139+
} catch (error) {
140+
// ignore errors when disposing
141+
console.debug('Error during player disposal:', error);
142+
}
143+
this.player = null;
144+
}
145+
this.videoElement = null;
146+
}
147+
148+
disconnectedCallback() {
149+
this.dispose();
150+
}
121151
}
122152

123153
customElements.define('multi-video-player', MultiVideoPlayer);

webapp/packages/shadow-player/src/streamer.ts

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,9 @@ export class ShadowPlayer extends HTMLElement {
4949
}
5050

5151
onEnd(callback: () => void) {
52-
this.videoElement.controls = true;
52+
if (this._videoElement) {
53+
this._videoElement.controls = true;
54+
}
5355
this.onEndCallback = callback;
5456
}
5557

@@ -59,8 +61,8 @@ export class ShadowPlayer extends HTMLElement {
5961
return;
6062
}
6163

62-
if (Object.prototype.hasOwnProperty.call(this.videoElement, name)) {
63-
this.videoElement.setAttribute(name, newValue !== null ? newValue : '');
64+
if (this._videoElement && Object.prototype.hasOwnProperty.call(this._videoElement, name)) {
65+
this._videoElement.setAttribute(name, newValue !== null ? newValue : '');
6466
}
6567
}
6668

@@ -104,8 +106,8 @@ export class ShadowPlayer extends HTMLElement {
104106
if (attr === 'src' && value !== null) {
105107
this.srcChange(value);
106108
}
107-
if (value !== null) {
108-
this.videoElement.setAttribute(attr, value);
109+
if (value !== null && this._videoElement) {
110+
this._videoElement.setAttribute(attr, value);
109111
}
110112
}
111113
}
@@ -119,7 +121,9 @@ export class ShadowPlayer extends HTMLElement {
119121
}
120122

121123
public play() {
122-
this.videoElement.play();
124+
if (this._videoElement) {
125+
this._videoElement.play();
126+
}
123127
}
124128

125129
private replay() {
@@ -130,10 +134,13 @@ export class ShadowPlayer extends HTMLElement {
130134
}
131135

132136
public srcChange(value: string) {
137+
if (!this._videoElement) {
138+
return;
139+
}
133140
this.isDisconnecting = false;
134141
const mediaSource = new MediaSource();
135142
this._src = value;
136-
this.videoElement.src = URL.createObjectURL(mediaSource);
143+
this._videoElement.src = URL.createObjectURL(mediaSource);
137144
mediaSource.addEventListener('sourceopen', () => {
138145
this.handleSourceOpen(mediaSource);
139146
});

0 commit comments

Comments
 (0)