Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 11 additions & 11 deletions MMM-RandomPhoto.css
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
:root {
--randomphoto-blur-value: 0px;
--_randomPhoto-blur-value: 0px;
}

#randomPhoto img {
#_randomPhoto img {
opacity: 0;
position: absolute;;
top: 0;
Expand All @@ -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;
}

Expand Down
49 changes: 28 additions & 21 deletions MMM-RandomPhoto.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/* global Module */

/* MagicMirror²
* Module: MMM-RandomPhoto
*
Expand All @@ -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"
Expand All @@ -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;
Expand All @@ -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;
Expand All @@ -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'.");
}
Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -155,16 +161,16 @@ Module.register("MMM-RandomPhoto",{
var self = this;
var img = $('<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);
});
});
},
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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) {
Expand All @@ -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");
}
}

Expand All @@ -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 = '<i id="randomPhotoStatusIcon" class="rpihidden"></i>';
statusIconObject.innerHTML = '<i id="'+this.statusIconID+'" class="rpihidden"></i>';
wrapper.appendChild(statusIconObject);
}
return wrapper;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down
19 changes: 19 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ The entry in `config.js` can include the following options:
| Option | Description
|-----------------------|------------
| `imageRepository` | *Optional* - The image source.<br><br>**Type:** `string`<br>**Allowed:** `picsum`, `nextcloud`, `localdirectory`<br>**Default:** `picsum`
| `id` | *Optional* - the unique id of this instance, defaults to '_'<br> 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.<br><br>**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.<br><br>**Type:** `boolean`<br>**Default:** `true`
| `width` | *Optional* - The width of the image in px. Only used when `imageRepository` is set to `picsum`<br><br>**Type:** `int`<br>**Default:** `1920`
Expand Down Expand Up @@ -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<br>and
--_random... to --foorandom...
## Dependencies

- [jQuery](https://www.npmjs.com/package/jquery) (installed via `npm install`)
Expand Down
61 changes: 33 additions & 28 deletions node_helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
});
},
Expand All @@ -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
Expand All @@ -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) {
Expand All @@ -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) {
Expand All @@ -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");
Expand All @@ -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;

Expand All @@ -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; });
Expand Down