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

fix(youtube-player): errors during server-side rendering #17091

Merged
merged 1 commit into from
Sep 16, 2019
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
1 change: 1 addition & 0 deletions src/universal-app/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ ng_module(
],
deps = [
"@npm//@angular/platform-server",
"//src/youtube-player",
] + CDK_TARGETS + CDK_EXPERIMENTAL_TARGETS + MATERIAL_TARGETS + MATERIAL_EXPERIMENTAL_TARGETS,
)

Expand Down
4 changes: 4 additions & 0 deletions src/universal-app/kitchen-sink/kitchen-sink.html
Original file line number Diff line number Diff line change
Expand Up @@ -379,3 +379,7 @@ <h2>Virtual scroll</h2>
Item #{{i}}
</div>
</cdk-virtual-scroll-viewport>

<h2>YouTube player</h2>

<youtube-player videoId="dQw4w9WgXcQ"></youtube-player>
4 changes: 4 additions & 0 deletions src/universal-app/kitchen-sink/kitchen-sink.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import {MatDividerModule} from '@angular/material/divider';
import {MatFormFieldModule} from '@angular/material/form-field';
import {MatSortModule} from '@angular/material/sort';
import {MatStepperModule} from '@angular/material/stepper';
import {YouTubePlayerModule} from '@angular/youtube-player';
import {Observable, of as observableOf} from 'rxjs';

