include $(TOPDIR)/rules.mk
LUCI_TITLE:=LuCI Support for miniDLNA
-LUCI_DEPENDS:=+luci-compat +minidlna
+LUCI_DEPENDS:=+minidlna
include ../../luci.mk
--- /dev/null
+'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();
+ }
+});
--- /dev/null
+'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);
+ }
+});
+++ /dev/null
--- 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
+++ /dev/null
--- 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
+++ /dev/null
-<%+minidlna_status%>
+++ /dev/null
-<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>
--- /dev/null
+{
+ "admin/services/minidlna": {
+ "title": "miniDLNA",
+ "action": {
+ "type": "view",
+ "path": "minidlna"
+ },
+ "depends": {
+ "uci": { "minidlna": true }
+ }
+ }
+}
--- /dev/null
+{
+ "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"
+ ]
+ }
+ }
+}