| Index: polymer_1.0.4/bower_components/google-castable-video/google-castable-video.html
|
| diff --git a/polymer_1.0.4/bower_components/google-castable-video/google-castable-video.html b/polymer_1.0.4/bower_components/google-castable-video/google-castable-video.html
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..2e420bdef361a7b8f068f505d0f488185ef47451
|
| --- /dev/null
|
| +++ b/polymer_1.0.4/bower_components/google-castable-video/google-castable-video.html
|
| @@ -0,0 +1,390 @@
|
| +<link rel="import" href="../polymer/polymer.html">
|
| +<link rel="import" href="google-cast-sender-api.html">
|
| +
|
| +<!-- TODO(tschaeff):
|
| +- ratechange to change playbackspeed (if even possible)
|
| +-->
|
| +<script>
|
| + (function() {
|
| + // Set static variable for the timeupdate delay.
|
| + var _GOOGLE_CASTABLE_VIDEO_TIMEUPDATE_DELAY = 250;
|
| +/**
|
| +The `google-castable-video` element enables your HTML5 videos to be casted to any Chromecast.
|
| +
|
| +It behaves exactly like an HTML5 video element except for some added methods and events.
|
| +
|
| +Instead of listening for the video element's `timeupdate` event please listen for the `google-castable-video-timeupdate` event. This event is fired if the video is playing locally and on the Chromecast device.
|
| +
|
| +##### Example
|
| +
|
| + <video is="google-castable-video">
|
| + <source src="video.mp4" type="video/mp4">
|
| + </video>
|
| +
|
| +@demo
|
| +*/
|
| + Polymer({
|
| +
|
| + is: 'google-castable-video',
|
| +
|
| + extends: 'video',
|
| +
|
| + properties: {
|
| + /**
|
| + * The appId that references which receiver the Chromecast will load.
|
| + *
|
| + * @default chrome.cast.media.DEFAULT_MEDIA_RECEIVER_APP_ID
|
| + */
|
| + appId: {
|
| + type: String,
|
| + value: null
|
| + }
|
| + },
|
| +
|
| + listeners: {
|
| + 'seeked': '_onSeeked',
|
| + 'volumechange': '_onVolumechange',
|
| + 'timeupdate': '_onTimeupdate'
|
| + },
|
| +
|
| + /**
|
| + * The real paused state for local and cast playback.
|
| + *
|
| + * @property bothPaused
|
| + * @type bool
|
| + * @default true
|
| + */
|
| + _bothPaused: true,
|
| + get bothPaused() {
|
| + return this._bothPaused;
|
| + },
|
| +
|
| + /**
|
| + * Sets or returns the current playback position (in seconds).
|
| + * Since the local video is paused when the video is playing on the Chromecast device
|
| + * the objects currentTime property doesn't represent the actual currentTime of the video
|
| + * playing on the Chromecast device. To always get the actual position please use bothCurrentTime.
|
| + *
|
| + * @property bothCurrentTime
|
| + * @type number
|
| + * @default 0
|
| + */
|
| + get bothCurrentTime() {
|
| + if (this._casting && this._castMedia) {
|
| + return this._castMedia.getEstimatedTime();
|
| + } else {
|
| + return this.currentTime;
|
| + }
|
| + },
|
| +
|
| + set bothCurrentTime(val) {
|
| + return this.currentTime = val;
|
| + },
|
| +
|
| + /**
|
| + * The mode state depending on whether the video is playing locally or on the cast device.
|
| + *
|
| + * @property casting
|
| + * @type bool
|
| + * @default false
|
| + */
|
| + _casting: false,
|
| + get casting() {
|
| + return this._casting;
|
| + },
|
| +
|
| + /**
|
| + * Returns if any Chromecast is available.
|
| + *
|
| + * @property receiverAvailable
|
| + * @type bool
|
| + * @default false
|
| + */
|
| + _receiverAvailable: false,
|
| + get receiverAvailable() {
|
| + return this._receiverAvailable;
|
| + },
|
| +
|
| + /**
|
| + * The `chrome.cast.Media` object.
|
| + *
|
| + * @property castMedia
|
| + * @type chrome.cast.Media
|
| + * @default null
|
| + */
|
| + _castMedia: null,
|
| + get castMedia() {
|
| + return this._castMedia;
|
| + },
|
| +
|
| + /**
|
| + * The `chrome.cast.Session` object.
|
| + *
|
| + * @property session
|
| + * @type chrome.cast.Session
|
| + * @default null
|
| + */
|
| + _session: null,
|
| + get session() {
|
| + return this._session;
|
| + },
|
| +
|
| + ready: function() {
|
| + // Initialize the cast api.
|
| + window['__onGCastApiAvailable'] = function(loaded, errorInfo) {
|
| + if (loaded) {
|
| + this._initializeCastApi();
|
| + } else {
|
| + this._triggerError('INITIALIZE_ERROR');
|
| + }
|
| + }.bind(this);
|
| + },
|
| +
|
| + // Called internally when the cast sender api has been loaded.
|
| + _initializeCastApi: function() {
|
| + if (this.appId === null || typeof this.appId === "undefined") {
|
| + this.appId = chrome.cast.media.DEFAULT_MEDIA_RECEIVER_APP_ID;
|
| + // TODO ... process for selecting styled media receiver
|
| + }
|
| + var sessionRequest = new chrome.cast.SessionRequest(this.appId);
|
| + var apiConfig = new chrome.cast.ApiConfig(sessionRequest,
|
| + function(e){
|
| + // The sessionListener.
|
| + this._triggerCasting(true);
|
| + this._session = e;
|
| + if (this._session.media.length) {
|
| + this._onMediaDiscovered.call(this, 'onRequestSessionSuccess', this._session.media[0]);
|
| + }
|
| + // Bind the session update listener.
|
| + this._session.addUpdateListener(this._sessionUpdateListener.bind(this));
|
| + // Set interval for cast timeupdate.
|
| + this._timeupdateInterval = setInterval(function(){
|
| + if (this._castMedia && this._castMedia.playerState === 'PLAYING') {
|
| + this._triggerTimeupdate(this._castMedia.getEstimatedTime());
|
| + this._bothPaused = false;
|
| + } else {
|
| + this._bothPaused = true;
|
| + }
|
| + }.bind(this), _GOOGLE_CASTABLE_VIDEO_TIMEUPDATE_DELAY);
|
| + // Start playing on cast if playing locally.
|
| + if (!this.paused) {
|
| + this.play();
|
| + this.pause(false);
|
| + }
|
| + }.bind(this),
|
| + function(e){
|
| + // The receiverListener
|
| + if (e === chrome.cast.ReceiverAvailability.AVAILABLE) {
|
| + this._triggerAvailability(true);
|
| + } else {
|
| + this._triggerAvailability(false);
|
| + }
|
| + }.bind(this));
|
| + chrome.cast.initialize(apiConfig, function(){
|
| + // The onInitSuccess method.
|
| + /**
|
| + * The `google-castable-video-initialized` event is fired when
|
| + * the cast client API has been initialized.
|
| + *
|
| + * @event google-castable-video-initialized
|
| + */
|
| + this.fire('google-castable-video-initialized');
|
| + }.bind(this), function(){
|
| + this._triggerError('INITIALIZE_ERROR');
|
| + });
|
| + },
|
| +
|
| + /**
|
| + * Call this when the user clicks the cast icon.
|
| + * Opens the cast extension to create a session with the selected receiver.
|
| + *
|
| + * @method launchSessionManager
|
| + */
|
| + launchSessionManager: function(){
|
| + if (this._receiverAvailable) {
|
| + // Create the session with the receiver.
|
| + chrome.cast.requestSession(function(e){
|
| + // The onRequestSessionSuccess handler gets executed when we're connected.
|
| + this._triggerCasting(true);
|
| + this._session = e;
|
| + this._session.addUpdateListener(this._sessionUpdateListener.bind(this));
|
| + // If video is playing start playing on chromecast at same position.
|
| + if (!this.paused) {
|
| + this.play();
|
| + this.pause(false);
|
| + }
|
| + // Set interval for cast timeupdate.
|
| + this._timeupdateInterval = setInterval(function(){
|
| + if (this._castMedia && this._castMedia.playerState === 'PLAYING') {
|
| + this._triggerTimeupdate(this._castMedia.getEstimatedTime());
|
| + this._bothPaused = false;
|
| + } else {
|
| + this._bothPaused = true;
|
| + }
|
| + }.bind(this), _GOOGLE_CASTABLE_VIDEO_TIMEUPDATE_DELAY);
|
| + }.bind(this));
|
| + }
|
| + },
|
| +
|
| + // Internal method gets called when the cast session status changes.
|
| + _sessionUpdateListener: function(isAlive){
|
| + if (!isAlive) {
|
| + this._triggerCasting(false);
|
| + this._synchronizeMedia(true);
|
| + // If video was playing on the receiver start playing locally.
|
| + if (this._castMedia.playerState === 'PLAYING') {
|
| + this.play();
|
| + }
|
| + this._castMedia = null;
|
| + this._session = null;
|
| + // The session died so remove the timeupdate interval.
|
| + clearInterval(this._timeupdateInterval);
|
| + }
|
| + },
|
| +
|
| + // Internal method gets called when media was set through `launchsession`
|
| + // or was already playing on cast device.
|
| + _onMediaDiscovered: function(how, media) {
|
| + this._castMedia = media;
|
| + if (how === 'loadMedia') {
|
| + this._synchronizeMedia(false);
|
| + }
|
| + },
|
| +
|
| + // Internal method to synchronize the media objects.
|
| + _synchronizeMedia: function(castMaster){
|
| + if (castMaster) {
|
| + var position = this._castMedia.getEstimatedTime();
|
| + this.currentTime = position;
|
| + } else {
|
| + var position = this.currentTime;
|
| + var req = new chrome.cast.media.SeekRequest();
|
| + req.currentTime = position;
|
| + this._castMedia.seek(req);
|
| + }
|
| + },
|
| + /**
|
| + * Call the `play` method from your controls.
|
| + *
|
| + * @method play
|
| + */
|
| + play: function(cast){
|
| + if ((cast != undefined && !cast) || (!cast && !this._casting)) {
|
| + Object.getPrototypeOf(Object.getPrototypeOf(this)).play.call(this);
|
| + // this.super();
|
| + } else {
|
| + // Handle cast media.
|
| + if (!this._castMedia) {
|
| + var mediaInfo = new chrome.cast.media.MediaInfo(this.currentSrc);
|
| + [].forEach.call( // loop through DOM video sources to find the contentType of the current source
|
| + document.querySelectorAll("video source"),
|
| + function(el) {
|
| + // the HTML5 video API resolves the DOM 'src' attribute to an absolute URL for the value of 'currentSrc'; to make it matchable, then, we can only rely on the last segment of the URL
|
| + if (el.getAttribute("src").split('/').pop()===this.currentSrc.split('/').pop()) {
|
| + mediaInfo.contentType = el.getAttribute('type');
|
| + }
|
| + }
|
| + );
|
| + var request = new chrome.cast.media.LoadRequest(mediaInfo);
|
| + this._session.loadMedia(request,
|
| + this._onMediaDiscovered.bind(this, 'loadMedia'),
|
| + function(e){
|
| + this._triggerError('LOAD_MEDIA_ERROR');
|
| + }.bind(this)
|
| + );
|
| + } else {
|
| + this._castMedia.play();
|
| + }
|
| + }
|
| + },
|
| +
|
| + /**
|
| + * Call the `pause` method from your controls.
|
| + *
|
| + * @method pause
|
| + */
|
| + pause: function(cast){
|
| + if ((cast != undefined && !cast) || (!cast && !this._casting)) {
|
| + Object.getPrototypeOf(Object.getPrototypeOf(this)).pause.call(this);
|
| + // this.super();
|
| + } else {
|
| + this._castMedia.pause();
|
| + }
|
| + },
|
| +
|
| + /**
|
| + * The `google-castable-video-timeupdate` event is fired whenever
|
| + * the video's playback position changes.
|
| + *
|
| + * @event google-castable-video-timeupdate
|
| + * @param {Object} detail
|
| + * @param {number} detail.currentTime The current video position.
|
| + */
|
| + _triggerTimeupdate: function(position) {
|
| + this.fire('google-castable-video-timeupdate', { currentTime: position });
|
| + },
|
| +
|
| + /**
|
| + * The `google-castable-video-error` event is fired whenever
|
| + * an error occurs.
|
| + *
|
| + * @event google-castable-video-error
|
| + * @param {Object} detail
|
| + * @param {string} detail.error The error type.
|
| + */
|
| + _triggerError: function(description) {
|
| + this.fire('google-castable-video-error', { error: description });
|
| + },
|
| +
|
| + /**
|
| + * The `google-castable-video-receiver-status` event is fired whenever
|
| + * the availability of Chromecasts changes. Use this to show or hide the cast icon.
|
| + *
|
| + * @event google-castable-video-receiver-status
|
| + * @param {Object} detail
|
| + * @param {bool} detail.available Shows if receivers are available.
|
| + */
|
| + _triggerAvailability: function(availability) {
|
| + this._receiverAvailable = availability;
|
| + this.fire('google-castable-video-receiver-status', { available: availability });
|
| + },
|
| +
|
| + /**
|
| + * The `google-castable-video-casting` event is fired whenever the
|
| + * connection status to a Chromecast changes. Use this to change the cast icon.
|
| + *
|
| + * @event google-castable-video-casting
|
| + * @param {Object} detail
|
| + * @param {bool} detail.casting True if connected.
|
| + */
|
| + _triggerCasting: function(casting) {
|
| + this._casting = casting;
|
| + this.fire('google-castable-video-casting', { casting: casting });
|
| + },
|
| +
|
| + // Redirecting `seeked` event to Chromecast.
|
| + _onSeeked: function(){
|
| + if (this._casting) {
|
| + var req = new chrome.cast.media.SeekRequest();
|
| + req.currentTime = this.currentTime;
|
| + this._castMedia.seek(req);
|
| + }
|
| + },
|
| +
|
| + // Redirecting `volumechange` event to Chromecast.
|
| + _onVolumechange: function(){
|
| + if (this._casting) {
|
| + var volume = new chrome.cast.Volume(this.volume, this.muted);
|
| + var volumeRequest = new chrome.cast.media.VolumeRequest(volume);
|
| + this._castMedia.setVolume(volumeRequest);
|
| + }
|
| + },
|
| +
|
| + // Redirecting `timeupdate` event to `google-castable-video-timeupdate`.
|
| + _onTimeupdate: function(){
|
| + this._triggerTimeupdate(this.currentTime);
|
| + this._bothPaused = this.paused;
|
| + }
|
| + });
|
| + })();
|
| +</script>
|
|
|