From 3d1ee1233ce816403a8dcf1ade1490ec9ffa1993 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Sun, 17 Aug 2014 01:38:13 -0400 Subject: [PATCH] re-enable chromecast --- dashboard-ui/css/librarybrowser.css | 62 +- dashboard-ui/itembynamedetails.html | 63 +- dashboard-ui/scripts/backdrops.js | 40 + dashboard-ui/scripts/chromecast.js | 1425 +++++++++-------- dashboard-ui/scripts/edititemimages.js | 2 +- dashboard-ui/scripts/extensions.js | 2 +- dashboard-ui/scripts/itembynamedetailpage.js | 35 +- dashboard-ui/scripts/itemdetailpage.js | 4 +- dashboard-ui/scripts/librarybrowser.js | 32 +- dashboard-ui/scripts/librarymenu.js | 3 +- dashboard-ui/scripts/livetvguide.js | 9 +- dashboard-ui/scripts/mediacontroller.js | 2 +- dashboard-ui/scripts/supporterpage.js | 10 +- dashboard-ui/supporter.html | 4 +- .../thirdparty/mediabrowser.apiclient.js | 4 + 15 files changed, 901 insertions(+), 796 deletions(-) diff --git a/dashboard-ui/css/librarybrowser.css b/dashboard-ui/css/librarybrowser.css index 3e3e88432f..2af64357fc 100644 --- a/dashboard-ui/css/librarybrowser.css +++ b/dashboard-ui/css/librarybrowser.css @@ -17,7 +17,11 @@ } .backdropPage { - background-color: rgba(15, 15,15, .94) !important; + background-color: rgba(15, 15,15, .86) !important; +} + +.backdropPage:not(.lightBackdropPage) { + background-color: rgba(15, 15,15, .92) !important; } .backdropContainer { @@ -125,7 +129,7 @@ } } -.detailPageContent { +.detailPageContent, .detailPageContent>table { margin: 0 auto; padding: 0; border-spacing: 0; @@ -441,6 +445,11 @@ a.itemTag:hover { box-shadow: 0 0 20px 3px #38c; } +.itemByNameImageContainer .itemDetailImage { + margin: 0 !important; + max-height: 220px; +} + .parentName { font-size: 14px; display: block; @@ -457,13 +466,13 @@ a.itemTag:hover { .itemOverview { text-overflow: ellipsis; + max-height: 100px; + overflow-y: auto; + padding-right: .5em; } .desktopOverview { min-width: 500px; - max-height: 100px; - overflow-y: auto; - padding-right: 1em; } .mobileOverview { @@ -471,6 +480,10 @@ a.itemTag:hover { padding: 0 1em; } +.empty { + margin: 0; +} + .mobileGenres { display: none; padding: 0 1em; @@ -608,7 +621,7 @@ a.itemTag:hover { margin-top: 0; } - .mobileOverview, .mobileGenres, .mobileDetailButtons { + .mobileOverview:not(.hide), .mobileGenres, .mobileDetailButtons { display: block; } @@ -626,15 +639,11 @@ a.itemTag:hover { display: block !important; } - .primaryDetailPageContent { - margin: 0 1em; + .primaryDetailPageContent .userDataIcons { + margin-left: 0 !important; + margin-top: 1em; + display: block; } - - .primaryDetailPageContent .userDataIcons { - margin-left: 0 !important; - margin-top: 1.5em; - display: block; - } } @@ -656,13 +665,6 @@ a.itemTag:hover { } } -/*@media all and (min-width: 1000px) { - - .primaryDetailPageContent { - max-width: 850px; - } -}*/ - .detailPageParentLink { text-decoration: none; } @@ -956,17 +958,6 @@ a.itemTag:hover { margin-right: auto; } -@media all and (min-width: 750px) { - - .lnkSibling:not(.hide) { - display: block; - } - - .itemBackdrop:not(.noBackdrop) { - border-bottom: 1px solid #111; - } -} - @media all and (min-height: 500px) { .alphabetPicker { @@ -1209,8 +1200,8 @@ a.itemTag:hover { } .itemsListview .playedIndicator { - right: 1em; - top: 55%; + left: 55px; + top: 2px; background: rgba(82, 181, 75, .8); } @@ -1266,7 +1257,8 @@ a.itemTag:hover { } .listItem a + a { - background-color: #202020 !important; + background-color: transparent !important; + z-index: 1000; } .listItem:hover a { diff --git a/dashboard-ui/itembynamedetails.html b/dashboard-ui/itembynamedetails.html index 3440a656a9..a2da0036ad 100644 --- a/dashboard-ui/itembynamedetails.html +++ b/dashboard-ui/itembynamedetails.html @@ -4,7 +4,8 @@ -
+ +
-
-
- - - - + +
-
-
-

-

- - -

-

-

-

-

-

+
+ + + + - -
+
+
+

+

+ + +

+

+

+

+

+

- -
- - ${ButtonEdit} -
-
-
-

+ +
+ + ${ButtonEdit} +
+
+

${ButtonEdit}
-
+
-
+
-
+
diff --git a/dashboard-ui/scripts/backdrops.js b/dashboard-ui/scripts/backdrops.js index 3e7904159a..cd9ae91e98 100644 --- a/dashboard-ui/scripts/backdrops.js +++ b/dashboard-ui/scripts/backdrops.js @@ -107,6 +107,41 @@ return val == '1' || (val != '0' && !$.browser.mobile); } + function setBackdrops(page, items) { + + var images = items.filter(function (i) { + + return i.BackdropImageTags.length > 0; + + }).map(function (i) { + return { + id: i.Id, + tag: i.BackdropImageTags[0] + }; + }); + + if (images.length) { + $(page).addClass('backdropPage'); + + var index = getRandom(0, images.length - 1); + var item = images[index]; + + var screenWidth = $(window).width(); + + var imgUrl = ApiClient.getScaledImageUrl(item.id, { + type: "Backdrop", + tag: item.tag, + maxWidth: screenWidth, + quality: 80 + }); + + getElement().css('backgroundImage', 'url(\'' + imgUrl + '\')'); + + } else { + $(page).removeClass('backdropPage'); + } + } + $(document).on('pagebeforeshow', ".page", function () { var page = this; @@ -128,4 +163,9 @@ }); + window.Backdrops = { + + setBackdrops: setBackdrops + }; + })(jQuery, document); \ No newline at end of file diff --git a/dashboard-ui/scripts/chromecast.js b/dashboard-ui/scripts/chromecast.js index 630731fe37..9a2da9bf59 100644 --- a/dashboard-ui/scripts/chromecast.js +++ b/dashboard-ui/scripts/chromecast.js @@ -1,693 +1,724 @@ -(function (window, chrome, console) { - - // Based on https://github.com/googlecast/CastVideos-chrome/blob/master/CastVideos.js - - /** - * Constants of states for Chromecast device - **/ +(function (window, chrome, console) { + + // Based on https://github.com/googlecast/CastVideos-chrome/blob/master/CastVideos.js + + /** + * Constants of states for Chromecast device + **/ var DEVICE_STATE = { - 'IDLE': 0, - 'ACTIVE': 1, - 'WARNING': 2, + 'IDLE': 0, + 'ACTIVE': 1, + 'WARNING': 2, 'ERROR': 3, - }; - - /** - * Constants of states for CastPlayer - **/ + }; + + /** + * Constants of states for CastPlayer + **/ var PLAYER_STATE = { - 'IDLE': 'IDLE', - 'LOADING': 'LOADING', - 'LOADED': 'LOADED', - 'PLAYING': 'PLAYING', - 'PAUSED': 'PAUSED', - 'STOPPED': 'STOPPED', - 'SEEKING': 'SEEKING', + 'IDLE': 'IDLE', + 'LOADING': 'LOADING', + 'LOADED': 'LOADED', + 'PLAYING': 'PLAYING', + 'PAUSED': 'PAUSED', + 'STOPPED': 'STOPPED', + 'SEEKING': 'SEEKING', 'ERROR': 'ERROR' - }; - - var PlayerName = 'Chromecast'; + }; + + var PlayerName = 'Chromecast'; + + var messageNamespace = 'urn:x-cast:com.google.cast.sample.playlist'; + var cPlayer = { deviceState: DEVICE_STATE.IDLE - }; - var CastPlayer = function () { - - /* device variables */ - // @type {DEVICE_STATE} A state for device - this.deviceState = DEVICE_STATE.IDLE; - - /* Cast player variables */ - // @type {Object} a chrome.cast.media.Media object - this.currentMediaSession = null; - // @type {Number} volume - this.currentVolume = 1; - - // @type {string} a chrome.cast.Session object - this.session = null; - // @type {PLAYER_STATE} A state for Cast media player - this.castPlayerState = PLAYER_STATE.IDLE; - - // @type {Boolean} Fullscreen mode on/off - this.fullscreen = false; - - /* Current media variables */ - // @type {Boolean} Audio on and off - this.audio = true; - // @type {Number} A number for current media index - this.currentMediaIndex = 0; - // @type {Number} A number for current media time - this.currentMediaTime = 0; - // @type {Number} A number for current media duration - this.currentMediaDuration = -1; - // @type {Timer} A timer for tracking progress of media - this.timer = null; - // @type {Boolean} A boolean to stop timer update of progress when triggered by media status event - this.progressFlag = true; - // @type {Number} A number in milliseconds for minimal progress update - this.timerStep = 1000; - - 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"; - - // bind once - commit 2ebffc2271da0bc5e8b13821586aee2a2e3c7753 - this.errorHandler = this.onError.bind(this); - this.incrementMediaTimeHandler = this.incrementMediaTime.bind(this); - this.mediaStatusUpdateHandler = this.onMediaStatusUpdate.bind(this); - + }; + + var CastPlayer = function () { + + /* device variables */ + // @type {DEVICE_STATE} A state for device + this.deviceState = DEVICE_STATE.IDLE; + + /* Cast player variables */ + // @type {Object} a chrome.cast.media.Media object + this.currentMediaSession = null; + // @type {Number} volume + this.currentVolume = 1; + + // @type {string} a chrome.cast.Session object + this.session = null; + // @type {PLAYER_STATE} A state for Cast media player + this.castPlayerState = PLAYER_STATE.IDLE; + + // @type {Boolean} Fullscreen mode on/off + this.fullscreen = false; + + /* Current media variables */ + // @type {Boolean} Audio on and off + this.audio = true; + // @type {Number} A number for current media index + this.currentMediaIndex = 0; + // @type {Number} A number for current media time + this.currentMediaTime = 0; + // @type {Number} A number for current media duration + this.currentMediaDuration = -1; + // @type {Timer} A timer for tracking progress of media + this.timer = null; + // @type {Boolean} A boolean to stop timer update of progress when triggered by media status event + this.progressFlag = true; + // @type {Number} A number in milliseconds for minimal progress update + this.timerStep = 1000; + + 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"; + + // bind once - commit 2ebffc2271da0bc5e8b13821586aee2a2e3c7753 + this.errorHandler = this.onError.bind(this); + this.incrementMediaTimeHandler = this.incrementMediaTime.bind(this); + this.mediaStatusUpdateHandler = this.onMediaStatusUpdate.bind(this); + this.initializeCastPlayer(); - }; - - /** - * Initialize Cast media player - * Initializes the API. Note that either successCallback and errorCallback will be - * invoked once the API has finished initialization. The sessionListener and - * receiverListener may be invoked at any time afterwards, and possibly more than once. - */ + }; + + /** + * Initialize Cast media player + * Initializes the API. Note that either successCallback and errorCallback will be + * invoked once the API has finished initialization. The sessionListener and + * receiverListener may be invoked at any time afterwards, and possibly more than once. + */ CastPlayer.prototype.initializeCastPlayer = function () { - + if (!chrome) { return; - } - + } + if (!chrome.cast || !chrome.cast.isAvailable) { - - setTimeout(this.initializeCastPlayer.bind(this), 1000); + + setTimeout(this.initializeCastPlayer.bind(this), 1000); return; - } - - // v1 Id AE4DA10A - // v2 Id 472F0435 - // default receiver chrome.cast.media.DEFAULT_MEDIA_RECEIVER_APP_ID - - var applicationID = chrome.cast.media.DEFAULT_MEDIA_RECEIVER_APP_ID; - - // request session - var sessionRequest = new chrome.cast.SessionRequest(applicationID); - var apiConfig = new chrome.cast.ApiConfig(sessionRequest, - this.sessionListener.bind(this), - this.receiverListener.bind(this)); - - console.log('chromecast.initialize'); - + } + + // v1 Id AE4DA10A + // v2 Id 472F0435 + // default receiver chrome.cast.media.DEFAULT_MEDIA_RECEIVER_APP_ID + + var applicationID = "472F0435"; + + // request session + var sessionRequest = new chrome.cast.SessionRequest(applicationID); + var apiConfig = new chrome.cast.ApiConfig(sessionRequest, + this.sessionListener.bind(this), + this.receiverListener.bind(this)); + + console.log('chromecast.initialize'); + chrome.cast.initialize(apiConfig, this.onInitSuccess.bind(this), this.errorHandler); - - }; - - /** - * Callback function for init success - */ + + }; + + /** + * Callback function for init success + */ CastPlayer.prototype.onInitSuccess = function () { - this.isInitialized = true; + this.isInitialized = true; console.log("chromecast init success"); - }; - - /** - * Generic error callback function - */ + }; + + /** + * Generic error callback function + */ CastPlayer.prototype.onError = function () { console.log("chromecast error"); - }; - - /** - * @param {!Object} e A new session - * This handles auto-join when a page is reloaded - * When active session is detected, playback will automatically - * join existing session and occur in Cast mode and media - * status gets synced up with current media of the session - */ + }; + + /** + * @param {!Object} e A new session + * This handles auto-join when a page is reloaded + * When active session is detected, playback will automatically + * join existing session and occur in Cast mode and media + * status gets synced up with current media of the session + */ CastPlayer.prototype.sessionListener = function (e) { - this.session = e; + + this.session = e; if (this.session) { - this.deviceState = DEVICE_STATE.ACTIVE; - MediaController.setActivePlayer(PlayerName); + + console.log('sessionListener'); + + this.deviceState = DEVICE_STATE.ACTIVE; + + MediaController.setActivePlayer(PlayerName); + if (this.session.media[0]) { this.onMediaDiscovered('activeSession', this.session.media[0]); - } - + } + + this.session.addMessageListener(messageNamespace, this.messageListener.bind(this)); this.session.addUpdateListener(this.sessionUpdateListener.bind(this)); } - }; - - /** - * @param {string} e Receiver availability - * This indicates availability of receivers but - * does not provide a list of device IDs - */ + }; + + CastPlayer.prototype.messageListener = function (namespace, message) { + + console.log('Message from receiver, namespace: ' + namespace + ', message: ' + message); + }; + + /** + * @param {string} e Receiver availability + * This indicates availability of receivers but + * does not provide a list of device IDs + */ CastPlayer.prototype.receiverListener = function (e) { - + if (e === 'available') { - console.log("chromecast receiver found"); + console.log("chromecast receiver found"); this.hasReceivers = true; - } + } else { - console.log("chromecast receiver list empty"); + console.log("chromecast receiver list empty"); this.hasReceivers = false; } - }; - - /** - * session update listener - */ + }; + + /** + * session update listener + */ CastPlayer.prototype.sessionUpdateListener = function (isAlive) { - if (!isAlive) { - this.session = null; - this.deviceState = DEVICE_STATE.IDLE; - this.castPlayerState = PLAYER_STATE.IDLE; - this.currentMediaSession = null; - clearInterval(this.timer); - + + console.log('sessionUpdateListener alive: ' + isAlive); + + if (isAlive) { + } + else { + this.session = null; + this.deviceState = DEVICE_STATE.IDLE; + this.castPlayerState = PLAYER_STATE.IDLE; + this.currentMediaSession = null; + clearInterval(this.timer); + MediaController.removeActivePlayer(PlayerName); } - }; - - /** - * Requests that a receiver application session be created or joined. By default, the SessionRequest - * passed to the API at initialization time is used; this may be overridden by passing a different - * session request in opt_sessionRequest. - */ + }; + + /** + * Requests that a receiver application session be created or joined. By default, the SessionRequest + * passed to the API at initialization time is used; this may be overridden by passing a different + * session request in opt_sessionRequest. + */ CastPlayer.prototype.launchApp = function () { - console.log("chromecast launching app..."); - chrome.cast.requestSession(this.onRequestSessionSuccess.bind(this), this.onLaunchError.bind(this)); + console.log("chromecast launching app..."); + chrome.cast.requestSession(this.onRequestSessionSuccess.bind(this), this.onLaunchError.bind(this)); if (this.timer) { clearInterval(this.timer); } - }; - - /** - * Callback function for request session success - * @param {Object} e A chrome.cast.Session object - */ + }; + + /** + * Callback function for request session success + * @param {Object} e A chrome.cast.Session object + */ CastPlayer.prototype.onRequestSessionSuccess = function (e) { - console.log("chromecast session success: " + e.sessionId); - this.session = e; - this.deviceState = DEVICE_STATE.ACTIVE; + console.log("chromecast session success: " + e.sessionId); + this.session = e; + this.deviceState = DEVICE_STATE.ACTIVE; this.session.addUpdateListener(this.sessionUpdateListener.bind(this)); - }; - - /** - * Callback function for launch error - */ + }; + + /** + * Callback function for launch error + */ CastPlayer.prototype.onLaunchError = function () { - console.log("chromecast launch error"); - this.deviceState = DEVICE_STATE.ERROR; - + console.log("chromecast launch error"); + this.deviceState = DEVICE_STATE.ERROR; + Dashboard.alert({ - - title: Globalize.translate("Error"), + + title: Globalize.translate("Error"), message: Globalize.translate("ErrorLaunchingChromecast") - - }); - + + }); + MediaController.removeActivePlayer(PlayerName); - }; - - /** - * Stops the running receiver application associated with the session. - */ + }; + + /** + * Stops the running receiver application associated with the session. + */ CastPlayer.prototype.stopApp = function () { - this.session.stop(this.onStopAppSuccess.bind(this, 'Session stopped'), + this.session.stop(this.onStopAppSuccess.bind(this, 'Session stopped'), this.errorHandler); - - }; - - /** - * Callback function for stop app success - */ + + }; + + /** + * Callback function for stop app success + */ CastPlayer.prototype.onStopAppSuccess = function (message) { - console.log(message); - this.deviceState = DEVICE_STATE.IDLE; - this.castPlayerState = PLAYER_STATE.IDLE; - this.currentMediaSession = null; + console.log(message); + this.deviceState = DEVICE_STATE.IDLE; + this.castPlayerState = PLAYER_STATE.IDLE; + this.currentMediaSession = null; clearInterval(this.timer); - }; - - /** - * Loads media into a running receiver application - * @param {Number} mediaIndex An index number to indicate current media content - */ - CastPlayer.prototype.loadMedia = function (userId, options, command) { - + }; + + /** + * Loads media into a running receiver application + * @param {Number} mediaIndex An index number to indicate current media content + */ + CastPlayer.prototype.loadMedia = function (options, command) { + if (!this.session) { - console.log("no session"); + console.log("no session"); return; } - options.userId = userId; - var message = { - playOptions: options, - command: command - }; - - this.session.sendMessage('urn:x-cast:com.google.cast.sample.playlist', JSON.stringify(message)); - }; - - /** - * Callback function for loadMedia success - * @param {Object} mediaSession A new media object. - */ + options: options, + command: command, + + userId: Dashboard.getCurrentUserId(), + deviceId: ApiClient.deviceId(), + accessToken: ApiClient.accessToken(), + serverAddress: ApiClient.serverAddress() + }; + + message = JSON.stringify(message); + //console.log(message); + + this.session.sendMessage(messageNamespace, message, this.onPlayCommandSuccess.bind(this), this.errorHandler); + }; + + CastPlayer.prototype.onPlayCommandSuccess = function() { + console.log('Play command was sent ok.'); + }; + + /** + * Callback function for loadMedia success + * @param {Object} mediaSession A new media object. + */ CastPlayer.prototype.onMediaDiscovered = function (how, mediaSession) { - - console.log("chromecast new media session ID:" + mediaSession.mediaSessionId + ' (' + how + ')'); - this.currentMediaSession = mediaSession; - this.currentMediaTime = mediaSession.currentTime; - + + console.log("chromecast new media session ID:" + mediaSession.mediaSessionId + ' (' + how + ')'); + this.currentMediaSession = mediaSession; + this.currentMediaTime = mediaSession.currentTime; + if (how == 'loadMedia') { - this.castPlayerState = PLAYER_STATE.PLAYING; - clearInterval(this.timer); + this.castPlayerState = PLAYER_STATE.PLAYING; + clearInterval(this.timer); this.startProgressTimer(); - } - + } + if (how == 'activeSession') { this.castPlayerState = mediaSession.playerState; - } - - if (this.castPlayerState == PLAYER_STATE.PLAYING) { - // start progress timer + } + + if (this.castPlayerState == PLAYER_STATE.PLAYING) { + // start progress timer this.startProgressTimer(); - } - - this.currentMediaSession.addUpdateListener(this.mediaStatusUpdateHandler); + } + + this.currentMediaSession.addUpdateListener(this.mediaStatusUpdateHandler); this.currentMediaDuration = mediaSession.media.duration * 10000000; - }; - - /** - * Callback function when media load returns error - */ + }; + + /** + * Callback function when media load returns error + */ CastPlayer.prototype.onLoadMediaError = function (e) { - console.log("chromecast media error"); + console.log("chromecast media error"); this.castPlayerState = PLAYER_STATE.IDLE; - }; - - /** - * Callback function for media status update from receiver - * @param {!Boolean} e true/false - */ + }; + + /** + * Callback function for media status update from receiver + * @param {!Boolean} e true/false + */ CastPlayer.prototype.onMediaStatusUpdate = function (e) { if (e == false) { - this.currentMediaTime = 0; + this.currentMediaTime = 0; this.castPlayerState = PLAYER_STATE.IDLE; - } - console.log("chromecast updating media"); + } + console.log("chromecast updating media"); this.updateProgressBarByTimer(); - }; - - /** - * Helper function - * Increment media current position by 1 second - */ + }; + + /** + * Helper function + * Increment media current position by 1 second + */ CastPlayer.prototype.incrementMediaTime = function () { if (this.castPlayerState == PLAYER_STATE.PLAYING) { if (this.currentMediaTime < this.currentMediaDuration) { - this.currentMediaTime += 1; + this.currentMediaTime += 1; this.updateProgressBarByTimer(); - } + } else { - this.currentMediaTime = 0; + this.currentMediaTime = 0; clearInterval(this.timer); } } - }; - - /** - * Play media in Cast mode - */ + }; + + /** + * Play media in Cast mode + */ CastPlayer.prototype.playMedia = function () { - + if (!this.currentMediaSession) { return; - } - + } + switch (this.castPlayerState) { - case PLAYER_STATE.LOADED: - case PLAYER_STATE.PAUSED: - this.currentMediaSession.play(null, - this.mediaCommandSuccessCallback.bind(this, "playing started for " + this.currentMediaSession.sessionId), - this.errorHandler); - this.currentMediaSession.addUpdateListener(this.mediaStatusUpdateHandler); - this.castPlayerState = PLAYER_STATE.PLAYING; - // start progress timer - clearInterval(this.timer); - this.startProgressTimer(); - break; - case PLAYER_STATE.IDLE: - case PLAYER_STATE.LOADING: - case PLAYER_STATE.STOPPED: - this.loadMedia(); - this.currentMediaSession.addUpdateListener(this.mediaStatusUpdateHandler); - this.castPlayerState = PLAYER_STATE.PLAYING; - break; - default: + case PLAYER_STATE.LOADED: + case PLAYER_STATE.PAUSED: + this.currentMediaSession.play(null, + this.mediaCommandSuccessCallback.bind(this, "playing started for " + this.currentMediaSession.sessionId), + this.errorHandler); + this.currentMediaSession.addUpdateListener(this.mediaStatusUpdateHandler); + this.castPlayerState = PLAYER_STATE.PLAYING; + // start progress timer + clearInterval(this.timer); + this.startProgressTimer(); + break; + case PLAYER_STATE.IDLE: + case PLAYER_STATE.LOADING: + case PLAYER_STATE.STOPPED: + this.loadMedia(); + this.currentMediaSession.addUpdateListener(this.mediaStatusUpdateHandler); + this.castPlayerState = PLAYER_STATE.PLAYING; + break; + default: break; } - }; - - /** - * Pause media playback in Cast mode - */ + }; + + /** + * Pause media playback in Cast mode + */ CastPlayer.prototype.pauseMedia = function () { - + if (!this.currentMediaSession) { return; - } - + } + if (this.castPlayerState == PLAYER_STATE.PLAYING) { - this.castPlayerState = PLAYER_STATE.PAUSED; - this.currentMediaSession.pause(null, - this.mediaCommandSuccessCallback.bind(this, "paused " + this.currentMediaSession.sessionId), - this.errorHandler); + this.castPlayerState = PLAYER_STATE.PAUSED; + this.currentMediaSession.pause(null, + this.mediaCommandSuccessCallback.bind(this, "paused " + this.currentMediaSession.sessionId), + this.errorHandler); clearInterval(this.timer); } - }; - - /** - * Stop CC playback - */ + }; + + /** + * Stop CC playback + */ CastPlayer.prototype.stopMedia = function () { - + if (!this.currentMediaSession) { return; - } - - this.currentMediaSession.stop(null, - this.mediaCommandSuccessCallback.bind(this, "stopped " + this.currentMediaSession.sessionId), - this.errorHandler); - this.castPlayerState = PLAYER_STATE.STOPPED; + } + + this.currentMediaSession.stop(null, + this.mediaCommandSuccessCallback.bind(this, "stopped " + this.currentMediaSession.sessionId), + this.errorHandler); + this.castPlayerState = PLAYER_STATE.STOPPED; clearInterval(this.timer); - }; - - /** - * Set media volume in Cast mode - * @param {Boolean} mute A boolean - */ + }; + + /** + * Set media volume in Cast mode + * @param {Boolean} mute A boolean + */ CastPlayer.prototype.setReceiverVolume = function (mute, vol) { - + if (!this.currentMediaSession) { return; - } - + } + if (!mute) { - this.currentVolume = vol || 1; - this.session.setReceiverVolumeLevel(this.currentVolume, - this.mediaCommandSuccessCallback.bind(this), - this.errorHandler); - } - else { - this.session.setReceiverMuted(true, - this.mediaCommandSuccessCallback.bind(this), + this.currentVolume = vol || 1; + this.session.setReceiverVolumeLevel(this.currentVolume, + this.mediaCommandSuccessCallback.bind(this), this.errorHandler); } - }; - - /** - * Toggle mute CC - */ + else { + this.session.setReceiverMuted(true, + this.mediaCommandSuccessCallback.bind(this), + this.errorHandler); + } + }; + + /** + * Toggle mute CC + */ CastPlayer.prototype.toggleMute = function () { if (this.audio == true) { this.mute(); - } + } else { this.unMute(); } - }; - - /** - * Mute CC - */ + }; + + /** + * Mute CC + */ CastPlayer.prototype.mute = function () { - this.audio = false; + this.audio = false; this.setReceiverVolume(true); - }; - - /** - * Unmute CC - */ + }; + + /** + * Unmute CC + */ CastPlayer.prototype.unMute = function () { - this.audio = true; + this.audio = true; this.setReceiverVolume(false); - }; - - - /** - * media seek function in either Cast or local mode - * @param {Event} e An event object from seek - */ + }; + + + /** + * media seek function in either Cast or local mode + * @param {Event} e An event object from seek + */ CastPlayer.prototype.seekMedia = function (event) { - var pos = parseInt(event); - - var curr = pos / 10000000; - + var pos = parseInt(event); + + var curr = pos / 10000000; + if (this.castPlayerState != PLAYER_STATE.PLAYING && this.castPlayerState != PLAYER_STATE.PAUSED) { return; - } - - this.currentMediaTime = curr; - console.log('Seeking ' + this.currentMediaSession.sessionId + ':' + - this.currentMediaSession.mediaSessionId + ' to ' + curr); - var request = new chrome.cast.media.SeekRequest(); - request.currentTime = this.currentMediaTime; - this.currentMediaSession.seek(request, - this.onSeekSuccess.bind(this, 'media seek done'), - this.errorHandler); + } + + this.currentMediaTime = curr; + console.log('Seeking ' + this.currentMediaSession.sessionId + ':' + + this.currentMediaSession.mediaSessionId + ' to ' + curr); + var request = new chrome.cast.media.SeekRequest(); + request.currentTime = this.currentMediaTime; + this.currentMediaSession.seek(request, + this.onSeekSuccess.bind(this, 'media seek done'), + this.errorHandler); this.castPlayerState = PLAYER_STATE.SEEKING; - }; - - /** - * Callback function for seek success - * @param {String} info A string that describe seek event - */ + }; + + /** + * Callback function for seek success + * @param {String} info A string that describe seek event + */ CastPlayer.prototype.onSeekSuccess = function (info) { - console.log(info); + console.log(info); this.castPlayerState = PLAYER_STATE.PLAYING; - }; - - /** - * Callback function for media command success - */ + }; + + /** + * Callback function for media command success + */ CastPlayer.prototype.mediaCommandSuccessCallback = function (info, e) { console.log(info); - }; - - /** - * Update progress bar when there is a media status update - * @param {Object} e An media status update object - */ + }; + + /** + * Update progress bar when there is a media status update + * @param {Object} e An media status update object + */ CastPlayer.prototype.updateProgressBar = function (e) { + + console.log('CastPlayer.updateProgressBar'); + if (e.idleReason == 'FINISHED' && e.playerState == 'IDLE') { - clearInterval(this.timer); - this.castPlayerState = PLAYER_STATE.STOPPED; + clearInterval(this.timer); + this.castPlayerState = PLAYER_STATE.STOPPED; if (e.idleReason == 'FINISHED') { $(this).trigger("/playback/complete", e); } - } - else { - var p = 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 } - }; - - /** - * Set progressFlag with a timeout of 1 second to avoid UI update - * until a media status update from receiver - */ + else { + var p = 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 + } + }; + + /** + * Set progressFlag with a timeout of 1 second to avoid UI update + * until a media status update from receiver + */ CastPlayer.prototype.setProgressFlag = function () { this.progressFlag = true; - }; - - /** - * Update progress bar based on timer - */ + }; + + /** + * Update progress bar based on timer + */ CastPlayer.prototype.updateProgressBarByTimer = function () { + + console.log('CastPlayer.updateProgressBarByTimer'); + if (!this.currentMediaTime) { this.currentMediaDuration = this.session.media[0].currentTime; - } - + } + if (!this.currentMediaDuration) { this.currentMediaDuration = this.session.media[0].media.customData.runTimeTicks; - } - - var pp = 0; + } + + var pp = 0; if (this.currentMediaDuration > 0) { pp = Number(this.currentMediaTime / this.currentMediaDuration).toFixed(3); - } - - if (this.progressFlag) { - // don't update progress if it's been updated on media status update event - $(this).trigger("/playback/update", + } + + if (this.progressFlag) { + // don't update progress if it's been updated on media status update event + $(this).trigger("/playback/update", [{ - positionTicks: this.currentMediaTime * 10000000, + positionTicks: this.currentMediaTime * 10000000, runtimeTicks: this.currentMediaDuration }]); - } - + } + if (pp > 100 || this.castPlayerState == PLAYER_STATE.IDLE) { - clearInterval(this.timer); - this.deviceState = DEVICE_STATE.IDLE; - this.castPlayerState = PLAYER_STATE.IDLE; + clearInterval(this.timer); + this.deviceState = DEVICE_STATE.IDLE; + this.castPlayerState = PLAYER_STATE.IDLE; $(this).trigger("/playback/complete", true); } - }; - - /** - * @param {function} A callback function for the fucntion to start timer - */ + }; + + /** + * @param {function} A callback function for the fucntion to start timer + */ CastPlayer.prototype.startProgressTimer = function () { if (this.timer) { - clearInterval(this.timer); + clearInterval(this.timer); this.timer = null; - } - - // start progress timer + } + + // start progress timer this.timer = setInterval(this.incrementMediaTimeHandler, this.timerStep); - }; - - // Create Cast Player - var castPlayer = new CastPlayer(); - - function getCustomData(item, mediaSourceId, startTimeTicks) { - - return { - - serverAddress: ApiClient.serverAddress(), - itemId: item.Id, - userId: Dashboard.getCurrentUserId(), - deviceName: ApiClient.deviceName(), - //deviceId: ApiClient.deviceId(), - startTimeTicks: startTimeTicks || 0, - runTimeTicks: item.RunTimeTicks - }; - - } - - function translateItemsForPlayback(items) { + }; - var deferred = $.Deferred(); - - var firstItem = items[0]; - var promise; - - if (firstItem.Type == "Playlist") { - - promise = self.getItemsForPlayback({ - ParentId: firstItem.Id, - }); - } - 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" - }); - } - else if (firstItem.IsFolder) { - - promise = self.getItemsForPlayback({ - ParentId: firstItem.Id, - Filters: "IsNotFolder", - Recursive: true, - SortBy: "SortName", - MediaTypes: "Audio,Video" - }); - } - - if (promise) { - promise.done(function (result) { - - deferred.resolveWith(null, [result.Items]); - }); - } else { - deferred.resolveWith(null, [items]); - } - - return deferred.promise(); - } + // Create Cast Player + var castPlayer = new CastPlayer(); function chromecastPlayer() { - - var self = this; - - var getItemFields = "MediaSources,Chapters"; - - self.name = PlayerName; - - self.isPaused = false; - - self.isMuted = false; - - self.positionTicks = 0; - - self.runtimeTicks = 0; - - $(castPlayer).on("/playback/complete", function (e) { - - var state = self.getPlayerStateInternal(); - - $(self).trigger("playbackstop", [state]); - - }); - - $(castPlayer).on("/playback/update", function (e, data) { - - self.positionTicks = data.positionTicks; - self.runtimeTicks = data.runtimeTicks; - - var state = self.getPlayerStateInternal(); - - $(self).trigger("positionchange", [state]); - }); - + + var self = this; + + var getItemFields = "MediaSources,Chapters"; + + // MediaController needs this + self.name = PlayerName; + + self.isPaused = false; + + self.isMuted = false; + + self.positionTicks = 0; + + self.runtimeTicks = 0; + + self.getItemsForPlayback = function (query) { + + var userId = Dashboard.getCurrentUserId(); + + query.Limit = query.Limit || 100; + query.Fields = getItemFields; + query.ExcludeLocationTypes = "Virtual"; + + return ApiClient.getItems(userId, query); + }; + + self.translateItemsForPlayback = function(items) { + + var deferred = $.Deferred(); + + var firstItem = items[0]; + var promise; + + if (firstItem.Type == "Playlist") { + + promise = self.getItemsForPlayback({ + ParentId: firstItem.Id, + }); + } 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" + }); + } else if (firstItem.IsFolder) { + + promise = self.getItemsForPlayback({ + ParentId: firstItem.Id, + Filters: "IsNotFolder", + Recursive: true, + SortBy: "SortName", + MediaTypes: "Audio,Video" + }); + } + + if (promise) { + promise.done(function(result) { + + deferred.resolveWith(null, [result.Items]); + }); + } else { + deferred.resolveWith(null, [items]); + } + + return deferred.promise(); + }; + + //$(castPlayer).on("/playback/complete", function (e) { + + // var state = self.getPlayerStateInternal(); + + // $(self).trigger("playbackstop", [state]); + + //}); + + //$(castPlayer).on("/playback/update", function (e, data) { + + // self.positionTicks = data.positionTicks; + // self.runtimeTicks = data.runtimeTicks; + + // var state = self.getPlayerStateInternal(); + + // $(self).trigger("positionchange", [state]); + //}); + self.play = function (options) { Dashboard.getCurrentUser().done(function (user) { if (options.items) { - translateItemsForPlayback(options.items).done(function (items) { + self.translateItemsForPlayback(options.items).done(function (items) { self.playWithIntros(items, options, user); }); @@ -700,7 +731,7 @@ }).done(function (result) { - translateItemsForPlayback(result.Items).done(function (items) { + self.translateItemsForPlayback(result.Items).done(function (items) { self.playWithIntros(items, options, user); }); @@ -716,10 +747,10 @@ var firstItem = items[0]; - if (options.startPositionTicks || firstItem.MediaType !== 'Video' || !self.canAutoPlayVideo()) { + if (options.startPositionTicks || firstItem.MediaType !== 'Video') { self.playWithCommand(options, 'PlayNow'); - - } + return; + } ApiClient.getJSON(ApiClient.getUrl('Users/' + user.Id + '/Items/' + firstItem.Id + '/Intros')).done(function (intros) { @@ -731,19 +762,29 @@ self.playWithCommand = function (options, command) { - castPlayer.loadMedia(Dashboard.getCurrentUserId(), options, command); - }; - + if (!options.items) { + ApiClient.getItem(Dashboard.getCurrentUserId(), options.ids[0]).done(function(item) { + + options.items = [item]; + self.playWithCommand(options, command); + }); + + return; + } + + castPlayer.loadMedia(options, command); + }; + self.unpause = function () { - self.isPaused = !self.isPaused; + self.isPaused = !self.isPaused; castPlayer.playMedia(); - }; - + }; + self.pause = function () { - self.isPaused = true; + self.isPaused = true; castPlayer.pauseMedia(); - }; - + }; + self.shuffle = function (id) { var userId = Dashboard.getCurrentUserId(); @@ -846,183 +887,185 @@ }); }; - + self.canQueueMediaType = function (mediaType) { return mediaType == "Audio"; - }; - + }; + self.queue = function (options) { self.playWithCommnd(options, 'PlayLast'); - }; - + }; + self.queueNext = function (options) { self.playWithCommand(options, 'PlayNext'); - }; - + }; + self.stop = function () { castPlayer.stopMedia(); - }; - + }; + self.displayContent = function (options) { - - }; - + + }; + self.mute = function () { - self.isMuted = true; + self.isMuted = true; castPlayer.mute(); - }; - + }; + self.unMute = function () { - self.isMuted = false; + self.isMuted = false; castPlayer.unMute(); - }; - + }; + self.toggleMute = function () { castPlayer.toggleMute(); - }; - + }; + self.getTargets = function () { - - var targets = []; - + + var targets = []; + if (castPlayer.hasReceivers) { targets.push(self.getCurrentTargetInfo()); - } - + } + return targets; - - }; - + + }; + self.getCurrentTargetInfo = function () { - - var appName = null; + + var appName = null; + if (castPlayer.session && castPlayer.session.receiver && castPlayer.session.receiver.friendlyName) { appName = castPlayer.session.receiver.friendlyName; - } - + } + return { - name: PlayerName, - id: PlayerName, - playerName: self.name, // TODO: PlayerName == self.name, so do we need to use either/or? - playableMediaTypes: ["Audio", "Video"], - isLocalPlayer: false, - appName: appName, - supportedCommands: ["VolumeUp", - "VolumeDown", - "Mute", - "Unmute", - "ToggleMute", - "SetVolume", + name: PlayerName, + id: PlayerName, + playerName: PlayerName, + playableMediaTypes: ["Audio", "Video"], + isLocalPlayer: false, + appName: PlayerName, + deviceName: appName, + supportedCommands: ["VolumeUp", + "VolumeDown", + "Mute", + "Unmute", + "ToggleMute", + "SetVolume", "DisplayContent"] }; - }; - + }; + self.seek = function (position) { castPlayer.seekMedia(position); - }; - + }; + self.nextTrack = function () { - }; - + }; + self.previousTrack = function () { - }; - - self.beginPlayerUpdates = function () { - // Setup polling here - }; - - self.endPlayerUpdates = function () { - // Stop polling here - }; - + }; + + self.beginPlayerUpdates = function () { + // Setup polling here + }; + + self.endPlayerUpdates = function () { + // Stop polling here + }; + self.volumeDown = function () { - var vol = castPlayer.volumeLevel - 0.02; + var vol = castPlayer.volumeLevel - 0.02; castPlayer.setReceiverVolume(false, vol / 100); - }; - + }; + self.volumeUp = function () { - var vol = castPlayer.volumeLevel + 0.02; + var vol = castPlayer.volumeLevel + 0.02; castPlayer.setReceiverVolume(false, vol / 100); - }; - + }; + self.setVolume = function (vol) { castPlayer.setReceiverVolume(false, vol / 100); - }; - + }; + self.getPlayerState = function () { - - var deferred = $.Deferred(); - - var result = self.getPlayerStateInternal(); - - deferred.resolveWith(null, [result]); - + + var deferred = $.Deferred(); + + var result = self.getPlayerStateInternal(); + + deferred.resolveWith(null, [result]); + return deferred.promise(); - }; - + }; + self.getPlayerStateInternal = function () { - + var state = { PlayState: { - - CanSeek: self.runtimeTicks && self.positionTicks > 0, - PositionTicks: self.positionTicks, - VolumeLevel: castPlayer.currentVolume * 100, - IsPaused: self.isPaused, - IsMuted: self.isMuted - - // TODO: Implement - // AudioStreamIndex: null, - // SubtitleStreamIndex: null, - // PlayMethod: 'DirectStream' or 'Transcode' + + CanSeek: self.runtimeTicks && self.positionTicks > 0, + PositionTicks: self.positionTicks, + VolumeLevel: castPlayer.currentVolume * 100, + IsPaused: self.isPaused, + IsMuted: self.isMuted + + // TODO: Implement + // AudioStreamIndex: null, + // SubtitleStreamIndex: null, + // PlayMethod: 'DirectStream' or 'Transcode' } - }; - - // TODO: Implement - var isPlaying = false; - - if (isPlaying) { - - //state.PlayState.MediaSourceId = 'xxx'; - + }; + + // TODO: Implement + var isPlaying = false; + + if (isPlaying) { + + //state.PlayState.MediaSourceId = 'xxx'; + state.NowPlayingItem = { - - RunTimeTicks: self.runtimeTicks, + + RunTimeTicks: self.runtimeTicks, Name: 'Chromecast' - }; - - var nowPlayingItem = state.NowPlayingItem; - - // TODO: Fill in these properties using chromecast mediainfo and/or custom data - //nowPlayingItem.Id = item.Id; - //nowPlayingItem.MediaType = item.MediaType; - //nowPlayingItem.Type = item.Type; - //nowPlayingItem.Name = item.Name; - - //nowPlayingItem.IndexNumber = item.IndexNumber; - //nowPlayingItem.IndexNumberEnd = item.IndexNumberEnd; - //nowPlayingItem.ParentIndexNumber = item.ParentIndexNumber; - //nowPlayingItem.ProductionYear = item.ProductionYear; - //nowPlayingItem.PremiereDate = item.PremiereDate; - //nowPlayingItem.SeriesName = item.SeriesName; - //nowPlayingItem.Album = item.Album; - //nowPlayingItem.Artists = item.Artists; - - } - + }; + + var nowPlayingItem = state.NowPlayingItem; + + // TODO: Fill in these properties using chromecast mediainfo and/or custom data + //nowPlayingItem.Id = item.Id; + //nowPlayingItem.MediaType = item.MediaType; + //nowPlayingItem.Type = item.Type; + //nowPlayingItem.Name = item.Name; + + //nowPlayingItem.IndexNumber = item.IndexNumber; + //nowPlayingItem.IndexNumberEnd = item.IndexNumberEnd; + //nowPlayingItem.ParentIndexNumber = item.ParentIndexNumber; + //nowPlayingItem.ProductionYear = item.ProductionYear; + //nowPlayingItem.PremiereDate = item.PremiereDate; + //nowPlayingItem.SeriesName = item.SeriesName; + //nowPlayingItem.Album = item.Album; + //nowPlayingItem.Artists = item.Artists; + + } + return state; }; - } - - //MediaController.registerPlayer(new chromecastPlayer()); - - //$(MediaController).on('playerchange', function () { - - // if (MediaController.getPlayerInfo().name == PlayerName) { - // if (castPlayer.deviceState != DEVICE_STATE.ACTIVE && castPlayer.isInitialized) { - // castPlayer.launchApp(); - // } - // } - //}); - + } + + MediaController.registerPlayer(new chromecastPlayer()); + + $(MediaController).on('playerchange', function () { + + if (MediaController.getPlayerInfo().name == PlayerName) { + if (castPlayer.deviceState != DEVICE_STATE.ACTIVE && castPlayer.isInitialized) { + castPlayer.launchApp(); + } + } + }); + })(window, window.chrome, console); \ No newline at end of file diff --git a/dashboard-ui/scripts/edititemimages.js b/dashboard-ui/scripts/edititemimages.js index b05e6b0407..92855e4648 100644 --- a/dashboard-ui/scripts/edititemimages.js +++ b/dashboard-ui/scripts/edititemimages.js @@ -310,7 +310,7 @@ html += '
'; - html += '
'; + html += '
'; html += '
'; diff --git a/dashboard-ui/scripts/extensions.js b/dashboard-ui/scripts/extensions.js index 4710c46ae2..712980d8b4 100644 --- a/dashboard-ui/scripts/extensions.js +++ b/dashboard-ui/scripts/extensions.js @@ -330,7 +330,7 @@ function parseISO8601Date(s, options) { } return new Date(ms); -}; +} // jqm.page.params.js - version 0.1 // Copyright (c) 2011, Kin Blas diff --git a/dashboard-ui/scripts/itembynamedetailpage.js b/dashboard-ui/scripts/itembynamedetailpage.js index aebfffc85e..fc6e2c7cbe 100644 --- a/dashboard-ui/scripts/itembynamedetailpage.js +++ b/dashboard-ui/scripts/itembynamedetailpage.js @@ -70,6 +70,8 @@ currentItem = item; + Backdrops.setBackdrops(page, [item]); + renderHeader(page, item, context); var name = item.Name; @@ -97,7 +99,7 @@ var editImagesHref = user.Configuration.IsAdministrator ? 'edititemimages.html' + editQuery : null; - $('#itemImage', page).html(LibraryBrowser.getDetailImageHtml(item, editImagesHref)); + $('#itemImage', page).html(LibraryBrowser.getDetailImageHtml(item, editImagesHref, true)); if (user.Configuration.IsAdministrator && item.LocationType !== "Offline") { $('.btnEdit', page).show(); @@ -162,49 +164,49 @@ if (item.MovieCount) { html += ''; - html += ''; + html += ''; } if (item.SeriesCount) { html += ''; - html += ''; + html += ''; } if (item.EpisodeCount) { html += ''; - html += ''; + html += ''; } if (item.TrailerCount) { html += ''; - html += ''; + html += ''; } if (item.GameCount) { html += ''; - html += ''; + html += ''; } if (item.AlbumCount) { html += ''; - html += ''; + html += ''; } if (item.SongCount) { html += ''; - html += ''; + html += ''; } if (item.MusicVideoCount) { html += ''; - html += ''; + html += ''; } html += ''; @@ -321,7 +323,7 @@ function renderDetails(page, item, context) { - LibraryBrowser.renderDetailPageBackdrop(page, item); + //LibraryBrowser.renderDetailPageBackdrop(page, item); LibraryBrowser.renderOverview($('.itemOverview', page), item); renderUserDataIcons(page, item); @@ -472,22 +474,17 @@ } else if (query.IncludeItemTypes == "MusicAlbum") { - html = LibraryBrowser.getPosterViewHtml({ + html = LibraryBrowser.getListViewHtml({ items: result.Items, - shape: "square", - context: 'music', - showTitle: true, - showParentTitle: true + smallIcon: true }); } else { - html = LibraryBrowser.getPosterViewHtml({ + html = LibraryBrowser.getListViewHtml({ items: result.Items, - shape: "square", - showTitle: true, - centerText: true + smallIcon: true }); } diff --git a/dashboard-ui/scripts/itemdetailpage.js b/dashboard-ui/scripts/itemdetailpage.js index ff3a0369b3..776d54763d 100644 --- a/dashboard-ui/scripts/itemdetailpage.js +++ b/dashboard-ui/scripts/itemdetailpage.js @@ -726,7 +726,9 @@ $('#childrenContent', page).html(LibraryBrowser.getListViewHtml({ items: result.Items, - smallIcon: true + smallIcon: true, + showIndex: true, + index: 'disc' })).trigger('create').createCardMenus(); diff --git a/dashboard-ui/scripts/librarybrowser.js b/dashboard-ui/scripts/librarybrowser.js index 4147354b1e..ea5534e9b2 100644 --- a/dashboard-ui/scripts/librarybrowser.js +++ b/dashboard-ui/scripts/librarybrowser.js @@ -27,7 +27,7 @@ getDefaultItemsView: function (view, mobileView) { - return view; + return $.browser.mobile ? mobileView : view; }, @@ -578,9 +578,14 @@ }, - getListViewIndex: function (item, sortBy) { + getListViewIndex: function (item, options) { - sortBy = (sortBy || '').toLowerCase(); + if (options.index == 'disc') { + + return item.ParentIndexNumber == null ? '' : 'Disc ' + item.ParentIndexNumber; + } + + var sortBy = (options.sortBy || '').toLowerCase(); var code, name; if (sortBy.indexOf('sortname') == 0) { @@ -666,7 +671,8 @@ var html = ''; if (options.showIndex !== false) { - var itemGroupTitle = LibraryBrowser.getListViewIndex(item, options.sortBy); + + var itemGroupTitle = LibraryBrowser.getListViewIndex(item, options); if (itemGroupTitle != groupTitle) { @@ -2026,7 +2032,7 @@ }); }, - getDetailImageHtml: function (item, href) { + getDetailImageHtml: function (item, href, preferThumb) { var imageTags = item.ImageTags || {}; @@ -2040,7 +2046,15 @@ var imageHeight = 280; - if (imageTags.Primary) { + if (preferThumb && imageTags.Thumb) { + + url = ApiClient.getScaledImageUrl(item.Id, { + type: "Thumb", + maxHeight: imageHeight, + tag: item.ImageTags.Thumb + }); + } + else if (imageTags.Primary) { url = ApiClient.getScaledImageUrl(item.Id, { type: "Primary", @@ -2263,6 +2277,12 @@ $(this).attr("target", "_blank"); }); + if (overview) { + elem.removeClass('empty'); + } else { + elem.addClass('empty'); + } + }, renderStudios: function (elem, item, context) { diff --git a/dashboard-ui/scripts/librarymenu.js b/dashboard-ui/scripts/librarymenu.js index 62d142e823..35b17d0b73 100644 --- a/dashboard-ui/scripts/librarymenu.js +++ b/dashboard-ui/scripts/librarymenu.js @@ -254,7 +254,8 @@ } else { $('.btnCast').removeClass('btnDefaultCast').addClass('btnActiveCast'); - $('.headerSelectedPlayer').html(info.deviceName || info.name); + + $('.headerSelectedPlayer').html((info.deviceName || info.name)); } } diff --git a/dashboard-ui/scripts/livetvguide.js b/dashboard-ui/scripts/livetvguide.js index 2683edf63c..bb92ded4e7 100644 --- a/dashboard-ui/scripts/livetvguide.js +++ b/dashboard-ui/scripts/livetvguide.js @@ -396,6 +396,10 @@ start.setHours(0, 0, 0, 0); end.setHours(0, 0, 0, 0); + if (start.getTime() >= end.getTime()) { + end.setDate(start.getDate() + 1); + } + start = new Date(Math.max(today, start)); var html = ''; @@ -417,7 +421,10 @@ var val = elem.val(); var date = new Date(); - date.setTime(parseInt(val)); + + if (val) { + date.setTime(parseInt(val)); + } changeDate(page, date); } diff --git a/dashboard-ui/scripts/mediacontroller.js b/dashboard-ui/scripts/mediacontroller.js index e6ec325a33..a7bcd237b1 100644 --- a/dashboard-ui/scripts/mediacontroller.js +++ b/dashboard-ui/scripts/mediacontroller.js @@ -439,7 +439,7 @@ var mirror = (!target.isLocalPlayer && target.supportedCommands.indexOf('DisplayContent') != -1) ? 'true' : 'false'; - html += ''; + html += ''; html += '