This commit is contained in:
CN_SZTL 2019-06-14 15:28:52 +08:00
commit d51120cee3
No known key found for this signature in database
GPG Key ID: 6850B6345C862176
97 changed files with 4121 additions and 65 deletions

View File

@ -6,13 +6,13 @@ ifdef CONFIG_TESTING_KERNEL
KERNEL_PATCHVER:=$(KERNEL_TESTING_PATCHVER)
endif
LINUX_VERSION-4.9 = .178
LINUX_VERSION-4.14 = .121
LINUX_VERSION-4.19 = .45
LINUX_VERSION-4.9 = .180
LINUX_VERSION-4.14 = .123
LINUX_VERSION-4.19 = .48
LINUX_KERNEL_HASH-4.9.178 = 13ffd8b0725d9ce9f9e927f4748ef640b049d7c69f21b28ca0d976cc23072566
LINUX_KERNEL_HASH-4.14.121 = 2eecd6825a0998c8b6dd686cd4cd76e9f0d195bbace89f0b82495cc11ddb2884
LINUX_KERNEL_HASH-4.19.45 = eb31e62fbb30aaeedb892cecdd8ac9746d42cbbef2ef2356f9ca8e13514b9d2b
LINUX_KERNEL_HASH-4.9.180 = d25a490d62a3e96a587333e163862585eac4086fc8d4893b4ec1711e7d387c5f
LINUX_KERNEL_HASH-4.14.123 = 25f58cb56bde388ac9bcee984f5f2d0ca094b0a8af6b92ad1f5b2fd0e6725b85
LINUX_KERNEL_HASH-4.19.48 = 01d0db4f10cc8e384241a605e29413e32c442aa6853c116877538b07c16893fa
remove_uri_prefix=$(subst git://,,$(subst http://,,$(subst https://,,$(1))))
sanitize_uri=$(call qstrip,$(subst @,_,$(subst :,_,$(subst .,_,$(subst -,_,$(subst /,_,$(1)))))))

View File

@ -0,0 +1,63 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=luci-app-qbittorrent
PKG_VERSION=1.0
PKG_RELEASE:=2
PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)
include $(INCLUDE_DIR)/package.mk
define Package/luci-app-qbittorrent/temple
SECTION:=luci
CATEGORY:=LuCI
SUBMENU:=3. Applications
TITLE:=qbittorrent-nox for LuCI
PKGARCH:=all
DEPENDS:=+qBittorrent
endef
define Package/luci-app-qbittorrent
$(call Package/luci-app-qbittorrent/temple)
endef
define Package/luci-i18n-qbittorrent-zh-cn
$(call Package/luci-app-qbittorrent/temple)
DEPENDS:=+luci-app-qbittorrent
endef
define Package/luci-app-qbittorrent/description
This package contains LuCI configuration pages for qbittorrent-nox
endef
define Package/luci-i18n-qbittorrent-zh-cn/description
Translation support for luci-app-qbittorrent - 简体中文 (Simplified Chinese)
endef
define Build/Prepare
endef
define Build/Configure
endef
define Build/Compile
endef
define Package/luci-app-qbittorrent/install
$(INSTALL_DIR) $(1)/etc/config
$(INSTALL_DIR) $(1)/etc/init.d
$(INSTALL_DIR) $(1)/usr/lib/lua/luci/model/cbi
$(INSTALL_DIR) $(1)/usr/lib/lua/luci/controller
$(INSTALL_CONF) ./src/config/qbittorrent $(1)/etc/config/qbittorrent
$(INSTALL_BIN) ./src/init.d/qbittorrent $(1)/etc/init.d/qbittorrent
$(INSTALL_DATA) ./src/cbi/qbittorrent.lua $(1)/usr/lib/lua/luci/model/cbi/qbittorrent.lua
$(INSTALL_DATA) ./src/controller/qbittorrent.lua $(1)/usr/lib/lua/luci/controller/qbittorrent.lua
endef
define Package/luci-i18n-qbittorrent-zh-cn/install
$(INSTALL_DIR) $(1)/usr/lib/lua/luci/i18n
$(INSTALL_DATA) ./src/i18n/*.lmo $(1)/usr/lib/lua/luci/i18n/
endef
$(eval $(call BuildPackage,luci-app-qbittorrent))
$(eval $(call BuildPackage,luci-i18n-qbittorrent-zh-cn))

View File

@ -0,0 +1,44 @@
require("luci.sys")
local e=require"luci.model.uci".cursor()
local o=e:get_first("qbittorrent","Preferences","port")or 8080
local a=(luci.sys.call("pidof qbittorrent-nox > /dev/null")==0)
local t=""
local e=""
if a then
t="&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<input class=\"cbi-button cbi-button-apply\" type=\"submit\" value=\" "..translate("Open Web Interface").." \" onclick=\"window.open('http://'+window.location.hostname+':"..o.."')\"/>"
end
if a then
e="<b><font color=\"green\">"..translate("Running").."</font></b>"
else
e="<b><font color=\"red\">"..translate("Not running").."</font></b>"
end
m = Map("qbittorrent", translate("qbittorrent"), translate("A BT/PT downloader base on Qt")..t
.."<br/><br/>"..translate("qbittorrent Run Status").." : "..e.."<br/>")
s_basic = m:section(TypedSection, "basic", translate("Basic Settings"))
s_basic.anonymous = true
enable = s_basic:option(Flag, "enable", translate("Enable"))
profile_dir = s_basic:option(Value,"profile_dir",translate("profile_dir"),translate("Store configuration files in the Path"))
profile_dir.default = "/tmp"
program_dir = s_basic:option(Value,"program_dir",translate("program_dir"),translate("Store Program files in the Path"))
program_dir.default = "/usr/bin"
library_dir = s_basic:option(Value,"library_dir",translate("library_dir"),translate("Store Library in the Path"))
library_dir.default = "/usr/lib"
s_download = m:section(TypedSection, "Preferences", translate("Download Settings"))
s_download.anonymous = true
download_dir = s_download:option(Value,"download_dir",translate("download_dir"),translate("Store download files in the Path"))
download_dir.default = "/tmp/download"
s_webui = m:section(TypedSection, "Preferences", translate("WEBUI Settings"))
s_webui.anonymous = true
port = s_webui:option(Value,"port",translate("port"),translate("WEBUI listening port"))
port.default = "8080"
local apply = luci.http.formvalue("cbi.apply")
if apply then
io.popen("/etc/init.d/qbittorrent restart")
end
return m

View File

@ -0,0 +1,3 @@
config basic
config Preferences

View File

@ -0,0 +1,4 @@
module("luci.controller.qbittorrent",package.seeall)
function index()
entry({"admin","services","qbittorrent"},cbi("qbittorrent"),_("qbittorrent"))
end

View File

@ -0,0 +1,67 @@
#!/bin/sh /etc/rc.common
START=99
config_qbittorrent()
{
local download_dir
local port
config_get download_dir $1 download_dir
config_get port $1 port
if [ -f $2/qBittorrent/config/qBittorrent.conf ]; then
sed -i 's/WebUI\\Port=[0-9]*/WebUI\\Port='"$port"'/g' $2/qBittorrent/config/qBittorrent.conf
sed -i 's/Downloads\\SavePath=[a-z,/]*//g' $2/qBittorrent/config/qBittorrent.conf
echo "Downloads\SavePath=$down" >> $2/qBittorrent/config/qBittorrent.conf
else
cat>$2/qBittorrent/config/qBittorrent.conf<<EOF
[AutoRun]
enabled=false
program=
[LegalNotice]
Accepted=true
[Network]
Cookies=@Invalid()
[Preferences]
Downloads\SavePath=$download_dir
General\Locale=zh
WebUI\Port=$port
EOF
fi
}
run_qbittorrent()
{
local enable
config_get_bool enable $1 enable
if [ $enable ]; then
local profile_dir
config_get profile_dir $1 profile_dir
config_foreach config_qbittorrent 'Preferences' $profile_dir
if [ "$profile_dir" != "" ]; then
config_get library_dir $1 library_dir
config_get program_dir $1 program_dir
export PATH=$PATH:$program_dir
export LD_LIBRARY_PATH=$library_dir
qbittorrent-nox -d --profile=$profile_dir
else
echo "no profile_dir,stop!"
fi
fi
}
start()
{
local profile_path
config_load 'qbittorrent'
config_foreach run_qbittorrent 'basic'
}
stop()
{
killall qbittorrent-nox
return 0
}

View File

@ -0,0 +1,25 @@
# Copyright (C) 2016 Openwrt.org
#
# This is free software, licensed under the Apache License, Version 2.0 .
#
# licheng
# www.maxlicheng.com
# 2019-06-08
#
include $(TOPDIR)/rules.mk
LUCI_TITLE:=LuCI support for Unblock NeteaseCloudMusic
LUCI_DEPENDS:=+node
LUCI_PKGARCH:=all
PKG_NAME:=luci-app-unblockmusic
PKG_VERSION:=1
PKG_RELEASE:=36
PKG_MAINTAINER:=<https://github.com/maxlicheng/luci-app-unblockmusic>
include $(TOPDIR)/feeds/luci/luci.mk
# call BuildPackage - OpenWrt buildroot signature

View File

@ -0,0 +1,22 @@
module("luci.controller.unblockmusic", package.seeall)
function index()
if not nixio.fs.access("/etc/config/unblockmusic") then
return
end
entry({"admin", "services", "unblockmusic"},firstchild(), _("解锁网易云灰色歌曲"), 50).dependent = false
entry({"admin", "services", "unblockmusic", "general"},cbi("unblockmusic"), _("Base Setting"), 1)
entry({"admin", "services", "unblockmusic", "log"},form("unblockmusiclog"), _("Log"), 2)
entry({"admin", "services", "unblockmusic", "status"},call("act_status")).leaf=true
end
function act_status()
local e={}
e.running=luci.sys.call("ps | grep app.js | grep -v grep >/dev/null")==0
luci.http.prepare_content("application/json")
luci.http.write_json(e)
end

View File

@ -0,0 +1,29 @@
mp = Map("unblockmusic", translate("解锁网易云灰色歌曲"))
mp.description = translate("原理:采用 [网易云旧链/QQ/虾米/百度/酷狗/酷我/咕咪/JOOX]等音源 替换网易云变灰歌曲链接<br />具体使用方法可查看github<br />https://github.com/maxlicheng/luci-app-unblockmusic")
mp:section(SimpleSection).template = "unblockmusic/unblockmusic_status"
s = mp:section(TypedSection, "unblockmusic")
s.anonymous=true
s.addremove=false
enabled = s:option(Flag, "enabled", translate("启用解锁"))
enabled.default = 0
enabled.rmempty = false
speedtype = s:option(ListValue, "musicapptype", translate("音源选择"))
speedtype:value("default", translate("默认"))
speedtype:value("NeteaseCloud", translate("网易云音乐"))
speedtype:value("QQ", translate("QQ音乐"))
speedtype:value("xiami", translate("虾米音乐"))
speedtype:value("baidu", translate("百度音乐"))
speedtype:value("kugou", translate("酷狗音乐"))
speedtype:value("koowo", translate("酷我音乐"))
speedtype:value("migu", translate("咕咪音乐"))
speedtype:value("joox", translate("JOOX音乐"))
account = s:option(Value, "port", translate("端口号"))
account.datatype = "string"
return mp

View File

@ -0,0 +1,14 @@
local fs = require "nixio.fs"
local conffile = "/tmp/unblockmusic.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

@ -0,0 +1,22 @@
<script type="text/javascript">//<![CDATA[
XHR.poll(3, '<%=url([[admin]], [[services]], [[unblockmusic]], [[status]])%>', null,
function(x, data) {
var tb = document.getElementById('unblockmusic_status');
if (data && tb) {
if (data.running) {
var links = '<em><b><font color=green>Unblock NeteaseCloudMusic <%:RUNNING%></font></b></em>';
tb.innerHTML = links;
} else {
tb.innerHTML = '<em><b><font color=red>Unblock NeteaseCloudMusic <%:NOT RUNNING%></font></b></em>';
}
}
}
);
//]]>
</script>
<style>.mar-10 {margin-left: 50px; margin-right: 10px;}</style>
<fieldset class="cbi-section">
<p id="unblockmusic_status">
<em><%:Collecting data...%></em>
</p>
</fieldset>

View File

@ -0,0 +1,6 @@
config unblockmusic
option usicapptype 'default'
option port '5200'
option enabled '0'

View File

@ -0,0 +1,24 @@
#!/bin/sh /etc/rc.common
START=99
STOP=10
TYPE=$(uci get unblockmusic.@unblockmusic[0].musicapptype)
PORT=$(uci get unblockmusic.@unblockmusic[0].port)
start()
{
stop
enable=$(uci get unblockmusic.@unblockmusic[0].enabled)
[ $enable -eq 0 ] && exit 0
node /usr/share/unblockmusic/app.js -p $PORT &
}
stop()
{
kill -9 $(ps | grep app.js | grep -v grep | awk '{print $1}') >/dev/null 2>&1
}

View File

@ -0,0 +1,3 @@
#!/bin/sh
sleep 60 && /etc/init.d/unblockmusic restart

View File

@ -0,0 +1,11 @@
#!/bin/sh
uci -q batch <<-EOF >/dev/null
delete ucitrack.@unblockmusic[-1]
add ucitrack unblockmusic
set ucitrack.@unblockmusic[-1].init=unblockmusic
commit ucitrack
EOF
rm -f /tmp/luci-indexcache
exit 0

View File

@ -0,0 +1,15 @@
.git
.vscode
.npmignore
.gitignore
.dockerignore
LICENSE
Procfile
README.md
node_modules
npm-debug.log
Dockerfile*
docker-compose*

View File

@ -0,0 +1,72 @@
# IDE
.vscode
.idea
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
# nyc test coverage
.nyc_output
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# TypeScript v1 declaration files
typings/
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variables file
.env
# next.js build output
.next
# pkg dist directory
dist/
# es6 transformation
browser/provider
browser/cache.js

View File

@ -0,0 +1,12 @@
FROM node:lts-alpine
ENV NODE_ENV production
WORKDIR /usr/src/app
COPY package*.json ./
RUN npm install --production
COPY . .
EXPOSE 8080
ENTRYPOINT ["node", "app.js"]

