From 3f50019539f10d491bbb05c064fbf96e08676a7d Mon Sep 17 00:00:00 2001 From: Tim Hobbs Date: Mon, 7 Apr 2014 09:26:48 -0700 Subject: [PATCH 01/15] Additional cast params --- ApiClient.js | 26 +++++++++++++++----------- dashboard-ui/scripts/chromecast.js | 14 ++++++++------ dashboard-ui/scripts/mediaplayer.js | 2 +- dashboard-ui/scripts/remotecontrol.js | 4 +++- 4 files changed, 27 insertions(+), 19 deletions(-) diff --git a/ApiClient.js b/ApiClient.js index c937c28ee6..1a526d8a18 100644 --- a/ApiClient.js +++ b/ApiClient.js @@ -3846,7 +3846,7 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout, wi * @param {String} userId * @param {String} itemId */ - self.reportPlaybackProgress = function (userId, itemId, mediaSourceId, positionTicks, isPaused, isMuted) { + self.reportPlaybackProgress = function (userId, itemId, mediaSourceId, params) { if (!userId) { throw new Error("null userId"); @@ -3862,25 +3862,29 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout, wi var msgData = itemId; - msgData += "|" + (positionTicks == null ? "" : positionTicks); - msgData += "|" + (isPaused == null ? "" : isPaused); - msgData += "|" + (isMuted == null ? "" : isMuted); + msgData += "|" + (params.positionTicks == null ? "" : params.positionTicks); + msgData += "|" + (params.isPaused == null ? "" : params.isPaused); + msgData += "|" + (params.isMuted == null ? "" : params.isMuted); msgData += "|" + (mediaSourceId == null ? "" : mediaSourceId); + msgData += "|" + (params.audioStreamIndex == null ? "" : params.audioStreamIndex); + msgData += "|" + (params.subtitleStreamIndex == null ? "" : params.subtitleStreamIndex); + msgData += "|" + (params.volumeLevel == null ? "" : params.volumeLevel); + self.sendWebSocketMessage("PlaybackProgress", msgData); deferred.resolveWith(null, []); return deferred.promise(); } - var params = { - isPaused: isPaused, - isMuted: isMuted - }; + ////var params = { + //// isPaused: isPaused, + //// isMuted: isMuted + ////}; - if (positionTicks) { - params.positionTicks = positionTicks; - } + ////if (positionTicks) { + //// params.positionTicks = positionTicks; + ////} if (mediaSourceId) { params.mediaSourceId = mediaSourceId; diff --git a/dashboard-ui/scripts/chromecast.js b/dashboard-ui/scripts/chromecast.js index d2b7f5d159..b39bf2d542 100644 --- a/dashboard-ui/scripts/chromecast.js +++ b/dashboard-ui/scripts/chromecast.js @@ -89,7 +89,7 @@ // v1 Id AE4DA10A // v2 Id 472F0435 - var applicationID = 'AE4DA10A'; + var applicationID = '472F0435'; // request session var sessionRequest = new chrome.cast.SessionRequest(applicationID); @@ -420,7 +420,7 @@ }; } - function getCustomData(item, mediaSourceId, startTimeTicks) { + function getCustomData(item, mediaSourceId, startTimeTicks, audioStreamIndex, subtitleStreamIndex) { return { @@ -429,7 +429,9 @@ userId: Dashboard.getCurrentUserId(), deviceName: ApiClient.deviceName(), //deviceId: ApiClient.deviceId(), - startTimeTicks: startTimeTicks || 0 + startTimeTicks: startTimeTicks || 0, + audioStreamIndex: audioStreamIndex, + subtitleStreamIndex: subtitleStreamIndex }; } @@ -613,7 +615,7 @@ var castMediaInfo = new chrome.cast.media.MediaInfo(streamUrl); - castMediaInfo.customData = getCustomData(item, mediaInfo.mediaSource.Id, startTimeTicks); + castMediaInfo.customData = getCustomData(item, mediaInfo.mediaSource.Id, startTimeTicks, audioStreamIndex, subtitleStreamIndex); castMediaInfo.metadata = getMetadata(item); if (mediaInfo.streamContainer == 'm3u8') { @@ -630,8 +632,8 @@ this.castPlayerState = PLAYER_STATE.LOADING; this.session.loadMedia(request, - this.onMediaDiscovered.bind(this, 'loadMedia'), - this.onLoadMediaError.bind(this)); + this.onMediaDiscovered.bind(this, 'loadMedia'), + this.onLoadMediaError.bind(this)); }; /** diff --git a/dashboard-ui/scripts/mediaplayer.js b/dashboard-ui/scripts/mediaplayer.js index 299a94e8ab..7916f36571 100644 --- a/dashboard-ui/scripts/mediaplayer.js +++ b/dashboard-ui/scripts/mediaplayer.js @@ -950,7 +950,7 @@ function sendProgressUpdate(itemId, mediaSourceId) { - ApiClient.reportPlaybackProgress(Dashboard.getCurrentUserId(), itemId, mediaSourceId, self.getCurrentTicks(), currentMediaElement.paused, currentMediaElement.volume == 0); + ApiClient.reportPlaybackProgress(Dashboard.getCurrentUserId(), itemId, mediaSourceId, { positionTicks: self.getCurrentTicks(), isPaused: currentMediaElement.paused, isMuted: currentMediaElement.volume == 0 }); } function clearProgressInterval() { diff --git a/dashboard-ui/scripts/remotecontrol.js b/dashboard-ui/scripts/remotecontrol.js index 35add5b195..ba622794f6 100644 --- a/dashboard-ui/scripts/remotecontrol.js +++ b/dashboard-ui/scripts/remotecontrol.js @@ -349,7 +349,7 @@ // don't display the current session sessions = sessions.filter(function (s) { - return s.DeviceId != deviceId && s.SupportsRemoteControl; + return s.DeviceId != deviceId && (s.SupportsRemoteControl || s.Client == "Chromecast"); }); var elem = $('#selectSession', popup); @@ -403,6 +403,8 @@ self.showMenu = function (options) { ApiClient.getSessions(sessionQuery).done(function (sessions) { + console.log("showMenu", sessions); + showMenu(sessions, options); }); From b7e743b6a433e1fe15a9f60e216d8f412644866e Mon Sep 17 00:00:00 2001 From: Tim Hobbs Date: Mon, 7 Apr 2014 12:12:16 -0700 Subject: [PATCH 02/15] Undo some changes --- ApiClient.js | 22 +++++++++++----------- dashboard-ui/scripts/chromecast.js | 5 +++-- dashboard-ui/scripts/mediaplayer.js | 2 +- 3 files changed, 15 insertions(+), 14 deletions(-) diff --git a/ApiClient.js b/ApiClient.js index 1a526d8a18..74f989fc07 100644 --- a/ApiClient.js +++ b/ApiClient.js @@ -3846,7 +3846,7 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout, wi * @param {String} userId * @param {String} itemId */ - self.reportPlaybackProgress = function (userId, itemId, mediaSourceId, params) { + self.reportPlaybackProgress = function (userId, itemId, mediaSourceId, positionTicks, isPaused, isMuted) { if (!userId) { throw new Error("null userId"); @@ -3867,9 +3867,9 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout, wi msgData += "|" + (params.isMuted == null ? "" : params.isMuted); msgData += "|" + (mediaSourceId == null ? "" : mediaSourceId); - msgData += "|" + (params.audioStreamIndex == null ? "" : params.audioStreamIndex); - msgData += "|" + (params.subtitleStreamIndex == null ? "" : params.subtitleStreamIndex); - msgData += "|" + (params.volumeLevel == null ? "" : params.volumeLevel); + ////msgData += "|" + (params.audioStreamIndex == null ? "" : params.audioStreamIndex); + ////msgData += "|" + (params.subtitleStreamIndex == null ? "" : params.subtitleStreamIndex); + ////msgData += "|" + (params.volumeLevel == null ? "" : params.volumeLevel); self.sendWebSocketMessage("PlaybackProgress", msgData); @@ -3877,14 +3877,14 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout, wi return deferred.promise(); } - ////var params = { - //// isPaused: isPaused, - //// isMuted: isMuted - ////}; + var params = { + isPaused: isPaused, + isMuted: isMuted + }; - ////if (positionTicks) { - //// params.positionTicks = positionTicks; - ////} + if (positionTicks) { + params.positionTicks = positionTicks; + } if (mediaSourceId) { params.mediaSourceId = mediaSourceId; diff --git a/dashboard-ui/scripts/chromecast.js b/dashboard-ui/scripts/chromecast.js index 4c0c593337..d233949fbc 100644 --- a/dashboard-ui/scripts/chromecast.js +++ b/dashboard-ui/scripts/chromecast.js @@ -643,8 +643,9 @@ this.castPlayerState = PLAYER_STATE.LOADING; this.session.loadMedia(request, - this.onMediaDiscovered.bind(this, 'loadMedia'), - this.onLoadMediaError.bind(this)); + this.onMediaDiscovered.bind(this, 'loadMedia'), + this.onLoadMediaError.bind(this) + ); }; /** diff --git a/dashboard-ui/scripts/mediaplayer.js b/dashboard-ui/scripts/mediaplayer.js index 5c67d97987..516c10cde1 100644 --- a/dashboard-ui/scripts/mediaplayer.js +++ b/dashboard-ui/scripts/mediaplayer.js @@ -961,7 +961,7 @@ function sendProgressUpdate(itemId, mediaSourceId) { - ApiClient.reportPlaybackProgress(Dashboard.getCurrentUserId(), itemId, mediaSourceId, { positionTicks: self.getCurrentTicks(), isPaused: currentMediaElement.paused, isMuted: currentMediaElement.volume == 0 }); + ApiClient.reportPlaybackProgress(Dashboard.getCurrentUserId(), itemId, mediaSourceId, self.getCurrentTicks(), currentMediaElement.paused, currentMediaElement.volume == 0); } function clearProgressInterval() { From 2365becc148fa8a1c519507b6ec7a3b21d530dc6 Mon Sep 17 00:00:00 2001 From: Tim Hobbs Date: Tue, 8 Apr 2014 11:22:03 -0700 Subject: [PATCH 03/15] Enabled lazy load of poster images This should make initial load of the movies page a little more responsive for larger libraries. This can be used on any page loading images. --- dashboard-ui/css/librarybrowser.css | 5 ++ dashboard-ui/scripts/librarybrowser.js | 13 +++- dashboard-ui/scripts/movies.js | 11 +++- .../thirdparty/jquery.unveil-custom.js | 62 +++++++++++++++++++ 4 files changed, 88 insertions(+), 3 deletions(-) create mode 100644 dashboard-ui/thirdparty/jquery.unveil-custom.js diff --git a/dashboard-ui/css/librarybrowser.css b/dashboard-ui/css/librarybrowser.css index a65d8a37dc..e97f9d215b 100644 --- a/dashboard-ui/css/librarybrowser.css +++ b/dashboard-ui/css/librarybrowser.css @@ -1144,6 +1144,11 @@ a.itemTag:hover { margin-left: .5em; } +.lazy { + opacity: 0; + transition: opacity .3s ease-in; +} + @media all and (min-height: 500px) { .alphabetPicker { diff --git a/dashboard-ui/scripts/librarybrowser.js b/dashboard-ui/scripts/librarybrowser.js index 6c18da00f3..c94937596f 100644 --- a/dashboard-ui/scripts/librarybrowser.js +++ b/dashboard-ui/scripts/librarybrowser.js @@ -433,6 +433,8 @@ options.shape = options.shape || "portrait"; + options.lazy = options.lazy || false; + var html = ""; var primaryImageAspectRatio = options.shape == 'auto' ? LibraryBrowser.getAveragePrimaryImageAspectRatio(items) : null; @@ -668,7 +670,7 @@ var style = ""; - if (imgUrl) { + if (imgUrl && !options.lazy) { style += 'background-image:url(\'' + imgUrl + '\');'; } @@ -681,9 +683,16 @@ imageCssClass += " coveredPosterItemImage"; } + var dataSrc = ""; + + if (options.lazy) { + imageCssClass += " lazy"; + dataSrc = ' data-src="' + imgUrl + '"'; + } + var progressHtml = options.showProgress === false ? '' : LibraryBrowser.getItemProgressBarHtml(item); - html += '
'; + html += '
'; html += '
'; diff --git a/dashboard-ui/scripts/movies.js b/dashboard-ui/scripts/movies.js index 4f44bbe16b..4e0a2dc72c 100644 --- a/dashboard-ui/scripts/movies.js +++ b/dashboard-ui/scripts/movies.js @@ -56,7 +56,8 @@ context: 'movies', showTitle: true, centerText: true, - selectionPanel: true + selectionPanel: true, + lazy: true }); $('.itemsContainer', page).removeClass('timelineItemsContainer'); } @@ -383,4 +384,12 @@ updateFilterControls(this); }); + $(function () { + $("body").on("create", function () { + $(".lazy").unveil(200, function () { + this.style.opacity = 1; + }); + }); + }); + })(jQuery, document); \ No newline at end of file diff --git a/dashboard-ui/thirdparty/jquery.unveil-custom.js b/dashboard-ui/thirdparty/jquery.unveil-custom.js new file mode 100644 index 0000000000..79f275a309 --- /dev/null +++ b/dashboard-ui/thirdparty/jquery.unveil-custom.js @@ -0,0 +1,62 @@ +/** + * jQuery Unveil + * A very lightweight jQuery plugin to lazy load images + * http://luis-almeida.github.com/unveil + * + * Licensed under the MIT license. + * Copyright 2013 Luís Almeida + * https://github.com/luis-almeida + */ + +; (function ($) { + + $.fn.unveil = function (threshold, callback) { + + var $w = $(window), + th = threshold || 0, + retina = window.devicePixelRatio > 1, + attrib = retina ? "data-src-retina" : "data-src", + images = this, + loaded; + + this.one("unveil", function () { + var elemType = $(this).get(0).tagName; + var source = this.getAttribute(attrib); + source = source || this.getAttribute("data-src"); + if (source) { + if (elemType === "DIV") { + this.style.backgroundImage = "url('" + source + "')"; + } else { + this.setAttribute("src", source); + } + if (typeof callback === "function") callback.call(this); + } + }); + + function unveil() { + var inview = images.filter(function () { + var $e = $(this); + if ($e.is(":hidden")) return; + + var wt = $w.scrollTop(), + wb = wt + $w.height(), + et = $e.offset().top, + eb = et + $e.height(); + + return eb >= wt - th && et <= wb + th; + }); + + loaded = inview.trigger("unveil"); + images = images.not(loaded); + } + + $w.scroll(unveil); + $w.resize(unveil); + + unveil(); + + return this; + + }; + +})(window.jQuery || window.Zepto); \ No newline at end of file From cfe02b9cfe8bf02c43294784e46d28e7d5ef879a Mon Sep 17 00:00:00 2001 From: Tim Hobbs Date: Tue, 8 Apr 2014 11:25:45 -0700 Subject: [PATCH 04/15] Removed api client changes --- ApiClient.js | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/ApiClient.js b/ApiClient.js index dbc7ca6d75..6109faf91b 100644 --- a/ApiClient.js +++ b/ApiClient.js @@ -3865,15 +3865,11 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout, wi var msgData = itemId; - msgData += "|" + (params.positionTicks == null ? "" : params.positionTicks); - msgData += "|" + (params.isPaused == null ? "" : params.isPaused); - msgData += "|" + (params.isMuted == null ? "" : params.isMuted); + msgData += "|" + (positionTicks == null ? "" : params.positionTicks); + msgData += "|" + (isPaused == null ? "" : params.isPaused); + msgData += "|" + (isMuted == null ? "" : params.isMuted); msgData += "|" + (mediaSourceId == null ? "" : mediaSourceId); - ////msgData += "|" + (params.audioStreamIndex == null ? "" : params.audioStreamIndex); - ////msgData += "|" + (params.subtitleStreamIndex == null ? "" : params.subtitleStreamIndex); - ////msgData += "|" + (params.volumeLevel == null ? "" : params.volumeLevel); - self.sendWebSocketMessage("PlaybackProgress", msgData); deferred.resolveWith(null, []); From 95686255f5127e6cc5164b913dbfd566de1b24f0 Mon Sep 17 00:00:00 2001 From: Tim Hobbs Date: Tue, 8 Apr 2014 11:28:25 -0700 Subject: [PATCH 05/15] Removed api client changes --- ApiClient.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ApiClient.js b/ApiClient.js index 6109faf91b..a4c4d97830 100644 --- a/ApiClient.js +++ b/ApiClient.js @@ -3865,9 +3865,9 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout, wi var msgData = itemId; - msgData += "|" + (positionTicks == null ? "" : params.positionTicks); - msgData += "|" + (isPaused == null ? "" : params.isPaused); - msgData += "|" + (isMuted == null ? "" : params.isMuted); + msgData += "|" + (positionTicks == null ? "" : positionTicks); + msgData += "|" + (isPaused == null ? "" : isPaused); + msgData += "|" + (isMuted == null ? "" : isMuted); msgData += "|" + (mediaSourceId == null ? "" : mediaSourceId); self.sendWebSocketMessage("PlaybackProgress", msgData); From afcaea8b0cc9aee9e103b81a507a8ab1b8ead6af Mon Sep 17 00:00:00 2001 From: Tim Hobbs Date: Tue, 8 Apr 2014 11:32:03 -0700 Subject: [PATCH 06/15] Undo changes --- dashboard-ui/scripts/chromecast.js | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/dashboard-ui/scripts/chromecast.js b/dashboard-ui/scripts/chromecast.js index d233949fbc..bca82b8327 100644 --- a/dashboard-ui/scripts/chromecast.js +++ b/dashboard-ui/scripts/chromecast.js @@ -89,7 +89,7 @@ // v1 Id AE4DA10A // v2 Id 472F0435 - var applicationID = '472F0435'; + var applicationID = 'AE4DA10A'; // request session var sessionRequest = new chrome.cast.SessionRequest(applicationID); @@ -429,7 +429,7 @@ }; } - function getCustomData(item, mediaSourceId, startTimeTicks, audioStreamIndex, subtitleStreamIndex) { + function getCustomData(item, mediaSourceId, startTimeTicks) { return { @@ -438,9 +438,7 @@ userId: Dashboard.getCurrentUserId(), deviceName: ApiClient.deviceName(), //deviceId: ApiClient.deviceId(), - startTimeTicks: startTimeTicks || 0, - audioStreamIndex: audioStreamIndex, - subtitleStreamIndex: subtitleStreamIndex + startTimeTicks: startTimeTicks || 0 }; } @@ -626,7 +624,7 @@ var castMediaInfo = new chrome.cast.media.MediaInfo(streamUrl); - castMediaInfo.customData = getCustomData(item, mediaInfo.mediaSource.Id, startTimeTicks, audioStreamIndex, subtitleStreamIndex); + castMediaInfo.customData = getCustomData(item, mediaInfo.mediaSource.Id, startTimeTicks); castMediaInfo.metadata = getMetadata(item); if (mediaInfo.streamContainer == 'm3u8') { @@ -643,9 +641,8 @@ this.castPlayerState = PLAYER_STATE.LOADING; this.session.loadMedia(request, - this.onMediaDiscovered.bind(this, 'loadMedia'), - this.onLoadMediaError.bind(this) - ); + this.onMediaDiscovered.bind(this, 'loadMedia'), + this.onLoadMediaError.bind(this)); }; /** From e4e14908b8ad9f2befaa66b56a9b96a9cdd46800 Mon Sep 17 00:00:00 2001 From: Tim Hobbs Date: Tue, 8 Apr 2014 11:33:25 -0700 Subject: [PATCH 07/15] Undo changes --- dashboard-ui/scripts/remotecontrol.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/dashboard-ui/scripts/remotecontrol.js b/dashboard-ui/scripts/remotecontrol.js index ba622794f6..6df582fb44 100644 --- a/dashboard-ui/scripts/remotecontrol.js +++ b/dashboard-ui/scripts/remotecontrol.js @@ -349,7 +349,7 @@ // don't display the current session sessions = sessions.filter(function (s) { - return s.DeviceId != deviceId && (s.SupportsRemoteControl || s.Client == "Chromecast"); + return s.DeviceId != deviceId && (s.SupportsRemoteControl); }); var elem = $('#selectSession', popup); @@ -403,8 +403,6 @@ self.showMenu = function (options) { ApiClient.getSessions(sessionQuery).done(function (sessions) { - console.log("showMenu", sessions); - showMenu(sessions, options); }); From 874c0ce0272974c6e5650425169bebcc24ffe945 Mon Sep 17 00:00:00 2001 From: Tim Hobbs Date: Tue, 8 Apr 2014 11:34:49 -0700 Subject: [PATCH 08/15] Undo changes --- dashboard-ui/scripts/remotecontrol.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dashboard-ui/scripts/remotecontrol.js b/dashboard-ui/scripts/remotecontrol.js index 6df582fb44..35add5b195 100644 --- a/dashboard-ui/scripts/remotecontrol.js +++ b/dashboard-ui/scripts/remotecontrol.js @@ -349,7 +349,7 @@ // don't display the current session sessions = sessions.filter(function (s) { - return s.DeviceId != deviceId && (s.SupportsRemoteControl); + return s.DeviceId != deviceId && s.SupportsRemoteControl; }); var elem = $('#selectSession', popup); From 32c1badcd9b5f795ae5df1e7bacdcca9e1129449 Mon Sep 17 00:00:00 2001 From: Tim Hobbs Date: Tue, 8 Apr 2014 16:12:09 -0700 Subject: [PATCH 09/15] Video fixes Variable collision caused some issues - sorted --- dashboard-ui/scripts/mediaplayer-video.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/dashboard-ui/scripts/mediaplayer-video.js b/dashboard-ui/scripts/mediaplayer-video.js index 5569a461e2..9d7c1a93df 100644 --- a/dashboard-ui/scripts/mediaplayer-video.js +++ b/dashboard-ui/scripts/mediaplayer-video.js @@ -872,18 +872,18 @@ $('#video-fullscreenButton', videoControls).show(); } - var videoElement = $("video", videoElement); + var video = $("video", videoElement); initialVolume = localStorage.getItem("volume") || 0.5; - videoElement.each(function () { + video.each(function () { this.volume = initialVolume; }); self.volumeSlider.val(initialVolume).slider('refresh'); self.updateVolumeButtons(initialVolume); - videoElement.on("volumechange", function (e) { + video.on("volumechange", function (e) { var muted = this.muted; @@ -899,13 +899,13 @@ }).on("play.once", function () { - videoElement.off("play.once"); + video.off("play.once"); }).on("playing.once", function () { self.updateCanClientSeek(this); - videoElement.off("playing.once"); + video.off("playing.once"); ApiClient.reportPlaybackStart(Dashboard.getCurrentUserId(), item.Id, mediaSource.Id, true, item.MediaType); @@ -1037,7 +1037,7 @@ currentItem = item; currentMediaSource = mediaSource; - return videoElement[0]; + return video[0]; } }; })(); \ No newline at end of file From a500f154d8c4f22c25fb7276e13de434c08f3b35 Mon Sep 17 00:00:00 2001 From: Tim Hobbs Date: Wed, 9 Apr 2014 16:58:09 -0700 Subject: [PATCH 10/15] Cast updates --- dashboard-ui/css/mediaplayer-video.css | 4 +- dashboard-ui/scripts/chromecast.js | 197 +++++++++++++++++------- dashboard-ui/scripts/mediacontroller.js | 16 ++ dashboard-ui/scripts/site.js | 28 ++-- 4 files changed, 176 insertions(+), 69 deletions(-) diff --git a/dashboard-ui/css/mediaplayer-video.css b/dashboard-ui/css/mediaplayer-video.css index 14f8b7b40d..814bc66e39 100644 --- a/dashboard-ui/css/mediaplayer-video.css +++ b/dashboard-ui/css/mediaplayer-video.css @@ -139,7 +139,7 @@ text-align: left; margin-left: 0; right: 0; - bottom: 83px; + bottom: 85px; } /* Media queries @@ -259,7 +259,7 @@ } #mediaPlayer #videoControls .mediaPlayerFlyout { - bottom: 148px; + bottom: 150px; } } diff --git a/dashboard-ui/scripts/chromecast.js b/dashboard-ui/scripts/chromecast.js index bca82b8327..1df3d958cd 100644 --- a/dashboard-ui/scripts/chromecast.js +++ b/dashboard-ui/scripts/chromecast.js @@ -66,6 +66,17 @@ this.hasReceivers = false; + this.currentMediaOffset = 0; + + // Progress bar element id + this.progressBar = "positionSlider"; + + // Timec display element id + this.duration = "currentTime"; + + // Playback display element id + this.playback = "playTime"; + this.initializeCastPlayer(); }; @@ -617,6 +628,8 @@ return; } + this.currentMediaOffset = startTimeTicks; + var maxBitrate = 12000000; var mediaInfo = getMediaSourceInfo(user, item, maxBitrate, mediaSourceId, audioStreamIndex, subtitleStreamIndex); @@ -653,40 +666,62 @@ console.log("chromecast new media session ID:" + mediaSession.mediaSessionId + ' (' + how + ')'); this.currentMediaSession = mediaSession; + this.currentMediaTime = this.session.media[0].currentTime; + if (how == 'loadMedia') { this.castPlayerState = PLAYER_STATE.PLAYING; + clearInterval(this.timer); + this.startProgressTimer(this.incrementMediaTime); } if (how == 'activeSession') { this.castPlayerState = this.session.media[0].playerState; - this.currentMediaTime = this.session.media[0].currentTime; } if (this.castPlayerState == PLAYER_STATE.PLAYING) { // start progress timer - //this.startProgressTimer(this.incrementMediaTime); + this.startProgressTimer(this.incrementMediaTime); } this.currentMediaSession.addUpdateListener(this.onMediaStatusUpdate.bind(this)); + this.currentMediaDuration = this.currentMediaSession.media.duration; - //this.currentMediaDuration = this.currentMediaSession.media.duration; - //var duration = this.currentMediaDuration; - //var hr = parseInt(duration / 3600); - //duration -= hr * 3600; - //var min = parseInt(duration / 60); - //var sec = parseInt(duration % 60); - //if (hr > 0) { - // duration = hr + ":" + min + ":" + sec; - //} - //else { - // if (min > 0) { - // duration = min + ":" + sec; - // } - // else { - // duration = sec; - // } - //} - //document.getElementById("duration").innerHTML = duration; + var playTime = document.getElementById(this.playback); + if (!playTime) { + // Set duration time + var totalTime = document.getElementById(this.duration); + totalTime.innerHTML = " / " + formatTime(this.currentMediaDuration); + + // Set play time + playTime = document.createElement("div"); + playTime.id = this.playback; + playTime.className = "currentTime"; + playTime.style.marginRight = "5px"; + totalTime.parentNode.insertBefore(playTime, totalTime); + playTime.innerHTML = formatTime(this.currentMediaTime); + } + }; + + function formatTime(duration) { + var hr = parseInt(duration / 3600); + duration -= hr * 3600; + var min = parseInt(duration / 60); + var sec = parseInt(duration % 60); + + hr = "" + hr; + min = "" + min; + sec = "" + sec; + var hh = pad(hr); + var mm = pad(min); + var ss = pad(sec); + + duration = hh + ":" + mm + ":" + ss; + + return duration; + }; + + function pad(s) { + return "00".substring(0, 2 - s.length) + s; }; /** @@ -707,7 +742,7 @@ this.castPlayerState = PLAYER_STATE.IDLE; } console.log("chromecast updating media"); - //this.updateProgressBar(e); + this.updateProgressBarByTimer(); }; /** @@ -745,6 +780,7 @@ this.currentMediaSession.addUpdateListener(this.onMediaStatusUpdate.bind(this)); this.castPlayerState = PLAYER_STATE.PLAYING; // start progress timer + clearInterval(this.timer); this.startProgressTimer(this.incrementMediaTime); break; case PLAYER_STATE.IDLE: @@ -850,18 +886,9 @@ */ CastPlayer.prototype.seekMedia = function (event) { var pos = parseInt(event.offsetX); - var pi = document.getElementById("progress_indicator"); - var p = document.getElementById("progress"); - if (event.currentTarget.id == 'progress_indicator') { - var curr = parseInt(this.currentMediaTime + this.currentMediaDuration * pos / PROGRESS_BAR_WIDTH); - var pp = parseInt(pi.style.marginLeft) + pos; - var pw = parseInt(p.style.width) + pos; - } - else { - var curr = parseInt(pos * this.currentMediaDuration / PROGRESS_BAR_WIDTH); - var pp = pos - 21 - PROGRESS_BAR_WIDTH; - var pw = pos; - } + var p = document.getElementById(this.progressBar); + var curr = parseInt(this.currentMediaTime + this.currentMediaDuration * pos); + var pw = parseInt(p.value) + pos; if (this.castPlayerState != PLAYER_STATE.PLAYING && this.castPlayerState != PLAYER_STATE.PAUSED) { return; @@ -899,20 +926,16 @@ * @param {Object} e An media status update object */ CastPlayer.prototype.updateProgressBar = function (e) { - var p = document.getElementById("progress"); - var pi = document.getElementById("progress_indicator"); + var p = document.getElementById(this.progressBar); if (e.idleReason == 'FINISHED' && e.playerState == 'IDLE') { - p.style.width = '0px'; - pi.style.marginLeft = -21 - PROGRESS_BAR_WIDTH + 'px'; + p.value = 0; clearInterval(this.timer); this.castPlayerState = PLAYER_STATE.STOPPED; } else { - p.style.width = Math.ceil(PROGRESS_BAR_WIDTH * e.currentTime / this.currentMediaSession.media.duration + 1) + 'px'; + p.value = Number(e.currentTime / this.currentMediaSession.media.duration + 1).toFixed(3); this.progressFlag = false; setTimeout(this.setProgressFlag.bind(this), 1000); // don't update progress in 1 second - var pp = Math.ceil(PROGRESS_BAR_WIDTH * e.currentTime / this.currentMediaSession.media.duration); - pi.style.marginLeft = -21 - PROGRESS_BAR_WIDTH + pp + 'px'; } }; @@ -928,47 +951,73 @@ * Update progress bar based on timer */ CastPlayer.prototype.updateProgressBarByTimer = function () { - var p = document.getElementById("progress"); - if (isNaN(parseInt(p.style.width))) { - p.style.width = 0; + var p = document.getElementById(this.progressBar); + if (isNaN(parseInt(p.value))) { + p.value = 0; } + if (this.currentMediaDuration > 0) { - var pp = Math.floor(PROGRESS_BAR_WIDTH * this.currentMediaTime / this.currentMediaDuration); + var pp = Number(this.currentMediaTime / this.currentMediaDuration).toFixed(3); + var startTime = this.currentMediaOffset / 10000000; + document.getElementById(this.playback).innerHTML = formatTime(startTime + this.currentMediaTime); } if (this.progressFlag) { // don't update progress if it's been updated on media status update event - p.style.width = pp + 'px'; - var pi = document.getElementById("progress_indicator"); - pi.style.marginLeft = -21 - PROGRESS_BAR_WIDTH + pp + 'px'; + p.value = pp; } - if (pp > PROGRESS_BAR_WIDTH) { + if (pp > 100) { clearInterval(this.timer); this.deviceState = DEVICE_STATE.IDLE; this.castPlayerState = PLAYER_STATE.IDLE; } }; + /** + * @param {function} A callback function for the fucntion to start timer + */ + CastPlayer.prototype.startProgressTimer = function(callback) { + if(this.timer) { + clearInterval(this.timer); + this.timer = null; + } + + // start progress timer + this.timer = setInterval(callback.bind(this), this.timerStep); + }; + var castPlayer = new CastPlayer(); - function chromecastPlayer() { + function chromecastPlayer(castPlayer) { var self = this; self.name = PlayerName; + self.isPaused = false; + self.play = function (options) { - if (options.items) { + $("#nowPlayingBar", "#footer").show(); + if (self.isPaused) { + + console.log("unpause"); + self.isPaused = !self.isPaused; + castPlayer.playMedia(); + + } else if (options.items) { + + console.log("play1", options); Dashboard.getCurrentUser().done(function (user) { - castPlayer.loadMedia(user, options.items[0], options.startTimeTicks); + castPlayer.loadMedia(user, options.items[0], options.startPositionTicks); }); } else { + console.log("play2"); var userId = Dashboard.getCurrentUserId(); var query = {}; @@ -988,6 +1037,18 @@ }; + self.unpause = function () { + console.log("unpause"); + self.isPaused = !self.isPaused; + castPlayer.playMedia(); + }; + + self.pause = function () { + console.log("pause"); + self.isPaused = true; + castPlayer.pauseMedia(); + }; + self.shuffle = function (id) { self.play({ ids: [id] }); }; @@ -1005,7 +1066,9 @@ }; self.stop = function () { - castPlayer.stop(); + console.log("stop"); + $("#nowPlayingBar", "#footer").hide(); + castPlayer.stopMedia(); }; self.canQueueMediaType = function (mediaType) { @@ -1051,9 +1114,37 @@ appName: appName }; }; + + self.setCurrentTime = function (ticks, item, updateSlider) { + + // Convert to ticks + ticks = Math.floor(ticks); + + var timeText = Dashboard.getDisplayTime(ticks); + + if (self.currentDurationTicks) { + + timeText += " / " + Dashboard.getDisplayTime(self.currentDurationTicks); + + if (updateSlider) { + var percent = ticks / self.currentDurationTicks; + percent *= 100; + + self.positionSlider.val(percent).slider('enable').slider('refresh'); + } + } else { + self.positionSlider.slider('disable').slider('refresh'); + } + + self.currentTimeElement.html(timeText); + }; + + self.changeStream = function (position) { + console.log("seek", position); + }; } - MediaController.registerPlayer(new chromecastPlayer()); + MediaController.registerPlayer(new chromecastPlayer(castPlayer)); $(MediaController).on('playerchange', function () { diff --git a/dashboard-ui/scripts/mediacontroller.js b/dashboard-ui/scripts/mediacontroller.js index 074bc880f1..3c9bd1f1af 100644 --- a/dashboard-ui/scripts/mediacontroller.js +++ b/dashboard-ui/scripts/mediacontroller.js @@ -174,6 +174,22 @@ return p.isDefaultPlayer; })[0]; }; + + self.pause = function () { + currentPlayer.pause(); + }; + + self.stop = function () { + currentPlayer.stop(); + }; + + self.unpause = function () { + currentPlayer.unpause(); + }; + + self.seek = function (position) { + self.changeStream(position); + }; } window.MediaController = new mediaController(); diff --git a/dashboard-ui/scripts/site.js b/dashboard-ui/scripts/site.js index 79b298ef05..477ba60119 100644 --- a/dashboard-ui/scripts/site.js +++ b/dashboard-ui/scripts/site.js @@ -1330,42 +1330,42 @@ $(function () { footerHtml += '
'; footerHtml += '
'; footerHtml += 'Playlist'; - footerHtml += ''; - footerHtml += ''; - footerHtml += ''; + footerHtml += ''; + footerHtml += ''; + footerHtml += ''; footerHtml += '
'; - footerHtml += ''; - footerHtml += ''; + footerHtml += ''; + footerHtml += ''; footerHtml += '
'; - footerHtml += ''; + footerHtml += ''; footerHtml += '
'; - footerHtml += '
'; + footerHtml += '
'; footerHtml += '
'; - footerHtml += ''; - footerHtml += ''; + footerHtml += ''; + footerHtml += ''; footerHtml += '
'; footerHtml += ''; footerHtml += '
'; - footerHtml += ''; + footerHtml += ''; footerHtml += '
'; - footerHtml += ''; + footerHtml += ''; footerHtml += '
'; - footerHtml += ''; + footerHtml += ''; footerHtml += '
'; - footerHtml += ''; + footerHtml += ''; footerHtml += '
'; - footerHtml += ''; + footerHtml += ''; footerHtml += '
'; From f893f175cb952901f2731f19cd812efdc56aaaf5 Mon Sep 17 00:00:00 2001 From: Tim Hobbs Date: Wed, 9 Apr 2014 20:47:57 -0700 Subject: [PATCH 11/15] Lazy loading enabled All pages with lots of images now have lazy loading enabled. The "animation" was removed since it made for a bit of an odd experience one the 2nd visit to a page. On pages with lots of images this has a nice impact as it only loads the images that the user can see initially. As a user scrolls it loads the next ones up. It saves a lot of extra requests initially. --- dashboard-ui/css/librarybrowser.css | 5 ----- dashboard-ui/scripts/episodes.js | 3 ++- dashboard-ui/scripts/moviecollections.js | 3 ++- dashboard-ui/scripts/moviepeople.js | 3 ++- dashboard-ui/scripts/movies.js | 17 ++++++----------- dashboard-ui/scripts/musicalbumartists.js | 3 ++- dashboard-ui/scripts/musicartists.js | 9 ++++++++- dashboard-ui/scripts/musicgenres.js | 7 ++++--- dashboard-ui/scripts/site.js | 7 +++++-- dashboard-ui/scripts/tvpeople.js | 3 ++- dashboard-ui/scripts/tvshows.js | 12 ++++++++---- 11 files changed, 41 insertions(+), 31 deletions(-) diff --git a/dashboard-ui/css/librarybrowser.css b/dashboard-ui/css/librarybrowser.css index 1defe8d9b1..cbb232c96d 100644 --- a/dashboard-ui/css/librarybrowser.css +++ b/dashboard-ui/css/librarybrowser.css @@ -1144,11 +1144,6 @@ a.itemTag:hover { margin-left: .5em; } -.lazy { - opacity: 0; - transition: opacity .3s ease-in; -} - @media all and (min-height: 500px) { .alphabetPicker { diff --git a/dashboard-ui/scripts/episodes.js b/dashboard-ui/scripts/episodes.js index d596716ffc..6f2dc42e30 100644 --- a/dashboard-ui/scripts/episodes.js +++ b/dashboard-ui/scripts/episodes.js @@ -34,7 +34,8 @@ showTitle: true, showParentTitle: true, overlayText: true, - selectionPanel: true + selectionPanel: true, + lazy: true }); $('.itemsContainer', page).removeClass('timelineItemsContainer'); diff --git a/dashboard-ui/scripts/moviecollections.js b/dashboard-ui/scripts/moviecollections.js index 8a04646073..8985316792 100644 --- a/dashboard-ui/scripts/moviecollections.js +++ b/dashboard-ui/scripts/moviecollections.js @@ -33,7 +33,8 @@ shape: "portrait", context: 'movies', showTitle: true, - centerText: true + centerText: true, + lazy: true }); html += LibraryBrowser.getPagingHtml(query, result.TotalRecordCount); diff --git a/dashboard-ui/scripts/moviepeople.js b/dashboard-ui/scripts/moviepeople.js index 3e35afca1c..0ef500846b 100644 --- a/dashboard-ui/scripts/moviepeople.js +++ b/dashboard-ui/scripts/moviepeople.js @@ -34,7 +34,8 @@ context: 'movies', showTitle: true, showItemCounts: true, - coverImage: true + coverImage: true, + lazy: true }); html += LibraryBrowser.getPagingHtml(query, result.TotalRecordCount, false, [], false); diff --git a/dashboard-ui/scripts/movies.js b/dashboard-ui/scripts/movies.js index 4e0a2dc72c..e3d2689c8d 100644 --- a/dashboard-ui/scripts/movies.js +++ b/dashboard-ui/scripts/movies.js @@ -35,7 +35,8 @@ shape: "backdrop", preferThumb: true, context: 'movies', - selectionPanel: true + selectionPanel: true, + lazy: true }); $('.itemsContainer', page).removeClass('timelineItemsContainer'); } @@ -45,7 +46,8 @@ items: result.Items, shape: "banner", preferBanner: true, - context: 'movies' + context: 'movies', + lazy: true }); $('.itemsContainer', page).removeClass('timelineItemsContainer'); } @@ -69,7 +71,8 @@ showTitle: true, timeline: true, centerText: true, - selectionPanel: true + selectionPanel: true, + lazy: true }); $('.itemsContainer', page).addClass('timelineItemsContainer'); } @@ -384,12 +387,4 @@ updateFilterControls(this); }); - $(function () { - $("body").on("create", function () { - $(".lazy").unveil(200, function () { - this.style.opacity = 1; - }); - }); - }); - })(jQuery, document); \ No newline at end of file diff --git a/dashboard-ui/scripts/musicalbumartists.js b/dashboard-ui/scripts/musicalbumartists.js index 26f7b070b0..a53c140035 100644 --- a/dashboard-ui/scripts/musicalbumartists.js +++ b/dashboard-ui/scripts/musicalbumartists.js @@ -32,7 +32,8 @@ context: 'music', showTitle: true, coverImage: true, - centerText: true + centerText: true, + lazy: true }); html += LibraryBrowser.getPagingHtml(query, result.TotalRecordCount); diff --git a/dashboard-ui/scripts/musicartists.js b/dashboard-ui/scripts/musicartists.js index 60d2fe572a..142bfaa3b2 100644 --- a/dashboard-ui/scripts/musicartists.js +++ b/dashboard-ui/scripts/musicartists.js @@ -31,7 +31,8 @@ context: 'music', showTitle: true, coverImage: true, - centerText: true + centerText: true, + lazy: true }); html += LibraryBrowser.getPagingHtml(query, result.TotalRecordCount); @@ -119,4 +120,10 @@ updateFilterControls(this); }); + $(function () { + $("body").on("create", function () { + $(".lazy").unveil(200); + }); + }); + })(jQuery, document); \ No newline at end of file diff --git a/dashboard-ui/scripts/musicgenres.js b/dashboard-ui/scripts/musicgenres.js index 51e38e50f7..a0aa9331c4 100644 --- a/dashboard-ui/scripts/musicgenres.js +++ b/dashboard-ui/scripts/musicgenres.js @@ -32,7 +32,8 @@ preferThumb: true, context: 'music', showItemCounts: true, - centerText: true + centerText: true, + lazy: true }); html += LibraryBrowser.getPagingHtml(query, result.TotalRecordCount); @@ -55,8 +56,8 @@ reloadItems(page); }); - LibraryBrowser.saveQueryValues('musicgenres', query); - + LibraryBrowser.saveQueryValues('musicgenres', query); + Dashboard.hideLoadingMsg(); }); } diff --git a/dashboard-ui/scripts/site.js b/dashboard-ui/scripts/site.js index 72e192ac27..ddc4e42d00 100644 --- a/dashboard-ui/scripts/site.js +++ b/dashboard-ui/scripts/site.js @@ -1386,6 +1386,10 @@ $(function () { ApiClient.closeWebSocket(); } }); + + $("body").on("create", function () { + $(".lazy").unveil(200); + }); }); Dashboard.jQueryMobileInit(); @@ -1427,5 +1431,4 @@ $(document).on('pagebeforeshow', ".page", function () { if (!ApiClient.isWebSocketOpen()) { Dashboard.refreshSystemInfoFromServer(); } -}); - +}); \ No newline at end of file diff --git a/dashboard-ui/scripts/tvpeople.js b/dashboard-ui/scripts/tvpeople.js index e8a573ac9a..d434886be1 100644 --- a/dashboard-ui/scripts/tvpeople.js +++ b/dashboard-ui/scripts/tvpeople.js @@ -34,7 +34,8 @@ context: 'tv', showTitle: true, showItemCounts: true, - coverImage: true + coverImage: true, + lazy: true }); html += LibraryBrowser.getPagingHtml(query, result.TotalRecordCount, false, [], false); diff --git a/dashboard-ui/scripts/tvshows.js b/dashboard-ui/scripts/tvshows.js index 2e751fa82e..96635ea906 100644 --- a/dashboard-ui/scripts/tvshows.js +++ b/dashboard-ui/scripts/tvshows.js @@ -34,7 +34,8 @@ items: result.Items, shape: "backdrop", preferThumb: true, - context: 'tv' + context: 'tv', + lazy: true }); $('.itemsContainer', page).removeClass('timelineItemsContainer'); @@ -45,7 +46,8 @@ items: result.Items, shape: "banner", preferBanner: true, - context: 'tv' + context: 'tv', + lazy: true }); $('.itemsContainer', page).removeClass('timelineItemsContainer'); } @@ -56,7 +58,8 @@ shape: "portrait", context: 'tv', showTitle: true, - centerText: true + centerText: true, + lazy: true }); $('.itemsContainer', page).removeClass('timelineItemsContainer'); } @@ -67,7 +70,8 @@ shape: "portrait", context: 'tv', timeline: true, - showTitle: true + showTitle: true, + lazy: true }); $('.itemsContainer', page).addClass('timelineItemsContainer'); From df87f66b8d352221cdeff988bc85eba79f2f2ac1 Mon Sep 17 00:00:00 2001 From: Tim Hobbs Date: Wed, 9 Apr 2014 20:48:51 -0700 Subject: [PATCH 12/15] "People" fix People that aren't in a file/show had an empty line that resulted in staggered layout, so I added a non-breaking space for consistent layout --- dashboard-ui/scripts/librarybrowser.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/dashboard-ui/scripts/librarybrowser.js b/dashboard-ui/scripts/librarybrowser.js index c94937596f..3220196744 100644 --- a/dashboard-ui/scripts/librarybrowser.js +++ b/dashboard-ui/scripts/librarybrowser.js @@ -765,6 +765,10 @@ var itemCountHtml = LibraryBrowser.getItemCountsHtml(options, item); + if (item.Type == "Person" && !itemCountHtml) { + itemCountHtml = " "; + } + if (itemCountHtml) { html += "
"; html += itemCountHtml; From e8084be0db8e60e278e00efa29431344b65a651b Mon Sep 17 00:00:00 2001 From: Tim Hobbs Date: Wed, 9 Apr 2014 21:12:04 -0700 Subject: [PATCH 13/15] More cast and lazy load updates --- dashboard-ui/scripts/mediacontroller.js | 8 ++++++++ dashboard-ui/scripts/musicalbums.js | 6 ++++-- dashboard-ui/scripts/playlist.js | 4 ++-- 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/dashboard-ui/scripts/mediacontroller.js b/dashboard-ui/scripts/mediacontroller.js index 3c9bd1f1af..e7222338dc 100644 --- a/dashboard-ui/scripts/mediacontroller.js +++ b/dashboard-ui/scripts/mediacontroller.js @@ -190,6 +190,14 @@ self.seek = function (position) { self.changeStream(position); }; + + self.currentPlaylistIndex = function (i) { + self.currentPlaylistIndex(i); + }; + + self.removeFromPlaylist = function (i) { + self.removeFromPlaylist(i); + }; } window.MediaController = new mediaController(); diff --git a/dashboard-ui/scripts/musicalbums.js b/dashboard-ui/scripts/musicalbums.js index 523835ba29..003e046761 100644 --- a/dashboard-ui/scripts/musicalbums.js +++ b/dashboard-ui/scripts/musicalbums.js @@ -34,7 +34,8 @@ shape: "square", context: 'music', showTitle: true, - showParentTitle: true + showParentTitle: true, + lazy: true }); $('.itemsContainer', page).removeClass('timelineItemsContainer'); } @@ -45,7 +46,8 @@ context: 'music', showTitle: true, showParentTitle: true, - timeline: true + timeline: true, + lazy: true }); $('.itemsContainer', page).addClass('timelineItemsContainer'); } diff --git a/dashboard-ui/scripts/playlist.js b/dashboard-ui/scripts/playlist.js index 187767152d..10c6b2d9bf 100644 --- a/dashboard-ui/scripts/playlist.js +++ b/dashboard-ui/scripts/playlist.js @@ -65,14 +65,14 @@ var index = parseInt(this.getAttribute('data-index')); - MediaPlayer.currentPlaylistIndex(index); + MediaController.currentPlaylistIndex(index); reloadPlaylist(page); }).on('click', '.lnkRemove', function () { var index = parseInt(this.getAttribute('data-index')); - MediaPlayer.removeFromPlaylist(index); + MediaController.removeFromPlaylist(index); reloadPlaylist(page); }); From dc2fc2f857370a1b3061228eccd7696dcbb2bead Mon Sep 17 00:00:00 2001 From: Tim Hobbs Date: Wed, 9 Apr 2014 21:26:06 -0700 Subject: [PATCH 14/15] Cast playback time update --- dashboard-ui/scripts/chromecast.js | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/dashboard-ui/scripts/chromecast.js b/dashboard-ui/scripts/chromecast.js index 1df3d958cd..05121aa77a 100644 --- a/dashboard-ui/scripts/chromecast.js +++ b/dashboard-ui/scripts/chromecast.js @@ -628,7 +628,7 @@ return; } - this.currentMediaOffset = startTimeTicks; + this.currentMediaOffset = startTimeTicks || 0; var maxBitrate = 12000000; var mediaInfo = getMediaSourceInfo(user, item, maxBitrate, mediaSourceId, audioStreamIndex, subtitleStreamIndex); @@ -827,6 +827,10 @@ this.onError.bind(this)); this.castPlayerState = PLAYER_STATE.STOPPED; clearInterval(this.timer); + var playTime = document.getElementById(this.playback); + if (playTime) { + playTime.parentNode.removeChild(playTime); + } }; /** @@ -958,8 +962,12 @@ if (this.currentMediaDuration > 0) { var pp = Number(this.currentMediaTime / this.currentMediaDuration).toFixed(3); + var startTime = this.currentMediaOffset / 10000000; - document.getElementById(this.playback).innerHTML = formatTime(startTime + this.currentMediaTime); + var playTime = document.getElementById(this.playback); + if (playTime) { + playTime.innerHTML = formatTime(startTime + this.currentMediaTime); + } } if (this.progressFlag) { From 6805109fb400e4b0fcbf3ab4a8c6a63cc09b9984 Mon Sep 17 00:00:00 2001 From: Tim Hobbs Date: Thu, 10 Apr 2014 04:12:48 -0700 Subject: [PATCH 15/15] Cast audio playback enhancements * Instant mix works, but not sure about managing the mix * Shuffle _should_ work, but same thing with mgmt Receiver page has also been enhanced for music playback. --- dashboard-ui/scripts/chromecast.js | 314 +++++++++++++++--- dashboard-ui/scripts/mediacontroller.js | 38 ++- dashboard-ui/scripts/playlist.js | 2 +- .../thirdparty/jquery.ba-tinypubsub.min.js | 4 + 4 files changed, 307 insertions(+), 51 deletions(-) create mode 100644 dashboard-ui/thirdparty/jquery.ba-tinypubsub.min.js diff --git a/dashboard-ui/scripts/chromecast.js b/dashboard-ui/scripts/chromecast.js index 05121aa77a..51ad1ee90c 100644 --- a/dashboard-ui/scripts/chromecast.js +++ b/dashboard-ui/scripts/chromecast.js @@ -100,7 +100,7 @@ // v1 Id AE4DA10A // v2 Id 472F0435 - var applicationID = 'AE4DA10A'; + var applicationID = '472F0435'; // request session var sessionRequest = new chrome.cast.SessionRequest(applicationID); @@ -456,6 +456,8 @@ function getMetadata(item) { + console.log("md item", item); + var metadata = {}; if (item.Type == 'Episode') { @@ -523,7 +525,7 @@ else if (item.MediaType == 'Movie') { metadata = new chrome.cast.media.MovieMediaMetadata(); - metadata.type = chrome.cast.media.MetadataType.MUSIC_TRACK; + metadata.type = chrome.cast.media.MetadataType.MOVIE; if (item.ProductionYear) { metadata.releaseYear = item.ProductionYear; @@ -889,7 +891,7 @@ * @param {Event} e An event object from seek */ CastPlayer.prototype.seekMedia = function (event) { - var pos = parseInt(event.offsetX); + var pos = parseInt(event); var p = document.getElementById(this.progressBar); var curr = parseInt(this.currentMediaTime + this.currentMediaDuration * pos); var pw = parseInt(p.value) + pos; @@ -935,6 +937,10 @@ p.value = 0; clearInterval(this.timer); this.castPlayerState = PLAYER_STATE.STOPPED; + if (e.idleReason == 'FINISHED') { + $.publish("/playback/complete", e); + console.log("playback complete", e); + } } else { p.value = Number(e.currentTime / this.currentMediaSession.media.duration + 1).toFixed(3); @@ -975,10 +981,12 @@ p.value = pp; } - if (pp > 100) { + if (pp > 100 || this.castPlayerState == PLAYER_STATE.IDLE) { clearInterval(this.timer); this.deviceState = DEVICE_STATE.IDLE; this.castPlayerState = PLAYER_STATE.IDLE; + $.publish("/playback/complete", true); + console.log("playback complete"); } }; @@ -1001,31 +1009,111 @@ var self = this; + var isPositionSliderActive = false; + self.name = PlayerName; self.isPaused = false; + self.playlist = []; + + self.playlistIndex = 0; + + $.subscribe("/playback/complete", function (e) { + if (self.playlistIndex < (self.playlist.items || []).length) { + self.play(self.playlist); + } + }); + + ////$(".positionSlider", "#footer").off("slidestart slidestop") + //// .on('slidestart', function (e) { + //// isPositionSliderActive = true; + ////}).on('slidestop', positionSliderChange); + + ////function positionSliderChange() { + //// isPositionSliderActive = false; + //// var newPercent = parseInt(this.value); + //// self.changeStream(newPercent); + ////} + + function getItemsForPlayback(query) { + var userId = Dashboard.getCurrentUserId(); + + query.Limit = query.Limit || 100; + query.Fields = getItemFields; + query.ExcludeLocationTypes = "Virtual"; + + return ApiClient.getItems(userId, query); + }; + + function queueItems (items) { + for (var i = 0, length = items.length; i < length; i++) { + self.playlist.push(items[i]); + } + }; + + function queueItemsNext(items) { + var insertIndex = 1; + for (var i = 0, length = items.length; i < length; i++) { + self.playlist.splice(insertIndex, 0, items[i]); + insertIndex++; + } + }; + + function translateItemsForPlayback(items) { + var deferred = $.Deferred(); + var firstItem = items[0]; + var promise; + if (firstItem.IsFolder) { + promise = self.getItemsForPlayback({ + ParentId: firstItem.Id, + Filters: "IsNotFolder", + Recursive: true, + SortBy: "SortName", + MediaTypes: "Audio,Video" + }); + } + else if (firstItem.Type == "MusicArtist") { + promise = self.getItemsForPlayback({ + Artists: firstItem.Name, + Filters: "IsNotFolder", + Recursive: true, + SortBy: "SortName", + MediaTypes: "Audio" + }); + } + else if (firstItem.Type == "MusicGenre") { + promise = self.getItemsForPlayback({ + Genres: firstItem.Name, + Filters: "IsNotFolder", + Recursive: true, + SortBy: "SortName", + MediaTypes: "Audio" + }); + } + + if (promise) { + promise.done(function (result) { + deferred.resolveWith(null, [result.Items]); + }); + } else { + deferred.resolveWith(null, [items]); + } + + return deferred.promise(); + }; + self.play = function (options) { - + console.log("play", options); $("#nowPlayingBar", "#footer").show(); - if (self.isPaused) { - - console.log("unpause"); self.isPaused = !self.isPaused; castPlayer.playMedia(); - } else if (options.items) { - - console.log("play1", options); Dashboard.getCurrentUser().done(function (user) { - - castPlayer.loadMedia(user, options.items[0], options.startPositionTicks); + castPlayer.loadMedia(user, options.items[self.playlistIndex++], options.startPositionTicks); }); - } else { - - console.log("play2"); var userId = Dashboard.getCurrentUserId(); var query = {}; @@ -1035,14 +1123,10 @@ query.Ids = options.ids.join(','); ApiClient.getItems(userId, query).done(function (result) { - options.items = result.Items; - self.play(options); - }); } - }; self.unpause = function () { @@ -1058,37 +1142,135 @@ }; self.shuffle = function (id) { - self.play({ ids: [id] }); + var userId = Dashboard.getCurrentUserId(); + ApiClient.getItem(userId, id).done(function (item) { + var query = { + UserId: userId, + Fields: getItemFields, + Limit: 50, + Filters: "IsNotFolder", + Recursive: true, + SortBy: "Random" + }; + + if (item.IsFolder) { + query.ParentId = id; + } + else if (item.Type == "MusicArtist") { + query.MediaTypes = "Audio"; + query.Artists = item.Name; + } + else if (item.Type == "MusicGenre") { + query.MediaTypes = "Audio"; + query.Genres = item.Name; + } else { + return; + } + + self.getItemsForPlayback(query).done(function (result) { + self.playlist = { items: result.Items }; + console.log("shuffle items", result.Items); + self.play(self.playlist); + }); + }); }; self.instantMix = function (id) { - self.play({ ids: [id] }); - }; + var userId = Dashboard.getCurrentUserId(); + ApiClient.getItem(userId, id).done(function (item) { + var promise; + var getItemFields = "MediaSources,Chapters"; + var mixLimit = 3; - self.queue = function (options) { + if (item.Type == "MusicArtist") { + promise = ApiClient.getInstantMixFromArtist(name, { + UserId: userId, + Fields: getItemFields, + Limit: mixLimit + }); + } + else if (item.Type == "MusicGenre") { + promise = ApiClient.getInstantMixFromMusicGenre(name, { + UserId: userId, + Fields: getItemFields, + Limit: mixLimit + }); + } + else if (item.Type == "MusicAlbum") { + promise = ApiClient.getInstantMixFromAlbum(id, { + UserId: userId, + Fields: getItemFields, + Limit: mixLimit + }); + } + else if (item.Type == "Audio") { + promise = ApiClient.getInstantMixFromSong(id, { + UserId: userId, + Fields: getItemFields, + Limit: mixLimit + }); + } + else { + return; + } - }; - - self.queueNext = function (options) { - - }; - - self.stop = function () { - console.log("stop"); - $("#nowPlayingBar", "#footer").hide(); - castPlayer.stopMedia(); + promise.done(function (result) { + self.playlist = { items: result.Items }; + console.log("instant mix items", result.Items); + self.play(self.playlist); + }); + }); }; self.canQueueMediaType = function (mediaType) { + return mediaType == "Audio"; + }; - return false; + self.queue = function (options) { + Dashboard.getCurrentUser().done(function (user) { + if (options.items) { + translateItemsForPlayback(options.items).done(function (items) { + queueItems(items); + }); + } else { + getItemsForPlayback({ + Ids: options.ids.join(',') + }).done(function (result) { + translateItemsForPlayback(result.Items).done(function (items) { + queueItems(items); + }); + }); + } + }); + }; + + self.queueNext = function (options) { + Dashboard.getCurrentUser().done(function (user) { + if (options.items) { + queueItemsNext(options.items); + } else { + self.getItemsForPlayback({ + Ids: options.ids.join(',') + }).done(function (result) { + options.items = result.Items; + queueItemsNext(options.items); + }); + } + }); + }; + + self.stop = function () { + $("#nowPlayingBar", "#footer").hide(); + self.playlist = []; + self.playlistIndex = 0; + castPlayer.stopMedia(); }; self.mute = function () { castPlayer.mute(); }; - self.unMute = function () { + self.unmute = function () { castPlayer.unMute(); }; @@ -1097,18 +1279,13 @@ }; self.getTargets = function () { - var targets = []; - targets.push(self.getCurrentTargetInfo()); - return targets; }; self.getCurrentTargetInfo = function () { - var appName = null; - if (castPlayer.session && castPlayer.session.receiver && castPlayer.session.friendlyName) { appName = castPlayer.session.friendlyName; } @@ -1124,20 +1301,14 @@ }; self.setCurrentTime = function (ticks, item, updateSlider) { - // Convert to ticks ticks = Math.floor(ticks); - var timeText = Dashboard.getDisplayTime(ticks); - if (self.currentDurationTicks) { - timeText += " / " + Dashboard.getDisplayTime(self.currentDurationTicks); - if (updateSlider) { var percent = ticks / self.currentDurationTicks; percent *= 100; - self.positionSlider.val(percent).slider('enable').slider('refresh'); } } else { @@ -1149,6 +1320,55 @@ self.changeStream = function (position) { console.log("seek", position); + ////castPlayer.seekMedia(position); + }; + + self.removeFromPlaylist = function (i) { + self.playlist.remove(i); + }; + + self.currentPlaylistIndex = function (i) { + if (i == null) { + return currentPlaylistIndex; + } + + var newItem = self.playlist[i]; + + Dashboard.getCurrentUser().done(function (user) { + self.playInternal(newItem, 0, user); + currentPlaylistIndex = i; + }); + }; + + self.nextTrack = function () { + var newIndex = currentPlaylistIndex + 1; + var newItem = self.playlist[newIndex]; + + if (newItem) { + Dashboard.getCurrentUser().done(function (user) { + self.playInternal(newItem, 0, user); + currentPlaylistIndex = newIndex; + }); + } + }; + + self.previousTrack = function () { + var newIndex = currentPlaylistIndex - 1; + if (newIndex >= 0) { + var newItem = self.playlist[newIndex]; + if (newItem) { + Dashboard.getCurrentUser().done(function (user) { + self.playInternal(newItem, 0, user); + currentPlaylistIndex = newIndex; + }); + } + } + }; + + self.volumeDown = function () { + }; + + self.volumeUp = function () { }; } diff --git a/dashboard-ui/scripts/mediacontroller.js b/dashboard-ui/scripts/mediacontroller.js index e7222338dc..c755df654f 100644 --- a/dashboard-ui/scripts/mediacontroller.js +++ b/dashboard-ui/scripts/mediacontroller.js @@ -188,15 +188,47 @@ }; self.seek = function (position) { - self.changeStream(position); + currentPlayer.changeStream(position); }; self.currentPlaylistIndex = function (i) { - self.currentPlaylistIndex(i); + currentPlayer.currentPlaylistIndex(i); }; self.removeFromPlaylist = function (i) { - self.removeFromPlaylist(i); + currentPlayer.removeFromPlaylist(i); + }; + + self.nextTrack = function () { + currentPlayer.nextTrack(); + }; + + self.previousTrack = function () { + currentPlayer.previousTrack(); + }; + + self.mute = function () { + currentPlayer.mute(); + }; + + self.unmute = function () { + currentPlayer.unmute(); + }; + + self.toggleMute = function () { + currentPlayer.toggleMute(); + }; + + self.volumeDown = function () { + currentPlayer.volumeDown(); + }; + + self.volumeUp = function () { + currentPlayer.volumeUp(); + }; + + self.shuffle = function (id) { + currentPlayer.shuffle(id); }; } diff --git a/dashboard-ui/scripts/playlist.js b/dashboard-ui/scripts/playlist.js index 10c6b2d9bf..651a4d2104 100644 --- a/dashboard-ui/scripts/playlist.js +++ b/dashboard-ui/scripts/playlist.js @@ -17,7 +17,7 @@ html += ''; - $.each(MediaPlayer.playlist, function (i, item) { + $.each(MediaController.playlist, function (i, item) { var name = LibraryBrowser.getPosterViewDisplayName(item); diff --git a/dashboard-ui/thirdparty/jquery.ba-tinypubsub.min.js b/dashboard-ui/thirdparty/jquery.ba-tinypubsub.min.js new file mode 100644 index 0000000000..9c552b4bc7 --- /dev/null +++ b/dashboard-ui/thirdparty/jquery.ba-tinypubsub.min.js @@ -0,0 +1,4 @@ +/* jQuery Tiny Pub/Sub - v0.7 - 10/27/2011 + * http://benalman.com/ + * Copyright (c) 2011 "Cowboy" Ben Alman; Licensed MIT, GPL */ +(function (a) { var b = a({}); a.subscribe = function () { b.on.apply(b, arguments) }, a.unsubscribe = function () { b.off.apply(b, arguments) }, a.publish = function () { b.trigger.apply(b, arguments) } })(jQuery) \ No newline at end of file