@@ -92,6 +92,24 @@ export class YouTubePlayer implements AfterViewInit, OnDestroy {
9292 private _width = DEFAULT_PLAYER_WIDTH ;
9393 private _widthObs = new EventEmitter < number > ( ) ;
9494
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+
95113 /** Outputs are direct proxies from the player itself. */
96114 @Output ( ) ready = new EventEmitter < YT . PlayerEvent > ( ) ;
97115 @Output ( ) stateChange = new EventEmitter < YT . OnStateChangeEvent > ( ) ;
@@ -117,6 +135,10 @@ export class YouTubePlayer implements AfterViewInit, OnDestroy {
117135 const widthObs = this . _widthObs . pipe ( startWith ( this . _width ) ) ;
118136 const heightObs = this . _heightObs . pipe ( startWith ( this . _height ) ) ;
119137
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+
120142 /** An observable of the currently loaded player. */
121143 const playerObs =
122144 createPlayerObservable (
@@ -132,7 +154,15 @@ export class YouTubePlayer implements AfterViewInit, OnDestroy {
132154
133155 bindSizeToPlayer ( playerObs , widthObs , heightObs ) ;
134156
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 ) ;
136166
137167 // After all of the subscriptions are set up, connect the observable.
138168 ( playerObs as ConnectableObservable < Player > ) . connect ( ) ;
@@ -345,6 +375,19 @@ function bindSizeToPlayer(
345375 . subscribe ( ( [ player , width , height ] ) => player && player . setSize ( width , height ) ) ;
346376}
347377
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+
348391/**
349392 * Returns an observable that emits the loaded player once it's ready. Certain properties/methods
350393 * won't be available until the iframe finishes loading.
@@ -433,30 +476,52 @@ function syncPlayerState(
433476function bindCueVideoCall (
434477 playerObs : Observable < Player | undefined > ,
435478 videoIdObs : Observable < string | undefined > ,
479+ startSecondsObs : Observable < number | undefined > ,
480+ endSecondsObs : Observable < number | undefined > ,
481+ suggestedQualityObs : Observable < YT . SuggestedVideoQuality | undefined > ,
436482 destroyed : Observable < undefined > ,
437483) {
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+
438491 // If the video id changed, there's no reason to run 'cue' unless the player
439492 // was initialized with a different video id.
440493 const changedVideoId = videoIdObs
441494 . pipe ( filterOnOther ( playerObs , ( player , videoId ) => ! ! player && player . videoId !== videoId ) ) ;
442495
443496 // If the player changed, there's no reason to run 'cue' unless there are cue options.
444497 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 ,
459519 } ) ;
520+ } ) ;
521+ }
522+
523+ function hasPlayerStarted ( player : YT . Player ) : boolean {
524+ return [ YT . PlayerState . UNSTARTED , YT . PlayerState . CUED ] . indexOf ( player . getPlayerState ( ) ) === - 1 ;
460525}
461526
462527/** Combines the two observables temporarily for the filter function. */
0 commit comments