View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2018 Nzix
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -0,0 +1,190 @@
<img src="https://user-images.githubusercontent.com/26399680/47980314-0e3f1700-e102-11e8-8857-e3436ecc8beb.png" alt="logo" width="140" height="140" align="right">
# UnblockNeteaseMusic
解锁网易云音乐客户端变灰歌曲
## 特性
- 使用网易云旧链 / QQ / 虾米 / 百度 / 酷狗 / 酷我 / 咕咪 / JOOX 音源替换变灰歌曲链接 (默认仅启用前四)
- 为请求增加 `X-Real-IP` 参数解锁海外限制,支持指定网易云服务器 IP支持设置上游 HTTP / HTTPS 代理
- 完整的流量代理功能 (HTTP / HTTPS),可直接作为系统代理 (同时支持 PAC)
## 运行
从源码运行
```
$ node app.js
```
或使用 Docker
```
$ docker run nondanee/unblockneteasemusic
```
```
$ docker-compose up
```
### 配置参数
```
$ node app.js -h
usage: unblockneteasemusic [-v] [-p port] [-u url] [-f host]
[-o source [source ...]] [-t token] [-e url] [-s]
[-h]
optional arguments:
-v, --version output the version number
-p port, --port port specify server port
-u url, --proxy-url url request through upstream proxy
-f host, --force-host host force the netease server ip
-o source [source ...], --match-order source [source ...]
set priority of sources
-t token, --token token set up http basic authentication
-e url, --endpoint url replace virtual endpoint with public host
-s, --strict enable proxy limitation
-h, --help output usage information
```
## 使用
**警告:本项目不提供线上 demo请不要轻易信任使用他人提供的公开代理服务以免发生安全问题**
**若将服务部署到公网,强烈建议使用严格模式 (此模式下仅放行网易云音乐所属域名的请求) `-s` 限制代理范围 (需使用 PAC 或 hosts)~~或启用 Proxy Authentication `-t <name>:<password>` 设置代理用户名密码~~ (目前密码认证在 Windows 客户端设置和 macOS 系统设置都无法生效,请不要使用),以防代理被他人滥用**
支持 Windows 客户端UWP 客户端Linux 客户端 (1.2 版本以上需要自签证书 MITM启动客户端需要增加 `--ignore-certificate-errors` 参数)macOS 客户端 (726 版本以上需要自签证书)iOS 客户端 (配置 https endpoint 或使用自签证书)Android 客户端和网页版 (需要自签证书,需要脚本配合)
目前除 UWP 外其它客户端均优先请求 HTTPS 接口,默认配置下本代理对网易云所有 HTTPS API 连接返回空数据,促使客户端降级使用 HTTP 接口 (新版 Linux 客户端和 macOS 客户端已无法降级)
因 UWP 应用存在网络隔离,限制流量发送到本机,若使用的代理在 localhost或修改的 hosts 指向 localhost需为 "网易云音乐 UWP" 手动开启 loopback 才能使用,请以**管理员身份**执行命令
```powershell
checknetisolation loopbackexempt -a -n="1F8B0F94.122165AE053F_j2p0p5q0044a6"
```
### 方法 1. 修改 hosts
向 hosts 文件添加两条规则
```
<Server IP> music.163.com
<Server IP> interface.music.163.com
```
> 使用此方法必须监听 80 端口 `-p 80`
>
> **若在本机运行程序**,请指定网易云服务器 IP `-f xxx.xxx.xxx.xxx` (可在修改 hosts 前通过 `ping music.163.com` 获得) **或** 使用代理 `-u http(s)://xxx.xxx.xxx.xxx:xxx`,以防请求死循环
>
> **Android 客户端下修改 hosts 无法直接使用**,原因和解决方法详见[云音乐安卓又搞事啦](https://jixun.moe/post/netease-android-hosts-bypass/)[安卓免 root 绕过网易云音乐 IP 限制](https://jixun.moe/post/android-block-netease-without-root/)
### 方法 2. 设置代理
PAC 自动代理脚本地址 `http://<Server Name:PORT>/proxy.pac`
全局代理地址填写服务器地址和端口号即可
| 平台 | 基础设置 |
| :------ | :------------------------------- |
| Windows | 设置 > 工具 > 自定义代理 (客户端内) |
| UWP | Windows 设置 > 网络和 Internet > 代理 |
| Linux | 系统设置 > 网络 > 网络代理 |
| macOS | 系统偏好设置 > 网络 > 高级 > 代理 |
| Android | WLAN > 修改网络 > 高级选项 > 代理 |
| iOS | 无线局域网 > HTTP 代理 > 配置代理 |
> 代理工具和方法有很多请自行探索,欢迎在 issues 讨论
### ✳方法 3. 调用接口
作为依赖库使用
```
$ npm install nondanee/UnblockNeteaseMusic
```
```javascript
const match = require('unblockneteasemusic')
/**
* Set proxy or hosts if needed
*/
global.proxy = require('url').parse('http://127.0.0.1:1080')
global.hosts = {'i.y.qq.com': '59.37.96.220'}
/**
* Find matching song from other platforms
* @param {Number} id netease song id
* @param {Array<String>||undefined} source support netease, qq, xiami, baidu, kugou, kuwo, migu, joox
* @return {Promise<Object>}
*/
match(418602084, ['netease', 'qq', 'xiami', 'baidu']).then(song => console.log(song))
```
## 效果
#### Windows 客户端
<img src="https://user-images.githubusercontent.com/26399680/57972590-425b9480-79cf-11e9-9761-b46a12d36249.png" width="100%">
#### UWP 客户端
<img src="https://user-images.githubusercontent.com/26399680/52215123-5a028780-28ce-11e9-8491-08c4c5dac3b4.png" width="100%">
#### Linux 客户端
<img src="https://user-images.githubusercontent.com/26399680/52214856-a7cac000-28cd-11e9-92dd-0c41dc619481.png" width="100%">
#### macOS 客户端
<img src="https://user-images.githubusercontent.com/26399680/52196035-51418f80-2895-11e9-8f33-78a631cdf151.png" width="100%">
#### Android 客户端
<img src="https://user-images.githubusercontent.com/26399680/57972549-eabd2900-79ce-11e9-8fef-95cb60906298.png" width="50%">
#### iOS 客户端
<img src="https://user-images.githubusercontent.com/26399680/57972440-f90a4580-79cc-11e9-8dbf-6150ee299b9c.jpg" width="50%">
## 致谢
感谢大佬们为逆向 eapi 所做的努力
使用的其它平台音源 API 出自
[trazyn/ieaseMusic](https://github.com/trazyn/ieaseMusic)
[listen1/listen1_chrome_extension](https://github.com/listen1/listen1_chrome_extension)
向所有同类项目致敬
[EraserKing/CloudMusicGear](https://github.com/EraserKing/CloudMusicGear)
[EraserKing/Unblock163MusicClient](https://github.com/EraserKing/Unblock163MusicClient)
[ITJesse/UnblockNeteaseMusic](https://github.com/ITJesse/UnblockNeteaseMusic/)
[bin456789/Unblock163MusicClient-Xposed](https://github.com/bin456789/Unblock163MusicClient-Xposed)
[YiuChoi/Unlock163Music](https://github.com/YiuChoi/Unlock163Music)
[yi-ji/NeteaseMusicAbroad](https://github.com/yi-ji/NeteaseMusicAbroad)
[stomakun/NeteaseReverseLadder](https://github.com/stomakun/NeteaseReverseLadder/)
[fengjueming/unblock-NetEaseMusic](https://github.com/fengjueming/unblock-NetEaseMusic)
[acgotaku/NetEaseMusicWorld](https://github.com/acgotaku/NetEaseMusicWorld)
[mengskysama/163-Cloud-Music-Unlock](https://github.com/mengskysama/163-Cloud-Music-Unlock)
[azureplus/163-music-unlock](https://github.com/azureplus/163-music-unlock)
[typcn/163music-mac-client-unlock](https://github.com/typcn/163music-mac-client-unlock)
## 许可
The MIT License

View File

@ -0,0 +1,84 @@
#!/usr/bin/env node
const package = require('./package.json')
const config = require('./cli.js')
.program({name: package.name, version: package.version})
.option(['-v', '--version'], {action: 'version'})
.option(['-p', '--port'], {metavar: 'port', help: 'specify server port'})
.option(['-u', '--proxy-url'], {metavar: 'url', help: 'request through upstream proxy'})
.option(['-f', '--force-host'], {metavar: 'host', help: 'force the netease server ip'})
.option(['-o', '--match-order'], {metavar: 'source', nargs: '+', help: 'set priority of sources'})
.option(['-t', '--token'], {metavar: 'token', help: 'set up http basic authentication'})
.option(['-e', '--endpoint'], {metavar: 'url', help: 'replace virtual endpoint with public host'})
.option(['-s', '--strict'], {action: 'store_true', help: 'enable proxy limitation'})
.option(['-h', '--help'], {action: 'help'})
.parse(process.argv)
config.port = (config.port || '8080').split(':').map(string => parseInt(string))
const invalid = value => (isNaN(value) || value < 1 || value > 65535)
if(config.port.some(invalid)){
console.log('Port must be a number higher than 0 and lower than 65535.')
process.exit(1)
}
if(config.proxyUrl && !/http(s?):\/\/.+:\d+/.test(config.proxyUrl)){
console.log('Please check the proxy url.')
process.exit(1)
}
if(config.endpoint && !/http(s?):\/\/.+/.test(config.endpoint)){
console.log('Please check the endpoint host.')
process.exit(1)
}
if(config.forceHost && !/\d+\.\d+\.\d+\.\d+/.test(config.forceHost)){
console.log('Please check the server host.')
process.exit(1)
}
if(config.matchOrder){
const provider = ['netease', 'qq', 'xiami', 'baidu', 'kugou', 'kuwo', 'migu', 'joox']
const candidate = config.matchOrder
if(candidate.some((key, index) => index != candidate.indexOf(key))){
console.log('Please check the duplication in match order.')
process.exit(1)
}
else if(candidate.some(key => !provider.includes(key))){
console.log('Please check the availability of match sources.')
process.exit(1)
}
global.source = candidate
}
if(config.token && !/\S+:\S+/.test(config.token)){
console.log('Please check the authentication token.')
process.exit(1)
}
const parse = require('url').parse
const hook = require('./hook')
const server = require('./server')
const escape = string => string.replace(/\./g, '\\.')
global.port = config.port
global.proxy = config.proxyUrl ? parse(config.proxyUrl) : null
global.hosts = {}, hook.target.host.forEach(host => global.hosts[host] = config.forceHost)
server.whitelist = ['music.126.net', 'vod.126.net'].map(escape)
if(config.strict) server.blacklist.push('.*')
server.authentication = config.token || null
global.endpoint = config.endpoint
if(config.endpoint) server.whitelist.push(escape(config.endpoint))
const dns = host => new Promise((resolve, reject) => require('dns').lookup(host, {all: true}, (error, records) => error? reject(error) : resolve(records.map(record => record.address))))
const httpdns = host => require('./request')('POST', 'http://music.httpdns.c.163.com/d', {}, host).then(response => response.json()).then(jsonBody => jsonBody.dns[0].ips)
Promise.all([httpdns(hook.target.host[0])].concat(hook.target.host.map(host => dns(host))))
.then(result => {
let extra = Array.from(new Set(result.reduce((merged, array) => merged.concat(array), [])))
hook.target.host = hook.target.host.concat(extra)
server.whitelist = server.whitelist.concat(hook.target.host.map(escape))
if(port[0]){
server.http.listen(port[0])
console.log(`HTTP Server running @ http://0.0.0.0:${port[0]}`)
}
if(port[1]){
server.https.listen(port[1])
console.log(`HTTPS Server running @ https://0.0.0.0:${port[1]}`)
}
})
.catch(error => console.log(error))

View File

@ -0,0 +1,33 @@
#!/usr/bin/env node
const cache = require('./cache')
const parse = require('url').parse
const router = {
qq: require('./provider/qq'),
xiami: require('./provider/xiami'),
baidu: require('./provider/baidu'),
kugou: require('./provider/kugou'),
kuwo: require('./provider/kuwo'),
migu: require('./provider/migu'),
joox: require('./provider/joox')
}
const distributor = (url, router) => Promise.resolve().then(() => {
const route = url.pathname.slice(1).split('/').map(path => decodeURIComponent(path))
let pointer = router, argument = decodeURIComponent(url.query)
try{argument = JSON.parse(argument)}catch(e){}
const miss = route.some(path => {
if(path in pointer) pointer = pointer[path]
else return true
})
if(miss || typeof pointer != 'function') return Promise.reject()
//return pointer.call(null, argument)
return cache(pointer, argument, 15 * 60 * 1000)
})
require('http').createServer().on('request', (req, res) =>
distributor(parse(req.url), router)
.then(data => res.write(data))
.catch(() => res.writeHead(404))
.then(() => res.end())
).listen(parseInt(process.argv[2]) || 9000)

View File

@ -0,0 +1,25 @@
# Web Extension Port
For test
## Implementation
- Convert node module to ES6 module which can be directly executed in Chrome 61+ without Babel
- Rewrite crypto module (using CryptoJS) and request (using XMLHttpRequest) module for browser environment
- Do matching in background and transfer result with chrome runtime communication
- Inject content script for hijacking Netease Music Web Ajax response
## Build
```
$ node convert.js
```
## Install
Load unpacked extension in Developer mode
## Reference
- [brix/crypto-js](https://github.com/brix/crypto-js)
- [JixunMoe/cuwcl4c](https://github.com/JixunMoe/cuwcl4c)

View File

@ -0,0 +1,4 @@
<script src="./crypto-js/core.js"></script>
<script src="./crypto-js/md5.js"></script>
<script src="./crypto-js/enc-base64.js"></script>
<script type="module" src="background.js"></script>

View File

@ -0,0 +1,22 @@
import match from './provider/match.js'
chrome.runtime.onMessageExternal.addListener((request, sender, sendResponse) => {
match(request.match, ['netease', 'qq', 'xiami'])
.then(song => sendResponse(song))
.catch(e => console.log(e))
return true
})
chrome.webRequest.onBeforeSendHeaders.addListener(details => {
let headers = details.requestHeaders
headers.push({name: 'X-Real-IP', value: '118.88.88.88'})
return {requestHeaders: headers}
}, {urls: ['*://music.163.com/*']}, ['blocking', 'requestHeaders'])
chrome.webRequest.onHeadersReceived.addListener(details => {
let headers = details.responseHeaders
if(details.initiator == "https://music.163.com" && details.type == 'media'){
headers.push({name: 'Access-Control-Allow-Origin', value: '*'})
}
return {responseHeaders: headers}
}, {urls: ['*://*/*']}, ['blocking', 'responseHeaders'])

View File

@ -0,0 +1,43 @@
const fs = require('fs')
const path = require('path')
const importReplacer = (match, state, alias, file) => {
file = file.endsWith('.js') ? file : file + '.js'
return `import ${alias} from '${file}'`
}
const converter = (input, output, processor) => {
let data = fs.readFileSync(input).toString()
if(processor){
data = processor(data)
}
else{
data = data.replace(/global\./g, 'window.')
data = data.replace(/(const|let|var)\s+(\w+)\s*=\s*require\(\s*['|"](.+)['|"]\s*\)/g, importReplacer)
data = data.replace(/module\.exports\s*=\s*/g, 'export default ')
}
fs.writeFileSync(output, data)
}
converter(path.resolve(__dirname, '..', 'cache.js'), path.resolve(__dirname, '.', 'cache.js'))
if(!fs.existsSync(path.resolve(__dirname, 'provider'))) fs.mkdirSync(path.resolve(__dirname, 'provider'))
fs.readdirSync(path.resolve(__dirname, '..', 'provider')).filter(file => !file.includes('test')).forEach(file => {
converter(path.resolve(__dirname, '..', 'provider', file), path.resolve(__dirname, 'provider', file))
})
const providerReplacer = (match, state, data) => {
let provider = []
let imports = data.match(/\w+\s*:\s*require\(['|"].+['|"]\)/g).map(line => {
line = line.match(/(\w+)\s*:\s*require\(['|"](.+)['|"]\)/)
provider.push(line[1])
return importReplacer(null, null, line[1], line[2])
})
return imports.join('\n') + '\n\n' + `${state} provider = {${provider.join(', ')}}`
}
converter(path.resolve(__dirname, 'provider', 'match.js'), path.resolve(__dirname, 'provider', 'match.js'), data => {
data = data.replace(/(const|let|var)\s+provider\s*=\s*{([^}]+)}/g, providerReplacer)
return data
})

View File

@ -0,0 +1,760 @@
;(function (root, factory) {
if (typeof exports === "object") {
// CommonJS
module.exports = exports = factory();
}
else if (typeof define === "function" && define.amd) {
// AMD
define([], factory);
}
else {
// Global (browser)
root.CryptoJS = factory();
}
}(this, function () {
/**
* CryptoJS core components.
*/
var CryptoJS = CryptoJS || (function (Math, undefined) {
/*
* Local polyfil of Object.create
*/
var create = Object.create || (function () {
function F() {};
return function (obj) {
var subtype;
F.prototype = obj;
subtype = new F();
F.prototype = null;
return subtype;
};
}())
/**
* CryptoJS namespace.
*/
var C = {};
/**
* Library namespace.
*/
var C_lib = C.lib = {};
/**
* Base object for prototypal inheritance.
*/
var Base = C_lib.Base = (function () {
return {
/**
* Creates a new object that inherits from this object.
*
* @param {Object} overrides Properties to copy into the new object.
*
* @return {Object} The new object.
*
* @static
*
* @example
*
* var MyType = CryptoJS.lib.Base.extend({
* field: 'value',
*
* method: function () {
* }
* });
*/
extend: function (overrides) {
// Spawn
var subtype = create(this);
// Augment
if (overrides) {
subtype.mixIn(overrides);
}
// Create default initializer
if (!subtype.hasOwnProperty('init') || this.init === subtype.init) {
subtype.init = function () {
subtype.$super.init.apply(this, arguments);
};
}
// Initializer's prototype is the subtype object
subtype.init.prototype = subtype;
// Reference supertype
subtype.$super = this;
return subtype;
},
/**
* Extends this object and runs the init method.
* Arguments to create() will be passed to init().
*
* @return {Object} The new object.
*
* @static
*
* @example
*
* var instance = MyType.create();
*/
create: function () {
var instance = this.extend();
instance.init.apply(instance, arguments);
return instance;
},
/**
* Initializes a newly created object.
* Override this method to add some logic when your objects are created.
*
* @example
*
* var MyType = CryptoJS.lib.Base.extend({
* init: function () {
* // ...
* }
* });
*/
init: function () {
},
/**
* Copies properties into this object.
*
* @param {Object} properties The properties to mix in.
*
* @example
*
* MyType.mixIn({
* field: 'value'
* });
*/
mixIn: function (properties) {
for (var propertyName in properties) {
if (properties.hasOwnProperty(propertyName)) {
this[propertyName] = properties[propertyName];
}
}
// IE won't copy toString using the loop above
if (properties.hasOwnProperty('toString')) {
this.toString = properties.toString;
}
},
/**
* Creates a copy of this object.
*
* @return {Object} The clone.
*
* @example
*
* var clone = instance.clone();
*/
clone: function () {
return this.init.prototype.extend(this);
}
};
}());
/**
* An array of 32-bit words.
*
* @property {Array} words The array of 32-bit words.
* @property {number} sigBytes The number of significant bytes in this word array.
*/
var WordArray = C_lib.WordArray = Base.extend({
/**
* Initializes a newly created word array.
*
* @param {Array} words (Optional) An array of 32-bit words.
* @param {number} sigBytes (Optional) The number of significant bytes in the words.
*
* @example
*
* var wordArray = CryptoJS.lib.WordArray.create();
* var wordArray = CryptoJS.lib.WordArray.create([0x00010203, 0x04050607]);
* var wordArray = CryptoJS.lib.WordArray.create([0x00010203, 0x04050607], 6);
*/
init: function (words, sigBytes) {
words = this.words = words || [];
if (sigBytes != undefined) {
this.sigBytes = sigBytes;
} else {
this.sigBytes = words.length * 4;
}
},
/**
* Converts this word array to a string.
*
* @param {Encoder} encoder (Optional) The encoding strategy to use. Default: CryptoJS.enc.Hex
*
* @return {string} The stringified word array.
*
* @example
*
* var string = wordArray + '';
* var string = wordArray.toString();
* var string = wordArray.toString(CryptoJS.enc.Utf8);
*/
toString: function (encoder) {
return (encoder || Hex).stringify(this);
},
/**
* Concatenates a word array to this word array.
*
* @param {WordArray} wordArray The word array to append.
*
* @return {WordArray} This word array.
*
* @example
*
* wordArray1.concat(wordArray2);
*/
concat: function (wordArray) {
// Shortcuts
var thisWords = this.words;
var thatWords = wordArray.words;
var thisSigBytes = this.sigBytes;
var thatSigBytes = wordArray.sigBytes;
// Clamp excess bits
this.clamp();
// Concat
if (thisSigBytes % 4) {
// Copy one byte at a time
for (var i = 0; i < thatSigBytes; i++) {
var thatByte = (thatWords[i >>> 2] >>> (24 - (i % 4) * 8)) & 0xff;
thisWords[(thisSigBytes + i) >>> 2] |= thatByte << (24 - ((thisSigBytes + i) % 4) * 8);
}
} else {
// Copy one word at a time
for (var i = 0; i < thatSigBytes; i += 4) {
thisWords[(thisSigBytes + i) >>> 2] = thatWords[i >>> 2];
}
}
this.sigBytes += thatSigBytes;
// Chainable
return this;
},
/**
* Removes insignificant bits.
*
* @example
*
* wordArray.clamp();
*/
clamp: function () {
// Shortcuts
var words = this.words;
var sigBytes = this.sigBytes;
// Clamp
words[sigBytes >>> 2] &= 0xffffffff << (32 - (sigBytes % 4) * 8);
words.length = Math.ceil(sigBytes / 4);
},
/**
* Creates a copy of this word array.
*
* @return {WordArray} The clone.
*
* @example
*
* var clone = wordArray.clone();
*/
clone: function () {
var clone = Base.clone.call(this);
clone.words = this.words.slice(0);
return clone;
},
/**
* Creates a word array filled with random bytes.
*
* @param {number} nBytes The number of random bytes to generate.
*
* @return {WordArray} The random word array.
*
* @static
*
* @example
*
* var wordArray = CryptoJS.lib.WordArray.random(16);
*/
random: function (nBytes) {
var words = [];
var r = (function (m_w) {
var m_w = m_w;
var m_z = 0x3ade68b1;
var mask = 0xffffffff;
return function () {
m_z = (0x9069 * (m_z & 0xFFFF) + (m_z >> 0x10)) & mask;
m_w = (0x4650 * (m_w & 0xFFFF) + (m_w >> 0x10)) & mask;
var result = ((m_z << 0x10) + m_w) & mask;
result /= 0x100000000;
result += 0.5;
return result * (Math.random() > .5 ? 1 : -1);
}
});
for (var i = 0, rcache; i < nBytes; i += 4) {
var _r = r((rcache || Math.random()) * 0x100000000);
rcache = _r() * 0x3ade67b7;
words.push((_r() * 0x100000000) | 0);
}
return new WordArray.init(words, nBytes);
}
});
/**
* Encoder namespace.
*/
var C_enc = C.enc = {};
/**
* Hex encoding strategy.
*/
var Hex = C_enc.Hex = {
/**
* Converts a word array to a hex string.
*
* @param {WordArray} wordArray The word array.
*
* @return {string} The hex string.
*
* @static
*
* @example
*
* var hexString = CryptoJS.enc.Hex.stringify(wordArray);
*/
stringify: function (wordArray) {
// Shortcuts
var words = wordArray.words;
var sigBytes = wordArray.sigBytes;
// Convert
var hexChars = [];
for (var i = 0; i < sigBytes; i++) {
var bite = (words[i >>> 2] >>> (24 - (i % 4) * 8)) & 0xff;
hexChars.push((bite >>> 4).toString(16));
hexChars.push((bite & 0x0f).toString(16));
}
return hexChars.join('');
},
/**
* Converts a hex string to a word array.
*
* @param {string} hexStr The hex string.
*
* @return {WordArray} The word array.
*
* @static
*
* @example
*
* var wordArray = CryptoJS.enc.Hex.parse(hexString);
*/
parse: function (hexStr) {
// Shortcut
var hexStrLength = hexStr.length;
// Convert
var words = [];
for (var i = 0; i < hexStrLength; i += 2) {
words[i >>> 3] |= parseInt(hexStr.substr(i, 2), 16) << (24 - (i % 8) * 4);
}
return new WordArray.init(words, hexStrLength / 2);
}
};
/**
* Latin1 encoding strategy.
*/
var Latin1 = C_enc.Latin1 = {
/**
* Converts a word array to a Latin1 string.
*
* @param {WordArray} wordArray The word array.
*
* @return {string} The Latin1 string.
*
* @static
*
* @example
*
* var latin1String = CryptoJS.enc.Latin1.stringify(wordArray);
*/
stringify: function (wordArray) {
// Shortcuts
var words = wordArray.words;
var sigBytes = wordArray.sigBytes;
// Convert
var latin1Chars = [];
for (var i = 0; i < sigBytes; i++) {
var bite = (words[i >>> 2] >>> (24 - (i % 4) * 8)) & 0xff;
latin1Chars.push(String.fromCharCode(bite));
}
return latin1Chars.join('');
},
/**
* Converts a Latin1 string to a word array.
*
* @param {string} latin1Str The Latin1 string.
*
* @return {WordArray} The word array.
*
* @static
*
* @example
*
* var wordArray = CryptoJS.enc.Latin1.parse(latin1String);
*/
parse: function (latin1Str) {
// Shortcut
var latin1StrLength = latin1Str.length;
// Convert
var words = [];
for (var i = 0; i < latin1StrLength; i++) {
words[i >>> 2] |= (latin1Str.charCodeAt(i) & 0xff) << (24 - (i % 4) * 8);
}
return new WordArray.init(words, latin1StrLength);
}
};
/**
* UTF-8 encoding strategy.
*/
var Utf8 = C_enc.Utf8 = {
/**
* Converts a word array to a UTF-8 string.
*
* @param {WordArray} wordArray The word array.
*
* @return {string} The UTF-8 string.
*
* @static
*
* @example
*
* var utf8String = CryptoJS.enc.Utf8.stringify(wordArray);
*/
stringify: function (wordArray) {
try {
return decodeURIComponent(escape(Latin1.stringify(wordArray)));
} catch (e) {
throw new Error('Malformed UTF-8 data');
}
},
/**
* Converts a UTF-8 string to a word array.
*
* @param {string} utf8Str The UTF-8 string.
*
* @return {WordArray} The word array.
*
* @static
*
* @example
*
* var wordArray = CryptoJS.enc.Utf8.parse(utf8String);
*/
parse: function (utf8Str) {
return Latin1.parse(unescape(encodeURIComponent(utf8Str)));
}
};
/**
* Abstract buffered block algorithm template.
*
* The property blockSize must be implemented in a concrete subtype.
*
* @property {number} _minBufferSize The number of blocks that should be kept unprocessed in the buffer. Default: 0
*/
var BufferedBlockAlgorithm = C_lib.BufferedBlockAlgorithm = Base.extend({
/**
* Resets this block algorithm's data buffer to its initial state.
*
* @example
*
* bufferedBlockAlgorithm.reset();
*/
reset: function () {
// Initial values
this._data = new WordArray.init();
this._nDataBytes = 0;
},
/**
* Adds new data to this block algorithm's buffer.
*
* @param {WordArray|string} data The data to append. Strings are converted to a WordArray using UTF-8.
*
* @example
*
* bufferedBlockAlgorithm._append('data');
* bufferedBlockAlgorithm._append(wordArray);
*/
_append: function (data) {
// Convert string to WordArray, else assume WordArray already
if (typeof data == 'string') {
data = Utf8.parse(data);
}
// Append
this._data.concat(data);
this._nDataBytes += data.sigBytes;
},
/**
* Processes available data blocks.
*
* This method invokes _doProcessBlock(offset), which must be implemented by a concrete subtype.
*
* @param {boolean} doFlush Whether all blocks and partial blocks should be processed.
*
* @return {WordArray} The processed data.
*
* @example
*
* var processedData = bufferedBlockAlgorithm._process();
* var processedData = bufferedBlockAlgorithm._process(!!'flush');
*/
_process: function (doFlush) {
// Shortcuts
var data = this._data;
var dataWords = data.words;
var dataSigBytes = data.sigBytes;
var blockSize = this.blockSize;
var blockSizeBytes = blockSize * 4;
// Count blocks ready
var nBlocksReady = dataSigBytes / blockSizeBytes;
if (doFlush) {
// Round up to include partial blocks
nBlocksReady = Math.ceil(nBlocksReady);
} else {
// Round down to include only full blocks,
// less the number of blocks that must remain in the buffer
nBlocksReady = Math.max((nBlocksReady | 0) - this._minBufferSize, 0);
}
// Count words ready
var nWordsReady = nBlocksReady * blockSize;
// Count bytes ready
var nBytesReady = Math.min(nWordsReady * 4, dataSigBytes);
// Process blocks
if (nWordsReady) {
for (var offset = 0; offset < nWordsReady; offset += blockSize) {
// Perform concrete-algorithm logic
this._doProcessBlock(dataWords, offset);
}
// Remove processed words
var processedWords = dataWords.splice(0, nWordsReady);
data.sigBytes -= nBytesReady;
}
// Return processed words
return new WordArray.init(processedWords, nBytesReady);
},
/**
* Creates a copy of this object.
*
* @return {Object} The clone.
*
* @example
*
* var clone = bufferedBlockAlgorithm.clone();
*/
clone: function () {
var clone = Base.clone.call(this);
clone._data = this._data.clone();
return clone;
},
_minBufferSize: 0
});
/**
* Abstract hasher template.
*
* @property {number} blockSize The number of 32-bit words this hasher operates on. Default: 16 (512 bits)
*/
var Hasher = C_lib.Hasher = BufferedBlockAlgorithm.extend({
/**
* Configuration options.
*/
cfg: Base.extend(),
/**
* Initializes a newly created hasher.
*
* @param {Object} cfg (Optional) The configuration options to use for this hash computation.
*
* @example
*
* var hasher = CryptoJS.algo.SHA256.create();
*/
init: function (cfg) {
// Apply config defaults
this.cfg = this.cfg.extend(cfg);
// Set initial values
this.reset();
},
/**
* Resets this hasher to its initial state.
*
* @example
*
* hasher.reset();
*/
reset: function () {
// Reset data buffer
BufferedBlockAlgorithm.reset.call(this);
// Perform concrete-hasher logic
this._doReset();
},
/**
* Updates this hasher with a message.
*
* @param {WordArray|string} messageUpdate The message to append.
*
* @return {Hasher} This hasher.
*
* @example
*
* hasher.update('message');
* hasher.update(wordArray);
*/
update: function (messageUpdate) {
// Append
this._append(messageUpdate);
// Update the hash
this._process();
// Chainable
return this;
},
/**
* Finalizes the hash computation.
* Note that the finalize operation is effectively a destructive, read-once operation.
*
* @param {WordArray|string} messageUpdate (Optional) A final message update.
*
* @return {WordArray} The hash.
*
* @example
*
* var hash = hasher.finalize();
* var hash = hasher.finalize('message');
* var hash = hasher.finalize(wordArray);
*/
finalize: function (messageUpdate) {
// Final message update
if (messageUpdate) {
this._append(messageUpdate);
}
// Perform concrete-hasher logic
var hash = this._doFinalize();
return hash;
},
blockSize: 512/32,
/**
* Creates a shortcut function to a hasher's object interface.
*
* @param {Hasher} hasher The hasher to create a helper for.
*
* @return {Function} The shortcut function.
*
* @static
*
* @example
*
* var SHA256 = CryptoJS.lib.Hasher._createHelper(CryptoJS.algo.SHA256);
*/
_createHelper: function (hasher) {
return function (message, cfg) {
return new hasher.init(cfg).finalize(message);
};
},
/**
* Creates a shortcut function to the HMAC's object interface.
*
* @param {Hasher} hasher The hasher to use in this HMAC helper.
*
* @return {Function} The shortcut function.
*
* @static
*
* @example
*
* var HmacSHA256 = CryptoJS.lib.Hasher._createHmacHelper(CryptoJS.algo.SHA256);
*/
_createHmacHelper: function (hasher) {
return function (message, key) {
return new C_algo.HMAC.init(hasher, key).finalize(message);
};
}
});
/**
* Algorithm namespace.
*/
var C_algo = C.algo = {};
return C;
}(Math));
return CryptoJS;
}));

View File

@ -0,0 +1,135 @@
;(function (root, factory) {
if (typeof exports === "object") {
// CommonJS
module.exports = exports = factory(require("./core"));
}
else if (typeof define === "function" && define.amd) {
// AMD
define(["./core"], factory);
}
else {
// Global (browser)
factory(root.CryptoJS);
}
}(this, function (CryptoJS) {
(function () {
// Shortcuts
var C = CryptoJS;
var C_lib = C.lib;
var WordArray = C_lib.WordArray;
var C_enc = C.enc;
/**
* Base64 encoding strategy.
*/
var Base64 = C_enc.Base64 = {
/**
* Converts a word array to a Base64 string.
*
* @param {WordArray} wordArray The word array.
*
* @return {string} The Base64 string.
*
* @static
*
* @example
*
* var base64String = CryptoJS.enc.Base64.stringify(wordArray);
*/
stringify: function (wordArray) {
// Shortcuts
var words = wordArray.words;
var sigBytes = wordArray.sigBytes;
var map = this._map;
// Clamp excess bits
wordArray.clamp();
// Convert
var base64Chars = [];
for (var i = 0; i < sigBytes; i += 3) {
var byte1 = (words[i >>> 2] >>> (24 - (i % 4) * 8)) & 0xff;
var byte2 = (words[(i + 1) >>> 2] >>> (24 - ((i + 1) % 4) * 8)) & 0xff;
var byte3 = (words[(i + 2) >>> 2] >>> (24 - ((i + 2) % 4) * 8)) & 0xff;
var triplet = (byte1 << 16) | (byte2 << 8) | byte3;
for (var j = 0; (j < 4) && (i + j * 0.75 < sigBytes); j++) {
base64Chars.push(map.charAt((triplet >>> (6 * (3 - j))) & 0x3f));
}
}
// Add padding
var paddingChar = map.charAt(64);
if (paddingChar) {
while (base64Chars.length % 4) {
base64Chars.push(paddingChar);
}
}
return base64Chars.join('');
},
/**
* Converts a Base64 string to a word array.
*
* @param {string} base64Str The Base64 string.
*
* @return {WordArray} The word array.
*
* @static
*
* @example
*
* var wordArray = CryptoJS.enc.Base64.parse(base64String);
*/
parse: function (base64Str) {
// Shortcuts
var base64StrLength = base64Str.length;
var map = this._map;
var reverseMap = this._reverseMap;
if (!reverseMap) {
reverseMap = this._reverseMap = [];
for (var j = 0; j < map.length; j++) {
reverseMap[map.charCodeAt(j)] = j;
}
}
// Ignore padding
var paddingChar = map.charAt(64);
if (paddingChar) {
var paddingIndex = base64Str.indexOf(paddingChar);
if (paddingIndex !== -1) {
base64StrLength = paddingIndex;
}
}
// Convert
return parseLoop(base64Str, base64StrLength, reverseMap);
},
_map: 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='
};
function parseLoop(base64Str, base64StrLength, reverseMap) {
var words = [];
var nBytes = 0;
for (var i = 0; i < base64StrLength; i++) {
if (i % 4) {
var bits1 = reverseMap[base64Str.charCodeAt(i - 1)] << ((i % 4) * 2);
var bits2 = reverseMap[base64Str.charCodeAt(i)] >>> (6 - (i % 4) * 2);
words[nBytes >>> 2] |= (bits1 | bits2) << (24 - (nBytes % 4) * 8);
nBytes++;
}
}
return WordArray.create(words, nBytes);
}
}());
return CryptoJS.enc.Base64;
}));

View File

@ -0,0 +1,268 @@
;(function (root, factory) {
if (typeof exports === "object") {
// CommonJS
module.exports = exports = factory(require("./core"));
}
else if (typeof define === "function" && define.amd) {
// AMD
define(["./core"], factory);
}
else {
// Global (browser)
factory(root.CryptoJS);
}
}(this, function (CryptoJS) {
(function (Math) {
// Shortcuts
var C = CryptoJS;
var C_lib = C.lib;
var WordArray = C_lib.WordArray;
var Hasher = C_lib.Hasher;
var C_algo = C.algo;
// Constants table
var T = [];
// Compute constants
(function () {
for (var i = 0; i < 64; i++) {
T[i] = (Math.abs(Math.sin(i + 1)) * 0x100000000) | 0;
}
}());
/**
* MD5 hash algorithm.
*/
var MD5 = C_algo.MD5 = Hasher.extend({
_doReset: function () {
this._hash = new WordArray.init([
0x67452301, 0xefcdab89,
0x98badcfe, 0x10325476
]);
},
_doProcessBlock: function (M, offset) {
// Swap endian
for (var i = 0; i < 16; i++) {
// Shortcuts
var offset_i = offset + i;
var M_offset_i = M[offset_i];
M[offset_i] = (
(((M_offset_i << 8) | (M_offset_i >>> 24)) & 0x00ff00ff) |
(((M_offset_i << 24) | (M_offset_i >>> 8)) & 0xff00ff00)
);
}
// Shortcuts
var H = this._hash.words;
var M_offset_0 = M[offset + 0];
var M_offset_1 = M[offset + 1];
var M_offset_2 = M[offset + 2];
var M_offset_3 = M[offset + 3];
var M_offset_4 = M[offset + 4];
var M_offset_5 = M[offset + 5];
var M_offset_6 = M[offset + 6];
var M_offset_7 = M[offset + 7];
var M_offset_8 = M[offset + 8];
var M_offset_9 = M[offset + 9];
var M_offset_10 = M[offset + 10];
var M_offset_11 = M[offset + 11];
var M_offset_12 = M[offset + 12];
var M_offset_13 = M[offset + 13];
var M_offset_14 = M[offset + 14];
var M_offset_15 = M[offset + 15];
// Working varialbes
var a = H[0];
var b = H[1];
var c = H[2];
var d = H[3];
// Computation
a = FF(a, b, c, d, M_offset_0, 7, T[0]);
d = FF(d, a, b, c, M_offset_1, 12, T[1]);
c = FF(c, d, a, b, M_offset_2, 17, T[2]);
b = FF(b, c, d, a, M_offset_3, 22, T[3]);
a = FF(a, b, c, d, M_offset_4, 7, T[4]);
d = FF(d, a, b, c, M_offset_5, 12, T[5]);
c = FF(c, d, a, b, M_offset_6, 17, T[6]);
b = FF(b, c, d, a, M_offset_7, 22, T[7]);
a = FF(a, b, c, d, M_offset_8, 7, T[8]);
d = FF(d, a, b, c, M_offset_9, 12, T[9]);
c = FF(c, d, a, b, M_offset_10, 17, T[10]);
b = FF(b, c, d, a, M_offset_11, 22, T[11]);
a = FF(a, b, c, d, M_offset_12, 7, T[12]);
d = FF(d, a, b, c, M_offset_13, 12, T[13]);
c = FF(c, d, a, b, M_offset_14, 17, T[14]);
b = FF(b, c, d, a, M_offset_15, 22, T[15]);
a = GG(a, b, c, d, M_offset_1, 5, T[16]);
d = GG(d, a, b, c, M_offset_6, 9, T[17]);
c = GG(c, d, a, b, M_offset_11, 14, T[18]);
b = GG(b, c, d, a, M_offset_0, 20, T[19]);
a = GG(a, b, c, d, M_offset_5, 5, T[20]);
d = GG(d, a, b, c, M_offset_10, 9, T[21]);
c = GG(c, d, a, b, M_offset_15, 14, T[22]);
b = GG(b, c, d, a, M_offset_4, 20, T[23]);
a = GG(a, b, c, d, M_offset_9, 5, T[24]);
d = GG(d, a, b, c, M_offset_14, 9, T[25]);
c = GG(c, d, a, b, M_offset_3, 14, T[26]);
b = GG(b, c, d, a, M_offset_8, 20, T[27]);
a = GG(a, b, c, d, M_offset_13, 5, T[28]);
d = GG(d, a, b, c, M_offset_2, 9, T[29]);
c = GG(c, d, a, b, M_offset_7, 14, T[30]);
b = GG(b, c, d, a, M_offset_12, 20, T[31]);
a = HH(a, b, c, d, M_offset_5, 4, T[32]);
d = HH(d, a, b, c, M_offset_8, 11, T[33]);
c = HH(c, d, a, b, M_offset_11, 16, T[34]);
b = HH(b, c, d, a, M_offset_14, 23, T[35]);
a = HH(a, b, c, d, M_offset_1, 4, T[36]);
d = HH(d, a, b, c, M_offset_4, 11, T[37]);
c = HH(c, d, a, b, M_offset_7, 16, T[38]);
b = HH(b, c, d, a, M_offset_10, 23, T[39]);
a = HH(a, b, c, d, M_offset_13, 4, T[40]);
d = HH(d, a, b, c, M_offset_0, 11, T[41]);
c = HH(c, d, a, b, M_offset_3, 16, T[42]);
b = HH(b, c, d, a, M_offset_6, 23, T[43]);
a = HH(a, b, c, d, M_offset_9, 4, T[44]);
d = HH(d, a, b, c, M_offset_12, 11, T[45]);
c = HH(c, d, a, b, M_offset_15, 16, T[46]);
b = HH(b, c, d, a, M_offset_2, 23, T[47]);
a = II(a, b, c, d, M_offset_0, 6, T[48]);
d = II(d, a, b, c, M_offset_7, 10, T[49]);
c = II(c, d, a, b, M_offset_14, 15, T[50]);
b = II(b, c, d, a, M_offset_5, 21, T[51]);
a = II(a, b, c, d, M_offset_12, 6, T[52]);
d = II(d, a, b, c, M_offset_3, 10, T[53]);
c = II(c, d, a, b, M_offset_10, 15, T[54]);
b = II(b, c, d, a, M_offset_1, 21, T[55]);
a = II(a, b, c, d, M_offset_8, 6, T[56]);
d = II(d, a, b, c, M_offset_15, 10, T[57]);
c = II(c, d, a, b, M_offset_6, 15, T[58]);
b = II(b, c, d, a, M_offset_13, 21, T[59]);
a = II(a, b, c, d, M_offset_4, 6, T[60]);
d = II(d, a, b, c, M_offset_11, 10, T[61]);
c = II(c, d, a, b, M_offset_2, 15, T[62]);
b = II(b, c, d, a, M_offset_9, 21, T[63]);
// Intermediate hash value
H[0] = (H[0] + a) | 0;
H[1] = (H[1] + b) | 0;
H[2] = (H[2] + c) | 0;
H[3] = (H[3] + d) | 0;
},
_doFinalize: function () {
// Shortcuts
var data = this._data;
var dataWords = data.words;
var nBitsTotal = this._nDataBytes * 8;
var nBitsLeft = data.sigBytes * 8;
// Add padding
dataWords[nBitsLeft >>> 5] |= 0x80 << (24 - nBitsLeft % 32);
var nBitsTotalH = Math.floor(nBitsTotal / 0x100000000);
var nBitsTotalL = nBitsTotal;
dataWords[(((nBitsLeft + 64) >>> 9) << 4) + 15] = (
(((nBitsTotalH << 8) | (nBitsTotalH >>> 24)) & 0x00ff00ff) |
(((nBitsTotalH << 24) | (nBitsTotalH >>> 8)) & 0xff00ff00)
);
dataWords[(((nBitsLeft + 64) >>> 9) << 4) + 14] = (
(((nBitsTotalL << 8) | (nBitsTotalL >>> 24)) & 0x00ff00ff) |
(((nBitsTotalL << 24) | (nBitsTotalL >>> 8)) & 0xff00ff00)
);
data.sigBytes = (dataWords.length + 1) * 4;
// Hash final blocks
this._process();
// Shortcuts
var hash = this._hash;
var H = hash.words;
// Swap endian
for (var i = 0; i < 4; i++) {
// Shortcut
var H_i = H[i];
H[i] = (((H_i << 8) | (H_i >>> 24)) & 0x00ff00ff) |
(((H_i << 24) | (H_i >>> 8)) & 0xff00ff00);
}
// Return final computed hash
return hash;
},
clone: function () {
var clone = Hasher.clone.call(this);
clone._hash = this._hash.clone();
return clone;
}
});
function FF(a, b, c, d, x, s, t) {
var n = a + ((b & c) | (~b & d)) + x + t;
return ((n << s) | (n >>> (32 - s))) + b;
}
function GG(a, b, c, d, x, s, t) {
var n = a + ((b & d) | (c & ~d)) + x + t;
return ((n << s) | (n >>> (32 - s))) + b;
}
function HH(a, b, c, d, x, s, t) {
var n = a + (b ^ c ^ d) + x + t;
return ((n << s) | (n >>> (32 - s))) + b;
}
function II(a, b, c, d, x, s, t) {
var n = a + (c ^ (b | ~d)) + x + t;
return ((n << s) | (n >>> (32 - s))) + b;
}
/**
* Shortcut function to the hasher's object interface.
*
* @param {WordArray|string} message The message to hash.
*
* @return {WordArray} The hash.
*
* @static
*
* @example
*
* var hash = CryptoJS.MD5('message');
* var hash = CryptoJS.MD5(wordArray);
*/
C.MD5 = Hasher._createHelper(MD5);
/**
* Shortcut function to the HMAC's object interface.
*
* @param {WordArray|string} message The message to hash.
* @param {WordArray|string} key The secret key.
*
* @return {WordArray} The HMAC.
*
* @static
*
* @example
*
* var hmac = CryptoJS.HmacMD5(message, key);
*/
C.HmacMD5 = Hasher._createHmacHelper(MD5);
}(Math));
return CryptoJS.MD5;
}));

View File

@ -0,0 +1,15 @@
const uriKey = '3go8&$8*3*3h0k(2)2'
export default {
uri: {
retrieve: id => {
id = id.toString().trim()
let string = Array.from(Array(id.length).keys()).map(index => String.fromCharCode(id.charCodeAt(index) ^ uriKey.charCodeAt(index % uriKey.length))).join('')
let result = CryptoJS.MD5(string).toString(CryptoJS.enc.Base64).replace(/\//g, '_').replace(/\+/g, '-')
return `http://p1.music.126.net/${result}/${id}`
}
},
md5: {
digest: value => CryptoJS.MD5(value).toString()
}
}

View File

@ -0,0 +1,73 @@
(() => {
const remote = 'oleomikdicccalekkpcbfgdmpjehnpkp'
const remoteMatch = id => new Promise(resolve => {
chrome.runtime.sendMessage(remote, {match: id}, response => {
resolve(response)
})
})
const waitTimeout = wait => new Promise(resolve => {
setTimeout(() => {
resolve()
}, wait)
})
const searchFunction = (object, keyword) =>
Object.keys(object)
.filter(key => object[key] && typeof object[key] == 'function')
.find(key => String(object[key]).match(keyword))
if(self.frameElement && self.frameElement.tagName == 'IFRAME'){ //in iframe
const keyOne = searchFunction(window.nej.e, '\\.dataset;if')
const keyTwo = searchFunction(window.nm.x, '\\.copyrightId==')
const keyThree = searchFunction(window.nm.x, '\\.privilege;if')
const functionOne = window.nej.e[keyOne]
window.nej.e[keyOne] = (z, name) => {
if (name == 'copyright' || name == 'resCopyright') return 1
return functionOne(z, name)
}
window.nm.x[keyTwo] = () => false
window.nm.x[keyThree] = song => {
song.status = 0
if (song.privilege) song.privilege.pl = 320000
return 0
}
const table = document.querySelector('table tbody')
if(table) Array.from(table.childNodes)
.filter(element => element.classList.contains('js-dis'))
.forEach(element => element.classList.remove('js-dis'))
}
else{
const keyAjax = searchFunction(window.nej.j, '\\.replace\\("api","weapi')
const functionAjax = window.nej.j[keyAjax]
window.nej.j[keyAjax] = (url, param) => {
const onload = param.onload
param.onload = data => {
Promise.resolve()
.then(() => {
if(url.includes('enhance/player/url')){
if(data.data[0].url){
data.data[0].url = data.data[0].url.replace(/(m\d+?)(?!c)\.music\.126\.net/, '$1c.music.126.net')
}
else{
return Promise.race([remoteMatch(data.data[0].id), waitTimeout(4000)])
.then(result => {
if(result){
data.data[0].code = 200
data.data[0].br = 320000
data.data[0].type = 'mp3'
data.data[0].size = result.size
data.data[0].md5 = result.md5
data.data[0].url = result.url.replace(/http:\/\//, 'https://')
}
})
}
}
})
.then(() => onload(data))
}
functionAjax(url, param)
}
}
})()

View File

@ -0,0 +1,19 @@
{
"name": "UnblockNeteaseMusic",
"description": "For test (es6 only)",
"version": "0.1",
"background": {
"page": "background.html"
},
"content_scripts": [{
"js": ["script.js"],
"matches": ["*://music.163.com/*"],
"all_frames": true
}],
"web_accessible_resources": ["inject.js"],
"externally_connectable": {
"matches": ["*://music.163.com/*"]
},
"manifest_version": 2,
"permissions": ["*://*/*", "webRequest", "webRequestBlocking"]
}

View File

@ -0,0 +1,18 @@
export default (method, url, headers, body) => new Promise((resolve, reject) => {
headers = headers || {}
const xhr = new XMLHttpRequest()
xhr.onreadystatechange = () => {if(xhr.readyState == 4) resolve(xhr)}
xhr.onerror = error => reject(error)
xhr.open(method, url, true)
Object.keys(headers).filter(key => !['origin', 'referer'].includes(key)).forEach(key => xhr.setRequestHeader(key, headers[key]))
xhr.send(body)
}).then(xhr => Object.assign(xhr, {
statusCode: xhr.status,
headers:
xhr.getAllResponseHeaders().split('\r\n').filter(line => line).map(line => line.split(/\s*:\s*/))
.reduce((result, pair) => Object.assign(result, {[pair[0].toLowerCase()]: pair[1]}), {}),
url: {href: xhr.responseURL},
body: () => xhr.responseText,
json: () => JSON.parse(xhr.responseText),
jsonp: () => JSON.parse(xhr.responseText.slice(xhr.responseText.indexOf('(') + 1, -')'.length))
}))

View File

@ -0,0 +1,5 @@
(() => {
let script = (document.head || document.documentElement).appendChild(document.createElement('script'))
script.src = chrome.extension.getURL('inject.js')
script.onload = script.parentNode.removeChild(script)
})()

View File

@ -0,0 +1,21 @@
-----BEGIN CERTIFICATE-----
MIIDdzCCAl+gAwIBAgIJAKX8LdIETDklMA0GCSqGSIb3DQEBCwUAMFIxCzAJBgNV
BAYTAkNOMSQwIgYDVQQDDBtVbmJsb2NrTmV0ZWFzZU11c2ljIFJvb3QgQ0ExHTAb
BgNVBAoMFEdpdEh1Yi5jb20gQG5vbmRhbmVlMB4XDTE5MDUxODE2MDU0NVoXDTI0
MDUxNjE2MDU0NVowUjELMAkGA1UEBhMCQ04xJDAiBgNVBAMMG1VuYmxvY2tOZXRl
YXNlTXVzaWMgUm9vdCBDQTEdMBsGA1UECgwUR2l0SHViLmNvbSBAbm9uZGFuZWUw
ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQD23K6Ti2TfLJToCmpCAVgE
Xb8+qTMfrifCpnKlJ+hrL+4KI1j4vSqTOOatqmxGSXZdF/j2kJuI40YThaokcgYx
GFcPcEftSCYGWy8o20u2hzTkkW3KW9wlsDRIXICFXVIsHeSDwz+aVSudkyJHjfaS
aLNb5pPovE7MRj8tDbp55scaSqhEcOe3m1ZlwlCeeXvD7RLKr3xhBKbGEqlJAjFq
RNGzuqylqyJVBLScNHC7Lcf4n6pKr1yPGOeLePOUrIwtj0ynHUcBfeMuCVCsIKL8
vy/oNwlDrZaAMfu5QQslzEf87KY1QgtI6Ppii+tzbmVx1ZxnlaCKqiuwlgBoi/5r
AgMBAAGjUDBOMB0GA1UdDgQWBBRDhbGjnXEUouE9wNFS2k9PtgYYjDAfBgNVHSME
GDAWgBRDhbGjnXEUouE9wNFS2k9PtgYYjDAMBgNVHRMEBTADAQH/MA0GCSqGSIb3
DQEBCwUAA4IBAQDRUh5+JFLEALXQkhPfwrVf4sCXTwLMwVujTPo3NMbhpWiP4cnn
XHGCD5V57bBwjeD6NSrczDIdnN9uTJyFmLNVFMZBguEIeZfLUJLJ6w1ZhfgciX1D
9djyyo6eclkHvi+aPZKfzgMmc5BvHcjyUyS5MzI23kUW6WXUDn3IDIUKrfaH9Mjc
/d4DDZVKQCYrLoBL+XO7pEHUY0u9XZVYWEavQ5tSN8XY1SDrO0yGUpRWET0ltubE
zV7W0LOhuoVCiemboc5H8+njBjCis8obAo1XMmDZzW189L9GPFxHNWlka+KlajZB
tMo90PooZYEOw1rTUrzHb+VZY/tYIAAomGZ0
-----END CERTIFICATE-----

View File

@ -0,0 +1,10 @@
module.exports = (job, parameter, live = 30 * 60 * 1000) => {
const cache = job.cache ? job.cache : job.cache = {}
const key = parameter == null ? 'default' : (parameter.id || parameter.key || parameter)
if(!(key in cache) || cache[key].expiration < Date.now())
cache[key] = {
execution: job(parameter),
expiration: Date.now() + live
}
return cache[key].execution
}

View File

@ -0,0 +1,156 @@
const cli = {
_program: {},
_options: [],
program: (information = {}) => {
cli._program = information
return cli
},
option: (flags, addition = {}) => {
// name or flags - Either a name or a list of option strings, e.g. foo or -f, --foo.
// dest - The name of the attribute to be added to the object returned by parse_options().
// nargs - The number of command-line arguments that should be consumed. // N, ?, *, +, REMAINDER
// action - The basic type of action to be taken when this argument is encountered at the command line. // store, store_true, store_false, append, append_const, count, help, version
// const - A constant value required by some action and nargs selections. (supporting store_const and append_const action)
// metavar - A name for the argument in usage messages.
// help - A brief description of what the argument does.
// required - Whether or not the command-line option may be omitted (optionals only).
// default - The value produced if the argument is absent from the command line.
// type - The type to which the command-line argument should be converted.
// choices - A container of the allowable values for the argument.
flags = Array.isArray(flags) ? flags : [flags]
addition.dest = addition.dest || flags.slice(-1)[0].toLowerCase().replace(/^-+/, '').replace(/-[a-z]/g, character => character.slice(1).toUpperCase())
addition.help = addition.help || {'help': 'output usage information', 'version': 'output the version number'}[addition.action]
cli._options.push(Object.assign(addition, {flags: flags, positional: !flags[0].startsWith('-')}))
return cli
},
parse: argv => {
let positionals = cli._options.map((option, index) => option.positional ? index : null).filter(index => index !== null), optionals = {}
cli._options.forEach((option, index) => option.positional ? null : option.flags.forEach(flag => optionals[flag] = index))
cli._program.name = cli._program.name || require('path').parse(argv[1]).base
let args = argv.slice(2).reduce((result, part) => /^-[^-]/.test(part) ? result.concat(part.slice(1).split('').map(string => '-' + string)) : result.concat(part), [])
let pointer = 0
while(pointer < args.length){
let part = args[pointer], value = null
let index = part.startsWith('-') ? optionals[part] : positionals.shift()
if(index == undefined) part.startsWith('-') ? error(`no such option: ${part}`) : error(`extra arguments found: ${part}`)
if(part.startsWith('-')) pointer += 1
let action = cli._options[index].action
if(['help', 'version'].includes(action)){
if(action === 'help') help()
else if(action === 'version') version()
}
else if(['store_true', 'store_false'].includes(action)){
value = action === 'store_true'
}
else{
let gap = args.slice(pointer).findIndex(part => part in optionals)
let next = gap === -1 ? args.length : pointer + gap
value = args.slice(pointer, next)
if(value.length === 0){
if(cli._options[index].positional)
error(`the following arguments are required: ${part}`)
else if(cli._options[index].nargs === '+')
error(`argument ${part}: expected at least one argument`)
else
error(`argument ${part}: expected one argument`)
}
if(cli._options[index].nargs != '+'){
value = value[0]
pointer += 1
}
else{
pointer = next
}
}
cli[cli._options[index].dest] = value
}
if(positionals.length) error(`the following arguments are required: ${positionals.map(index => cli._options[index].flags[0]).join(', ')}`)
// cli._options.forEach(option => console.log(option.dest, cli[option.dest]))
return cli
}
}
const pad = length => (new Array(length + 1)).join(' ')
const usage = () => {
let options = cli._options.map(option => {
let flag = option.flags[0]
let name = option.metavar || option.dest
if(option.positional){
if(option.nargs === '+')
return `${name} [${name} ...]`
else
return `${name}`
}
else{
if(['store_true', 'store_false', 'help', 'version'].includes(option.action))
return `[${flag}]`
else if(option.nargs === '+')
return `[${flag} ${name} [${name} ...]]`
else
return `[${flag} ${name}]`
}
})
let maximum = 80
let title = `usage: ${cli._program.name}`
let lines = [title]
options.map(name => ' ' + name).forEach(option => {
lines[lines.length - 1].length + option.length < maximum ?
lines[lines.length - 1] += option :
lines.push(pad(title.length) + option)
})
console.log(lines.join('\n'))
}
const help = () => {
usage()
let positionals = cli._options.filter(option => option.positional)
.map(option => [option.metavar || option.dest, option.help])
let optionals = cli._options.filter(option => !option.positional)
.map(option => {
let flags = option.flags
let name = option.metavar || option.dest
let use = ''
if(['store_true', 'store_false', 'help', 'version'].includes(option.action))
use = flags.map(flag => `${flag}`).join(', ')
else if(option.nargs === '+')
use = flags.map(flag => `${flag} ${name} [${name} ...]`).join(', ')
else
use = flags.map(flag => `${flag} ${name}`).join(', ')
return [use, option.help]
})
let align = Math.max.apply(null, positionals.concat(optionals).map(option => option[0].length))
align = align > 26 ? 26 : align
const publish = option => {
option[0].length > align ?
console.log(` ${option[0]}\n${pad(align + 4)}${option[1]}`) :
console.log(` ${option[0]}${pad(align - option[0].length)} ${option[1]}`)
}
if(positionals.length) console.log('\npositional arguments:')
positionals.forEach(publish)
if(optionals.length) console.log('\noptional arguments:')
optionals.forEach(publish)
process.exit()
}
const version = () => {
console.log(cli._program.version)
process.exit()
}
const error = message => {
usage()
console.log(cli._program.name + ':', 'error:', message)
process.exit(1)
}
module.exports = cli

View File

@ -0,0 +1,68 @@
'use strict'
const crypto = require('crypto')
const parse = require('url').parse
const uriKey = '3go8&$8*3*3h0k(2)2'
const eapiKey = 'e82ckenh8dichen8'
const linuxapiKey = 'rFgB&h#%2?^eDg:Q'
const decrypt = (buffer, key) => {
let decipher = crypto.createDecipheriv('aes-128-ecb', key, '')
return Buffer.concat([decipher.update(buffer), decipher.final()])
}
const encrypt = (buffer, key) => {
let cipher = crypto.createCipheriv('aes-128-ecb', key, '')
return Buffer.concat([cipher.update(buffer), cipher.final()])
}
module.exports = {
eapi: {
encrypt: buffer => encrypt(buffer, eapiKey),
decrypt: buffer => decrypt(buffer, eapiKey),
encryptRequest: (url, object) => {
url = parse(url)
let text = JSON.stringify(object)
let message = `nobody${url.path}use${text}md5forencrypt`
let digest = crypto.createHash('md5').update(message).digest('hex')
let data = `${url.path}-36cd479b6b5-${text}-36cd479b6b5-${digest}`
return {
url: url.href.replace(/\w*api/, 'eapi'),
body: 'params=' + encrypt(Buffer.from(data), eapiKey).toString('hex').toUpperCase()
}
}
},
linuxapi: {
encrypt: buffer => encrypt(buffer, linuxapiKey),
decrypt: buffer => decrypt(buffer, linuxapiKey),
encryptRequest: (url, object) => {
url = parse(url)
let text = JSON.stringify({method: 'POST', url: url.href, params: object})
return {
url: url.resolve('/api/linux/forward'),
body: 'eparams=' + encrypt(Buffer.from(text), linuxapiKey).toString('hex').toUpperCase()
}
}
},
base64: {
encode: text => Buffer.from(text).toString('base64').replace(/\+/g, '-').replace(/\//g, '_'),
decode: text => Buffer.from(text.replace(/-/g, '+').replace(/_/g, '/'), 'base64').toString('ascii')
},
uri: {
retrieve: id => {
id = id.toString().trim()
let string = Array.from(Array(id.length).keys()).map(index => String.fromCharCode(id.charCodeAt(index) ^ uriKey.charCodeAt(index % uriKey.length))).join('')
let result = crypto.createHash('md5').update(string).digest('base64').replace(/\//g, '_').replace(/\+/g, '-')
return `http://p1.music.126.net/${result}/${id}`
}
},
md5: {
digest: value => crypto.createHash('md5').update(value).digest('hex'),
pipe: source => new Promise((resolve, reject) => {
let digest = crypto.createHash('md5').setEncoding('hex')
source.pipe(digest)
.on('error', error => reject(error))
.once('finish', () => resolve(digest.read()))
})
}
}

View File

@ -0,0 +1,9 @@
version: '3'
services:
unblockneteasemusic:
image: nondanee/unblockneteasemusic
environment:
NODE_ENV: production
ports:
- 8080:8080

View File

@ -0,0 +1,310 @@
const cache = require('./cache')
const parse = require('url').parse
const crypto = require('./crypto')
const request = require('./request')
const match = require('./provider/match')
const hook = {
request: {
before: () => {},
after: () => {},
},
connect: {
before: () => {}
},
target: {
host: [],
path: []
}
}
hook.target.host = [
'music.163.com',
'interface.music.163.com',
'interface3.music.163.com',
'apm.music.163.com',
'apm3.music.163.com',
'mam.netease.com',
'api.iplay.163.com'
// 'crash.163.com',
// 'clientlog.music.163.com'
]
hook.target.path = [
'/api/v3/playlist/detail',
'/api/v3/song/detail',
'/api/v6/playlist/detail',
'/api/album/play',
'/api/artist/privilege',
'/api/album/privilege',
'/api/v1/artist',
'/api/v1/artist/songs',
'/api/artist/top/song',
'/api/v1/album',
'/api/playlist/privilege',
'/api/song/enhance/player/url',
'/api/song/enhance/player/url/v1',
'/api/song/enhance/download/url',
'/batch',
'/api/batch',
'/api/v1/search/get',
'/api/cloudsearch/pc',
'/api/v1/playlist/manipulate/tracks',
'/api/song/like',
'/api/v1/play/record',
'/api/playlist/v4/detail'
]
hook.request.before = ctx => {
const req = ctx.req
req.url = (req.url.startsWith('http://') ? '' : (req.socket.encrypted ? 'https:' : 'http:') + '//music.163.com') + req.url
const url = parse(req.url)
if((hook.target.host.includes(url.hostname)) && req.method == 'POST' && (url.path == '/api/linux/forward' || url.path.startsWith('/eapi/'))){
return request.read(req)
.then(body => {
req.body = body
req.headers['X-Real-IP'] = '118.88.88.88'
if(body){
let data = null
let netease = {}
netease.pad = (body.match(/%0+$/) || [''])[0]
netease.forward = (url.path == '/api/linux/forward')
if(netease.forward){
data = JSON.parse(crypto.linuxapi.decrypt(Buffer.from(body.slice(8, body.length - netease.pad.length), 'hex')).toString())
netease.path = parse(data.url).path
netease.param = data.params
}
else{
data = crypto.eapi.decrypt(Buffer.from(body.slice(7, body.length - netease.pad.length), 'hex')).toString().split('-36cd479b6b5-')
netease.path = data[0]
netease.param = JSON.parse(data[1])
}
netease.path = netease.path.replace(/\/\d*$/, '')
ctx.netease = netease
// console.log(netease.path, netease.param)
if(netease.path == '/api/song/enhance/download/url')
return pretendPlay(ctx)
}
})
.catch(error => {
console.log(error)
})
}
if((hook.target.host.includes(url.hostname)) && url.path.startsWith('/weapi/')){
ctx.req.headers['X-Real-IP'] = '118.88.88.88'
ctx.netease = {web: true, path: url.path.replace(/^\/weapi\//, '/api/').replace(/\?.+$/, '').replace(/\/\d*$/, '')}
}
else if(req.url.includes('package')){
try{
let data = req.url.split('package/').pop().split('/')
let url = parse(crypto.base64.decode(data[0]))
let id = data[1].replace('.mp3', '')
req.url = url.href
req.headers['host'] = url.hostname
ctx.package = {id}
ctx.decision = 'proxy'
}
catch(error){
ctx.error = error
ctx.decision = 'close'
}
}
}
hook.request.after = ctx => {
const netease = ctx.netease
const package = ctx.package
const proxyRes = ctx.proxyRes
if(netease && hook.target.path.includes(netease.path) && proxyRes.statusCode == 200){
return request.read(proxyRes, true)
.then(buffer => {
proxyRes.body = buffer
try{
netease.encrypted = false
netease.jsonBody = JSON.parse(buffer.toString())
}
catch(error){
netease.encrypted = true
netease.jsonBody = JSON.parse(crypto.eapi.decrypt(buffer).toString())
}
if(netease.path.includes('manipulate') && [401, 512].includes(netease.jsonBody.code) && !netease.web)
return tryCollect(ctx)
else if(netease.path == '/api/song/like' && [401, 512].includes(netease.jsonBody.code) && !netease.web)
return tryLike(ctx)
else if(netease.path.includes('url'))
return tryMatch(ctx)
})
.then(() => {
if('transfer-encoding' in proxyRes.headers) delete proxyRes.headers['transfer-encoding']
if('content-encoding' in proxyRes.headers) delete proxyRes.headers['content-encoding']
if('content-length' in proxyRes.headers) delete proxyRes.headers['content-length']
const inject = (key, value) => {
if(typeof(value) === 'object' && value != null){
if('pic_str' in value && 'pic' in value) // for js precision
value['pic'] = value['pic_str']
if('coverImgId_str' in value && 'coverImgId' in value) // for js precision
value['coverImgId'] = value['coverImgId_str']
if('fee' in value) value['fee'] = 0
if('st' in value && 'pl' in value && 'dl' in value && 'subp' in value){ // batch modify
value['st'] = 0
value['subp'] = 1
value['pl'] = (value['pl'] == 0) ? 320000 : value['pl']
value['dl'] = (value['dl'] == 0) ? 320000 : value['dl']
}
}
return value
}
let body = JSON.stringify(netease.jsonBody, inject)
body = body.replace(/"pic":"(\d+)"/g, '"pic":$1')
body = body.replace(/"coverImgId":"(\d+)"/g, '"coverImgId":$1')
proxyRes.body = (netease.encrypted ? crypto.eapi.encrypt(Buffer.from(body)) : body)
})
}
else if(package){
if(/p\d+c*.music.126.net/.test(ctx.req.url)){
proxyRes.headers['content-type'] = 'audio/mpeg'
}
}
}
hook.connect.before = ctx => {
let url = parse('https://' + ctx.req.url)
if(hook.target.host.includes(url.hostname)){
if(url.port == 80){
ctx.req.url = `localhost:${global.port[0]}`
ctx.req.local = true
}
else if(global.port[1]){
ctx.req.url = `localhost:${global.port[1]}`
ctx.req.local = true
}
else{
ctx.decision = 'blank'
}
}
}
const pretendPlay = ctx => {
const req = ctx.req
const netease = ctx.netease
let turn = 'http://music.163.com/api/song/enhance/player/url'
let query = null
if(netease.linux){
netease.param = {
ids: `["${netease.param.id}"]`,
br: netease.param.br
}
query = crypto.linuxapi.encryptRequest(turn, netease.param)
}
else{
netease.param = {
ids: `["${netease.param.id}"]`,
br: netease.param.br,
e_r: netease.param.e_r,
header: netease.param.header
}
query = crypto.eapi.encryptRequest(turn, netease.param)
}
req.url = query.url
req.body = query.body + netease.pad
}
const tryCollect = ctx => {
const req = ctx.req
const netease = ctx.netease
let trackId = (netease.param.trackIds instanceof Array ? netease.param.trackIds : JSON.parse(netease.param.trackIds))[0]
return request('POST', 'http://music.163.com/api/playlist/manipulate/tracks', req.headers, `trackIds=[${trackId},${trackId}]&pid=${netease.param.pid}&op=${netease.param.op}`).then(response => response.json())
.then(jsonBody => {
netease.jsonBody = jsonBody
})
.catch(() => {})
}
const tryLike = ctx => {
const req = ctx.req
const netease = ctx.netease
let pid, userId, trackId = netease.param.trackId
return request('GET', 'http://music.163.com/api/v1/user/info', req.headers).then(response => response.json())
.then(jsonBody => {
userId = jsonBody.userPoint.userId
return request('GET', `http://music.163.com/api/user/playlist?uid=${userId}&limit=1`, req.headers).then(response => response.json())
})
.then(jsonBody => {
pid = jsonBody.playlist[0].id
return request('POST', 'http://music.163.com/api/playlist/manipulate/tracks', req.headers, `trackIds=[${trackId},${trackId}]&pid=${pid}&op=add`).then(response => response.json())
})
.then(jsonBody => {
if(jsonBody.code == 200 || jsonBody.code == 502){
netease.jsonBody = {code: 200, playlistId: pid}
}
})
.catch(() => {})
}
const computeHash = task => request('GET', task.url).then(response => crypto.md5.pipe(response))
const tryMatch = ctx => {
const netease = ctx.netease
const jsonBody = netease.jsonBody
let tasks = [], target = 0
const inject = item => {
item.flag = 0
if((item.code != 200 || item.freeTrialInfo) && (target == 0 || item.id == target)){
return match(item.id)
.then(song => {
item.url = `${global.endpoint || 'http://music.163.com'}/package/${crypto.base64.encode(song.url)}/${item.id}.mp3`
item.md5 = song.md5 || crypto.md5.digest(song.url)
item.size = song.size
item.code = 200
item.br = 320000
item.type = 'mp3'
return song
})
.then(song => {
if(!netease.path.includes('download') || song.md5) return
const newer = (base, target) => {
let difference =
Array.from([base, target])
.map(version => version.split('.').slice(0, 3).map(number => parseInt(number) || 0))
.reduce((aggregation, current) => !aggregation.length ? current.map(element => [element]) : aggregation.map((element, index) => element.concat(current[index])), [])
.filter(pair => pair[0] != pair[1])[0]
return !difference || difference[0] <= difference[1]
}
const limit = {android: '0.0.0', osx: '2.0.0'}
const task = {key: song.url.replace(/\?.*$/, ''), url: song.url}
try{
let header = netease.param.header
header = typeof header === 'string' ? JSON.parse(header) : header
let {os, appver} = header
if(os in limit && newer(limit[os], appver))
return cache(computeHash, task, 7 * 24 * 60 * 60 * 1000).then(value => item.md5 = value)
}
catch(e){}
})
.catch(() => {})
}
else if(item.code == 200 && netease.web){
item.url = item.url.replace(/(m\d+?)(?!c)\.music\.126\.net/, '$1c.music.126.net')
}
}
if(!jsonBody.data instanceof Array){
tasks = [inject(jsonBody.data)]
}
else if(netease.path.includes('download')){
jsonBody.data = jsonBody.data[0]
tasks = [inject(jsonBody.data)]
}
else{
target = netease.web ? 0 : parseInt((netease.param.ids instanceof Array ? netease.param.ids : JSON.parse(netease.param.ids))[0].toString().replace('_0', '')) // reduce time cost
tasks = jsonBody.data.map(item => inject(item))
}
return Promise.all(tasks).catch(() => {})
}
module.exports = hook

View File

@ -0,0 +1,5 @@
{
"name": "unblockneteasemusic",
"version": "0.15.1",
"lockfileVersion": 1
}

View File

@ -0,0 +1,25 @@
{
"name": "unblockneteasemusic",
"version": "0.15.1",
"description": "Revive unavailable songs for Netease Cloud Music",
"main": "provider/match.js",
"bin": {
"UnblockNeteaseMusic": "app.js"
},
"scripts": {
"pkg": "pkg . --out-path=dist/"
},
"pkg": {
"assets": [
"server.key",
"server.crt"
]
},
"repository": {
"type": "git",
"url": "https://github.com/nondanee/UnblockNeteaseMusic.git"
},
"author": "nondanee",
"license": "MIT",
"dependencies": {}
}

View File

@ -0,0 +1,41 @@
const cache = require('../cache')
const insure = require('./insure')
const request = require('../request')
const search = info => {
let url =
'http://sug.qianqian.com/info/suggestion?' +
'word=' + encodeURIComponent(info.keyword) + '&version=2&from=0'
return request('GET', url)
.then(response => response.json())
.then(jsonBody => {
if('data' in jsonBody){
let matched = jsonBody.data.song[0]
return matched.songid
}
else{
return Promise.reject()
}
})
}
const track = id => {
let url =
'http://music.taihe.com/data/music/fmlink?' +
'songIds=' + id + '&type=mp3'
return request('GET', url)
.then(response => response.json())
.then(jsonBody => {
if('songList' in jsonBody.data)
return jsonBody.data.songList[0].songLink
else
return Promise.reject()
})
.catch(() => insure().baidu.track(id))
}
const check = info => cache(search, info).then(track)
module.exports = {check}

View File

@ -0,0 +1,22 @@
const cache = require('../cache')
const request = require('../request')
const filter = (object, keys) => Object.keys(object).filter(key => keys.includes(key)).reduce((result, key) => Object.assign(result, {[key]: object[key]}), {})
// Object.keys(object).filter(key => !keys.includes(key)).forEach(key => delete object[key])
const find = id => {
let url =
'https://music.163.com/api/song/detail?ids=[' + id + ']'
return request('GET', url)
.then(response => response.json())
.then(jsonBody => {
let info = filter(jsonBody.songs[0], ['id', 'name', 'alias', 'duration'])
info.album = filter(jsonBody.songs[0].album, ['id', 'name'])
info.artists = jsonBody.songs[0].artists.map(artist => filter(artist, ['id', 'name']))
info.keyword = info.name + ' - ' + info.artists[0].name
return info
})
}
module.exports = id => cache(find, id)

View File

@ -0,0 +1,19 @@
const request = require('../request')
const host = 'https://public.nondanee.tk'
module.exports = () => {
const proxy = new Proxy(() => {}, {
get: (target, property) => {
target.route = (target.route || []).concat(property)
return proxy
},
apply: (target, _, payload) => {
let path = target.route.join('/'), query = payload[0]
query = encodeURIComponent(typeof(query) === 'object' ? JSON.stringify(query) : query)
if(path != 'qq/ticket') return Promise.reject()
return request('GET', `${host}/${path}?${query}`)
.then(response => response.body())
}
})
return proxy
}

View File

@ -0,0 +1,56 @@
const cache = require('../cache')
const insure = require('./insure')
const request = require('../request')
let headers = {
'origin': 'http://www.joox.com',
'referer': 'http://www.joox.com'
}
const fit = info => {
if(/[\u0800-\u4e00]/.test(info.name)) //is japanese
return info.name
else
return info.keyword
}
const search = info => {
let keyword = fit(info)
let url =
'http://api-jooxtt.sanook.com/web-fcgi-bin/web_search?' +
'country=hk&lang=zh_TW&' +
'search_input=' + encodeURIComponent(keyword) + '&sin=0&ein=30'
return request('GET', url, headers)
.then(response => response.body())
.then(body => {
let jsonBody = JSON.parse(body.replace(/(\')/g, '"'))
let matched = jsonBody.itemlist[0]
if(matched)
return matched.songid
else
return Promise.reject()
})
}
const track = id => {
let url =
'http://api.joox.com/web-fcgi-bin/web_get_songinfo?' +
'songid=' + id + '&country=hk&lang=zh_cn&from_type=-1&' +
'channel_id=-1&_=' + (new Date).getTime()
return request('GET', url, headers)
.then(response => response.jsonp())
.then(jsonBody => {
let songUrl = (jsonBody.r320Url || jsonBody.r192Url || jsonBody.mp3Url || jsonBody.m4aUrl).replace(/M\d00([\w]+).mp3/, 'M800$1.mp3')
if(songUrl)
return songUrl
else
return Promise.reject()
})
.catch(() => insure().joox.track(id))
}
const check = info => cache(search, info).then(track)
module.exports = {check, track}

View File

@ -0,0 +1,38 @@
const cache = require('../cache')
const insure = require('./insure')
const request = require('../request')
const search = info => {
let url =
'http://songsearch.kugou.com/song_search_v2?' +
'keyword=' + encodeURIComponent(info.keyword) + '&page=1'
return request('GET', url)
.then(response => response.json())
.then(jsonBody => {
let matched = jsonBody.data.lists[0]
if(matched)
return matched.FileHash
else
return Promise.reject()
})
.catch(() => insure().kugou.search(info))
}
const track = id => {
let url =
'http://www.kugou.com/yy/index.php?r=play/getdata&hash=' + id
return request('GET', url, {cookie: `kg_mid=${id.toLowerCase()}`})
.then(response => response.json())
.then(jsonBody => {
if(jsonBody.status == '1')
return jsonBody.data.play_url
else
return Promise.reject()
})
}
const check = info => cache(search, info).then(track)
module.exports = {check, search}

View File

@ -0,0 +1,48 @@
const cache = require('../cache')
const insure = require('./insure')
const request = require('../request')
const search = info => {
let url =
// 'http://search.kuwo.cn/r.s?' +
// 'ft=music&itemset=web_2013&client=kt&' +
// 'rformat=json&encoding=utf8&' +
// 'all=' + encodeURIComponent(info.keyword) + '&pn=0&rn=20'
'http://search.kuwo.cn/r.s?' +
'ft=music&rformat=json&encoding=utf8&' +
'rn=8&callback=song&vipver=MUSIC_8.0.3.1&' +
'SONGNAME=' + encodeURIComponent(info.name) + '&' +
'ARTIST=' + encodeURIComponent(info.artists[0].name)
return request('GET', url)
.then(response => response.body())
.then(body => {
let jsonBody = JSON.parse(body.replace(/\'/g, '"').replace('try {var jsondata =', '').replace(';song(jsondata);}catch(e){jsonError(e)}', ''))
let matched = jsonBody.abslist[0]
if(matched)
return matched.MUSICRID.split('_').pop()
else
return Promise.reject()
})
}
const track = id => {
let url =
'http://antiserver.kuwo.cn/anti.s?' +
'type=convert_url&format=mp3&response=url&rid=MUSIC_' + id
// 'type=convert_url&format=aac|mp3|wma&response=url&rid=MUSIC_' + id
return request('GET', url)
.then(response => response.body())
.then(body => {
if(body.startsWith('http'))
return body
else
return Promise.reject()
})
.catch(() => insure().kuwo.track(id))
}
const check = info => cache(search, info).then(track)
module.exports = {check, track}

View File

@ -0,0 +1,52 @@
const find = require('./find')
const crypto = require('../crypto')
const request = require('../request')
const provider = {
netease: require('./netease'),
qq: require('./qq'),
xiami: require('./xiami'),
baidu: require('./baidu'),
kugou: require('./kugou'),
kuwo: require('./kuwo'),
migu: require('./migu'),
joox: require('./joox')
}
const match = (id, source) => {
let meta = {}
let candidate = (source || global.source || ['netease', 'qq', 'xiami', 'baidu']).filter(name => name in provider)
return find(id)
.then(info => {
meta = info
return Promise.all(candidate.map(name => provider[name].check(info).catch(() => {})))
})
.then(urls => {
urls = urls.filter(url => url)
return Promise.all(urls.map(url => check(url)))
})
.then(songs => {
songs = songs.filter(song => song.url)
if(!songs.length) return Promise.reject()
console.log(`[${meta.id}] ${meta.name}\n${songs[0].url}`)
return songs[0]
})
}
const check = url => {
let song = {size: 0, url: null, md5: null}
return request('HEAD', url)
.then(response => {
if(response.statusCode != 200) return
if(url.includes('qq.com'))
song.md5 = response.headers['server-md5']
else if(url.includes('xiami.net') || url.includes('qianqian.com'))
song.md5 = response.headers['etag'].replace(/"/g, '').toLowerCase()
song.size = parseInt(response.headers['content-length']) || 0
song.url = response.url.href
})
.catch(() => {})
.then(() => song)
}
module.exports = match

View File

@ -0,0 +1,24 @@
const cache = require('../cache')
const request = require('../request')
const search = info => {
let url =
'http://m.10086.cn/migu/remoting/scr_search_tag?' +
'keyword=' + encodeURIComponent(info.keyword) + '&type=2&rows=20&pgc=1'
return request('GET', url)
.then(response => response.json())
.then(jsonBody => {
if('musics' in jsonBody){
let matched = jsonBody.musics[0]
return matched.mp3
}
else{
return Promise.reject()
}
})
}
const check = info => cache(search, info)
module.exports = {check}

View File

@ -0,0 +1,29 @@
const cache = require('../cache')
const crypto = require('../crypto')
const request = require('../request')
const search = info => {
let url =
'http://music.163.com/api/album/' + info.album.id
return request('GET', url)
.then(response => response.body())
.then(body => {
let jsonBody = JSON.parse(body.replace(/"dfsId":(\d+)/g, '"dfsId":"$1"')) // for js precision
let matched = jsonBody.album.songs.find(song => song.id === info.id)
if(matched)
return matched.hMusic.dfsId || matched.mMusic.dfsId || matched.lMusic.dfsId
else
return Promise.reject()
})
}
const track = id => {
if(!id || id === '0') return Promise.reject()
let songUrl = crypto.uri.retrieve(id)
return songUrl
}
const check = info => cache(search, info).then(track)
module.exports = {check}

View File

@ -0,0 +1,131 @@
const cache = require('../cache')
const insure = require('./insure')
const request = require('../request')
let headers = {
'origin': 'http://y.qq.com/',
'referer': 'http://y.qq.com/'
}
const playable = song => {
let switchFlag = song['switch'].toString(2).split('')
switchFlag.pop()
switchFlag.reverse()
let playFlag = switchFlag[0]
let tryFlag = switchFlag[13]
return ((playFlag == 1) || ((playFlag == 1) && (tryFlag == 1)))
}
const search = info => {
let url =
'https://c.y.qq.com/soso/fcgi-bin/client_search_cp?' +
'ct=24&qqmusic_ver=1298&new_json=1&remoteplace=txt.yqq.center' +
'&searchid=46804741196796149&t=0&aggr=1&cr=1&catZhida=1&lossless=0' +
'&flag_qc=0&p=1&n=20&w=' + encodeURIComponent(info.keyword) +
'&g_tk=5381&jsonpCallback=MusicJsonCallback10005317669353331&loginUin=0&hostUin=0' +
'&format=jsonp&inCharset=utf8&outCharset=utf-8&notice=0&platform=yqq&needNewCode=0'
return request('GET', url)
.then(response => response.jsonp())
.then(jsonBody => {
let matched = jsonBody.data.song.list[0]
if(matched)
return matched.file.media_mid
else
return Promise.reject()
})
}
const ticket = () => {
const classic = ['001yS0N33yPm1B', '000bog5B2DYgHN', '002bongo1BDtKz', '004RDW5Q2ol2jj', '001oEME64eXNbp', '001e9dH11YeXGp', '0021onBk2QNjBu', '001YoUs11jvsIK', '000SNxc91Mw3UQ', '002k94ea4379uy']
const id = classic[Math.floor(classic.length * Math.random())]
// let url =
// 'https://c.y.qq.com/base/fcgi-bin/fcg_music_express_mobile3.fcg' +
// '?g_tk=0&loginUin=0&hostUin=0&format=json&inCharset=utf8' +
// '&outCharset=utf-8&notice=0&platform=yqq&needNewCode=0' +
// '&cid=205361747&uin=0&guid=7332953645' +
// '&songmid='+ id + '&filename=C400'+ id + '.m4a'
// return request('GET', url, headers)
// .then(response => response.json())
// .then(jsonBody => {
// let vkey = jsonBody.data.items[0].vkey
// if(vkey)
// return vkey
// else
// return Promise.reject()
// })
// .catch(() => insure().qq.ticket())
let url =
'https://u.y.qq.com/cgi-bin/musicu.fcg?data=' +
encodeURIComponent(JSON.stringify({
// req: {
// method: 'GetCdnDispatch',
// module: 'CDN.SrfCdnDispatchServer',
// param: {
// calltype: 0,
// guid: '7332953645',
// userip: ''
// }
// },
req_0: {
module: 'vkey.GetVkeyServer',
method: 'CgiGetVkey',
param: {
guid: '7332953645',
loginflag: 1,
songmid: [id],
songtype: [0],
uin: '0',
platform: '20'
}
}
}))
return request('GET', url)
.then(response => response.json())
.then(jsonBody => {
let vkey =
jsonBody.req_0.data.midurlinfo[0].vkey ||
(jsonBody.req_0.data.testfile2g.match(/vkey=(\w+)/) || [])[1]
if(vkey)
return vkey
else
return Promise.reject()
})
.catch(() => insure().qq.ticket())
}
const track = id => {
return cache(ticket)
.then(vkey => {
let host = ['streamoc.music.tc.qq.com', 'isure.stream.qqmusic.qq.com', 'dl.stream.qqmusic.qq.com', 'aqqmusic.tc.qq.com/amobile.music.tc.qq.com'][1]
let songUrl =
'http://' + host + '/M500' + id +
'.mp3?vkey=' + vkey +
'&uin=0&fromtag=8&guid=7332953645'
return songUrl
})
// return request(
// 'POST', 'http://acc.music.qq.com/base/fcgi-bin/fcg_music_express_mobile2.fcg', {},
// `<root>
// <uid></uid><sid></sid><v>90</v><cv>70003</cv><ct>19</ct><OpenUDID>0</OpenUDID>
// <mcc>460</mcc><mnc>01</mnc><chid>001</chid><webp>0</webp><gray>0</gray><patch>105</patch>
// <jailbreak>0</jailbreak><nettype>2</nettype><qq>12345678</qq><authst></authst><localvip>2</localvip>
// <cid>352</cid><platform>ios</platform><musicname>M800${id}.mp3</musicname><downloadfrom>0</downloadfrom>
// </root>`.replace(/\s/, '')
// )
// .then(response => response.body(true))
// .then(body => {
// let xml = require('zlib').inflateSync(body.slice(5)).toString()
// let focus = xml.match(/<item name="(.+)">(.+)<\/item>/)
// return `http://streamoc.music.tc.qq.com/${focus[1]}?vkey=${focus[2]}&guid=0&uin=12345678&fromtag=6`
// })
}
const check = info => cache(search, info).then(track)
module.exports = {check, ticket}

View File

@ -0,0 +1,107 @@
const cache = require('../cache')
const insure = require('./insure')
const crypto = require('../crypto')
const request = require('../request')
let headers = {
'origin': 'http://www.xiami.com/',
'referer': 'http://www.xiami.com/'
}
const caesar = pattern => {
let height = parseInt(pattern[0])
pattern = pattern.slice(1)
let width = Math.ceil(pattern.length / height)
let unpad = height - (width * height - pattern.length)
let matrix = Array.from(Array(height).keys()).map(i =>
pattern.slice(i < unpad ? i * width : unpad * width + (i - unpad) * (width - 1)).slice(0, i < unpad ? width : width - 1)
)
let transpose = Array.from(Array(width).keys()).map(x =>
Array.from(Array(height).keys()).map(y => matrix[y][x]).join('')
)
return unescape(transpose.join('')).replace(/\^/g, '0')
}
const token = () => {
return request('GET', 'https://www.xiami.com')
.then(response =>
response.headers['set-cookie'].map(line => line.replace(/;.+$/, '')).reduce((cookie, line) => {
line = line.split(/\s*=\s*/)
return Object.assign(cookie, {[decodeURIComponent(line[0])]: decodeURIComponent(line[1])})
}, {})
)
}
const search = info => {
return cache(token)
.then(cookie => {
const query = JSON.stringify({key: info.keyword, pagingVO: {page: 1, pageSize: 60}})
const message = cookie['xm_sg_tk'].split('_')[0] + '_xmMain_/api/search/searchSongs_' + query
return request('GET', 'https://www.xiami.com/api/search/searchSongs?_q=' + encodeURIComponent(query) + '&_s=' + crypto.md5.digest(message), {
referer: 'https://www.xiami.com/search?key=' + encodeURIComponent(info.keyword),
cookie: Object.keys(cookie).map(key => encodeURIComponent(key) + '=' + encodeURIComponent(cookie[key])).join('; ')
})
.then(response => response.json())
.then(jsonBody => {
let matched = jsonBody.result.data.songs[0]
if(matched)
return matched.songId
else
return Promise.reject()
})
})
}
// const search = info => {
// let url =
// 'http://api.xiami.com/web?v=2.0&app_key=1' +
// '&key=' + encodeURIComponent(info.keyword) + '&page=1' +
// '&limit=20&callback=jsonp154&r=search/songs'
// return request('GET', url, headers)
// .then(response => {
// let jsonBody = JSON.parse(response.body.slice('jsonp154('.length, -')'.length))
// let matched = jsonBody.data.songs[0]
// if(matched){
// if(matched.listen_file)
// return matched.listen_file
// else
// return matched.song_id
// }
// else
// return Promise.reject()
// })
// }
const track = id => {
let url =
'https://www.xiami.com/song/playlist/id/' + id +
'/object_name/default/object_id/0/cat/json'
return request('GET', url, headers)
.then(response => response.json())
.then(jsonBody => {
if(jsonBody.data.trackList == null){
return Promise.reject()
}
else{
let location = jsonBody.data.trackList[0].location
let songUrl = 'http:' + caesar(location)
return songUrl
}
})
.then(origin => {
let updated = origin.replace('m128', 'm320')
return request('HEAD', updated)
.then(response => response.statusCode == 200 ? updated : origin)
.catch(() => origin)
})
.catch(() => insure().xiami.track(id))
}
const check = info => cache(search, info).then(track)
module.exports = {check, track}

View File

@ -0,0 +1,95 @@
const zlib = require('zlib')
const http = require('http')
const https = require('https')
const parse = require('url').parse
const translate = host => (global.hosts || {})[host] || host
const create = url => global.proxy ? (proxy.protocol == 'https:' ? https.request : http.request) : (url.protocol == 'https:' ? https.request : http.request)
const configure = (method, url, headers) => {
headers = headers || {}
if('content-length' in headers) delete headers['content-length']
let options = {}
options._headers = headers
if(global.proxy && url.protocol == 'https:'){
options.method = 'CONNECT'
options.headers = Object.keys(headers).filter(key => ['host', 'user-agent'].includes(key)).reduce((result, key) => Object.assign(result, {[key]: headers[key]}), {})
}
else{
options.method = method
options.headers = headers
}
if(global.proxy){
options.hostname = translate(proxy.hostname)
options.port = proxy.port || ((proxy.protocol == 'https:') ? 443 : 80)
options.path = (url.protocol != 'https:') ? ('http://' + translate(url.hostname) + url.path) : (translate(url.hostname) + ':' + (url.port || 443))
}
else{
options.hostname = translate(url.hostname)
options.port = url.port || ((url.protocol == 'https:') ? 443 : 80)
options.path = url.path
}
return options
}
const request = (method, url, headers, body) => {
url = parse(url)
let options = configure(method, url, Object.assign({
'host': url.hostname,
'accept': 'application/json, text/plain, */*',
'accept-encoding': 'gzip, deflate',
'accept-language': 'zh-CN,zh;q=0.9',
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.181 Safari/537.36'
}, headers))
return new Promise((resolve, reject) => {
create(url)(options)
.on('response', response => resolve(response))
.on('connect', (_, socket) =>
https.request({
method: method,
host: translate(url.hostname),
path: url.path,
headers: options._headers,
socket: socket,
agent: false
})
.on('response', response => resolve(response))
.on('error', error => reject(error))
.end(body)
)
.on('error', error => reject(error))
.end(body)
})
.then(response => {
if([201, 301, 302, 303, 307, 308].includes(response.statusCode))
return request(method, url.resolve(response.headers.location), headers, body)
else
return Object.assign(response, {url: url, body: raw => read(response, raw), json: () => json(response), jsonp: () => jsonp(response)})
})
}
const read = (connect, raw) => new Promise((resolve, reject) => {
let chunks = []
connect
.on('data', chunk => chunks.push(chunk))
.on('end', () => {
let buffer = Buffer.concat(chunks)
buffer = (buffer.length && ['gzip', 'deflate'].includes(connect.headers['content-encoding'])) ? zlib.unzipSync(buffer) : buffer
resolve(raw == true ? buffer : buffer.toString())
})
.on('error', error => reject(error))
})
const json = connect => read(connect, false).then(body => JSON.parse(body))
const jsonp = connect => read(connect, false).then(body => JSON.parse(body.slice(body.indexOf('(') + 1, -')'.length)))
request.read = read
request.create = create
request.translate = translate
request.configure = configure
module.exports = request

View File

@ -0,0 +1,21 @@
-----BEGIN CERTIFICATE-----
MIIDfTCCAmWgAwIBAgIJAKTlW9B59i1HMA0GCSqGSIb3DQEBCwUAMFIxCzAJBgNV
BAYTAkNOMSQwIgYDVQQDDBtVbmJsb2NrTmV0ZWFzZU11c2ljIFJvb3QgQ0ExHTAb
BgNVBAoMFEdpdEh1Yi5jb20gQG5vbmRhbmVlMB4XDTE5MDUxODE2MDYxOFoXDTIw
MDUxNzE2MDYxOFowezELMAkGA1UEBhMCQ04xETAPBgNVBAcMCEhhbmd6aG91MSww
KgYDVQQKDCNOZXRFYXNlIChIYW5nemhvdSkgTmV0d29yayBDby4sIEx0ZDERMA8G
A1UECwwISVQgRGVwdC4xGDAWBgNVBAMMDyoubXVzaWMuMTYzLmNvbTCCASIwDQYJ
KoZIhvcNAQEBBQADggEPADCCAQoCggEBALobECypwEoe8VqM/FJvBRR3p2T+ZWdi
MSPrwfiRJr5p7OMtWBlLveCBV85+R3feidYbQTXlvVTdToY+GN6mFE1x6zG2dvLD
s4UuRnipmvGcFYhIRTX8J4AJiN8VMtW0TNXscRMudpz/FAVtsRrggRaThYg4f/rI
oAPMqKMsS4JoYhxs9ED6E6/tpj3XmSg1ekaXhgacYSYHeyxizZwoOFVCLH3TG5sF
sD6CYNnukYol8bR+VRpvHftIYss5Yz+DyyhYEAMJm1CfQo+xoGR3D0ozbT3hUnzm
fEoOhmSp3sALrFVE4iJSuajoh2/3xhmcyi3xZdWyq4F8hpb+URyaoW0CAwEAAaMt
MCswKQYDVR0RBCIwIIINbXVzaWMuMTYzLmNvbYIPKi5tdXNpYy4xNjMuY29tMA0G
CSqGSIb3DQEBCwUAA4IBAQB32SVz5jHUYv3ZG7SNF/LFJ904/LI8QlTe9R+Abb9z
bpXmQeo4pvNNOk3LgcTyuSIPQSHEFn32hk/MedB6Q2cKaGVKQq7Usne1jsV0JirG
wMx3PTcKPnX+XexRY8s6v6cNKSx5YlMQNFeH7p8MgKqdM/UX/dNCxT04X/ClmP1K
/rKqonXn4i3wmWprl7Q7Z1wqt0ygQRkNJKqdYKTu4oQcPON8/dRcseYdJzSoK2/G
H6cOJwKrRLzuUqQlphe6wyUsyTIbIJiFu1a1Gml6zB4lhLZhL89H2lYwdS8wWlc+
M+wYi+XTM/ylNHEIoKsOe2nscnwi/hTfHJOPPchHbEuM
-----END CERTIFICATE-----

View File

@ -0,0 +1,177 @@
const fs = require('fs')
const net = require('net')
const path = require('path')
const parse = require('url').parse
const hook = require('./hook')
const request = require('./request')
const proxy = {
core: {
mitm: (req, res) => {
if(req.url == '/proxy.pac'){
let url = parse('http://' + req.headers.host)
res.writeHead(200, {'Content-Type': 'application/x-ns-proxy-autoconfig'})
res.end(`
function FindProxyForURL(url, host) {
if (${hook.target.host.map(host => (`host == '${host}'`)).join(' || ')}) {
return 'PROXY ${url.hostname}:${url.port || 80}'
}
return 'DIRECT'
}
`)
}
else{
const ctx = {res, req}
Promise.resolve()
.then(() => proxy.authenticate(ctx))
.then(() => hook.request.before(ctx))
.then(() => proxy.filter(ctx))
.then(() => proxy.log(ctx))
.then(() => proxy.mitm.request(ctx))
.then(() => hook.request.after(ctx))
.then(() => proxy.mitm.response(ctx))
.catch(() => proxy.mitm.close(ctx))
}
},
tunnel: (req, socket, head) => {
const ctx = {req, socket, head}
Promise.resolve()
.then(() => proxy.authenticate(ctx))
.then(() => hook.connect.before(ctx))
.then(() => proxy.filter(ctx))
.then(() => proxy.log(ctx))
.then(() => proxy.tunnel.connect(ctx))
.then(() => proxy.tunnel.handshake(ctx))
.then(() => proxy.tunnel.pipe(ctx))
.catch(() => proxy.tunnel.close(ctx))
}
},
log: ctx => {
const mark = {close: '|', blank: '-', proxy: '>'}[ctx.decision] || '>'
if(ctx.socket)
console.log('TUNNEL', mark, ctx.req.url)
else
console.log('MITM', mark, parse(ctx.req.url).host, ctx.req.socket.encrypted ? '(ssl)' : '')
},
authenticate: ctx => {
const req = ctx.req
const res = ctx.res
const socket = ctx.socket
let credential = Buffer.from((req.headers['proxy-authorization'] || '').split(/\s+/).pop() || '', 'base64').toString()
if(server.authentication && credential != server.authentication){
if(socket)
socket.write('HTTP/1.1 407 Proxy Auth Required\r\nProxy-Authenticate: Basic realm="realm"\r\n\r\n')
else
res.writeHead(407, {'proxy-authenticate': 'Basic realm="realm"'})
return Promise.reject(ctx.error = 'authenticate')
}
},
filter: ctx => {
const url = parse((ctx.socket ? 'https://' : '') + ctx.req.url)
const match = pattern => url.href.search(new RegExp(pattern, 'g')) != -1
if(!(ctx.decision || ctx.req.local)){
try{
let allow = server.whitelist.some(match)
let deny = server.blacklist.some(match)
// console.log('allow', allow, 'deny', deny)
if(!allow && deny){
return Promise.reject(ctx.error = 'filter')
}
}
catch(error){
ctx.error = error
}
}
},
mitm: {
request: ctx => new Promise((resolve, reject) => {
if(ctx.decision === 'close') return reject(ctx.error = ctx.decision)
const req = ctx.req
const url = parse(req.url)
const options = request.configure(req.method, url, req.headers)
ctx.proxyReq = request.create(url)(options)
.on('response', proxyRes => {
return resolve(ctx.proxyRes = proxyRes)
})
.on('error', error => {
return reject(ctx.error = error)
})
req.readable ? req.pipe(ctx.proxyReq) : ctx.proxyReq.end(req.body)
}),
response: ctx => {
const res = ctx.res
const proxyRes = ctx.proxyRes
res.writeHead(proxyRes.statusCode, proxyRes.headers)
proxyRes.readable ? proxyRes.pipe(res) : res.end(proxyRes.body)
},
close: ctx => {
ctx.res.socket.end()
}
},
tunnel: {
connect: ctx => new Promise((resolve, reject) => {
if(ctx.decision === 'close') return reject(ctx.error = ctx.decision)
const req = ctx.req
const socket = ctx.socket
const head = ctx.head
const url = parse('https://' + req.url)
socket.on('error', error => {
return reject(ctx.error = error)
})
if(global.proxy && !req.local){
const options = request.configure(req.method, url, req.headers)
request.create(proxy)(options)
.on('connect', (_, proxySocket) => {
return resolve(ctx.proxySocket = proxySocket)
})
.on('error', error => {
return reject(ctx.error = error)
})
.end()
}
else{
const proxySocket = net.connect(url.port || 443, request.translate(url.hostname))
.on('connect', () => {
proxySocket.write(head)
return resolve(ctx.proxySocket = proxySocket)
})
.on('error', error => {
return reject(ctx.error = error)
})
}
}),
handshake: ctx => {
const req = ctx.req
const socket = ctx.socket
const message = `HTTP/${req.httpVersion} 200 Connection established\r\n\r\n`
socket.write(message)
},
pipe: ctx => new Promise((resolve, reject) => {
if(ctx.decision === 'blank') return reject(ctx.error = ctx.decision)
const socket = ctx.socket
const proxySocket = ctx.proxySocket
socket.pipe(proxySocket)
proxySocket.pipe(socket)
}),
close: ctx => {
ctx.socket.end()
}
}
}
const options = {
key: fs.readFileSync(path.join(__dirname, 'server.key')),
cert: fs.readFileSync(path.join(__dirname, 'server.crt'))
}
const server = {
http: require('http').createServer().on('request', proxy.core.mitm).on('connect', proxy.core.tunnel),
https: require('https').createServer(options).on('request', proxy.core.mitm).on('connect', proxy.core.tunnel)
}
server.whitelist = []
server.blacklist = ['//127\.\d+\.\d+\.\d+', '//localhost']
server.authentication = null
module.exports = server

View File

@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEAuhsQLKnASh7xWoz8Um8FFHenZP5lZ2IxI+vB+JEmvmns4y1Y
GUu94IFXzn5Hd96J1htBNeW9VN1Ohj4Y3qYUTXHrMbZ28sOzhS5GeKma8ZwViEhF
NfwngAmI3xUy1bRM1exxEy52nP8UBW2xGuCBFpOFiDh/+sigA8yooyxLgmhiHGz0
QPoTr+2mPdeZKDV6RpeGBpxhJgd7LGLNnCg4VUIsfdMbmwWwPoJg2e6RiiXxtH5V
Gm8d+0hiyzljP4PLKFgQAwmbUJ9Cj7GgZHcPSjNtPeFSfOZ8Sg6GZKnewAusVUTi
IlK5qOiHb/fGGZzKLfFl1bKrgXyGlv5RHJqhbQIDAQABAoIBAEmAvtalBMlBh1mY
LV/xcTQwPfDpeOtoILhrOOUPjxnNhD4FfrIe9BNjgmaQAXIadp4VjZ/X6PtHnOfw
RqpJNeOQhq/PvRMMsC59pF+rvQKH/wkgYhV8Ta2IFoLlQHqfB2nGRLKquzYumJ28
QSK4YMOl6CtxBTrrWiemAUTRDdGm8tARiipJH1SEJrS6d/NoRoJx2vixFgD2eS6X
bjnhGjIzvX/w5FWjctqj+SFITP1UI62b6DyWsPOkoosKNteK+Ulz+K6ZFvOx7day
XgUoTcVpwCVr2dVGhJtOrbKPcl1jYCYHJAHwzUZND4x4yftm1mnnsi3bthYqbtHQ
vxLE9YECgYEA9hiZxwiVvLjSe1xT/D75HbB8S1XSnwzpMmqgzStguxCQ0Qg5yiLI
UKRDY8UZvEDV4i2bQGy7mk8lFvX1q2z7Q30+dtT9r2N9a6ujMk5RMfo2BZg/poI6
yDWe2tKUg9cTwfgni4TutLOYkpz3VDPIQHs3k2mpNh7f+8X4RIybDqkCgYEAwZhp
uWMV38Bb0WytswHXL1dRuwBskKqALUBY61dtXkyBuocj8AuRRxfxfZpgJRrHFxDX
O9bQ2nxpVlwKsR6DJDUdxU3+kvwyPfseU5XUBey8WdkuAKD7cKZOHMhFVWccks0U
YJzykNrxB+rGTiwVKa0MOhipuJ7boerwwaN2SyUCgYBP9Ow5o4tq9q3EUNoksZ0k
zUuE+oxlCr/VlplKL9bM0HQMxlxoVWa59LTEfKyA4pvbUbAIfYtydlZ5oE5CdTUp
105tM4R88Jk2W1y5ooJ093OH29CKW/OXSvyi4hpIv592vRa0GOupoFRpBkDBhdWB
RcdnyMOmht+FIOwp8XkLiQKBgAUK3j4Y6ZnxXbLfvMp70soF4TgYs7s05a/IDEjc
9xlMrthX6sS22GrcocqeucBdqS/dnW2Ok9QNB4VbUl/4pnvL8mGQPYBAl2Jr5wdQ
ULxyxRkmAf+8MbBmdIRlZwDpdaIRO2Wk0OCbA0osgEvK9CYovrfIqqsHYDsgbnLs
ugkNAoGBAJok06BN05caPXXLQ2pMwI/7mjcZFjcOMxSloYi7LFkxlyvoTqReAeSa
yOb6W/7obS1X8ms/EAkqiyzJuPtNZJCW/nvV0iCoZ/NxLuyHnFaO344GBAweol+S
Jx0MY8KuDCyeGErc2xdz/yr3ld2PSTq71dhBluGyba2YX+peJ2Yv
-----END RSA PRIVATE KEY-----

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 211 KiB

View File

@ -0,0 +1,57 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=qBittorrent
PKG_VERSION:=4.1.3
PKG_RELEASE=1
PKG_SOURCE_PROTO:=git
PKG_SOURCE_URL:=https://github.com/qbittorrent/qBittorrent.git
PKG_SOURCE_SUBDIR:=$(PKG_NAME)-$(PKG_VERSION)
PKG_SOURCE_VERSION:=a574c4a70a5a3293d2f6135952c301d930d23627
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
PKG_LICENSE:=GPL-2.0+
PKG_LICENSE_FILES:=COPYING
PKG_BUILD_PARALLEL:=1
PKG_INSTALL:=1
PKG_USE_MIPS16:=0
include $(INCLUDE_DIR)/package.mk
define Package/qBittorrent
SUBMENU:=BitTorrent
SECTION:=net
CATEGORY:=Network
TITLE:=bittorrent client programmed in C++ / Qt
URL:=https://www.qbittorrent.org/
DEPENDS:=+qt5-core +qt5-network +qt5-xml +rblibtorrent
endef
define Package/qBittorrent/description
qBittorrent is a bittorrent client programmed in C++ / Qt that uses
libtorrent (sometimes called libtorrent-rasterbar) by Arvid Norberg.
It aims to be a good alternative to all other bittorrent clients out
there. qBittorrent is fast, stable and provides unicode support as
well as many features.
endef
CONFIGURE_VARS += \
QT_QMAKE="$(TOOLCHAIN_DIR)/bin" \
PKG_CONFIG_PATH="$(TOOLCHAIN_DIR)/lib/pkgconfig"
CONFIGURE_ARGS += \
--disable-gui \
--with-boost=$(STAGING_DIR)/usr
MAKE_VARS += \
INSTALL_ROOT="$(PKG_INSTALL_DIR)"
define Package/qBittorrent/install
$(INSTALL_DIR) $(1)/usr/man/man1
$(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/man/man1/qbittorrent-nox.1 $(1)/usr/man/man1/qbittorrent-nox.1
$(INSTALL_DIR) $(1)/usr/bin
$(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/bin/qbittorrent-nox $(1)/usr/bin
endef
$(eval $(call BuildPackage,qBittorrent))

213
package/lean/qt5/Makefile Normal file
View File

@ -0,0 +1,213 @@
#
# Copyright (C) 2013 Riccardo Ferrazzo <f.riccardo87@gmail.com>
# Copyright (C) 2017 Paweł Knioła <pawel.kn@gmail.com>
#
# This is free software, licensed under the GNU General Public License v2.
# See /LICENSE for more information.
#
include $(TOPDIR)/rules.mk
PKG_NAME:=qt5
PKG_VERSION:=5.8
PKG_RELEASE:=0
PKG_MD5SUM:=a9f2494f75f966e2f22358ec367d8f41
PKG_SOURCE:=qt-everywhere-opensource-src-$(PKG_VERSION).$(PKG_RELEASE).tar.gz
PKG_SOURCE_URL:=http://download.qt-project.org/archive/qt/$(PKG_VERSION)/$(PKG_VERSION).$(PKG_RELEASE)/single
PKG_BUILD_DIR=$(BUILD_DIR)/qt-everywhere-opensource-src-$(PKG_VERSION).$(PKG_RELEASE)
PKG_BUILD_PARALLEL:=1
PKG_USE_MIPS16:=0
include $(INCLUDE_DIR)/package.mk
# not using sstrip here as this fucks up the .so's somehow
STRIP:=/bin/true
RSTRIP:= \
NM="$(TOOLCHAIN_DIR)/bin/$(TARGET_CROSS)nm" \
STRIP="$(STRIP)" \
STRIP_KMOD="$(STRIP)" \
$(SCRIPT_DIR)/rstrip.sh
define Package/qt5/Default
SECTION:=libs
CATEGORY:=Libraries
SUBMENU:=Qt5
TITLE:=qt5
URL:=http://qt-project.org
DEPENDS:=+libatomic +libstdcpp +libopenssl +zlib @!LINUX_2_6
endef
define Package/qt5-core
$(call Package/qt5/Default)
TITLE+=core
endef
define Package/qt5-concurrent
$(call Package/qt5/Default)
TITLE+=concurrent
DEPENDS+=+qt5-core
endef
define Package/qt5-network
$(call Package/qt5/Default)
TITLE+=network
DEPENDS+=+qt5-core
endef
define Package/qt5-widgets
$(call Package/qt5/Default)
TITLE+=widgets
DEPENDS+=+qt5-core +qt5-network
endef
define Package/qt5-sql
$(call Package/qt5/Default)
TITLE+=sql
DEPENDS+=+qt5-core
endef
define Package/qt5-xml
$(call Package/qt5/Default)
TITLE+=xml
DEPENDS+=+qt5-core
endef
define Package/qt5-xmlpatterns
$(call Package/qt5/Default)
TITLE+=xmlpatterns
DEPENDS+=+qt5-core +qt5-network
endef
define Package/qt5-test
$(call Package/qt5/Default)
TITLE+=test
DEPENDS+=+qt5-core
endef
define Build/Configure
$(INSTALL_DIR) $(PKG_BUILD_DIR)/qtbase/mkspecs/linux-openwrt-g++
$(CP) ./files/qplatformdefs.h $(PKG_BUILD_DIR)/qtbase/mkspecs/linux-openwrt-g++/qplatformdefs.h
$(CP) ./files/qmake.conf $(PKG_BUILD_DIR)/qtbase/mkspecs/linux-openwrt-g++/qmake.conf
$(SED) 's@$$$$(TARGET_CROSS)@$(TARGET_CROSS)@g' $(PKG_BUILD_DIR)/qtbase/mkspecs/linux-openwrt-g++/qmake.conf
( cd $(PKG_BUILD_DIR) ; \
./configure \
-prefix $(TOOLCHAIN_DIR) \
-xplatform linux-openwrt-g++ \
-opensource \
-confirm-license \
-no-iconv \
-no-pch \
-no-rpath \
-no-qml-debug \
-no-strip \
-no-cups \
-no-dbus \
-no-eglfs \
-no-kms \
-no-opengl \
-no-directfb \
-no-xcb \
-qt-zlib \
-qt-pcre \
-qt-freetype \
-make libs \
-nomake examples \
-nomake tools \
-nomake tests \
-skip qtdeclarative \
-skip qtmultimedia \
-skip activeqt \
-skip qtdoc \
-skip qtconnectivity \
-skip wayland \
-skip qtscript \
-v \
)
endef
define Build/Compile
$(MAKE) $(PKG_JOBS) -C $(PKG_BUILD_DIR)
endef
define Package/qt5-core/install
$(INSTALL_DIR) $(1)/usr/lib/
$(CP) $(PKG_BUILD_DIR)/qtbase/lib/libQt5Core.so* $(1)/usr/lib/
$(CP) $(PKG_BUILD_DIR)/qtbase/lib/libQt5Core.prl $(1)/usr/lib/
$(CP) $(PKG_BUILD_DIR)/qtbase/lib/libQt5Core.la $(1)/usr/lib/
endef
define Package/qt5-concurrent/install
$(INSTALL_DIR) $(1)/usr/lib/
$(CP) $(PKG_BUILD_DIR)/qtbase/lib/libQt5Concurrent.so* $(1)/usr/lib/
$(CP) $(PKG_BUILD_DIR)/qtbase/lib/libQt5Concurrent.prl $(1)/usr/lib/
$(CP) $(PKG_BUILD_DIR)/qtbase/lib/libQt5Concurrent.la $(1)/usr/lib/
endef
define Package/qt5-network/install
$(INSTALL_DIR) $(1)/usr/lib/
$(CP) $(PKG_BUILD_DIR)/qtbase/lib/libQt5Network.so* $(1)/usr/lib/
$(CP) $(PKG_BUILD_DIR)/qtbase/lib/libQt5Network.prl $(1)/usr/lib/
$(CP) $(PKG_BUILD_DIR)/qtbase/lib/libQt5Network.la $(1)/usr/lib/
endef
define Package/qt5-widgets/install
$(INSTALL_DIR) $(1)/usr/lib/
$(INSTALL_DIR) $(1)/usr/lib/Qt/plugins/generic/
$(INSTALL_DIR) $(1)/usr/lib/Qt/plugins/platforms/
$(INSTALL_DIR) $(1)/usr/lib/Qt/plugins/imageformats/
$(INSTALL_DIR) $(1)/usr/lib/fonts/
$(CP) $(PKG_BUILD_DIR)/qtbase/lib/libQt5Gui.so* $(1)/usr/lib/
$(CP) $(PKG_BUILD_DIR)/qtbase/lib/libQt5Gui.prl $(1)/usr/lib/
$(CP) $(PKG_BUILD_DIR)/qtbase/lib/libQt5Gui.la $(1)/usr/lib/
$(CP) $(PKG_BUILD_DIR)/qtsvg/lib/libQt5Svg.so* $(1)/usr/lib/
$(CP) $(PKG_BUILD_DIR)/qtsvg/lib/libQt5Svg.prl $(1)/usr/lib/
$(CP) $(PKG_BUILD_DIR)/qtsvg/lib/libQt5Svg.la $(1)/usr/lib/
$(CP) $(PKG_BUILD_DIR)/qtbase/lib/libQt5Widgets.so* $(1)/usr/lib/
$(CP) $(PKG_BUILD_DIR)/qtbase/lib/libQt5Widgets.prl $(1)/usr/lib/
$(CP) $(PKG_BUILD_DIR)/qtbase/lib/libQt5Widgets.la $(1)/usr/lib/
$(CP) $(PKG_BUILD_DIR)/qtbase/plugins/generic/*.so $(1)/usr/lib/Qt/plugins/generic/
$(CP) $(PKG_BUILD_DIR)/qtbase/plugins/platforms/*.so $(1)/usr/lib/Qt/plugins/platforms/
$(CP) $(PKG_BUILD_DIR)/qtbase/plugins/imageformats/*.so $(1)/usr/lib/Qt/plugins/imageformats/
$(CP) $(PKG_BUILD_DIR)/qtbase/lib/fonts/* $(1)/usr/lib/fonts/
endef
define Package/qt5-sql/install
$(INSTALL_DIR) $(1)/usr/lib/
$(INSTALL_DIR) $(1)/usr/lib/Qt/plugins/sqldrivers/
$(CP) $(PKG_BUILD_DIR)/qtbase/lib/libQt5Sql.so* $(1)/usr/lib/
$(CP) $(PKG_BUILD_DIR)/qtbase/lib/libQt5Sql.prl $(1)/usr/lib/
$(CP) $(PKG_BUILD_DIR)/qtbase/lib/libQt5Sql.la $(1)/usr/lib/
$(CP) $(PKG_BUILD_DIR)/qtbase/plugins/sqldrivers/*.so $(1)/usr/lib/Qt/plugins/sqldrivers/
endef
define Package/qt5-xml/install
$(INSTALL_DIR) $(1)/usr/lib/
$(CP) $(PKG_BUILD_DIR)/qtbase/lib/libQt5Xml.so* $(1)/usr/lib/
$(CP) $(PKG_BUILD_DIR)/qtbase/lib/libQt5Xml.prl $(1)/usr/lib/
$(CP) $(PKG_BUILD_DIR)/qtbase/lib/libQt5Xml.la $(1)/usr/lib/
endef
define Package/qt5-xmlpatterns/install
$(INSTALL_DIR) $(1)/usr/lib/
$(CP) $(PKG_BUILD_DIR)/qtxmlpatterns/lib/libQt5XmlPatterns.so* $(1)/usr/lib/
$(CP) $(PKG_BUILD_DIR)/qtxmlpatterns/lib/libQt5XmlPatterns.prl $(1)/usr/lib/
$(CP) $(PKG_BUILD_DIR)/qtxmlpatterns/lib/libQt5XmlPatterns.la $(1)/usr/lib/
endef
define Package/qt5-test/install
$(INSTALL_DIR) $(1)/usr/lib/
$(CP) $(PKG_BUILD_DIR)/qtbase/lib/libQt5Test.so* $(1)/usr/lib/
$(CP) $(PKG_BUILD_DIR)/qtbase/lib/libQt5Test.prl $(1)/usr/lib/
$(CP) $(PKG_BUILD_DIR)/qtbase/lib/libQt5Test.la $(1)/usr/lib/
endef
$(eval $(call BuildPackage,qt5-core))
$(eval $(call BuildPackage,qt5-concurrent))
$(eval $(call BuildPackage,qt5-network))
$(eval $(call BuildPackage,qt5-widgets))
$(eval $(call BuildPackage,qt5-sql))
$(eval $(call BuildPackage,qt5-xml))
$(eval $(call BuildPackage,qt5-xmlpatterns))
$(eval $(call BuildPackage,qt5-test))

View File

@ -0,0 +1,33 @@
#
# qmake configuration for building with linux-openwrt-g++
#
MAKEFILE_GENERATOR = UNIX
CONFIG += incremental
QMAKE_INCREMENTAL_STYLE = sublib
include(../common/linux.conf)
include(../common/gcc-base-unix.conf)
include(../common/g++-unix.conf)
# modifications to g++.conf
QMAKE_CC = $(TARGET_CROSS)gcc
QMAKE_CXX = $(TARGET_CROSS)g++
QMAKE_CFLAGS += $$(TARGET_CFLAGS)
QMAKE_CXXFLAGS += $$(TARGET_CXXFLAGS)
QMAKE_LFLAGS += $$(TARGET_LDFLAGS)
QMAKE_LINK_C = $$QMAKE_CC
QMAKE_LINK_C_SHLIB = $$QMAKE_CC
QMAKE_LINK = $$QMAKE_CXX
QMAKE_LINK_SHLIB = $$QMAKE_CXX
# modifications to linux.conf
QMAKE_AR = $(TARGET_CROSS)ar cqs
QMAKE_OBJCOPY = $(TARGET_CROSS)objcopy
QMAKE_NM = $(TARGET_CROSS)nm -P
QMAKE_STRIP = $(TARGET_CROSS)strip
load(qt_config)

View File

@ -0,0 +1,40 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the qmake spec of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or (at your option) the GNU General
** Public license version 3 or any later version approved by the KDE Free
** Qt Foundation. The licenses are as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
** https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "../linux-g++/qplatformdefs.h"

View File

@ -0,0 +1,71 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=rblibtorrent
PKG_VERSION:=1.1.11
PKG_RELEASE=1
PKG_SOURCE_PROTO:=git
PKG_SOURCE_URL:=https://github.com/arvidn/libtorrent.git
PKG_SOURCE_SUBDIR:=$(PKG_NAME)-$(PKG_VERSION)
PKG_SOURCE_VERSION:=25a1f12991b4af1e133b966fae963168116306e9
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
PKG_LICENSE:=BSD
PKG_LICENSE_FILES:=COPYING
PKG_USE_MIPS16:=0
PKG_BUILD_PARALLEL:=1
PKG_INSTALL:=1
include $(INCLUDE_DIR)/package.mk
define Package/rblibtorrent
SECTION:=libs
CATEGORY:=Libraries
TITLE:=Rasterbar BitTorrent library
URL:=https://www.libtorrent.org/
DEPENDS:=+libstdcpp +libopenssl +boost +boost-system +boost-chrono +boost-random
MAINTAINER:=Arvid Norberg <arvid@libtorrent.org>
endef
define Package/rblibtorrent/description
Rasterbar libtorrent is a C++ library that aims to be a good alternative to
all the other bittorrent implementations around. It is a library and not a
full featured client, although it comes with a working example client.
endef
TARGET_CFLAGS += $(if $(CONFIG_SOFT_FLOAT),-DBOOST_NO_FENV_H) -fPIC
EXTRA_CXXFLAGS += $(if $(CONFIG_GCC_VERSION_4_8),-std=gnu++11,-std=gnu++14)
TARGET_LDFLAGS += -lstdc++
CONFIGURE_ARGS += \
--prefix /usr \
--enable-shared \
--enable-static \
--disable-debug \
--enable-encryption \
--with-boost=$(STAGING_DIR)/usr
define Build/Configure
cd $(PKG_BUILD_DIR) ; \
sh autotool.sh
$(call Build/Configure/Default)
endef
define Build/InstallDev
$(INSTALL_DIR) $(1)/usr/include
$(CP) $(PKG_INSTALL_DIR)/usr/include/libtorrent $(1)/usr/include/
$(INSTALL_DIR) $(1)/usr/lib
$(CP) $(PKG_INSTALL_DIR)/usr/lib/libtorrent-rasterbar.{a,so*} $(1)/usr/lib/
$(INSTALL_DIR) $(1)/usr/lib/pkgconfig
$(CP) $(PKG_INSTALL_DIR)/usr/lib/pkgconfig/libtorrent-rasterbar.pc $(1)/usr/lib/pkgconfig/
endef
define Package/rblibtorrent/install
$(INSTALL_DIR) $(1)/usr/lib
$(CP) $(PKG_INSTALL_DIR)/usr/lib/libtorrent-rasterbar.so.* $(1)/usr/lib/
endef
$(eval $(call BuildPackage,rblibtorrent))

View File

@ -12,7 +12,7 @@
# ZLIB_INFLATE; DECOMPRESS_GZIP is just a wrapper.)
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -135,6 +135,7 @@ obj-$(CONFIG_XZ_DEC) += xz/
@@ -146,6 +146,7 @@ obj-$(CONFIG_XZ_DEC) += xz/
obj-$(CONFIG_RAID6_PQ) += raid6/
obj-$(CONFIG_LZMA_COMPRESS) += lzma/
obj-$(CONFIG_LZMA_DECOMPRESS) += lzma/

View File

@ -17,7 +17,7 @@ Signed-off-by: Phil Elwell <phil@raspberrypi.org>
--- a/kernel/cgroup/cgroup.c
+++ b/kernel/cgroup/cgroup.c
@@ -5162,6 +5162,8 @@ int __init cgroup_init_early(void)
@@ -5168,6 +5168,8 @@ int __init cgroup_init_early(void)
}
static u16 cgroup_disable_mask __initdata;
@ -26,7 +26,7 @@ Signed-off-by: Phil Elwell <phil@raspberrypi.org>
/**
* cgroup_init - cgroup initialization
@@ -5200,6 +5202,12 @@ int __init cgroup_init(void)
@@ -5206,6 +5208,12 @@ int __init cgroup_init(void)
mutex_unlock(&cgroup_mutex);
@ -39,7 +39,7 @@ Signed-off-by: Phil Elwell <phil@raspberrypi.org>
for_each_subsys(ss, ssid) {
if (ss->early_init) {
struct cgroup_subsys_state *css =
@@ -5583,6 +5591,28 @@ static int __init cgroup_disable(char *s
@@ -5589,6 +5597,28 @@ static int __init cgroup_disable(char *s
}
__setup("cgroup_disable=", cgroup_disable);

View File

@ -39,7 +39,7 @@
/**
--- a/drivers/spi/spi.c
+++ b/drivers/spi/spi.c
@@ -1027,6 +1027,9 @@ static int spi_transfer_one_message(stru
@@ -1029,6 +1029,9 @@ static int spi_transfer_one_message(stru
list_for_each_entry(xfer, &msg->transfers, transfer_list) {
trace_spi_transfer_start(msg, xfer);

View File

@ -12,7 +12,7 @@
/* For Max performance, the MRRS must be set to the largest supported
--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -845,7 +845,7 @@ enum pcie_bus_config_types {
@@ -847,7 +847,7 @@ enum pcie_bus_config_types {
PCIE_BUS_DEFAULT, /* ensure MPS matches upstream bridge */
PCIE_BUS_SAFE, /* use largest MPS boot-time devices support */
PCIE_BUS_PERFORMANCE, /* use MPS and MRRS for best performance */

View File

@ -39,7 +39,7 @@
/**
--- a/drivers/spi/spi.c
+++ b/drivers/spi/spi.c
@@ -1018,6 +1018,9 @@ static int spi_transfer_one_message(stru
@@ -1020,6 +1020,9 @@ static int spi_transfer_one_message(stru
list_for_each_entry(xfer, &msg->transfers, transfer_list) {
trace_spi_transfer_start(msg, xfer);

View File

@ -1,6 +1,6 @@
--- a/drivers/pci/probe.c
+++ b/drivers/pci/probe.c
@@ -2629,7 +2629,8 @@ static void pcie_write_mrrs(struct pci_d
@@ -2634,7 +2634,8 @@ static void pcie_write_mrrs(struct pci_d
* In the "safe" case, do not configure the MRRS. There appear to be
* issues with setting MRRS to 0 on a number of devices.
*/
@ -12,7 +12,7 @@
/*
--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -866,7 +866,7 @@ enum pcie_bus_config_types {
@@ -868,7 +868,7 @@ enum pcie_bus_config_types {
PCIE_BUS_DEFAULT, /* Ensure MPS matches upstream bridge */
PCIE_BUS_SAFE, /* Use largest MPS boot-time devices support */
PCIE_BUS_PERFORMANCE, /* Use MPS and MRRS for best performance */

View File

@ -56,7 +56,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
*/
--- a/include/linux/skbuff.h
+++ b/include/linux/skbuff.h
@@ -2512,6 +2512,10 @@ static inline int pskb_trim(struct sk_bu
@@ -2514,6 +2514,10 @@ static inline int pskb_trim(struct sk_bu
return (len < skb->len) ? __pskb_trim(skb, len) : 0;
}
@ -67,7 +67,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
/**
* pskb_trim_unique - remove end from a paged unique (not cloned) buffer
* @skb: buffer to alter
@@ -2642,16 +2646,6 @@ static inline struct sk_buff *dev_alloc_
@@ -2645,16 +2649,6 @@ static inline struct sk_buff *dev_alloc_
}

View File

@ -30,7 +30,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
#endif /* _KOBJECT_H_ */
--- a/lib/kobject_uevent.c
+++ b/lib/kobject_uevent.c
@@ -599,6 +599,43 @@ int add_uevent_var(struct kobj_uevent_en
@@ -602,6 +602,43 @@ int add_uevent_var(struct kobj_uevent_en
EXPORT_SYMBOL_GPL(add_uevent_var);
#if defined(CONFIG_NET)

View File

@ -98,7 +98,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
--- a/init/Kconfig
+++ b/init/Kconfig
@@ -1994,6 +1994,13 @@ config TRIM_UNUSED_KSYMS
@@ -1997,6 +1997,13 @@ config TRIM_UNUSED_KSYMS
If unsure, or if you need to build out-of-tree modules, say N.
@ -114,7 +114,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
config MODULES_TREE_LOOKUP
--- a/kernel/module.c
+++ b/kernel/module.c
@@ -3004,9 +3004,11 @@ static int setup_load_info(struct load_i
@@ -3009,9 +3009,11 @@ static int setup_load_info(struct load_i
static int check_modinfo(struct module *mod, struct load_info *info, int flags)
{
@ -127,7 +127,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
if (flags & MODULE_INIT_IGNORE_VERMAGIC)
modmagic = NULL;
@@ -3027,6 +3029,7 @@ static int check_modinfo(struct module *
@@ -3032,6 +3034,7 @@ static int check_modinfo(struct module *
mod->name);
add_taint_module(mod, TAINT_OOT_MODULE, LOCKDEP_STILL_OK);
}

View File

@ -15,7 +15,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
--- a/Makefile
+++ b/Makefile
@@ -1231,7 +1231,6 @@ all: modules
@@ -1224,7 +1224,6 @@ all: modules
PHONY += modules
modules: $(vmlinux-dirs) $(if $(KBUILD_BUILTIN),vmlinux) modules.builtin
@ -23,7 +23,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
@$(kecho) ' Building modules, stage 2.';
$(Q)$(MAKE) -f $(srctree)/scripts/Makefile.modpost
@@ -1260,7 +1259,6 @@ _modinst_:
@@ -1253,7 +1252,6 @@ _modinst_:
rm -f $(MODLIB)/build ; \
ln -s $(CURDIR) $(MODLIB)/build ; \
fi

View File

@ -33,7 +33,7 @@ Signed-off-by: Gabor Juhos <juhosg@openwrt.org>
# Read KERNELRELEASE from include/config/kernel.release (if it exists)
KERNELRELEASE = $(shell cat include/config/kernel.release 2> /dev/null)
KERNELVERSION = $(VERSION)$(if $(PATCHLEVEL),.$(PATCHLEVEL)$(if $(SUBLEVEL),.$(SUBLEVEL)))$(EXTRAVERSION)
@@ -787,11 +792,6 @@ ifdef CONFIG_DEBUG_SECTION_MISMATCH
@@ -780,11 +785,6 @@ ifdef CONFIG_DEBUG_SECTION_MISMATCH
KBUILD_CFLAGS += $(call cc-option, -fno-inline-functions-called-once)
endif

View File

@ -56,7 +56,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
*/
--- a/include/linux/skbuff.h
+++ b/include/linux/skbuff.h
@@ -2527,6 +2527,10 @@ static inline int pskb_trim(struct sk_bu
@@ -2529,6 +2529,10 @@ static inline int pskb_trim(struct sk_bu
return (len < skb->len) ? __pskb_trim(skb, len) : 0;
}
@ -67,7 +67,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
/**
* pskb_trim_unique - remove end from a paged unique (not cloned) buffer
* @skb: buffer to alter
@@ -2657,16 +2661,6 @@ static inline struct sk_buff *dev_alloc_
@@ -2660,16 +2664,6 @@ static inline struct sk_buff *dev_alloc_
}

View File

@ -30,7 +30,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
#endif /* _KOBJECT_H_ */
--- a/lib/kobject_uevent.c
+++ b/lib/kobject_uevent.c
@@ -685,6 +685,43 @@ int add_uevent_var(struct kobj_uevent_en
@@ -688,6 +688,43 @@ int add_uevent_var(struct kobj_uevent_en
EXPORT_SYMBOL_GPL(add_uevent_var);
#if defined(CONFIG_NET)

View File

@ -41,7 +41,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
+#endif
--- a/init/Kconfig
+++ b/init/Kconfig
@@ -1046,6 +1046,10 @@ config RELAY
@@ -1049,6 +1049,10 @@ config RELAY
If unsure, say N.

View File

@ -1119,7 +1119,7 @@ Signed-off-by: Alexandros C. Couloumbis <alex@ozo.com>
ifdef CONFIG_FUNCTION_TRACER
ORIG_CFLAGS := $(KBUILD_CFLAGS)
KBUILD_CFLAGS = $(subst $(CC_FLAGS_FTRACE),,$(ORIG_CFLAGS))
@@ -123,6 +133,8 @@ obj-$(CONFIG_ZSTD_COMPRESS) += zstd/
@@ -134,6 +144,8 @@ obj-$(CONFIG_ZSTD_COMPRESS) += zstd/
obj-$(CONFIG_ZSTD_DECOMPRESS) += zstd/
obj-$(CONFIG_XZ_DEC) += xz/
obj-$(CONFIG_RAID6_PQ) += raid6/

View File

@ -9,7 +9,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
--- a/include/linux/skbuff.h
+++ b/include/linux/skbuff.h
@@ -2476,7 +2476,7 @@ static inline int pskb_network_may_pull(
@@ -2478,7 +2478,7 @@ static inline int pskb_network_may_pull(
* NET_IP_ALIGN(2) + ethernet_header(14) + IP_header(20/40) + ports(8)
*/
#ifndef NET_SKB_PAD

View File

@ -32,7 +32,7 @@ Signed-off-by: Gabor Juhos <juhosg@openwrt.org>
/* The Mellanox Tavor device gives false positive parity errors
* Mark this device with a broken_parity_status, to allow
* PCI scanning code to "skip" this now blacklisted device.
@@ -3077,6 +3078,7 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_I
@@ -3094,6 +3095,7 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_I
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x65f9, quirk_intel_mc_errata);
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x65fa, quirk_intel_mc_errata);
@ -40,7 +40,7 @@ Signed-off-by: Gabor Juhos <juhosg@openwrt.org>
/*
* Ivytown NTB BAR sizes are misreported by the hardware due to an erratum. To
@@ -3133,6 +3135,8 @@ static void fixup_debug_report(struct pc
@@ -3150,6 +3152,8 @@ static void fixup_debug_report(struct pc
}
}
@ -49,7 +49,7 @@ Signed-off-by: Gabor Juhos <juhosg@openwrt.org>
/*
* Some BIOS implementations leave the Intel GPU interrupts enabled,
* even though no one is handling them (f.e. i915 driver is never loaded).
@@ -3171,6 +3175,8 @@ DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_IN
@@ -3188,6 +3192,8 @@ DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_IN
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x010a, disable_igfx_irq);
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x0152, disable_igfx_irq);

View File

@ -14,7 +14,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
--- a/Makefile
+++ b/Makefile
@@ -661,12 +661,12 @@ KBUILD_CFLAGS += $(call cc-disable-warni
@@ -654,12 +654,12 @@ KBUILD_CFLAGS += $(call cc-disable-warni
KBUILD_CFLAGS += $(call cc-disable-warning, int-in-bool-context)
ifdef CONFIG_CC_OPTIMIZE_FOR_SIZE

View File

@ -13,7 +13,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
--- a/init/Kconfig
+++ b/init/Kconfig
@@ -1146,6 +1146,17 @@ config SYSCTL_ARCH_UNALIGN_ALLOW
@@ -1149,6 +1149,17 @@ config SYSCTL_ARCH_UNALIGN_ALLOW
the unaligned access emulation.
see arch/parisc/kernel/unaligned.c for reference

View File

@ -58,7 +58,7 @@
--- a/include/linux/compiler_types.h
+++ b/include/linux/compiler_types.h
@@ -260,8 +260,7 @@ struct ftrace_likely_data {
@@ -264,8 +264,7 @@ struct ftrace_likely_data {
* of extern inline functions at link time.
* A lot of inline functions can cause havoc with function tracing.
*/

View File

@ -1119,7 +1119,7 @@ Signed-off-by: Alexandros C. Couloumbis <alex@ozo.com>
ifdef CONFIG_FUNCTION_TRACER
ORIG_CFLAGS := $(KBUILD_CFLAGS)
KBUILD_CFLAGS = $(subst $(CC_FLAGS_FTRACE),,$(ORIG_CFLAGS))
@@ -128,6 +138,8 @@ obj-$(CONFIG_ZSTD_COMPRESS) += zstd/
@@ -139,6 +149,8 @@ obj-$(CONFIG_ZSTD_COMPRESS) += zstd/
obj-$(CONFIG_ZSTD_DECOMPRESS) += zstd/
obj-$(CONFIG_XZ_DEC) += xz/
obj-$(CONFIG_RAID6_PQ) += raid6/

View File

@ -9,7 +9,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
--- a/include/linux/skbuff.h
+++ b/include/linux/skbuff.h
@@ -2491,7 +2491,7 @@ static inline int pskb_network_may_pull(
@@ -2493,7 +2493,7 @@ static inline int pskb_network_may_pull(
* NET_IP_ALIGN(2) + ethernet_header(14) + IP_header(20/40) + ports(8)
*/
#ifndef NET_SKB_PAD

View File

@ -157,7 +157,7 @@ Signed-off-by: Jonas Gorski <jogo@openwrt.org>
case RTN_THROW:
case RTN_UNREACHABLE:
default:
@@ -3758,6 +3777,17 @@ static int ip6_pkt_prohibit_out(struct n
@@ -3774,6 +3793,17 @@ static int ip6_pkt_prohibit_out(struct n
return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_OUTNOROUTES);
}
@ -175,7 +175,7 @@ Signed-off-by: Jonas Gorski <jogo@openwrt.org>
/*
* Allocate a dst for local (unicast / anycast) address.
*/
@@ -4205,7 +4235,8 @@ static int rtm_to_fib6_config(struct sk_
@@ -4221,7 +4251,8 @@ static int rtm_to_fib6_config(struct sk_
if (rtm->rtm_type == RTN_UNREACHABLE ||
rtm->rtm_type == RTN_BLACKHOLE ||
rtm->rtm_type == RTN_PROHIBIT ||
@ -185,7 +185,7 @@ Signed-off-by: Jonas Gorski <jogo@openwrt.org>
cfg->fc_flags |= RTF_REJECT;
if (rtm->rtm_type == RTN_LOCAL)
@@ -5053,6 +5084,8 @@ static int ip6_route_dev_notify(struct n
@@ -5069,6 +5100,8 @@ static int ip6_route_dev_notify(struct n
#ifdef CONFIG_IPV6_MULTIPLE_TABLES
net->ipv6.ip6_prohibit_entry->dst.dev = dev;
net->ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(dev);
@ -194,7 +194,7 @@ Signed-off-by: Jonas Gorski <jogo@openwrt.org>
net->ipv6.ip6_blk_hole_entry->dst.dev = dev;
net->ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(dev);
#endif
@@ -5064,6 +5097,7 @@ static int ip6_route_dev_notify(struct n
@@ -5080,6 +5113,7 @@ static int ip6_route_dev_notify(struct n
in6_dev_put_clear(&net->ipv6.ip6_null_entry->rt6i_idev);
#ifdef CONFIG_IPV6_MULTIPLE_TABLES
in6_dev_put_clear(&net->ipv6.ip6_prohibit_entry->rt6i_idev);
@ -202,7 +202,7 @@ Signed-off-by: Jonas Gorski <jogo@openwrt.org>
in6_dev_put_clear(&net->ipv6.ip6_blk_hole_entry->rt6i_idev);
#endif
}
@@ -5258,6 +5292,15 @@ static int __net_init ip6_route_net_init
@@ -5274,6 +5308,15 @@ static int __net_init ip6_route_net_init
net->ipv6.ip6_blk_hole_entry->dst.ops = &net->ipv6.ip6_dst_ops;
dst_init_metrics(&net->ipv6.ip6_blk_hole_entry->dst,
ip6_template_metrics, true);
@ -218,7 +218,7 @@ Signed-off-by: Jonas Gorski <jogo@openwrt.org>
#endif
net->ipv6.sysctl.flush_delay = 0;
@@ -5276,6 +5319,8 @@ out:
@@ -5292,6 +5335,8 @@ out:
return ret;
#ifdef CONFIG_IPV6_MULTIPLE_TABLES
@ -227,7 +227,7 @@ Signed-off-by: Jonas Gorski <jogo@openwrt.org>
out_ip6_prohibit_entry:
kfree(net->ipv6.ip6_prohibit_entry);
out_ip6_null_entry:
@@ -5296,6 +5341,7 @@ static void __net_exit ip6_route_net_exi
@@ -5312,6 +5357,7 @@ static void __net_exit ip6_route_net_exi
#ifdef CONFIG_IPV6_MULTIPLE_TABLES
kfree(net->ipv6.ip6_prohibit_entry);
kfree(net->ipv6.ip6_blk_hole_entry);
@ -235,7 +235,7 @@ Signed-off-by: Jonas Gorski <jogo@openwrt.org>
#endif
dst_entries_destroy(&net->ipv6.ip6_dst_ops);
}
@@ -5372,6 +5418,9 @@ void __init ip6_route_init_special_entri
@@ -5388,6 +5434,9 @@ void __init ip6_route_init_special_entri
init_net.ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
init_net.ipv6.ip6_blk_hole_entry->dst.dev = init_net.loopback_dev;
init_net.ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);

View File

@ -33,7 +33,7 @@ Signed-off-by: Gabor Juhos <juhosg@openwrt.org>
/*
* The Mellanox Tavor device gives false positive parity errors. Mark this
* device with a broken_parity_status to allow PCI scanning code to "skip"
@@ -3135,6 +3136,8 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_I
@@ -3152,6 +3153,8 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_I
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x65f9, quirk_intel_mc_errata);
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x65fa, quirk_intel_mc_errata);
@ -42,7 +42,7 @@ Signed-off-by: Gabor Juhos <juhosg@openwrt.org>
/*
* Ivytown NTB BAR sizes are misreported by the hardware due to an erratum.
* To work around this, query the size it should be configured to by the
@@ -3160,6 +3163,8 @@ static void quirk_intel_ntb(struct pci_d
@@ -3177,6 +3180,8 @@ static void quirk_intel_ntb(struct pci_d
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x0e08, quirk_intel_ntb);
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x0e0d, quirk_intel_ntb);
@ -51,7 +51,7 @@ Signed-off-by: Gabor Juhos <juhosg@openwrt.org>
/*
* Some BIOS implementations leave the Intel GPU interrupts enabled, even
* though no one is handling them (e.g., if the i915 driver is never
@@ -3198,6 +3203,8 @@ DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_IN
@@ -3215,6 +3220,8 @@ DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_IN
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x010a, disable_igfx_irq);
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x0152, disable_igfx_irq);

View File

@ -13,7 +13,7 @@ Signed-off-by: Imre Kaloz <kaloz@openwrt.org>
--- a/init/Kconfig
+++ b/init/Kconfig
@@ -1528,6 +1528,15 @@ config EMBEDDED
@@ -1531,6 +1531,15 @@ config EMBEDDED
an embedded system so certain expert options are available
for configuration.

View File

@ -32,7 +32,7 @@ Signed-off-by: Gabor Juhos <juhosg@openwrt.org>
/* The Mellanox Tavor device gives false positive parity errors
* Mark this device with a broken_parity_status, to allow
* PCI scanning code to "skip" this now blacklisted device.
@@ -3038,6 +3039,7 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_I
@@ -3055,6 +3056,7 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_I
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x65f9, quirk_intel_mc_errata);
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x65fa, quirk_intel_mc_errata);
@ -40,7 +40,7 @@ Signed-off-by: Gabor Juhos <juhosg@openwrt.org>
/*
* Ivytown NTB BAR sizes are misreported by the hardware due to an erratum. To
@@ -3094,6 +3096,8 @@ static void fixup_debug_report(struct pc
@@ -3111,6 +3113,8 @@ static void fixup_debug_report(struct pc
}
}
@ -49,7 +49,7 @@ Signed-off-by: Gabor Juhos <juhosg@openwrt.org>
/*
* Some BIOS implementations leave the Intel GPU interrupts enabled,
* even though no one is handling them (f.e. i915 driver is never loaded).
@@ -3132,6 +3136,8 @@ DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_IN
@@ -3149,6 +3153,8 @@ DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_IN
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x010a, disable_igfx_irq);
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x0152, disable_igfx_irq);

View File

@ -5494,7 +5494,7 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
(transaction layer end-to-end CRC checking).
--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -1302,6 +1302,8 @@ void pci_walk_bus(struct pci_bus *top, i
@@ -1304,6 +1304,8 @@ void pci_walk_bus(struct pci_bus *top, i
void *userdata);
int pci_cfg_space_size(struct pci_dev *dev);
unsigned char pci_bus_max_busnr(struct pci_bus *bus);

View File

@ -241,7 +241,7 @@ Signed-off-by: Biwen Li <biwen.li@nxp.com>
void __kfree_skb(struct sk_buff *skb);
extern struct kmem_cache *skbuff_head_cache;
@@ -3294,6 +3295,7 @@ static inline void skb_free_datagram_loc
@@ -3297,6 +3298,7 @@ static inline void skb_free_datagram_loc
}
int skb_kill_datagram(struct sock *sk, struct sk_buff *skb, unsigned int flags);
int skb_copy_bits(const struct sk_buff *skb, int offset, void *to, int len);

View File

@ -290,7 +290,7 @@ Signed-off-by: Biwen Li <biwen.li@nxp.com>
#define __HAVE_PHYS_MEM_ACCESS_PROT
--- a/arch/arm64/mm/dma-mapping.c
+++ b/arch/arm64/mm/dma-mapping.c
@@ -937,3 +937,4 @@ void arch_setup_dma_ops(struct device *d
@@ -947,3 +947,4 @@ void arch_setup_dma_ops(struct device *d
}
#endif
}

View File

@ -558,9 +558,9 @@ Signed-off-by: Biwen Li <biwen.li@nxp.com>
* them, we have to fall back to INTx or other interrupts, e.g., a
--- a/drivers/pci/quirks.c
+++ b/drivers/pci/quirks.c
@@ -3376,6 +3376,13 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_A
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_ATHEROS, 0x003c, quirk_no_bus_reset);
@@ -3394,6 +3394,13 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_A
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_ATHEROS, 0x0033, quirk_no_bus_reset);
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_ATHEROS, 0x0034, quirk_no_bus_reset);
+/*
+ * NXP (Freescale Vendor ID) LS1088 chips do not behave correctly after
@ -572,9 +572,9 @@ Signed-off-by: Biwen Li <biwen.li@nxp.com>
static void quirk_no_pm_reset(struct pci_dev *dev)
{
/*
@@ -4859,3 +4866,11 @@ static void quirk_no_ats(struct pci_dev
/* AMD Stoney platform GPU */
@@ -4878,3 +4885,11 @@ static void quirk_no_ats(struct pci_dev
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_ATI, 0x98e4, quirk_no_ats);
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_ATI, 0x6900, quirk_no_ats);
#endif /* CONFIG_PCI_ATS */
+
+/* Freescale PCIe doesn't support MSI in RC mode */
@ -586,7 +586,7 @@ Signed-off-by: Biwen Li <biwen.li@nxp.com>
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_FREESCALE, PCI_ANY_ID, quirk_fsl_no_msi);
--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -1944,6 +1944,7 @@ void pcibios_release_device(struct pci_d
@@ -1946,6 +1946,7 @@ void pcibios_release_device(struct pci_d
void pcibios_penalize_isa_irq(int irq, int active);
int pcibios_alloc_irq(struct pci_dev *dev);
void pcibios_free_irq(struct pci_dev *dev);

View File

@ -112,7 +112,7 @@ Signed-off-by: Biwen Li <biwen.li@nxp.com>
temp = sdhci_readl(host, ESDHC_SYSTEM_CONTROL);
temp &= ~(ESDHC_CLOCK_SDCLKEN | ESDHC_CLOCK_IPGEN | ESDHC_CLOCK_HCKEN |
@@ -797,6 +830,7 @@ static struct soc_device_attribute soc_i
@@ -800,6 +833,7 @@ static struct soc_device_attribute soc_i
static void esdhc_init(struct platform_device *pdev, struct sdhci_host *host)
{
@ -120,7 +120,7 @@ Signed-off-by: Biwen Li <biwen.li@nxp.com>
struct sdhci_pltfm_host *pltfm_host;
struct sdhci_esdhc *esdhc;
struct device_node *np;
@@ -816,6 +850,9 @@ static void esdhc_init(struct platform_d
@@ -819,6 +853,9 @@ static void esdhc_init(struct platform_d
else
esdhc->quirk_incorrect_hostver = false;
@ -130,7 +130,7 @@ Signed-off-by: Biwen Li <biwen.li@nxp.com>
np = pdev->dev.of_node;
clk = of_clk_get(np, 0);
if (!IS_ERR(clk)) {
@@ -915,14 +952,6 @@ static int sdhci_esdhc_probe(struct plat
@@ -923,14 +960,6 @@ static int sdhci_esdhc_probe(struct plat
return ret;
}

View File

@ -13,7 +13,7 @@ Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com>
--- a/drivers/mmc/host/sdhci-of-esdhc.c
+++ b/drivers/mmc/host/sdhci-of-esdhc.c
@@ -658,6 +658,7 @@ static void esdhc_reset(struct sdhci_hos
@@ -661,6 +661,7 @@ static void esdhc_reset(struct sdhci_hos
static const struct of_device_id scfg_device_ids[] = {
{ .compatible = "fsl,t1040-scfg", },
{ .compatible = "fsl,ls1012a-scfg", },