From 63722821291b8f1daf5edc5c5a752c4ac2ad3d8b Mon Sep 17 00:00:00 2001 From: Kristiyan Kostadinov Date: Fri, 8 Nov 2024 09:53:06 +0100 Subject: [PATCH 1/2] fix(youtube-player): startSeconds not applied when using placeholder Fixes that the `startSeconds` input wasn't doing anything if there's a placeholder. This used to work, but seems to have broken during the transition to using the placeholder. Fixes #29874. --- src/dev-app/youtube-player/youtube-player-demo.html | 2 ++ src/dev-app/youtube-player/youtube-player-demo.ts | 1 + src/youtube-player/youtube-player.ts | 6 ++++++ 3 files changed, 9 insertions(+) diff --git a/src/dev-app/youtube-player/youtube-player-demo.html b/src/dev-app/youtube-player/youtube-player-demo.html index 1f08254f95b6..4225e9f7bd9e 100644 --- a/src/dev-app/youtube-player/youtube-player-demo.html +++ b/src/dev-app/youtube-player/youtube-player-demo.html @@ -13,9 +13,11 @@

Basic Example

Disable cookies Disable placeholder + Start at 30s
0) { + // We have to use `seekTo` when `startSeconds` are specified to simulate it playing from + // a specific time. The "proper" way to do it would be to either go through `cueVideoById` + // or `playerVars.start`, but at the time of writing both end up resetting the video + // to the state as if the user hasn't interacted with it. + player.seekTo(this.startSeconds, true); } this._changeDetectorRef.markForCheck(); From 943309123be29e49aa78a10fa6f23ec6dbe02c29 Mon Sep 17 00:00:00 2001 From: Kristiyan Kostadinov Date: Fri, 8 Nov 2024 10:04:10 +0100 Subject: [PATCH 2/2] fix(youtube-player): ready event not emitting Fixes that the `youtube-player`'s `ready` event wasn't emitting. The issue is that we create the outputs lazily based on a stream of newly-created players, however that stream emits after the `ready` event. Relates to #29874. --- src/youtube-player/youtube-player.spec.ts | 5 ----- src/youtube-player/youtube-player.ts | 17 +++++++++++++---- .../youtube-player/youtube-player.md | 5 ----- 3 files changed, 13 insertions(+), 14 deletions(-) diff --git a/src/youtube-player/youtube-player.spec.ts b/src/youtube-player/youtube-player.spec.ts index 19a4792e8d94..9e37727eecb9 100644 --- a/src/youtube-player/youtube-player.spec.ts +++ b/src/youtube-player/youtube-player.spec.ts @@ -725,17 +725,12 @@ describe('YoutubePlayer', () => { const player = noEventsApp.componentInstance.player; const subscriptions: Subscription[] = []; - const readySpy = jasmine.createSpy('ready spy'); const stateChangeSpy = jasmine.createSpy('stateChange spy'); const playbackQualityChangeSpy = jasmine.createSpy('playbackQualityChange spy'); const playbackRateChangeSpy = jasmine.createSpy('playbackRateChange spy'); const errorSpy = jasmine.createSpy('error spy'); const apiChangeSpy = jasmine.createSpy('apiChange spy'); - subscriptions.push(player.ready.subscribe(readySpy)); - events.onReady({target: playerSpy}); - expect(readySpy).toHaveBeenCalledWith({target: playerSpy}); - subscriptions.push(player.stateChange.subscribe(stateChangeSpy)); events.onStateChange({target: playerSpy, data: 5}); expect(stateChangeSpy).toHaveBeenCalledWith({target: playerSpy, data: 5}); diff --git a/src/youtube-player/youtube-player.ts b/src/youtube-player/youtube-player.ts index 344a84f11051..0d3bd9febf93 100644 --- a/src/youtube-player/youtube-player.ts +++ b/src/youtube-player/youtube-player.ts @@ -29,6 +29,7 @@ import { CSP_NONCE, ChangeDetectorRef, AfterViewInit, + EventEmitter, } from '@angular/core'; import {isPlatformBrowser} from '@angular/common'; import {Observable, of as observableOf, Subject, BehaviorSubject, fromEventPattern} from 'rxjs'; @@ -218,22 +219,29 @@ export class YouTubePlayer implements AfterViewInit, OnChanges, OnDestroy { */ @Input() placeholderImageQuality: PlaceholderImageQuality; - /** Outputs are direct proxies from the player itself. */ - @Output() readonly ready: Observable = - this._getLazyEmitter('onReady'); + // Note: ready event can't go through the lazy emitter, because it + // happens before the `_playerChanges` stream emits the new player. + /** Emits when the player is initialized. */ + @Output() readonly ready: Observable = new EventEmitter(); + + /** Emits when the state of the player has changed. */ @Output() readonly stateChange: Observable = this._getLazyEmitter('onStateChange'); + /** Emits when there's an error while initializing the player. */ @Output() readonly error: Observable = this._getLazyEmitter('onError'); + /** Emits when the underlying API of the player has changed. */ @Output() readonly apiChange: Observable = this._getLazyEmitter('onApiChange'); + /** Emits when the playback quality has changed. */ @Output() readonly playbackQualityChange: Observable = this._getLazyEmitter('onPlaybackQualityChange'); + /** Emits when the playback rate has changed. */ @Output() readonly playbackRateChange: Observable = this._getLazyEmitter('onPlaybackRateChange'); @@ -575,7 +583,7 @@ export class YouTubePlayer implements AfterViewInit, OnChanges, OnDestroy { }), ); - const whenReady = () => { + const whenReady = (event: YT.PlayerEvent) => { // Only assign the player once it's ready, otherwise YouTube doesn't expose some APIs. this._ngZone.run(() => { this._isLoading = false; @@ -584,6 +592,7 @@ export class YouTubePlayer implements AfterViewInit, OnChanges, OnDestroy { this._pendingPlayer = undefined; player.removeEventListener('onReady', whenReady); this._playerChanges.next(player); + (this.ready as EventEmitter).emit(event); this._setSize(); this._setQuality(); diff --git a/tools/public_api_guard/youtube-player/youtube-player.md b/tools/public_api_guard/youtube-player/youtube-player.md index fb8690f358f4..236abe369746 100644 --- a/tools/public_api_guard/youtube-player/youtube-player.md +++ b/tools/public_api_guard/youtube-player/youtube-player.md @@ -24,12 +24,10 @@ export const YOUTUBE_PLAYER_CONFIG: InjectionToken; // @public export class YouTubePlayer implements AfterViewInit, OnChanges, OnDestroy { constructor(...args: unknown[]); - // (undocumented) readonly apiChange: Observable; disableCookies: boolean; disablePlaceholder: boolean; endSeconds: number | undefined; - // (undocumented) readonly error: Observable; getAvailablePlaybackRates(): number[]; getAvailableQualityLevels(): YT.SuggestedVideoQuality[]; @@ -77,9 +75,7 @@ export class YouTubePlayer implements AfterViewInit, OnChanges, OnDestroy { pauseVideo(): void; placeholderButtonLabel: string; placeholderImageQuality: PlaceholderImageQuality; - // (undocumented) readonly playbackQualityChange: Observable; - // (undocumented) readonly playbackRateChange: Observable; playerVars: YT.PlayerVars | undefined; playVideo(): void; @@ -90,7 +86,6 @@ export class YouTubePlayer implements AfterViewInit, OnChanges, OnDestroy { protected _shouldShowPlaceholder(): boolean; showBeforeIframeApiLoads: boolean; startSeconds: number | undefined; - // (undocumented) readonly stateChange: Observable; stopVideo(): void; suggestedQuality: YT.SuggestedVideoQuality | undefined;