From 7832c3d79e7ed9773c711343c8374f6afd78800f Mon Sep 17 00:00:00 2001 From: Matti LeBlanc Date: Tue, 24 Jun 2025 12:18:08 +1000 Subject: [PATCH 1/5] Fix null issue in url youtube url when running a playlist with no videoId When playing a playlist via playerVars.list = [playlistId] we dont have a videoId available yet. Plus we don't need one, Youtube will start at the first video of the playlist by default. By injecting an undefined videoId in the YT Player constructur, we end up with null value in the iframe url (`ps://www.youtube.com/embed/null?cc_load_policy=1&enablejsapi=1&controls=1&showinfo=0&rel=0&listType=playlist&list=PLxf07yK_HAvdwhW-L0X3uP5aMe6eEt50v`) which is causing an Invalid Video Id error for consumers. --- src/youtube-player/youtube-player.ts | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/src/youtube-player/youtube-player.ts b/src/youtube-player/youtube-player.ts index c5008ceb3ae6..0b2adef41f64 100644 --- a/src/youtube-player/youtube-player.ts +++ b/src/youtube-player/youtube-player.ts @@ -587,17 +587,21 @@ export class YouTubePlayer implements AfterViewInit, OnChanges, OnDestroy { // Important! We need to create the Player object outside of the `NgZone`, because it kicks // off a 250ms setInterval which will continually trigger change detection if we don't. + const params: any = { + host: this.disableCookies ? 'https://www.youtube-nocookie.com' : undefined, + width: this.width, + height: this.height, + // Calling `playVideo` on load doesn't appear to actually play + // the video so we need to trigger it through `playerVars` instead. + playerVars: playVideo ? {...(this.playerVars || {}), autoplay: 1} : this.playerVars, + } + // We only want to injecct a videoId if one is provided, otherwise loading a playlist via playerVars.list, the missing videoId will create a null value in the youtube iframe url and that can trigger a JS error `Invalid video id` in widget api. + if (this.videoId) { + params.videoId = this.videoId; + } const player = this._ngZone.runOutsideAngular( () => - new YT.Player(this.youtubeContainer.nativeElement, { - videoId: this.videoId, - host: this.disableCookies ? 'https://www.youtube-nocookie.com' : undefined, - width: this.width, - height: this.height, - // Calling `playVideo` on load doesn't appear to actually play - // the video so we need to trigger it through `playerVars` instead. - playerVars: playVideo ? {...(this.playerVars || {}), autoplay: 1} : this.playerVars, - }), + new YT.Player(this.youtubeContainer.nativeElement, params), ); const whenReady = (event: YT.PlayerEvent) => { From 7c849509e91e95046f1ca7ecc92fee9e00aa72fc Mon Sep 17 00:00:00 2001 From: Matti LeBlanc Date: Tue, 24 Jun 2025 16:00:25 +1000 Subject: [PATCH 2/5] Update youtube-player.ts apply YT.PlayerOptions to params --- src/youtube-player/youtube-player.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/youtube-player/youtube-player.ts b/src/youtube-player/youtube-player.ts index 0b2adef41f64..c487262c691f 100644 --- a/src/youtube-player/youtube-player.ts +++ b/src/youtube-player/youtube-player.ts @@ -587,7 +587,7 @@ export class YouTubePlayer implements AfterViewInit, OnChanges, OnDestroy { // Important! We need to create the Player object outside of the `NgZone`, because it kicks // off a 250ms setInterval which will continually trigger change detection if we don't. - const params: any = { + const params: YT.PlayerOptions = { host: this.disableCookies ? 'https://www.youtube-nocookie.com' : undefined, width: this.width, height: this.height, From 25855b16d5096d176a2aab26a344f7589912e6d9 Mon Sep 17 00:00:00 2001 From: Matti LeBlanc Date: Tue, 24 Jun 2025 16:03:22 +1000 Subject: [PATCH 3/5] Update youtube-player.ts fix formatting --- src/youtube-player/youtube-player.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/youtube-player/youtube-player.ts b/src/youtube-player/youtube-player.ts index c487262c691f..7b47f9fe7382 100644 --- a/src/youtube-player/youtube-player.ts +++ b/src/youtube-player/youtube-player.ts @@ -600,8 +600,7 @@ export class YouTubePlayer implements AfterViewInit, OnChanges, OnDestroy { params.videoId = this.videoId; } const player = this._ngZone.runOutsideAngular( - () => - new YT.Player(this.youtubeContainer.nativeElement, params), + () => new YT.Player(this.youtubeContainer.nativeElement, params) ); const whenReady = (event: YT.PlayerEvent) => { From 397b8b5fd004808d73fb4c1b2e3ab4efeb578477 Mon Sep 17 00:00:00 2001 From: Matti LeBlanc Date: Tue, 24 Jun 2025 16:09:41 +1000 Subject: [PATCH 4/5] Update youtube-player.spec.ts fix unit test --- src/youtube-player/youtube-player.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/youtube-player/youtube-player.spec.ts b/src/youtube-player/youtube-player.spec.ts index b0cfafebdb2a..ab552cc0b6bf 100644 --- a/src/youtube-player/youtube-player.spec.ts +++ b/src/youtube-player/youtube-player.spec.ts @@ -515,7 +515,7 @@ describe('YoutubePlayer', () => { let calls = playerCtorSpy.calls.all(); expect(calls.length).toBe(1); - expect(calls[0].args[1]).toEqual(jasmine.objectContaining({playerVars, videoId: undefined})); + expect(calls[0].args[1]).toEqual(jasmine.objectContaining({playerVars})); playerSpy.destroy.calls.reset(); playerCtorSpy.calls.reset(); From 4e525402a79af98acad10fbc0ce414fcec8584e1 Mon Sep 17 00:00:00 2001 From: Matti LeBlanc Date: Wed, 25 Jun 2025 10:49:32 +1000 Subject: [PATCH 5/5] Update youtube-player.ts Fix formatting --- src/youtube-player/youtube-player.ts | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/youtube-player/youtube-player.ts b/src/youtube-player/youtube-player.ts index 7b47f9fe7382..e88844e39f99 100644 --- a/src/youtube-player/youtube-player.ts +++ b/src/youtube-player/youtube-player.ts @@ -594,13 +594,15 @@ export class YouTubePlayer implements AfterViewInit, OnChanges, OnDestroy { // Calling `playVideo` on load doesn't appear to actually play // the video so we need to trigger it through `playerVars` instead. playerVars: playVideo ? {...(this.playerVars || {}), autoplay: 1} : this.playerVars, - } - // We only want to injecct a videoId if one is provided, otherwise loading a playlist via playerVars.list, the missing videoId will create a null value in the youtube iframe url and that can trigger a JS error `Invalid video id` in widget api. + }; + // We only want to injecct a videoId if one is provided, otherwise loading a playlist via + // playerVars.list, the missing videoId will create a null value in the youtube iframe url + // and that can trigger a JS error `Invalid video id` in widget api. if (this.videoId) { params.videoId = this.videoId; } const player = this._ngZone.runOutsideAngular( - () => new YT.Player(this.youtubeContainer.nativeElement, params) + () => new YT.Player(this.youtubeContainer.nativeElement, params), ); const whenReady = (event: YT.PlayerEvent) => { @@ -713,9 +715,9 @@ export class YouTubePlayer implements AfterViewInit, OnChanges, OnDestroy { player.addEventListener(name, listener); }, listener => { - // The API seems to throw when we try to unbind from a destroyed player and it doesn't - // expose whether the player has been destroyed so we have to wrap it in a try/catch to - // prevent the entire stream from erroring out. + // The API seems to throw when we try to unbind from a destroyed player and it + // doesn'texpose whether the player has been destroyed so we have to wrap it in a + // try/catch to prevent the entire stream from erroring out. try { player?.removeEventListener?.(name, listener); } catch {}