diff --git a/src/components/apphost.js b/src/components/apphost.js index ad3540e210..b1b1c30dc9 100644 --- a/src/components/apphost.js +++ b/src/components/apphost.js @@ -278,6 +278,7 @@ define(["appSettings", "browser", "events", "htmlMediaHelper"], function (appSet features.push("targetblank"); // allows users to connect to more than one server //features.push("multiserver"); + features.push("screensaver"); if (!browser.orsay && !browser.tizen && !browser.msie && (browser.firefox || browser.ps4 || browser.edge || cueSupported())) { features.push("subtitleappearancesettings"); diff --git a/src/components/backdropscreensaver/plugin.js b/src/components/backdropscreensaver/plugin.js new file mode 100644 index 0000000000..c0bd31fb86 --- /dev/null +++ b/src/components/backdropscreensaver/plugin.js @@ -0,0 +1,56 @@ +define(["connectionManager"], function (connectionManager) { + + return function () { + + var self = this; + + self.name = "Backdrop ScreenSaver"; + self.type = "screensaver"; + self.id = "backdropscreensaver"; + self.supportsAnonymous = false; + + var currentSlideshow; + + self.show = function () { + + var query = { + ImageTypes: "Backdrop", + EnableImageTypes: "Backdrop", + IncludeItemTypes: "Movie,Series,MusicArtist", + SortBy: "Random", + Recursive: true, + Fields: "Taglines", + ImageTypeLimit: 1, + StartIndex: 0, + Limit: 200 + }; + + var apiClient = connectionManager.currentApiClient(); + apiClient.getItems(apiClient.getCurrentUserId(), query).then(function (result) { + + if (result.Items.length) { + + require(["slideshow"], function (slideshow) { + + var newSlideShow = new slideshow({ + showTitle: true, + cover: true, + items: result.Items + }); + + newSlideShow.show(); + currentSlideshow = newSlideShow; + }); + } + }); + }; + + self.hide = function () { + + if (currentSlideshow) { + currentSlideshow.hide(); + currentSlideshow = null; + } + }; + } +}); diff --git a/src/components/logoscreensaver/logowhite.png b/src/components/logoscreensaver/logowhite.png new file mode 100644 index 0000000000..560d910d74 Binary files /dev/null and b/src/components/logoscreensaver/logowhite.png differ diff --git a/src/components/logoscreensaver/plugin.js b/src/components/logoscreensaver/plugin.js new file mode 100644 index 0000000000..521afd2690 --- /dev/null +++ b/src/components/logoscreensaver/plugin.js @@ -0,0 +1,192 @@ +define(["pluginManager"], function (pluginManager) { + + return function () { + + var self = this; + + self.name = "Logo ScreenSaver"; + self.type = "screensaver"; + self.id = "logoscreensaver"; + self.supportsAnonymous = true; + + var interval; + + function animate() { + + var animations = [ + + bounceInLeft, + bounceInRight, + swing, + tada, + wobble, + rotateIn, + rotateOut + ]; + + var elem = document.querySelector(".logoScreenSaverImage"); + + if (elem && elem.animate) { + var random = getRandomInt(0, animations.length - 1); + + animations[random](elem, 1); + } + } + + function getRandomInt(min, max) { + return Math.floor(Math.random() * (max - min + 1)) + min; + } + + function bounceInLeft(elem, iterations) { + var keyframes = [ + { transform: "translate3d(-3000px, 0, 0)", opacity: "0", offset: 0 }, + { transform: "translate3d(25px, 0, 0)", opacity: "1", offset: 0.6 }, + { transform: "translate3d(-100px, 0, 0)", offset: 0.75 }, + { transform: "translate3d(5px, 0, 0)", offset: 0.9 }, + { transform: "none", opacity: "1", offset: 1 }]; + var timing = { duration: 900, iterations: iterations, easing: "cubic-bezier(0.215, 0.610, 0.355, 1.000)" }; + return elem.animate(keyframes, timing); + } + + function bounceInRight(elem, iterations) { + var keyframes = [ + { transform: "translate3d(3000px, 0, 0)", opacity: "0", offset: 0 }, + { transform: "translate3d(-25px, 0, 0)", opacity: "1", offset: 0.6 }, + { transform: "translate3d(100px, 0, 0)", offset: 0.75 }, + { transform: "translate3d(-5px, 0, 0)", offset: 0.9 }, + { transform: "none", opacity: "1", offset: 1 }]; + var timing = { duration: 900, iterations: iterations, easing: "cubic-bezier(0.215, 0.610, 0.355, 1.000)" }; + return elem.animate(keyframes, timing); + } + + function shake(elem, iterations) { + var keyframes = [ + { transform: "translate3d(0, 0, 0)", offset: 0 }, + { transform: "translate3d(-10px, 0, 0)", offset: 0.1 }, + { transform: "translate3d(10px, 0, 0)", offset: 0.2 }, + { transform: "translate3d(-10px, 0, 0)", offset: 0.3 }, + { transform: "translate3d(10px, 0, 0)", offset: 0.4 }, + { transform: "translate3d(-10px, 0, 0)", offset: 0.5 }, + { transform: "translate3d(10px, 0, 0)", offset: 0.6 }, + { transform: "translate3d(-10px, 0, 0)", offset: 0.7 }, + { transform: "translate3d(10px, 0, 0)", offset: 0.8 }, + { transform: "translate3d(-10px, 0, 0)", offset: 0.9 }, + { transform: "translate3d(0, 0, 0)", offset: 1 }]; + var timing = { duration: 900, iterations: iterations }; + return elem.animate(keyframes, timing); + } + + function swing(elem, iterations) { + var keyframes = [ + { transform: "translate(0%)", offset: 0 }, + { transform: "rotate3d(0, 0, 1, 15deg)", offset: 0.2 }, + { transform: "rotate3d(0, 0, 1, -10deg)", offset: 0.4 }, + { transform: "rotate3d(0, 0, 1, 5deg)", offset: 0.6 }, + { transform: "rotate3d(0, 0, 1, -5deg)", offset: 0.8 }, + { transform: "rotate3d(0, 0, 1, 0deg)", offset: 1 }]; + var timing = { duration: 900, iterations: iterations }; + return elem.animate(keyframes, timing); + } + + function tada(elem, iterations) { + var keyframes = [ + { transform: "scale3d(1, 1, 1)", offset: 0 }, + { transform: "scale3d(.9, .9, .9) rotate3d(0, 0, 1, -3deg)", offset: 0.1 }, + { transform: "scale3d(.9, .9, .9) rotate3d(0, 0, 1, -3deg)", offset: 0.2 }, + { transform: "scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, 3deg)", offset: 0.3 }, + { transform: "scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, -3deg)", offset: 0.4 }, + { transform: "scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, 3deg)", offset: 0.5 }, + { transform: "scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, -3deg)", offset: 0.6 }, + { transform: "scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, 3deg)", offset: 0.7 }, + { transform: "scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, -3deg)", offset: 0.8 }, + { transform: "scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, 3deg)", offset: 0.9 }, + { transform: "scale3d(1, 1, 1)", offset: 1 }]; + var timing = { duration: 900, iterations: iterations }; + return elem.animate(keyframes, timing); + } + + function wobble(elem, iterations) { + var keyframes = [ + { transform: "translate(0%)", offset: 0 }, + { transform: "translate3d(20%, 0, 0) rotate3d(0, 0, 1, 3deg)", offset: 0.15 }, + { transform: "translate3d(-15%, 0, 0) rotate3d(0, 0, 1, -3deg)", offset: 0.45 }, + { transform: "translate3d(10%, 0, 0) rotate3d(0, 0, 1, 2deg)", offset: 0.6 }, + { transform: "translate3d(-5%, 0, 0) rotate3d(0, 0, 1, -1deg)", offset: 0.75 }, + { transform: "translateX(0%)", offset: 1 }]; + var timing = { duration: 900, iterations: iterations }; + return elem.animate(keyframes, timing); + } + + function rotateIn(elem, iterations) { + var transformOrigin = elem.style["transform-origin"]; + var keyframes = [{ transform: "rotate3d(0, 0, 1, -200deg)", opacity: "0", transformOrigin: "center", offset: 0 }, + { transform: "none", opacity: "1", transformOrigin: "center", offset: 1 }]; + var timing = { duration: 900, iterations: iterations }; + return elem.animate(keyframes, timing); + } + + function rotateOut(elem, iterations) { + var transformOrigin = elem.style["transform-origin"]; + var keyframes = [{ transform: "none", opacity: "1", transformOrigin: "center", offset: 0 }, + { transform: "rotate3d(0, 0, 1, 200deg)", opacity: "0", transformOrigin: "center", offset: 1 }]; + var timing = { duration: 900, iterations: iterations }; + return elem.animate(keyframes, timing); + + } + + function fadeOut(elem, iterations) { + var keyframes = [ + { opacity: "1", offset: 0 }, + { opacity: "0", offset: 1 }]; + var timing = { duration: 400, iterations: iterations }; + return elem.animate(keyframes, timing); + } + + function stopInterval() { + if (interval) { + clearInterval(interval); + interval = null; + } + } + + self.show = function () { + + require(["css!" + pluginManager.mapPath(self, "style.css")], function () { + + var elem = document.querySelector(".logoScreenSaver"); + + if (!elem) { + elem = document.createElement("div"); + elem.classList.add("logoScreenSaver"); + document.body.appendChild(elem); + + elem.innerHTML = ''; + } + + stopInterval(); + interval = setInterval(animate, 3000); + }); + }; + + self.hide = function () { + + stopInterval(); + + var elem = document.querySelector(".logoScreenSaver"); + + if (elem) { + + var onAnimationFinish = function () { + elem.parentNode.removeChild(elem); + }; + + if (elem.animate) { + var animation = fadeOut(elem, 1); + animation.onfinish = onAnimationFinish; + } else { + onAnimationFinish(); + } + } + }; + } +}); diff --git a/src/components/logoscreensaver/style.css b/src/components/logoscreensaver/style.css new file mode 100644 index 0000000000..92c8139717 --- /dev/null +++ b/src/components/logoscreensaver/style.css @@ -0,0 +1,18 @@ +.logoScreenSaver { + background: #101010; + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + z-index: 1000; + text-align: center; +} + +.logoScreenSaverImage { + height: 120px; + position: absolute; + top: 50%; + margin-top: -60px; + margin-left: -197px; +} diff --git a/src/components/screensavermanager.js b/src/components/screensavermanager.js new file mode 100644 index 0000000000..5b7613c98d --- /dev/null +++ b/src/components/screensavermanager.js @@ -0,0 +1,132 @@ +define(["events", "playbackManager", "pluginManager", "inputManager", "connectionManager", "userSettings"], function (events, playbackManager, pluginManager, inputManager, connectionManager, userSettings) { + "use strict"; + + function getMinIdleTime() { + // Returns the minimum amount of idle time required before the screen saver can be displayed + //time units used Millisecond + return 180000; + } + + var lastFunctionalEvent = 0; + + function getFunctionalEventIdleTime() { + return new Date().getTime() - lastFunctionalEvent; + } + + events.on(playbackManager, "playbackstop", function (e, stopInfo) { + var state = stopInfo.state; + if (state.NowPlayingItem && state.NowPlayingItem.MediaType == "Video") { + lastFunctionalEvent = new Date().getTime(); + } + }); + + function getScreensaverPlugin(isLoggedIn) { + + var option; + try { + option = userSettings.get("screensaver", false); + } catch (err) { + option = isLoggedIn ? "backdropscreensaver" : "logoscreensaver"; + } + + var plugins = pluginManager.ofType("screensaver"); + + for (var i = 0, length = plugins.length; i < length; i++) { + var plugin = plugins[i]; + + if (plugin.id === option) { + return plugin; + } + } + + return null; + } + + function ScreenSaverManager() { + + var self = this; + var activeScreenSaver; + + function showScreenSaver(screensaver) { + + if (activeScreenSaver) { + throw new Error("An existing screensaver is already active."); + } + + console.log("Showing screensaver " + screensaver.name); + + screensaver.show(); + activeScreenSaver = screensaver; + + if (screensaver.hideOnClick !== false) { + window.addEventListener("click", hide, true); + } + if (screensaver.hideOnMouse !== false) { + window.addEventListener("mousemove", hide, true); + } + if (screensaver.hideOnKey !== false) { + window.addEventListener("keydown", hide, true); + } + } + + function hide() { + if (activeScreenSaver) { + console.log("Hiding screensaver"); + activeScreenSaver.hide(); + activeScreenSaver = null; + } + + window.removeEventListener("click", hide, true); + window.removeEventListener("mousemove", hide, true); + window.removeEventListener("keydown", hide, true); + } + + self.isShowing = function () { + return activeScreenSaver != null; + }; + + self.show = function () { + var isLoggedIn; + var apiClient = connectionManager.currentApiClient(); + + if (apiClient && apiClient.isLoggedIn()) { + isLoggedIn = true; + } + + var screensaver = getScreensaverPlugin(isLoggedIn); + + if (screensaver) { + showScreenSaver(screensaver); + } + }; + + self.hide = function () { + hide(); + }; + + function onInterval() { + + if (self.isShowing()) { + return; + } + + if (inputManager.idleTime() < getMinIdleTime()) { + return; + } + + if (getFunctionalEventIdleTime < getMinIdleTime()) { + return; + } + + if (playbackManager.isPlayingVideo()) { + return; + } + + self.show(); + } + + setInterval(onInterval, 10000); + } + + return new ScreenSaverManager(); +}); diff --git a/src/scripts/site.js b/src/scripts/site.js index 14d7d01bbb..6deb620c40 100644 --- a/src/scripts/site.js +++ b/src/scripts/site.js @@ -532,7 +532,9 @@ var AppInfo = {}; "components/htmlaudioplayer/plugin", "components/htmlvideoplayer/plugin", "components/photoplayer/plugin", - "components/youtubeplayer/plugin" + "components/youtubeplayer/plugin", + "components/backdropscreensaver/plugin", + "components/logoscreensaver/plugin" ]; if (appHost.supports("remotecontrol")) { @@ -594,6 +596,8 @@ var AppInfo = {}; require(["playerSelectionMenu", "components/playback/remotecontrolautoplay"]); } + require(["components/screensavermanager"]); + if (!appHost.supports("physicalvolumecontrol") || browser.touch) { require(["components/playback/volumeosd"]); } @@ -671,7 +675,8 @@ var AppInfo = {}; autoPlayDetect: componentsPath + "/playback/autoplaydetect", nowPlayingHelper: componentsPath + "/playback/nowplayinghelper", pluginManager: componentsPath + "/pluginManager", - packageManager: componentsPath + "/packagemanager" + packageManager: componentsPath + "/packagemanager", + screensaverManager: componentsPath + "/screensavermanager" }; requirejs.onError = onRequireJsError;