update 2023-01-02 20:15:39

This commit is contained in:
github-actions[bot]
2023-01-02 20:15:39 +08:00
parent f289f3e477
commit 1c27851c7d
36 changed files with 868 additions and 927 deletions

View File

@ -16,7 +16,7 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=luci-app-amlogic
PKG_VERSION:=3.1.153
PKG_VERSION:=3.1.154
PKG_RELEASE:=1
PKG_LICENSE:=GPL-2.0 License

View File

@ -130,7 +130,7 @@ check_kernel() {
# check if kernel is locked
if [ -n "${LOCK_KERNEL}" ]; then
if [ "${LOCK_KERNEL}" != "${kernel_name}" ]; then
if ! echo "${kernel_name}" | grep -E '^5.10.\d+-.*?rk3588.*?$' >/dev/null; then
if ! echo "${kernel_name}" | grep -E '^5.10.\d+-.*?rk35.*?$' >/dev/null; then
error_msg "The kernel version is locked to [ ${LOCK_KERNEL} ], but your kernel is [ ${kernel_name} ]. "
fi
fi

View File

@ -1,19 +1,19 @@
# SPDX-License-Identifier: GPL-3.0-only
#
# Copyright (C) 2021-2022 sirpdboy <herboy2008@gmail.com> https://github.com/sirpdboy/luci-app-lucky
#
# This is free software, licensed under the Apache License, Version 2.0 .
#
include $(TOPDIR)/rules.mk
PKG_NAME:=luci-app-lucky
PKG_VERSION:=1.0.4
PKG_RELEASE:=4
LUCI_TITLE:=LuCI Support for Dynamic lucky Client
LUCI_DEPENDS:=+lucky
LUCI_PKGARCH:=all
include $(TOPDIR)/feeds/luci/luci.mk
# call BuildPackage - OpenWrt buildroot signature
# SPDX-License-Identifier: GPL-3.0-only
#
# Copyright (C) 2021-2022 sirpdboy <herboy2008@gmail.com> https://github.com/sirpdboy/luci-app-lucky
#
# This is free software, licensed under the Apache License, Version 2.0 .
#
include $(TOPDIR)/rules.mk
PKG_NAME:=luci-app-lucky
PKG_VERSION:=1.0.4
PKG_RELEASE:=5
LUCI_TITLE:=LuCI Support for Dynamic lucky Client
LUCI_DEPENDS:=+lucky
LUCI_PKGARCH:=all
include $(TOPDIR)/feeds/luci/luci.mk
# call BuildPackage - OpenWrt buildroot signature

View File

@ -1,179 +0,0 @@
### 访问数:[![](https://visitor-badge.glitch.me/badge?page_id=sirpdboy-visitor-badge)] [![](https://img.shields.io/badge/TG群-点击加入-FFFFFF.svg)](https://t.me/joinchat/AAAAAEpRF88NfOK5vBXGBQ)
欢迎来到sirpdboy的源码仓库
=
# Lucky(大吉)
luci-app-lucky 动态域名ddns-go服务,替代socat主要用于公网IPv6 tcp/udp转内网ipv4,http/https反向代理
[![若部分图片无法正常显示,请挂上机场浏览或点这里到末尾看修复教程](https://visitor-badge.glitch.me/badge?page_id=sirpdboy-visitor-badge)](#解决-github-网页上图片显示失败的问题) [![](https://img.shields.io/badge/TG群-点击加入-FFFFFF.svg)](https://t.me/joinchat/AAAAAEpRF88NfOK5vBXGBQ)
[luci-app-lucky Lucky(大吉)](https://github.com/sirpdboy/luci-app-lucky)
======================
**认真阅读完毕** 本页面,本页面包含注意事项和如何使用。
## 功能说明:
### Lucky(大吉)
#### 动态域名ddns-go服务,替代socat主要用于公网IPv6 tcp/udp转内网ipv4,http/https反向代理
#### 在LUCI中可以配置访问端口和增加是否允许外网访问设置。
<!-- TOC -->
- [lucky](#lucky)
- [特性](#特性)
- [使用方法](#使用方法)
- [说明](#说明)
- [问题](#常见问题)
- [界面](#界面)
- [捐助](#捐助)
<!-- /TOC -->
## 特性
- 目前已经实现的功能有
- 1.替代socat,主要用于公网IPv6 tcp/udp转 内网ipv4
- 支持界面化(web后台)管理转发规则,单条转发规则支持设置多个转发端口,一键开关指定转发规则
- 单条规则支持黑白名单安全模式切换,白名单模式可以让没有安全验证的内网服务端口稍微安全一丢丢暴露到公网
- Web后台支持查看最新100条日志
- 另有精简版不带后台,支持命令行快捷设置转发规则,有利于空间有限的嵌入式设备运行.(不再提供编译版本,如有需求可以自己编译)
- 2.动态域名服务
- 参考和部分代码来自 https://github.com/jeessy2/ddns-go
- 在ddns-go的基础上主要改进/增加的功能有
- 1.同时支持接入多个不同的DNS服务商
- 2.支持http/https/socks5代理设置
- 3.自定义(Callback)和Webhook支持自定义headers
- 4.支持BasicAuth
- 5.DDNS任务列表即可了解全部信息(包含错误信息),无需单独查看日志.
- 6.调用DNS服务商接口更新域名信息前可以先通过DNS解析域名比较IP,减少对服务商接口调用.
- 其它细节功能自己慢慢发现...
- 没有文档,后台各处的提示信息已经足够多.
- 支持的DNS服务商和DDNS-GO一样,有Alidns(阿里云),百度云,Cloudflare,Dnspod(腾讯云),华为云.自定义(Callback)内置有每步,No-IP,Dynv6,Dynu模版,一键填充,仅需修改相应用户密码或者token即可快速接入.
- 3.http/https反向代理
- 特点
- 设置简单
- 支持HttpBasic认证
- 支持IP黑白名单
- 支持UserAgent黑白名单
- 日志记录最近访问情况
- 一键开关子规则
- 前端域名与后端地址 支持一对一,一对多(均衡负载),多对多(下一级反向代理)
- 4.网络唤醒
- 特点
- 支持远程控制唤醒和关机操作
- 远程唤醒需要 待唤醒端所在局域网内有开启中继唤醒指令的lucky唤醒客户端
- 远程关机需要 待关机端运行有luck唤醒客户端
- 支持接入第三方物联网平台(点灯科技 巴法云),可通过各大平台的语音助手控制设备唤醒和关机.
- 点灯科技支持 小爱同学 小度 天猫精灵
- 巴法云支持小爱同学 小度 天猫精灵 google语音 AmazonAlexa
- 具备但一般用不上的功能:支持一个设备设置多组网卡mac和多个广播地址,实现批量控制设备.
## 使用方法
- 将luci-app-lucky添加至 LEDE/OpenWRT 源码的方法。
### 下载源码方法一:
- 编辑源码文件夹根目录feeds.conf.default并加入如下内容:
```Brach
# feeds获取源码
src-git luci-app-lucky https://github.com/sirpdboy/luci-app-lucky
```
```Brach
# 更新feeds并安装主题
scripts/feeds update luci-app-lucky
scripts/feeds install luci-app-lucky
```
### 下载源码方法二:
```Brach
# 下载源码
git clone https://github.com/sirpdboy/luci-app-lucky.git package/lucky
make menuconfig
```
### 配置菜单
```Brach
make menuconfig
# 找到 LuCI -> Applications, 选择 luci-app-lucky, 保存后退出。
```
### 编译
```Brach
# 编译固件
make package/lucky/luci-app-lucky/compile V=s
```
## 说明
- 源码来源https://github.com/gdy666/lucky
- 源码来源https://github.com/sirpdboy/luci-app-lucky
- 你可以随意使用其中的源码,但请注明出处。
## 常见问题
- 不同于防火墙端口转发规则,不要设置没有用上的端口,会增加内存的使用.
- 小米路由 ipv4 类型的80和443端口被占用,但只设置监听tcp6(ipv6)的80/443端口转发规则完全没问题.
- 如果需要使用白名单模式,请根据自身需求打开外网访问后台管理页面开关.
- 转发规则启用异常,端口转发没有生效时请登录后台查看日志.
- 开启外网访问可以直接修改配置文件中的"AllowInternetaccess": false, 将false改为true
## 界面
![screenshots](https://raw.githubusercontent.com/sirpdboy/openwrt/master/doc/lucky1.jpg)
![screenshots](https://raw.githubusercontent.com/sirpdboy/openwrt/master/doc/lucky2.jpg)
![screenshots](https://raw.githubusercontent.com/sirpdboy/openwrt/master/doc/lucky3.jpg)
# My other project
网络速度测试 https://github.com/sirpdboy/NetSpeedTest
定时设置插件 : https://github.com/sirpdboy/luci-app-autotimeset
关机功能插件 : https://github.com/sirpdboy/luci-app-poweroffdevice
opentopd主题 : https://github.com/sirpdboy/luci-theme-opentopd
opentoks 主题: https://github.com/sirpdboy/luci-theme-opentoks [仿KOOLSAHRE主题]
btmob 主题: https://github.com/sirpdboy/luci-theme-btmob
系统高级设置 : https://github.com/sirpdboy/luci-app-advanced
DDNS-GO动态域名: https://github.com/sirpdboy/luci-app-DDNS-GO
Lucky(大吉): https://github.com/sirpdboy/luci-app-lucky
## 捐助
-如果你觉得此项目对你有帮助,请捐助我们,以使项目能持续发展,更加完善。··请作者喝杯咖啡~~~**
-你们的支持就是我的动力!**
| <img src="https://img.shields.io/badge/-支付宝-F5F5F5.svg" href="#赞助支持本项目-" height="25" alt="图飞了😂"/> | <img src="https://img.shields.io/badge/-微信-F5F5F5.svg" height="25" alt="图飞了😂" href="#赞助支持本项目-"/> |
| :-----------------: | :-------------: |
|![xm1](https://raw.githubusercontent.com/sirpdboy/openwrt/master/doc/支付宝.png) | ![xm1](https://raw.githubusercontent.com/sirpdboy/openwrt/master/doc/微信.png) |
<a href="#readme">
<img src="https://img.shields.io/badge/-返回顶部-orange.svg" alt="图飞了😂" title="返回顶部" align="right"/>
</a>

View File

@ -4,9 +4,6 @@
module("luci.controller.lucky", package.seeall)
function index()
if not nixio.fs.access("/etc/config/lucky") then
return
end
entry({"admin", "services", "lucky"}, alias("admin", "services", "lucky", "setting"),_("Lucky"), 57).dependent = true
entry({"admin", "services", "lucky", "setting"}, cbi("lucky"), _("Base Setting"), 20).leaf=true

View File

@ -1,11 +1,11 @@
<%#
Copyright 2021-2022 sirpdboy Wich <sirpdboy@qq.com>
Copyright 2021-2022 sirpdboy Wich <herboy2008@gmail.com>
https://github.com/sirpdboy/luci-app-lucky
Licensed to the public under the Apache License 2.0.
-%>
<%
local safeURL = luci.sys.exec("cat /etc/lucky/lucky.conf | grep SafeURL | sed 's/\"//g' | sed 's/: /\\n/g'|sed '1d' ");
local safeURL = luci.sys.exec("cat /etc/lucky/lucky.conf | grep SafeURL | sed -e 's/,//g' -e 's/\"//g' -e 's/\ //g' | awk -F ':' '{print $2}' ")
local luckyHttpPort = luci.model.uci.cursor():get("lucky", "lucky", "port");
-%>

View File

@ -1,8 +1,4 @@
<%#
Copyright 2021-2022 sirpdboy Wich <herboy2008@gmail.com>
https://github.com/sirpdboy/luci-app-lucky
Licensed to the public under the Apache License 2.0.
-%>
<script type="text/javascript">//<![CDATA[

View File

@ -1,56 +1,56 @@
msgid ""
msgstr "Content-Type: text/plain; charset=UTF-8"
msgid "Lucky"
msgstr "Lucky大吉"
msgid "Main functions of Lucky: dynamic domain name ddns-go service, which replaces socat. It is mainly used for public IPv6 tcp/udp to intranet ipv4, http/https reverse proxy frp"
msgstr "大吉主要功能动态域名ddns-go服务,替代socat主要用于公网IPv6 tcp/udp转内网ipv4,http/https反向代理网络唤醒(可通过第三方物联网平台接入各大语音助手实现语音控制开关电脑)"
msgid "Running state"
msgstr "运行状态"
msgid "The Lucky service is running."
msgstr "大吉服务已启动"
msgid "The Lucky service is not running."
msgstr "大吉服务未启动"
msgid "Lucky Status"
msgstr "大吉服务状态"
msgid "Collecting data..."
msgstr "收集数据..."
msgid "Set the Lucky access port"
msgstr "设置访问端口"
msgid "Lucky configuration file"
msgstr "Lucky 配置文件"
msgid "</br>For specific usage, see:"
msgstr "</br>具体使用方法参见:"
msgid "Base Setting"
msgstr "基本设置"
msgid "Enable Internet access"
msgstr "开启外网访问"
msgid "Click the new page to open Lucky"
msgstr "点击新页面打开大吉"
msgid "View Password"
msgstr "查看密码"
msgid "Username:"
msgstr "用户名:"
msgid "Password:"
msgstr "密码:"
msgid "Reading..."
msgstr "读取中..."
msgid "Read"
msgid ""
msgstr "Content-Type: text/plain; charset=UTF-8"
msgid "Lucky"
msgstr "Lucky大吉"
msgid "Main functions of Lucky: dynamic domain name ddns-go service, which replaces socat. It is mainly used for public IPv6 tcp/udp to intranet ipv4, http/https reverse proxy frp"
msgstr "大吉主要功能动态域名ddns-go服务,替代socat主要用于公网IPv6 tcp/udp转内网ipv4,http/https反向代理网络唤醒(可通过第三方物联网平台接入各大语音助手实现语音控制开关电脑)"
msgid "Running state"
msgstr "运行状态"
msgid "The Lucky service is running."
msgstr "大吉服务已启动"
msgid "The Lucky service is not running."
msgstr "大吉服务未启动"
msgid "Lucky Status"
msgstr "大吉服务状态"
msgid "Collecting data..."
msgstr "收集数据..."
msgid "Set the Lucky access port"
msgstr "设置访问端口"
msgid "Lucky configuration file"
msgstr "Lucky 配置文件"
msgid "</br>For specific usage, see:"
msgstr "</br>具体使用方法参见:"
msgid "Base Setting"
msgstr "基本设置"
msgid "Enable Internet access"
msgstr "开启外网访问"
msgid "Click the new page to open Lucky"
msgstr "点击新页面打开大吉"
msgid "View Password"
msgstr "查看密码"
msgid "Username:"
msgstr "用户名:"
msgid "Password:"
msgstr "密码:"
msgid "Reading..."
msgstr "读取中..."
msgid "Read"
msgstr "读取"

View File

@ -1,5 +1,5 @@
config lucky 'lucky'
option enabled '0'
option logger '1'
option port '16601'
option AllowInternetaccess '0'
config lucky 'lucky'
option enabled '0'
option logger '1'
option port '16601'
option AllowInternetaccess '0'

View File

@ -0,0 +1,58 @@
#!/bin/sh /etc/rc.common
#
# Copyright (C) 2021-2022 sirpdboy <herboy2008@gmail.com> https://github.com/sirpdboy/luci-app-lucky
# This file is part of lucky .
#
# This is free software, licensed under the Apache License, Version 2.0 .
USE_PROCD=1
START=99
STOP=10
CONF="lucky"
PROG=/usr/bin/lucky
LUCKYDIR=/etc/lucky
LUCKYCONF=/etc/lucky/lucky.conf
get_config() {
config_get_bool enabled $1 enabled 1
config_get_bool logger $1 logger 1
config_get port $1 port 16601
config_get AllowInternetaccess $1 AllowInternetaccess 0
}
lucky_prepare() {
pgrep -f $PROG | xargs kill -9 >/dev/null 2>&1
}
start_service() {
stop_service
config_load "$CONF"
config_foreach get_config "$CONF"
[ "x$enabled" = "x1" ] || return 1
if [ "x$AllowInternetaccess" = "x1" ] ;then
sed -i 's/"AllowInternetaccess": false,/"AllowInternetaccess": true,/g' "$LUCKYCONF"
else
sed -i 's/"AllowInternetaccess": true,/"AllowInternetaccess": false,/g' "$LUCKYCONF"
fi
# set_firewall
procd_open_instance
procd_set_param command "$PROG"
procd_append_param command -c "$LUCKYCONF" -p $port
[ "x$logger" == x1 ] && procd_set_param stderr 1
procd_set_param respawn
procd_close_instance
}
stop_service() {
lucky_prepare
}
service_triggers() {
procd_add_reload_trigger "$CONF"
}

View File

@ -1,11 +1,11 @@
{
"luci-app-lucky": {
"description": "Grant UCI access for luci-app-lucky",
"read": {
"uci": [ "lucky" ]
},
"write": {
"uci": [ "lucky" ]
}
}
}
{
"luci-app-lucky": {
"description": "Grant UCI access for luci-app-lucky",
"read": {
"uci": [ "lucky" ]
},
"write": {
"uci": [ "lucky" ]
}
}
}

View File

@ -1,90 +1,70 @@
# SPDX-License-Identifier: GPL-3.0-only
#
# Copyright (C) 2021-2022 sirpdboy <herboy2008@gmail.com>
#
# This is free software, licensed under the Apache License, Version 2.0 .
#
include $(TOPDIR)/rules.mk
PKG_NAME:=lucky
PKG_VERSION:=1.7.5
PKG_RELEASE:=1
PKGARCH:=all
ifeq ($(ARCH),mipsel)
LUCKY_ARCH:=mipsle_softfloat
endif
ifeq ($(ARCH),mips)
LUCKY_ARCH:=mips_softfloat
endif
ifeq ($(ARCH),i386)
LUCKY_ARCH:=i386
endif
ifeq ($(ARCH),x86_64)
LUCKY_ARCH:=x86_64
endif
ifeq ($(ARCH),arm)
LUCKY_ARCH:=armv7
endif
ifeq ($(BOARD),bcm53xx)
LUCKY_ARCH:=armv6
ifeq ($(word 2,$(subst +,$(space),$(call qstrip,$(CONFIG_CPU_TYPE)))),)
LUCKY_ARCH:=armv5
endif
endif
ifeq ($(BOARD),kirkwood)
LUCKY_ARCH:=armv5
endif
ifeq ($(ARCH),aarch64)
LUCKY_ARCH:=arm64
endif
PKG_LICENSE:=GPL-3.0-only
PKG_LICENSE_FILES:=LICENSE
PKG_MAINTAINER:=GDY666 <gdy666@foxmail.com>
PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)-$(PKG_VERSION)
PKG_HASH:=skip
include $(INCLUDE_DIR)/package.mk
define Package/$(PKG_NAME)
SECTION:=net
CATEGORY:=Network
TITLE:=Lucky gdy
DEPENDS:=@(i386||x86_64||arm||aarch64||mipsel||mips)
URL:=https://github.com/gdy666/lucky
endef
define Package/$(PKG_NAME)/description
Main functions of Lucky: ipv4/ipv6 portforward,ddns,IOT wake on lan ,reverse proxy and more...
endef
define Package/$(PKG_NAME)/postinst
#!/bin/sh
if [ -z "$${IPKG_INSTROOT}" ]; then
[ -f /etc/uci-defaults/luci-lucky ] && /etc/uci-defaults/luci-lucky && rm -f /etc/uci-defaults/luci-lucky
fi
endef
define Build/Prepare
[ ! -f $(PKG_BUILD_DIR)/$(PKG_NAME)_$(PKG_VERSION)_Linux_$(LUCKY_ARCH).tar.gz ] && wget https://github.com/gdy666/lucky/releases/download/v$(PKG_VERSION)/$(PKG_NAME)_$(PKG_VERSION)_Linux_$(LUCKY_ARCH).tar.gz -O $(PKG_BUILD_DIR)/$(PKG_NAME)_$(PKG_VERSION)_Linux_$(LUCKY_ARCH).tar.gz
tar -xzvf $(PKG_BUILD_DIR)/$(PKG_NAME)_$(PKG_VERSION)_Linux_$(LUCKY_ARCH).tar.gz -C $(PKG_BUILD_DIR)
endef
define Build/Configure
endef
define Build/Compile
endef
define Package/$(PKG_NAME)/install
$(INSTALL_DIR) $(1)/usr/bin $(1)/etc/init.d $(1)/etc/uci-defaults $(1)/etc/lucky/
$(INSTALL_BIN) $(PKG_BUILD_DIR)/lucky $(1)/usr/bin/lucky
$(INSTALL_BIN) ./files/lucky.init $(1)/etc/init.d/lucky
$(INSTALL_BIN) ./files/luci-lucky.uci-default $(1)/etc/uci-defaults/luci-lucky
$(INSTALL_DATA) ./files/default.conf $(1)/etc/lucky/lucky.conf
endef
$(eval $(call BuildPackage,$(PKG_NAME)))
# SPDX-License-Identifier: GPL-3.0-only
#
# Copyright (C) 2021-2022 sirpdboy <herboy2008@gmail.com>
#
# This is free software, licensed under the Apache License, Version 2.0 .
#
include $(TOPDIR)/rules.mk
PKG_NAME:=lucky
PKG_VERSION:=1.7.5
PKG_RELEASE:=1
PKGARCH:=all
ifeq ($(ARCH),mipsel)
LUCKY_ARCH:=mipsle_softfloat
endif
ifeq ($(ARCH),mips)
LUCKY_ARCH:=mips_softfloat
endif
ifeq ($(ARCH),i386)
LUCKY_ARCH:=i386
endif
ifeq ($(ARCH),x86_64)
LUCKY_ARCH:=x86_64
endif
ifeq ($(ARCH),arm)
LUCKY_ARCH:=armv7
endif
ifeq ($(BOARD),bcm53xx)
LUCKY_ARCH:=armv6
ifeq ($(word 2,$(subst +,$(space),$(call qstrip,$(CONFIG_CPU_TYPE)))),)
LUCKY_ARCH:=armv5
endif
endif
ifeq ($(BOARD),kirkwood)
LUCKY_ARCH:=armv5
endif
ifeq ($(ARCH),aarch64)
LUCKY_ARCH:=arm64
endif
PKG_LICENSE:=GPL-3.0-only
PKG_LICENSE_FILES:=LICENSE
PKG_MAINTAINER:=GDY666 <gdy666@foxmail.com>
PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)-$(PKG_VERSION)
PKG_HASH:=skip
include $(INCLUDE_DIR)/package.mk
define Package/$(PKG_NAME)
SECTION:=net
CATEGORY:=Network
TITLE:=Lucky gdy
DEPENDS:=@(i386||x86_64||arm||aarch64||mipsel||mips)
URL:=https://github.com/gdy666/lucky
endef
define Package/$(PKG_NAME)/description
Main functions of Lucky: ipv4/ipv6 portforward,ddns,IOT wake on lan ,reverse proxy and more...
endef
define Package/$(PKG_NAME)/install
$(INSTALL_DIR) $(1)/usr/bin
$(INSTALL_BIN) $(PKG_BUILD_DIR)/lucky $(1)/usr/bin/lucky
endef
$(eval $(call BuildPackage,$(PKG_NAME)))

View File

@ -1,86 +0,0 @@
#!/bin/sh /etc/rc.common
#
# Copyright (C) 2021-2022 sirpdboy <herboy2008@gmail.com> https://github.com/sirpdboy/luci-app-lucky
#
# This file is part of lucky .
#
# This is free software, licensed under the Apache License, Version 2.0 .
#
START=99
STOP=15
USE_PROCD=1
CONF=lucky
PROG=/usr/bin/lucky
LUCKYDIR=/etc/lucky
LUCKYCONF=$LUCKYDIR/lucky.conf
get_config() {
config_get_bool enabled $1 enabled 1
config_get_bool logger $1 logger 1
config_get port $1 port 16601
config_get AllowInternetaccess $1 AllowInternetaccess 0
}
set_firewall() {
if [ "x$AllowInternetaccess" = "x1" ]; then
uci -q delete firewall.lucky
uci set firewall.lucky=rule
uci set firewall.lucky.name="lucky"
uci set firewall.lucky.target="ACCEPT"
uci set firewall.lucky.src="wan"
uci set firewall.lucky.proto='tcp udp'
uci set firewall.lucky.dest_port="$port"
uci set firewall.lucky.enabled="1"
uci commit firewall
/etc/init.d/firewall reload >/dev/null 2>&1
else
uci -q delete firewall.lucky
uci commit firewall
/etc/init.d/firewall reload >/dev/null 2>&1
fi
}
start_service() {
stop_service
config_load "$CONF"
config_foreach get_config "$CONF"
[ -s ${LUCKYCONF} ] || {
[ -d "$LUCKYDIR" ] || mkdir -p "$LUCKYDIR" >/dev/null 2>&1
cat > $LUCKYCONF <<EOF
{"BaseConfigure": {"AdminWebListenPort": $port,"AdminWebListenTLS": false,"AdminWebListenHttpsPort": 16626,"AdminAccount": "666","AdminPassword": "666","AllowInternetaccess": false,"LogMaxSize": 2048,"HttpClientTimeout": 20,"InsecureSkipVerify": false,"DisableModules": null,"SafeURL": ""},"WhiteListConfigure": {"BaseConfigure": {"URL": "","ActivelifeDuration": 36,"BasicAccount": "666","BasicPassword": "666"},"WhiteList": null},"BlackListConfigure": {"BlackList": null},"DDNSConfigure": {"Enable": false,"FirstCheckDelay": 0,"Intervals": 0},"DDNSTaskList": null,"ReverseProxyRuleList": null,"SSLCertficateList": null,"PortForwardsRuleList": null,"PortForwardsConfigure": {"PortForwardsLimit": 128,"TCPPortforwardMaxConnections": 1024,"UDPReadTargetDataMaxgoroutineCount": 1024},"WOLDeviceList": null,"WOLServiceConfigure": {"Server": {"Enable": false,"Token": "666666" },"Client": {"Enable": false,"ServerURL": "","Token": "","Relay": false,"Key": "","DeviceName": "OpenWrt","Mac": "","BroadcastIP": "","Port": 9,"Repeat": 5,"PowerOffCMD": "poweroff","UpdateTime": 0}}}
EOF
}
[ "x$enabled" = "x1" ] || return 1
if [ "x$AllowInternetaccess" = "x1" ] ;then
sed -i 's/"AllowInternetaccess": false,/"AllowInternetaccess": true,/g' "$LUCKYCONF"
else
sed -i 's/"AllowInternetaccess": true,/"AllowInternetaccess": false,/g' "$LUCKYCONF"
fi
set_firewall
procd_open_instance
procd_set_param command "$PROG"
procd_append_param command -c "$LUCKYCONF" -p $port
[ "x$logger" == x1 ] && procd_set_param stderr 1
procd_set_param respawn
procd_close_instance
}
stop_service() {
killall -9 "$PROG" >/dev/null 2>&1
}
reload_service() {
stop
start
}
service_triggers() {
procd_add_reload_trigger "$CONF"
}

View File

@ -4,14 +4,15 @@
include $(TOPDIR)/rules.mk
LUCI_TITLE:=LuCI support for UnblockNeteaseMusic
LUCI_DEPENDS:=+dnsmasq-full +ipset +node \
LUCI_TITLE:=LuCI support for UnblockNeteaseMusic (JavaScript)
LUCI_DEPENDS:=+dnsmasq-full +node \
+PACKAGE_firewall:ipset \
@(PACKAGE_libustream-mbedtls||PACKAGE_libustream-openssl||PACKAGE_libustream-wolfssl)
LUCI_PKGARCH:=all
PKG_NAME:=luci-app-unblockneteasemusic
PKG_VERSION:=2.13
PKG_RELEASE:=5
PKG_VERSION:=3.1
PKG_RELEASE:=3
PKG_MAINTAINER:=Tianling Shen <cnsztl@immortalwrt.org>

View File

@ -42,11 +42,10 @@
### 效果图
#### LuCI 界面
![Image text](https://raw.githubusercontent.com/UnblockNeteaseMusic/luci-app-unblockneteasemusic/master/views/view1.jpg)
![Image text](https://raw.githubusercontent.com/UnblockNeteaseMusic/luci-app-unblockneteasemusic/master/views/view2.jpg)
![Image text](https://raw.githubusercontent.com/UnblockNeteaseMusic/luci-app-unblockneteasemusic/master/views/view3.jpg)
![Image text](https://raw.githubusercontent.com/UnblockNeteaseMusic/luci-app-unblockneteasemusic/js/views/view1.jpg)
![Image text](https://raw.githubusercontent.com/UnblockNeteaseMusic/luci-app-unblockneteasemusic/js/views/view2.jpg)
#### UWP 网易云音乐客户端
![Image text](https://raw.githubusercontent.com/UnblockNeteaseMusic/luci-app-unblockneteasemusic/master/views/view4.jpg)
![Image text](https://raw.githubusercontent.com/UnblockNeteaseMusic/luci-app-unblockneteasemusic/js/views/view3.jpg)
### 鸣谢
[UnblockNeteaseMusic](https://github.com/UnblockNeteaseMusic/server)的开发者:[nondanee](https://github.com/nondanee)、[pan93412](https://github.com/pan93412)、[1715173329](https://github.com/1715173329)<br/>

View File

@ -0,0 +1,336 @@
/* SPDX-License-Identifier: GPL-3.0-only
*
* Copyright (C) 2022 ImmortalWrt.org
*/
'use strict';
'require form';
'require fs';
'require network';
'require poll';
'require rpc';
'require uci';
'require ui';
'require view';
var CBIStaticList = form.DynamicList.extend({
__name__: 'CBI.StaticList',
renderWidget: function(/* ... */) {
var dl = form.DynamicList.prototype.renderWidget.apply(this, arguments);
dl.querySelector('.add-item ul > li[data-value="-"]').remove();
return dl;
}
});
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;
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(CBIStaticList, 'music_source', _('音源接口'),
_('留空以使用默认音源。'));
o.value('bilibili', _('Bilibili 音乐'));
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.value('ytdownload', _('Youtube 音乐ytdownload'));
o = s.option(form.Value, 'joox_cookie', _('JOOX Cookie'),
_('在 joox.com 获取,需要 wmid 和 session_key 值。'));
o.placeholder = 'wmid=; session_key=';
o.depends({'music_source': 'joox', '!contains': true});
o = s.option(form.Value, 'migu_cookie', _('Migu Cookie'),
_('通过抓包手机客户端请求获取,需要 aversionid 值。'));
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.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.depends({'music_source': 'youtube', '!contains': true});
o = s.option(form.Flag, 'follow_source_order', _('顺序查询'),
_('默认为并行查询并返回第一个结果,开启后将严格按照配置音源的顺序进行查询。'))
o.default = o.disabled;
o.rmempty = false;
o = s.option(form.Flag, 'search_album', _('附加专辑名'),
_('在其他音源搜索歌曲时携带专辑名称(默认搜索条件 <code>歌曲名 - 歌手</code>,启用后搜索条件 <code>歌曲名 - 歌手 专辑名</code>)。')
o.default = o.disabled;
o.rmempty = false;
o = s.option(form.Flag, 'local_vip', _('启用本地 VIP'),
_('启用后,可以使用去广告、个性换肤、鲸云音效等本地功能。'));
o.default = o.disabled;
o.rmempty = false;
o = s.option(form.Flag, 'enable_flac', _('启用无损音质'),
_('目前仅支持酷狗、酷我、咪咕、pyncmd、QQ 音源。'));
o.default = o.disabled;
o.rmempty = false;
o = s.option(form.Flag, 'disable_upgrade_check', _('禁用更新检查'),
_('禁止客户端检查更新,全平台支持。'));
o.default = o.enabled;
o.rmempty = false;
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.rmempty = false;
o = s.option(form.Flag, 'auto_update', _('启用自动更新'),
_('启用后,每天将定时自动检查最新版本并更新。'));
o.default = o.disabled;
o.rmempty = false;
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.rmempty = false;
o = s.option(form.ListValue, 'log_level', _('日志等级'));
o.value('debug', _('调试'));
o.value('info', _('信息(默认)'));
o.value('silent', _('静默'));
o.default = 'info';
o.rmempty = false;
o.depends('advanced_mode', '1');
o = s.option(form.Value, 'http_port', _('HTTP 监听端口'));
o.datatype = 'port';
o.default = '5200';
o.rmempty = false;
o.depends({'advanced_mode': '1', 'hijack_ways': 'dont_hijack'});
o.depends({'advanced_mode': '1', 'hijack_ways': 'use_ipset'});
o = s.option(form.Value, 'https_port', _('HTTPS 监听端口'));
o.datatype = 'port';
o.default = '5201';
o.rmempty = false;
o.depends({'advanced_mode': '1', 'hijack_ways': 'dont_hijack'});
o.depends({'advanced_mode': '1', '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 劫持,程序监听的 HTTP/HTTPS 端口将被锁定为 80/443。'));
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, 'keep_core_when_upgrade', _('升级时保留核心程序'));
o.default = o.disabled;
o.rmempty = false;
o.depends('advanced_mode', '1');
o = s.option(form.Flag, 'pub_access', _('部署到公网'),
_('默认仅放行局域网请求,如需提供公开访问请勾选此选项。'));
o.default = o.disabled;
o.rmempty = false;
o.depends('advanced_mode', '1');
o = s.option(form.Flag, 'strict_mode', _('启用严格模式'),
_('若将服务部署到公网,则强烈建议使用严格模式,此模式下仅放行网易云音乐所属域名的请求。<br/>注意:该模式下不能使用全局代理。'));
o.default = o.disabled;
o.rmempty = false;
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, 'ip_addr', _('IP 地址'));
o.datatype = 'ip4addr';
for (var i of Object.entries(data[1].hosts))
for (var v in i[1].ipaddrs)
if (i[1].ipaddrs[v]) {
var ip_addr = i[1].ipaddrs[v], ip_host = i[1].name;
o.value(ip_addr, ip_host ? String.format('%s (%s)', ip_host, ip_addr) : ip_addr)
}
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();
}
});

View File

@ -0,0 +1,224 @@
/* SPDX-License-Identifier: GPL-3.0-only
*
* Copyright (C) 2022 ImmortalWrt.org
*/
'use strict';
'require dom';
'require form';
'require fs';
'require poll';
'require rpc';
'require ui';
'require view';
return view.extend({
render: function() {
var m, s, o;
var unm_helper = '/usr/share/unblockneteasemusic/update.sh';
m = new form.Map('unblockneteasemusic');
s = m.section(form.NamedSection, 'config', 'unblockneteasemusic', _('核心管理'));
s.anonymous = true;
o = s.option(form.DummyValue, '_core_version', _('核心版本'));
o.cfgvalue = function() {
var _this = this;
var spanTemp = '<div style="color:%s;margin-top:5px;"><strong>%s</strong></div>';
return fs.exec(unm_helper, [ 'check_version' ]).then(function(res) {
if (res.code === 0)
_this.default = String.format(spanTemp, 'green', res.stdout.trim());
else if (res.code === 2)
_this.default = String.format(spanTemp, 'red', _('未安装'));
else {
ui.addNotification(null, E('p', [ _('获取版本信息失败:%s。').format(res) ]));
_this.default = String.format(spanTemp, 'red', _('未知错误'));
}
return null;
}).catch(function(err) {
ui.addNotification(null, E('p', [ _('未知错误:%s。').format(err) ]));
_this.default = String.format(spanTemp, 'red', _('未知错误'));
return null;
});
}
o.rawhtml = true;
o = s.option(form.Button, '_remove_core', _('删除核心'),
_('删除核心后,需手动点击下面的按钮重新下载,有助于解决版本冲突问题。'));
o.inputstyle = 'remove';
o.onclick = function() {
var _this = this;
return fs.exec(unm_helper, [ 'remove_core' ]).then(function(res) {
_this.description = '删除完毕。'
return _this.map.reset();
}).catch(function(err) {
ui.addNotification(null, E('p', [ _('未知错误:%s。').format(err) ]));
_this.description = '删除失败。'
return _this.map.reset();
});
}
o = s.option(form.Button, '_update_core', _('更新核心'),
_('更新完毕后会自动在后台重启插件,无需手动重启。'));
o.inputstyle = 'action';
o.onclick = function() {
var _this = this;
return fs.exec(unm_helper, [ 'update_core' ]).then(function (res) {
if (res.code === 0)
_this.description = _('更新成功。');
else if (res.code === 1)
_this.description = _('更新失败。');
else if (res.code === 2)
_this.description = _('更新程序正在运行中。');
else if (res.code === 3)
_this.description = _('当前已是最新版本。');
return _this.map.reset();
}).catch(function (err) {
ui.addNotification(null, E('p', [ _('未知错误:%s。').format(err) ]));
_this.description = _('更新失败。');
return _this.map.reset();
});
}
o = s.option(form.Button, '_debug_log', _('调试报告'),
_('若您遇到使用上的问题,请点此打印调试报告,并将其附在您的 issue 中。'));
o.inputstyle = 'action';
o.inputtitle = _('打印报告');
o.onclick = function() {
var log_modal = ui.showModal(_('打印调试报告'), [
E('p', { 'class': 'spinning' },
_('正在打印调试报告中...'))
]);
return fs.exec_direct('/usr/bin/unm-debug', 'text').then(function (res) {
log_modal.removeChild(log_modal.lastChild);
if (res) {
log_modal.appendChild(E('p', _('提交 issue 时,您只需附上最后的链接,无需提供整个输出。')));
log_modal.appendChild(E('textarea', {
'id': 'content_debugLog',
'class': 'cbi-input-textarea',
'style': 'font-size:13px; resize: none',
'readonly': 'readonly',
'wrap': 'soft',
'rows': '30'
}, [ res.trim() ])
);
} else {
log_modal.appendChild(E('p', _('错误')));
log_modal.appendChild(E('pre', { 'class': 'errors' }, [ _('无法打印调试报告。') ]));
}
var log_element = document.getElementById('content_debugLog') || null;
if (log_element)
log_element.scrollTop = log_element.scrollHeight;
log_modal.appendChild(E('div', { 'class': 'right' }, [
log_element ? E('button', {
'class': 'btn cbi-button-action',
'click': ui.createHandlerFn(this, function() {
var links = log_element.value.match(/https:\/\/(litter.catbox.moe|transfer.sh)\/.*.txt/g);
var textarea = document.createElement('textarea');
document.body.appendChild(textarea);
textarea.style.position = 'absolute';
textarea.style.clip = 'rect(0 0 0 0)';
textarea.value = links ? links.join('\n'): log_element.value;
textarea.select()
document.execCommand('copy', true);
document.body.removeChild(textarea);
})
}, _('复制')) : '',
E('button', {
'class': 'btn',
'click': ui.hideModal
}, _('关闭'))
]));
return null;
}).catch(function (err) {
ui.addNotification(null, E('p', _('无法打印调试报告:%s。').format(err)));
ui.hideModal();
return null;
});
}
o = s.option(form.DummyValue, '_logview');
o.render = function() {
/* Thanks to luci-app-aria2 */
var css = ' \
#log_textarea { \
padding: 10px; \
text-align: left; \
} \
#log_textarea pre { \
padding: .5rem; \
word-break: break-all; \
margin: 0; \
} \
.description { \
background-color: #33ccff; \
}';
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(L.bind(function() {
return fs.read('/tmp/unblockneteasemusic.log', 'text')
.then(function(res) {
var log = E('pre', { 'wrap': 'pre' }, [
res.trim() || _('当前无日志。')
]);
dom.content(log_textarea, log);
}).catch(function(err) {
if (err.toString().includes('NotFoundError'))
var log = E('pre', { 'wrap': 'pre' }, [
_('日志文件不存在。')
]);
else
var log = E('pre', { 'wrap': 'pre' }, [
_('未知错误:%s。').format(err)
]);
dom.content(log_textarea, log);
});
}));
return E([
E('style', [ css ]),
E('div', {'class': 'cbi-map'}, [
E('h3', {'name': 'content'}, _('运行日志')),
E('div', {'class': 'cbi-section'}, [
log_textarea,
E('div', {'style': 'text-align:right'},
E('small', {}, _('每 %s 秒刷新。').format(L.env.pollinterval))
)
])
])
]);
}
return m.render();
},
handleSaveApply: null,
handleSave: null,
handleReset: null
});

View File

@ -1,69 +0,0 @@
-- SPDX-License-Identifer: GPL-3.0-only
-- Copyright (C) 2019-2022 Tianling Shen <cnsztl@immortalwrt.org>
module("luci.controller.unblockneteasemusic", package.seeall)
function index()
if not nixio.fs.access("/etc/config/unblockneteasemusic") then
return
end
local page
page = entry({"admin", "services", "unblockneteasemusic"}, firstchild(), _("解除网易云音乐播放限制"), 50)
page.dependent = false
page.acl_depends = { "luci-app-unblockneteasemusic" }
entry({"admin", "services", "unblockneteasemusic", "general"}, cbi("unblockneteasemusic/main"), _("基本设定"), 1)
entry({"admin", "services", "unblockneteasemusic", "upgrade"}, form("unblockneteasemusic/upgrade"), _("更新组件"), 2).leaf = true
entry({"admin", "services", "unblockneteasemusic", "log"}, form("unblockneteasemusic/log"), _("日志"), 3)
entry({"admin", "services", "unblockneteasemusic", "status"}, call("act_status")).leaf = true
entry({"admin", "services", "unblockneteasemusic", "update_core"}, call("act_update_core"))
entry({"admin", "services", "unblockneteasemusic", "remove_core"}, call("act_remove_core"))
end
function act_status()
local stat = luci.util.ubus("service", "list", { name = "unblockneteasemusic" })
local running = next(stat) and next(stat.unblockneteasemusic) and stat.unblockneteasemusic.instances.unblockneteasemusic.running or false
local e = { running = running }
luci.http.prepare_content("application/json")
luci.http.write_json(e)
end
function update_core()
local core_cloud_ver = luci.sys.exec("uclient-fetch -qO- 'https://api.github.com/repos/UnblockNeteaseMusic/server/commits?sha=enhanced&path=precompiled' | jsonfilter -e '@[0].sha'")
local core_cloud_ver_mini = string.sub(core_cloud_ver, 1, 7)
local core_local_ver
if not core_cloud_ver or not core_cloud_ver_mini then
return "1"
else
core_local_ver = luci.sys.exec("cat '/usr/share/unblockneteasemusic/core_local_ver' 2>'/dev/null'")
if not core_local_ver or (core_local_ver ~= core_cloud_ver) then
luci.sys.call("rm -f /usr/share/unblockneteasemusic/update_core_successfully")
luci.sys.call("/usr/share/unblockneteasemusic/update.sh update_core_from_luci")
if not nixio.fs.access("/usr/share/unblockneteasemusic/update_core_successfully") then
return "2"
else
luci.sys.call("rm -f /usr/share/unblockneteasemusic/update_core_successfully")
return core_cloud_ver_mini
end
else
return "0"
end
end
end
function act_update_core()
luci.http.prepare_content("application/json")
luci.http.write_json({
ret = update_core();
})
end
function act_remove_core()
local ret = {}
ret.ret = luci.sys.call("cd /usr/share/unblockneteasemusic && rm -rf core/* && rm -f core_local_ver") == 0
luci.http.prepare_content("application/json")
luci.http.write_json(ret)
end

View File

@ -1,14 +0,0 @@
local fs = require "nixio.fs"
local conffile = "/tmp/unblockneteasemusic.log"
f = SimpleForm("logview")
t = f:field(TextValue, "conf")
t.rmempty = true
t.rows = 15
function t.cfgvalue()
return fs.readfile(conffile) or ""
end
t.readonly="readonly"
return f

View File

@ -1,247 +0,0 @@
local m, s, o
m = Map("unblockneteasemusic", translate("解除网易云音乐播放限制"))
m.description = translate("原理:采用 [Bilibili/JOOX/酷狗/酷我/咪咕/pyncmd/QQ/Youtube] 等音源,替换网易云音乐 无版权/收费 歌曲链接<br/>具体使用方法参见:<a href=\"https://github.com/UnblockNeteaseMusic/luci-app-unblockneteasemusic\" target=\"_blank\">GitHub @UnblockNeteaseMusic/luci-app-unblockneteasemusic</a>")
m:section(SimpleSection).template = "unblockneteasemusic/status"
s = m:section(NamedSection, "config", "unblockneteasemusic")
o = s:option(Flag, "enable", translate("启用本插件"))
o.description = translate("启用本插件以解除网易云音乐播放限制")
o.default = 0
o.rmempty = false
o = s:option(Value, "music_source", translate("音源接口"))
o:value("default", translate("默认"))
o:value("bilibili", translate("Bilibili音乐"))
o:value("joox", translate("JOOX音乐"))
o:value("kugou", translate("酷狗音乐"))
o:value("kuwo", translate("酷我音乐"))
o:value("migu", translate("咪咕音乐"))
o:value("pyncmd", translate("网易云音乐pyncmd"))
o:value("qq", translate("QQ音乐"))
o:value("youtube", translate("Youtube音乐"))
o:value("youtubedl", translate("Youtube音乐youtube-dl"))
o:value("ytdlp", translate("Youtube音乐yt-dlp"))
o:value("ytdownload", translate("Youtube音乐ytdownload"))
o.description = translate("自定义模式下,多个音源请用空格隔开")
o.default = "default"
o.rmempty = false
o = s:option(Flag, "follow_source_order", translate("顺序查询"))
o.description = translate("默认为并行查询并返回第一个结果,开启后将严格按照配置音源的顺序进行查询")
o.default = 0
o.rmempty = false
o = s:option(Flag, "search_album", translate("附加专辑名"))
o.description = translate("在其他音源搜索歌曲时携带专辑名称(默认搜索条件 歌曲名 - 歌手,启用后搜索条件 歌曲名 - 歌手 专辑名)")
o.default = 0
o.rmempty = false
o = s:option(Flag, "local_vip", translate("启用本地 VIP"))
o.description = translate("启用后,可以使用去广告、个性换肤、鲸云音效等本地功能")
o.default = 0
o.rmempty = false
o = s:option(Flag, "enable_flac", translate("启用无损音质"))
o.description = translate("目前仅支持酷狗、酷我、咪咕、pyncmd、QQ 音源")
o.default = 0
o.rmempty = false
o = s:option(Flag, "disable_upgrade_check", translate("禁用更新检查"))
o.description = translate("禁止客户端检查更新,全平台支持")
o.default = 1
o.rmempty = false
o = s:option(ListValue, "replace_music_source", translate("强制音乐音源替换"))
o:value("dont_replace", translate("不强制替换音乐音源"))
o:value("lower_than_192kbps", translate("当音质低于 192 Kbps"))
o:value("lower_than_320kbps", translate("当音质低于 320 Kbps"))
o:value("lower_than_999kbps", translate("当音质低于 999 Kbps无损"))
o:value("replace_all", translate("替换所有音乐音源"))
o.description = translate("当音乐音质低于指定数值时,尝试强制使用其他平台的高音质版本进行替换")
o.default = "dont_replace"
o.rmempty = false
o = s:option(Flag, "use_custom_cookie", translate("使用自定义 Cookie"))
o.description = translate("使用自定义 Cookie 请求音源接口")
o.default = 0
o.rmempty = false
o = s:option(Value, "joox_cookie", translate("JOOX Cookie"))
o.description = translate("在 joox.com 获取,需要 wmid 和 session_key 值")
o.placeholder = "wmid=; session_key="
o.datatype = "string"
o:depends("use_custom_cookie", 1)
o = s:option(Value, "migu_cookie", translate("Migu Cookie"))
o.description = translate("通过抓包手机客户端请求获取,需要 aversionid 值")
o.datatype = "string"
o:depends("use_custom_cookie", 1)
o = s:option(Value, "qq_cookie", translate("QQ Cookie"))
o.description = translate("在 y.qq.com 获取,需要 uin 和 qm_keyst值 ")
o.placeholder = "uin=; qm_keyst="
o.datatype = "string"
o:depends("use_custom_cookie", 1)
o = s:option(Value, "youtube_key", translate("Youtube API Key"))
o.description = translate("API Key 申请地址https://developers.google.com/youtube/v3/getting-started#before-you-start")
o.datatype = "string"
o:depends("use_custom_cookie", 1)
o = s:option(Flag, "auto_update", translate("启用自动更新"))
o.description = translate("启用后,每天将定时自动检查最新版本并更新")
o.default = 0
o.rmempty = false
o = s:option(ListValue, "update_time", translate("检查更新时间"))
for update_time_hour = 0,23 do
o:value(update_time_hour, update_time_hour..":00")
end
o.default = "3"
o.description = translate("设定每天自动检查更新时间")
o:depends("auto_update", 1)
o = s:option(Button,"certificate", translate("HTTPS 证书"))
o.inputtitle = translate("下载 CA 根证书")
o.description = translate("Linux/iOS/MacOSX 在信任根证书后方可正常使用")
o.inputstyle = "reload"
o.write = function()
act_download_cert()
end
function act_download_cert()
local t, e
t = nixio.open("/usr/share/unblockneteasemusic/core/ca.crt","r")
luci.http.header('Content-Disposition', 'attachment; filename="ca.crt"')
luci.http.prepare_content("application/octet-stream")
while true do
e = t:read(nixio.const.buffersize)
if (not e) or (#e == 0) then
break
else
luci.http.write(e)
end
end
t:close()
luci.http.close()
end
o = s:option(Flag, "advanced_mode", translate("启用进阶设置"))
o.description = translate("非必要不推荐使用")
o.default = 0
o.rmempty = false
o = s:option(ListValue, "log_level", translate("日志等级"))
o:value("debug", translate("'调试"));
o:value("info", translate("信息(默认)"))
o:value("silent", translate("静默"))
o.default = "info";
o:depends("advanced_mode", 1)
o = s:option(Value, "http_port", translate("HTTP 监听端口"))
o.description = translate("程序监听的 HTTP 端口,不可与 其他程序/HTTPS 共用一个端口")
o.placeholder = "5200"
o.default = "5200"
o.datatype = "port"
o:depends({advanced_mode = true, hijack_ways = "dont_hijack"})
o:depends({advanced_mode = true, hijack_ways = "use_ipset"})
o = s:option(Value, "https_port", translate("HTTPS 监听端口"))
o.description = translate("程序监听的 HTTPS 端口,不可与 其他程序/HTTP 共用一个端口")
o.placeholder = "5201"
o.default = "5201"
o.datatype = "port"
o:depends({advanced_mode = true, hijack_ways = "dont_hijack"})
o:depends({advanced_mode = true, hijack_ways = "use_ipset"})
o = s:option(Value, "endpoint_url", translate("EndPoint"))
o.description = translate("具体说明参见https://github.com/UnblockNeteaseMusic/server")
o.default = "https://music.163.com"
o.placeholder = "https://music.163.com"
o.datatype = "string"
o:depends("advanced_mode", 1)
o = s:option(Value, "cnrelay", translate("UNM bridge 服务器"))
o.description = translate("使用 UnblockNeteaseMusic 中继桥bridge以获取音源信息")
o.placeholder = "http(s)://host:port"
o.datatype = "string"
o:depends("advanced_mode", 1)
o = s:option(ListValue, "hijack_ways", translate("劫持方法"))
o:value("dont_hijack", translate("不开启劫持"))
o:value("use_ipset", translate("使用 IPSet 劫持"))
o:value("use_hosts", translate("使用 Hosts 劫持"))
o.description = translate("如果使用Hosts劫持程序监听的 HTTP/HTTPS 端口将被锁定为 80/443")
o.default = "dont_hijack"
o:depends("advanced_mode", 1)
o = s:option(Flag, "keep_core_when_upgrade", translate("升级时保留核心程序"))
o.description = translate("默认情况下,在系统升级后会导致核心程序丢失,开启此选项后会保留当前下载的核心程序")
o.default = 0
o.rmempty = false
o:depends("advanced_mode", 1)
o = s:option(Flag, "pub_access", translate("部署到公网"))
o.description = translate("默认仅监听局域网,如需提供公开访问请勾选此选项")
o.default = 0
o.rmempty = false
o:depends("advanced_mode", 1)
o = s:option(Flag, "strict_mode", translate("启用严格模式"))
o.description = translate("若将服务部署到公网,则强烈建议使用严格模式,此模式下仅放行网易云音乐所属域名的请求;注意:该模式下不能使用全局代理")
o.default = 0
o.rmempty = false
o:depends("advanced_mode", 1)
o = s:option(Value, "netease_server_ip", translate("网易云服务器 IP"))
o.description = translate("通过 ping music.163.com 即可获得 IP 地址,仅限填写一个")
o.placeholder = "59.111.181.38"
o.datatype = "ipaddr"
o:depends("advanced_mode", 1)
o = s:option(Value, "proxy_server_ip", translate("代理服务器地址"))
o.description = translate("使用代理服务器获取音乐信息")
o.placeholder = "http(s)://host:port"
o.datatype = "string"
o:depends("advanced_mode", 1)
o = s:option(Value, "self_issue_cert_crt", translate("自签发证书公钥位置"))
o.description = translate("[公钥] 默认使用 UnblockNeteaseMusic 项目提供的 CA 证书,您可以指定为您自己的证书")
o.placeholder = "/usr/share/unblockneteasemusic/core/server.crt"
o.datatype = "file"
o:depends("advanced_mode", 1)
o = s:option(Value, "self_issue_cert_key", translate("自签发证书私钥位置"))
o.description = translate("[私钥] 默认使用 UnblockNeteaseMusic 项目提供的 CA 证书,您可以指定为您自己的证书")
o.placeholder = "/usr/share/unblockneteasemusic/core/server.key"
o.datatype = "file"
o:depends("advanced_mode", 1)
s = m:section(TypedSection, "acl_rule", translate("例外客户端规则"), translate("可以为局域网客户端分别设置不同的例外模式,默认无需设置"))
s.template = "cbi/tblsection"
s.sortable = true
s.anonymous = true
s.addremove = true
o = s:option(Value, "ip_addr", translate("IP 地址"))
o.width = "40%"
o.datatype = "ip4addr"
o.placeholder = "0.0.0.0/0"
luci.ip.neighbors({ family = 4 }, function(entry)
if entry.reachable then
o:value(entry.dest:string())
end
end)
o = s:option(ListValue, "filter_mode", translate("规则"))
o.width = "40%"
o.default = "disable_all"
o.rmempty = false
o:value("disable_all", translate("不代理 HTTP 和 HTTPS"))
o:value("disable_http", translate("不代理 HTTP"))
o:value("disable_https", translate("不代理 HTTPS"))
return m

View File

@ -1,19 +0,0 @@
local m, o
m = SimpleForm("Version")
m.reset = false
m.submit = false
o = m:field(DummyValue, "remove_core", translate("删除核心"))
o.rawhtml = true
o.template = "unblockneteasemusic/remove_core"
o.value = translate("")
o.description = "删除核心后,需手动点击下面的按钮重新下载,有助于解决版本冲突问题"
o = m:field(DummyValue, "update_core", translate("更新核心"))
o.rawhtml = true
o.template = "unblockneteasemusic/update_core"
o.value = translate("")
o.description = "更新完毕后会自动在后台重启插件,无需手动重启"
return m

View File

@ -1,32 +0,0 @@
<%+cbi/valueheader%>
<script type="text/javascript">//<![CDATA[
function act_remove_core(btn, dataname)
{
btn.disabled = true;
btn.value = '<%:正在删除核心...%>';
XHR.get('<%=luci.dispatcher.build_url("admin", "services", "unblockneteasemusic", "remove_core")%>',
status.ret,
function(x, status)
{
var s = document.getElementById(dataname + '-status');
if (s)
{
if(status.ret)
s.innerHTML = "<font style=\"color:green\">" + "<%:删除核心成功%>" + "</font>";
else
s.innerHTML = "<font style=\"color:red\">" + "<%:删除核心失败%>"+"</font>";
}
btn.disabled = false;
btn.value = '<%:点此删除核心%>';
}
);
return false;
}
//]]></script>
<input type="button" class="btn cbi-button cbi-input-reload" value="<%:点此删除核心%>" onclick="return act_remove_core(this, '<%=self.option%>')" />
<span id="<%=self.option%>-status"><em><%=self.value%></em></span>
<%+cbi/valuefooter%>

View File

@ -1,21 +0,0 @@
<script type="text/javascript">//<![CDATA[
XHR.poll(3, '<%=url([[admin]], [[services]], [[unblockneteasemusic]], [[status]])%>', null,
function(x, data) {
var tb = document.getElementById('unblockneteasemusic_status');
if (data && tb) {
if (data.running) {
tb.innerHTML = '<em><b style=color:green>UnblockNeteaseMusic <%:运行中%></b></em>';
} else {
tb.innerHTML = '<em><b style=color:red>UnblockNeteaseMusic <%:未在运行%></b></em>';
}
}
}
);
//]]>
</script>
<style>.mar-10 {margin-left: 50px; margin-right: 10px;}</style>
<fieldset class="cbi-section">
<p id="unblockneteasemusic_status">
<em><%:Collecting data...%></em>
</p>
</fieldset>

View File

@ -1,36 +0,0 @@
<%+cbi/valueheader%>
<script type="text/javascript">//<![CDATA[
function act_update_core(btn, dataname)
{
btn.disabled = true;
btn.value = '<%:正在更新核心...%>';
XHR.get('<%=luci.dispatcher.build_url("admin", "services", "unblockneteasemusic", "update_core")%>',
status.ret,
function(x, status)
{
var s = document.getElementById(dataname + '-status');
if (s)
{
if(status.ret == "0")
s.innerHTML = "<font style=\"color:green\">" + "<%:当前已是最新版本%>" + "</font>";
else if (status.ret == "1")
s.innerHTML = "<font style=\"color:red\">" + "<%:无法检测最新版本%>" + "</font>";
else if(status.ret == "2")
s.innerHTML = "<font style=\"color:red\">" + "<%:更新失败,请稍后重试%>" + "</font>";
else
s.innerHTML = "<font style=\"color:green\">" + "<%:更新成功,当前版本号:%>" + status.ret + "</font>";
}
btn.disabled = false;
btn.value = '<%:点此更新核心%>';
}
);
return false;
}
//]]></script>
<input type="button" class="btn cbi-button cbi-input-reload" value="<%:点此更新核心%>" onclick="return act_update_core(this, '<%=self.option%>')" />
<span id="<%=self.option%>-status"><em><%=self.value%></em></span>
<%+cbi/valuefooter%>

View File

@ -1,13 +1,12 @@
config unblockneteasemusic 'config'
option enable '0'
option music_source 'default'
option music_source ''
option follow_source_order '0'
option local_vip '0'
option enable_flac '0'
option disable_upgrade_check '1'
option replace_music_source 'dont_replace'
option use_custom_cookie '0'
option auto_update '1'
option update_time '3'
option advanced_mode '0'

View File

@ -46,6 +46,8 @@ append_param_env() {
append_filter_client() {
local cfg="$1"
is_enabled "$cfg" "enable" || return 1
local ip_addr filter_mode
config_get ip_addr "$cfg" "ip_addr"
config_get filter_mode "$cfg" "filter_mode"
@ -124,10 +126,7 @@ start_service() {
fi
fi
local music_source
config_get music_source "config" "music_source" "default"
[ "${music_source}" != "default" ] && append_param -o "${music_source}"
append_param_arg "config" "music_source" "-o"
append_param_arg "config" "cnrelay" "-c"
append_param_arg "config" "endpoint_url" "-e" "https://music.163.com"
append_param_arg "config" "netease_server_ip" "-f"

View File

@ -1,20 +1,23 @@
#!/bin/sh
uci -q batch <<-EOF >/dev/null
if [ "$(uci -q get unblockneteasemusic.config.music_source)" = "default" ]; then
uci -q delete unblockneteasemusic.config.music_source
uci -q commit unblockneteasemusic
fi
uci -q batch <<-EOF >"/dev/null"
delete ucitrack.@unblockneteasemusic[-1]
add ucitrack unblockneteasemusic
set ucitrack.@unblockneteasemusic[-1].init=unblockneteasemusic
commit ucitrack
EOF
[ -e "$(command -v fw4)" ] || {
uci -q batch <<-EOF >/dev/null
delete firewall.unblockneteasemusic
set firewall.unblockneteasemusic=include
set firewall.unblockneteasemusic.type=script
set firewall.unblockneteasemusic.path=/var/etc/unblockneteasemusic.include
set firewall.unblockneteasemusic.reload=1
commit firewall
EOF
uci -q batch <<-EOF >"/dev/null"
delete firewall.unblockneteasemusic
set firewall.unblockneteasemusic=include
set firewall.unblockneteasemusic.type=script
set firewall.unblockneteasemusic.path=/var/etc/unblockneteasemusic.include
set firewall.unblockneteasemusic.reload=1
commit firewall
EOF
}
rm -f /tmp/luci-indexcache

View File

@ -0,0 +1,29 @@
{
"admin/services/unblockneteasemusic": {
"title": "解除网易云音乐播放限制",
"order": 50,
"action": {
"type": "firstchild"
},
"depends": {
"acl": [ "luci-app-unblockneteasemusic" ],
"uci": { "unblockneteasemusic": true }
}
},
"admin/services/unblockneteasemusic/config": {
"title": "基本设定",
"order": 10,
"action": {
"type": "view",
"path": "unblockneteasemusic/config"
}
},
"admin/services/unblockneteasemusic/status": {
"title": "状态信息",
"order": 20,
"action": {
"type": "view",
"path": "unblockneteasemusic/status"
}
}
}

View File

@ -1,10 +1,24 @@
{
"luci-app-unblockneteasemusic": {
"description": "Grant UCI access for luci-app-unblockneteasemusic",
"description": "Grant access to UnblockNeteaseMusic configuration",
"read": {
"file": {
"/etc/init.d/unblockneteasemusic": [ "exec" ],
"/tmp/unblockneteasemusic.log": [ "read" ],
"/usr/bin/unm-debug": [ "exec" ],
"/usr/share/unblockneteasemusic/update.sh": [ "exec" ],
"/usr/share/unblockneteasemusic/core/ca.crt": [ "read" ]
},
"ubus": {
"service": [ "list" ]
},
"uci": [ "unblockneteasemusic" ]
},
"write": {
"file": {
"/usr/share/unblockneteasemusic/server.crt": [ "write" ],
"/usr/share/unblockneteasemusic/server.key": [ "write" ]
},
"uci": [ "unblockneteasemusic" ]
}
}

View File

@ -65,7 +65,6 @@ update_core() {
}
done
[ -z "${update_core_from_luci}" ] || touch "/usr/share/$NAME/update_core_successfully"
echo -e "${core_latest_ver}" > "/usr/share/$NAME/core_local_ver"
[ -n "${non_restart}" ] || /etc/init.d/"$NAME" restart
@ -75,6 +74,17 @@ update_core() {
}
case "$1" in
"check_version")
if [ ! -e "/usr/share/$NAME/core_local_ver" ] || [ ! -e "/usr/share/$NAME/core/app.js" ]; then
echo -e "Not installed."
exit 2
else
version="$(node "/usr/share/$NAME/core/app.js" -v)"
commit="$(cat "/usr/share/$NAME/core_local_ver" | head -c7)"
echo "$version ($commit)"
exit 0
fi
;;
"update_core")
check_core_if_already_running
check_core_latest_version
@ -84,12 +94,11 @@ case "$1" in
check_core_if_already_running
check_core_latest_version
;;
"update_core_from_luci")
update_core_from_luci=1
check_core_if_already_running
check_core_latest_version
"remove_core")
/etc/init.d/"$NAME" stop
rm -rf "/usr/share/$NAME/core" "/usr/share/$NAME/core_local_ver"
;;
*)
echo -e "Usage: $0/update.sh update_core"
echo -e "Usage: $0/update.sh check_version | update_core | remove_core"
;;
esac

Binary file not shown.

Before

Width:  |  Height:  |  Size: 786 KiB

After

Width:  |  Height:  |  Size: 776 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 76 KiB

After

Width:  |  Height:  |  Size: 185 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 165 KiB

After

Width:  |  Height:  |  Size: 117 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 117 KiB