mirror of
				https://gitea.invidious.io/iv-org/videojs-quality-selector.git
				synced 2024-08-15 00:43:13 +00:00 
			
		
		
		
	Merge pull request #17 from yokuze/fix_no_preload_no_resume_play_16
Refs #16 Fix video does not resume playing after quality change
This commit is contained in:
		
						commit
						94a9648b03
					
				
					 5 changed files with 118 additions and 7 deletions
				
			
		|  | @ -3,6 +3,12 @@ | ||||||
| In general, this project adheres to [Semantic Versioning](http://semver.org/). If for some | In general, this project adheres to [Semantic Versioning](http://semver.org/). If for some | ||||||
| reason we do something that's not strictly semantic, it will be clearly called out below. | reason we do something that's not strictly semantic, it will be clearly called out below. | ||||||
| 
 | 
 | ||||||
|  | ## 1.1.2 | ||||||
|  | 
 | ||||||
|  |    * Fixed a bug where selecting a quality menu item while a video was playing did not resume | ||||||
|  |    playback after the source changed. Affected Safari and players whose `preload` attribute | ||||||
|  |    was `none` (8feeafb Fixes #16). | ||||||
|  | 
 | ||||||
| ## 1.1.1 | ## 1.1.1 | ||||||
| 
 | 
 | ||||||
|    * Reference underscore as a dependency since we depend on it (931d8a4 See #12) |    * Reference underscore as a dependency since we depend on it (931d8a4 See #12) | ||||||
|  |  | ||||||
|  | @ -26,7 +26,6 @@ | ||||||
|   "homepage": "https://github.com/silvermine/videojs-quality-selector#readme", |   "homepage": "https://github.com/silvermine/videojs-quality-selector#readme", | ||||||
|   "devDependencies": { |   "devDependencies": { | ||||||
|     "autoprefixer": "7.1.1", |     "autoprefixer": "7.1.1", | ||||||
|     "class.extend": "0.9.2", |  | ||||||
|     "coveralls": "2.13.1", |     "coveralls": "2.13.1", | ||||||
|     "eslint": "4.0.0", |     "eslint": "4.0.0", | ||||||
|     "eslint-config-silvermine": "1.3.0", |     "eslint-config-silvermine": "1.3.0", | ||||||
|  | @ -51,6 +50,7 @@ | ||||||
|     "video.js": "6.x" |     "video.js": "6.x" | ||||||
|   }, |   }, | ||||||
|   "dependencies": { |   "dependencies": { | ||||||
|  |     "class.extend": "0.9.2", | ||||||
|     "underscore": "1.8.3" |     "underscore": "1.8.3" | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -3,7 +3,8 @@ | ||||||
| var _ = require('underscore'), | var _ = require('underscore'), | ||||||
|     events = require('./events'), |     events = require('./events'), | ||||||
|     qualitySelectorFactory = require('./components/QualitySelector'), |     qualitySelectorFactory = require('./components/QualitySelector'), | ||||||
|     sourceInterceptorFactory = require('./middleware/SourceInterceptor'); |     sourceInterceptorFactory = require('./middleware/SourceInterceptor'), | ||||||
|  |     SafeSeek = require('./util/SafeSeek'); | ||||||
| 
 | 
 | ||||||
| module.exports = function(videojs) { | module.exports = function(videojs) { | ||||||
|    videojs = videojs || window.videojs; |    videojs = videojs || window.videojs; | ||||||
|  | @ -12,8 +13,7 @@ module.exports = function(videojs) { | ||||||
|    sourceInterceptorFactory(videojs); |    sourceInterceptorFactory(videojs); | ||||||
| 
 | 
 | ||||||
|    videojs.hook('setup', function(player) { |    videojs.hook('setup', function(player) { | ||||||
|       // Add handler to switch sources when the user requests a change
 |       function changeQuality(event, newSource) { | ||||||
|       player.on(events.QUALITY_REQUESTED, function(event, newSource) { |  | ||||||
|          var sources = player.currentSources(), |          var sources = player.currentSources(), | ||||||
|              currentTime = player.currentTime(), |              currentTime = player.currentTime(), | ||||||
|              isPaused = player.paused(), |              isPaused = player.paused(), | ||||||
|  | @ -29,15 +29,36 @@ module.exports = function(videojs) { | ||||||
|          // following updates the original object in `sources`.
 |          // following updates the original object in `sources`.
 | ||||||
|          selectedSource.selected = true; |          selectedSource.selected = true; | ||||||
| 
 | 
 | ||||||
|  |          if (player._qualitySelectorSafeSeek) { | ||||||
|  |             player._qualitySelectorSafeSeek.onQualitySelectionChange(); | ||||||
|  |          } | ||||||
|  | 
 | ||||||
|          player.src(sources); |          player.src(sources); | ||||||
| 
 | 
 | ||||||
|          player.one('loadeddata', function() { |          player.ready(function() { | ||||||
|             player.currentTime(currentTime); |             if (!player._qualitySelectorSafeSeek || player._qualitySelectorSafeSeek.hasFinished()) { | ||||||
|  |                // Either we don't have a pending seek action or the one that we have is no
 | ||||||
|  |                // longer applicable. This block must be within a `player.ready` callback
 | ||||||
|  |                // because the call to `player.src` above is asynchronous, and so not
 | ||||||
|  |                // having it within this `ready` callback would cause the SourceInterceptor
 | ||||||
|  |                // to execute after this block instead of before.
 | ||||||
|  |                //
 | ||||||
|  |                // We save the `currentTime` within the SafeSeek instance because if
 | ||||||
|  |                // multiple QUALITY_REQUESTED events are received before the SafeSeek
 | ||||||
|  |                // operation finishes, the player's `currentTime` will be `0` if the
 | ||||||
|  |                // player's `src` is updated but the player's `currentTime` has not yet
 | ||||||
|  |                // been set by the SafeSeek operation.
 | ||||||
|  |                player._qualitySelectorSafeSeek = new SafeSeek(player, currentTime); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|             if (!isPaused) { |             if (!isPaused) { | ||||||
|                player.play(); |                player.play(); | ||||||
|             } |             } | ||||||
|          }); |          }); | ||||||
|       }); |       } | ||||||
|  | 
 | ||||||
|  |       // Add handler to switch sources when the user requests a change
 | ||||||
|  |       player.on(events.QUALITY_REQUESTED, changeQuality); | ||||||
|    }); |    }); | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -13,6 +13,10 @@ module.exports = function(videojs) { | ||||||
|             var sources = player.currentSources(), |             var sources = player.currentSources(), | ||||||
|                 userSelectedSource, chosenSource; |                 userSelectedSource, chosenSource; | ||||||
| 
 | 
 | ||||||
|  |             if (player._qualitySelectorSafeSeek) { | ||||||
|  |                player._qualitySelectorSafeSeek.onPlayerSourcesChange(); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|             // There are generally two source options, the one that videojs
 |             // There are generally two source options, the one that videojs
 | ||||||
|             // auto-selects and the one that a "user" of this plugin has
 |             // auto-selects and the one that a "user" of this plugin has
 | ||||||
|             // supplied via the `selected` property. `selected` can come from
 |             // supplied via the `selected` property. `selected` can come from
 | ||||||
|  |  | ||||||
							
								
								
									
										80
									
								
								src/js/util/SafeSeek.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										80
									
								
								src/js/util/SafeSeek.js
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,80 @@ | ||||||
|  | 'use strict'; | ||||||
|  | 
 | ||||||
|  | var Class = require('class.extend'); | ||||||
|  | 
 | ||||||
|  | module.exports = Class.extend({ | ||||||
|  |    init: function(player, seekToTime) { | ||||||
|  |       this._player = player; | ||||||
|  |       this._seekToTime = seekToTime; | ||||||
|  |       this._hasFinished = false; | ||||||
|  |       this._keepThisInstanceWhenPlayerSourcesChange = false; | ||||||
|  |       this._seekWhenSafe(); | ||||||
|  |    }, | ||||||
|  | 
 | ||||||
|  |    _seekWhenSafe: function() { | ||||||
|  |       var HAVE_FUTURE_DATA = 3; | ||||||
|  | 
 | ||||||
|  |       // `readyState` in Video.js is the same as the HTML5 Media element's `readyState`
 | ||||||
|  |       // property.
 | ||||||
|  |       //
 | ||||||
|  |       // `readyState` is an enum of 5 values (0-4), each of which represent a state of
 | ||||||
|  |       // readiness to play. The meaning of the values range from HAVE_NOTHING (0), meaning
 | ||||||
|  |       // no data is available to HAVE_ENOUGH_DATA (4), meaning all data is loaded and the
 | ||||||
|  |       // video can be played all the way through.
 | ||||||
|  |       //
 | ||||||
|  |       // In order to seek successfully, the `readyState` must be at least HAVE_FUTURE_DATA
 | ||||||
|  |       // (3).
 | ||||||
|  |       //
 | ||||||
|  |       // @see http://docs.videojs.com/player#readyState
 | ||||||
|  |       // @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/readyState
 | ||||||
|  |       // @see https://dev.w3.org/html5/spec-preview/media-elements.html#seek-the-media-controller
 | ||||||
|  |       if (this._player.readyState() < HAVE_FUTURE_DATA) { | ||||||
|  |          this._seekFn = this._seek.bind(this); | ||||||
|  |          // The `canplay` event means that the `readyState` is at least HAVE_FUTURE_DATA.
 | ||||||
|  |          this._player.one('canplay', this._seekFn); | ||||||
|  |       } else { | ||||||
|  |          this._seek(); | ||||||
|  |       } | ||||||
|  |    }, | ||||||
|  | 
 | ||||||
|  |    onPlayerSourcesChange: function() { | ||||||
|  |       if (this._keepThisInstanceWhenPlayerSourcesChange) { | ||||||
|  |          // By setting this to `false`, we know that if the player sources change again
 | ||||||
|  |          // the change did not originate from a quality selection change, the new sources
 | ||||||
|  |          // are likely different from the old sources, and so this pending seek no longer
 | ||||||
|  |          // applies.
 | ||||||
|  |          this._keepThisInstanceWhenPlayerSourcesChange = false; | ||||||
|  |       } else { | ||||||
|  |          this.cancel(); | ||||||
|  |       } | ||||||
|  |    }, | ||||||
|  | 
 | ||||||
|  |    onQualitySelectionChange: function() { | ||||||
|  |       // `onPlayerSourcesChange` will cancel this pending seek unless we tell it not to.
 | ||||||
|  |       // We need to reuse this same pending seek instance because when the player is
 | ||||||
|  |       // paused, the `preload` attribute is set to `none`, and the user selects one
 | ||||||
|  |       // quality option and then another, the player cannot seek until the player has
 | ||||||
|  |       // enough data to do so (and the `canplay` event is fired) and thus on the second
 | ||||||
|  |       // selection the player's `currentTime()` is `0` and when the video plays we would
 | ||||||
|  |       // seek to `0` instead of the correct time.
 | ||||||
|  |       if (!this.hasFinished()) { | ||||||
|  |          this._keepThisInstanceWhenPlayerSourcesChange = true; | ||||||
|  |       } | ||||||
|  |    }, | ||||||
|  | 
 | ||||||
|  |    _seek: function() { | ||||||
|  |       this._player.currentTime(this._seekToTime); | ||||||
|  |       this._keepThisInstanceWhenPlayerSourcesChange = false; | ||||||
|  |       this._hasFinished = true; | ||||||
|  |    }, | ||||||
|  | 
 | ||||||
|  |    hasFinished: function() { | ||||||
|  |       return this._hasFinished; | ||||||
|  |    }, | ||||||
|  | 
 | ||||||
|  |    cancel: function() { | ||||||
|  |       this._player.off('canplay', this._seekFn); | ||||||
|  |       this._keepThisInstanceWhenPlayerSourcesChange = false; | ||||||
|  |       this._hasFinished = true; | ||||||
|  |    }, | ||||||
|  | }); | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue