luci-app-minidlna: convert to client side rendering
Signed-off-by: Jo-Philipp Wich <jo@mein.io>
This commit is contained in:
parent
0d68d0ad7b
commit
9ae591b38f
9 changed files with 180 additions and 246 deletions
|
@ -7,7 +7,7 @@
|
||||||
include $(TOPDIR)/rules.mk
|
include $(TOPDIR)/rules.mk
|
||||||
|
|
||||||
LUCI_TITLE:=LuCI Support for miniDLNA
|
LUCI_TITLE:=LuCI Support for miniDLNA
|
||||||
LUCI_DEPENDS:=+luci-compat +minidlna
|
LUCI_DEPENDS:=+minidlna
|
||||||
|
|
||||||
include ../../luci.mk
|
include ../../luci.mk
|
||||||
|
|
||||||
|
|
|
@ -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();
|
||||||
|
}
|
||||||
|
});
|
|
@ -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);
|
||||||
|
}
|
||||||
|
});
|
|
@ -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
|
|
|
@ -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
|
|
|
@ -1 +0,0 @@
|
||||||
<%+minidlna_status%>
|
|
|
@ -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>
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
{
|
||||||
|
"admin/services/minidlna": {
|
||||||
|
"title": "miniDLNA",
|
||||||
|
"action": {
|
||||||
|
"type": "view",
|
||||||
|
"path": "minidlna"
|
||||||
|
},
|
||||||
|
"depends": {
|
||||||
|
"uci": { "minidlna": true }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue