diff --git a/MMM-RandomPhoto.css b/MMM-RandomPhoto.css index b53ec0e..be66c28 100644 --- a/MMM-RandomPhoto.css +++ b/MMM-RandomPhoto.css @@ -1,8 +1,8 @@ :root { - --randomphoto-blur-value: 0px; + --_randomPhoto-blur-value: 0px; } -#randomPhoto img { +#_randomPhoto img { opacity: 0; position: absolute;; top: 0; @@ -12,39 +12,39 @@ object-fit: cover; } -#randomPhoto img.grayscale { +#_randomPhoto img.grayscale { filter: grayscale(100%); } -#randomPhoto img.blur { +#_randomPhoto img.blur { filter: blur(var(--randomphoto-blur-value)); } -#randomPhotoIcon { +#_randomPhotoIcon { position: absolute; } -#randomPhotoIcon.rpitop { +#_randomPhotoIcon.rpitop { top: 5px; } -#randomPhotoIcon.rpibottom { +#_randomPhotoIcon.rpibottom { bottom: 5px; } -#randomPhotoIcon.rpiright { +#_randomPhotoIcon.rpiright { right: 10px; } -#randomPhotoIcon.rpileft { +#_randomPhotoIcon.rpileft { left: 10px; } -#randomPhotoIcon i { +#_randomPhotoIcon i { opacity: 1; } -#randomPhotoIcon i.rpihidden { +#_randomPhotoIcon i.rpihidden { opacity: 0; } diff --git a/MMM-RandomPhoto.js b/MMM-RandomPhoto.js index a403906..0e53c0b 100644 --- a/MMM-RandomPhoto.js +++ b/MMM-RandomPhoto.js @@ -1,5 +1,5 @@ /* global Module */ - + /* MagicMirror² * Module: MMM-RandomPhoto * @@ -13,7 +13,8 @@ Module.register("MMM-RandomPhoto",{ opacity: 0.3, animationSpeed: 500, updateInterval: 60, - imageRepository: "picsum", // Select the image repository source. One of "picsum" (default / fallback), "localdirectory" or "nextcloud" (currently broken because of CORS bug in nextcloud) + imageRepository: "picsum", // Select the image repository source. One of "picsum" (default / fallback), "localdirectory" or "nextcloud" (currently broken because of CORS bug in nextcloud) + config : '-', repositoryConfig: { // if imageRepository = "picsum" -> "path", "username" and "password" are ignored and can be left empty // if imageRepository = "nextcloud" @@ -39,7 +40,10 @@ Module.register("MMM-RandomPhoto",{ showStatusIcon: true, statusIconMode: "show", // one of: "show" (default / fallback) or "fade" statusIconPosition: "top_right", // one of: "top_right" (default / fallback), "top_left", "bottom_right" or "bottom_left" - }, + }, + imgID1: "", + imgID2: "", + statusIconID:"", start: function() { this.updateTimer = null; @@ -48,8 +52,10 @@ Module.register("MMM-RandomPhoto",{ this.running = false; this.nextcloud = false; - this.localdirectory = false; - + this.localdirectory = false; + this.imgID1 = this.config.id + "randomPhoto-placeholder1" + this.imgID2 = this.config.id + "randomPhoto-placeholder2" + this.statusIconID = this.config.id +"randomPhotoStatusIcon" this.config.imageRepository = this.config.imageRepository.toLowerCase(); if (this.config.imageRepository === "nextcloud") { this.nextcloud = true; @@ -73,7 +79,7 @@ Module.register("MMM-RandomPhoto",{ fetchImageList: function() { if (typeof this.config.repositoryConfig.path !== "undefined" && this.config.repositoryConfig.path !== null) { - this.sendSocketNotification('FETCH_IMAGE_LIST'); + this.sendSocketNotification('FETCH_IMAGE_LIST',{ id: this.config.id }); } else { Log.error("[" + this.name + "] Trying to use 'nextcloud' or 'localdirectory' but did not specify any 'config.repositoryConfig.path'."); } @@ -107,7 +113,7 @@ Module.register("MMM-RandomPhoto",{ if (self.localdirectory || self.nextcloud) { if (self.imageList && self.imageList.length > 0) { - url = "/" + this.name + "/images/" + this.returnImageFromList(mode); + url = "/" + this.name + "/images/"+this.config.id+'/' + this.returnImageFromList(mode); jQuery.ajax({ method: "GET", @@ -155,16 +161,16 @@ Module.register("MMM-RandomPhoto",{ var self = this; var img = $('').attr('src', url); img.on('load', function() { - $('#randomPhoto-placeholder1').attr('src', url).animate({ + $('#'+self.imgID1).attr('src', url).animate({ opacity: self.config.opacity }, self.config.animationSpeed, function() { - $(this).attr('id', 'randomPhoto-placeholder2'); + $(this).attr('id', self.imgID2); }); - $('#randomPhoto-placeholder2').animate({ + $('#'+self.imgID2).animate({ opacity: 0 }, self.config.animationSpeed, function() { - $(this).attr('id', 'randomPhoto-placeholder1'); + $(this).attr('id', self.imgID1); }); }); }, @@ -200,7 +206,7 @@ Module.register("MMM-RandomPhoto",{ loadIcon: function(navigate="none") { var self = this; - const statusIcon = document.getElementById("randomPhotoStatusIcon"); + const statusIcon = document.getElementById(self.statusIconID); let currentIndex = -1; let iconloadInProgress = false; @@ -294,12 +300,12 @@ Module.register("MMM-RandomPhoto",{ getDom: function() { var wrapper = document.createElement("div"); - wrapper.id = "randomPhoto"; + wrapper.id = this.config.id+"randomPhoto"; var img1 = document.createElement("img"); - img1.id = "randomPhoto-placeholder1"; + img1.id = this.imgID1; var img2 = document.createElement("img"); - img2.id = "randomPhoto-placeholder2"; + img2.id = this.imgID2; // Only apply grayscale / blur css classes if we are NOT using picsum, as picsum is doing it via URL parameters if (this.nextcloud || this.localdirectory) { @@ -310,8 +316,8 @@ Module.register("MMM-RandomPhoto",{ if (this.config.blur) { img1.classList.add("blur"); img2.classList.add("blur"); - img1.style.setProperty("--randomphoto-blur-value", this.config.blurAmount + "px"); - img2.style.setProperty("--randomphoto-blur-value", this.config.blurAmount + "px"); + img1.style.setProperty("--"+this.config.id+"randomphoto-blur-value", this.config.blurAmount + "px"); + img2.style.setProperty("--"+this.config.id+"randomphoto-blur-value", this.config.blurAmount + "px"); } } @@ -324,12 +330,12 @@ Module.register("MMM-RandomPhoto",{ this.config.statusIconPosition = 'top_right'; } var statusIconObject = document.createElement("span"); - statusIconObject.id = "randomPhotoIcon"; + statusIconObject.id = this.config.id+"blurandomPhotoIcon"; statusIconObject.classList.add("dimmed"); this.config.statusIconPosition.split("_").forEach(function(extractedName) { statusIconObject.classList.add("rpi" + extractedName); }); - statusIconObject.innerHTML = ''; + statusIconObject.innerHTML = ''; wrapper.appendChild(statusIconObject); } return wrapper; @@ -359,6 +365,7 @@ Module.register("MMM-RandomPhoto",{ } } } + if (notification === "RANDOMPHOTO_NEXT") { // Don't call the pause or resume functions here, so we can actually work with both states ("paused" and "active"), so independent of what "this.running" is set to clearTimeout(this.updateTimer); @@ -395,8 +402,8 @@ Module.register("MMM-RandomPhoto",{ socketNotificationReceived: function(notification, payload) { //Log.log("["+ this.name + "] received a '" + notification + "' with payload: " + payload); //console.dir(payload); - if (notification === "IMAGE_LIST") { - this.imageList = payload; + if (notification === "IMAGE_LIST" && payload.id === this.config.id) { + this.imageList = payload.data; // After we now received the image list, go ahead and display them (only when not starting as hidden) if(!this.config.startHidden) { this.resumeImageLoading(true); diff --git a/README.md b/README.md index 3a13c11..41c8096 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,7 @@ The entry in `config.js` can include the following options: | Option | Description |-----------------------|------------ | `imageRepository` | *Optional* - The image source.

