From 363e2af9d6e4ac198dfb885eb6b6660f82bff88b Mon Sep 17 00:00:00 2001 From: CyberMind-FR Date: Fri, 6 Mar 2026 11:34:09 +0100 Subject: [PATCH] feat(photoprism): Add configurable originals_path via UCI/LuCI - Add originals_path option to UCI config (default: /srv/photoprism/originals) - Add set_config RPC method to update originals_path from LuCI - Add Storage Settings section to LuCI dashboard - Update LXC config to use configurable ORIGINALS_PATH - Update get_stats to scan originals_path instead of data_path/originals - Lyrion media_path already configurable via Settings page Both services now support external mount points: - PhotoPrism: /mnt/PHOTO for photos - Lyrion: /mnt/MUSIC for music Co-Authored-By: Claude Opus 4.5 --- .../resources/view/photoprism/overview.js | 56 ++++++++++++++++++- .../root/usr/libexec/rpcd/luci.photoprism | 30 ++++++++-- .../usr/share/rpcd/acl.d/luci-photoprism.json | 3 +- .../files/usr/sbin/photoprismctl | 4 +- 4 files changed, 85 insertions(+), 8 deletions(-) diff --git a/package/secubox/luci-app-photoprism/htdocs/luci-static/resources/view/photoprism/overview.js b/package/secubox/luci-app-photoprism/htdocs/luci-static/resources/view/photoprism/overview.js index 65892b7a..876f51ec 100644 --- a/package/secubox/luci-app-photoprism/htdocs/luci-static/resources/view/photoprism/overview.js +++ b/package/secubox/luci-app-photoprism/htdocs/luci-static/resources/view/photoprism/overview.js @@ -59,6 +59,19 @@ var callEmancipate = rpc.declare({ expect: {} }); +var callGetConfig = rpc.declare({ + object: 'luci.photoprism', + method: 'get_config', + expect: {} +}); + +var callSetConfig = rpc.declare({ + object: 'luci.photoprism', + method: 'set_config', + params: ['originals_path'], + expect: {} +}); + return view.extend({ css: ` :root { @@ -225,11 +238,13 @@ return view.extend({ status: null, stats: null, + config: null, load: function() { return Promise.all([ callStatus(), - callGetStats() + callGetStats(), + callGetConfig() ]); }, @@ -237,6 +252,7 @@ return view.extend({ var self = this; this.status = data[0] || {}; this.stats = data[1] || {}; + this.config = data[2] || {}; var container = E('div', { 'class': 'kiss-container' }, [ E('style', {}, this.css), @@ -245,9 +261,10 @@ return view.extend({ ]); poll.add(function() { - return Promise.all([callStatus(), callGetStats()]).then(function(results) { + return Promise.all([callStatus(), callGetStats(), callGetConfig()]).then(function(results) { self.status = results[0] || {}; self.stats = results[1] || {}; + self.config = results[2] || {}; self.updateView(); }); }, 10); @@ -376,6 +393,41 @@ return view.extend({ ]) ]), + // Settings + E('div', { 'class': 'kiss-section' }, [ + E('h3', {}, 'Storage Settings'), + E('div', { 'class': 'kiss-input-group' }, [ + E('label', { 'style': 'color: var(--kiss-muted); margin-right: 10px;' }, 'Photos Path:'), + E('input', { + 'class': 'kiss-input', + 'type': 'text', + 'id': 'originals-path', + 'value': self.config.originals_path || '/srv/photoprism/originals', + 'placeholder': '/mnt/PHOTO' + }), + E('button', { + 'class': 'kiss-btn kiss-btn-secondary', + 'click': function() { + var path = document.getElementById('originals-path').value; + if (!path) { + ui.addNotification(null, E('p', {}, 'Please enter a path'), 'warning'); + return; + } + this.disabled = true; + this.textContent = 'Saving...'; + var btn = this; + callSetConfig(path).then(function(res) { + if (res.success) { + ui.addNotification(null, E('p', {}, 'Path updated. Restart PhotoPrism to apply.'), 'success'); + } + btn.disabled = false; + btn.textContent = 'Save'; + }); + } + }, 'Save') + ]) + ]), + // Emancipate E('div', { 'class': 'kiss-section' }, [ E('h3', {}, 'Public Exposure'), diff --git a/package/secubox/luci-app-photoprism/root/usr/libexec/rpcd/luci.photoprism b/package/secubox/luci-app-photoprism/root/usr/libexec/rpcd/luci.photoprism index e62e2e85..ba74fd7b 100644 --- a/package/secubox/luci-app-photoprism/root/usr/libexec/rpcd/luci.photoprism +++ b/package/secubox/luci-app-photoprism/root/usr/libexec/rpcd/luci.photoprism @@ -17,6 +17,7 @@ method_get_config() { json_add_boolean "enabled" "$([ "$(uci -q get ${CONFIG}.main.enabled)" = "1" ] && echo true || echo false)" json_add_string "data_path" "$(uci -q get ${CONFIG}.main.data_path || echo '/srv/photoprism')" + json_add_string "originals_path" "$(uci -q get ${CONFIG}.main.originals_path || echo '/srv/photoprism/originals')" json_add_string "http_port" "$(uci -q get ${CONFIG}.main.http_port || echo '2342')" json_add_string "memory_limit" "$(uci -q get ${CONFIG}.main.memory_limit || echo '2G')" json_add_string "admin_user" "$(uci -q get ${CONFIG}.admin.username || echo 'admin')" @@ -28,18 +29,33 @@ method_get_config() { json_dump } +# Method: set_config +method_set_config() { + local originals_path="$1" + + if [ -n "$originals_path" ]; then + uci set ${CONFIG}.main.originals_path="$originals_path" + uci commit ${CONFIG} + fi + + json_init + json_add_boolean "success" true + json_dump +} + # Method: get_stats method_get_stats() { local data_path=$(uci -q get ${CONFIG}.main.data_path || echo '/srv/photoprism') + local originals_path=$(uci -q get ${CONFIG}.main.originals_path || echo '/srv/photoprism/originals') local originals_size="0" local storage_size="0" local photo_count=0 local video_count=0 - if [ -d "${data_path}/originals" ]; then - originals_size=$(du -sh "${data_path}/originals" 2>/dev/null | cut -f1 || echo "0") - photo_count=$(find "${data_path}/originals" -type f \( -iname "*.jpg" -o -iname "*.jpeg" -o -iname "*.png" -o -iname "*.heic" -o -iname "*.raw" -o -iname "*.dng" \) 2>/dev/null | wc -l || echo 0) - video_count=$(find "${data_path}/originals" -type f \( -iname "*.mp4" -o -iname "*.mov" -o -iname "*.avi" -o -iname "*.mkv" \) 2>/dev/null | wc -l || echo 0) + if [ -d "${originals_path}" ]; then + originals_size=$(du -sh "${originals_path}" 2>/dev/null | cut -f1 || echo "0") + photo_count=$(find "${originals_path}" -type f \( -iname "*.jpg" -o -iname "*.jpeg" -o -iname "*.png" -o -iname "*.heic" -o -iname "*.raw" -o -iname "*.dng" \) 2>/dev/null | wc -l || echo 0) + video_count=$(find "${originals_path}" -type f \( -iname "*.mp4" -o -iname "*.mov" -o -iname "*.avi" -o -iname "*.mkv" \) 2>/dev/null | wc -l || echo 0) fi if [ -d "${data_path}/storage" ]; then @@ -162,6 +178,7 @@ case "$1" in echo '{ "status": {}, "get_config": {}, + "set_config": {"originals_path": "string"}, "get_stats": {}, "start": {}, "stop": {}, @@ -178,6 +195,11 @@ case "$1" in case "$2" in status) method_status ;; get_config) method_get_config ;; + set_config) + read -r input + originals_path=$(echo "$input" | jsonfilter -e '@.originals_path' 2>/dev/null) + method_set_config "$originals_path" + ;; get_stats) method_get_stats ;; start) method_start ;; stop) method_stop ;; diff --git a/package/secubox/luci-app-photoprism/root/usr/share/rpcd/acl.d/luci-photoprism.json b/package/secubox/luci-app-photoprism/root/usr/share/rpcd/acl.d/luci-photoprism.json index 72136b36..339d8c21 100644 --- a/package/secubox/luci-app-photoprism/root/usr/share/rpcd/acl.d/luci-photoprism.json +++ b/package/secubox/luci-app-photoprism/root/usr/share/rpcd/acl.d/luci-photoprism.json @@ -22,7 +22,8 @@ "uninstall", "index", "import", - "emancipate" + "emancipate", + "set_config" ] }, "uci": ["photoprism"] diff --git a/package/secubox/secubox-app-photoprism/files/usr/sbin/photoprismctl b/package/secubox/secubox-app-photoprism/files/usr/sbin/photoprismctl index 36ea70e8..495b3ada 100644 --- a/package/secubox/secubox-app-photoprism/files/usr/sbin/photoprismctl +++ b/package/secubox/secubox-app-photoprism/files/usr/sbin/photoprismctl @@ -36,6 +36,7 @@ uci_set() { uci set "${CONFIG}.$1=$2" && uci commit "$CONFIG"; } defaults() { ENABLED=$(uci_get main.enabled 0) DATA_PATH=$(uci_get main.data_path /srv/photoprism) + ORIGINALS_PATH=$(uci_get main.originals_path /srv/photoprism/originals) HTTP_PORT=$(uci_get main.http_port 2342) MEMORY_LIMIT=$(uci_get main.memory_limit 2G) TIMEZONE=$(uci_get main.timezone Europe/Paris) @@ -123,7 +124,7 @@ lxc.net.0.type = none lxc.mount.auto = proc:mixed sys:ro # Bind mounts for data persistence -lxc.mount.entry = ${DATA_PATH}/originals opt/photoprism/originals none bind,create=dir 0 0 +lxc.mount.entry = ${ORIGINALS_PATH} opt/photoprism/originals none bind,create=dir 0 0 lxc.mount.entry = ${DATA_PATH}/storage opt/photoprism/storage none bind,create=dir 0 0 lxc.mount.entry = ${DATA_PATH}/import opt/photoprism/import none bind,create=dir 0 0 @@ -439,6 +440,7 @@ cmd_status() { "videos": $videos, "storage_used": "$storage_used", "data_path": "$DATA_PATH", + "originals_path": "$ORIGINALS_PATH", "domain": "$DOMAIN", "admin_user": "$ADMIN_USER", "face_recognition": $([ "$FACE_RECOGNITION" = "1" ] && echo "true" || echo "false"),