From 790ee453bfcc1652a43abaf29b1242c0212b803d Mon Sep 17 00:00:00 2001 From: Yi-Jyun Pan Date: Wed, 9 Jun 2021 01:58:23 +0800 Subject: [PATCH 1/3] refactor(): remove lots of deprecated code --- .github/workflows/publish.yml | 62 ----------------------------- .npmignore | 10 ----- docker-compose.yml | 2 +- endpoint.worker.js | 20 ---------- src/browser/README.md | 31 --------------- src/browser/background.html | 3 -- src/browser/background.js | 34 ---------------- src/browser/convert.js | 43 --------------------- src/browser/crypto.js | 32 --------------- src/browser/inject.js | 73 ----------------------------------- src/browser/manifest.json | 20 ---------- src/browser/request.js | 21 ---------- src/browser/script.js | 5 --- 13 files changed, 1 insertion(+), 355 deletions(-) delete mode 100644 .github/workflows/publish.yml delete mode 100644 .npmignore delete mode 100644 endpoint.worker.js delete mode 100644 src/browser/README.md delete mode 100644 src/browser/background.html delete mode 100644 src/browser/background.js delete mode 100644 src/browser/convert.js delete mode 100644 src/browser/crypto.js delete mode 100644 src/browser/inject.js delete mode 100644 src/browser/manifest.json delete mode 100644 src/browser/request.js delete mode 100644 src/browser/script.js diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml deleted file mode 100644 index 6f84d4f6cb..0000000000 --- a/.github/workflows/publish.yml +++ /dev/null @@ -1,62 +0,0 @@ -name: publish - -on: - push: - tags: - - '*' - -jobs: - docker: - runs-on: ubuntu-latest - env: - REPOSITORY: unblockneteasemusic - DOCKER_USERNAME: nondanee - DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }} - steps: - - - name: Prepare - id: prepare - run: | - ARCH=(amd64 arm/v6 arm/v7 arm64 386 ppc64le s390x) - PLATFORM=$(printf ",linux/%s" "${ARCH[@]}") - echo ::set-output name=build_platform::${PLATFORM:1} - echo ::set-output name=image_name::${DOCKER_USERNAME}/${REPOSITORY} - - - name: Checkout - uses: actions/checkout@v2 - - - name: Setup Buildx - uses: crazy-max/ghaction-docker-buildx@v1 - - - name: Login - run: | - echo "${DOCKER_PASSWORD}" | docker login --username ${DOCKER_USERNAME} --password-stdin - - - name: Build - run: | - docker buildx build \ - --tag ${{ steps.prepare.outputs.image_name }} \ - --platform ${{ steps.prepare.outputs.build_platform }} \ - --output "type=image,push=true" \ - --file Dockerfile . - - - name: Check Manifest - run: | - docker buildx imagetools inspect ${{ steps.prepare.outputs.image_name }} - - npm: - runs-on: ubuntu-latest - steps: - - - name: Checkout - uses: actions/checkout@v2 - - - name: Setup Node.js - uses: actions/setup-node@v1 - with: - registry-url: https://registry.npmjs.org/ - - - name: Publish - run: npm publish - env: - NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} diff --git a/.npmignore b/.npmignore deleted file mode 100644 index e05d349a91..0000000000 --- a/.npmignore +++ /dev/null @@ -1,10 +0,0 @@ -.npmignore -.gitignore -.dockerignore - -Dockerfile* -*.yml - -src/browser/ -ca.* -*.worker.js \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index ec6a911959..9a34adaa12 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -2,7 +2,7 @@ version: '3' services: unblockneteasemusic: - image: nondanee/unblockneteasemusic + build: . environment: NODE_ENV: production ports: diff --git a/endpoint.worker.js b/endpoint.worker.js deleted file mode 100644 index 150cdad23b..0000000000 --- a/endpoint.worker.js +++ /dev/null @@ -1,20 +0,0 @@ -addEventListener('fetch', event => { - event.respondWith(handleRequest(event.request)) -}) - -const pattern = /^\/package\/([0-9a-zA-Z_\-=]+)\/(\w+\.\w+)$/ - -const handleRequest = async request => { - const notFound = new Response(null, { status: 404 }) - const path = new URL(request.url).pathname - const [matched, base64Url, fileName] = pattern.exec(path || '') || [] - if (!matched) return notFound - let url = base64Url.replace(/-/g, '+').replace(/_/g, '/') - try { url = new URL(atob(url)) } catch(_) { url = null } - if (!url) return notFound - const headers = new Headers(request.headers) - headers.set('host', url.host) - headers.delete('cookie') - const { method, body } = request - return fetch(url, { method, headers, body }) -} \ No newline at end of file diff --git a/src/browser/README.md b/src/browser/README.md deleted file mode 100644 index 8587ba1ca6..0000000000 --- a/src/browser/README.md +++ /dev/null @@ -1,31 +0,0 @@ -# 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 - -## Known Issue - -Audio resources from `kuwo`, `kugou` and `migu` are limited in http protocol only and hence can't load -Most audio resources from `qq` don't support preflight request (OPTIONS) and make playbar buggy - -## Reference - -- [brix/crypto-js](https://github.com/brix/crypto-js) -- [travist/jsencrypt](https://github.com/travist/jsencrypt) -- [JixunMoe/cuwcl4c](https://github.com/JixunMoe/cuwcl4c) \ No newline at end of file diff --git a/src/browser/background.html b/src/browser/background.html deleted file mode 100644 index c6421acc31..0000000000 --- a/src/browser/background.html +++ /dev/null @@ -1,3 +0,0 @@ - - - \ No newline at end of file diff --git a/src/browser/background.js b/src/browser/background.js deleted file mode 100644 index 650e8e5cf7..0000000000 --- a/src/browser/background.js +++ /dev/null @@ -1,34 +0,0 @@ -import match from './provider/match.js' -const self = chrome.runtime.id - -chrome.runtime.onMessageExternal.addListener((request, sender, sendResponse) => { - match(request.match, ['qq']) - .then(song => sendResponse(song)) - .catch(console.log) - return true -}) - -chrome.webRequest.onBeforeSendHeaders.addListener(details => { - let headers = details.requestHeaders - if(details.url.includes('//music.163.com/')){ - headers.push({name: 'X-Real-IP', value: '118.88.88.88'}) - } - if(details.initiator == `chrome-extension://${self}`){ - let index = headers.findIndex(item => item.name.toLowerCase() === 'additional-headers') - if(index === -1) return - Object.entries(JSON.parse(atob(headers[index].value))).forEach(entry => headers.push({name: entry[0], value: entry[1]})) - headers.splice(index, 1) - } - if(details.initiator == 'https://music.163.com' && (details.type == 'media' || details.url.includes('.mp3'))){ - headers = headers.filter(item => !['referer', 'origin'].includes(item.name.toLowerCase())) - } - return {requestHeaders: headers} -}, {urls: ['*://*/*']}, ['blocking', 'requestHeaders', 'extraHeaders']) - -chrome.webRequest.onHeadersReceived.addListener(details => { - let headers = details.responseHeaders - if(details.initiator == 'https://music.163.com' && (details.type == 'media' || details.url.includes('.mp3'))){ - headers.push({name: 'Access-Control-Allow-Origin', value: '*'}) - } - return {responseHeaders: headers} -}, {urls: ['*://*/*']}, ['blocking', 'responseHeaders']) \ No newline at end of file diff --git a/src/browser/convert.js b/src/browser/convert.js deleted file mode 100644 index fc7e8d8b21..0000000000 --- a/src/browser/convert.js +++ /dev/null @@ -1,43 +0,0 @@ -const fs = require('fs') -const path = require('path') - -const importReplacer = (match, state, alias, file) => { - file = file + (file.endsWith('.js') ? '' : '.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 -}) \ No newline at end of file diff --git a/src/browser/crypto.js b/src/browser/crypto.js deleted file mode 100644 index baef8b2d8e..0000000000 --- a/src/browser/crypto.js +++ /dev/null @@ -1,32 +0,0 @@ - -const bodyify = object => Object.entries(object).map(entry => entry.map(encodeURIComponent).join('=')).join('&') - -const toBuffer = string => (new TextEncoder()).encode(string) -const toHex = arrayBuffer => Array.from(arrayBuffer).map(n => n.toString(16).padStart(2, '0')).join('') -const toBase64 = arrayBuffer => btoa(arrayBuffer) - -export default { - uri: { - retrieve: id => { - id = id.toString().trim() - const key = '3go8&$8*3*3h0k(2)2' - let string = Array.from(Array(id.length).keys()).map(index => String.fromCharCode(id.charCodeAt(index) ^ key.charCodeAt(index % key.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() - }, - miguapi: { - encrypt: object => { - let text = JSON.stringify(object), signer = new JSEncrypt() - let password = Array.from(window.crypto.getRandomValues(new Uint8Array(32))).map(n => n.toString(16).padStart(2, '0')).join('') - signer.setPublicKey('-----BEGIN PUBLIC KEY-----\nMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC8asrfSaoOb4je+DSmKdriQJKWVJ2oDZrs3wi5W67m3LwTB9QVR+cE3XWU21Nx+YBxS0yun8wDcjgQvYt625ZCcgin2ro/eOkNyUOTBIbuj9CvMnhUYiR61lC1f1IGbrSYYimqBVSjpifVufxtx/I3exReZosTByYp4Xwpb1+WAQIDAQAB\n-----END PUBLIC KEY-----') - return bodyify({ - data: CryptoJS.AES.encrypt(text, password).toString(), - secKey: signer.encrypt(password) - }) - } - } -} \ No newline at end of file diff --git a/src/browser/inject.js b/src/browser/inject.js deleted file mode 100644 index 5d5e6cfbb5..0000000000 --- a/src/browser/inject.js +++ /dev/null @@ -1,73 +0,0 @@ -(() => { - 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) - } - } -})() \ No newline at end of file diff --git a/src/browser/manifest.json b/src/browser/manifest.json deleted file mode 100644 index a2a6124ab6..0000000000 --- a/src/browser/manifest.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "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"], - "content_security_policy": "script-src 'self' 'unsafe-eval' https://cdn.jsdelivr.net; object-src 'self'" -} \ No newline at end of file diff --git a/src/browser/request.js b/src/browser/request.js deleted file mode 100644 index a257a6babe..0000000000 --- a/src/browser/request.js +++ /dev/null @@ -1,21 +0,0 @@ -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) - const safe = {}, unsafe = {} - Object.keys(headers).filter(key => (['origin', 'referer'].includes(key.toLowerCase()) ? unsafe : safe)[key] = headers[key]) - Object.entries(safe).forEach(entry => xhr.setRequestHeader.apply(xhr, entry)) - if (Object.keys(unsafe)) xhr.setRequestHeader('Additional-Headers', btoa(JSON.stringify(unsafe))) - 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)) -})) \ No newline at end of file diff --git a/src/browser/script.js b/src/browser/script.js deleted file mode 100644 index 01eb7d84f5..0000000000 --- a/src/browser/script.js +++ /dev/null @@ -1,5 +0,0 @@ -(() => { - let script = (document.head || document.documentElement).appendChild(document.createElement('script')) - script.src = chrome.extension.getURL('inject.js') - script.onload = script.parentNode.removeChild(script) -})() \ No newline at end of file From 7fa62c924173da87d34edbdccac651363afe52f1 Mon Sep 17 00:00:00 2001 From: Yi-Jyun Pan Date: Wed, 9 Jun 2021 02:07:36 +0800 Subject: [PATCH 2/3] feat(README): update to match our fork --- README.md | 52 +++++++++++++++++++++++++--------------------------- 1 file changed, 25 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index 4835102830..828dcdab99 100755 --- a/README.md +++ b/README.md @@ -4,33 +4,35 @@ 解锁网易云音乐客户端变灰歌曲 +fork 自 [nondanee 的原版](https://github.com/nondanee/UnblockNeteaseMusic),仅作部分优化。十分感谢 nondanee 提供如此棒的工具! + ## 特性 -- 使用 QQ / 虾米 / 百度 / 酷狗 / 酷我 / 咪咕 / JOOX 音源替换变灰歌曲链接 (默认仅启用一、五、六) +- 使用 bb音乐 / QQ / 虾米 / 百度 / 酷狗 / 酷我 / 咪咕 / JOOX 音源替换变灰歌曲链接 (默认仅启用一、五、六) - 为请求增加 `X-Real-IP` 参数解锁海外限制,支持指定网易云服务器 IP,支持设置上游 HTTP / HTTPS 代理 - 完整的流量代理功能 (HTTP / HTTPS),可直接作为系统代理 (同时支持 PAC) ## 运行 -使用 npx +### docker 作法 +```bash +git clone https://github.com/1715173329/UnblockNeteaseMusic +cd UnblockNeteaseMusic +docker-compose up ``` -$ npx @nondanee/unblockneteasemusic -``` - -或使用 Docker -``` -$ docker run nondanee/unblockneteasemusic -``` +### 传统作法 -``` -$ docker-compose up +```bash +git clone https://github.com/1715173329/UnblockNeteaseMusic +cd UnblockNeteaseMusic +node app.js # 建议使用 screen / tmux 把 app.js 挂后台 ``` ### 配置参数 -``` +```bash $ unblockneteasemusic -h usage: unblockneteasemusic [-v] [-p port] [-a address] [-u url] [-f host] [-o source [source ...]] [-t token] [-e url] [-s] @@ -52,7 +54,7 @@ optional arguments: ## 使用 -**警告:本项目不提供线上 demo,请不要轻易信任使用他人提供的公开代理服务,以免发生安全问题** +**警告**:本项目不提供线上 demo,请不要轻易信任使用他人提供的公开代理服务,以免发生安全问题 **若将服务部署到公网,强烈建议使用严格模式 (此模式下仅放行网易云音乐所属域名的请求) `-s` 限制代理范围 (需使用 PAC 或 hosts),~~或启用 Proxy Authentication `-t :` 设置代理用户名密码~~ (目前密码认证在 Windows 客户端设置和 macOS 系统设置都无法生效,请不要使用),以防代理被他人滥用** @@ -70,12 +72,12 @@ checknetisolation loopbackexempt -a -n="1F8B0F94.122165AE053F_j2p0p5q0044a6" 向 hosts 文件添加两条规则 -``` +```hosts music.163.com interface.music.163.com ``` -> 使用此方法必须监听 80 端口 `-p 80` +> 使用此方法必须监听 80 端口 `-p 80` > > **若在本机运行程序**,请指定网易云服务器 IP `-f xxx.xxx.xxx.xxx` (可在修改 hosts 前通过 `ping music.163.com` 获得) **或** 使用代理 `-u http(s)://xxx.xxx.xxx.xxx:xxx`,以防请求死循环 > @@ -102,12 +104,8 @@ PAC 自动代理脚本地址 `http:///proxy.pac` 作为依赖库使用 -``` -$ npm install @nondanee/unblockneteasemusic -``` - ```javascript -const match = require('@nondanee/unblockneteasemusic') +const match = require('@1715173329/unblockneteasemusic') /** * Set proxy or hosts if needed @@ -126,27 +124,27 @@ match(418602084, ['qq', 'kuwo', 'migu']).then(console.log) ## 效果 -#### Windows 客户端 +### Windows 客户端 -#### UWP 客户端 +### UWP 客户端 -#### Linux 客户端 +### Linux 客户端 -#### macOS 客户端 +### macOS 客户端 -#### Android 客户端 +### Android 客户端 -#### iOS 客户端 +### iOS 客户端 @@ -188,4 +186,4 @@ match(418602084, ['qq', 'kuwo', 'migu']).then(console.log) ## 许可 -The MIT License \ No newline at end of file +The MIT License From f5dbab96d712adf40d17965c13e64c1752e6cbbb Mon Sep 17 00:00:00 2001 From: Yi-Jyun Pan Date: Wed, 9 Jun 2021 02:07:52 +0800 Subject: [PATCH 3/3] bump(): 0.26.0 --- package.json | 26 ++++++-------------------- 1 file changed, 6 insertions(+), 20 deletions(-) diff --git a/package.json b/package.json index 177dd801c2..ec3db0f10c 100644 --- a/package.json +++ b/package.json @@ -1,28 +1,14 @@ { - "name": "@nondanee/unblockneteasemusic", - "version": "0.25.3", + "name": "@1715173329/unblockneteasemusic", + "version": "0.26.0", "description": "Revive unavailable songs for Netease Cloud Music", - "main": "src/provider/match.js", - "bin": { - "unblockneteasemusic": "app.js" - }, - "scripts": { - "pkg": "pkg . --out-path=dist/" - }, - "pkg": { - "assets": [ - "server.key", - "server.crt" - ] - }, + "main": "app.js", "repository": { "type": "git", - "url": "https://github.com/nondanee/UnblockNeteaseMusic.git" + "url": "https://github.com/1715173329/UnblockNeteaseMusic.git" }, - "author": "nondanee", + "author": "nondanee, 1715173329, pan93412", "license": "MIT", "dependencies": {}, - "publishConfig": { - "access": "public" - } + "private": true }