**Type:** `string`
**Allowed:** `picsum`, `nextcloud`, `localdirectory`
**Default:** `picsum` +| `id` | *Optional* - the unique id of this instance, defaults to '_'
see the section on ****multiple instances*** below on how to use this property | `repositoryConfig` | *Optional* - The configuration block for the selected image repository. See below.

**Type:** `Object` | `random` | *Optional* - Should the images be shown at random? Has **NO** effect when `imageRepository` is set to `picsum`, as it is forced there.

**Type:** `boolean`
**Default:** `true` | `width` | *Optional* - The width of the image in px. Only used when `imageRepository` is set to `picsum`

**Type:** `int`
**Default:** `1920` @@ -134,7 +135,25 @@ Thinking about implementing the following things: - possibility to show the EXIF comment from each image on screen (target selectable) - ... +## Using multiple instances +This module uses content ids to do the transitions between images. These were hard coded before, preventing multiple instances from working. +now you can use an ID in the config for each instance and the content ids will include that value. +now you need to create updated css values that include the id for each instance + +to do that follow these steps +1. copy all the MMM-RandomPhoto.css lines to custom.css +2. mass change the default id value '_' to whatever value you set in the id for an instance +3. replace the one _ in the :root section at the top +``` +:root { + --_randomPhoto-blur-value: 0px; +} +``` +4. repeat steps 1 and 2 for each additional instance + +for example: if the config id value is 'foo', then the info in the css file needs to change from #_random to #foorandom
and + --_random... to --foorandom... ## Dependencies - [jQuery](https://www.npmjs.com/package/jquery) (installed via `npm install`) diff --git a/node_helper.js b/node_helper.js index bc9c32d..d5ede04 100644 --- a/node_helper.js +++ b/node_helper.js @@ -6,16 +6,16 @@ const Log = require("logger"); const NodeHelper = require("node_helper"); module.exports = NodeHelper.create({ - + config: {}, + imageList: {}, start: function() { var self = this; this.nextcloud = false; this.localdirectory = false; - this.imageList = []; - this.expressApp.get("/" + this.name + "/images/:randomImageName", async function(request, response) { - var imageBase64Encoded = await self.fetchEncodedImage(decodeURIComponent(request.params.randomImageName)); + this.expressApp.get("/" + this.name + "/images/:id/:randomImageName", async function(request, response) { + var imageBase64Encoded = await self.fetchEncodedImage(decodeURIComponent(request.params.randomImageName, request.params.id)); response.send(imageBase64Encoded); }); }, @@ -24,24 +24,28 @@ module.exports = NodeHelper.create({ socketNotificationReceived: function(notification, payload) { //console.log("["+ this.name + "] received a '" + notification + "' with payload: " + payload); if (notification === "SET_CONFIG") { - this.config = payload; - if (this.config.imageRepository === "nextcloud") { + this.imageList[payload.id]=[] + this.config[payload.id] = payload; + if (this.config[payload.id].imageRepository === "nextcloud") { + this.nextcloud = true; - } else if (this.config.imageRepository === "localdirectory") { + } else if (this.config[payload.id].imageRepository === "localdirectory") { this.localdirectory = true; } } if (notification === "FETCH_IMAGE_LIST") { - if (this.config.imageRepository === "nextcloud") { - this.fetchNextcloudImageList(); + if (this.imageList[payload.id] === undefined) + this.imageList[payload.id] + if (this.config[payload.id].imageRepository === "nextcloud") { + this.fetchNextcloudImageList(this.config[payload.id]); } - if (this.config.imageRepository === "localdirectory") { - this.fetchLocalImageList(); + if (this.config[payload.id].imageRepository === "localdirectory") { + this.fetchLocalImageList(this.config[payload.id]); } } }, - fetchLocalImageDirectory: function(path) { + fetchLocalImageDirectory: function(path,config) { var self = this; // Validate path @@ -50,7 +54,7 @@ module.exports = NodeHelper.create({ return false; } - const excludePattern = self.config.repositoryConfig.exclude?.map(pattern => new RegExp(pattern)); + const excludePattern = config.repositoryConfig.exclude?.map(pattern => new RegExp(pattern)); var fileList = fs.readdirSync(path, { withFileTypes: true }); if (fileList.length > 0) { @@ -59,38 +63,38 @@ module.exports = NodeHelper.create({ if (fileList[f].isFile()) { //TODO: add mime type check here - self.imageList.push(encodeURIComponent(path + "/" + fileList[f].name)); + self.imageList[config.id].push(encodeURIComponent(path + "/" + fileList[f].name)); } - if ((self.config.repositoryConfig.recursive === true) && fileList[f].isDirectory()) { - self.fetchLocalImageDirectory(path + "/" + fileList[f].name); + if ((config.repositoryConfig.recursive === true) && fileList[f].isDirectory()) { + self.fetchLocalImageDirectory(path + "/" + fileList[f].name,config); } } return; } }, - fetchLocalImageList: function() { + fetchLocalImageList: function(config) { var self = this; - var path = self.config.repositoryConfig.path; + var path = config.repositoryConfig.path; - self.imageList = []; - self.fetchLocalImageDirectory(path); + self.imageList[config.id] = []; + self.fetchLocalImageDirectory(path,config); - self.sendSocketNotification("IMAGE_LIST", self.imageList); + self.sendSocketNotification("IMAGE_LIST", { data: self.imageList[config.id], id: config.id }); return false; }, - fetchNextcloudImageList: function() { + fetchNextcloudImageList: function(config) { var self = this; var imageList = []; - var path = self.config.repositoryConfig.path; + var path = config.repositoryConfig.path; const urlParts = new URL(path); const requestOptions = { method: "PROPFIND", headers: { - "Authorization": "Basic " + new Buffer.from(this.config.repositoryConfig.username + ":" + this.config.repositoryConfig.password).toString("base64") + "Authorization": "Basic " + new Buffer.from(config.repositoryConfig.username + ":" + config.repositoryConfig.password).toString("base64") } }; https.get(path, requestOptions, function(response) { @@ -107,7 +111,7 @@ module.exports = NodeHelper.create({ imageList[index] = encodeURIComponent(item.replace("href>" + urlParts.pathname, "")); //console.log("[" + self.name + "] Found entry: " + imageList[index]); }); - self.sendSocketNotification("IMAGE_LIST", imageList); + self.sendSocketNotification("IMAGE_LIST", { data: imageList, id: config.id }); return; } else { console.log("[" + this.name + "] WARNING: did not get any images from nextcloud url"); @@ -122,8 +126,9 @@ module.exports = NodeHelper.create({ }, - fetchEncodedImage: async function(passedImageName) { + fetchEncodedImage: async function(passedImageName,id) { var self = this; + config=this.config[id] return new Promise(function(resolve, reject) { var fullImagePath = passedImageName; @@ -138,10 +143,10 @@ module.exports = NodeHelper.create({ const requestOptions = { method: "GET", headers: { - "Authorization": "Basic " + new Buffer.from(self.config.repositoryConfig.username + ":" + self.config.repositoryConfig.password).toString("base64") + "Authorization": "Basic " + new Buffer.from(self.config.repositoryConfig.username + ":" + config.repositoryConfig.password).toString("base64") } }; - https.get(self.config.repositoryConfig.path + fullImagePath, requestOptions, (response) => { + https.get(config.repositoryConfig.path + fullImagePath, requestOptions, (response) => { response.setEncoding('base64'); var fileEncoded = "data:" + response.headers["content-type"] + ";base64,"; response.on("data", (data) => { fileEncoded += data; });