Initial commit

This commit is contained in:
nsieg 2022-11-15 21:16:35 +01:00
commit 3994a1ca63
9 changed files with 2387 additions and 0 deletions

61
MMM-piwigo-photo-wheel.js Normal file
View file

@ -0,0 +1,61 @@
Module.register("MMM-piwigo-photo-wheel", {
defaults: {
updateInterval: 60 * 60 * 1000,
switchInterval: 10 * 1000,
maxWidth: "250px",
maxHeight: "333px",
piwigoBaseUrl: "",
piwigoUsername: "",
piwigoPassword: "",
piwigoSize: "large",
loadingText: "Loading...",
},
start: function () {
this.imageFileNames = [];
this.imageIndex = 0;
setInterval(this.updateImages, this.config.updateInterval);
setInterval(() => {
this.imageIndex = (this.imageIndex + 1) % this.imageFileNames.length;
this.updateDom();
}, this.config.switchInterval);
this.updateImages();
},
socketNotificationReceived: function (notification, payload) {
this.imageFileNames = payload;
this.imageIndex = 0;
this.updateDom();
},
getDom: function () {
const photoDiv = document.createElement("div");
photoDiv.className = "piwigo-photo";
photoDiv.style.width = this.config.maxWidth;
photoDiv.style.height = this.config.maxHeight;
if (this.imageFileNames && this.imageFileNames.length) {
const curImage = this.imageFileNames[this.imageIndex];
photoDiv.textContent = "";
photoDiv.style.backgroundImage =
"url('" + this.file("images/" + curImage) + "')";
} else {
photoDiv.textContent = this.config.loadingText;
}
return photoDiv;
},
getStyles: function () {
return [this.file("css/style.css")];
},
updateImages: function () {
this.sendSocketNotification("UPDATE", {
username: this.config.piwigoUsername,
password: this.config.piwigoPassword,
url: this.config.piwigoBaseUrl,
size: this.config.piwigoSize,
});
},
});

6
css/style.css Normal file
View file

@ -0,0 +1,6 @@
div.piwigo-photo {
background-repeat: no-repeat;
background-size: contain;
background-position: center;
box-shadow: 0px 0px 10px 10px black inset;
}

0
images/.gitkeep Normal file
View file

58
loader.js Normal file
View file

@ -0,0 +1,58 @@
const fsprom = require("fs").promises;
const path = require("path");
const fs = require("fs");
const superagent = require("superagent");
async function getImages(rootPath) {
const content = await fsprom.readdir(rootPath, { withFileTypes: true });
const images = content
.filter((x) => !x.isDirectory())
.map((x) => x.name)
.filter((x) => x !== ".gitkeep");
return images;
}
async function deleteImages(rootPath, fileNamesLocal, fileNamesServer) {
const diff = fileNamesLocal.filter((x) => !fileNamesServer.includes(x));
try {
for (fileName of diff) {
//console.log("delete " + path.join("/", rootPath, fileName));
await fsprom.unlink(path.join("/", rootPath, fileName));
}
} catch (err) {
// do not propagate errors so that failed delete will not lead to failed download
console.log(err);
}
}
async function downloadImages(rootPath, config) {
const localFileNames = await getImages(rootPath);
const agent = superagent.agent();
await agent
.post(config.url + "?format=json&method=pwg.session.login")
.field("username", config.username)
.field("password", config.password);
const data = await agent.get(
config.url + "?format=json&method=pwg.categories.getImages"
);
respText = JSON.parse(data.res.text);
images = respText.result.images;
serverFileNames = [];
for (img of images) {
const fileName = img.file;
serverFileNames.push(fileName);
// Only download if file does not yet exist
if (!localFileNames.some((x) => x === fileName)) {
await new Promise((resolve) =>
agent
.get(img.derivatives[config.size].url)
.pipe(fs.createWriteStream(path.join("/", rootPath, fileName)))
.on("finish", resolve)
);
}
}
await deleteImages(rootPath, localFileNames, serverFileNames);
}
module.exports = { getImages, downloadImages };

26
node_helper.js Normal file
View file

@ -0,0 +1,26 @@
const NodeHelper = require("node_helper");
const path = require("path");
const _ = require("lodash");
const { getImages, downloadImages } = require("./loader");
const Log = require("logger");
module.exports = NodeHelper.create({
socketNotificationReceived: function (notification, payload) {
this.syncImages(payload);
},
syncImages: function (config) {
const imagePath = path.join("/", __dirname, "images");
downloadImages(imagePath, config)
.then(() => {
getImages(imagePath).then((images) => {
shuffledImages = _.shuffle(images);
this.sendSocketNotification("IMAGES_UPDATED", shuffledImages);
});
})
.catch((err) => {
Log.error("Could not download images from piwigo! Error was: ");
Log.error(err);
});
},
});

2050
package-lock.json generated Normal file

File diff suppressed because it is too large Load diff

18
package.json Normal file
View file

