From 90ea72799852d799a39f759a2a72d6c8f6a2bed9 Mon Sep 17 00:00:00 2001 From: Luke Date: Sat, 6 Jun 2015 21:24:54 -0400 Subject: [PATCH 1/7] update card margins --- dashboard-ui/css/card.css | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dashboard-ui/css/card.css b/dashboard-ui/css/card.css index e166310677..8b5030fbfc 100644 --- a/dashboard-ui/css/card.css +++ b/dashboard-ui/css/card.css @@ -44,7 +44,7 @@ } .cardBox { - margin: 3px; + margin: 2px; } .largeCardMargin .cardBox { @@ -55,7 +55,7 @@ margin: 4px; } -@media all and (max-width: 600px) { +@media all and (max-width: 800px) { .cardBox { margin: 1px; From dfd651131a4957190c93c890797c7224c1927a13 Mon Sep 17 00:00:00 2001 From: Luke Date: Sat, 6 Jun 2015 22:51:04 -0400 Subject: [PATCH 2/7] remove bundled scripts --- dashboard-ui/livetvsettings.html | 6 +--- dashboard-ui/scripts/livetvsettings.js | 45 ++++++++++++------------ dashboard-ui/scripts/wizardagreement.js | 19 +++++----- dashboard-ui/scripts/wizardfinishpage.js | 16 ++++++--- dashboard-ui/scripts/wizardservice.js | 2 +- dashboard-ui/wizardagreement.html | 6 +--- dashboard-ui/wizardfinish.html | 4 +-- dashboard-ui/wizardservice.html | 2 +- 8 files changed, 48 insertions(+), 52 deletions(-) diff --git a/dashboard-ui/livetvsettings.html b/dashboard-ui/livetvsettings.html index a31669e0fd..6e09abc416 100644 --- a/dashboard-ui/livetvsettings.html +++ b/dashboard-ui/livetvsettings.html @@ -4,7 +4,7 @@ ${TitleLiveTV} -
+
@@ -70,10 +70,6 @@
- -
diff --git a/dashboard-ui/scripts/livetvsettings.js b/dashboard-ui/scripts/livetvsettings.js index dd5d1b5a23..d0c5eacc90 100644 --- a/dashboard-ui/scripts/livetvsettings.js +++ b/dashboard-ui/scripts/livetvsettings.js @@ -19,7 +19,29 @@ Dashboard.hideLoadingMsg(); } - $(document).on('pageshow', "#liveTvSettingsPage", function () { + function onSubmit() { + + Dashboard.showLoadingMsg(); + + var form = this; + + ApiClient.getNamedConfiguration("livetv").done(function (config) { + + config.GuideDays = $('#selectGuideDays', form).val() || null; + config.EnableMovieProviders = $('#chkMovies', form).checked(); + + ApiClient.updateNamedConfiguration("livetv", config).done(Dashboard.processServerConfigurationUpdateResult); + }); + + // Disable default form submission + return false; + } + + $(document).on('pageinitdepends', "#liveTvSettingsPage", function () { + + $('.liveTvSettingsForm').off('submit', onSubmit).on('submit', onSubmit); + + }).on('pageshowready', "#liveTvSettingsPage", function () { Dashboard.showLoadingMsg(); @@ -37,25 +59,4 @@ }); - window.LiveTvSettingsPage = { - - onSubmit: function () { - - Dashboard.showLoadingMsg(); - - var form = this; - - ApiClient.getNamedConfiguration("livetv").done(function (config) { - - config.GuideDays = $('#selectGuideDays', form).val() || null; - config.EnableMovieProviders = $('#chkMovies', form).checked(); - - ApiClient.updateNamedConfiguration("livetv", config).done(Dashboard.processServerConfigurationUpdateResult); - }); - - // Disable default form submission - return false; - } - }; - })(jQuery, document, window); diff --git a/dashboard-ui/scripts/wizardagreement.js b/dashboard-ui/scripts/wizardagreement.js index 0919b6c2cb..ade27ad6c5 100644 --- a/dashboard-ui/scripts/wizardagreement.js +++ b/dashboard-ui/scripts/wizardagreement.js @@ -1,6 +1,8 @@ (function (window, $) { - function onSubmit(page) { + function onSubmit() { + + var page = $(this).parents('.page'); if ($('#chkAccept', page).checked()) { Dashboard.navigate('wizardfinish.html'); @@ -11,18 +13,13 @@ title: '' }); } + + return false; } - window.WizardAgreementPage = { + $(document).on('pageinitdepends', '#wizardAgreementPage', function(){ - onSubmit: function () { - - var page = $(this).parents('.page'); - - onSubmit(page); - - return false; - } - }; + $('.wizardAgreementForm').off('submit', onSubmit).on('submit', onSubmit); + }); })(window, jQuery); \ No newline at end of file diff --git a/dashboard-ui/scripts/wizardfinishpage.js b/dashboard-ui/scripts/wizardfinishpage.js index 28834db4c3..7f641de41d 100644 --- a/dashboard-ui/scripts/wizardfinishpage.js +++ b/dashboard-ui/scripts/wizardfinishpage.js @@ -1,8 +1,8 @@ -var WizardFinishPage = { +(function(){ - onFinish: function () { + function onFinish() { - ApiClient.ajax({ + ApiClient.ajax({ url: ApiClient.getUrl('Startup/Complete'), type: 'POST' @@ -11,5 +11,11 @@ Dashboard.navigate('dashboard.html'); }); - } -}; \ No newline at end of file + } + + $(document).on('pageinitdepends', '#wizardFinishPage', function(){ + + $('.btnWizardNext', this).on('click', onFinish); + }); + +})(); \ No newline at end of file diff --git a/dashboard-ui/scripts/wizardservice.js b/dashboard-ui/scripts/wizardservice.js index dc8922c6c8..2f68245ce7 100644 --- a/dashboard-ui/scripts/wizardservice.js +++ b/dashboard-ui/scripts/wizardservice.js @@ -1,6 +1,6 @@ (function ($, document) { - $(document).on('pageinit', "#wizardServicePage", function () { + $(document).on('pageinitdepends', "#wizardServicePage", function () { var page = this; diff --git a/dashboard-ui/wizardagreement.html b/dashboard-ui/wizardagreement.html index 2ac400e2b9..28c35eff79 100644 --- a/dashboard-ui/wizardagreement.html +++ b/dashboard-ui/wizardagreement.html @@ -4,7 +4,7 @@ Emby -
+
@@ -36,10 +36,6 @@
- -
diff --git a/dashboard-ui/wizardfinish.html b/dashboard-ui/wizardfinish.html index bfe31a3a2c..a46c931714 100644 --- a/dashboard-ui/wizardfinish.html +++ b/dashboard-ui/wizardfinish.html @@ -4,7 +4,7 @@ Emby -
+
@@ -39,7 +39,7 @@
- +
diff --git a/dashboard-ui/wizardservice.html b/dashboard-ui/wizardservice.html index 47f33a2bb3..047a0f655a 100644 --- a/dashboard-ui/wizardservice.html +++ b/dashboard-ui/wizardservice.html @@ -4,7 +4,7 @@ Emby -
+
From 2bcdb9060a0d508f2cd3d4375e61e9edd22d07bd Mon Sep 17 00:00:00 2001 From: Luke Date: Sat, 6 Jun 2015 22:51:20 -0400 Subject: [PATCH 3/7] update server delete function --- dashboard-ui/scripts/selectserver.js | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/dashboard-ui/scripts/selectserver.js b/dashboard-ui/scripts/selectserver.js index 3c058be5aa..39c571f1ba 100644 --- a/dashboard-ui/scripts/selectserver.js +++ b/dashboard-ui/scripts/selectserver.js @@ -1,6 +1,7 @@ (function () { - function connectToServer(page, server) { + var serverList = []; + function connectToServer(page, server) { Dashboard.showLoadingMsg(); @@ -109,6 +110,8 @@ function renderServers(page, servers) { + serverList = servers; + if (servers.length) { $('.noServersMessage', page).hide(); } else { @@ -178,7 +181,13 @@ ConnectionManager.deleteServer(id).done(function () { Dashboard.hideModalLoadingMsg(); - loadPage(page); + + // Just re-render the servers without discovering them again + // If we re-discover then the one they deleted may just come back + var newServerList = serverList.filter(function(s){ + return s.Id != id; + }); + renderServers(page, newServerList); }).fail(function () { @@ -195,6 +204,7 @@ ConnectionManager.rejectServer(id).done(function () { Dashboard.hideModalLoadingMsg(); + loadPage(page); }).fail(function () { From 723d7e4485d78c152adaeeade88da1dc11be91c4 Mon Sep 17 00:00:00 2001 From: Luke Date: Sun, 7 Jun 2015 15:13:28 -0400 Subject: [PATCH 4/7] update collection page --- dashboard-ui/scripts/moviecollections.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/dashboard-ui/scripts/moviecollections.js b/dashboard-ui/scripts/moviecollections.js index 93cfc97612..639f815ac4 100644 --- a/dashboard-ui/scripts/moviecollections.js +++ b/dashboard-ui/scripts/moviecollections.js @@ -257,7 +257,7 @@ reloadItems(page); }); - }).on('pagebeforeshowready', "#boxsetsPage", function () { + }).on('pageshowready', "#boxsetsPage", function () { var page = this; @@ -273,6 +273,9 @@ $('.collectionTabs', page).show(); $('.movieTabs', page).hide(); query.ParentId = LibraryMenu.getTopParentId(); + + // Doing this for now due to reports of the page being empty for AutoBoxSet users + query.ParentId = null; } var limit = LibraryBrowser.getDefaultPageSize(); @@ -296,8 +299,6 @@ } }); - }).on('pageshowready', "#boxsetsPage", function () { - updateFilterControls(this); }); From 5f45adbf56dc86cae6409045118a051e89d12e54 Mon Sep 17 00:00:00 2001 From: Luke Date: Sun, 7 Jun 2015 15:13:44 -0400 Subject: [PATCH 5/7] normalize url --- dashboard-ui/thirdparty/apiclient/connectionmanager.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/dashboard-ui/thirdparty/apiclient/connectionmanager.js b/dashboard-ui/thirdparty/apiclient/connectionmanager.js index bd17e7003b..120b6237e3 100644 --- a/dashboard-ui/thirdparty/apiclient/connectionmanager.js +++ b/dashboard-ui/thirdparty/apiclient/connectionmanager.js @@ -964,6 +964,9 @@ function normalizeAddress(address) { + // attempt to correct bad input + address = address.trim(); + if (address.toLowerCase().indexOf('http') != 0) { address = "http://" + address; } From db61c30557497c2e6b0b449ff9ad3d9c5baf42ce Mon Sep 17 00:00:00 2001 From: Luke Date: Sun, 7 Jun 2015 15:24:45 -0400 Subject: [PATCH 6/7] handle empty artists --- dashboard-ui/scripts/edititemmetadata.js | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/dashboard-ui/scripts/edititemmetadata.js b/dashboard-ui/scripts/edititemmetadata.js index 301c6287f6..0b77479c0e 100644 --- a/dashboard-ui/scripts/edititemmetadata.js +++ b/dashboard-ui/scripts/edititemmetadata.js @@ -808,7 +808,11 @@ function getAlbumArtists(form) { - return $('#txtAlbumArtist', form).val().split(';').map(function (a) { + return $('#txtAlbumArtist', form).val().trim().split(';').filter(function(s){ + + return s.length > 0; + + }).map(function (a) { return { Name: a @@ -818,7 +822,11 @@ function getArtists(form) { - return $('#txtArtist', form).val().split(';').map(function (a) { + return $('#txtArtist', form).val().trim().split(';').filter(function(s){ + + return s.length > 0; + + }).map(function (a) { return { Name: a From 440bfc832ad1c48846b76cfa4bb0a27ecf557519 Mon Sep 17 00:00:00 2001 From: Luke Date: Sun, 7 Jun 2015 16:06:18 -0400 Subject: [PATCH 7/7] modularize media player --- dashboard-ui/css/librarymenu.css | 2 +- dashboard-ui/scripts/mediaplayer-video.js | 52 +-- dashboard-ui/scripts/mediaplayer.js | 458 ++++++++++++++++------ 3 files changed, 353 insertions(+), 159 deletions(-) diff --git a/dashboard-ui/css/librarymenu.css b/dashboard-ui/css/librarymenu.css index fdbe317dd0..b4d6d4bf44 100644 --- a/dashboard-ui/css/librarymenu.css +++ b/dashboard-ui/css/librarymenu.css @@ -282,7 +282,7 @@ } } -@media all and (max-width: 1200px) { +@media all and (max-width: 800px) { /* They can use the left menu */ .dashboardEntryHeaderButton { diff --git a/dashboard-ui/scripts/mediaplayer-video.js b/dashboard-ui/scripts/mediaplayer-video.js index 15a7a80bc5..930c684039 100644 --- a/dashboard-ui/scripts/mediaplayer-video.js +++ b/dashboard-ui/scripts/mediaplayer-video.js @@ -271,7 +271,7 @@ var mediaControls = $("#videoPlayer"); - var state = self.getPlayerStateInternal(self.currentMediaElement, item, self.currentMediaSource); + var state = self.getPlayerStateInternal(self.currentMediaRenderer, item, self.currentMediaSource); var url = ""; var imageWidth = 400; @@ -751,7 +751,7 @@ return currentStream.Type == "Audio"; }); - var currentIndex = getParameterByName('AudioStreamIndex', self.getCurrentSrc(self.currentMediaElement)); + var currentIndex = getParameterByName('AudioStreamIndex', self.getCurrentSrc(self.currentMediaRenderer)); var html = ''; html += '
'; @@ -917,7 +917,7 @@ function getQualityFlyoutHtml() { - var currentSrc = self.getCurrentSrc(self.currentMediaElement).toLowerCase(); + var currentSrc = self.getCurrentSrc(self.currentMediaRenderer).toLowerCase(); var isStatic = currentSrc.indexOf('static=true') != -1; var videoStream = self.currentMediaSource.MediaStreams.filter(function (stream) { @@ -1042,13 +1042,11 @@ }; // Replace audio version - self.cleanup = function (playerElement) { + self.cleanup = function (mediaRenderer) { - if (playerElement.tagName.toLowerCase() == 'video') { - currentTimeElement.html('--:--'); + currentTimeElement.html('--:--'); - unbindEventsForPlayback(); - } + unbindEventsForPlayback(); }; self.playVideo = function (item, mediaSource, startPosition) { @@ -1189,41 +1187,18 @@ videoControls.removeClass('hide'); } - var video = $("video", videoElement); + var mediaRenderer = new HtmlMediaRenderer('video'); initialVolume = self.getSavedVolume(); - video.each(function () { - this.volume = initialVolume; - }); + mediaRenderer.volume(initialVolume); volumeSlider.val(initialVolume).slider('refresh'); updateVolumeButtons(initialVolume); - video.one("loadedmetadata.mediaplayerevent", function (e) { + $(mediaRenderer).on("volumechange.mediaplayerevent", function (e) { - // The IE video player won't autoplay without this - if ($.browser.msie) { - this.play(); - } - - }).one("playing.mediaplayerevent", function (e) { - - // TODO: This is not working in chrome. Is it too early? - - // Appending #t=xxx to the query string doesn't seem to work with HLS - if (startPositionInSeekParam && this.currentSrc && this.currentSrc.toLowerCase().indexOf('.m3u8') != -1) { - var element = this; - setTimeout(function () { - element.currentTime = startPositionInSeekParam; - }, 3000); - } - - }).on("volumechange.mediaplayerevent", function (e) { - - var vol = this.volume; - - updateVolumeButtons(vol); + updateVolumeButtons(this.volume()); }).one("playing.mediaplayerevent", function () { @@ -1262,9 +1237,6 @@ self.stop(); - var errorCode = this.error ? this.error.code : ''; - console.log('Html5 Video error code: ' + errorCode); - var errorMsg = Globalize.translate('MessageErrorPlayingVideo'); if (item.Type == "TvChannel") { @@ -1289,7 +1261,7 @@ }).on("click.mediaplayerevent", function (e) { if (!$.browser.mobile) { - if (this.paused) { + if (this.paused()) { self.unpause(); } else { self.pause(); @@ -1311,7 +1283,7 @@ $('body').addClass('bodyWithPopupOpen'); - self.currentMediaElement = video[0]; + self.currentMediaRenderer = mediaRenderer; self.currentDurationTicks = self.currentMediaSource.RunTimeTicks; self.updateNowPlayingInfo(item); diff --git a/dashboard-ui/scripts/mediaplayer.js b/dashboard-ui/scripts/mediaplayer.js index 1d46c21b35..2f45aac62b 100644 --- a/dashboard-ui/scripts/mediaplayer.js +++ b/dashboard-ui/scripts/mediaplayer.js @@ -4,12 +4,11 @@ var self = this; - var testableVideoElement = document.createElement('video'); var currentProgressInterval; var canClientSeek; var currentPlaylistIndex = 0; - self.currentMediaElement = null; + self.currentMediaRenderer = null; self.currentItem = null; self.currentMediaSource = null; @@ -395,20 +394,20 @@ return profile; }; - self.updateCanClientSeek = function (elem) { + self.updateCanClientSeek = function (mediaRenderer) { - var duration = elem.duration; + var duration = mediaRenderer.duration(); canClientSeek = duration && !isNaN(duration) && duration != Number.POSITIVE_INFINITY && duration != Number.NEGATIVE_INFINITY; }; - self.getCurrentSrc = function (mediaElement) { - return mediaElement.currentSrc; + self.getCurrentSrc = function (mediaRenderer) { + return mediaRenderer.currentSrc(); }; - self.getCurrentTicks = function (mediaElement) { + self.getCurrentTicks = function (mediaRenderer) { - var playerTime = Math.floor(10000000 * (mediaElement || self.currentMediaElement).currentTime); + var playerTime = Math.floor(10000000 * (mediaRenderer || self.currentMediaRenderer).currentTime()); playerTime += self.startTimeTicksOffset; @@ -429,7 +428,7 @@ currentProgressInterval = setInterval(function () { - if (self.currentMediaElement) { + if (self.currentMediaRenderer) { if ((new Date().getTime() - self.lastProgressReport) > intervalTime) { @@ -449,7 +448,7 @@ self.canPlayNativeHls = function () { - var media = testableVideoElement; + var media = document.createElement('video'); // safari if (media.canPlayType('application/x-mpegURL').replace(/no/, '') || @@ -472,17 +471,17 @@ self.changeStream = function (ticks, params) { - var element = self.currentMediaElement; + var mediaRenderer = self.currentMediaRenderer; if (canClientSeek && params == null) { - element.currentTime = ticks / (1000 * 10000); + mediaRenderer.currentTime(ticks / (1000 * 10000)); return; } params = params || {}; - var currentSrc = element.currentSrc; + var currentSrc = mediaRenderer.currentSrc(); var playSessionId = getParameterByName('PlaySessionId', currentSrc); var liveStreamId = getParameterByName('LiveStreamId', currentSrc); @@ -490,7 +489,7 @@ if (params.AudioStreamIndex == null && params.SubtitleStreamIndex == null && params.Bitrate == null) { currentSrc = replaceQueryString(currentSrc, 'starttimeticks', ticks || 0); - changeStreamToUrl(element, playSessionId, currentSrc, ticks); + changeStreamToUrl(mediaRenderer, playSessionId, currentSrc, ticks); return; } @@ -514,23 +513,16 @@ self.currentSubtitleStreamIndex = subtitleStreamIndex; currentSrc = ApiClient.getUrl(self.currentMediaSource.TranscodingUrl); - changeStreamToUrl(element, playSessionId, currentSrc, ticks); + changeStreamToUrl(mediaRenderer, playSessionId, currentSrc, ticks); } }); }; - function changeStreamToUrl(element, playSessionId, url, newPositionTicks) { + function changeStreamToUrl(mediaRenderer, playSessionId, url, newPositionTicks) { clearProgressInterval(); - $(element).off('ended.playbackstopped').off('ended.playnext').one("loadedmetadata.mediaplayerevent", function (e) { - - // The IE video player won't autoplay without this - if ($.browser.msie && self.currentItem.MediaType == "Video") { - this.play(); - } - - }).one("play", function () { + $(mediaRenderer).off('ended.playbackstopped').off('ended.playnext').one("play", function () { self.updateCanClientSeek(this); @@ -545,15 +537,14 @@ ApiClient.stopActiveEncodings(playSessionId).done(function () { self.startTimeTicksOffset = newPositionTicks; - element.src = url; + mediaRenderer.setCurrentSrc(url); }); self.updateTextStreamUrls(newPositionTicks || 0); } else { self.startTimeTicksOffset = newPositionTicks; - element.src = url; - element.play(); + mediaRenderer.setCurrentSrc(url); } } @@ -587,7 +578,7 @@ currentTimeElement.html(timeText); } - var state = self.getPlayerStateInternal(self.currentMediaElement, self.currentItem, self.currentMediaSource); + var state = self.getPlayerStateInternal(self.currentMediaRenderer, self.currentItem, self.currentMediaSource); $(self).trigger('positionchange', [state]); }; @@ -902,7 +893,7 @@ } else if (item.MediaType === "Audio") { - self.currentMediaElement = playAudio(item, self.currentMediaSource, startPosition); + self.currentMediaRenderer = playAudio(item, self.currentMediaSource, startPosition); self.currentDurationTicks = self.currentMediaSource.RunTimeTicks; } @@ -1071,7 +1062,7 @@ self.queue = function (options) { - if (!self.currentMediaElement) { + if (!self.playlist.length) { self.play(options); return; } @@ -1105,7 +1096,7 @@ self.queueNext = function (options) { - if (!self.currentMediaElement) { + if (!self.playlist.length) { self.play(options); return; } @@ -1136,11 +1127,11 @@ self.pause = function () { - self.currentMediaElement.pause(); + self.currentMediaRenderer.pause(); }; self.unpause = function () { - self.currentMediaElement.play(); + self.currentMediaRenderer.unpause(); }; self.seek = function (position) { @@ -1159,12 +1150,12 @@ }; self.volume = function () { - return self.currentMediaElement.volume * 100; + return self.currentMediaRenderer.volume() * 100; }; self.toggleMute = function () { - if (self.currentMediaElement) { + if (self.currentMediaRenderer) { console.log('MediaPlayer toggling mute'); @@ -1178,14 +1169,14 @@ self.volumeDown = function () { - if (self.currentMediaElement) { + if (self.currentMediaRenderer) { self.setVolume(Math.max(self.volume() - 2, 0)); } }; self.volumeUp = function () { - if (self.currentMediaElement) { + if (self.currentMediaRenderer) { self.setVolume(Math.min(self.volume() + 2, 100)); } }; @@ -1193,14 +1184,14 @@ // Sets volume using a 0-100 scale self.setVolume = function (val) { - if (self.currentMediaElement) { + if (self.currentMediaRenderer) { console.log('MediaPlayer setting volume to ' + val); - self.currentMediaElement.volume = val / 100; + self.currentMediaRenderer.volume(val / 100); - self.onVolumeChanged(self.currentMediaElement); + self.onVolumeChanged(self.currentMediaRenderer); - self.saveVolume(); + //self.saveVolume(); } }; @@ -1333,36 +1324,25 @@ self.stop = function () { - var elem = self.currentMediaElement; + var mediaRenderer = self.currentMediaRenderer; - if (elem) { + if (mediaRenderer) { - elem.pause(); + mediaRenderer.pause(); - $(elem).off("ended.playnext").one("ended", function () { + $(mediaRenderer).off("ended.playnext").one("ended", function () { $(this).off(); - if (this.tagName.toLowerCase() != 'audio') { - $(this).remove(); - } - - elem.src = null; - elem.src = ""; - self.currentMediaElement = null; + this.destroy(); + self.currentMediaRenderer = null; self.currentItem = null; self.currentMediaSource = null; - // When the browser regains focus it may start auto-playing the last video - if ($.browser.safari) { - elem.src = 'files/dummy.mp4'; - elem.play(); - } - }).trigger("ended"); } else { - self.currentMediaElement = null; + self.currentMediaRenderer = null; self.currentItem = null; self.currentMediaSource = null; } @@ -1374,34 +1354,34 @@ }; self.isPlaying = function () { - return self.currentMediaElement != null; + return self.playlist.length > 0; }; self.getPlayerState = function () { var deferred = $.Deferred(); - var result = self.getPlayerStateInternal(self.currentMediaElement, self.currentItem, self.currentMediaSource); + var result = self.getPlayerStateInternal(self.currentMediaRenderer, self.currentItem, self.currentMediaSource); deferred.resolveWith(null, [result]); return deferred.promise(); }; - self.getPlayerStateInternal = function (playerElement, item, mediaSource) { + self.getPlayerStateInternal = function (mediaRenderer, item, mediaSource) { var state = { PlayState: {} }; - if (playerElement) { + if (mediaRenderer) { - state.PlayState.VolumeLevel = playerElement.volume * 100; - state.PlayState.IsMuted = playerElement.volume == 0; - state.PlayState.IsPaused = playerElement.paused; - state.PlayState.PositionTicks = self.getCurrentTicks(playerElement); + state.PlayState.VolumeLevel = mediaRenderer.volume() * 100; + state.PlayState.IsMuted = mediaRenderer.volume() == 0; + state.PlayState.IsPaused = mediaRenderer.paused(); + state.PlayState.PositionTicks = self.getCurrentTicks(mediaRenderer); - var currentSrc = playerElement.currentSrc; + var currentSrc = mediaRenderer.currentSrc(); if (currentSrc) { @@ -1521,22 +1501,22 @@ // Nothing to setup here }; - self.onPlaybackStart = function (playerElement, item, mediaSource) { + self.onPlaybackStart = function (mediaRenderer, item, mediaSource) { - self.updateCanClientSeek(playerElement); + self.updateCanClientSeek(mediaRenderer); - var state = self.getPlayerStateInternal(playerElement, item, mediaSource); + var state = self.getPlayerStateInternal(mediaRenderer, item, mediaSource); $(self).trigger('playbackstart', [state]); self.startProgressInterval(); }; - self.onVolumeChanged = function (playerElement) { + self.onVolumeChanged = function (mediaRenderer) { - self.saveVolume(playerElement.volume); + self.saveVolume(mediaRenderer.volume()); - var state = self.getPlayerStateInternal(playerElement, self.currentItem, self.currentMediaSource); + var state = self.getPlayerStateInternal(mediaRenderer, self.currentItem, self.currentMediaSource); $(self).trigger('volumechange', [state]); }; @@ -1551,11 +1531,11 @@ $('body').removeClass('bodyWithPopupOpen'); - var playerElement = this; + var mediaRenderer = this; - $(playerElement).off('.mediaplayerevent').off('ended.playbackstopped'); + $(mediaRenderer).off('.mediaplayerevent').off('ended.playbackstopped'); - self.cleanup(playerElement); + self.cleanup(mediaRenderer); clearProgressInterval(); @@ -1570,14 +1550,14 @@ self.resetEnhancements(); } - var state = self.getPlayerStateInternal(playerElement, item, mediaSource); + var state = self.getPlayerStateInternal(mediaRenderer, item, mediaSource); $(self).trigger('playbackstop', [state]); }; - self.onPlaystateChange = function (playerElement) { + self.onPlaystateChange = function (mediaRenderer) { - var state = self.getPlayerStateInternal(playerElement, self.currentItem, self.currentMediaSource); + var state = self.getPlayerStateInternal(mediaRenderer, self.currentItem, self.currentMediaSource); $(self).trigger('playstatechange', [state]); }; @@ -1585,16 +1565,15 @@ $(window).on("beforeunload", function () { // Try to report playback stopped before the browser closes - if (self.currentItem && self.currentMediaElement && currentProgressInterval) { + if (self.currentItem && self.currentMediaRenderer && currentProgressInterval) { - self.onPlaybackStopped.call(self.currentMediaElement); + self.onPlaybackStopped.call(self.currentMediaRenderer); } }); function sendProgressUpdate() { - var element = self.currentMediaElement; - var state = self.getPlayerStateInternal(element, self.currentItem, self.currentMediaSource); + var state = self.getPlayerStateInternal(self.currentMediaRenderer, self.currentItem, self.currentMediaSource); var info = { QueueableMediaTypes: state.NowPlayingItem.MediaType, @@ -1615,9 +1594,13 @@ } } + self._canPlayWebm = null; self.canPlayWebm = function () { - return testableVideoElement.canPlayType('video/webm').replace(/no/, ''); + if (self._canPlayWebm == null) { + self._canPlayWebm = document.createElement('video').canPlayType('video/webm').replace(/no/, ''); + } + return self._canPlayWebm; }; self.canAutoPlayAudio = function () { @@ -1633,30 +1616,9 @@ return true; }; - function getAudioElement() { + function getAudioRenderer() { - var elem = $('.mediaPlayerAudio'); - - if (elem.length) { - return elem; - } - - var html = ''; - - var requiresControls = !self.canAutoPlayAudio(); - - if (requiresControls) { - html += '
';; - } else { - html += ''; - - $(document.body).append(html); - - return $('.mediaPlayerAudio'); + return new HtmlMediaRenderer('audio'); } function onTimeUpdate() { @@ -1673,14 +1635,13 @@ var initialVolume = self.getSavedVolume(); - return getAudioElement().each(function () { + var mediaRenderer = getAudioRenderer(); - this.src = audioUrl; - this.volume = initialVolume; - this.poster = self.getPosterUrl(item); - this.play(); + mediaRenderer.setCurrentSrc(audioUrl); + mediaRenderer.volume(initialVolume); + mediaRenderer.setPoster(self.getPosterUrl(item)); - }).on("volumechange.mediaplayerevent", function () { + $(mediaRenderer).on("volumechange.mediaplayerevent", function () { console.log('audio element event: volumechange'); @@ -1690,8 +1651,6 @@ console.log('audio element event: playing'); - $('.mediaPlayerAudioContainer').hide(); - // For some reason this is firing at the start, so don't bind until playback has begun $(this).on("ended.playbackstopped", self.onPlaybackStopped).one('ended.playnext', self.playNextAfterEnded); @@ -1715,7 +1674,9 @@ // In the event timeupdate isn't firing, at least we can update when this happens self.setCurrentTime(self.getCurrentTicks()); - }).on("timeupdate.mediaplayerevent", onTimeUpdate)[0]; + }).on("timeupdate.mediaplayerevent", onTimeUpdate); + + return mediaRenderer; }; var getItemFields = "MediaSources,Chapters"; @@ -1736,4 +1697,265 @@ }); -})(document, setTimeout, clearTimeout, screen, window.appStorage, $, setInterval, window); \ No newline at end of file +})(document, setTimeout, clearTimeout, screen, window.appStorage, $, setInterval, window); + +(function(){ + + function htmlMediaRenderer(type) { + + var mediaElement; + var self = this; + + function onEnded() { + $(self).trigger('ended'); + } + + function onTimeUpdate() { + $(self).trigger('timeupdate'); + } + + function onVolumeChange() { + $(self).trigger('volumechange'); + } + + function onOneAudioPlaying() { + $('.mediaPlayerAudioContainer').hide(); + } + + function onPlaying() { + $(self).trigger('playing'); + } + + function onPlay() { + $(self).trigger('play'); + } + + function onPause() { + $(self).trigger('pause'); + } + + function onClick() { + $(self).trigger('click'); + } + + function onDblClick() { + $(self).trigger('dblclick'); + } + + function onError() { + + var errorCode = this.error ? this.error.code : ''; + console.log('Media element error code: ' + errorCode); + + $(self).trigger('error'); + } + + function onLoadedMetadata() { + + // The IE video player won't autoplay without this + if ($.browser.msie) { + this.play(); + } + } + + function onOneVideoPlaying() { + + var currentSrc = (this.currentSrc || '').toLowerCase(); + + var parts = currentSrc.split('#'); + + if (parts.length > 1) { + + var parts = parts[parts.length -1].split('='); + + if (parts.length == 2) { + + var startPositionInSeekParam = parseFloat(parts[1]); + + // Appending #t=xxx to the query string doesn't seem to work with HLS + if (startPositionInSeekParam && currentSrc.indexOf('.m3u8') != -1) { + var element = this; + setTimeout(function () { + element.currentTime = startPositionInSeekParam; + }, 3000); + } + } + } + } + + function createAudioElement() { + + var elem = $('.mediaPlayerAudio'); + + if (!elem.length) { + var html = ''; + + var requiresControls = !MediaPlayer.canAutoPlayAudio(); + + if (requiresControls) { + html += '
';; + } else { + html += ''; + + $(document.body).append(html); + + elem = $('.mediaPlayerAudio'); + } + + return $(elem) + .on('timeupdate', onTimeUpdate) + .on('ended', onEnded) + .on('volumechange', onVolumeChange) + .one('playing', onOneAudioPlaying) + .on('play', onPlay) + .on('pause', onPause) + .on('playing', onPlaying) + .on('error', onError)[0]; + } + + function createVideoElement() { + + var elem = $('.itemVideo'); + + return $(elem) + .one('.loadedmetadata') + .one('playing', onOneVideoPlaying) + .on('timeupdate', onTimeUpdate) + .on('ended', onEnded) + .on('volumechange', onVolumeChange) + .on('play', onPlay) + .on('pause', onPause) + .on('playing', onPlaying) + .on('error', onError)[0]; + } + + self.currentTime = function(val) { + + if (mediaElement) { + if (val != null) { + mediaElement.currentTime = val; + return; + } + + return mediaElement.currentTime; + } + }; + + self.duration = function(val) { + + if (mediaElement) { + return mediaElement.duration; + } + + return null; + }; + + self.pause = function() { + if (mediaElement) { + mediaElement.pause(); + } + }; + + self.unpause = function() { + if (mediaElement) { + mediaElement.play(); + } + }; + + self.volume = function(val) { + if (mediaElement) { + if (val != null) { + mediaElement.volume = val; + return; + } + + return mediaElement.volume; + } + }; + + self.setCurrentSrc = function(val) { + + var elem = mediaElement; + + if (!elem) { + return; + } + + if (!val) { + elem.src = null; + elem.src = ""; + + // When the browser regains focus it may start auto-playing the last video + if ($.browser.safari) { + elem.src = 'files/dummy.mp4'; + elem.play(); + } + + return; + } + + elem.src = val; + + if (elem.tagName.toLowerCase() == 'audio') { + elem.play(); + } + else { + + $(elem).one("loadedmetadata.mediaplayerevent", onLoadedMetadata); + } + }; + + self.currentSrc = function() { + if (mediaElement) { + return mediaElement.currentSrc; + } + }; + + self.paused = function() { + + if (mediaElement) { + return mediaElement.paused; + } + + return false; + }; + + self.destroy = function() { + + self.setCurrentSrc(null); + + var elem = mediaElement; + + if (elem) { + + $(elem).off(); + + if (elem.tagName.toLowerCase() != 'audio') { + $(elem).remove(); + } + } + }; + + self.setPoster = function(url) { + var elem = mediaElement; + + if (elem) { + elem.poster = url; + } + }; + + if (type == 'audio') { + mediaElement = createAudioElement(); + } + else { + mediaElement = createVideoElement(); + } + } + + window.HtmlMediaRenderer = htmlMediaRenderer; + +})(); \ No newline at end of file