@@ -92,6 +92,24 @@ export class YouTubePlayer implements AfterViewInit, OnDestroy {
92
92
private _width = DEFAULT_PLAYER_WIDTH ;
93
93
private _widthObs = new EventEmitter < number > ( ) ;
94
94
95
+ /** The moment when the player is supposed to start playing */
96
+ @Input ( ) set startSeconds ( startSeconds : number | undefined ) {
97
+ this . _startSeconds . emit ( startSeconds ) ;
98
+ }
99
+ private _startSeconds = new EventEmitter < number | undefined > ( ) ;
100
+
101
+ /** The moment when the player is supposed to stop playing */
102
+ @Input ( ) set endSeconds ( endSeconds : number | undefined ) {
103
+ this . _endSeconds . emit ( endSeconds ) ;
104
+ }
105
+ private _endSeconds = new EventEmitter < number | undefined > ( ) ;
106
+
107
+ /** The suggested quality of the player */
108
+ @Input ( ) set suggestedQuality ( suggestedQuality : YT . SuggestedVideoQuality | undefined ) {
109
+ this . _suggestedQuality . emit ( suggestedQuality ) ;
110
+ }
111
+ private _suggestedQuality = new EventEmitter < YT . SuggestedVideoQuality | undefined > ( ) ;
112
+
95
113
/** Outputs are direct proxies from the player itself. */
96
114
@Output ( ) ready = new EventEmitter < YT . PlayerEvent > ( ) ;
97
115
@Output ( ) stateChange = new EventEmitter < YT . OnStateChangeEvent > ( ) ;
@@ -117,6 +135,10 @@ export class YouTubePlayer implements AfterViewInit, OnDestroy {
117
135
const widthObs = this . _widthObs . pipe ( startWith ( this . _width ) ) ;
118
136
const heightObs = this . _heightObs . pipe ( startWith ( this . _height ) ) ;
119
137
138
+ const startSecondsObs = this . _startSeconds . pipe ( startWith ( undefined ) ) ;
139
+ const endSecondsObs = this . _endSeconds . pipe ( startWith ( undefined ) ) ;
140
+ const suggestedQualityObs = this . _suggestedQuality . pipe ( startWith ( undefined ) ) ;
141
+
120
142
/** An observable of the currently loaded player. */
121
143
const playerObs =
122
144
createPlayerObservable (
@@ -132,7 +154,15 @@ export class YouTubePlayer implements AfterViewInit, OnDestroy {
132
154
133
155
bindSizeToPlayer ( playerObs , widthObs , heightObs ) ;
134
156
135
- bindCueVideoCall ( playerObs , this . _videoId , this . _destroyed ) ;
157
+ bindSuggestedQualityToPlayer ( playerObs , suggestedQualityObs ) ;
158
+
159
+ bindCueVideoCall (
160
+ playerObs ,
161
+ this . _videoId ,
162
+ startSecondsObs ,
163
+ endSecondsObs ,
164
+ suggestedQualityObs ,
165
+ this . _destroyed ) ;
136
166
137
167
// After all of the subscriptions are set up, connect the observable.
138
168
( playerObs as ConnectableObservable < Player > ) . connect ( ) ;
@@ -345,6 +375,19 @@ function bindSizeToPlayer(
345
375
. subscribe ( ( [ player , width , height ] ) => player && player . setSize ( width , height ) ) ;
346
376
}
347
377
378
+ /** Listens to changes from the suggested quality and sets it on the given player. */
379
+ function bindSuggestedQualityToPlayer (
380
+ playerObs : Observable < YT . Player | undefined > ,
381
+ suggestedQualityObs : Observable < YT . SuggestedVideoQuality | undefined >
382
+ ) {
383
+ return combineLatest (
384
+ playerObs ,
385
+ suggestedQualityObs
386
+ ) . subscribe (
387
+ ( [ player , suggestedQuality ] ) =>
388
+ player && suggestedQuality && player . setPlaybackQuality ( suggestedQuality ) ) ;
389
+ }
390
+
348
391
/**
349
392
* Returns an observable that emits the loaded player once it's ready. Certain properties/methods
350
393
* won't be available until the iframe finishes loading.
@@ -433,30 +476,52 @@ function syncPlayerState(
433
476
function bindCueVideoCall (
434
477
playerObs : Observable < Player | undefined > ,
435
478
videoIdObs : Observable < string | undefined > ,
479
+ startSecondsObs : Observable < number | undefined > ,
480
+ endSecondsObs : Observable < number | undefined > ,
481
+ suggestedQualityObs : Observable < YT . SuggestedVideoQuality | undefined > ,
436
482
destroyed : Observable < undefined > ,
437
483
) {
484
+ const cueOptionsObs = combineLatest ( startSecondsObs , endSecondsObs )
485
+ . pipe ( map ( ( [ startSeconds , endSeconds ] ) => ( { startSeconds, endSeconds} ) ) ) ;
486
+
487
+ // Only respond to changes in cue options if the player is not running.
488
+ const filteredCueOptions = cueOptionsObs
489
+ . pipe ( filterOnOther ( playerObs , player => ! ! player && ! hasPlayerStarted ( player ) ) ) ;
490
+
438
491
// If the video id changed, there's no reason to run 'cue' unless the player
439
492
// was initialized with a different video id.
440
493
const changedVideoId = videoIdObs
441
494
. pipe ( filterOnOther ( playerObs , ( player , videoId ) => ! ! player && player . videoId !== videoId ) ) ;
442
495
443
496
// If the player changed, there's no reason to run 'cue' unless there are cue options.
444
497
const changedPlayer = playerObs . pipe (
445
- filterOnOther ( videoIdObs , ( videoId , player ) => ! ! player && videoId != player . videoId ) ) ;
446
-
447
- merge ( changedPlayer , changedVideoId )
448
- . pipe (
449
- withLatestFrom ( combineLatest ( playerObs , videoIdObs ) ) ,
450
- map ( ( [ _ , values ] ) => values ) ,
451
- takeUntil ( destroyed ) ,
452
- )
453
- . subscribe ( ( [ player , videoId ] ) => {
454
- if ( ! videoId || ! player ) {
455
- return ;
456
- }
457
- player . videoId = videoId ;
458
- player . cueVideoById ( { videoId} ) ;
498
+ filterOnOther (
499
+ combineLatest ( videoIdObs , cueOptionsObs ) ,
500
+ ( [ videoId , cueOptions ] , player ) =>
501
+ ! ! player &&
502
+ ( videoId != player . videoId || ! ! cueOptions . startSeconds || ! ! cueOptions . endSeconds ) ) ) ;
503
+
504
+ merge ( changedPlayer , changedVideoId , filteredCueOptions )
505
+ . pipe (
506
+ withLatestFrom ( combineLatest ( playerObs , videoIdObs , cueOptionsObs , suggestedQualityObs ) ) ,
507
+ map ( ( [ _ , values ] ) => values ) ,
508
+ takeUntil ( destroyed ) ,
509
+ )
510
+ . subscribe ( ( [ player , videoId , cueOptions , suggestedQuality ] ) => {
511
+ if ( ! videoId || ! player ) {
512
+ return ;
513
+ }
514
+ player . videoId = videoId ;
515
+ player . cueVideoById ( {
516
+ videoId,
517
+ suggestedQuality,
518
+ ...cueOptions ,
459
519
} ) ;
520
+ } ) ;
521
+ }
522
+
523
+ function hasPlayerStarted ( player : YT . Player ) : boolean {
524
+ return [ YT . PlayerState . UNSTARTED , YT . PlayerState . CUED ] . indexOf ( player . getPlayerState ( ) ) === - 1 ;
460
525
}
461
526
462
527
/** Combines the two observables temporarily for the filter function. */
0 commit comments