From 9ae591b38fedf16c3e5c97350b7182c5e28ed71f Mon Sep 17 00:00:00 2001 From: Jo-Philipp Wich Date: Sun, 1 Mar 2020 20:21:14 +0100 Subject: [PATCH] luci-app-minidlna: convert to client side rendering Signed-off-by: Jo-Philipp Wich --- applications/luci-app-minidlna/Makefile | 2 +- .../luci-static/resources/view/minidlna.js | 118 ++++++++++++ .../view/status/include/80_minidlna.js | 30 ++++ .../luasrc/controller/minidlna.lua | 46 ----- .../luasrc/model/cbi/minidlna.lua | 169 ------------------ .../view/admin_status/index/minidlna.htm | 1 - .../luasrc/view/minidlna_status.htm | 29 --- .../share/luci/menu.d/luci-app-minidlna.json | 12 ++ .../share/rpcd/acl.d/luci-app-minidlna.json | 19 ++ 9 files changed, 180 insertions(+), 246 deletions(-) create mode 100644 applications/luci-app-minidlna/htdocs/luci-static/resources/view/minidlna.js create mode 100644 applications/luci-app-minidlna/htdocs/luci-static/resources/view/status/include/80_minidlna.js delete mode 100644 applications/luci-app-minidlna/luasrc/controller/minidlna.lua delete mode 100644 applications/luci-app-minidlna/luasrc/model/cbi/minidlna.lua delete mode 100644 applications/luci-app-minidlna/luasrc/view/admin_status/index/minidlna.htm delete mode 100644 applications/luci-app-minidlna/luasrc/view/minidlna_status.htm create mode 100644 applications/luci-app-minidlna/root/usr/share/luci/menu.d/luci-app-minidlna.json create mode 100644 applications/luci-app-minidlna/root/usr/share/rpcd/acl.d/luci-app-minidlna.json diff --git a/applications/luci-app-minidlna/Makefile b/applications/luci-app-minidlna/Makefile index 3d3ec4d52..4790aa32c 100644 --- a/applications/luci-app-minidlna/Makefile +++ b/applications/luci-app-minidlna/Makefile @@ -7,7 +7,7 @@ include $(TOPDIR)/rules.mk LUCI_TITLE:=LuCI Support for miniDLNA -LUCI_DEPENDS:=+luci-compat +minidlna +LUCI_DEPENDS:=+minidlna include ../../luci.mk diff --git a/applications/luci-app-minidlna/htdocs/luci-static/resources/view/minidlna.js b/applications/luci-app-minidlna/htdocs/luci-static/resources/view/minidlna.js new file mode 100644 index 000000000..ff3cae13e --- /dev/null +++ b/applications/luci-app-minidlna/htdocs/luci-static/resources/view/minidlna.js @@ -0,0 +1,118 @@ +'use strict'; +'require fs'; +'require uci'; +'require form'; +'require tools.widgets as widgets'; + +var CBIMiniDLNAStatus = form.DummyValue.extend({ + load: function() { + var port = +uci.get_first('minidlna', 'minidlna', 'port'); + + if (isNaN(port) || port < 0 || port > 65535) + port = 8200; + + return L.resolveDefault(fs.exec_direct('/usr/bin/wget', [ '-q', 'http://127.0.0.1:%d/'.format(port), '-O', '-' ]), null) + .then(L.bind(function(html) { + if (html == null) { + this.default = E('em', {}, [ _('The miniDLNA service is not running.') ]); + } + else { + var audio = html.match(/Audio files<\/td>(\d+)/), + video = html.match(/Video files<\/td>(\d+)/), + image = html.match(/Image files<\/td>(\d+)/); + + this.default = _('The miniDLNA service is active, serving %d audio, %d video and %d image files.') + .format(audio ? +audio[1] : 0, video ? +video[1] : 0, image ? +image[1] : 0); + } + }, this)); + } +}); + +return L.view.extend({ + render: function() { + var m, s, o; + + m = new form.Map('minidlna', _('miniDLNA'), _('MiniDLNA is server software with the aim of being fully compliant with DLNA/UPnP-AV clients.')); + + s = m.section(form.TypedSection); + s.title = _('Status'); + s.anonymous = true; + s.cfgsections = function() { return [ '_status' ] }; + + o = s.option(CBIMiniDLNAStatus); + + + s = m.section(form.TypedSection, 'minidlna', 'miniDLNA Settings'); + s.anonymous = true; + s.addremove = false; + + s.tab('general', _('General Settings')); + s.tab('advanced', _('Advanced Settings')); + + o = s.taboption('general', form.Flag, 'enabled', _('Enable')); + + o = s.taboption('general', form.Value, 'port', _('Port'), + _('Port for HTTP (descriptions, SOAP, media transfer) traffic.')); + o.default = '8200'; + + o = s.taboption('general', widgets.DeviceSelect, 'interface', _('Interfaces'), _('Network interfaces to serve.')); + o.multiple = true; + o.noaliases = true; + o.cfgvalue = function(section_id) { + return L.toArray(uci.get('minidlna', section_id, 'interface')).join(',').split(/[ \t,]+/); + }; + o.write = function(section_id, value) { + return uci.set('minidlna', section_id, 'interface', L.toArray(value).join(',')); + }; + + o = s.taboption('general', form.Value, 'friendly_name', _('Friendly name'), _('Set this if you want to customize the name that shows up on your clients.')); + + o = s.taboption('general', form.ListValue, 'root_container', _('Root container')); + o.value('.', _('Standard container')); + o.value('B', _('Browse directory')); + o.value('M', _('Music')); + o.value('V', _('Video')); + o.value('P', _('Pictures')); + + o = s.taboption('general', form.DynamicList, 'media_dir', _('Media directories'), _('Set this to the directory you want scanned. If you want to restrict the directory to a specific content type, you can prepend the type (\'A\' for audio, \'V\' for video, \'P\' for images), followed by a comma, to the directory (eg. A,/mnt/media/Music). Multiple directories can be specified.')); + + o = s.taboption('general', form.DynamicList, 'album_art_names', _('Album art names'), _('This is a list of file names to check for when searching for album art.')); + o.cfgvalue = function(section_id) { + return L.toArray(uci.get('minidlna', section_id, 'album_art_names')).join('/').split(/\//); + }; + o.write = function(section_id, value) { + return uci.set('minidlna', section_id, 'album_art_names', L.toArray(value).join('/')); + }; + + o = s.taboption('advanced', form.Value, 'db_dir', _('Database directory'), _('Set this if you would like to specify the directory where you want MiniDLNA to store its database and album art cache.')); + + o = s.taboption('advanced', form.Value, 'log_dir', _('Log directory'), _('Set this if you would like to specify the directory where you want MiniDLNA to store its log file.')); + + o = s.taboption('advanced', form.Flag, 'inotify', _('Enable inotify'), _('Set this to enable inotify monitoring to automatically discover new files.')); + o.default = o.enabled; + + o = s.taboption('advanced', form.Flag, 'enable_tivo', _('Enable TIVO'), _('Set this to enable support for streaming .jpg and .mp3 files to a TiVo supporting HMO.')); + + o = s.taboption('advanced', form.Flag, 'wide_links', _('Allow wide links'), _('Set this to allow serving content outside the media root (via symlinks).')); + + o = s.taboption('advanced', form.Flag, 'strict_dlna', _('Strict to DLNA standard'), _('Set this to strictly adhere to DLNA standards. This will allow server-side downscaling of very large JPEG images, which may hurt JPEG serving performance on (at least) Sony DLNA products.')); + + o = s.taboption('advanced', form.Value, 'presentation_url', _('Presentation URL')); + + o = s.taboption('advanced', form.Value, 'notify_interval', _('Notify interval'), _('Notify interval in seconds.')); + o.placeholder = '900'; + + o = s.taboption('advanced', form.Value, 'serial', _('Announced serial number'), _('Serial number the miniDLNA daemon will report to clients in its XML description.')); + o.placeholder = '12345678'; + + o = s.taboption('advanced', form.Value, 'uuid', _('Announced UUID')); + o.placeholder = '019f9a56-ff60-44c0-9edc-eae88d09fa05'; + + o = s.taboption('advanced', form.Value, 'model_number', _('Announced model number'), _('Model number the miniDLNA daemon will report to clients in its XML description.')); + o.placeholder = '1'; + + o = s.taboption('advanced', form.Value, 'minissdpsocket', _('miniSSDP socket'), _('Specify the path to the MiniSSDPd socket.')); + + return m.render(); + } +}); diff --git a/applications/luci-app-minidlna/htdocs/luci-static/resources/view/status/include/80_minidlna.js b/applications/luci-app-minidlna/htdocs/luci-static/resources/view/status/include/80_minidlna.js new file mode 100644 index 000000000..f67e5f84b --- /dev/null +++ b/applications/luci-app-minidlna/htdocs/luci-static/resources/view/status/include/80_minidlna.js @@ -0,0 +1,30 @@ +'use strict'; +'require fs'; +'require uci'; + +return L.Class.extend({ + title: _('miniDLNA Status'), + + load: function() { + return uci.load('minidlna').then(function() { + var port = +uci.get_first('minidlna', 'minidlna', 'port'); + + if (isNaN(port) || port < 0 || port > 65535) + port = 8200; + + return L.resolveDefault(fs.exec_direct('/usr/bin/wget', [ '-q', 'http://127.0.0.1:%d/'.format(port), '-O', '-' ]), null); + }); + }, + + render: function(html) { + if (html == null) + return E('em', {}, [ _('The miniDLNA service is not running.') ]); + + var audio = html.match(/Audio files<\/td>(\d+)/), + video = html.match(/Video files<\/td>(\d+)/), + image = html.match(/Image files<\/td>(\d+)/); + + return _('The miniDLNA service is active, serving %d audio, %d video and %d image files.') + .format(audio ? +audio[1] : 0, video ? +video[1] : 0, image ? +image[1] : 0); + } +}); diff --git a/applications/luci-app-minidlna/luasrc/controller/minidlna.lua b/applications/luci-app-minidlna/luasrc/controller/minidlna.lua deleted file mode 100644 index 3420f305e..000000000 --- a/applications/luci-app-minidlna/luasrc/controller/minidlna.lua +++ /dev/null @@ -1,46 +0,0 @@ --- Copyright 2012 Gabor Juhos --- Licensed to the public under the Apache License 2.0. - -module("luci.controller.minidlna", package.seeall) - -function index() - if not nixio.fs.access("/etc/config/minidlna") then - return - end - - local page - - page = entry({"admin", "services", "minidlna"}, cbi("minidlna"), _("miniDLNA")) - page.dependent = true - - entry({"admin", "services", "minidlna_status"}, call("minidlna_status")) -end - -function minidlna_status() - local sys = require "luci.sys" - local uci = require "luci.model.uci".cursor() - local port = tonumber(uci:get_first("minidlna", "minidlna", "port")) - - local status = { - running = (sys.call("pidof minidlnad >/dev/null") == 0), - audio = 0, - video = 0, - image = 0 - } - - if status.running then - local fd = sys.httpget("http://127.0.0.1:%d/" % (port or 8200), true) - if fd then - local html = fd:read("*a") - if html then - status.audio = (tonumber(html:match("Audio files(%d+)")) or 0) - status.video = (tonumber(html:match("Video files(%d+)")) or 0) - status.image = (tonumber(html:match("Image files(%d+)")) or 0) - end - fd:close() - end - end - - luci.http.prepare_content("application/json") - luci.http.write_json(status) -end diff --git a/applications/luci-app-minidlna/luasrc/model/cbi/minidlna.lua b/applications/luci-app-minidlna/luasrc/model/cbi/minidlna.lua deleted file mode 100644 index 9dcc527a3..000000000 --- a/applications/luci-app-minidlna/luasrc/model/cbi/minidlna.lua +++ /dev/null @@ -1,169 +0,0 @@ --- Copyright 2012 Gabor Juhos --- Licensed to the public under the Apache License 2.0. - -local m, s, o - -m = Map("minidlna", translate("miniDLNA"), - translate("MiniDLNA is server software with the aim of being fully compliant with DLNA/UPnP-AV clients.")) - -m:section(SimpleSection).template = "minidlna_status" - -s = m:section(TypedSection, "minidlna", "miniDLNA Settings") -s.addremove = false -s.anonymous = true - -s:tab("general", translate("General Settings")) -s:tab("advanced", translate("Advanced Settings")) - -o = s:taboption("general", Flag, "enabled", translate("Enable")) -o.rmempty = false - -function o.cfgvalue(self, section) - return luci.sys.init.enabled("minidlna") and self.enabled or self.disabled -end - -function o.write(self, section, value) - if value == "1" then - luci.sys.init.enable("minidlna") - luci.sys.call("/etc/init.d/minidlna start >/dev/null") - else - luci.sys.call("/etc/init.d/minidlna stop >/dev/null") - luci.sys.init.disable("minidlna") - end - - return Flag.write(self, section, value) -end - -o = s:taboption("general", Value, "port", translate("Port"), - translate("Port for HTTP (descriptions, SOAP, media transfer) traffic.")) -o.datatype = "port" -o.default = 8200 - - -o = s:taboption("general", Value, "interface", translate("Interfaces"), - translate("Network interfaces to serve.")) - -o.template = "cbi/network_ifacelist" -o.widget = "checkbox" -o.nocreate = true - -function o.cfgvalue(self, section) - local rv = { } - local val = Value.cfgvalue(self, section) - if val then - local ifc - for ifc in val:gmatch("[^,%s]+") do - rv[#rv+1] = ifc - end - end - return rv -end - -function o.write(self, section, value) - local rv = { } - local ifc - for ifc in luci.util.imatch(value) do - rv[#rv+1] = ifc - end - Value.write(self, section, table.concat(rv, ",")) -end - - -o = s:taboption("general", Value, "friendly_name", translate("Friendly name"), - translate("Set this if you want to customize the name that shows up on your clients.")) -o.rmempty = true -o.placeholder = "OpenWrt DLNA Server" - -o = s:taboption("advanced", Value, "db_dir", translate("Database directory"), - translate("Set this if you would like to specify the directory where you want MiniDLNA to store its database and album art cache.")) -o.rmempty = true -o.placeholder = "/var/cache/minidlna" - -o = s:taboption("advanced", Value, "log_dir", translate("Log directory"), - translate("Set this if you would like to specify the directory where you want MiniDLNA to store its log file.")) -o.rmempty = true -o.placeholder = "/var/log" - -s:taboption("advanced", Flag, "inotify", translate("Enable inotify"), - translate("Set this to enable inotify monitoring to automatically discover new files.")) - -s:taboption("advanced", Flag, "enable_tivo", translate("Enable TIVO"), - translate("Set this to enable support for streaming .jpg and .mp3 files to a TiVo supporting HMO.")) -o.rmempty = true - -s:taboption("advanced", Flag, "wide_links", translate("Allow wide links"), - translate("Set this to allow serving content outside the media root (via symlinks).")) -o.rmempty = true - -o = s:taboption("advanced", Flag, "strict_dlna", translate("Strict to DLNA standard"), - translate("Set this to strictly adhere to DLNA standards. This will allow server-side downscaling of very large JPEG images, which may hurt JPEG serving performance on (at least) Sony DLNA products.")) -o.rmempty = true - -o = s:taboption("advanced", Value, "presentation_url", translate("Presentation URL")) -o.rmempty = true -o.placeholder = "http://192.168.1.1/" - -o = s:taboption("advanced", Value, "notify_interval", translate("Notify interval"), - translate("Notify interval in seconds.")) -o.datatype = "uinteger" -o.placeholder = 900 - -o = s:taboption("advanced", Value, "serial", translate("Announced serial number"), - translate("Serial number the miniDLNA daemon will report to clients in its XML description.")) -o.placeholder = "12345678" - -s:taboption("advanced", Value, "model_number", translate("Announced model number"), - translate("Model number the miniDLNA daemon will report to clients in its XML description.")) -o.placholder = "1" - -o = s:taboption("advanced", Value, "minissdpsocket", translate("miniSSDP socket"), - translate("Specify the path to the MiniSSDPd socket.")) -o.rmempty = true -o.placeholder = "/var/run/minissdpd.sock" - -o = s:taboption("general", ListValue, "root_container", translate("Root container")) -o:value(".", translate("Standard container")) -o:value("B", translate("Browse directory")) -o:value("M", translate("Music")) -o:value("V", translate("Video")) -o:value("P", translate("Pictures")) - - -s:taboption("general", DynamicList, "media_dir", translate("Media directories"), - translate("Set this to the directory you want scanned. If you want to restrict the directory to a specific content type, you can prepend the type ('A' for audio, 'V' for video, 'P' for images), followed by a comma, to the directory (eg. A,/mnt/media/Music). Multiple directories can be specified.")) - - -o = s:taboption("general", DynamicList, "album_art_names", translate("Album art names"), - translate("This is a list of file names to check for when searching for album art.")) -o.rmempty = true -o.placeholder = "Cover.jpg" - -function o.cfgvalue(self, section) - local rv = { } - - local val = Value.cfgvalue(self, section) - if type(val) == "table" then - val = table.concat(val, "/") - elseif not val then - val = "" - end - - local file - for file in val:gmatch("[^/%s]+") do - rv[#rv+1] = file - end - - return rv -end - -function o.write(self, section, value) - local rv = { } - local file - for file in luci.util.imatch(value) do - rv[#rv+1] = file - end - Value.write(self, section, table.concat(rv, "/")) -end - - -return m diff --git a/applications/luci-app-minidlna/luasrc/view/admin_status/index/minidlna.htm b/applications/luci-app-minidlna/luasrc/view/admin_status/index/minidlna.htm deleted file mode 100644 index b2feeb2ef..000000000 --- a/applications/luci-app-minidlna/luasrc/view/admin_status/index/minidlna.htm +++ /dev/null @@ -1 +0,0 @@ -<%+minidlna_status%> diff --git a/applications/luci-app-minidlna/luasrc/view/minidlna_status.htm b/applications/luci-app-minidlna/luasrc/view/minidlna_status.htm deleted file mode 100644 index eaf372137..000000000 --- a/applications/luci-app-minidlna/luasrc/view/minidlna_status.htm +++ /dev/null @@ -1,29 +0,0 @@ - - -
- <%:miniDLNA Status%> -

- <%:Collecting data...%> -

-
diff --git a/applications/luci-app-minidlna/root/usr/share/luci/menu.d/luci-app-minidlna.json b/applications/luci-app-minidlna/root/usr/share/luci/menu.d/luci-app-minidlna.json new file mode 100644 index 000000000..ddb4129b4 --- /dev/null +++ b/applications/luci-app-minidlna/root/usr/share/luci/menu.d/luci-app-minidlna.json @@ -0,0 +1,12 @@ +{ + "admin/services/minidlna": { + "title": "miniDLNA", + "action": { + "type": "view", + "path": "minidlna" + }, + "depends": { + "uci": { "minidlna": true } + } + } +} diff --git a/applications/luci-app-minidlna/root/usr/share/rpcd/acl.d/luci-app-minidlna.json b/applications/luci-app-minidlna/root/usr/share/rpcd/acl.d/luci-app-minidlna.json new file mode 100644 index 000000000..a49d2f1e5 --- /dev/null +++ b/applications/luci-app-minidlna/root/usr/share/rpcd/acl.d/luci-app-minidlna.json @@ -0,0 +1,19 @@ +{ + "luci-app-minidlna": { + "description": "Grant access to minidlna status and configuration", + "read": { + "cgi-io": [ "exec" ], + "file": { + "/usr/bin/wget -q http://127.0.0.1:[0-9]*/ -O -": [ "exec" ] + }, + "uci": [ + "minidlna" + ] + }, + "write": { + "uci": [ + "minidlna" + ] + } + } +} -- 2.25.1