Files
2025-11-02 14:26:26 +08:00

339 lines
13 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* SPDX-License-Identifier: GPL-3.0-only
*
* Copyright (C) 2022-2023 ImmortalWrt.org
*/
'use strict';
'require form';
'require fs';
'require network';
'require poll';
'require rpc';
'require uci';
'require ui';
'require view';
var callServiceList = rpc.declare({
object: 'service',
method: 'list',
params: ['name'],
expect: { '': {} }
});
function getServiceStatus() {
return L.resolveDefault(callServiceList('unblockneteasemusic'), {}).then(function (res) {
var isRunning = false;
try {
isRunning = res['unblockneteasemusic']['instances']['unblockneteasemusic']['running'];
} catch (e) { }
return isRunning;
});
}
function renderStatus(isRunning) {
var spanTemp = '<em><span style="color:%s"><strong>%s %s</strong></span></em>';
var renderHTML;
if (isRunning) {
renderHTML = spanTemp.format('green', _('UnblockNeteaseMusic'), _('运行中'));
} else {
renderHTML = spanTemp.format('red', _('UnblockNeteaseMusic'), _('未运行'));
}
return renderHTML;
}
function uploadCertificate(type, filename, ev) {
return ui.uploadFile('/usr/share/unblockneteasemusic/' + filename, ev.target)
.then(L.bind(function(btn, res) {
btn.firstChild.data = _('检查 %s 中...').format(type);
if (res.size <= 0) {
ui.addNotification(null, E('p', _('上传的 %s 为空。').format(tyupe)));
return fs.remove('/usr/share/unblockneteasemusic/' + filename);
}
ui.addNotification(null, E('p', _('您的 %s 已成功上传。大小:%sB。').format(type, res.size)));
}, this, ev.target))
.catch(function(e) { ui.addNotification(null, E('p', e.message)) })
.finally(L.bind(function(btn, input) {
btn.firstChild.data = _('上传...');
}, this, ev.target));
}
return view.extend({
load: function() {
return Promise.all([
uci.load('unblockneteasemusic'),
network.getHostHints()
]);
},
render: function(data) {
var m, s, o;
var hosts = data[1]?.hosts;
m = new form.Map('unblockneteasemusic', _('解除网易云音乐播放限制'),
_('原理:采用 [Bilibili/JOOX/酷狗/酷我/咪咕/pyncmd/QQ/Youtube] 等音源,替换网易云音乐 无版权/收费 歌曲链接<br/>' +
'具体使用方法参见:<a href="https://github.com/UnblockNeteaseMusic/luci-app-unblockneteasemusic" target="_blank">GitHub @UnblockNeteaseMusic/luci-app-unblockneteasemusic</a>'));
s = m.section(form.TypedSection);
s.anonymous = true;
s.render = function () {
poll.add(function () {
return L.resolveDefault(getServiceStatus()).then(function (res) {
var view = document.getElementById('service_status');
view.innerHTML = renderStatus(res);
});
});
return E('div', { class: 'cbi-section', id: 'status_bar' }, [
E('p', { id: 'service_status' }, _('收集数据中...'))
]);
}
s = m.section(form.NamedSection, 'config', 'unblockneteasemusic');
o = s.option(form.Flag, 'enable', _('启用服务'));
o.default = o.disabled;
o.rmempty = false;
o = s.option(form.DynamicList, 'music_source', _('音源接口'),
_('留空以使用默认音源。'));
o.value('bilibili', _('Bilibili 音乐'));
o.value('bilivideo', _('Bilibili 音乐 (bilivideo)'));
o.value('bodian', _('波点音乐'));
o.value('joox', _('JOOX 音乐'));
o.value('kugou', _('酷狗音乐'));
o.value('kuwo', _('酷我音乐'));
o.value('migu', _('咪咕音乐'));
o.value('pyncmd', _('网易云音乐pyncmd'));
o.value('qq', _('QQ 音乐'));
o.value('youtube', _('Youtube 音乐'));
o.value('youtubedl', _('Youtube 音乐youtube-dl'));
o.value('ytdlp', _('Youtube 音乐yt-dlp'));
o.renderWidget = function(/* ... */) {
let dl = form.DynamicList.prototype.renderWidget.apply(this, arguments);
dl?.querySelector('.add-item ul > li[data-value="-"]')?.remove();
return dl;
}
o = s.option(form.Value, 'joox_cookie', _('JOOX Cookie'),
_('在 joox.com 获取,需要 wmid 和 session_key 值。'));
o.placeholder = 'wmid=; session_key=';
o.password = true;
o.rmempty = false;
o.depends({'music_source': 'joox', '!contains': true});
o = s.option(form.Value, 'migu_cookie', _('Migu Cookie'),
_('通过抓包手机客户端请求获取,需要 aversionid 值。'));
o.password = true;
o.depends({'music_source': 'migu', '!contains': true});
o = s.option(form.Value, 'qq_cookie', _('QQ Cookie'),
_('在 y.qq.com 获取,需要 uin 和 qm_keyst 值。'));
o.placeholder = 'uin=; qm_keyst=';
o.password = true;
o.rmempty = false;
o.depends({'music_source': 'qq', '!contains': true});
o = s.option(form.Value, 'youtube_key', _('Youtube API Key'),
_('API Key 申请地址https://developers.google.com/youtube/v3/getting-started#before-you-start'));
o.password = true;
o.depends({'music_source': 'youtube', '!contains': true});
o = s.option(form.Flag, 'follow_source_order', _('顺序查询'),
_('默认为并行查询并返回第一个结果,开启后将严格按照配置音源的顺序进行查询。'))
o.default = o.disabled;
o = s.option(form.Flag, 'search_album', _('附加专辑名'),
_('在其他音源搜索歌曲时携带专辑名称(默认搜索条件 <code>歌曲名 - 歌手</code>,启用后搜索条件 <code>歌曲名 - 歌手 专辑名</code>)。'));
o.default = o.disabled;
o = s.option(form.Flag, 'enable_flac', _('启用无损音质'),
_('目前仅支持酷狗、酷我、咪咕、pyncmd、QQ 音源。'));
o.default = o.disabled;
o.depends('music_source', []);
o.depends({'music_source': 'kugou', '!contains': true});
o.depends({'music_source': 'kuwo', '!contains': true});
o.depends({'music_source': 'migu', '!contains': true});
o.depends({'music_source': 'pyncmd', '!contains': true});
o.depends({'music_source': 'qq', '!contains': true});
o = s.option(form.Flag, 'select_max_br', _('选取最高音质'),
_('选择所有音源中的最高码率替换音频。'));
o.default = o.disabled;
o.depends('enable_flac', '1');
o = s.option(form.ListValue, 'replace_music_source', _('音源替换'),
_('当源音乐音质低于指定数值时,尝试强制使用其他平台的高音质版本进行替换。'));
o.value('dont_replace', _('不强制替换音乐音源'));
o.value('lower_than_192kbps', _('当音质低于 192 Kbps时'));
o.value('lower_than_320kbps', _('当音质低于 320 Kbps时'));
o.value('lower_than_999kbps', _('当音质低于 999 Kbps无损时'));
o.value('replace_all', _('替换所有音乐音源'));
o.default = 'dont_replace';
o = s.option(form.Flag, 'disable_upgrade_check', _('禁用更新检查'),
_('拦截网易云音乐客户端更新请求,全平台支持。'));
o.default = o.disabled;
o = s.option(form.Flag, 'block_ads', _('屏蔽广告'),
('启用后,可屏蔽应用内<strong>部分</strong>广告。'));
o.default = o.disabled;
o = s.option(form.ListValue, 'local_vip', _('伪装本地 VIP'),
_('设置后,可以使用去广告、个性换肤、鲸云音效等本地功能。'));
o.value('', _('禁用'));
o.value('cvip', _('CVIP'));
o.value('svip', _('SVIP'));
o = s.option(form.Flag, 'auto_update', _('启用自动更新'),
_('启用后,每天将定时自动检查最新核心版本并更新。'));
o.default = o.disabled;
o = s.option(form.ListValue, 'update_time', '检查更新时间',
_('设定每天自动检查更新时间。'));
for (var i = 0; i < 24; i++)
o.value(i, i + ':00');
o.default = '3';
o.depends('auto_update', '1');
o = s.option(form.Button, '_download_cert', _('CA 根证书'),
_('Linux / iOS / MacOSX 在信任根证书后方可正常使用。'));
o.inputstyle = 'apply';
o.inputtitle = _('下载 ca.crt');
o.onclick = function() {
return fs.read_direct('/usr/share/unblockneteasemusic/core/ca.crt', 'blob').then(function(blob) {
var url = window.URL.createObjectURL(blob);
var link = E('a', { 'style': 'display:none', 'href': url, 'download': 'ca.crt' });
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
window.URL.revokeObjectURL(url);
}).catch(function(err) {
ui.addNotification(null, E('p', [ _('下载文件失败:%s。').format(err.message) ]));
});
}
o = s.option(form.Flag, 'advanced_mode', _('启用进阶设置'),
_('非必要不推荐启用。'));
o.default = o.disabled,
o = s.option(form.ListValue, 'log_level', _('日志等级'));
o.value('debug', _('调试'));
o.value('info', _('信息(默认)'));
o.value('silent', _('静默'));
o.default = 'info';
o.depends('advanced_mode', '1');
o = s.option(form.Value, 'http_port', _('HTTP 监听端口'));
o.datatype = 'port';
o.default = '5200';
o.rmempty = false;
o.depends('hijack_ways', 'dont_hijack');
o.depends('hijack_ways', 'use_ipset');
o = s.option(form.Value, 'https_port', _('HTTPS 监听端口'));
o.datatype = 'port';
o.default = '5201';
o.rmempty = false;
o.depends('hijack_ways', 'dont_hijack');
o.depends('hijack_ways', 'use_ipset');
o = s.option(form.Value, 'endpoint_url', _('EndPoint'),
_('音源地址反代(包装)。'));
o.default = 'https://music.163.com';
o.rmempty = false;
o.depends('advanced_mode', '1');
o = s.option(form.Value, 'cnrelay', _('UNM Bridge 服务器'),
_('使用 UnblockNeteaseMusic 中继桥bridge以获取音源信息。'));
o.placeholder = 'http(s)://host:port'
o.depends('advanced_mode', '1');
o = s.option(form.ListValue, 'hijack_ways', _('劫持方法'),
_('如果使用 Hosts 劫持,监听端口将固定为 80/443请注意更改您的 webUI 端口。'));
o.value('dont_hijack', _('不开启劫持'));
o.value('use_ipset', _('使用 IPSet 劫持'));
o.value('use_hosts', _('使用 Hosts 劫持'));
o.default = 'dont_hijack';
o.rmempty = false;
o.depends('advanced_mode', '1');
o = s.option(form.Flag, 'pub_access', _('部署到公网'),
_('默认仅放行局域网请求,如需提供公开访问请勾选此选项。'));
o.default = o.disabled;
o.depends('advanced_mode', '1');
o = s.option(form.Flag, 'strict_mode', _('启用严格模式'),
_('若将服务部署到公网,则强烈建议使用严格模式,此模式下仅放行网易云音乐所属域名的请求。<br/>注意:该模式下不能使用全局代理。'));
o.default = o.disabled;
o.depends('advanced_mode', '1');
o = s.option(form.Value, 'netease_server_ip', _('网易云服务器 IP'));
o.datatype = 'ipaddr';
o.depends('advanced_mode', '1');
o = s.option(form.Value, 'proxy_server_ip', _('代理服务器地址'),
_('使用代理服务器获取音乐信息。'));
o.placeholder = 'http(s)://host:port';
o.depends('advanced_mode', '1');
o = s.option(form.Value, 'self_issue_cert_crt', _('自签发证书公钥位置'));
o.value('/usr/share/unblockneteasemusic/core/server.crt', _('内置公钥'));
o.value('/usr/share/unblockneteasemusic/server.crt');
o.default = '/usr/share/unblockneteasemusic/core/server.crt';
o.datatype = 'file';
o.depends('advanced_mode', '1');
o = s.option(form.Button, '_upload_cert', _('上传公钥'));
o.inputstyle = 'action';
o.inputtitle = _('上传...');
o.depends('self_issue_cert_crt', '/usr/share/unblockneteasemusic/server.crt');
o.onclick = L.bind(uploadCertificate, this, _('公钥'), 'server.crt');
o.modalonly = true;
o = s.option(form.Value, 'self_issue_cert_key', _('自签发证书私钥位置'));
o.value('/usr/share/unblockneteasemusic/core/server.key', _('内置私钥'));
o.value('/usr/share/unblockneteasemusic/server.key');
o.default = '/usr/share/unblockneteasemusic/core/server.key';
o.datatype = 'file'
o.depends('advanced_mode', '1');
o = s.option(form.Button, '_upload_key', _('上传私钥'));
o.inputstyle = 'action';
o.inputtitle = _('上传...');
o.depends('self_issue_cert_key', '/usr/share/unblockneteasemusic/server.key');
o.onclick = L.bind(uploadCertificate, this, _('私钥'), 'server.key');
o.modalonly = true;
s = m.section(form.TableSection, 'acl_rule', _('例外客户端规则'),
_('可以为局域网客户端分别设置不同的例外模式。'));
s.addremove = true;
s.anonymous = true;
s.sortable = true;
o = s.option(form.Flag, 'enable', _('启用'));
o.default = o.enabled;
o.rmempty = false;
o = s.option(form.Value, 'mac_addr', _('MAC 地址'));
o.datatype = 'macaddr';
Object.keys(hosts).forEach(function(mac) {
var hint = hosts[mac].name || L.toArray(hosts[mac].ipaddrs || hosts[mac].ipv4)[0];
o.value(mac, hint ? '%s (%s)'.format(mac, hint) : mac);
});
o.rmempty = false;
o = s.option(form.ListValue, 'filter_mode', _('规则'));
o.value('disable_all', _('不代理 HTTP 和 HTTPS'));
o.value('disable_http', _('不代理 HTTP'));
o.value('disable_https', _('不代理 HTTPS'));
o.default = 'disable_all';
o.rmempty = false;
return m.render();
}
});