export class TableDataSource extends DataSource<any> {
Expand Down Expand Up @@ -136,6 +137,9 @@ export class KitchenSink {
// CDK Modules
CdkTableModule,
DragDropModule,

// Other modules
YouTubePlayerModule,
],
declarations: [KitchenSink, TestEntryComponent],
exports: [KitchenSink, TestEntryComponent],
Expand Down
1 change: 0 additions & 1 deletion src/youtube-player/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ ng_module(
),
module_name = "@angular/youtube-player",
deps = [
"@npm//@angular/common",
"@npm//@angular/core",
"@npm//@types/youtube",
"@npm//rxjs",
Expand Down
2 changes: 0 additions & 2 deletions src/youtube-player/youtube-module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
* found in the LICENSE file at https://angular.io/license
*/

import {CommonModule} from '@angular/common';
import {NgModule} from '@angular/core';

import {YouTubePlayer} from './youtube-player';
Expand All @@ -16,7 +15,6 @@ const COMPONENTS = [YouTubePlayer];
@NgModule({
declarations: COMPONENTS,
exports: COMPONENTS,
imports: [CommonModule],
})
export class YouTubePlayerModule {
}
132 changes: 48 additions & 84 deletions src/youtube-player/youtube-player.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,12 @@ interface Player extends YT.Player {
// The only field available is destroy and addEventListener.
type UninitializedPlayer = Pick<Player, 'videoId' | 'destroy' | 'addEventListener'>;

/**
* Whether we're currently rendering inside a browser. Equivalent of `Platform.isBrowser`,
* but copied over here so we don't have to add another dependency.
*/
const isBrowser = typeof window === 'object' && !!window;

/**
* Angular component that renders a YouTube player via the YouTube player
* iframe API.
Expand All @@ -80,7 +86,7 @@ type UninitializedPlayer = Pick<Player, 'videoId' | 'destroy' | 'addEventListene
changeDetection: ChangeDetectionStrategy.OnPush,
encapsulation: ViewEncapsulation.None,
// This div is *replaced* by the YouTube player embed.
template: '<div #youtube_container></div>',
template: '<div #youtubeContainer></div>',
})
export class YouTubePlayer implements AfterViewInit, OnDestroy, OnInit {
/** YouTube Video ID to view */
Expand Down Expand Up @@ -147,15 +153,21 @@ export class YouTubePlayer implements AfterViewInit, OnDestroy, OnInit {
@Output() playbackRateChange = new EventEmitter<YT.OnPlaybackRateChangeEvent>();

/** The element that will be replaced by the iframe. */
@ViewChild('youtube_container', {static: false}) youtubeContainer: ElementRef | undefined;
@ViewChild('youtubeContainer', {static: false})
youtubeContainer: ElementRef<HTMLElement>;

private _youtubeContainer = new EventEmitter<HTMLElement>();
private _destroyed = new EventEmitter<undefined>();

private _player: Player | undefined;

constructor(private _ngZone: NgZone) {}

ngOnInit() {
// Don't do anything if we're not in a browser environment.
if (!isBrowser) {
return;
}

let iframeApiAvailableObs: Observable<boolean> = observableOf(true);
if (!window.YT) {
if (this.showBeforeIframeApiLoads) {
Expand Down Expand Up @@ -223,19 +235,15 @@ export class YouTubePlayer implements AfterViewInit, OnDestroy, OnInit {
}

ngAfterViewInit() {
if (!this.youtubeContainer) {
return;
}
this._youtubeContainer.emit(this.youtubeContainer.nativeElement);
}

ngOnDestroy() {
if (!this._player) {
return;
if (this._player) {
this._player.destroy();
window.onYouTubeIframeAPIReady = undefined;
this._destroyed.emit();
}
this._player.destroy();
window.onYouTubeIframeAPIReady = undefined;
this._destroyed.emit();
}

private _runInZone<T extends (...args: any[]) => void>(callback: T):
Expand All @@ -249,166 +257,122 @@ export class YouTubePlayer implements AfterViewInit, OnDestroy, OnInit {

/** See https://developers.google.com/youtube/iframe_api_reference#playVideo */
playVideo() {
if (!this._player) {
return;
if (this._player) {
this._player.playVideo();
}
this._player.playVideo();
}

/** See https://developers.google.com/youtube/iframe_api_reference#pauseVideo */
pauseVideo() {
if (!this._player) {
return;
if (this._player) {
this._player.pauseVideo();
}
this._player.pauseVideo();
}

/** See https://developers.google.com/youtube/iframe_api_reference#stopVideo */
stopVideo() {
if (!this._player) {
return;
if (this._player) {
this._player.stopVideo();
}
this._player.stopVideo();
}

/** See https://developers.google.com/youtube/iframe_api_reference#seekTo */
seekTo(seconds: number, allowSeekAhead: boolean) {
if (!this._player) {
return;
if (this._player) {
this._player.seekTo(seconds, allowSeekAhead);
}
this._player.seekTo(seconds, allowSeekAhead);
}

/** See https://developers.google.com/youtube/iframe_api_reference#mute */
mute() {
if (!this._player) {
return;
if (this._player) {
this._player.mute();
}
this._player.mute();
}

/** See https://developers.google.com/youtube/iframe_api_reference#unMute */
unMute() {
if (!this._player) {
return;
if (this._player) {
this._player.unMute();
}
this._player.unMute();
}

/** See https://developers.google.com/youtube/iframe_api_reference#isMuted */
isMuted(): boolean {
if (!this._player) {
return false;
}
return this._player.isMuted();
return !this._player || this._player.isMuted();
}

/** See https://developers.google.com/youtube/iframe_api_reference#setVolume */
setVolume(volume: number) {
if (!this._player) {
return;
if (this._player) {
this._player.setVolume(volume);
}
this._player.setVolume(volume);
}

/** See https://developers.google.com/youtube/iframe_api_reference#getVolume */
getVolume(): number {
if (!this._player) {
return 0;
}
return this._player.getVolume();
return this._player ? this._player.getVolume() : 0;
}

/** See https://developers.google.com/youtube/iframe_api_reference#setPlaybackRate */
setPlaybackRate(playbackRate: number) {
if (!this._player) {
return;
if (this._player) {
return this._player.setPlaybackRate(playbackRate);
}
return this._player.setPlaybackRate(playbackRate);
}

/** See https://developers.google.com/youtube/iframe_api_reference#getPlaybackRate */
getPlaybackRate(): number {
if (!this._player) {
return 0;
}
return this._player.getPlaybackRate();
return this._player ? this._player.getPlaybackRate() : 0;
}

/** See https://developers.google.com/youtube/iframe_api_reference#getAvailablePlaybackRates */
getAvailablePlaybackRates(): number[] {
if (!this._player) {
return [];
}
return this._player.getAvailablePlaybackRates();
return this._player ? this._player.getAvailablePlaybackRates() : [];
}

/** See https://developers.google.com/youtube/iframe_api_reference#getVideoLoadedFraction */
getVideoLoadedFraction(): number {
if (!this._player) {
return 0;
}
return this._player.getVideoLoadedFraction();
return this._player ? this._player.getVideoLoadedFraction() : 0;
}

/** See https://developers.google.com/youtube/iframe_api_reference#getPlayerState */
getPlayerState(): YT.PlayerState | undefined {
if (!window.YT) {
if (!isBrowser || !window.YT) {
return undefined;
}

if (!this._player) {
return YT.PlayerState.UNSTARTED;
}
return this._player.getPlayerState();
return this._player ? this._player.getPlayerState() : YT.PlayerState.UNSTARTED;
}

/** See https://developers.google.com/youtube/iframe_api_reference#getCurrentTime */
getCurrentTime(): number {
if (!this._player) {
return 0;
}
return this._player.getCurrentTime();
return this._player ? this._player.getCurrentTime() : 0;
}

/** See https://developers.google.com/youtube/iframe_api_reference#getPlaybackQuality */
getPlaybackQuality(): YT.SuggestedVideoQuality {
if (!this._player) {
return 'default';
}
return this._player.getPlaybackQuality();
return this._player ? this._player.getPlaybackQuality() : 'default';
}

/** See https://developers.google.com/youtube/iframe_api_reference#getAvailableQualityLevels */
getAvailableQualityLevels(): YT.SuggestedVideoQuality[] {
if (!this._player) {
return [];
}
return this._player.getAvailableQualityLevels();
return this._player ? this._player.getAvailableQualityLevels() : [];
}

/** See https://developers.google.com/youtube/iframe_api_reference#getDuration */
getDuration(): number {
if (!this._player) {
return 0;
}
return this._player.getDuration();
return this._player ? this._player.getDuration() : 0;
}

/** See https://developers.google.com/youtube/iframe_api_reference#getVideoUrl */
getVideoUrl(): string {
if (!this._player) {
return '';
}
return this._player.getVideoUrl();
return this._player ? this._player.getVideoUrl() : '';
}

/** See https://developers.google.com/youtube/iframe_api_reference#getVideoEmbedCode */
getVideoEmbedCode(): string {
if (!this._player) {
return '';
}
return this._player.getVideoEmbedCode();
return this._player ? this._player.getVideoEmbedCode() : '';
}
}

Expand Down