@ -0,0 +1,18 @@
{
"name": "mmm-piwigo-photo-wheel",
"version": "1.0.0",
"main": "MMM-piwigo-photo-wheel.js",
"scripts": {
"test": "mocha",
"start": "node test_manual/serve.js"
},
"dependencies": {
"chai": "^4.3.6",
"mocha": "^10.1.0",
"superagent": "^8.0.3",
"superagent-mock": "^4.0.0"
},
"devDependencies": {
"prettier": "2.7.1"
}
}

View file

@ -0,0 +1,118 @@
module.exports = [
{
pattern: "https://piwigo.example(.*)",
fixtures: function (match, params, headers, context) {
if (
match[1] === "/i.php?/upload/2022/11/01/20221101211408-9a0c06b1-la.jpg"
) {
return {
image: "test",
};
}
if (match[1] === "?format=json&method=pwg.session.login") {
return {
stat: "ok",
result: true,
};
}
if (match[1] === "?format=json&method=pwg.categories.getImages") {
return {
stat: "ok",
result: {
paging: {
page: 0,
per_page: 100,
count: 1,
total_count: "1",
},
images: [
{
is_favorite: false,
id: 1,
width: 4016,
height: 5197,
hit: 0,
file: "matthew-brodeur-DH_u2aV3nGM-unsplash.jpg",
name: "matthew-brodeur-DH u2aV3nGM-unsplash",
comment: null,
date_creation: null,
date_available: "2022-11-01 21:14:08",
page_url: "https://piwigo.example/picture.php?/1",
element_url:
"https://piwigo.example/upload/2022/11/01/20221101211408-9a0c06b1.jpg",
derivatives: {
square: {
url: "https://piwigo.example/_data/i/upload/2022/11/01/20221101211408-9a0c06b1-sq.jpg",
width: 120,
height: 120,
},
thumb: {
url: "https://piwigo.example/i.php?/upload/2022/11/01/20221101211408-9a0c06b1-th.jpg",
width: 111,
height: 144,
},
"2small": {
url: "https://piwigo.example/_data/i/upload/2022/11/01/20221101211408-9a0c06b1-2s.jpg",
width: 185,
height: 240,
},
xsmall: {
url: "https://piwigo.example/i.php?/upload/2022/11/01/20221101211408-9a0c06b1-xs.jpg",
width: 250,
height: 324,
},
small: {
url: "https://piwigo.example/_data/i/upload/2022/11/01/20221101211408-9a0c06b1-sm.jpg",
width: 333,
height: 432,
},
medium: {
url: "https://piwigo.example/_data/i/upload/2022/11/01/20221101211408-9a0c06b1-me.jpg",
width: 459,
height: 594,
},
large: {
url: "https://piwigo.example/i.php?/upload/2022/11/01/20221101211408-9a0c06b1-la.jpg",
width: 584,
height: 756,
},
xlarge: {
url: "https://piwigo.example/i.php?/upload/2022/11/01/20221101211408-9a0c06b1-xl.jpg",
width: 709,
height: 918,
},
xxlarge: {
url: "https://piwigo.example/i.php?/upload/2022/11/01/20221101211408-9a0c06b1-xx.jpg",
width: 959,
height: 1242,
},
},
categories: [
{
id: 1,
url: "https://piwigo.example/index.php?/category/1",
page_url:
"https://piwigo.example/picture.php?/1/category/1",
},
],
},
],
},
};
}
},
get: function (match, data) {
return {
body: data,
};
},
post: function (match, data) {
return {
status: 201,
};
},
},
];

50
test/test.js Normal file
View file

@ -0,0 +1,50 @@
const path = require("path");
const fs = require("fs").promises;
const os = require("os");
const chai = require("chai");
const expect = chai.expect;
const request = require("superagent");
const mockConfig = require("./superagent-mock-config");
const { getImages, downloadImages } = require("./../loader");
describe("Loader", function () {
describe("getImages()", function () {
it("should return array of file names", async () => {
// Given
const dir = await fs.mkdtemp(path.join(os.tmpdir(), "foo-"));
await fs.appendFile(dir + "/mynewfile1.txt", "Hello content!");
// When
const data = await getImages(dir);
// Then
expect(data).to.eql(["mynewfile1.txt"]);
});
it("should ignore .gitkeep", async () => {
// Given
const dir = await fs.mkdtemp(path.join(os.tmpdir(), "foo-"));
await fs.appendFile(dir + "/mynewfile1.txt", "Hello content!");
await fs.appendFile(dir + "/.gitkeep", "Hello content!");
await fs.appendFile(dir + "/.gitbla", "Hello content!");
// When
const data = await getImages(dir);
// Then
expect(data).to.eql([".gitbla", "mynewfile1.txt"]);
});
});
});
describe("Loader", function () {
describe("downloadImages()", function () {
it("should connect", async () => {
const config = {
username: "tester",
password: "secret",
url: "https://piwigo.example",
size: "large",
};
var superagentMock = require("superagent-mock")(request, mockConfig);
await downloadImages(path.join("/", __dirname, "../images"), config);
superagentMock.unset();
}).timeout(30000);
});
});