@angular/youtube-player 21.0.0-next.9 → 21.0.0-rc.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -7,34 +7,56 @@ import { Subject, BehaviorSubject, fromEventPattern, of, Observable } from 'rxjs
7
7
  import { switchMap, takeUntil } from 'rxjs/operators';
8
8
 
9
9
  class YouTubePlayerPlaceholder {
10
- /** ID of the video for which to show the placeholder. */
11
- videoId;
12
- /** Width of the video for which to show the placeholder. */
13
- width;
14
- /** Height of the video for which to show the placeholder. */
15
- height;
16
- /** Whether the video is currently being loaded. */
17
- isLoading;
18
- /** Accessible label for the play button. */
19
- buttonLabel;
20
- /** Quality of the placeholder image. */
21
- quality;
22
- /** Gets the background image showing the placeholder. */
23
- _getBackgroundImage() {
24
- let url;
25
- if (this.quality === 'low') {
26
- url = `https://i.ytimg.com/vi/${this.videoId}/hqdefault.jpg`;
27
- }
28
- else if (this.quality === 'high') {
29
- url = `https://i.ytimg.com/vi/${this.videoId}/maxresdefault.jpg`;
30
- }
31
- else {
32
- url = `https://i.ytimg.com/vi_webp/${this.videoId}/sddefault.webp`;
33
- }
34
- return `url(${url})`;
35
- }
36
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: YouTubePlayerPlaceholder, deps: [], target: i0.ɵɵFactoryTarget.Component });
37
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.2.0-next.2", type: YouTubePlayerPlaceholder, isStandalone: true, selector: "youtube-player-placeholder", inputs: { videoId: "videoId", width: "width", height: "height", isLoading: "isLoading", buttonLabel: "buttonLabel", quality: "quality" }, host: { properties: { "class.youtube-player-placeholder-loading": "isLoading", "style.background-image": "_getBackgroundImage()", "style.width.px": "width", "style.height.px": "height" }, classAttribute: "youtube-player-placeholder" }, ngImport: i0, template: `
10
+ videoId;
11
+ width;
12
+ height;
13
+ isLoading;
14
+ buttonLabel;
15
+ quality;
16
+ _getBackgroundImage() {
17
+ let url;
18
+ if (this.quality === 'low') {
19
+ url = `https://i.ytimg.com/vi/${this.videoId}/hqdefault.jpg`;
20
+ } else if (this.quality === 'high') {
21
+ url = `https://i.ytimg.com/vi/${this.videoId}/maxresdefault.jpg`;
22
+ } else {
23
+ url = `https://i.ytimg.com/vi_webp/${this.videoId}/sddefault.webp`;
24
+ }
25
+ return `url(${url})`;
26
+ }
27
+ static ɵfac = i0.ɵɵngDeclareFactory({
28
+ minVersion: "12.0.0",
29
+ version: "20.2.0-next.2",
30
+ ngImport: i0,
31
+ type: YouTubePlayerPlaceholder,
32
+ deps: [],
33
+ target: i0.ɵɵFactoryTarget.Component
34
+ });
35
+ static ɵcmp = i0.ɵɵngDeclareComponent({
36
+ minVersion: "14.0.0",
37
+ version: "20.2.0-next.2",
38
+ type: YouTubePlayerPlaceholder,
39
+ isStandalone: true,
40
+ selector: "youtube-player-placeholder",
41
+ inputs: {
42
+ videoId: "videoId",
43
+ width: "width",
44
+ height: "height",
45
+ isLoading: "isLoading",
46
+ buttonLabel: "buttonLabel",
47
+ quality: "quality"
48
+ },
49
+ host: {
50
+ properties: {
51
+ "class.youtube-player-placeholder-loading": "isLoading",
52
+ "style.background-image": "_getBackgroundImage()",
53
+ "style.width.px": "width",
54
+ "style.height.px": "height"
55
+ },
56
+ classAttribute: "youtube-player-placeholder"
57
+ },
58
+ ngImport: i0,
59
+ template: `
38
60
  <button type="button" class="youtube-player-placeholder-button" [attr.aria-label]="buttonLabel">
39
61
  <svg
40
62
  height="100%"
@@ -46,11 +68,25 @@ class YouTubePlayerPlaceholder {
46
68
  <path d="M 45,24 27,14 27,34" fill="#fff"></path>
47
69
  </svg>
48
70
  </button>
49
- `, isInline: true, styles: [".youtube-player-placeholder{display:flex;align-items:center;justify-content:center;width:100%;overflow:hidden;cursor:pointer;background-color:#000;background-position:center center;background-size:cover;transition:box-shadow 300ms ease;box-shadow:inset 0 120px 90px -90px rgba(0,0,0,.8)}:fullscreen .youtube-player-placeholder{min-width:100vw;min-height:100vh}.youtube-player-placeholder-button{transition:opacity 300ms ease;-moz-appearance:none;-webkit-appearance:none;background:none;border:none;padding:0;display:flex}.youtube-player-placeholder-button svg{width:68px;height:48px}.youtube-player-placeholder-loading{box-shadow:none}.youtube-player-placeholder-loading .youtube-player-placeholder-button{opacity:0}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
71
+ `,
72
+ isInline: true,
73
+ styles: [".youtube-player-placeholder{display:flex;align-items:center;justify-content:center;width:100%;overflow:hidden;cursor:pointer;background-color:#000;background-position:center center;background-size:cover;transition:box-shadow 300ms ease;box-shadow:inset 0 120px 90px -90px rgba(0,0,0,.8)}:fullscreen .youtube-player-placeholder{min-width:100vw;min-height:100vh}.youtube-player-placeholder-button{transition:opacity 300ms ease;-moz-appearance:none;-webkit-appearance:none;background:none;border:none;padding:0;display:flex}.youtube-player-placeholder-button svg{width:68px;height:48px}.youtube-player-placeholder-loading{box-shadow:none}.youtube-player-placeholder-loading .youtube-player-placeholder-button{opacity:0}\n"],
74
+ changeDetection: i0.ChangeDetectionStrategy.OnPush,
75
+ encapsulation: i0.ViewEncapsulation.None
76
+ });
50
77
  }
51
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: YouTubePlayerPlaceholder, decorators: [{
52
- type: Component,
53
- args: [{ selector: 'youtube-player-placeholder', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, template: `
78
+ i0.ɵɵngDeclareClassMetadata({
79
+ minVersion: "12.0.0",
80
+ version: "20.2.0-next.2",
81
+ ngImport: i0,
82
+ type: YouTubePlayerPlaceholder,
83
+ decorators: [{
84
+ type: Component,
85
+ args: [{
86
+ selector: 'youtube-player-placeholder',
87
+ changeDetection: ChangeDetectionStrategy.OnPush,
88
+ encapsulation: ViewEncapsulation.None,
89
+ template: `
54
90
  <button type="button" class="youtube-player-placeholder-button" [attr.aria-label]="buttonLabel">
55
91
  <svg
56
92
  height="100%"
@@ -62,557 +98,478 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.0-next.2",
62
98
  <path d="M 45,24 27,14 27,34" fill="#fff"></path>
63
99
  </svg>
64
100
  </button>
65
- `, host: {
66
- 'class': 'youtube-player-placeholder',
67
- '[class.youtube-player-placeholder-loading]': 'isLoading',
68
- '[style.background-image]': '_getBackgroundImage()',
69
- '[style.width.px]': 'width',
70
- '[style.height.px]': 'height',
71
- }, styles: [".youtube-player-placeholder{display:flex;align-items:center;justify-content:center;width:100%;overflow:hidden;cursor:pointer;background-color:#000;background-position:center center;background-size:cover;transition:box-shadow 300ms ease;box-shadow:inset 0 120px 90px -90px rgba(0,0,0,.8)}:fullscreen .youtube-player-placeholder{min-width:100vw;min-height:100vh}.youtube-player-placeholder-button{transition:opacity 300ms ease;-moz-appearance:none;-webkit-appearance:none;background:none;border:none;padding:0;display:flex}.youtube-player-placeholder-button svg{width:68px;height:48px}.youtube-player-placeholder-loading{box-shadow:none}.youtube-player-placeholder-loading .youtube-player-placeholder-button{opacity:0}\n"] }]
72
- }], propDecorators: { videoId: [{
73
- type: Input
74
- }], width: [{
75
- type: Input
76
- }], height: [{
77
- type: Input
78
- }], isLoading: [{
79
- type: Input
80
- }], buttonLabel: [{
81
- type: Input
82
- }], quality: [{
83
- type: Input
84
- }] } });
101
+ `,
102
+ host: {
103
+ 'class': 'youtube-player-placeholder',
104
+ '[class.youtube-player-placeholder-loading]': 'isLoading',
105
+ '[style.background-image]': '_getBackgroundImage()',
106
+ '[style.width.px]': 'width',
107
+ '[style.height.px]': 'height'
108
+ },
109
+ styles: [".youtube-player-placeholder{display:flex;align-items:center;justify-content:center;width:100%;overflow:hidden;cursor:pointer;background-color:#000;background-position:center center;background-size:cover;transition:box-shadow 300ms ease;box-shadow:inset 0 120px 90px -90px rgba(0,0,0,.8)}:fullscreen .youtube-player-placeholder{min-width:100vw;min-height:100vh}.youtube-player-placeholder-button{transition:opacity 300ms ease;-moz-appearance:none;-webkit-appearance:none;background:none;border:none;padding:0;display:flex}.youtube-player-placeholder-button svg{width:68px;height:48px}.youtube-player-placeholder-loading{box-shadow:none}.youtube-player-placeholder-loading .youtube-player-placeholder-button{opacity:0}\n"]
110
+ }]
111
+ }],
112
+ propDecorators: {
113
+ videoId: [{
114
+ type: Input
115
+ }],
116
+ width: [{
117
+ type: Input
118
+ }],
119
+ height: [{
120
+ type: Input
121
+ }],
122
+ isLoading: [{
123
+ type: Input
124
+ }],
125
+ buttonLabel: [{
126
+ type: Input
127
+ }],
128
+ quality: [{
129
+ type: Input
130
+ }]
131
+ }
132
+ });
85
133
 
86
- // Workaround for: https://github.com/bazelbuild/rules_nodejs/issues/1265
87
- /// <reference types="youtube" preserve="true" />
88
- /** Injection token used to configure the `YouTubePlayer`. */
89
134
  const YOUTUBE_PLAYER_CONFIG = new InjectionToken('YOUTUBE_PLAYER_CONFIG');
90
135
  const DEFAULT_PLAYER_WIDTH = 640;
91
136
  const DEFAULT_PLAYER_HEIGHT = 390;
92
- /** Coercion function for time values. */
93
137
  function coerceTime(value) {
94
- return value == null ? value : numberAttribute(value, 0);
138
+ return value == null ? value : numberAttribute(value, 0);
95
139
  }
96
- /**
97
- * Equivalent of `YT.PlayerState` which we can't use, because it's meant to
98
- * be read off the `window` which we can't do before the API has been loaded.
99
- */
100
140
  var PlayerState;
101
141
  (function (PlayerState) {
102
- PlayerState[PlayerState["UNSTARTED"] = -1] = "UNSTARTED";
103
- PlayerState[PlayerState["ENDED"] = 0] = "ENDED";
104
- PlayerState[PlayerState["PLAYING"] = 1] = "PLAYING";
105
- PlayerState[PlayerState["PAUSED"] = 2] = "PAUSED";
106
- PlayerState[PlayerState["BUFFERING"] = 3] = "BUFFERING";
107
- PlayerState[PlayerState["CUED"] = 5] = "CUED";
142
+ PlayerState[PlayerState["UNSTARTED"] = -1] = "UNSTARTED";
143
+ PlayerState[PlayerState["ENDED"] = 0] = "ENDED";
144
+ PlayerState[PlayerState["PLAYING"] = 1] = "PLAYING";
145
+ PlayerState[PlayerState["PAUSED"] = 2] = "PAUSED";
146
+ PlayerState[PlayerState["BUFFERING"] = 3] = "BUFFERING";
147
+ PlayerState[PlayerState["CUED"] = 5] = "CUED";
108
148
  })(PlayerState || (PlayerState = {}));
109
- /**
110
- * Angular component that renders a YouTube player via the YouTube player
111
- * iframe API.
112
- * @see https://developers.google.com/youtube/iframe_api_reference
113
- */
114
149
  class YouTubePlayer {
115
- _ngZone = inject(NgZone);
116
- _nonce = inject(CSP_NONCE, { optional: true });
117
- _changeDetectorRef = inject(ChangeDetectorRef);
118
- _elementRef = inject(ElementRef);
119
- _player;
120
- _pendingPlayer;
121
- _existingApiReadyCallback;
122
- _pendingPlayerState;
123
- _destroyed = new Subject();
124
- _playerChanges = new BehaviorSubject(undefined);
125
- _isLoading = false;
126
- _hasPlaceholder = true;
127
- /** Whether we're currently rendering inside a browser. */
128
- _isBrowser;
129
- /** YouTube Video ID to view */
130
- videoId;
131
- /** Height of video player */
132
- get height() {
133
- return this._height;
134
- }
135
- set height(height) {
136
- this._height = height == null || isNaN(height) ? DEFAULT_PLAYER_HEIGHT : height;
137
- }
138
- _height = DEFAULT_PLAYER_HEIGHT;
139
- /** Width of video player */
140
- get width() {
141
- return this._width;
142
- }
143
- set width(width) {
144
- this._width = width == null || isNaN(width) ? DEFAULT_PLAYER_WIDTH : width;
145
- }
146
- _width = DEFAULT_PLAYER_WIDTH;
147
- /** The moment when the player is supposed to start playing */
148
- startSeconds;
149
- /** The moment when the player is supposed to stop playing */
150
- endSeconds;
151
- /** The suggested quality of the player */
152
- suggestedQuality;
153
- /**
154
- * Extra parameters used to configure the player. See:
155
- * https://developers.google.com/youtube/player_parameters.html?playerVersion=HTML5#Parameters
156
- */
157
- playerVars;
158
- /** Whether cookies inside the player have been disabled. */
159
- disableCookies = false;
160
- /** Whether to automatically load the YouTube iframe API. Defaults to `true`. */
161
- loadApi;
162
- /**
163
- * By default the player shows a placeholder image instead of loading the YouTube API which
164
- * improves the initial page load performance. This input allows for the behavior to be disabled.
165
- */
166
- disablePlaceholder = false;
167
- /**
168
- * Whether the iframe will attempt to load regardless of the status of the api on the
169
- * page. Set this to true if you don't want the `onYouTubeIframeAPIReady` field to be
170
- * set on the global window.
171
- */
172
- showBeforeIframeApiLoads = false;
173
- /** Accessible label for the play button inside of the placeholder. */
174
- placeholderButtonLabel;
175
- /**
176
- * Quality of the displayed placeholder image. Defaults to `standard`,
177
- * because not all video have a high-quality placeholder.
178
- */
179
- placeholderImageQuality;
180
- // Note: ready event can't go through the lazy emitter, because it
181
- // happens before the `_playerChanges` stream emits the new player.
182
- /** Emits when the player is initialized. */
183
- ready = new EventEmitter();
184
- /** Emits when the state of the player has changed. */
185
- stateChange = this._getLazyEmitter('onStateChange');
186
- /** Emits when there's an error while initializing the player. */
187
- error = this._getLazyEmitter('onError');
188
- /** Emits when the underlying API of the player has changed. */
189
- apiChange = this._getLazyEmitter('onApiChange');
190
- /** Emits when the playback quality has changed. */
191
- playbackQualityChange = this._getLazyEmitter('onPlaybackQualityChange');
192
- /** Emits when the playback rate has changed. */
193
- playbackRateChange = this._getLazyEmitter('onPlaybackRateChange');
194
- /** The element that will be replaced by the iframe. */
195
- youtubeContainer;
196
- constructor() {
197
- const platformId = inject(PLATFORM_ID);
198
- const config = inject(YOUTUBE_PLAYER_CONFIG, { optional: true });
199
- this.loadApi = config?.loadApi ?? true;
200
- this.disablePlaceholder = !!config?.disablePlaceholder;
201
- this.placeholderButtonLabel = config?.placeholderButtonLabel || 'Play video';
202
- this.placeholderImageQuality = config?.placeholderImageQuality || 'standard';
203
- this._isBrowser = isPlatformBrowser(platformId);
204
- }
205
- ngAfterViewInit() {
206
- this._conditionallyLoad();
207
- }
208
- ngOnChanges(changes) {
209
- if (this._shouldRecreatePlayer(changes)) {
210
- this._conditionallyLoad();
211
- }
212
- else if (this._player) {
213
- if (changes['width'] || changes['height']) {
214
- this._setSize();
215
- }
216
- if (changes['suggestedQuality']) {
217
- this._setQuality();
218
- }
219
- if (changes['startSeconds'] || changes['endSeconds'] || changes['suggestedQuality']) {
220
- this._cuePlayer();
221
- }
222
- }
223
- }
224
- ngOnDestroy() {
225
- this._pendingPlayer?.destroy();
226
- if (this._player) {
227
- this._player.destroy();
228
- window.onYouTubeIframeAPIReady = this._existingApiReadyCallback;
229
- }
230
- this._playerChanges.complete();
231
- this._destroyed.next();
232
- this._destroyed.complete();
233
- }
234
- /** See https://developers.google.com/youtube/iframe_api_reference#playVideo */
235
- playVideo() {
236
- if (this._player) {
237
- this._player.playVideo();
238
- }
239
- else {
240
- this._getPendingState().playbackState = PlayerState.PLAYING;
241
- this._load(true);
242
- }
243
- }
244
- /** See https://developers.google.com/youtube/iframe_api_reference#pauseVideo */
245
- pauseVideo() {
246
- if (this._player) {
247
- this._player.pauseVideo();
248
- }
249
- else {
250
- this._getPendingState().playbackState = PlayerState.PAUSED;
251
- }
252
- }
253
- /** See https://developers.google.com/youtube/iframe_api_reference#stopVideo */
254
- stopVideo() {
255
- if (this._player) {
256
- this._player.stopVideo();
257
- }
258
- else {
259
- // It seems like YouTube sets the player to CUED when it's stopped.
260
- this._getPendingState().playbackState = PlayerState.CUED;
261
- }
262
- }
263
- /** See https://developers.google.com/youtube/iframe_api_reference#seekTo */
264
- seekTo(seconds, allowSeekAhead) {
265
- if (this._player) {
266
- this._player.seekTo(seconds, allowSeekAhead);
267
- }
268
- else {
269
- this._getPendingState().seek = { seconds, allowSeekAhead };
270
- }
271
- }
272
- /** See https://developers.google.com/youtube/iframe_api_reference#mute */
273
- mute() {
274
- if (this._player) {
275
- this._player.mute();
276
- }
277
- else {
278
- this._getPendingState().muted = true;
279
- }
280
- }
281
- /** See https://developers.google.com/youtube/iframe_api_reference#unMute */
282
- unMute() {
283
- if (this._player) {
284
- this._player.unMute();
285
- }
286
- else {
287
- this._getPendingState().muted = false;
288
- }
289
- }
290
- /** See https://developers.google.com/youtube/iframe_api_reference#isMuted */
291
- isMuted() {
292
- if (this._player) {
293
- return this._player.isMuted();
294
- }
150
+ _ngZone = inject(NgZone);
151
+ _nonce = inject(CSP_NONCE, {
152
+ optional: true
153
+ });
154
+ _changeDetectorRef = inject(ChangeDetectorRef);
155
+ _elementRef = inject(ElementRef);
156
+ _player;
157
+ _pendingPlayer;
158
+ _existingApiReadyCallback;
159
+ _pendingPlayerState;
160
+ _destroyed = new Subject();
161
+ _playerChanges = new BehaviorSubject(undefined);
162
+ _isLoading = false;
163
+ _hasPlaceholder = true;
164
+ _isBrowser;
165
+ videoId;
166
+ get height() {
167
+ return this._height;
168
+ }
169
+ set height(height) {
170
+ this._height = height == null || isNaN(height) ? DEFAULT_PLAYER_HEIGHT : height;
171
+ }
172
+ _height = DEFAULT_PLAYER_HEIGHT;
173
+ get width() {
174
+ return this._width;
175
+ }
176
+ set width(width) {
177
+ this._width = width == null || isNaN(width) ? DEFAULT_PLAYER_WIDTH : width;
178
+ }
179
+ _width = DEFAULT_PLAYER_WIDTH;
180
+ startSeconds;
181
+ endSeconds;
182
+ suggestedQuality;
183
+ playerVars;
184
+ disableCookies = false;
185
+ loadApi;
186
+ disablePlaceholder = false;
187
+ showBeforeIframeApiLoads = false;
188
+ placeholderButtonLabel;
189
+ placeholderImageQuality;
190
+ ready = new EventEmitter();
191
+ stateChange = this._getLazyEmitter('onStateChange');
192
+ error = this._getLazyEmitter('onError');
193
+ apiChange = this._getLazyEmitter('onApiChange');
194
+ playbackQualityChange = this._getLazyEmitter('onPlaybackQualityChange');
195
+ playbackRateChange = this._getLazyEmitter('onPlaybackRateChange');
196
+ youtubeContainer;
197
+ constructor() {
198
+ const platformId = inject(PLATFORM_ID);
199
+ const config = inject(YOUTUBE_PLAYER_CONFIG, {
200
+ optional: true
201
+ });
202
+ this.loadApi = config?.loadApi ?? true;
203
+ this.disablePlaceholder = !!config?.disablePlaceholder;
204
+ this.placeholderButtonLabel = config?.placeholderButtonLabel || 'Play video';
205
+ this.placeholderImageQuality = config?.placeholderImageQuality || 'standard';
206
+ this._isBrowser = isPlatformBrowser(platformId);
207
+ }
208
+ ngAfterViewInit() {
209
+ this._conditionallyLoad();
210
+ }
211
+ ngOnChanges(changes) {
212
+ if (this._shouldRecreatePlayer(changes)) {
213
+ this._conditionallyLoad();
214
+ } else if (this._player) {
215
+ if (changes['width'] || changes['height']) {
216
+ this._setSize();
217
+ }
218
+ if (changes['suggestedQuality']) {
219
+ this._setQuality();
220
+ }
221
+ if (changes['startSeconds'] || changes['endSeconds'] || changes['suggestedQuality']) {
222
+ this._cuePlayer();
223
+ }
224
+ }
225
+ }
226
+ ngOnDestroy() {
227
+ this._pendingPlayer?.destroy();
228
+ if (this._player) {
229
+ this._player.destroy();
230
+ window.onYouTubeIframeAPIReady = this._existingApiReadyCallback;
231
+ }
232
+ this._playerChanges.complete();
233
+ this._destroyed.next();
234
+ this._destroyed.complete();
235
+ }
236
+ playVideo() {
237
+ if (this._player) {
238
+ this._player.playVideo();
239
+ } else {
240
+ this._getPendingState().playbackState = PlayerState.PLAYING;
241
+ this._load(true);
242
+ }
243
+ }
244
+ pauseVideo() {
245
+ if (this._player) {
246
+ this._player.pauseVideo();
247
+ } else {
248
+ this._getPendingState().playbackState = PlayerState.PAUSED;
249
+ }
250
+ }
251
+ stopVideo() {
252
+ if (this._player) {
253
+ this._player.stopVideo();
254
+ } else {
255
+ this._getPendingState().playbackState = PlayerState.CUED;
256
+ }
257
+ }
258
+ seekTo(seconds, allowSeekAhead) {
259
+ if (this._player) {
260
+ this._player.seekTo(seconds, allowSeekAhead);
261
+ } else {
262
+ this._getPendingState().seek = {
263
+ seconds,
264
+ allowSeekAhead
265
+ };
266
+ }
267
+ }
268
+ mute() {
269
+ if (this._player) {
270
+ this._player.mute();
271
+ } else {
272
+ this._getPendingState().muted = true;
273
+ }
274
+ }
275
+ unMute() {
276
+ if (this._player) {
277
+ this._player.unMute();
278
+ } else {
279
+ this._getPendingState().muted = false;
280
+ }
281
+ }
282
+ isMuted() {
283
+ if (this._player) {
284
+ return this._player.isMuted();
285
+ }
286
+ if (this._pendingPlayerState) {
287
+ return !!this._pendingPlayerState.muted;
288
+ }
289
+ return false;
290
+ }
291
+ setVolume(volume) {
292
+ if (this._player) {
293
+ this._player.setVolume(volume);
294
+ } else {
295
+ this._getPendingState().volume = volume;
296
+ }
297
+ }
298
+ getVolume() {
299
+ if (this._player) {
300
+ return this._player.getVolume();
301
+ }
302
+ if (this._pendingPlayerState && this._pendingPlayerState.volume != null) {
303
+ return this._pendingPlayerState.volume;
304
+ }
305
+ return 0;
306
+ }
307
+ setPlaybackRate(playbackRate) {
308
+ if (this._player) {
309
+ return this._player.setPlaybackRate(playbackRate);
310
+ } else {
311
+ this._getPendingState().playbackRate = playbackRate;
312
+ }
313
+ }
314
+ getPlaybackRate() {
315
+ if (this._player) {
316
+ return this._player.getPlaybackRate();
317
+ }
318
+ if (this._pendingPlayerState && this._pendingPlayerState.playbackRate != null) {
319
+ return this._pendingPlayerState.playbackRate;
320
+ }
321
+ return 0;
322
+ }
323
+ getAvailablePlaybackRates() {
324
+ return this._player ? this._player.getAvailablePlaybackRates() : [];
325
+ }
326
+ getVideoLoadedFraction() {
327
+ return this._player ? this._player.getVideoLoadedFraction() : 0;
328
+ }
329
+ getPlayerState() {
330
+ if (!this._isBrowser || !window.YT) {
331
+ return undefined;
332
+ }
333
+ if (this._player) {
334
+ return this._player.getPlayerState();
335
+ }
336
+ if (this._pendingPlayerState && this._pendingPlayerState.playbackState != null) {
337
+ return this._pendingPlayerState.playbackState;
338
+ }
339
+ return PlayerState.UNSTARTED;
340
+ }
341
+ getCurrentTime() {
342
+ if (this._player) {
343
+ return this._player.getCurrentTime();
344
+ }
345
+ if (this._pendingPlayerState && this._pendingPlayerState.seek) {
346
+ return this._pendingPlayerState.seek.seconds;
347
+ }
348
+ return 0;
349
+ }
350
+ getPlaybackQuality() {
351
+ return this._player ? this._player.getPlaybackQuality() : 'default';
352
+ }
353
+ getAvailableQualityLevels() {
354
+ return this._player ? this._player.getAvailableQualityLevels() : [];
355
+ }
356
+ getDuration() {
357
+ return this._player ? this._player.getDuration() : 0;
358
+ }
359
+ getVideoUrl() {
360
+ return this._player ? this._player.getVideoUrl() : '';
361
+ }
362
+ getVideoEmbedCode() {
363
+ return this._player ? this._player.getVideoEmbedCode() : '';
364
+ }
365
+ async requestFullscreen(options) {
366
+ const element = this._elementRef.nativeElement;
367
+ return element.requestFullscreen ? element.requestFullscreen(options) : Promise.reject(new Error('Fullscreen API not supported by browser.'));
368
+ }
369
+ _load(playVideo) {
370
+ if (!this._isBrowser) {
371
+ return;
372
+ }
373
+ if (!window.YT || !window.YT.Player) {
374
+ if (this.loadApi) {
375
+ this._isLoading = true;
376
+ loadApi(this._nonce);
377
+ } else if (this.showBeforeIframeApiLoads && (typeof ngDevMode === 'undefined' || ngDevMode)) {
378
+ throw new Error('Namespace YT not found, cannot construct embedded youtube player. ' + 'Please install the YouTube Player API Reference for iframe Embeds: ' + 'https://developers.google.com/youtube/iframe_api_reference');
379
+ }
380
+ this._existingApiReadyCallback = window.onYouTubeIframeAPIReady;
381
+ window.onYouTubeIframeAPIReady = () => {
382
+ this._existingApiReadyCallback?.();
383
+ this._ngZone.run(() => this._createPlayer(playVideo));
384
+ };
385
+ } else {
386
+ this._createPlayer(playVideo);
387
+ }
388
+ }
389
+ _conditionallyLoad() {
390
+ if (!this._shouldShowPlaceholder()) {
391
+ this._load(false);
392
+ } else if (this.playerVars?.autoplay === 1) {
393
+ this._load(true);
394
+ }
395
+ }
396
+ _shouldShowPlaceholder() {
397
+ if (this.disablePlaceholder) {
398
+ return false;
399
+ }
400
+ if (!this._isBrowser) {
401
+ return true;
402
+ }
403
+ return this._hasPlaceholder && !!this.videoId && !this._player;
404
+ }
405
+ _getPendingState() {
406
+ if (!this._pendingPlayerState) {
407
+ this._pendingPlayerState = {};
408
+ }
409
+ return this._pendingPlayerState;
410
+ }
411
+ _shouldRecreatePlayer(changes) {
412
+ const change = changes['videoId'] || changes['playerVars'] || changes['disableCookies'] || changes['disablePlaceholder'];
413
+ return !!change && !change.isFirstChange();
414
+ }
415
+ _createPlayer(playVideo) {
416
+ this._player?.destroy();
417
+ this._pendingPlayer?.destroy();
418
+ if (typeof YT === 'undefined' || !this.videoId && !this.playerVars?.list) {
419
+ return;
420
+ }
421
+ const params = {
422
+ host: this.disableCookies ? 'https://www.youtube-nocookie.com' : undefined,
423
+ width: this.width,
424
+ height: this.height,
425
+ playerVars: playVideo ? {
426
+ ...(this.playerVars || {}),
427
+ autoplay: 1
428
+ } : this.playerVars
429
+ };
430
+ if (this.videoId) {
431
+ params.videoId = this.videoId;
432
+ }
433
+ const player = this._ngZone.runOutsideAngular(() => new YT.Player(this.youtubeContainer.nativeElement, params));
434
+ const whenReady = event => {
435
+ this._ngZone.run(() => {
436
+ this._isLoading = false;
437
+ this._hasPlaceholder = false;
438
+ this._player = player;
439
+ this._pendingPlayer = undefined;
440
+ player.removeEventListener('onReady', whenReady);
441
+ this._playerChanges.next(player);
442
+ this.ready.emit(event);
443
+ this._setSize();
444
+ this._setQuality();
295
445
  if (this._pendingPlayerState) {
296
- return !!this._pendingPlayerState.muted;
446
+ this._applyPendingPlayerState(player, this._pendingPlayerState);
447
+ this._pendingPlayerState = undefined;
297
448
  }
298
- return false;
299
- }
300
- /** See https://developers.google.com/youtube/iframe_api_reference#setVolume */
301
- setVolume(volume) {
302
- if (this._player) {
303
- this._player.setVolume(volume);
304
- }
305
- else {
306
- this._getPendingState().volume = volume;
307
- }
308
- }
309
- /** See https://developers.google.com/youtube/iframe_api_reference#getVolume */
310
- getVolume() {
311
- if (this._player) {
312
- return this._player.getVolume();
313
- }
314
- if (this._pendingPlayerState && this._pendingPlayerState.volume != null) {
315
- return this._pendingPlayerState.volume;
316
- }
317
- return 0;
318
- }
319
- /** See https://developers.google.com/youtube/iframe_api_reference#setPlaybackRate */
320
- setPlaybackRate(playbackRate) {
321
- if (this._player) {
322
- return this._player.setPlaybackRate(playbackRate);
323
- }
324
- else {
325
- this._getPendingState().playbackRate = playbackRate;
326
- }
327
- }
328
- /** See https://developers.google.com/youtube/iframe_api_reference#getPlaybackRate */
329
- getPlaybackRate() {
330
- if (this._player) {
331
- return this._player.getPlaybackRate();
449
+ const state = player.getPlayerState();
450
+ if (state === PlayerState.UNSTARTED || state === PlayerState.CUED || state == null) {
451
+ this._cuePlayer();
452
+ } else if (playVideo && this.startSeconds && this.startSeconds > 0) {
453
+ player.seekTo(this.startSeconds, true);
332
454
  }
333
- if (this._pendingPlayerState && this._pendingPlayerState.playbackRate != null) {
334
- return this._pendingPlayerState.playbackRate;
335
- }
336
- return 0;
337
- }
338
- /** See https://developers.google.com/youtube/iframe_api_reference#getAvailablePlaybackRates */
339
- getAvailablePlaybackRates() {
340
- return this._player ? this._player.getAvailablePlaybackRates() : [];
341
- }
342
- /** See https://developers.google.com/youtube/iframe_api_reference#getVideoLoadedFraction */
343
- getVideoLoadedFraction() {
344
- return this._player ? this._player.getVideoLoadedFraction() : 0;
345
- }
346
- /** See https://developers.google.com/youtube/iframe_api_reference#getPlayerState */
347
- getPlayerState() {
348
- if (!this._isBrowser || !window.YT) {
349
- return undefined;
350
- }
351
- if (this._player) {
352
- return this._player.getPlayerState();
353
- }
354
- if (this._pendingPlayerState && this._pendingPlayerState.playbackState != null) {
355
- return this._pendingPlayerState.playbackState;
356
- }
357
- return PlayerState.UNSTARTED;
358
- }
359
- /** See https://developers.google.com/youtube/iframe_api_reference#getCurrentTime */
360
- getCurrentTime() {
361
- if (this._player) {
362
- return this._player.getCurrentTime();
363
- }
364
- if (this._pendingPlayerState && this._pendingPlayerState.seek) {
365
- return this._pendingPlayerState.seek.seconds;
366
- }
367
- return 0;
368
- }
369
- /** See https://developers.google.com/youtube/iframe_api_reference#getPlaybackQuality */
370
- getPlaybackQuality() {
371
- return this._player ? this._player.getPlaybackQuality() : 'default';
372
- }
373
- /** See https://developers.google.com/youtube/iframe_api_reference#getAvailableQualityLevels */
374
- getAvailableQualityLevels() {
375
- return this._player ? this._player.getAvailableQualityLevels() : [];
376
- }
377
- /** See https://developers.google.com/youtube/iframe_api_reference#getDuration */
378
- getDuration() {
379
- return this._player ? this._player.getDuration() : 0;
380
- }
381
- /** See https://developers.google.com/youtube/iframe_api_reference#getVideoUrl */
382
- getVideoUrl() {
383
- return this._player ? this._player.getVideoUrl() : '';
384
- }
385
- /** See https://developers.google.com/youtube/iframe_api_reference#getVideoEmbedCode */
386
- getVideoEmbedCode() {
387
- return this._player ? this._player.getVideoEmbedCode() : '';
388
- }
389
- /**
390
- * Attempts to put the player into fullscreen mode, depending on browser support.
391
- * @param options Options controlling how the element behaves in fullscreen mode.
392
- */
393
- async requestFullscreen(options) {
394
- // Note that we do this on the host, rather than the iframe, because it allows us to handle the
395
- // placeholder in fullscreen mode. Null check the method since it's not supported everywhere.
396
- const element = this._elementRef.nativeElement;
397
- return element.requestFullscreen
398
- ? element.requestFullscreen(options)
399
- : Promise.reject(new Error('Fullscreen API not supported by browser.'));
400
- }
401
- /**
402
- * Loads the YouTube API and sets up the player.
403
- * @param playVideo Whether to automatically play the video once the player is loaded.
404
- */
405
- _load(playVideo) {
406
- // Don't do anything if we're not in a browser environment.
407
- if (!this._isBrowser) {
408
- return;
409
- }
410
- if (!window.YT || !window.YT.Player) {
411
- if (this.loadApi) {
412
- this._isLoading = true;
413
- loadApi(this._nonce);
414
- }
415
- else if (this.showBeforeIframeApiLoads && (typeof ngDevMode === 'undefined' || ngDevMode)) {
416
- throw new Error('Namespace YT not found, cannot construct embedded youtube player. ' +
417
- 'Please install the YouTube Player API Reference for iframe Embeds: ' +
418
- 'https://developers.google.com/youtube/iframe_api_reference');
419
- }
420
- this._existingApiReadyCallback = window.onYouTubeIframeAPIReady;
421
- window.onYouTubeIframeAPIReady = () => {
422
- this._existingApiReadyCallback?.();
423
- this._ngZone.run(() => this._createPlayer(playVideo));
424
- };
425
- }
426
- else {
427
- this._createPlayer(playVideo);
428
- }
429
- }
430
- /** Loads the player depending on the internal state of the component. */
431
- _conditionallyLoad() {
432
- // If the placeholder isn't shown anymore, we have to trigger a load.
433
- if (!this._shouldShowPlaceholder()) {
434
- this._load(false);
435
- }
436
- else if (this.playerVars?.autoplay === 1) {
437
- // If it's an autoplaying video, we have to hide the placeholder and start playing.
438
- this._load(true);
439
- }
440
- }
441
- /** Whether to show the placeholder element. */
442
- _shouldShowPlaceholder() {
443
- if (this.disablePlaceholder) {
444
- return false;
445
- }
446
- // Since we don't load the API on the server, we show the placeholder permanently.
447
- if (!this._isBrowser) {
448
- return true;
449
- }
450
- return this._hasPlaceholder && !!this.videoId && !this._player;
451
- }
452
- /** Gets an object that should be used to store the temporary API state. */
453
- _getPendingState() {
454
- if (!this._pendingPlayerState) {
455
- this._pendingPlayerState = {};
456
- }
457
- return this._pendingPlayerState;
458
- }
459
- /**
460
- * Determines whether a change in the component state
461
- * requires the YouTube player to be recreated.
462
- */
463
- _shouldRecreatePlayer(changes) {
464
- const change = changes['videoId'] ||
465
- changes['playerVars'] ||
466
- changes['disableCookies'] ||
467
- changes['disablePlaceholder'];
468
- return !!change && !change.isFirstChange();
469
- }
470
- /**
471
- * Creates a new YouTube player and destroys the existing one.
472
- * @param playVideo Whether to play the video once it loads.
473
- */
474
- _createPlayer(playVideo) {
475
- this._player?.destroy();
476
- this._pendingPlayer?.destroy();
477
- // A player can't be created if the API isn't loaded,
478
- // or there isn't a video or playlist to be played.
479
- if (typeof YT === 'undefined' || (!this.videoId && !this.playerVars?.list)) {
480
- return;
481
- }
482
- // Important! We need to create the Player object outside of the `NgZone`, because it kicks
483
- // off a 250ms setInterval which will continually trigger change detection if we don't.
484
- const params = {
485
- host: this.disableCookies ? 'https://www.youtube-nocookie.com' : undefined,
486
- width: this.width,
487
- height: this.height,
488
- // Calling `playVideo` on load doesn't appear to actually play
489
- // the video so we need to trigger it through `playerVars` instead.
490
- playerVars: playVideo ? { ...(this.playerVars || {}), autoplay: 1 } : this.playerVars,
491
- };
492
- // We only want to injecct a videoId if one is provided, otherwise loading a playlist via
493
- // playerVars.list, the missing videoId will create a null value in the youtube iframe url
494
- // and that can trigger a JS error `Invalid video id` in widget api.
495
- if (this.videoId) {
496
- params.videoId = this.videoId;
497
- }
498
- const player = this._ngZone.runOutsideAngular(() => new YT.Player(this.youtubeContainer.nativeElement, params));
499
- const whenReady = (event) => {
500
- // Only assign the player once it's ready, otherwise YouTube doesn't expose some APIs.
501
- this._ngZone.run(() => {
502
- this._isLoading = false;
503
- this._hasPlaceholder = false;
504
- this._player = player;
505
- this._pendingPlayer = undefined;
506
- player.removeEventListener('onReady', whenReady);
507
- this._playerChanges.next(player);
508
- this.ready.emit(event);
509
- this._setSize();
510
- this._setQuality();
511
- if (this._pendingPlayerState) {
512
- this._applyPendingPlayerState(player, this._pendingPlayerState);
513
- this._pendingPlayerState = undefined;
514
- }
515
- // Only cue the player when it either hasn't started yet or it's cued,
516
- // otherwise cuing it can interrupt a player with autoplay enabled.
517
- const state = player.getPlayerState();
518
- if (state === PlayerState.UNSTARTED || state === PlayerState.CUED || state == null) {
519
- this._cuePlayer();
520
- }
521
- else if (playVideo && this.startSeconds && this.startSeconds > 0) {
522
- // We have to use `seekTo` when `startSeconds` are specified to simulate it playing from
523
- // a specific time. The "proper" way to do it would be to either go through `cueVideoById`
524
- // or `playerVars.start`, but at the time of writing both end up resetting the video
525
- // to the state as if the user hasn't interacted with it.
526
- player.seekTo(this.startSeconds, true);
527
- }
528
- this._changeDetectorRef.markForCheck();
529
- });
530
- };
531
- this._pendingPlayer = player;
532
- player.addEventListener('onReady', whenReady);
533
- }
534
- /** Applies any state that changed before the player was initialized. */
535
- _applyPendingPlayerState(player, pendingState) {
536
- const { playbackState, playbackRate, volume, muted, seek } = pendingState;
537
- switch (playbackState) {
538
- case PlayerState.PLAYING:
539
- player.playVideo();
540
- break;
541
- case PlayerState.PAUSED:
542
- player.pauseVideo();
543
- break;
544
- case PlayerState.CUED:
545
- player.stopVideo();
546
- break;
547
- }
548
- if (playbackRate != null) {
549
- player.setPlaybackRate(playbackRate);
550
- }
551
- if (volume != null) {
552
- player.setVolume(volume);
553
- }
554
- if (muted != null) {
555
- muted ? player.mute() : player.unMute();
556
- }
557
- if (seek != null) {
558
- player.seekTo(seek.seconds, seek.allowSeekAhead);
559
- }
560
- }
561
- /** Cues the player based on the current component state. */
562
- _cuePlayer() {
563
- if (this._player && this.videoId) {
564
- this._player.cueVideoById({
565
- videoId: this.videoId,
566
- startSeconds: this.startSeconds,
567
- endSeconds: this.endSeconds,
568
- suggestedQuality: this.suggestedQuality,
569
- });
570
- }
571
- }
572
- /** Sets the player's size based on the current input values. */
573
- _setSize() {
574
- this._player?.setSize(this.width, this.height);
575
- }
576
- /** Sets the player's quality based on the current input values. */
577
- _setQuality() {
578
- if (this._player && this.suggestedQuality) {
579
- this._player.setPlaybackQuality(this.suggestedQuality);
580
- }
581
- }
582
- /** Gets an observable that adds an event listener to the player when a user subscribes to it. */
583
- _getLazyEmitter(name) {
584
- // Start with the stream of players. This way the events will be transferred
585
- // over to the new player if it gets swapped out under-the-hood.
586
- return this._playerChanges.pipe(
587
- // Switch to the bound event. `switchMap` ensures that the old event is removed when the
588
- // player is changed. If there's no player, return an observable that never emits.
589
- switchMap(player => {
590
- return player
591
- ? fromEventPattern(listener => {
592
- player.addEventListener(name, listener);
593
- }, listener => {
594
- // The API seems to throw when we try to unbind from a destroyed player and it
595
- // doesn'texpose whether the player has been destroyed so we have to wrap it in a
596
- // try/catch to prevent the entire stream from erroring out.
597
- try {
598
- player?.removeEventListener?.(name, listener);
599
- }
600
- catch { }
601
- })
602
- : of();
603
- }),
604
- // By default we run all the API interactions outside the zone
605
- // so we have to bring the events back in manually when they emit.
606
- source => new Observable(observer => source.subscribe({
607
- next: value => this._ngZone.run(() => observer.next(value)),
608
- error: error => observer.error(error),
609
- complete: () => observer.complete(),
610
- })),
611
- // Ensures that everything is cleared out on destroy.
612
- takeUntil(this._destroyed));
613
- }
614
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: YouTubePlayer, deps: [], target: i0.ɵɵFactoryTarget.Component });
615
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.2.0-next.2", type: YouTubePlayer, isStandalone: true, selector: "youtube-player", inputs: { videoId: "videoId", height: ["height", "height", numberAttribute], width: ["width", "width", numberAttribute], startSeconds: ["startSeconds", "startSeconds", coerceTime], endSeconds: ["endSeconds", "endSeconds", coerceTime], suggestedQuality: "suggestedQuality", playerVars: "playerVars", disableCookies: ["disableCookies", "disableCookies", booleanAttribute], loadApi: ["loadApi", "loadApi", booleanAttribute], disablePlaceholder: ["disablePlaceholder", "disablePlaceholder", booleanAttribute], showBeforeIframeApiLoads: ["showBeforeIframeApiLoads", "showBeforeIframeApiLoads", booleanAttribute], placeholderButtonLabel: "placeholderButtonLabel", placeholderImageQuality: "placeholderImageQuality" }, outputs: { ready: "ready", stateChange: "stateChange", error: "error", apiChange: "apiChange", playbackQualityChange: "playbackQualityChange", playbackRateChange: "playbackRateChange" }, viewQueries: [{ propertyName: "youtubeContainer", first: true, predicate: ["youtubeContainer"], descendants: true, static: true }], usesOnChanges: true, ngImport: i0, template: `
455
+ this._changeDetectorRef.markForCheck();
456
+ });
457
+ };
458
+ this._pendingPlayer = player;
459
+ player.addEventListener('onReady', whenReady);
460
+ }
461
+ _applyPendingPlayerState(player, pendingState) {
462
+ const {
463
+ playbackState,
464
+ playbackRate,
465
+ volume,
466
+ muted,
467
+ seek
468
+ } = pendingState;
469
+ switch (playbackState) {
470
+ case PlayerState.PLAYING:
471
+ player.playVideo();
472
+ break;
473
+ case PlayerState.PAUSED:
474
+ player.pauseVideo();
475
+ break;
476
+ case PlayerState.CUED:
477
+ player.stopVideo();
478
+ break;
479
+ }
480
+ if (playbackRate != null) {
481
+ player.setPlaybackRate(playbackRate);
482
+ }
483
+ if (volume != null) {
484
+ player.setVolume(volume);
485
+ }
486
+ if (muted != null) {
487
+ muted ? player.mute() : player.unMute();
488
+ }
489
+ if (seek != null) {
490
+ player.seekTo(seek.seconds, seek.allowSeekAhead);
491
+ }
492
+ }
493
+ _cuePlayer() {
494
+ if (this._player && this.videoId) {
495
+ this._player.cueVideoById({
496
+ videoId: this.videoId,
497
+ startSeconds: this.startSeconds,
498
+ endSeconds: this.endSeconds,
499
+ suggestedQuality: this.suggestedQuality
500
+ });
501
+ }
502
+ }
503
+ _setSize() {
504
+ this._player?.setSize(this.width, this.height);
505
+ }
506
+ _setQuality() {
507
+ if (this._player && this.suggestedQuality) {
508
+ this._player.setPlaybackQuality(this.suggestedQuality);
509
+ }
510
+ }
511
+ _getLazyEmitter(name) {
512
+ return this._playerChanges.pipe(switchMap(player => {
513
+ return player ? fromEventPattern(listener => {
514
+ player.addEventListener(name, listener);
515
+ }, listener => {
516
+ try {
517
+ player?.removeEventListener?.(name, listener);
518
+ } catch {}
519
+ }) : of();
520
+ }), source => new Observable(observer => source.subscribe({
521
+ next: value => this._ngZone.run(() => observer.next(value)),
522
+ error: error => observer.error(error),
523
+ complete: () => observer.complete()
524
+ })), takeUntil(this._destroyed));
525
+ }
526
+ static ɵfac = i0.ɵɵngDeclareFactory({
527
+ minVersion: "12.0.0",
528
+ version: "20.2.0-next.2",
529
+ ngImport: i0,
530
+ type: YouTubePlayer,
531
+ deps: [],
532
+ target: i0.ɵɵFactoryTarget.Component
533
+ });
534
+ static ɵcmp = i0.ɵɵngDeclareComponent({
535
+ minVersion: "17.0.0",
536
+ version: "20.2.0-next.2",
537
+ type: YouTubePlayer,
538
+ isStandalone: true,
539
+ selector: "youtube-player",
540
+ inputs: {
541
+ videoId: "videoId",
542
+ height: ["height", "height", numberAttribute],
543
+ width: ["width", "width", numberAttribute],
544
+ startSeconds: ["startSeconds", "startSeconds", coerceTime],
545
+ endSeconds: ["endSeconds", "endSeconds", coerceTime],
546
+ suggestedQuality: "suggestedQuality",
547
+ playerVars: "playerVars",
548
+ disableCookies: ["disableCookies", "disableCookies", booleanAttribute],
549
+ loadApi: ["loadApi", "loadApi", booleanAttribute],
550
+ disablePlaceholder: ["disablePlaceholder", "disablePlaceholder", booleanAttribute],
551
+ showBeforeIframeApiLoads: ["showBeforeIframeApiLoads", "showBeforeIframeApiLoads", booleanAttribute],
552
+ placeholderButtonLabel: "placeholderButtonLabel",
553
+ placeholderImageQuality: "placeholderImageQuality"
554
+ },
555
+ outputs: {
556
+ ready: "ready",
557
+ stateChange: "stateChange",
558
+ error: "error",
559
+ apiChange: "apiChange",
560
+ playbackQualityChange: "playbackQualityChange",
561
+ playbackRateChange: "playbackRateChange"
562
+ },
563
+ viewQueries: [{
564
+ propertyName: "youtubeContainer",
565
+ first: true,
566
+ predicate: ["youtubeContainer"],
567
+ descendants: true,
568
+ static: true
569
+ }],
570
+ usesOnChanges: true,
571
+ ngImport: i0,
572
+ template: `
616
573
  @if (_shouldShowPlaceholder()) {
617
574
  <youtube-player-placeholder
618
575
  [videoId]="videoId!"
@@ -626,11 +583,32 @@ class YouTubePlayer {
626
583
  <div [style.display]="_shouldShowPlaceholder() ? 'none' : ''">
627
584
  <div #youtubeContainer></div>
628
585
  </div>
629
- `, isInline: true, styles: ["youtube-player:fullscreen,youtube-player:fullscreen iframe{min-width:100vw;min-height:100vh}\n"], dependencies: [{ kind: "component", type: YouTubePlayerPlaceholder, selector: "youtube-player-placeholder", inputs: ["videoId", "width", "height", "isLoading", "buttonLabel", "quality"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
586
+ `,
587
+ isInline: true,
588
+ styles: ["youtube-player:fullscreen,youtube-player:fullscreen iframe{min-width:100vw;min-height:100vh}\n"],
589
+ dependencies: [{
590
+ kind: "component",
591
+ type: YouTubePlayerPlaceholder,
592
+ selector: "youtube-player-placeholder",
593
+ inputs: ["videoId", "width", "height", "isLoading", "buttonLabel", "quality"]
594
+ }],
595
+ changeDetection: i0.ChangeDetectionStrategy.OnPush,
596
+ encapsulation: i0.ViewEncapsulation.None
597
+ });
630
598
  }
631
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: YouTubePlayer, decorators: [{
632
- type: Component,
633
- args: [{ selector: 'youtube-player', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, imports: [YouTubePlayerPlaceholder], template: `
599
+ i0.ɵɵngDeclareClassMetadata({
600
+ minVersion: "12.0.0",
601
+ version: "20.2.0-next.2",
602
+ ngImport: i0,
603
+ type: YouTubePlayer,
604
+ decorators: [{
605
+ type: Component,
606
+ args: [{
607
+ selector: 'youtube-player',
608
+ changeDetection: ChangeDetectionStrategy.OnPush,
609
+ encapsulation: ViewEncapsulation.None,
610
+ imports: [YouTubePlayerPlaceholder],
611
+ template: `
634
612
  @if (_shouldShowPlaceholder()) {
635
613
  <youtube-player-placeholder
636
614
  [videoId]="videoId!"
@@ -644,101 +622,166 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.0-next.2",
644
622
  <div [style.display]="_shouldShowPlaceholder() ? 'none' : ''">
645
623
  <div #youtubeContainer></div>
646
624
  </div>
647
- `, styles: ["youtube-player:fullscreen,youtube-player:fullscreen iframe{min-width:100vw;min-height:100vh}\n"] }]
648
- }], ctorParameters: () => [], propDecorators: { videoId: [{
649
- type: Input
650
- }], height: [{
651
- type: Input,
652
- args: [{ transform: numberAttribute }]
653
- }], width: [{
654
- type: Input,
655
- args: [{ transform: numberAttribute }]
656
- }], startSeconds: [{
657
- type: Input,
658
- args: [{ transform: coerceTime }]
659
- }], endSeconds: [{
660
- type: Input,
661
- args: [{ transform: coerceTime }]
662
- }], suggestedQuality: [{
663
- type: Input
664
- }], playerVars: [{
665
- type: Input
666
- }], disableCookies: [{
667
- type: Input,
668
- args: [{ transform: booleanAttribute }]
669
- }], loadApi: [{
670
- type: Input,
671
- args: [{ transform: booleanAttribute }]
672
- }], disablePlaceholder: [{
673
- type: Input,
674
- args: [{ transform: booleanAttribute }]
675
- }], showBeforeIframeApiLoads: [{
676
- type: Input,
677
- args: [{ transform: booleanAttribute }]
678
- }], placeholderButtonLabel: [{
679
- type: Input
680
- }], placeholderImageQuality: [{
681
- type: Input
682
- }], ready: [{
683
- type: Output
684
- }], stateChange: [{
685
- type: Output
686
- }], error: [{
687
- type: Output
688
- }], apiChange: [{
689
- type: Output
690
- }], playbackQualityChange: [{
691
- type: Output
692
- }], playbackRateChange: [{
693
- type: Output
694
- }], youtubeContainer: [{
695
- type: ViewChild,
696
- args: ['youtubeContainer', { static: true }]
697
- }] } });
625
+ `,
626
+ styles: ["youtube-player:fullscreen,youtube-player:fullscreen iframe{min-width:100vw;min-height:100vh}\n"]
627
+ }]
628
+ }],
629
+ ctorParameters: () => [],
630
+ propDecorators: {
631
+ videoId: [{
632
+ type: Input
633
+ }],
634
+ height: [{
635
+ type: Input,
636
+ args: [{
637
+ transform: numberAttribute
638
+ }]
639
+ }],
640
+ width: [{
641
+ type: Input,
642
+ args: [{
643
+ transform: numberAttribute
644
+ }]
645
+ }],
646
+ startSeconds: [{
647
+ type: Input,
648
+ args: [{
649
+ transform: coerceTime
650
+ }]
651
+ }],
652
+ endSeconds: [{
653
+ type: Input,
654
+ args: [{
655
+ transform: coerceTime
656
+ }]
657
+ }],
658
+ suggestedQuality: [{
659
+ type: Input
660
+ }],
661
+ playerVars: [{
662
+ type: Input
663
+ }],
664
+ disableCookies: [{
665
+ type: Input,
666
+ args: [{
667
+ transform: booleanAttribute
668
+ }]
669
+ }],
670
+ loadApi: [{
671
+ type: Input,
672
+ args: [{
673
+ transform: booleanAttribute
674
+ }]
675
+ }],
676
+ disablePlaceholder: [{
677
+ type: Input,
678
+ args: [{
679
+ transform: booleanAttribute
680
+ }]
681
+ }],
682
+ showBeforeIframeApiLoads: [{
683
+ type: Input,
684
+ args: [{
685
+ transform: booleanAttribute
686
+ }]
687
+ }],
688
+ placeholderButtonLabel: [{
689
+ type: Input
690
+ }],
691
+ placeholderImageQuality: [{
692
+ type: Input
693
+ }],
694
+ ready: [{
695
+ type: Output
696
+ }],
697
+ stateChange: [{
698
+ type: Output
699
+ }],
700
+ error: [{
701
+ type: Output
702
+ }],
703
+ apiChange: [{
704
+ type: Output
705
+ }],
706
+ playbackQualityChange: [{
707
+ type: Output
708
+ }],
709
+ playbackRateChange: [{
710
+ type: Output
711
+ }],
712
+ youtubeContainer: [{
713
+ type: ViewChild,
714
+ args: ['youtubeContainer', {
715
+ static: true
716
+ }]
717
+ }]
718
+ }
719
+ });
698
720
  let apiLoaded = false;
699
- /** Loads the YouTube API from a specified URL only once. */
700
721
  function loadApi(nonce) {
701
- if (apiLoaded) {
702
- return;
703
- }
704
- // We can use `document` directly here, because this logic doesn't run outside the browser.
705
- const url = trustedResourceUrl `https://www.youtube.com/iframe_api`;
706
- const script = document.createElement('script');
707
- const callback = (event) => {
708
- script.removeEventListener('load', callback);
709
- script.removeEventListener('error', callback);
710
- if (event.type === 'error') {
711
- apiLoaded = false;
712
- if (typeof ngDevMode === 'undefined' || ngDevMode) {
713
- console.error(`Failed to load YouTube API from ${url}`);
714
- }
715
- }
716
- };
717
- script.addEventListener('load', callback);
718
- script.addEventListener('error', callback);
719
- setScriptSrc(script, url);
720
- script.async = true;
721
- if (nonce) {
722
- script.setAttribute('nonce', nonce);
723
- }
724
- // Set this immediately to true so we don't start loading another script
725
- // while this one is pending. If loading fails, we'll flip it back to false.
726
- apiLoaded = true;
727
- document.body.appendChild(script);
722
+ if (apiLoaded) {
723
+ return;
724
+ }
725
+ const url = trustedResourceUrl`https://www.youtube.com/iframe_api`;
726
+ const script = document.createElement('script');
727
+ const callback = event => {
728
+ script.removeEventListener('load', callback);
729
+ script.removeEventListener('error', callback);
730
+ if (event.type === 'error') {
731
+ apiLoaded = false;
732
+ if (typeof ngDevMode === 'undefined' || ngDevMode) {
733
+ console.error(`Failed to load YouTube API from ${url}`);
734
+ }
735
+ }
736
+ };
737
+ script.addEventListener('load', callback);
738
+ script.addEventListener('error', callback);
739
+ setScriptSrc(script, url);
740
+ script.async = true;
741
+ if (nonce) {
742
+ script.setAttribute('nonce', nonce);
743
+ }
744
+ apiLoaded = true;
745
+ document.body.appendChild(script);
728
746
  }
729
747
 
730
748
  class YouTubePlayerModule {
731
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: YouTubePlayerModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
732
- static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.2.0-next.2", ngImport: i0, type: YouTubePlayerModule, imports: [YouTubePlayer], exports: [YouTubePlayer] });
733
- static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: YouTubePlayerModule });
749
+ static ɵfac = i0.ɵɵngDeclareFactory({
750
+ minVersion: "12.0.0",
751
+ version: "20.2.0-next.2",
752
+ ngImport: i0,
753
+ type: YouTubePlayerModule,
754
+ deps: [],
755
+ target: i0.ɵɵFactoryTarget.NgModule
756
+ });
757
+ static ɵmod = i0.ɵɵngDeclareNgModule({
758
+ minVersion: "14.0.0",
759
+ version: "20.2.0-next.2",
760
+ ngImport: i0,
761
+ type: YouTubePlayerModule,
762
+ imports: [YouTubePlayer],
763
+ exports: [YouTubePlayer]
764
+ });
765
+ static ɵinj = i0.ɵɵngDeclareInjector({
766
+ minVersion: "12.0.0",
767
+ version: "20.2.0-next.2",
768
+ ngImport: i0,
769
+ type: YouTubePlayerModule
770
+ });
734
771
  }
735
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: YouTubePlayerModule, decorators: [{
736
- type: NgModule,
737
- args: [{
738
- imports: [YouTubePlayer],
739
- exports: [YouTubePlayer],
740
- }]
741
- }] });
772
+ i0.ɵɵngDeclareClassMetadata({
773
+ minVersion: "12.0.0",
774
+ version: "20.2.0-next.2",
775
+ ngImport: i0,
776
+ type: YouTubePlayerModule,
777
+ decorators: [{
778
+ type: NgModule,
779
+ args: [{
780
+ imports: [YouTubePlayer],
781
+ exports: [YouTubePlayer]
782
+ }]
783
+ }]
784
+ });
742
785
 
743
786
  export { YOUTUBE_PLAYER_CONFIG, YouTubePlayer, YouTubePlayerModule };
744
787
  //# sourceMappingURL=youtube-player.mjs.map