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

Youtube player startSeconds and ready event not working #29986

Merged
merged 2 commits into from
Nov 11, 2024
Merged
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
2 changes: 2 additions & 0 deletions src/dev-app/youtube-player/youtube-player-demo.html
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,11 @@ <h2>Basic Example</h2>
<div class="demo-video-selection">
<mat-checkbox [(ngModel)]="disableCookies">Disable cookies</mat-checkbox>
<mat-checkbox [(ngModel)]="disablePlaceholder">Disable placeholder</mat-checkbox>
<mat-checkbox [(ngModel)]="startAt30s">Start at 30s</mat-checkbox>
</div>
<youtube-player [videoId]="selectedVideoId"
[playerVars]="playerVars"
[startSeconds]="startAt30s ? 30 : 0"
[width]="videoWidth"
[height]="videoHeight"
[disableCookies]="disableCookies"
Expand Down
1 change: 1 addition & 0 deletions src/dev-app/youtube-player/youtube-player-demo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ export class YouTubePlayerDemo implements AfterViewInit, OnDestroy {
videoHeight: number | undefined;
disableCookies = false;
disablePlaceholder = false;
startAt30s = false;
placeholderQuality: PlaceholderImageQuality;

constructor() {
Expand Down
5 changes: 0 additions & 5 deletions src/youtube-player/youtube-player.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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});
Expand Down
23 changes: 19 additions & 4 deletions src/youtube-player/youtube-player.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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<YT.PlayerEvent> =
this._getLazyEmitter<YT.PlayerEvent>('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<YT.PlayerEvent> = new EventEmitter<YT.PlayerEvent>();

/** Emits when the state of the player has changed. */
@Output() readonly stateChange: Observable<YT.OnStateChangeEvent> =
this._getLazyEmitter<YT.OnStateChangeEvent>('onStateChange');

/** Emits when there's an error while initializing the player. */
@Output() readonly error: Observable<YT.OnErrorEvent> =
this._getLazyEmitter<YT.OnErrorEvent>('onError');

/** Emits when the underlying API of the player has changed. */
@Output() readonly apiChange: Observable<YT.PlayerEvent> =
this._getLazyEmitter<YT.PlayerEvent>('onApiChange');

/** Emits when the playback quality has changed. */
@Output() readonly playbackQualityChange: Observable<YT.OnPlaybackQualityChangeEvent> =
this._getLazyEmitter<YT.OnPlaybackQualityChangeEvent>('onPlaybackQualityChange');

/** Emits when the playback rate has changed. */
@Output() readonly playbackRateChange: Observable<YT.OnPlaybackRateChangeEvent> =
this._getLazyEmitter<YT.OnPlaybackRateChangeEvent>('onPlaybackRateChange');

Expand Down Expand Up @@ -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;
Expand All @@ -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<YT.PlayerEvent>).emit(event);
this._setSize();
this._setQuality();

Expand All @@ -597,6 +606,12 @@ export class YouTubePlayer implements AfterViewInit, OnChanges, OnDestroy {
const state = player.getPlayerState();
if (state === PlayerState.UNSTARTED || state === PlayerState.CUED || state == null) {
this._cuePlayer();
} else if (playVideo && this.startSeconds && this.startSeconds > 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();
Expand Down
5 changes: 0 additions & 5 deletions tools/public_api_guard/youtube-player/youtube-player.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,10 @@ export const YOUTUBE_PLAYER_CONFIG: InjectionToken<YouTubePlayerConfig>;
// @public
export class YouTubePlayer implements AfterViewInit, OnChanges, OnDestroy {
constructor(...args: unknown[]);
// (undocumented)
readonly apiChange: Observable<YT.PlayerEvent>;
disableCookies: boolean;
disablePlaceholder: boolean;
endSeconds: number | undefined;
// (undocumented)
readonly error: Observable<YT.OnErrorEvent>;
getAvailablePlaybackRates(): number[];
getAvailableQualityLevels(): YT.SuggestedVideoQuality[];
Expand Down Expand Up @@ -77,9 +75,7 @@ export class YouTubePlayer implements AfterViewInit, OnChanges, OnDestroy {
pauseVideo(): void;
placeholderButtonLabel: string;
placeholderImageQuality: PlaceholderImageQuality;
// (undocumented)
readonly playbackQualityChange: Observable<YT.OnPlaybackQualityChangeEvent>;
// (undocumented)
readonly playbackRateChange: Observable<YT.OnPlaybackRateChangeEvent>;
playerVars: YT.PlayerVars | undefined;
playVideo(): void;
Expand All @@ -90,7 +86,6 @@ export class YouTubePlayer implements AfterViewInit, OnChanges, OnDestroy {
protected _shouldShowPlaceholder(): boolean;
showBeforeIframeApiLoads: boolean;
startSeconds: number | undefined;
// (undocumented)
readonly stateChange: Observable<YT.OnStateChangeEvent>;
stopVideo(): void;
suggestedQuality: YT.SuggestedVideoQuality | undefined;
Expand Down
Loading