diff options
Diffstat (limited to 'applications')
14 files changed, 832 insertions, 803 deletions
diff --git a/applications/luci-app-aria2/Makefile b/applications/luci-app-aria2/Makefile index 30089f8612..ad6bd1d184 100644 --- a/applications/luci-app-aria2/Makefile +++ b/applications/luci-app-aria2/Makefile @@ -7,7 +7,7 @@ include $(TOPDIR)/rules.mk LUCI_TITLE:=LuCI Support for Aria2 -LUCI_DEPENDS:=+luci-compat +aria2 +luci-lib-ipkg +LUCI_DEPENDS:=+aria2 LUCI_PKGARCH:=all PKG_MAINTAINER:=Xingwang Liao <kuoruan@gmail.com> diff --git a/applications/luci-app-aria2/htdocs/luci-static/resources/view/aria2/config.js b/applications/luci-app-aria2/htdocs/luci-static/resources/view/aria2/config.js new file mode 100644 index 0000000000..84b5075fd7 --- /dev/null +++ b/applications/luci-app-aria2/htdocs/luci-static/resources/view/aria2/config.js @@ -0,0 +1,630 @@ +'use strict'; +'require dom'; +'require form'; +'require fs'; +'require poll'; +'require rpc'; +'require tools.widgets as widgets'; +'require ui'; +'require view'; + +var callServiceList, CBIAria2Status, CBIRpcSecret, CBIRpcUrl; + +callServiceList = rpc.declare({ + object: 'service', + method: 'list', + params: [ 'name' ], + expect: { '': {} }, + filter: function (data, args, extra) { + var i, res = data[args.name] || {}; + for (i = 0; (i < extra.length) && (Object.keys(res).length > 0); ++i) + res = res[extra[i]] || {}; + return res; + } +}); + +CBIAria2Status = form.DummyValue.extend({ + renderWidget: function() { + var extra = ['instances', 'aria2.main']; + var node = E('div', {}, E('p', {}, E('em', {}, _('Collecting data...')))); + poll.add(function() { + return Promise.all([ + callServiceList('aria2', extra) + .then(function(res) { + return E('p', {}, E('em', {}, res.running + ? _('The Aria2 service is running.') + : _('The Aria2 service is not running.')) + ); + }), + getWebFrontInstalled() + .then(function(installed) { + var btns = [E('label'), _('Installed web interface: ')]; + for (var i in installed) { + btns.push(E('button', { + 'class': 'btn cbi-button', + 'click': openWebInterface.bind(this, i) + }, installed[i])); + } + return btns.length > 0 ? E('p', btns) : null; + }) + ]).then(function(res) { + res = res.filter(function(r) { return r ? 1 : 0 }); + dom.content(node, res); + }); + }); + return node; + } +}); + +CBIRpcSecret = form.Value.extend({ + renderWidget: function(section_id, option_index, cfgvalue) { + var node = this.super('renderWidget', [section_id, option_index, cfgvalue]); + dom.append(node, [ + E('br'), + E('span', { 'class': 'control-group' }, + E('button', { + 'class': 'btn cbi-button cbi-button-neutral', + 'click': this.clickFn.bind(this, section_id) + }, this.btnTitle) + ) + ]); + return node; + } +}); + +CBIRpcUrl = form.DummyValue.extend({ + renderWidget: function(section_id, option_index, cfgvalue) { + var inputEl = new ui.Textfield('', {'id': this.cbid(section_id), 'readonly': true}); + return E([inputEl.render(), + E('br'), + E('span', { 'class': 'control-group' }, [ + E('button', { + 'class': 'btn cbi-button cbi-button-neutral', + 'click': this.clickFn.bind(this, section_id, 0, inputEl) + }, 'HTTP(s)'), + E('button', { + 'class': 'btn cbi-button cbi-button-neutral', + 'click': this.clickFn.bind(this, section_id, 1, inputEl) + }, 'WebSocket(s)') + ]) + ]); + } +}); + +function getToken(section_id) { + var len = 32, randomStr = ''; + var inputLength = prompt(_('Please input token length:'), len); + if (inputLength === null || inputLength === '') { + return; + } else if (/^\d+$/.test(inputLength)) { + len = parseInt(inputLength); + } + + while(len - randomStr.length > 0) { + randomStr += Math.random().toString(36).substring(2, 2 + len - randomStr.length); + } + document.getElementById('widget.' + this.cbid(section_id)).value = randomStr; +}; + +function getWebFrontInstalled() { + var supported = {'ariang': 'AriaNg', 'webui-aria2': 'WebUI-Aria2', 'yaaw': 'YAAW'}; + var actions = []; + + for (var s in supported) { + actions.push(fs.stat('/www/' + s + '/index.html') + .then(L.bind(function(s) { return s; }, this, s)) + .catch(function(err) { return null; })); + } + + return Promise.all(actions).then(function(res) { + var installed = {}; + for (var i = 0; i < res.length; ++i) + if (res[i]) + installed[res[i]] = supported[res[i]]; + return installed; + }); +} + +function openWebInterface(path) { + var host = window.location.host; + var protocol = window.location.protocol; + window.open(protocol + '//' + host + '/' + path); +}; + +function showRPCURL(section_id, useWS, inputEl) { + var getOptVal = L.bind(function(opt, default_val) { + default_val = default_val || null; + return this.section.formvalue(section_id, opt) || default_val; + }, this); + + var port = getOptVal('rpc_listen_port', 6800); + var authMethod = getOptVal('rpc_auth_method', 'none'); + var secure = JSON.parse(getOptVal('rpc_secure', false)); + + var protocol = useWS + ? (secure ? 'wss' : 'ws') + : (secure ? 'https' : 'http'); + var url = protocol + '://'; + + if (authMethod == 'token') { + var authToken = getOptVal('rpc_secret'); + if (authToken) + url += 'token:' + authToken + '@'; + } else if (authMethod == 'user_pass') { + var authUser = getOptVal('rpc_user'); + var authPasswd = getOptVal('rpc_passwd'); + if (authUser && authPasswd) + url += authUser + ':' + authPasswd + '@'; + } + url += window.location.hostname + ':' + port + '/jsonrpc'; + inputEl.setValue(url); +}; + +return view.extend({ + load: function() { + return fs.exec_direct('/usr/bin/aria2c', [ '-v' ]).then(function(res) { + var info = {}, lines = res.split(/\r?\n|\r/g); + + for (var i = 0; i < lines.length; ++i) { + if (/^aria2 version/.exec(lines[i])) { + info.version = lines[i].match(/(\d+\.){2}\d+/)[0]; + } + else if (/^Enabled Features/.exec(lines[i])) { + info.gzip = lines[i].search(/GZip/) >= 0; + info.https = lines[i].search(/HTTPS/) >= 0; + info.bt = lines[i].search(/BitTorrent/) >= 0; + info.sftp = lines[i].search(/SFTP/) >= 0; + info.adns = lines[i].search(/Async DNS/) >= 0; + info.cookie = lines[i].search(/Firefox3 Cookie/) >= 0; + } + } + return info; + }); + }, + + render: function(aria2) { + var m, s, o; + + m = new form.Map('aria2', '%s - %s'.format(_('Aria2'), _('Settings')), '<p>%s</p><p>%s</p>'.format( + _('Aria2 is a lightweight multi-protocol & multi-source, cross platform download utility.'), + _('For more information, please visit: %s.') + .format('<a href="https://aria2.github.io" target="_blank">https://aria2.github.io</a>'))); + + s = m.section(form.TypedSection); + s.title = '%s - %s'.format(_('Aria2'), _('Running Status')); + s.anonymous = true; + s.cfgsections = function() { return [ 'status' ] }; + + o = s.option(CBIAria2Status); + + s = m.section(form.NamedSection, 'main', 'aria2'); + s.addremove = false; + s.anonymous = true; + + s.tab('basic', _('Basic Options')); + + o = s.taboption('basic', form.Flag, 'enabled', _('Enabled')); + o.rmempty = false; + + o = s.taboption('basic', widgets.UserSelect, 'user', _('Run daemon as user'), + _('Leave blank to use default user.')); + + o = s.taboption('basic', form.Value, 'dir', _('Download directory'), + _('The directory to store the downloaded file. For example <code>/mnt/sda1</code>.')); + o.rmempty = false; + + o = s.taboption('basic', form.Value, 'config_dir', _('Config file directory'), + _('The directory to store the config file, session file and DHT file.')); + o.placeholder = '/var/etc/aria2'; + + o = s.taboption('basic', form.Flag, 'enable_logging', _('Enable logging')); + o.rmempty = false; + + o = s.taboption('basic', form.Value, 'log', _('Log file'), + _('The file name of the log file.')); + o.depends('enable_logging', '1'); + o.placeholder = '/var/log/aria2.log'; + + o = s.taboption('basic', form.ListValue, 'log_level', _('Log level')); + o.depends('enable_logging', '1'); + o.value('debug', _('Debug')); + o.value('info', _('Info')); + o.value('notice', _('Notice')); + o.value('warn', _('Warn')); + o.value('error', _('Error')); + o.default = 'warn'; + + o = s.taboption('basic', form.Value, 'max_concurrent_downloads', _('Max concurrent downloads')); + o.placeholder = '5'; + + s.tab('rpc', _('RPC Options')) + + o = s.taboption('rpc', form.Flag, 'pause', _('Pause'), _('Pause download after added.')); + o.enabled = 'true'; + o.disabled = 'false'; + o.default = 'false'; + + o = s.taboption('rpc', form.Flag, 'pause_metadata', _('Pause metadata'), + _('Pause downloads created as a result of metadata download.')); + o.enabled = 'true'; + o.disabled = 'false'; + o.default = 'false'; + + o = s.taboption('rpc', form.Value, 'rpc_listen_port', _('RPC port')); + o.datatype = 'range(1024,65535)'; + o.placeholder = '6800'; + + o = s.taboption('rpc', form.ListValue, 'rpc_auth_method', _('RPC authentication method')); + o.value('none', _('No Authentication')); + o.value('user_pass', _('Username & Password')); + o.value('token', _('Token')); + + o = s.taboption('rpc', form.Value, 'rpc_user', _('RPC username')); + o.depends('rpc_auth_method', 'user_pass'); + + o = s.taboption('rpc', form.Value, 'rpc_passwd', _('RPC password')); + o.depends('rpc_auth_method', 'user_pass'); + o.password = true; + + o = s.taboption('rpc', CBIRpcSecret, 'rpc_secret', _('RPC token')); + o.depends('rpc_auth_method', 'token'); + o.btnTitle = _('Generate Randomly'); + o.clickFn = getToken; + o.password = true; + + if (aria2.https) { + o = s.taboption('rpc', form.Flag, 'rpc_secure', _('RPC secure'), + _('RPC transport will be encrypted by SSL/TLS. The RPC clients must use https' + + ' scheme to access the server. For WebSocket client, use wss scheme.')); + o.enabled = 'true'; + o.disabled = 'false'; + o.rmempty = false; + + o = s.taboption('rpc', form.Value, 'rpc_certificate', _('RPC certificate'), + _('Use the certificate in FILE for RPC server. The certificate must be either' + + ' in PKCS12 (.p12, .pfx) or in PEM format.<br/>PKCS12 files must contain the' + + ' certificate, a key and optionally a chain of additional certificates. Only PKCS12' + + ' files with a blank import password can be opened!<br/>When using PEM, you have to' + + ' specify the "RPC private key" as well.')); + o.depends('rpc_secure', 'true'); + o.optional = false; + o.rmempty = false; + + o = s.taboption('rpc', form.Value, 'rpc_private_key', _('RPC private key'), + _('Use the private key in FILE for RPC server. The private key must be' + + ' decrypted and in PEM format.')); + o.depends('rpc_secure', 'true'); + o.optional = false; + o.rmempty = false; + } + + o = s.taboption('rpc', CBIRpcUrl, '_rpc_url', _('Json-RPC URL')); + o.clickFn = showRPCURL; + + s.tab('http', _('HTTP/FTP/SFTP Options')); + + o = s.taboption('http', form.Flag, 'enable_proxy', _('Enable proxy')); + o.rmempty = false; + + o = s.taboption('http', form.Value, 'all_proxy', _('All proxy'), + _('Use a proxy server for all protocols.')); + o.depends('enable_proxy', '1'); + o.placeholder = '[http://][USER:PASSWORD@]HOST[:PORT]'; + + o = s.taboption('http', form.Value, 'all_proxy_user', _('Proxy user')); + o.depends('enable_proxy', '1'); + + o = s.taboption('http', form.Value, 'all_proxy_passwd', _('Proxy password')); + o.depends('enable_proxy', '1'); + o.password = true; + + if (aria2.https) { + o = s.taboption('http', form.Flag, 'check_certificate', _('Check certificate'), + _('Verify the peer using certificates specified in "CA certificate" option.')); + o.enabled = 'true'; + o.disabled = 'false'; + o.default = 'true'; + o.rmempty = false; + + o = s.taboption('http', form.Value, 'ca_certificate', _('CA certificate'), + _('Use the certificate authorities in FILE to verify the peers. The certificate' + + ' file must be in PEM format and can contain multiple CA certificates.')); + o.depends('check_certificate', 'true'); + + o = s.taboption('http', form.Value, 'certificate', _('Certificate'), + _('Use the client certificate in FILE. The certificate must be either in PKCS12' + + ' (.p12, .pfx) or in PEM format.<br/>PKCS12 files must contain the certificate, a' + + ' key and optionally a chain of additional certificates. Only PKCS12 files with a' + + ' blank import password can be opened!<br/>When using PEM, you have to specify the' + + ' "Private key" as well.')); + + o = s.taboption('http', form.Value, 'private_key', _('Private key'), + _('Use the private key in FILE. The private key must be decrypted and in PEM' + + ' format. The behavior when encrypted one is given is undefined.')); + } + + if (aria2.gzip) { + o = s.taboption('http', form.Flag, 'http_accept_gzip', _('HTTP accept gzip'), + _('Send <code>Accept: deflate, gzip</code> request header and inflate response' + + ' if remote server responds with <code>Content-Encoding: gzip</code> or' + + ' <code>Content-Encoding: deflate</code>.')); + o.enabled = 'true'; + o.disabled = 'false'; + o.default = 'false'; + } + + o = s.taboption('http', form.Flag, 'http_no_cache', _('HTTP no cache'), + _('Send <code>Cache-Control: no-cache</code> and <code>Pragma: no-cache</code>' + + ' header to avoid cached content. If disabled, these headers are not sent and you' + + ' can add Cache-Control header with a directive you like using "Header" option.')); + o.enabled = 'true'; + o.disabled = 'false'; + o.default = 'false'; + + o = s.taboption('http', form.DynamicList, 'header', _('Header'), + _('Append HEADERs to HTTP request header.')); + + o = s.taboption('http', form.Value, 'connect_timeout', _('Connect timeout'), + _('Set the connect timeout in seconds to establish connection to HTTP/FTP/proxy server.' + + ' After the connection is established, this option makes no effect and "Timeout" option is used instead.')); + o.datatype = 'uinteger'; + o.placeholder = '60'; + + o = s.taboption('http', form.Value, 'timeout', _('Timeout')); + o.datatype = 'uinteger'; + o.placeholder = '60'; + + o = s.taboption('http', form.Value, 'lowest_speed_limit', _('Lowest speed limit'), + '%s %s'.format( + _('Close connection if download speed is lower than or equal to this value (bytes per sec). ' + + '0 means has no lowest speed limit.'), + _('You can append K or M.') + )); + o.placeholder = '0'; + + o = s.taboption('http', form.Value, 'max_connection_per_server', _('Max connection per server'), + _('The maximum number of connections to one server for each download.')); + o.datatype = 'uinteger'; + o.placeholder = '1'; + + o = s.taboption('http', form.Value, 'split', _('Max number of split'), + _('Download a file using N connections.')); + o.datatype = 'uinteger'; + o.placeholder = '5'; + + o = s.taboption('http', form.Value, 'min_split_size', _('Min split size'), + _('Don\'t split less than 2*SIZE byte range. Possible values: 1M-1024M.')); + o.placeholder = '20M'; + + o = s.taboption('http', form.Value, 'max_tries', _('Max tries')); + o.datatype = 'uinteger'; + o.placeholder = '5'; + + o = s.taboption('http', form.Value, 'retry_wait', _('Retry wait'), + _('Set the seconds to wait between retries.')); + o.datatype = 'uinteger'; + o.placeholder = '0'; + + o = s.taboption('http', form.Value, 'user_agent', _('User agent'), + _('Set user agent for HTTP(S) downloads.')); + o.placeholder = 'aria2/%s'.format(aria2.version ? aria2.version : '$VERSION'); + + if (aria2.bt) { + s.tab('bt', _('BitTorrent Options')); + + o = s.taboption('bt', form.Flag, 'enable_dht', _('IPv4 <abbr title="Distributed Hash Table">DHT</abbr> enabled'), + '%s %s'.format( + _('Enable IPv4 DHT functionality. It also enables UDP tracker support.'), + _('This option will be ignored if a private flag is set in a torrent.') + )); + o.enabled = 'true'; + o.disabled = 'false'; + o.default = 'true'; + o.rmempty = false; + + o = s.taboption('bt', form.Flag, 'enable_dht6', _('IPv6 <abbr title="Distributed Hash Table">DHT</abbr> enabled'), + '%s %s'.format( + _('Enable IPv6 DHT functionality.'), + _('This option will be ignored if a private flag is set in a torrent.') + )); + o.enabled = 'true'; + o.disabled = 'false'; + + o = s.taboption('bt', form.Flag, 'bt_enable_lpd', _('<abbr title="Local Peer Discovery">LPD</abbr> enabled'), + '%s %s'.format( + _('Enable Local Peer Discovery.'), + _('This option will be ignored if a private flag is set in a torrent.') + )); + o.enabled = 'true'; + o.disabled = 'false'; + o.default = 'false'; + + o = s.taboption('bt', form.Flag, 'enable_peer_exchange', _('Enable peer exchange'), + '%s %s'.format( + _('Enable Peer Exchange extension.'), + _('This option will be ignored if a private flag is set in a torrent.') + )); + o.enabled = 'true'; + o.disabled = 'false'; + o.default = 'true'; + o.rmempty = false; + + o = s.taboption('bt', form.Flag, 'bt_save_metadata', _('Save metadata'), + _('Save meta data as ".torrent" file. This option has effect only when BitTorrent' + + ' Magnet URI is used. The file name is hex encoded info hash with suffix ".torrent".')); + o.enabled = 'true'; + o.disabled = 'false'; + o.default = 'false'; + + o = s.taboption('bt', form.Flag, 'bt_remove_unselected_file', _('Remove unselected file'), + _('Removes the unselected files when download is completed in BitTorrent. Please' + + ' use this option with care because it will actually remove files from your disk.')); + o.enabled = 'true'; + o.disabled = 'false'; + o.default = 'false'; + + o = s.taboption('bt', form.Flag, 'bt_seed_unverified', _('Seed unverified'), + _('Seed previously downloaded files without verifying piece hashes.')); + o.enabled = 'true'; + o.disabled = 'false'; + o.default = 'false'; + + o = s.taboption('bt', form.Value, 'listen_port', _('BitTorrent listen port'), + _('Set TCP port number for BitTorrent downloads. Accept format: "6881,6885",' + + ' "6881-6999" and "6881-6889,6999". Make sure that the specified ports are open' + + ' for incoming TCP traffic.')); + o.placeholder = '6881-6999'; + + o = s.taboption('bt', form.Value, 'dht_listen_port', _('DHT Listen port'), + _('Set UDP listening port used by DHT (IPv4, IPv6) and UDP tracker. Make sure that the ' + + 'specified ports are open for incoming UDP traffic.')); + o.depends('enable_dht', 'true'); + o.depends('enable_dht6', 'true'); + o.placeholder = '6881-6999'; + + o = s.taboption('bt', form.ListValue, 'follow_torrent', _('Follow torrent')); + o.value('true', _('True')); + o.value('false', _('False')); + o.value('mem', _('Keep in memory')); + + o = s.taboption('bt', form.Value, 'max_overall_upload_limit', _('Max overall upload limit'), + '%s %s'.format( + _('Set max overall upload speed in bytes/sec. 0 means unrestricted.'), + _('You can append K or M.') + )); + o.placeholder = '0'; + + o = s.taboption('bt', form.Value, 'max_upload_limit', _('Max upload limit'), + '%s %s'.format( + _('Set max upload speed per each torrent in bytes/sec. 0 means unrestricted.'), + _('You can append K or M.') + )); + o.placeholder = '0'; + + o = s.taboption('bt', form.Value, 'bt_max_open_files', _('Max open files'), + _('Specify maximum number of files to open in multi-file BitTorrent download globally.')); + o.datatype = 'uinteger'; + o.placeholder = '100'; + + o = s.taboption('bt', form.Value, 'bt_max_peers', _('Max peers'), + _('Specify the maximum number of peers per torrent, 0 means unlimited.')); + o.datatype = 'uinteger'; + o.placeholder = '55'; + + o = s.taboption('bt', form.Value, 'bt_request_peer_speed_limit', _('Request peer speed limit'), + '%s %s'.format( + _('If the whole download speed of every torrent is lower than SPEED, aria2' + + ' temporarily increases the number of peers to try for more download speed.' + + ' Configuring this option with your preferred download speed can increase your' + + ' download speed in some cases.'), + _('You can append K or M.') + )); + o.placeholder = '50K'; + + o = s.taboption('bt', form.Value, 'bt_stop_timeout', _('Stop timeout'), + _('Stop BitTorrent download if download speed is 0 in consecutive N seconds. If 0 is' + + ' given, this feature is disabled.')); + o.datatype = 'uinteger'; + o.placeholder = '0'; + + o = s.taboption('bt', form.Value, 'peer_id_prefix', _('Prefix of peer ID'), + _('Specify the prefix of peer ID. The peer ID in BitTorrent is 20 byte length.' + + ' If more than 20 bytes are specified, only first 20 bytes are used. If less than 20' + + ' bytes are specified, random byte data are added to make its length 20 bytes.')); + o.placeholder = 'A2-%s-'.format( + aria2.version ? aria2.version.replace(/\./g, '-') : '$MAJOR-$MINOR-$PATCH' + ); + + o = s.taboption('bt', form.Value, 'seed_ratio', _('Seed ratio'), + _('Specify share ratio. Seed completed torrents until share ratio reaches RATIO.' + + ' You are strongly encouraged to specify equals or more than 1.0 here. Specify 0.0 if' + + ' you intend to do seeding regardless of share ratio.')); + o.datatype = 'ufloat'; + o.placeholder = '1.0'; + + o = s.taboption('bt', form.Value, 'seed_time', _('Seed time'), + _('Specify seeding time in minutes. If "Seed ratio" option is' + + ' specified along with this option, seeding ends when at least one of the conditions' + + ' is satisfied. Specifying 0 disables seeding after download completed.')); + o.datatype = 'ufloat'; + + o = s.taboption('bt', form.DynamicList, 'bt_tracker', _('Additional BT tracker'), + _('List of additional BitTorrent tracker\'s announce URI.')); + o.placeholder = 'http://tracker.example.com/announce'; + } + + s.tab('advance', _('Advanced Options')); + + o = s.taboption('advance', form.Flag, 'disable_ipv6', _('IPv6 disabled'), + _('Disable IPv6. This is useful if you have to use broken DNS and want to avoid terribly' + + ' slow AAAA record lookup.')); + o.enabled = 'true'; + o.disabled = 'false'; + o.default = 'false'; + + o = s.taboption('advance', form.Value, 'auto_save_interval', _('Auto save interval'), + _('Save a control file (*.aria2) every N seconds. If 0 is given, a control file is not' + + ' saved during download.')); + o.datatype = 'range(0, 600)'; + o.placeholder = '60'; + + o = s.taboption('advance', form.Value, 'save_session_interval', _('Save session interval'), + _('Save error/unfinished downloads to session file every N seconds. If 0 is given, file' + + ' will be saved only when aria2 exits.')); + o.datatype = 'uinteger'; + o.placeholder = '0'; + + o = s.taboption('advance', form.Value, 'disk_cache', _('Disk cache'), + '%s %s'.format( + _('Enable disk cache (in bytes), set 0 to disabled.'), + _('You can append K or M.') + )); + o.placeholder = '16M'; + + o = s.taboption('advance', form.ListValue, 'file_allocation', _('File allocation'), + _('Specify file allocation method. If you are using newer file systems such as ext4' + + ' (with extents support), btrfs, xfs or NTFS (MinGW build only), "falloc" is your best choice.' + + ' It allocates large(few GiB) files almost instantly, but it may not be available if your system' + + ' doesn\'t have posix_fallocate(3) function. Don\'t use "falloc" with legacy file systems such as' + + ' ext3 and FAT32 because it takes almost same time as "prealloc" and it blocks aria2 entirely' + + ' until allocation finishes.')); + o.value('none', _('None')); + o.value('prealloc', _('prealloc')); + o.value('trunc', _('trunc')); + o.value('falloc', _('falloc')); + o.default = 'prealloc'; + + o = s.taboption('advance', form.Flag, 'force_save', _('Force save'), + _('Save download to session file even if the download is completed or removed.' + + ' This option also saves control file in that situations. This may be useful to save' + + ' BitTorrent seeding which is recognized as completed state.')); + o.enabled = 'true'; + o.disabled = 'false'; + o.default = 'false'; + + o = s.taboption('advance', form.Value, 'max_overall_download_limit', _('Max overall download limit'), + '%s %s'.format( + _('Set max overall download speed in bytes/sec. 0 means unrestricted.'), + _('You can append K or M.') + )); + o.placeholder = '0'; + + o = s.taboption('advance', form.Value, 'max_download_limit', _('Max download limit'), + '%s %s'.format( + _('Set max download speed per each download in bytes/sec. 0 means unrestricted.'), + _('You can append K or M.') + )); + o.placeholder = '0'; + + s = m.section(form.NamedSection, 'main', 'aria2', _('Extra Settings'), + _('Settings in this section will be added to config file.')); + s.addremove = false; + s.anonymous = true; + + o = s.option(form.DynamicList, 'extra_settings', _('Settings list'), + _('List of extra settings. Format: option=value, eg. <code>netrc-path=/tmp/.netrc</code>.')); + o.placeholder = 'option=value'; + + return m.render(); + } +}); diff --git a/applications/luci-app-aria2/htdocs/luci-static/resources/view/aria2/files.js b/applications/luci-app-aria2/htdocs/luci-static/resources/view/aria2/files.js new file mode 100644 index 0000000000..c86c794a93 --- /dev/null +++ b/applications/luci-app-aria2/htdocs/luci-static/resources/view/aria2/files.js @@ -0,0 +1,55 @@ +'use strict'; +'require fs'; +'require view'; + +return view.extend({ + load: function() { + var list_files = ['conf', 'session'], + actions = []; + for (var index = 0; index < list_files.length; ++index) { + actions.push( + fs.exec_direct('/usr/libexec/aria2-call', [ 'cat', list_files[index] ]) + .then(function(json) { + var res = {}; + + try { res = JSON.parse(json); } + catch(err) {} + + res.file = res.file || ''; + res.content = 'content' in res ? res.content.trim() : ''; + res.rows = res.content.split('\n', 20).length; + return res; + }) + ); + } + return Promise.all(actions); + }, + + render: function(data) { + var textareaEl = function(id, data, descr) { + return E('div', {'class': 'cbi-section'}, [ + E('div', {'class': 'cbi-section-descr'}, descr.format(data.file)), + E('div', { 'id' : id}, + E('textarea', { + 'id': 'widget.' + id, + 'style': 'width: 100%', + 'readonly': true, + 'wrap': 'off', + 'rows': data.rows >= 20 ? 20 : data.rows + 1 + }, data.content) + ) + ]); + }; + + return E('div', {'class': 'cbi-map'}, [ + E('h2', {'name': 'content'}, '%s - %s'.format(_('Aria2'), _('Files'))), + E('div', {'class': 'cbi-map-descr'}, _('Here shows the files used by aria2.')), + textareaEl('config_area', data[0], _('Content of config file: <code>%s</code>')), + textareaEl('session_area', data[1], _('Content of session file: <code>%s</code>')) + ]); + }, + + handleSave: null, + handleSaveApply: null, + handleReset: null +}); diff --git a/applications/luci-app-aria2/htdocs/luci-static/resources/view/aria2/log.js b/applications/luci-app-aria2/htdocs/luci-static/resources/view/aria2/log.js new file mode 100644 index 0000000000..075b110d59 --- /dev/null +++ b/applications/luci-app-aria2/htdocs/luci-static/resources/view/aria2/log.js @@ -0,0 +1,72 @@ +'use strict'; +'require dom'; +'require fs'; +'require poll'; +'require view'; + +var css = ' \ +#log_textarea { \ + padding: 10px; \ + text-align: left; \ +} \ +#log_textarea pre { \ + padding: .5rem; \ + word-break: break-all; \ + margin: 0; \ +} \ +.description { \ + background-color: #33ccff; \ +}'; + +function pollLog(e) { + return Promise.all([ + fs.exec_direct('/usr/libexec/aria2-call', [ 'tail' ]).then(function(res) { + return res.trim().split(/\n/).reverse().join('\n') + }), + fs.exec_direct('/sbin/logread', [ '-e', 'aria2' ]).then(function(res) { + return res.trim().split(/\n/).reverse().slice(0, 50).join('\n') + }) + ]).then(function(data) { + var t = E('pre', { 'wrap': 'pre' }, [ + E('div', { 'class': 'description' }, _('Last 50 lines of log file:')), + E('br'), + data[0] || _('No log data.'), + E('br'), + E('br'), + E('div', { 'class': 'description' }, _('Last 50 lines of syslog:')), + E('br'), + data[1] || _('No log data.') + ]); + dom.content(e, t); + }); +}; + +return view.extend({ + render: function() { + var log_textarea = E('div', { 'id': 'log_textarea' }, + E('img', { + 'src': L.resource(['icons/loading.gif']), + 'alt': _('Loading'), + 'style': 'vertical-align:middle' + }, _('Collecting data...')) + ); + + poll.add(pollLog.bind(this, log_textarea)); + return E([ + E('style', [ css ]), + E('div', {'class': 'cbi-map'}, [ + E('h2', {'name': 'content'}, '%s - %s'.format(_('Aria2'), _('Log Data'))), + E('div', {'class': 'cbi-section'}, [ + log_textarea, + E('div', {'style': 'text-align:right'}, + E('small', {}, _('Refresh every %s seconds.').format(L.env.pollinterval)) + ) + ]) + ]) + ]); + }, + + handleSave: null, + handleSaveApply: null, + handleReset: null +}); diff --git a/applications/luci-app-aria2/luasrc/controller/aria2.lua b/applications/luci-app-aria2/luasrc/controller/aria2.lua deleted file mode 100644 index 52d7c94ebb..0000000000 --- a/applications/luci-app-aria2/luasrc/controller/aria2.lua +++ /dev/null @@ -1,62 +0,0 @@ --- Copyright 2016-2019 Xingwang Liao <kuoruan@gmail.com> --- Licensed to the public under the MIT License. - -local fs = require "nixio.fs" -local sys = require "luci.sys" -local http = require "luci.http" -local util = require "luci.util" -local uci = require "luci.model.uci".cursor() - -module("luci.controller.aria2", package.seeall) - -function index() - if not nixio.fs.access("/etc/config/aria2") then - return - end - - local e = entry({"admin", "services", "aria2"}, firstchild(), _("Aria2")) - e.dependent = false - e.acl_depends = { "luci-app-aria2" } - - entry({"admin", "services", "aria2", "config"}, - cbi("aria2/config"), _("Configuration"), 1) - - entry({"admin", "services", "aria2", "file"}, - form("aria2/files"), _("Files"), 2) - - entry({"admin", "services", "aria2", "log"}, - firstchild(), _("Log"), 3) - - entry({"admin", "services", "aria2", "log", "view"}, - template("aria2/log_template")) - - entry({"admin", "services", "aria2", "log", "read"}, - call("action_log_read")) - - entry({"admin", "services", "aria2", "status"}, - call("action_status")) - -end - -function action_status() - local status = { - running = (sys.call("pidof aria2c >/dev/null") == 0) - } - - http.prepare_content("application/json") - http.write_json(status) -end - -function action_log_read() - local data = { log = "", syslog = "" } - - local log_file = uci:get("aria2", "main", "log") or "/var/log/aria2.log" - if fs.access(log_file) then - data.log = util.trim(sys.exec("tail -n 50 %s | sed 'x;1!H;$!d;x'" % log_file)) - end - - data.syslog = util.trim(sys.exec("logread | grep aria2 | tail -n 50 | sed 'x;1!H;$!d;x'")) - - http.prepare_content("application/json") - http.write_json(data) -end diff --git a/applications/luci-app-aria2/luasrc/model/cbi/aria2/config.lua b/applications/luci-app-aria2/luasrc/model/cbi/aria2/config.lua deleted file mode 100644 index 3927b1765e..0000000000 --- a/applications/luci-app-aria2/luasrc/model/cbi/aria2/config.lua +++ /dev/null @@ -1,491 +0,0 @@ --- Copyright 2017-2019 Xingwang Liao <kuoruan@gmail.com> --- Licensed to the public under the MIT License. - -local sys = require "luci.sys" -local util = require "luci.util" - -local m, s, o - -local function aria2_info() - if sys.call("command -v aria2c >/dev/null") ~= 0 then - return nil - end - - local info = {} - local line - for line in util.execi("aria2c -v 2>/dev/null | grep -E '^(aria2 version|Enabled Features)'") do - if line:match("^aria2 version") then - local _, _, v = line:find("([%d%.]+)$") - info.version = v - elseif line:match("^Enabled Features") then - info.gzip = line:find("GZip") ~= nil - info.https = line:find("HTTPS") ~= nil - info.bt = line:find("BitTorrent") ~= nil - info.sftp = line:find("SFTP") ~= nil - info.adns = line:find("Async DNS") ~= nil - info.cookie = line:find("Firefox3 Cookie") ~= nil - end - end - - return info -end - -local aria2 = aria2_info() - -m = Map("aria2", "%s - %s" % { translate("Aria2"), translate("Settings") }, -"<p>%s</p><p>%s</p>" % { - translate("Aria2 is a lightweight multi-protocol & multi-source, cross platform download utility."), - translatef("For more information, please visit: %s", - "<a href=\"https://aria2.github.io\" target=\"_blank\">https://aria2.github.io</a>") -}) - -if not aria2 then - m:section(SimpleSection, nil, "<span style=\"color: red;\">%s</span>" % - translate("Error: Can't find aria2c in PATH, please reinstall aria2.")) - m.reset = false - m.submit = false - return m -end - -m:append(Template("aria2/settings_header")) - -s = m:section(NamedSection, "main", "aria2") -s.addremove = false -s.anonymous = true - -s:tab("basic", translate("Basic Options")) - -o = s:taboption("basic", Flag, "enabled", translate("Enabled")) -o.rmempty = false - -o = s:taboption("basic", ListValue, "user", translate("Run daemon as user"), - translate("Leave blank to use default user.")) -o:value("") -local user -for user in util.execi("cat /etc/passwd | cut -d':' -f1") do - o:value(user) -end - -o = s:taboption("basic", Value, "dir", translate("Download directory"), - translate("The directory to store the downloaded file. For example <code>/mnt/sda1</code>.")) -o.rmempty = false - -o = s:taboption("basic", Value, "config_dir", translate("Config file directory"), - translate("The directory to store the config file, session file and DHT file.")) -o.placeholder = "/var/etc/aria2" - -o = s:taboption("basic", Flag, "enable_logging", translate("Enable logging")) -o.rmempty = false - -o = s:taboption("basic", Value, "log", translate("Log file"), - translate("The file name of the log file.")) -o:depends("enable_logging", "1") -o.placeholder = "/var/log/aria2.log" - -o = s:taboption("basic", ListValue, "log_level", translate("Log level")) -o:depends("enable_logging", "1") -o:value("debug", translate("Debug")) -o:value("info", translate("Info")) -o:value("notice", translate("Notice")) -o:value("warn", translate("Warn")) -o:value("error", translate("Error")) -o.default = "warn" - -o = s:taboption("basic", Value, "max_concurrent_downloads", translate("Max concurrent downloads")) -o.placeholder = "5" - -s:tab("rpc", translate("RPC Options")) - -o = s:taboption("rpc", Flag, "pause", translate("Pause"), translate("Pause download after added.")) -o.enabled = "true" -o.disabled = "false" -o.default = "false" - -o = s:taboption("rpc", Flag, "pause_metadata", translate("Pause metadata"), - translate("Pause downloads created as a result of metadata download.")) -o.enabled = "true" -o.disabled = "false" -o.default = "false" - -o = s:taboption("rpc", Value, "rpc_listen_port", translate("RPC port")) -o.datatype = "range(1024,65535)" -o.placeholder = "6800" - -o = s:taboption("rpc", ListValue, "rpc_auth_method", translate("RPC authentication method")) -o:value("none", translate("No Authentication")) -o:value("user_pass", translate("Username & Password")) -o:value("token", translate("Token")) - -o = s:taboption("rpc", Value, "rpc_user", translate("RPC username")) -o:depends("rpc_auth_method", "user_pass") - -o = s:taboption("rpc", Value, "rpc_passwd", translate("RPC password")) -o:depends("rpc_auth_method", "user_pass") -o.password = true - -o = s:taboption("rpc", Value, "rpc_secret", translate("RPC token")) -o:depends("rpc_auth_method", "token") -o.template = "aria2/value_with_btn" -o.btntext = translate("Generate Randomly") -o.btnclick = "randomToken();" - -if aria2.https then - o = s:taboption("rpc", Flag, "rpc_secure", translate("RPC secure"), - translate("RPC transport will be encrypted by SSL/TLS. The RPC clients must use https" - .. " scheme to access the server. For WebSocket client, use wss scheme.")) - o.enabled = "true" - o.disabled = "false" - o.rmempty = false - - o = s:taboption("rpc", Value, "rpc_certificate", translate("RPC certificate"), - translate("Use the certificate in FILE for RPC server. The certificate must be either" - .. " in PKCS12 (.p12, .pfx) or in PEM format.<br/>PKCS12 files must contain the" - .. " certificate, a key and optionally a chain of additional certificates. Only PKCS12" - .. " files with a blank import password can be opened!<br/>When using PEM, you have to" - .. " specify the \"RPC private key\" as well.")) - o:depends("rpc_secure", "true") - o.datatype = "file" - - o = s:taboption("rpc", Value, "rpc_private_key", translate("RPC private key"), - translate("Use the private key in FILE for RPC server. The private key must be" - .. " decrypted and in PEM format.")) - o:depends("rpc_secure", "true") - o.datatype = "file" -end - -o = s:taboption("rpc", Flag, "_use_ws", translate("Use WebSocket")) - -o = s:taboption("rpc", Value, "_rpc_url", translate("Json-RPC URL")) -o.template = "aria2/value_with_btn" -o.onmouseover = "this.focus();this.select();" -o.btntext = translate("Show URL") -o.btnclick = "showRPCURL();" - -s:tab("http", translate("HTTP/FTP/SFTP Options")) - -o = s:taboption("http", Flag, "enable_proxy", translate("Enable proxy")) -o.rmempty = false - -o = s:taboption("http", Value, "all_proxy", translate("All proxy"), - translate("Use a proxy server for all protocols.")) -o:depends("enable_proxy", "1") -o.placeholder = "[http://][USER:PASSWORD@]HOST[:PORT]" - -o = s:taboption("http", Value, "all_proxy_user", translate("Proxy user")) -o:depends("enable_proxy", "1") - -o = s:taboption("http", Value, "all_proxy_passwd", translate("Proxy password")) -o:depends("enable_proxy", "1") -o.password = true - -if aria2.https then - o = s:taboption("http", Flag, "check_certificate", translate("Check certificate"), - translate("Verify the peer using certificates specified in \"CA certificate\" option.")) - o.enabled = "true" - o.disabled = "false" - o.default = "true" - o.rmempty = false - - o = s:taboption("http", Value, "ca_certificate", translate("CA certificate"), - translate("Use the certificate authorities in FILE to verify the peers. The certificate" - .. " file must be in PEM format and can contain multiple CA certificates.")) - o:depends("check_certificate", "true") - o.datatype = "file" - - o = s:taboption("http", Value, "certificate", translate("Certificate"), - translate("Use the client certificate in FILE. The certificate must be either in PKCS12" - .. " (.p12, .pfx) or in PEM format.<br/>PKCS12 files must contain the certificate, a" - .. " key and optionally a chain of additional certificates. Only PKCS12 files with a" - .. " blank import password can be opened!<br/>When using PEM, you have to specify the" - .. " \"Private key\" as well.")) - o.datatype = "file" - - o = s:taboption("http", Value, "private_key", translate("Private key"), - translate("Use the private key in FILE. The private key must be decrypted and in PEM" - .. " format. The behavior when encrypted one is given is undefined.")) - o.datatype = "file" -end - -if aria2.gzip then - o = s:taboption("http", Flag, "http_accept_gzip", translate("HTTP accept gzip"), - translate("Send <code>Accept: deflate, gzip</code> request header and inflate response" - .. " if remote server responds with <code>Content-Encoding: gzip</code> or" - .. " <code>Content-Encoding: deflate</code>.")) - o.enabled = "true" - o.disabled = "false" - o.default = "false" -end - -o = s:taboption("http", Flag, "http_no_cache", translate("HTTP no cache"), - translate("Send <code>Cache-Control: no-cache</code> and <code>Pragma: no-cache</code>" - .. " header to avoid cached content. If disabled, these headers are not sent and you" - .. " can add Cache-Control header with a directive you like using \"Header\" option.")) -o.enabled = "true" -o.disabled = "false" -o.default = "false" - -o = s:taboption("http", DynamicList, "header", translate("Header"), - translate("Append HEADERs to HTTP request header.")) - -o = s:taboption("http", Value, "connect_timeout", translate("Connect timeout"), - translate("Set the connect timeout in seconds to establish connection to HTTP/FTP/proxy server." .. - " After the connection is established, this option makes no effect and \"Timeout\" option is used instead.")) -o.datatype = "uinteger" -o.placeholder = "60" - -o = s:taboption("http", Value, "timeout", translate("Timeout")) -o.datatype = "uinteger" -o.placeholder = "60" - -o = s:taboption("http", Value, "lowest_speed_limit", translate("Lowest speed limit"), - "%s %s" % { - translate("Close connection if download speed is lower than or equal to this value (bytes per sec). " .. - "0 means has no lowest speed limit."), - translate("You can append K or M.") - }) -o.placeholder = "0" - -o = s:taboption("http", Value, "max_connection_per_server", translate("Max connection per server"), - translate("The maximum number of connections to one server for each download.")) -o.datatype = "uinteger" -o.placeholder = "1" - -o = s:taboption("http", Value, "split", translate("Max number of split"), - translate("Download a file using N connections.")) -o.datatype = "uinteger" -o.placeholder = "5" - -o = s:taboption("http", Value, "min_split_size", translate("Min split size"), - translate("Don't split less than 2*SIZE byte range. Possible values: 1M-1024M.")) -o.placeholder = "20M" - -o = s:taboption("http", Value, "max_tries", translate("Max tries")) -o.datatype = "uinteger" -o.placeholder = "5" - -o = s:taboption("http", Value, "retry_wait", translate("Retry wait"), - translate("Set the seconds to wait between retries.")) -o.datatype = "uinteger" -o.placeholder = "0" - -o = s:taboption("http", Value, "user_agent", translate("User agent"), - translate("Set user agent for HTTP(S) downloads.")) -o.placeholder = "aria2/%s" % { aria2.version and aria2.version or "$VERSION" } - -if aria2.bt then - s:tab("bt", translate("BitTorrent Options")) - - o = s:taboption("bt", Flag, "enable_dht", translate("IPv4 <abbr title=\"Distributed Hash Table\">DHT</abbr> enabled"), - "%s %s" % { - translate("Enable IPv4 DHT functionality. It also enables UDP tracker support."), - translate("This option will be ignored if a private flag is set in a torrent.") - }) - o.enabled = "true" - o.disabled = "false" - o.default = "true" - o.rmempty = false - - o = s:taboption("bt", Flag, "enable_dht6", translate("IPv6 <abbr title=\"Distributed Hash Table\">DHT</abbr> enabled"), - "%s %s" % { - translate("Enable IPv6 DHT functionality."), - translate("This option will be ignored if a private flag is set in a torrent.") - }) - o.enabled = "true" - o.disabled = "false" - - o = s:taboption("bt", Flag, "bt_enable_lpd", translate("<abbr title=\"Local Peer Discovery\">LPD</abbr> enabled"), - "%s %s" % { - translate("Enable Local Peer Discovery."), - translate("This option will be ignored if a private flag is set in a torrent.") - }) - o.enabled = "true" - o.disabled = "false" - o.default = "false" - - o = s:taboption("bt", Flag, "enable_peer_exchange", translate("Enable peer exchange"), - "%s %s" % { - translate("Enable Peer Exchange extension."), - translate("This option will be ignored if a private flag is set in a torrent.") - }) - o.enabled = "true" - o.disabled = "false" - o.default = "true" - o.rmempty = false - - o = s:taboption("bt", Flag, "bt_save_metadata", translate("Save metadata"), - translate("Save meta data as \".torrent\" file. This option has effect only when BitTorrent" - .. " Magnet URI is used. The file name is hex encoded info hash with suffix \".torrent\".")) - o.enabled = "true" - o.disabled = "false" - o.default = "false" - - o = s:taboption("bt", Flag, "bt_remove_unselected_file", translate("Remove unselected file"), - translate("Removes the unselected files when download is completed in BitTorrent. Please" - .. " use this option with care because it will actually remove files from your disk.")) - o.enabled = "true" - o.disabled = "false" - o.default = "false" - - o = s:taboption("bt", Flag, "bt_seed_unverified", translate("Seed unverified"), - translate("Seed previously downloaded files without verifying piece hashes.")) - o.enabled = "true" - o.disabled = "false" - o.default = "false" - - o = s:taboption("bt", Value, "listen_port", translate("BitTorrent listen port"), - translate("Set TCP port number for BitTorrent downloads. Accept format: \"6881,6885\"," - .. " \"6881-6999\" and \"6881-6889,6999\". Make sure that the specified ports are open" - .. " for incoming TCP traffic.")) - o.placeholder = "6881-6999" - - o = s:taboption("bt", Value, "dht_listen_port", translate("DHT Listen port"), - translate("Set UDP listening port used by DHT (IPv4, IPv6) and UDP tracker. Make sure that the " - .. "specified ports are open for incoming UDP traffic.")) - o:depends("enable_dht", "true") - o:depends("enable_dht6", "true") - o.placeholder = "6881-6999" - - o = s:taboption("bt", ListValue, "follow_torrent", translate("Follow torrent")) - o:value("true", translate("True")) - o:value("false", translate("False")) - o:value("mem", translate("Keep in memory")) - - o = s:taboption("bt", Value, "max_overall_upload_limit", translate("Max overall upload limit"), - "%s %s" % { - translate("Set max overall upload speed in bytes/sec. 0 means unrestricted."), - translate("You can append K or M.") - }) - o.placeholder = "0" - - o = s:taboption("bt", Value, "max_upload_limit", translate("Max upload limit"), - "%s %s" % { - translate("Set max upload speed per each torrent in bytes/sec. 0 means unrestricted."), - translate("You can append K or M.") - }) - o.placeholder = "0" - - o = s:taboption("bt", Value, "bt_max_open_files", translate("Max open files"), - translate("Specify maximum number of files to open in multi-file BitTorrent download globally.")) - o.datatype = "uinteger" - o.placeholder = "100" - - o = s:taboption("bt", Value, "bt_max_peers", translate("Max peers"), - translate("Specify the maximum number of peers per torrent, 0 means unlimited.")) - o.datatype = "uinteger" - o.placeholder = "55" - - o = s:taboption("bt", Value, "bt_request_peer_speed_limit", translate("Request peer speed limit"), - "%s %s" % { - translate("If the whole download speed of every torrent is lower than SPEED, aria2" - .. " temporarily increases the number of peers to try for more download speed." - .. " Configuring this option with your preferred download speed can increase your" - .. " download speed in some cases."), - translate("You can append K or M.") - }) - o.placeholder = "50K" - - o = s:taboption("bt", Value, "bt_stop_timeout", translate("Stop timeout"), - translate("Stop BitTorrent download if download speed is 0 in consecutive N seconds. If 0 is" - .. " given, this feature is disabled.")) - o.datatype = "uinteger" - o.placeholder = "0" - - o = s:taboption("bt", Value, "peer_id_prefix", translate("Prefix of peer ID"), - translate("Specify the prefix of peer ID. The peer ID in BitTorrent is 20 byte length." - .. " If more than 20 bytes are specified, only first 20 bytes are used. If less than 20" - .. " bytes are specified, random byte data are added to make its length 20 bytes.")) - o.placeholder = "A2-%s-" % { - aria2.version and string.gsub(aria2.version, "%.", "-") or "$MAJOR-$MINOR-$PATCH" - } - - o = s:taboption("bt", Value, "seed_ratio", translate("Seed ratio"), - translate("Specify share ratio. Seed completed torrents until share ratio reaches RATIO." - .. " You are strongly encouraged to specify equals or more than 1.0 here. Specify 0.0 if" - .. " you intend to do seeding regardless of share ratio.")) - o.datatype = "ufloat" - o.placeholder = "1.0" - - o = s:taboption("bt", Value, "seed_time", translate("Seed time"), - translate("Specify seeding time in minutes. If \"Seed ratio\" option is" - .. " specified along with this option, seeding ends when at least one of the conditions" - .. " is satisfied. Specifying 0 disables seeding after download completed.")) - o.datatype = "ufloat" - - o = s:taboption("bt", DynamicList, "bt_tracker", translate("Additional BT tracker"), - translate("List of additional BitTorrent tracker's announce URI.")) - o.placeholder = "http://tracker.example.com/announce" -end - -s:tab("advance", translate("Advanced Options")) - -o = s:taboption("advance", Flag, "disable_ipv6", translate("IPv6 disabled"), - translate("Disable IPv6. This is useful if you have to use broken DNS and want to avoid terribly" - .. " slow AAAA record lookup.")) -o.enabled = "true" -o.disabled = "false" -o.default = "false" - -o = s:taboption("advance", Value, "auto_save_interval", translate("Auto save interval"), - translate("Save a control file (*.aria2) every N seconds. If 0 is given, a control file is not" - .. " saved during download.")) -o.datatype = "range(0, 600)" -o.placeholder = "60" - -o = s:taboption("advance", Value, "save_session_interval", translate("Save session interval"), - translate("Save error/unfinished downloads to session file every N seconds. If 0 is given, file" - .. " will be saved only when aria2 exits.")) -o.datatype = "uinteger" -o.placeholder = "0" - -o = s:taboption("advance", Value, "disk_cache", translate("Disk cache"), - "%s %s" % { - translate("Enable disk cache (in bytes), set 0 to disabled."), - translate("You can append K or M.") - }) -o.placeholder = "16M" - -o = s:taboption("advance", ListValue, "file_allocation", translate("File allocation"), - translate("Specify file allocation method. If you are using newer file systems such as ext4" - .. " (with extents support), btrfs, xfs or NTFS (MinGW build only), \"falloc\" is your best choice." - .. " It allocates large(few GiB) files almost instantly, but it may not be available if your system" - .. " doesn't have posix_fallocate(3) function. Don't use \"falloc\" with legacy file systems such as" - .. " ext3 and FAT32 because it takes almost same time as \"prealloc\" and it blocks aria2 entirely" - .. " until allocation finishes.")) -o:value("none", translate("None")) -o:value("prealloc", translate("prealloc")) -o:value("trunc", translate("trunc")) -o:value("falloc", translate("falloc")) -o.default = "prealloc" - -o = s:taboption("advance", Flag, "force_save", translate("Force save"), - translate("Save download to session file even if the download is completed or removed." - .. " This option also saves control file in that situations. This may be useful to save" - .. " BitTorrent seeding which is recognized as completed state.")) -o.enabled = "true" -o.disabled = "false" -o.default = "false" - -o = s:taboption("advance", Value, "max_overall_download_limit", translate("Max overall download limit"), - "%s %s" % { - translate("Set max overall download speed in bytes/sec. 0 means unrestricted."), - translate("You can append K or M.") - }) -o.placeholder = "0" - -o = s:taboption("advance", Value, "max_download_limit", translate("Max download limit"), - "%s %s" % { - translate("Set max download speed per each download in bytes/sec. 0 means unrestricted."), - translate("You can append K or M.") - }) -o.placeholder = "0" - -s = m:section(NamedSection, "main", "aria2", translate("Extra Settings"), - translate("Settings in this section will be added to config file.")) -s.addremove = false -s.anonymous = true - -o = s:option(DynamicList, "extra_settings", translate("Settings list"), - translate("List of extra settings. Format: option=value, eg. <code>netrc-path=/tmp/.netrc</code>.")) -o.placeholder = "option=value" - -return m diff --git a/applications/luci-app-aria2/luasrc/model/cbi/aria2/files.lua b/applications/luci-app-aria2/luasrc/model/cbi/aria2/files.lua deleted file mode 100644 index 954a4ec5ef..0000000000 --- a/applications/luci-app-aria2/luasrc/model/cbi/aria2/files.lua +++ /dev/null @@ -1,39 +0,0 @@ --- Copyright 2017-2019 Xingwang Liao <kuoruan@gmail.com> --- Licensed to the public under the MIT License. - -local m, s, o - -local fs = require "nixio.fs" -local util = require "luci.util" -local uci = require "luci.model.uci".cursor() - -local config_dir = uci:get("aria2", "main", "config_dir") or "/var/etc/aria2" -local config_file = "%s/aria2.conf.main" % config_dir -local session_file = "%s/aria2.session.main" % config_dir - -m = SimpleForm("aria2", "%s - %s" % { translate("Aria2"), translate("Files") }, - translate("Here shows the files used by aria2.")) -m.reset = false -m.submit = false - -s = m:section(SimpleSection, nil, translatef("Content of config file: <code>%s</code>", config_file)) - -o = s:option(TextValue, "_config") -o.rows = 20 -o.readonly = true -o.cfgvalue = function() - local v = fs.readfile(config_file) or translate("File does not exist.") - return util.trim(v) ~= "" and v or translate("Empty file.") -end - -s = m:section(SimpleSection, nil, translatef("Content of session file: <code>%s</code>", session_file)) - -o = s:option(TextValue, "_session") -o.rows = 20 -o.readonly = true -o.cfgvalue = function() - local v = fs.readfile(session_file) or translate("File does not exist.") - return util.trim(v) ~= "" and v or translate("Empty file.") -end - -return m diff --git a/applications/luci-app-aria2/luasrc/view/aria2/log_template.htm b/applications/luci-app-aria2/luasrc/view/aria2/log_template.htm deleted file mode 100644 index ed91168dbc..0000000000 --- a/applications/luci-app-aria2/luasrc/view/aria2/log_template.htm +++ /dev/null @@ -1,55 +0,0 @@ -<%# - Copyright 2017-2019 Xingwang Liao <kuoruan@gmail.com> - Licensed to the public under the MIT License. --%> - -<% css = [[ - - #log_text { - padding: 10px; - text-align: left; - } - #log_text pre { - word-break: break-all; - margin: 0; - } - .description { - background-color: #33ccff; - } - -]] --%> - -<%+header%> - -<script type="text/javascript" src="<%=resource%>/cbi.js"></script> -<script type="text/javascript">//<![CDATA[ - XHR.poll(10, '<%=url("admin/services/aria2/log/read")%>', null, - function(x, data) { - var logElm = document.getElementById('log_text'); - if (logElm) { - logElm.innerHTML = data - ? String.format( - '<pre>%s%s%s%s</pre>', - '<span class="description"><%:Last 50 lines of log file:%></span><br/><br/>', - data.log || '<%:No log data.%>', - '<br/><br/><span class="description"><%:Last 50 lines of syslog:%></span><br/><br/>', - data.syslog || '<%:No log data.%>' - ) - : '<strong><%:Failed to load log data.%></strong>'; - } - } - ); -//]]></script> -<div class="cbi-map"> - <h2 name="content"><%:Aria2%> - <%:Log Data%></h2> - <fieldset class="cbi-section"> - <div id="log_text"> - <img src="<%=resource%>/icons/loading.gif" alt="<%:Loading%>" style="vertical-align:middle" /> - <%:Collecting data...%> - </div> - <div style="text-align:right"><small><%:Refresh every 10 seconds.%></small></div> - </fieldset> -</div> - -<%+footer%> diff --git a/applications/luci-app-aria2/luasrc/view/aria2/settings_header.htm b/applications/luci-app-aria2/luasrc/view/aria2/settings_header.htm deleted file mode 100644 index 7e71e8e478..0000000000 --- a/applications/luci-app-aria2/luasrc/view/aria2/settings_header.htm +++ /dev/null @@ -1,116 +0,0 @@ -<%# - Copyright 2017-2019 Xingwang Liao <kuoruan@gmail.com> - Licensed to the public under the MIT License. --%> - -<% - local ipkg = require "luci.model.ipkg" - local has_ui = false - - local uilist = { - supported = { - ["ariang"] = "AriaNg", - ["webui-aria2"] = "WebUI-Aria2", - ["yaaw"] = "YAAW" - }, - installed = {} - } - - for k in pairs(uilist.supported) do - if ipkg.installed(k) then - uilist.installed[#uilist.installed + 1] = k - has_ui = true - end - end -%> - -<fieldset class="cbi-section"> - <p id="aria2_status"> - <em><%:Collecting data...%></em> - </p> - <% if has_ui then %> - <p> - <%:Installed web interface: %> - <%- for _, v in pairs(uilist.installed) do %> - <input type="button" class="cbi-button" style="margin: 0 5px;" value="<%=uilist.supported[v]%>" onclick="openWebInterface('<%=v%>');" /> - <%- end %> - </p> - <% end %> -</fieldset> - -<script type="text/javascript">//<![CDATA[ -XHR.poll(5, '<%=url("admin/services/aria2/status")%>', null, - function(x, data) { - var tb = document.getElementById('aria2_status'); - if (data && tb) { - tb.innerHTML = data.running - ? '<%:The Aria2 service is running.%>' - : '<%:The Aria2 service is not running.%>'; - } - } -); - -function randomString(len) { - var randomStr = ''; - var restLen = len; - while ((restLen = len - randomStr.length) > 0) { - randomStr += Math.random().toString(36).substring(2, 2 + restLen); - } - return randomStr; -} - -function randomToken() { - var len = 32; - var inputLength = prompt('<%:Please input token length:%>', len); - if (inputLength === null || inputLength === '') { - return; - } else if (/^\d+$/.test(inputLength)) { - len = parseInt(inputLength); - } - - var secretInput = document.getElementById('cbid.aria2.main.rpc_secret'); - if (secretInput) { - secretInput.value = randomString(len); - } -}; - -function showRPCURL() { - var portElm = document.getElementById('cbid.aria2.main.rpc_listen_port'); - var authMethodElm = document.getElementById('cbid.aria2.main.rpc_auth_method'); - var useWSElm = document.getElementById('cbid.aria2.main._use_ws'); - var secureElm = document.getElementById('cbid.aria2.main.rpc_secure'); - - var port = (portElm && /^\d+$/.test(portElm.value)) ? parseInt(portElm.value) : 6800; - var authMethod = (authMethodElm && authMethodElm.value) ? authMethodElm.value : "none"; - var useWS = (useWSElm && useWSElm.checked) ? true : false; - var secure = (secureElm && secureElm.checked) ? true : false; - - var protocol = useWS - ? (secure ? 'wss' : 'ws') - : (secure ? 'https' : 'http'); - var url = protocol + "://"; - - if (authMethod == 'token') { - var authToken = document.getElementById('cbid.aria2.main.rpc_secret').value; - url += 'token:' + authToken + '@'; - } else if (authMethod == 'user_pass') { - var authUser = document.getElementById('cbid.aria2.main.rpc_user').value; - var authPasswd = document.getElementById('cbid.aria2.main.rpc_passwd').value; - url += authUser + ':' + authPasswd + '@'; - } - url += window.location.hostname + ':' + port + '/jsonrpc'; - var rpcUrlElm = document.getElementById('cbid.aria2.main._rpc_url'); - - if (rpcUrlElm) { - rpcUrlElm.value = url; - } else { - alert(url) - } -}; - -function openWebInterface(path) { - var host = window.location.host; - var protocol = window.location.protocol; - window.open(protocol + '//' + host + '/' + path); -}; -//]]></script> diff --git a/applications/luci-app-aria2/luasrc/view/aria2/value_with_btn.htm b/applications/luci-app-aria2/luasrc/view/aria2/value_with_btn.htm deleted file mode 100644 index 487e107b6d..0000000000 --- a/applications/luci-app-aria2/luasrc/view/aria2/value_with_btn.htm +++ /dev/null @@ -1,22 +0,0 @@ -<%# - Copyright 2017-2019 Xingwang Liao <kuoruan@gmail.com> - Licensed to the public under the MIT License. --%> - -<%+cbi/valueheader%> - <input data-update="change" type="text" class="cbi-input-text"<%= - attr("id", cbid) .. - attr("name", cbid) .. - attr("value", self:cfgvalue(section) or self.default) .. - ifattr(self.size, "size") .. - ifattr(self.placeholder, "placeholder") .. - ifattr(self.maxlength, "maxlength") .. - ifattr(self.datatype, "data-type", self.datatype) .. - ifattr(self.onmouseover, "onmouseover") - %> /> - <%- if self.btntext then -%> - <div class="cbi-button cbi-button-neutral" title="<%=self.btntext%>" aria-label="<%=self.btntext%>"<%= - ifattr(self.btnclick, "onclick", self.btnclick) - %>><span style="font-weight: normal;"><%=self.btntext%></span></div> - <% end %> -<%+cbi/valuefooter%> diff --git a/applications/luci-app-aria2/root/etc/uci-defaults/40_luci-aria2 b/applications/luci-app-aria2/root/etc/uci-defaults/40_luci-aria2 deleted file mode 100755 index becc1c6995..0000000000 --- a/applications/luci-app-aria2/root/etc/uci-defaults/40_luci-aria2 +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/sh - -if [ ."$(uci -q get aria2.main)" != ."aria2" ]; then - uci -q batch <<-EOF >/dev/null - add aria2 aria2 - rename aria2.@aria2[-1]="main" - set aria2.main.enabled=0 - set aria2.main.dir="/var/run/aria2" - set aria2.main.config_dir="/var/etc/aria2" - add_list aria2.main.header="" - add_list aria2.main.bt_tracker="" - add_list aria2.main.extra_settings="" - commit aria2 - EOF -fi - -exit 0 diff --git a/applications/luci-app-aria2/root/usr/libexec/aria2-call b/applications/luci-app-aria2/root/usr/libexec/aria2-call new file mode 100755 index 0000000000..eba7aea5ec --- /dev/null +++ b/applications/luci-app-aria2/root/usr/libexec/aria2-call @@ -0,0 +1,25 @@ +#!/bin/sh + +. "$IPKG_INSTROOT/usr/share/libubox/jshn.sh" + +action=$1 +shift + +case "$action" in + cat) + case "$1" in + conf|session) + config_dir="$(uci -q get aria2.main.config_dir)" + list_file="${config_dir:-/var/etc/aria2}/aria2.$1.main" + json_init + json_add_string file "$list_file" + json_add_string content "$(cat "$list_file")" + json_dump + ;; + esac + ;; + tail) + log_file=$(uci -q get aria2.main.log) + tail -n 50 "${log_file:-/var/log/aria2.log}" + ;; +esac diff --git a/applications/luci-app-aria2/root/usr/share/luci/menu.d/luci-app-aria2.json b/applications/luci-app-aria2/root/usr/share/luci/menu.d/luci-app-aria2.json new file mode 100644 index 0000000000..8728252501 --- /dev/null +++ b/applications/luci-app-aria2/root/usr/share/luci/menu.d/luci-app-aria2.json @@ -0,0 +1,40 @@ +{ + "admin/services/aria2": { + "title": "Aria2", + "order": 30, + "action": { + "type": "firstchild" + }, + "depends": { + "acl": [ "luci-app-aria2" ], + "uci": { "aria2": true } + } + }, + + "admin/services/aria2/config": { + "title": "Configuration", + "order": 10, + "action": { + "type": "view", + "path": "aria2/config" + } + }, + + "admin/services/aria2/files": { + "title": "Files", + "order": 20, + "action": { + "type": "view", + "path": "aria2/files" + } + }, + + "admin/services/aria2/log": { + "title": "Log", + "order": 30, + "action": { + "type": "view", + "path": "aria2/log" + } + } +} diff --git a/applications/luci-app-aria2/root/usr/share/rpcd/acl.d/luci-app-aria2.json b/applications/luci-app-aria2/root/usr/share/rpcd/acl.d/luci-app-aria2.json index 891f97c28f..af5d31e9bd 100644 --- a/applications/luci-app-aria2/root/usr/share/rpcd/acl.d/luci-app-aria2.json +++ b/applications/luci-app-aria2/root/usr/share/rpcd/acl.d/luci-app-aria2.json @@ -2,6 +2,15 @@ "luci-app-aria2": { "description": "Grant UCI access for luci-app-aria2", "read": { + "ubus": { + "service": [ "list" ] + }, + "file": { + "/etc/passwd": [ "read" ], + "/sbin/logread": [ "exec" ], + "/usr/bin/aria2c": [ "exec" ], + "/usr/libexec/aria2-call": [ "exec" ] + }, "uci": [ "aria2" ] }, "write": { |