@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.
- package/fesm2022/youtube-player.mjs +706 -663
- package/fesm2022/youtube-player.mjs.map +1 -1
- package/package.json +1 -1
|
@@ -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
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
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
|
-
`,
|
|
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({
|
|
52
|
-
|
|
53
|
-
|
|
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
|
-
`,
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
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
|
-
|
|
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
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
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
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
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
|
-
|
|
446
|
+
this._applyPendingPlayerState(player, this._pendingPlayerState);
|
|
447
|
+
this._pendingPlayerState = undefined;
|
|
297
448
|
}
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
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
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
}
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
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
|
-
`,
|
|
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({
|
|
632
|
-
|
|
633
|
-
|
|
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
|
-
`,
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
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
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
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
|
-
|
|
732
|
-
|
|
733
|
-
|
|
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({
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
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
|