Last active
September 3, 2021 10:45
-
-
Save jscmidt/adee935961d8581717c95fe2d904722f to your computer and use it in GitHub Desktop.
iOS-Widgets for Piwigo using the Scriptable-App.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ----- Begin of Configuration ----- | |
// URL of the Piwigo-Server | |
const piwigoBaseUrl = "https://example.com/piwigo"; | |
// Username and password for Piwigo-API login | |
const piwigoUsername = ""; | |
const piwigoPassword = ""; | |
// use the category-id to choose the ramdom photo from the specified category, or use "" if the random photo should be selected from all categories | |
const imageCategory = ""; | |
// the size in which the images are downloded from Piwigo. Allowed sizes: 'square', 'thumb', '2small', 'xsmall', 'small', 'medium', 'large', 'xlarge', 'xxlarge' | |
const imageSize = "small"; | |
// if 0: ramdom image is selected from all images. if > 0: random image is selected from the n newest images, where n is defined by useRecent | |
const useRecent = 50; | |
// size of the widget preview, allowed sizes: 0, 1, 2 | |
const widgetSize = 0; | |
// ----- End of Configuration ----- | |
checkParams(imageCategory, imageSize, useRecent); | |
await piwigoLogin(piwigoUsername, piwigoPassword, piwigoBaseUrl); | |
const data = await piwigoGetUrls(piwigoBaseUrl, imageCategory, imageSize, useRecent); | |
await piwigoLogout(piwigoBaseUrl); | |
const img = await imageRequest(data.imageUrl); | |
let widget = createWidget(img, data.pageUrl); | |
Script.setWidget(widget); | |
Script.complete(); | |
switch(widgetSize){ | |
case 0: widget.presentSmall(); break; | |
case 1: widget.presentMedium(); break; | |
case 2: widget.presentLarge(); break; | |
} | |
function createWidget(img, url) { | |
let widget = new ListWidget(); | |
widget.backgroundColor = new Color("#1A1A1A"); | |
widget.url = url; | |
widget.centerAlignContent; | |
widget.backgroundImage = img; | |
return widget; | |
} | |
async function imageRequest(url){ | |
const imgRequest = await new Request(url); | |
const img = await imgRequest.loadImage(); | |
console.log("Successfully got image from Piwigo"); | |
return img; | |
} | |
async function piwigoLogin(username, password, url){ | |
url = url + "/ws.php?format=json&method=pwg.session.login"; | |
let req = new Request(url); | |
req.method = 'post'; | |
let body = "username=" + username + "&password=" + password; | |
req.body = body; | |
let result = await req.loadJSON(); | |
if(result.stat != "ok") throw "Error logging in to Piwigo"; | |
console.log("Successfully logged in to Piwigo"); | |
} | |
async function piwigoLogout(url){ | |
url = url + "/ws.php?format=json&method=pwg.session.logout"; | |
let req = new Request(url); | |
let result = await req.loadJSON(); | |
if(result.stat != "ok") throw "Error logging out to Piwigo"; | |
console.log("Successfully logged out to Piwigo"); | |
} | |
async function piwigoGetUrls(url, category, size, recent){ | |
if(Number(recent) == 0){ | |
url = url + "/ws.php?format=json&method=pwg.categories.getImages&cat_id="+ category.toString() + "&recursive=true&per_page=1&page=1&order=random"; | |
} | |
else{ | |
url = url + "/ws.php?format=json&method=pwg.categories.getImages&cat_id="+ category.toString() + "&recursive=true&per_page=" + recent.toString() + "&page=0&order=date_available%20desc"; | |
} | |
let req = new Request(url); | |
let result = await req.loadJSON(); | |
if(result.stat != "ok") throw "Error getting image URl"; | |
let imagesCount = result.result.images.length; | |
let image = result.result.images[getRandomInt(0, imagesCount - 1)]; | |
let data = new Object(); | |
data.imageUrl = image['derivatives'][size]['url']; | |
if(image.categories != undefined){ | |
let categories = image.categories; | |
let categoriesCount = categories.length; | |
data.pageUrl = categories[getRandomInt(0, categoriesCount - 1)]['page_url']; | |
} | |
else{ | |
data.pageUrl = image.page_url; | |
} | |
console.log("Successfully got urls from piwigo"); | |
return(data); | |
} | |
function getRandomInt(min, max) { | |
min = Math.ceil(min); | |
max = Math.floor(max); | |
return Math.floor(Math.random() * (max - min + 1)) + min; | |
} | |
function checkParams(category, size, recent){ | |
const allowedSizes = ['square', 'thumb', '2small', 'xsmall', 'small', 'medium', 'large', 'xlarge', 'xxlarge']; | |
if(!allowedSizes.includes(size)) throw "Image Size not allowed"; | |
if(isNaN(Number(category))) throw "Category ID has to be a number"; | |
if(isNaN(Number(recent))) throw "useRecent has to be a number"; | |
} | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ----- Begin of Piwigo Configuration ----- | |
// URL of the Piwigo-Server | |
const piwigoBaseUrl = "https://example.com/piwigo"; | |
// Username and password to access the Piwigo-API | |
const piwigoUsername = ""; | |
const piwigoPassword = ""; | |
// ----- End of Piwigo Configuration ----- | |
//-------------------------------------------------------------------------------------- | |
// URL that is opened in Safari when tipping the widget: | |
let widgetURL = "https://example.com/piwigo/"; | |
//-------------------------------------------------------------------------------------- | |
//-------------------------------------------------------------------------------------- | |
// Should the values have borders? | |
// 0: No, 1-4: width of the borders | |
let Border = 3; | |
// How strong should the opacity of the borders be? (Value between 0.1 and 1.0) | |
let BorderStaerke = 0.6; | |
//-------------------------------------------------------------------------------------- | |
// How should Namen, Footnotes and the values be arranged in their areas? | |
// 0: left-justified, 1: centered | |
let Alignment = 1; | |
//-------------------------------------------------------------------------------------- | |
// Title of the Widget | |
let Titel = "Piwigo"; | |
//-------------------------------------------------------------------------------------- | |
//-------------------------------------------------------------------------------------- | |
// in the following the colors of the widget can be customized | |
// e.g. white: "FFFFFF", black: "000000"; A good website for this: https://htmlcolorcodes.com/ | |
//-------------------------------------------------------------------------------------- | |
// default text color, for title, footnotes etc.: | |
let TextColor = "FFFFFF"; | |
//-------------------------------------------------------------------------------------- | |
// type of the widget background, 0: single-color, 1: color-gradient | |
let BackgroundType = 1; | |
//-------------------------------------------------------------------------------------- | |
// color with single-color background: | |
let BackgroundColor = "000000"; | |
//-------------------------------------------------------------------------------------- | |
// colors with color-gradient background: | |
let BackgroundGradient1 = "000000"; | |
let BackgroundGradient2 = "202020"; | |
//-------------------------------------------------------------------------------------- | |
//-------------------------------------------------------------------------------------- | |
// Allgemeine Widget-Einstellungen | |
let widget = new ListWidget(); | |
widget.url = widgetURL; | |
widget.setPadding(20, 20, 20, 20); | |
const startTime = new Date(); | |
await piwigoLogin(piwigoUsername, piwigoPassword, piwigoBaseUrl); | |
data = await piwigoGetData(piwigoBaseUrl); | |
await piwigoLogout(piwigoBaseUrl); | |
const endTime = new Date(); | |
const timeDiff = Math.round((endTime - startTime)/3); | |
// aktuellen Timestamp erstellen und formatieren | |
let date = new Date(); | |
let df = new DateFormatter(); | |
df.dateFormat = "HH:mm"; | |
let timestamp = (df.string(date)); | |
// vStackV ist vertikal, vStack0 ist ist Überschrift, vStack1 ist Zeile 1, vStack2 ist Zeile 2 | |
let vStackV = widget.addStack(); | |
vStackV.layoutVertically(); | |
let vStack0 = vStackV.addStack(); | |
vStack0.layoutHorizontally(); | |
vStack0.centerAlignContent(); | |
vStackV.addSpacer(); | |
let vStack1 = vStackV.addStack(); | |
vStack1.layoutHorizontally(); | |
vStackV.addSpacer(); | |
let vStack2 = vStackV.addStack(); | |
vStack2.layoutHorizontally(); | |
// Überschrift des Widgets | |
AlignStack(vStack0, 0); | |
if(Alignment == 1){ | |
vStack0.addText(""); | |
} | |
let header = vStack0.addText(Titel); | |
header.font = Font.mediumSystemFont(12); | |
header.textColor = new Color(TextColor); | |
AlignStack(vStack0, 1); | |
// Inhalt des Widgets | |
// Hintergrund oder Verlauf festlegen | |
if(BackgroundType == 0){ | |
widget.backgroundColor = new Color(BackgroundColor); | |
} | |
else{ | |
const gradient = new LinearGradient() | |
gradient.locations = [0, 1] | |
gradient.colors = [ new Color(BackgroundGradient1), new Color(BackgroundGradient2) ] | |
widget.backgroundGradient = gradient | |
} | |
// Daten aufbereiten, berechnen und auf Widget darstellen | |
vStack1.addSpacer(); | |
addDataView(vStack1, data.nb_elements.toString(), "", "Fotos", "in " + data.nb_categories.toString() + " Alben", TextColor); | |
vStack1.addSpacer(); | |
vStack2.addSpacer(); | |
addDataView(vStack2, timeDiff.toString() + "ms", defineColorNegative(500, 1000, timeDiff), "Dauer", "je Request", TextColor); | |
vStack2.addSpacer(); | |
Script.setWidget(widget); | |
Script.complete(); | |
widget.presentSmall(); | |
//-------------------------------------------------------------------------------------- | |
//-------------------------------------------------------------------------------------- | |
// zentriert/linksbündigt/rechtbündigt Text | |
function AlignStack(stack, pos){ | |
if(Alignment == 1 || (Alignment == 0 && pos == 1)){ | |
stack.addSpacer(); | |
} | |
} | |
// legt ein neues Datenstack an | |
function addDataView(widget, data, color, name, foot, BorderColor, img){ | |
let viewStack = widget.addStack(); | |
viewStack.layoutVertically(); | |
viewStack.cornerRadius = 5; | |
if(BorderColor != 0){ | |
viewStack.size = new Size(97, 47); | |
viewStack.borderWidth = Border; | |
viewStack.borderColor = new Color(BorderColor, BorderStaerke); | |
viewStack.setPadding(5, 5, 5, 3); | |
} | |
let labelStack = viewStack.addStack(); | |
AlignStack(labelStack, 0); | |
let label = labelStack.addText(name); | |
label.font = Font.mediumSystemFont(12); | |
label.textColor = new Color(TextColor); | |
AlignStack(labelStack, 1); | |
let footnoteStack = viewStack.addStack(); | |
AlignStack(footnoteStack, 0); | |
let footnote = footnoteStack.addText(foot); | |
footnote.font = Font.mediumSystemFont(8); | |
footnote.textColor = new Color(TextColor); | |
AlignStack(footnoteStack, 1); | |
let valueStack = viewStack.addStack(); | |
AlignStack(valueStack, 0); | |
let value = valueStack.addText(data); | |
value.font = Font.mediumSystemFont(18); | |
value.textColor = colorForString(color); | |
AlignStack(valueStack, 1); | |
} | |
//Gibt das entsprechende Color-Objekt zurück | |
function colorForString(colorString){ | |
if (colorString == "red") { | |
return Color.red(); | |
} | |
if (colorString == "yellow") { | |
return Color.yellow(); | |
} | |
if (colorString == "green") { | |
return Color.green(); | |
} | |
return (new Color(TextColor)); | |
} | |
// Legt Farbe fest | |
function defineColorNegative(TreshYellow, TreshRed, value){ | |
if (value >= TreshRed){ | |
return("red"); | |
} | |
else if (value >= TreshYellow){ | |
return("yellow"); | |
} | |
else{ | |
return("green"); | |
} | |
} | |
async function piwigoLogin(username, password, url){ | |
url = url + "/ws.php?format=json&method=pwg.session.login"; | |
let req = new Request(url); | |
req.method = 'post'; | |
let body = "username=" + username + "&password=" + password; | |
req.body = body; | |
let result = await req.loadJSON(); | |
if(result.stat != "ok") throw "Error logging in to Piwigo"; | |
console.log("Successfully logged in to Piwigo"); | |
} | |
async function piwigoLogout(url){ | |
url = url + "/ws.php?format=json&method=pwg.session.logout"; | |
let req = new Request(url); | |
let result = await req.loadJSON(); | |
if(result.stat != "ok") throw "Error logging out to Piwigo"; | |
console.log("Successfully logged out to Piwigo"); | |
} | |
async function piwigoGetData(url){ | |
url = url + "/ws.php?format=json&method=pwg.getInfos"; | |
let req = new Request(url); | |
let result = await req.loadJSON(); | |
if(result.stat != "ok") throw "Error getting Piwigo data"; | |
let data = new Object(); | |
data.nb_elements = result.result.infos[1].value; | |
data.nb_categories = result.result.infos[2].value; | |
console.log("Successfully got data from piwigo"); | |
return(data); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Mit der App Scriptable habe ich zwei iOS-Widgets für Piwigo gebaut.
Das erste Widget ist eher für Administratoren einer Piwigo-Seite gedacht, es zeigt die Anzahl an Fotos und Alben in Piwigo an sowie wie lange die drei API-Requests zum Holen dieser Daten gebraucht haben. Das ist zwar sehr ungenau, gibt mir aber einen schnellen Überblick über die aktuelle Performance des Servers. Aber auch andere Sachen wie z.B. eine schlechte Internetverbindung können natürlich die Zeit beeinflussen…
Verwendet wird hier die kleinste (quadratische) Widget-Größe.
Das zweite Widget ist vor allem für die Anwender gedacht. Es kann a) ein zufälliges Bild der Piwigo-Instanz anzeigen. Wenn gewünscht können die Zufallsbilder auf ein Album begrenzt werden.
Alternativ kann das Widget auch b) ein zufälliges Bild aus den letzten x hochgeladen Bilder anzeigen, wobei x selbst festgelegt werden kann. Auch hier kann das ganze wieder auf ein Album begrenzt werden.
Bei diesem Widget können alle Widget-Größen verwendet werden, die Bilder werden immer so zugeschnitten dass sie das Widget komplett ausfüllen.
Insbesondere bei der Logik der API-Zugriffe habe ich mich an diesem Projekt orientiert.
Vielen Dank für die hier gemachte Vorarbeit, ohne die ich das sicherlich nicht so schnell zum laufen bekommen hätte.