From 7978037c18f8fd330196158351d79a4f923fb147 Mon Sep 17 00:00:00 2001 From: Daniel Habenicht Date: Thu, 31 Dec 2020 12:34:23 +0100 Subject: [PATCH 1/2] cache images for an hour for performance * otherwiese three seperate requests would be made which each load the whole picture, because max-age=0 on the MagicMirror server instance * add prettier file --- .prettierrc.json | 4 ++ node_helper.js | 98 +++++++++++++++++++++++++++++------------------- 2 files changed, 63 insertions(+), 39 deletions(-) create mode 100644 .prettierrc.json diff --git a/.prettierrc.json b/.prettierrc.json new file mode 100644 index 0000000..e9c0f50 --- /dev/null +++ b/.prettierrc.json @@ -0,0 +1,4 @@ +{ + "trailingComma": "none", + "singleQuote": true +} diff --git a/node_helper.js b/node_helper.js index 303966f..1f7f39f 100755 --- a/node_helper.js +++ b/node_helper.js @@ -13,21 +13,26 @@ */ // call in the required classes -const Log = require("../../js/logger.js"); +const Log = require('../../js/logger.js'); var NodeHelper = require('node_helper'); var FileSystemImageSlideshow = require('fs'); + const { exec } = require('child_process'); +var express = require('express'); +const basePath = '/images/'; // the main module helper create module.exports = NodeHelper.create({ + expressInstance: undefined, // subclass start method, clears the initial config array - start: function() { + start: function () { this.excludePaths = new Set(); this.validImageFileExtensions = new Set(); + this.expressInstance = this.expressApp; }, - + // shuffles an array at random and returns it - shuffleArray: function(array) { + shuffleArray: function (array) { for (let i = array.length - 1; i > 0; i--) { // j is a random index in [0, i]. const j = Math.floor(Math.random() * (i + 1)); @@ -35,9 +40,9 @@ module.exports = NodeHelper.create({ } return array; }, - + // sort by filename attribute - sortByFilename: function(a, b) { + sortByFilename: function (a, b) { aL = a.path.toLowerCase(); bL = b.path.toLowerCase(); if (aL > bL) return 1; @@ -45,7 +50,7 @@ module.exports = NodeHelper.create({ }, // sort by created attribute - sortByCreated: function(a, b) { + sortByCreated: function (a, b) { aL = a.created; bL = b.created; if (aL > bL) return 1; @@ -53,7 +58,7 @@ module.exports = NodeHelper.create({ }, // sort by created attribute - sortByModified: function(a, b) { + sortByModified: function (a, b) { aL = a.modified; bL = b.modified; if (aL > bL) return 1; @@ -65,13 +70,14 @@ module.exports = NodeHelper.create({ switch (sortBy) { case 'created': // Log.log('Sorting by created date...'); - sortedList = imageList.sort(this.sortByCreated);; + sortedList = imageList.sort(this.sortByCreated); break; case 'modified': // Log.log('Sorting by modified date...'); - sortedList = imageList.sort(this.sortByModified);; + sortedList = imageList.sort(this.sortByModified); break; - default: // sort by name + default: + // sort by name // Log.log('Sorting by name...'); sortedList = imageList.sort(this.sortByFilename); } @@ -86,7 +92,7 @@ module.exports = NodeHelper.create({ }, // checks there's a valid image file extension - checkValidImageFileExtension: function(filename) { + checkValidImageFileExtension: function (filename) { if (!filename.includes('.')) { // No file extension. return false; @@ -96,7 +102,7 @@ module.exports = NodeHelper.create({ }, // gathers the image list - gatherImageList: function(config) { + gatherImageList: function (config) { // create an empty main image list let imageList = []; for (let i = 0; i < config.imagePaths.length; i++) { @@ -105,18 +111,19 @@ module.exports = NodeHelper.create({ imageList = config.randomizeImageOrder ? this.shuffleArray(imageList) - : this.sortImageList(imageList, config.sortImagesBy, config.sortImagesDescending); + : this.sortImageList( + imageList, + config.sortImagesBy, + config.sortImagesDescending + ); // build the return payload const returnPayload = { identifier: config.identifier, - imageList: imageList.map( item => item.path) // map the array to only extract the paths + imageList: imageList.map((item) => basePath + item.path) // map the array to only extract the paths }; // send the image list back - this.sendSocketNotification( - 'BACKGROUNDSLIDESHOW_FILELIST', - returnPayload - ); + this.sendSocketNotification('BACKGROUNDSLIDESHOW_FILELIST', returnPayload); }, getFiles(path, imageList, config) { @@ -130,42 +137,55 @@ module.exports = NodeHelper.create({ if (stats.isDirectory() && config.recursiveSubDirectories) { this.getFiles(currentItem, imageList, config); } else if (stats.isFile()) { - const isValidImageFileExtension = - this.checkValidImageFileExtension(currentItem); - if (isValidImageFileExtension) { - imageList.push({ - "path": currentItem, - "created": stats.ctimeMs, - "modified": stats.mtimeMs - }); - } + const isValidImageFileExtension = this.checkValidImageFileExtension( + currentItem + ); + if (isValidImageFileExtension) { + imageList.push({ + path: currentItem, + created: stats.ctimeMs, + modified: stats.mtimeMs + }); + } } } }, // subclass socketNotificationReceived, received notification from module - socketNotificationReceived: function(notification, payload) { + socketNotificationReceived: function (notification, payload) { if (notification === 'BACKGROUNDSLIDESHOW_REGISTER_CONFIG') { const config = payload; + this.expressInstance.use( + basePath + config.imagePaths[0], + express.static(config.imagePaths[0], { maxAge: 3600000 }) + ); // Create set of excluded subdirectories. this.excludePaths = new Set(config.excludePaths); // Create set of valid image extensions. - const validExtensionsList = config.validImageFileExtensions.toLowerCase().split(','); + const validExtensionsList = config.validImageFileExtensions + .toLowerCase() + .split(','); this.validImageFileExtensions = new Set(validExtensionsList); - // Get the image list in a non-blocking way since large # of images would cause + // Get the image list in a non-blocking way since large # of images would cause // the MagicMirror startup banner to get stuck sometimes. - setTimeout(() => {this.gatherImageList(config)}, 200); - } - else if (notification === 'BACKGROUNDSLIDESHOW_PLAY_VIDEO') { // XXXXX + setTimeout(() => { + this.gatherImageList(config); + }, 200); + } else if (notification === 'BACKGROUNDSLIDESHOW_PLAY_VIDEO') { Log.info('mw got BACKGROUNDSLIDESHOW_PLAY_VIDEO'); - Log.info('cmd line:' + 'omxplayer --win 0,0,1920,1080 --alpha 180 ' + payload[0]); - exec('omxplayer --win 0,0,1920,1080 --alpha 180 ' + payload[0], (e, stdout, stderr) => { - this.sendSocketNotification('BACKGROUNDSLIDESHOW_PLAY', null); - Log.info('mw video done'); - }); + Log.info( + 'cmd line:' + 'omxplayer --win 0,0,1920,1080 --alpha 180 ' + payload[0] + ); + exec( + 'omxplayer --win 0,0,1920,1080 --alpha 180 ' + payload[0], + (e, stdout, stderr) => { + this.sendSocketNotification('BACKGROUNDSLIDESHOW_PLAY', null); + Log.info('mw video done'); + } + ); } } }); From 7e2b3087ecfd347aa86d1279c3061304fbe74fd2 Mon Sep 17 00:00:00 2001 From: Daniel Habenicht Date: Thu, 31 Dec 2020 12:44:18 +0100 Subject: [PATCH 2/2] add inverted panorma scrolling --- BackgroundSlideshow.css | 23 ++++++ MMM-BackgroundSlideshow.js | 142 +++++++++++++++++++++++++++---------- 2 files changed, 129 insertions(+), 36 deletions(-) diff --git a/BackgroundSlideshow.css b/BackgroundSlideshow.css index bf83360..7402b82 100755 --- a/BackgroundSlideshow.css +++ b/BackgroundSlideshow.css @@ -208,6 +208,17 @@ animation-name: slideV; } +.MMM-BackgroundSlideshow .slideHInv { + background-size: cover; + background-position: right; + animation-name: slideHInv; +} + +.MMM-BackgroundSlideshow .slideVInv { + background-size: cover; + background-position: bottom; + animation-name: slideVInv; +} .MMM-BackgroundSlideshow .zoomIn { animation-name: zoomIn; transform: scale(1); @@ -230,6 +241,18 @@ } } +@keyframes slideHInv { + to { + background-position: left; + } +} + +@keyframes slideVInv { + to { + background-position: top; + } +} + @keyframes zoomIn { to { transform: scale(1.1); diff --git a/MMM-BackgroundSlideshow.js b/MMM-BackgroundSlideshow.js index 2061c38..a41d433 100755 --- a/MMM-BackgroundSlideshow.js +++ b/MMM-BackgroundSlideshow.js @@ -50,8 +50,18 @@ Module.register('MMM-BackgroundSlideshow', { // transition from one image to the other (may be a bit choppy on slower devices, or if the images are too big) transitionImages: false, // the gradient to make the text more visible - gradient: ['rgba(0, 0, 0, 0.75) 0%', 'rgba(0, 0, 0, 0) 40%', 'rgba(0, 0, 0, 0) 80%', 'rgba(0, 0, 0, 0.75) 100%'], - horizontalGradient: ['rgba(0, 0, 0, 0.75) 0%', 'rgba(0, 0, 0, 0) 40%', 'rgba(0, 0, 0, 0) 80%', 'rgba(0, 0, 0, 0.75) 100%'], + gradient: [ + 'rgba(0, 0, 0, 0.75) 0%', + 'rgba(0, 0, 0, 0) 40%', + 'rgba(0, 0, 0, 0) 80%', + 'rgba(0, 0, 0, 0.75) 100%' + ], + horizontalGradient: [ + 'rgba(0, 0, 0, 0.75) 0%', + 'rgba(0, 0, 0, 0) 40%', + 'rgba(0, 0, 0, 0) 80%', + 'rgba(0, 0, 0, 0.75) 100%' + ], // the direction the gradient goes, vertical or horizontal gradientDirection: 'vertical', // Whether to scroll larger pictures rather than cut them off @@ -76,11 +86,11 @@ Module.register('MMM-BackgroundSlideshow', { 'slideFromBottomLeft', 'slideFromBottomRight', 'flipX', - 'flipY', + 'flipY' ], transitionTimingFunction: 'cubic-bezier(.17,.67,.35,.96)', animations: ['slide', 'zoomOut', 'zoomIn'], - changeImageOnResume: false, + changeImageOnResume: false }, // load function @@ -97,14 +107,22 @@ Module.register('MMM-BackgroundSlideshow', { //validate imageinfo property. This will make sure we have at least 1 valid value const imageInfoRegex = /\bname\b|\bdate\b/gi; - if (this.config.showImageInfo && !imageInfoRegex.test(this.config.imageInfo)) { - Log.warn('MMM-BackgroundSlideshow: showImageInfo is set, but imageInfo does not have a valid value.'); + if ( + this.config.showImageInfo && + !imageInfoRegex.test(this.config.imageInfo) + ) { + Log.warn( + 'MMM-BackgroundSlideshow: showImageInfo is set, but imageInfo does not have a valid value.' + ); // Use name as the default this.config.imageInfo = ['name']; } else { // convert to lower case and replace any spaces with , to make sure we get an array back // even if the user provided space separated values - this.config.imageInfo = this.config.imageInfo.toLowerCase().replace(/\s/g, ',').split(','); + this.config.imageInfo = this.config.imageInfo + .toLowerCase() + .replace(/\s/g, ',') + .split(','); // now filter the array to only those that have values this.config.imageInfo = this.config.imageInfo.filter((n) => n); } @@ -116,18 +134,25 @@ Module.register('MMM-BackgroundSlideshow', { // Lets make sure the backgroundAnimation duration matches the slideShowSpeed unless it has been // overriden if (this.config.backgroundAnimationDuration === '1s') { - this.config.backgroundAnimationDuration = this.config.slideshowSpeed / 1000 + 's'; + this.config.backgroundAnimationDuration = + this.config.slideshowSpeed / 1000 + 's'; } // Chrome versions < 81 do not support EXIF orientation natively. A CSS transformation // needs to be applied for the image to display correctly - see http://crbug.com/158753 . - this.browserSupportsExifOrientationNatively = CSS.supports('image-orientation: from-image'); + this.browserSupportsExifOrientationNatively = CSS.supports( + 'image-orientation: from-image' + ); this.playingVideo = false; }, getScripts: function () { - return ['modules/' + this.name + '/node_modules/exif-js/exif.js', 'modules/' + this.name + '/node_modules/lodash/lodash.js', 'moment.js']; + return [ + 'modules/' + this.name + '/node_modules/exif-js/exif.js', + 'modules/' + this.name + '/node_modules/lodash/lodash.js', + 'moment.js' + ]; }, getStyles: function () { @@ -183,7 +208,11 @@ Module.register('MMM-BackgroundSlideshow', { this.updateImage(false, payload.url); } } else if (notification === 'BACKGROUNDSLIDESHOW_URLS') { - console.log(`Notification Received: BACKGROUNDSLIDESHOW_URLS. Payload: ${JSON.stringify(payload)}`); + console.log( + `Notification Received: BACKGROUNDSLIDESHOW_URLS. Payload: ${JSON.stringify( + payload + )}` + ); if (payload && payload.urls && payload.urls.length) { // check if image list has been saved. If not, this is the first time the notification is received // save the image list and index. @@ -221,7 +250,10 @@ Module.register('MMM-BackgroundSlideshow', { this.imageList = urls; this.imageIndex = 0; this.updateImage(); - if (!this.playingVideo && (this.timer || (this.savedImages && this.savedImages.length == 0))) { + if ( + !this.playingVideo && + (this.timer || (this.savedImages && this.savedImages.length == 0)) + ) { // Restart timer only if timer was already running this.resume(); } @@ -250,8 +282,7 @@ Module.register('MMM-BackgroundSlideshow', { } } } - } - else if (notification === 'BACKGROUNDSLIDESHOW_PLAY') { + } else if (notification === 'BACKGROUNDSLIDESHOW_PLAY') { // Change to next image and start timer. this.updateImage(); if (!this.playingVideo) { @@ -267,11 +298,17 @@ Module.register('MMM-BackgroundSlideshow', { this.imagesDiv.className = 'images'; wrapper.appendChild(this.imagesDiv); - if (this.config.gradientDirection === 'vertical' || this.config.gradientDirection === 'both') { + if ( + this.config.gradientDirection === 'vertical' || + this.config.gradientDirection === 'both' + ) { this.createGradientDiv('bottom', this.config.gradient, wrapper); } - if (this.config.gradientDirection === 'horizontal' || this.config.gradientDirection === 'both') { + if ( + this.config.gradientDirection === 'horizontal' || + this.config.gradientDirection === 'both' + ) { this.createGradientDiv('right', this.config.gradient, wrapper); } @@ -284,7 +321,9 @@ Module.register('MMM-BackgroundSlideshow', { } if (this.config.imagePaths.length == 0) { - Log.error('MMM-BackgroundSlideshow: Missing required parameter imagePaths.'); + Log.error( + 'MMM-BackgroundSlideshow: Missing required parameter imagePaths.' + ); } else { // create an empty image list this.imageList = []; @@ -298,7 +337,8 @@ Module.register('MMM-BackgroundSlideshow', { createGradientDiv: function (direction, gradient, wrapper) { var div = document.createElement('div'); - div.style.backgroundImage = 'linear-gradient( to ' + direction + ', ' + gradient.join() + ')'; + div.style.backgroundImage = + 'linear-gradient( to ' + direction + ', ' + gradient.join() + ')'; div.className = 'gradient'; wrapper.appendChild(div); }, @@ -358,8 +398,7 @@ Module.register('MMM-BackgroundSlideshow', { let mw_image_src = null; if (imageToDisplay) { mw_image_src = imageToDisplay; - } - else { + } else { // mw_image_src = encodeURI(this.imageList[this.imageIndex]); mw_image_src = this.imageList[this.imageIndex]; this.imageIndex += 1; @@ -367,13 +406,12 @@ Module.register('MMM-BackgroundSlideshow', { const mw_lc = mw_image_src.toLowerCase(); if (mw_lc.endsWith('.mp4') || mw_lc.endsWith('.m4v')) { - payload = [ mw_image_src, 'PLAY' ]; - mw_image_src = "modules/MMM-BackgroundSlideshow/transparent1080p.png"; + payload = [mw_image_src, 'PLAY']; + mw_image_src = 'modules/MMM-BackgroundSlideshow/transparent1080p.png'; this.sendSocketNotification('BACKGROUNDSLIDESHOW_PLAY_VIDEO', payload); this.playingVideo = true; this.suspend(); - } - else { + } else { this.playingVideo = false; } @@ -390,10 +428,14 @@ Module.register('MMM-BackgroundSlideshow', { const transitionDiv = document.createElement('div'); transitionDiv.className = 'transition'; if (this.config.transitionImages && this.config.transitions.length > 0) { - let randomNumber = Math.floor(Math.random() * this.config.transitions.length); + let randomNumber = Math.floor( + Math.random() * this.config.transitions.length + ); transitionDiv.style.animationDuration = this.config.transitionSpeed; transitionDiv.style.transition = `opacity ${this.config.transitionSpeed} ease-in-out`; - transitionDiv.style.animationName = this.config.transitions[randomNumber]; + transitionDiv.style.animationName = this.config.transitions[ + randomNumber + ]; transitionDiv.style.animationTimingFunction = this.config.transitionTimingFunction; } @@ -413,8 +455,13 @@ Module.register('MMM-BackgroundSlideshow', { } // Check to see if we need to animate the background - if (this.config.backgroundAnimationEnabled && this.config.animations.length) { - randomNumber = Math.floor(Math.random() * this.config.animations.length); + if ( + this.config.backgroundAnimationEnabled && + this.config.animations.length + ) { + randomNumber = Math.floor( + Math.random() * this.config.animations.length + ); const animation = this.config.animations[randomNumber]; imageDiv.style.animationDuration = this.config.backgroundAnimationDuration; imageDiv.style.animationDelay = this.config.transitionSpeed; @@ -430,12 +477,23 @@ Module.register('MMM-BackgroundSlideshow', { imageDiv.style.animationIterationCount = this.config.backgroundAnimationLoopCount; imageDiv.style.backgroundSize = 'cover'; - if (adjustedWidth / window.innerWidth > adjustedHeight / window.innerHeight) { + if ( + adjustedWidth / window.innerWidth > + adjustedHeight / window.innerHeight + ) { // Scrolling horizontally... - imageDiv.className += ' slideH'; + if (Math.floor(Math.random() * 2)) { + imageDiv.className += ' slideH'; + } else { + imageDiv.className += ' slideHInv'; + } } else { // Scrolling vertically... - imageDiv.className += ' slideV'; + if (Math.floor(Math.random() * 2)) { + imageDiv.className += ' slideV'; + } else { + imageDiv.className += ' slideVInv'; + } } } else { imageDiv.className += ` ${animation}`; @@ -451,7 +509,11 @@ Module.register('MMM-BackgroundSlideshow', { dateTime = moment(dateTime, 'YYYY:MM:DD HH:mm:ss'); dateTime = dateTime.format('dddd MMMM D, YYYY HH:mm'); } catch (e) { - console.log('Failed to parse dateTime: ' + dateTime + ' to format YYYY:MM:DD HH:mm:ss'); + console.log( + 'Failed to parse dateTime: ' + + dateTime + + ' to format YYYY:MM:DD HH:mm:ss' + ); dateTime = ''; } } @@ -476,7 +538,9 @@ Module.register('MMM-BackgroundSlideshow', { image.src = encodeURI(mw_image_src); - this.sendNotification('BACKGROUNDSLIDESHOW_IMAGE_UPDATED', { url: image.src }); + this.sendNotification('BACKGROUNDSLIDESHOW_IMAGE_UPDATED', { + url: image.src + }); // console.info('Updating image, source:' + image.src); }, @@ -536,7 +600,10 @@ Module.register('MMM-BackgroundSlideshow', { imageProps.push(`${this.imageIndex} of ${this.imageList.length}`); break; default: - Log.warn(prop + ' is not a valid value for imageInfo. Please check your configuration'); + Log.warn( + prop + + ' is not a valid value for imageInfo. Please check your configuration' + ); } }); @@ -574,6 +641,9 @@ Module.register('MMM-BackgroundSlideshow', { this.suspend(); // console.info('Getting Images'); // ask helper function to get the image list - this.sendSocketNotification('BACKGROUNDSLIDESHOW_REGISTER_CONFIG', this.config); - }, + this.sendSocketNotification( + 'BACKGROUNDSLIDESHOW_REGISTER_CONFIG', + this.config + ); + } });