luci-app-minidlna: convert to client side rendering

Signed-off-by: Jo-Philipp Wich <jo@mein.io>
This commit is contained in:
Jo-Philipp Wich 2020-03-01 20:21:14 +01:00
parent 0d68d0ad7b
commit 9ae591b38f
9 changed files with 180 additions and 246 deletions

View file

@ -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

View file

@ -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><td>(\d+)/),
video = html.match(/Video files<\/td><td>(\d+)/),
image = html.match(/Image files<\/td><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();
}
});

View file

@ -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><td>(\d+)/),
video = html.match(/Video files<\/td><td>(\d+)/),
image = html.match(/Image files<\/td><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);
}
});

View file

@ -1,46 +0,0 @@
-- Copyright 2012 Gabor Juhos <juhosg@openwrt.org>
-- 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</td><td>(%d+)")) or 0)
status.video = (tonumber(html:match("Video files</td><td>(%d+)")) or 0)
status.image = (tonumber(html:match("Image files</td><td>(%d+)")) or 0)
end
fd:close()
end
end
luci.http.prepare_content("application/json")
luci.http.write_json(status)
end

View file

@ -1,169 +0,0 @@
-- Copyright 2012 Gabor Juhos <juhosg@openwrt.org>
-- 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

View file

@ -1,29 +0,0 @@
<script type="text/javascript">//<![CDATA[
XHR.poll(-1, '<%=url("admin/services/minidlna_status")%>', null,
function(x, st)
{
var tb = document.getElementById('minidlna_status');
if (st && tb)
{
if (st.running)
{
tb.innerHTML = String.format(
'<%:The miniDLNA service is active, serving %d audio, %d video and %d image files.%>',
st.audio, st.video, st.image
);
}
else
{
tb.innerHTML = '<em><%:The miniDLNA service is not running.%></em>';
}
}
}
);
//]]></script>
<fieldset class="cbi-section">
<legend><%:miniDLNA Status%></legend>
<p id="minidlna_status">
<em><%:Collecting data...%></em>
</p>
</fieldset>

View file

@ -0,0 +1,12 @@
{
"admin/services/minidlna": {
"title": "miniDLNA",
"action": {
"type": "view",
"path": "minidlna"
},
"depends": {
"uci": { "minidlna": true }
}
}
}

View file

@ -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"
]
}
}
}