11/**
2- * @file playlist-loader.js
2+ * @module playlist-loader
33 *
4- * A state machine that manages the loading, caching, and updating of
4+ * @file A state machine that manages the loading, caching, and updating of
55 * M3U8 playlists.
6- *
76 */
87import resolveUrl from './resolve-url' ;
98import { mergeOptions , EventTarget , log } from 'video.js' ;
109import m3u8 from 'm3u8-parser' ;
1110import window from 'global/window' ;
1211
1312/**
14- * Returns a new array of segments that is the result of merging
15- * properties from an older list of segments onto an updated
16- * list. No properties on the updated playlist will be overridden.
17- *
18- * @param {Array } original the outdated list of segments
19- * @param {Array } update the updated list of segments
20- * @param {Number= } offset the index of the first update
21- * segment in the original segment list. For non-live playlists,
22- * this should always be zero and does not need to be
23- * specified. For live playlists, it should be the difference
24- * between the media sequence numbers in the original and updated
25- * playlists.
26- * @return a list of merged segment objects
27- */
13+ * Returns a new array of segments that is the result of merging
14+ * properties from an older list of segments onto an updated
15+ * list. No properties on the updated playlist will be overridden.
16+ *
17+ * @param {Array } original the outdated list of segments
18+ * @param {Array } update the updated list of segments
19+ * @param {Number= } offset the index of the first update
20+ * segment in the original segment list. For non-live playlists,
21+ * this should always be zero and does not need to be
22+ * specified. For live playlists, it should be the difference
23+ * between the media sequence numbers in the original and updated
24+ * playlists.
25+ * @return a list of merged segment objects
26+ */
2827export const updateSegments = ( original , update , offset ) => {
2928 const result = update . slice ( ) ;
3029
@@ -172,18 +171,24 @@ export const refreshDelay = (media, update) => {
172171 * Load a playlist from a remote location
173172 *
174173 * @class PlaylistLoader
175- * @extends Stream
174+ * @extends videojs.EventTarget
176175 * @param {String } srcUrl the url to start with
177- * @param {Boolean } withCredentials the withCredentials xhr option
178- * @constructor
176+ * @param {Object } hls
177+ * @param {Object } [options]
178+ * @param {Boolean } [options.withCredentials=false] the withCredentials xhr option
179+ * @param {Boolean } [options.handleManifestRedirects=false] whether to follow redirects, when any
180+ * playlist request was redirected
179181 */
180182export default class PlaylistLoader extends EventTarget {
181- constructor ( srcUrl , hls , withCredentials ) {
183+ constructor ( srcUrl , hls , options ) {
182184 super ( ) ;
183185
186+ options = options || { } ;
187+
184188 this . srcUrl = srcUrl ;
185189 this . hls_ = hls ;
186- this . withCredentials = withCredentials ;
190+ this . withCredentials = ! ! options . withCredentials ;
191+ this . handleManifestRedirects = ! ! options . handleManifestRedirects ;
187192
188193 if ( ! this . srcUrl ) {
189194 throw new Error ( 'A non-empty playlist URL is required' ) ;
@@ -360,7 +365,7 @@ export default class PlaylistLoader extends EventTarget {
360365
361366 // there is already an outstanding playlist request
362367 if ( this . request ) {
363- if ( resolveUrl ( this . master . uri , playlist . uri ) === this . request . url ) {
368+ if ( playlist . resolvedUri === this . request . url ) {
364369 // requesting to switch to the same playlist multiple times
365370 // has no effect after the first
366371 return ;
@@ -376,14 +381,16 @@ export default class PlaylistLoader extends EventTarget {
376381 }
377382
378383 this . request = this . hls_ . xhr ( {
379- uri : resolveUrl ( this . master . uri , playlist . uri ) ,
384+ uri : playlist . resolvedUri ,
380385 withCredentials : this . withCredentials
381386 } , ( error , req ) => {
382387 // disposed
383388 if ( ! this . request ) {
384389 return ;
385390 }
386391
392+ playlist . resolvedUri = this . resolveManifestRedirect ( playlist . resolvedUri , req ) ;
393+
387394 if ( error ) {
388395 return this . playlistRequestError ( this . request , playlist . uri , startingState ) ;
389396 }
@@ -399,6 +406,28 @@ export default class PlaylistLoader extends EventTarget {
399406 } ) ;
400407 }
401408
409+ /**
410+ * Checks whether xhr request was redirected and returns correct url depending
411+ * on `handleManifestRedirects` option
412+ *
413+ * @api private
414+ *
415+ * @param {String } url - an url being requested
416+ * @param {XMLHttpRequest } req - xhr request result
417+ *
418+ * @return {String }
419+ */
420+ resolveManifestRedirect ( url , req ) {
421+ if ( this . handleManifestRedirects &&
422+ req . responseURL &&
423+ url !== req . responseURL
424+ ) {
425+ return req . responseURL ;
426+ }
427+
428+ return url ;
429+ }
430+
402431 /**
403432 * pause loading of the playlist
404433 */
@@ -492,6 +521,8 @@ export default class PlaylistLoader extends EventTarget {
492521
493522 this . state = 'HAVE_MASTER' ;
494523
524+ this . srcUrl = this . resolveManifestRedirect ( this . srcUrl , req ) ;
525+
495526 parser . manifest . uri = this . srcUrl ;
496527
497528 // loaded a master playlist
@@ -521,14 +552,14 @@ export default class PlaylistLoader extends EventTarget {
521552 } ,
522553 uri : window . location . href ,
523554 playlists : [ {
524- uri : this . srcUrl
555+ uri : this . srcUrl ,
556+ resolvedUri : this . srcUrl ,
557+ // m3u8-parser does not attach an attributes property to media playlists so make
558+ // sure that the property is attached to avoid undefined reference errors
559+ attributes : { }
525560 } ]
526561 } ;
527562 this . master . playlists [ this . srcUrl ] = this . master . playlists [ 0 ] ;
528- this . master . playlists [ 0 ] . resolvedUri = this . srcUrl ;
529- // m3u8-parser does not attach an attributes property to media playlists so make
530- // sure that the property is attached to avoid undefined reference errors
531- this . master . playlists [ 0 ] . attributes = this . master . playlists [ 0 ] . attributes || { } ;
532563 this . haveMetadata ( req , this . srcUrl ) ;
533564 return this . trigger ( 'loadedmetadata' ) ;
534565 } ) ;
0 commit comments