37 Commits

Author SHA1 Message Date
MengYX
2ffcbf79b5 Bump Version 2020-11-07 01:22:45 +08:00
MengYX
6a2b98798b Fix #103 #100 duplicated metadata 2020-11-07 01:12:04 +08:00
MengYX
60e2039e56 Update CI 2020-11-06 22:40:23 +08:00
MengYX
fbdad625c5 Update Deps 2020-11-06 22:40:13 +08:00
MengYX
10814ea109 Merge pull request #106 from lc6464/master
适配浏览器深色模式
2020-11-01 11:40:21 +08:00
NULL-LC
52657046d6 适配浏览器深色模式 2020-10-31 19:25:14 +08:00
MengYX
175112180d Merge pull request #101 from renbaoshuo/patch-1
更新 Edge 浏览器下载链接
2020-10-30 10:48:45 +08:00
Baoshuo Ren
7b26630428 更新 Edge 浏览器下载链接 2020-10-18 19:02:40 +08:00
MengYX
55b2f17ed7 Bump Version 2020-09-23 16:51:17 +08:00
MengYX
be09790810 Merge pull request #97 from qq1010903229/patch-1
Merge pull request #97 增加对QQ音乐微云网盘格式的支持
2020-09-23 16:45:54 +08:00
MengYX
df2d409351 Fix Kgm Decrypt Bug 2020-09-23 14:15:47 +08:00
MengYX
a558dac34b Update Deps 2020-09-23 12:55:14 +08:00
MengYX
44642b1c39 Fix kgm bug 2020-09-23 12:54:54 +08:00
qq1010903229
6e66d2da4f Update qmc.js 2020-09-19 12:03:02 +08:00
qq1010903229
e1cf15cf8c Update common.js 2020-09-19 12:00:58 +08:00
MengYX
1d415cae52 Add Comment for Issue Template 2020-09-04 19:12:58 +08:00
MengYX
66e2b96bad Bump Version 2020-09-02 00:04:26 +08:00
MengYX
79c0c85ab3 Merge remote-tracking branch into master 2020-09-01 23:31:43 +08:00
MengYX
ad47a713ad Add Tips for .kgm while using "file:" protocol 2020-09-01 23:26:02 +08:00
MengYX
6ef0850c40 Use Small Cover Image for .ncm 2020-09-01 23:17:17 +08:00
MengYX
9af2ba5e62 Update README.md 2020-08-15 10:30:13 +08:00
MengYX
0f52c53d6c fix .xm filename detect 2020-08-13 21:27:50 +08:00
MengYX
24764875f3 Update CI 2020-08-13 21:27:25 +08:00
MengYX
65a41b21c3 Merge remote-tracking branch 'origin/master' 2020-08-13 16:01:55 +08:00
MengYX
b93b93110b Update GitHub CI 2020-08-13 16:01:35 +08:00
MengYX
7a5cefd950 Bump Version and Update Deps 2020-08-13 15:58:31 +08:00
MengYX
3b885f82ca Fix .xm read info from filename 2020-08-13 15:33:15 +08:00
MengYX
b6757e81a2 Fix #84 2020-08-13 15:26:15 +08:00
MengYX
8d79035675 Fix .xm file type recognize error 2020-08-13 15:25:41 +08:00
MengYX
6592f304b6 Update issue templates 2020-08-07 19:33:28 +08:00
MengYX
e5bff35f89 Fix ncm cover image too big to write into meta 2020-08-03 15:04:54 +08:00
MengYX
9b28676c43 Clean up 2020-08-03 14:03:10 +08:00
MengYX
4a2d31238b Fix #79 ncm->flac no metadata (file downloaded from phone) 2020-08-03 14:02:17 +08:00
MengYX
fd2866f53d Bump Version 2020-08-02 18:25:56 +08:00
MengYX
5e8af22f08 Fix wrong zip file in release [Skip CI] 2020-08-01 01:20:56 +08:00
MengYX
4aa2ff7f91 Fix #77 ncm flac meta duplicated
Fix #78 write flac cover sometimes fail
2020-08-01 01:10:27 +08:00
MengYX
c9a4a901be Update README.md 2020-07-19 00:03:41 +08:00
16 changed files with 2483 additions and 1693 deletions

38
.github/ISSUE_TEMPLATE/bug-report.md vendored Normal file
View File

@@ -0,0 +1,38 @@
---
name: Bug报告
about: 报告Bug以帮助改进程序
title: ''
labels: bug
assignees: ''
---
- 请按照此模板填写,否则可能立即被关闭
- [x] 我确认已经搜索过Issue不存并确认相同的Issue
- [x] 我认为这是程序导致的问题如不确认请先通过Telegram或者Email进行咨询
**Bug描述**
简要地复述你遇到的Bug
**复现方法**
描述复现方法,必要时请提供样本文件
**程序截图或者Console报错信息**
如果可以请提供二者之一
**环境信息:**
- 操作系统和浏览器:
- 程序版本:
- 获取音乐文件所使用的客户端及其版本信息:
**附加信息**
其他能够帮助确认问题的信息

26
.github/ISSUE_TEMPLATE/new-feature.md vendored Normal file
View File

@@ -0,0 +1,26 @@
---
name: 新功能
about: 对于程序新的想法或建议
title: ''
labels: enhancement
assignees: ''
---
- 请按照此模板填写,否则可能立即被关闭
**背景和说明**
简要说明产生此想法的背景和此想法的具体内容
**实现途径**
- 如果没有设计方案,请简要描述实现思路
- 如果你没有任何的实现思路请通过Telegram进行讨论
**附加信息**
更多你想要表达的内容

67
.github/workflows/build.yml vendored Normal file
View File

@@ -0,0 +1,67 @@
name: Build
on:
push:
branches: [ master ]
paths:
- "**/*.js"
- "**/*.vue"
- "public/**/*"
- "package-lock.json"
- "package.json"
pull_request:
branches: [ master ]
types: [ opened, synchronize, reopened ]
paths:
- "**/*.js"
- "**/*.vue"
- "public/**/*"
- "package-lock.json"
- "package.json"
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
build: [ legacy, modern ]
include:
- build: legacy
BUILD_ARGS:
- build: modern
BUILD_ARGS: "-- --modern"
steps:
- uses: actions/checkout@v2
- name: Use Node.js 14.x
uses: actions/setup-node@v1
with:
node-version: 14.x
- name: Get npm cache directory
id: npm-cache
run: echo "::set-output name=dir::$(npm config get cache)"
- uses: actions/cache@v2
with:
path: ${{ steps.npm-cache.outputs.dir }}
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
restore-keys: ${{ runner.os }}-node-
- name: Install Dependencies
run: |
npm ci
npm run fix-compatibility
- name: Build
env:
GZIP: "--best"
run: |
npm run build ${{ matrix.BUILD_ARGS }}
tar -czvf dist.tar.gz -C ./dist .
- name: Publish artifact
uses: actions/upload-artifact@v2
with:
name: unlock-music-${{ matrix.build }}.tar.gz
path: ./dist.tar.gz

View File

@@ -3,100 +3,119 @@ name: Release and GitHub Pages
on: on:
push: push:
tags: tags:
- "v*" - "v*"
jobs: jobs:
build: build:
runs-on: ubuntu-latest
runs-on: ubuntu-18.04
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- name: Use Node.js 12.x - name: Use Node.js 14.x
uses: actions/setup-node@v1 uses: actions/setup-node@v1
with: with:
node-version: 12.x node-version: 14.x
- name: Install Dependencies - name: Get npm cache directory
run: | id: npm-cache
npm ci run: echo "::set-output name=dir::$(npm config get cache)"
npm run fix-compatibility - uses: actions/cache@v2
- name: Build Legacy with:
run: | path: ${{ steps.npm-cache.outputs.dir }}
npm run build key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
tar -czf legacy.tar.gz -C ./dist . restore-keys: ${{ runner.os }}-node-
zip -J9 legacy.zip ./dist
- name: Build Modern
run: |
npm run build -- --modern
tar -czf modern.tar.gz -C ./dist .
zip -J9 modern.zip ./dist
- run: sha256sum *.tar.gz *.zip > sha256sum.txt
- name: Deploy - name: Install Dependencies
uses: peaceiris/actions-gh-pages@v3 run: |
with: npm ci
github_token: ${{ secrets.GITHUB_TOKEN }} npm run fix-compatibility
publish_dir: ./dist
- name: Create a Release - name: Build Legacy
id: create_release env:
uses: actions/create-release@v1 GZIP: "--best"
env: run: |
npm run build
tar -czf legacy.tar.gz -C ./dist .
zip -rJ9 legacy.zip ./dist
- name: Build Modern
env:
GZIP: "--best"
run: |
npm run build -- --modern
tar -czf modern.tar.gz -C ./dist .
zip -rJ9 modern.zip ./dist
- name: Checksum
run: sha256sum *.tar.gz *.zip > sha256sum.txt
- name: Deploy
uses: peaceiris/actions-gh-pages@v3
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./dist
- name: Get current time
id: date
run: echo "::set-output name=date::$(date +'%Y/%m/%d')"
- name: Create a Release
id: create_release
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with: with:
tag_name: ${{ github.ref }} tag_name: ${{ github.ref }}
release_name: "Build $(date %Y/%m/%d)" release_name: "Build ${{ steps.date.outputs.date }}"
draft: true draft: true
- name: Upload Release Assets - legacy.tar.gz - name: Upload Release Assets - legacy.tar.gz
uses: actions/upload-release-asset@v1.0.2 uses: actions/upload-release-asset@v1.0.2
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ./legacy.tar.gz
asset_name: legacy.tar.gz
asset_content_type: application/gzip
- name: Upload Release Assets - legacy.zip
uses: actions/upload-release-asset@v1.0.2
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ./legacy.zip
asset_name: legacy.zip
asset_content_type: application/zip
- name: Upload Release Assets - modern.tar.gz
uses: actions/upload-release-asset@v1.0.2
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with: with:
upload_url: ${{ steps.create_release.outputs.upload_url }} upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ./modern.tar.gz asset_path: ./legacy.tar.gz
asset_name: modern.tar.gz asset_name: legacy.tar.gz
asset_content_type: application/gzip asset_content_type: application/gzip
- name: Upload Release Assets - modern.zip - name: Upload Release Assets - legacy.zip
uses: actions/upload-release-asset@v1.0.2 uses: actions/upload-release-asset@v1.0.2
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with: with:
upload_url: ${{ steps.create_release.outputs.upload_url }} upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ./modern.zip asset_path: ./legacy.zip
asset_name: modern.zip asset_name: legacy.zip
asset_content_type: application/zip asset_content_type: application/zip
- name: Upload Release Assets - sha256sum.txt - name: Upload Release Assets - modern.tar.gz
uses: actions/upload-release-asset@v1.0.2 uses: actions/upload-release-asset@v1.0.2
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with: with:
upload_url: ${{ steps.create_release.outputs.upload_url }} upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ./sha256sum.txt asset_path: ./modern.tar.gz
asset_name: sha256sum.txt asset_name: modern.tar.gz
asset_content_type: text/plain asset_content_type: application/gzip
- name: Upload Release Assets - modern.zip
uses: actions/upload-release-asset@v1.0.2
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ./modern.zip
asset_name: modern.zip
asset_content_type: application/zip
- name: Upload Release Assets - sha256sum.txt
uses: actions/upload-release-asset@v1.0.2
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ./sha256sum.txt
asset_name: sha256sum.txt
asset_content_type: text/plain

View File

@@ -1,22 +1,23 @@
# Unlock Music 音乐解锁 # Unlock Music 音乐解锁
- Unlock encrypted music file in browser. - 在浏览器中解锁加密的音乐文件。 Unlock encrypted music file in the browser.
- 在浏览器中解锁加密的音乐文件。
- unlock-music项目是以学习和技术研究的初衷创建的修改、再分发时请遵循[License](https://github.com/ix64/unlock-music/blob/master/LICENSE) - unlock-music项目是以学习和技术研究的初衷创建的修改、再分发时请遵循[License](https://github.com/ix64/unlock-music/blob/master/LICENSE)
- 由于存在可能的法律风险以及滥用风险不再提供Demo服务Unlock Music的CLI版本正在开发中。 - Unlock Music的CLI版本正在开发中。
- 我们新建了Telegram群组欢迎加入[https://t.me/unlock_music_chat](https://t.me/unlock_music_chat)
- [其他测试版工具](https://github.com/ix64/unlock-music/wiki/%E5%85%B6%E4%BB%96%E9%9F%B3%E4%B9%90%E6%A0%BC%E5%BC%8F%E5%B7%A5%E5%85%B7) - [其他测试版工具](https://github.com/ix64/unlock-music/wiki/%E5%85%B6%E4%BB%96%E9%9F%B3%E4%B9%90%E6%A0%BC%E5%BC%8F%E5%B7%A5%E5%85%B7)
[![Build Status](https://ci.ixarea.com/api/badges/ix64/unlock-music/status.svg)](https://ci.ixarea.com/ix64/unlock-music) - ![Release and GitHub Pages](https://github.com/ix64/unlock-music/workflows/Release%20and%20GitHub%20Pages/badge.svg)
# 特性 # 特性
## 支持的格式 ## 支持的格式
- [x] QQ音乐 (.qmc0/.qmc2/.qmc3/.qmcflac/.qmcogg/[.tkm](https://github.com/ix64/unlock-music/issues/9)) - [x] QQ音乐 (.qmc0/.qmc2/.qmc3/.qmcflac/.qmcogg/[.tkm](https://github.com/ix64/unlock-music/issues/9))
- [x] 写入封面图片
- [x] Moo音乐格式 ([.bkcmp3/.bkcflac](https://github.com/ix64/unlock-music/issues/11)) - [x] Moo音乐格式 ([.bkcmp3/.bkcflac](https://github.com/ix64/unlock-music/issues/11))
- [x] QQ音乐Tm格式 (.tm0/.tm2/.tm3/.tm6) - [x] QQ音乐Tm格式 (.tm0/.tm2/.tm3/.tm6)
- [x] QQ音乐新格式 (实验性支持) - [x] QQ音乐新格式 (实验性支持)
- [x] .mflac - [x] .mflac
- [x] [.mgg](https://github.com/ix64/unlock-music/issues/3) - [x] [.mgg](https://github.com/ix64/unlock-music/issues/3)
- [x] 网易云音乐格式 (.ncm) - [x] 网易云音乐格式 (.ncm)
- [x] 补全ncm的ID3信息 - [x] 补全ncm的ID3/FlacMeta信息
- [x] 虾米音乐格式 (.xm) (测试阶段) - [x] 虾米音乐格式 (.xm) (测试阶段)
- [x] 酷我音乐格式 (.kwm) (测试阶段) - [x] 酷我音乐格式 (.kwm) (测试阶段)
- [x] 酷狗音乐格式 (.kgm) ([CLI版本](https://github.com/ix64/unlock-music/wiki/%E5%85%B6%E4%BB%96%E9%9F%B3%E4%B9%90%E6%A0%BC%E5%BC%8F%E5%B7%A5%E5%85%B7#%E9%85%B7%E7%8B%97%E9%9F%B3%E4%B9%90-kgmvpr%E8%A7%A3%E9%94%81%E5%B7%A5%E5%85%B7)) - [x] 酷狗音乐格式 (.kgm) ([CLI版本](https://github.com/ix64/unlock-music/wiki/%E5%85%B6%E4%BB%96%E9%9F%B3%E4%B9%90%E6%A0%BC%E5%BC%8F%E5%B7%A5%E5%85%B7#%E9%85%B7%E7%8B%97%E9%9F%B3%E4%B9%90-kgmvpr%E8%A7%A3%E9%94%81%E5%B7%A5%E5%85%B7))
@@ -33,6 +34,7 @@
# 使用方法 # 使用方法
## 使用已构建版本 ## 使用已构建版本
- 从[GitHub Release](https://github.com/ix64/unlock-music/releases/latest)下载已构建的版本 - 从[GitHub Release](https://github.com/ix64/unlock-music/releases/latest)下载已构建的版本
- 本地使用请下载`legacy版本``modern版本`只能通过**http/https协议**访问)
- 解压缩后即可部署或本地使用(**请勿直接运行源代码** - 解压缩后即可部署或本地使用(**请勿直接运行源代码**
## 自行构建 ## 自行构建

3641
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,7 @@
{ {
"name": "unlock-music", "name": "unlock-music",
"version": "1.6.0", "version": "1.7.0",
"updateInfo": "增加对flac文件的meta写入支持QQ音乐封面写入实验性", "updateInfo": "适配深色模式;修复.ncm解锁的一些问题",
"license": "MIT", "license": "MIT",
"description": "Unlock encrypted music file in browser.", "description": "Unlock encrypted music file in browser.",
"repository": { "repository": {
@@ -17,22 +17,23 @@
"dependencies": { "dependencies": {
"base64-js": "^1.3.1", "base64-js": "^1.3.1",
"browser-id3-writer": "^4.4.0", "browser-id3-writer": "^4.4.0",
"core-js": "^3.6.4", "core-js": "^3.7.0",
"crypto-js": "^4.0.0", "crypto-js": "^4.0.0",
"element-ui": "^2.13.0", "element-ui": "^2.14.0",
"iconv-lite": "^0.5.1", "iconv-lite": "^0.5.1",
"jimp": "^0.14.0",
"metaflac-js": "^1.0.5", "metaflac-js": "^1.0.5",
"music-metadata-browser": "^2.0.5", "music-metadata-browser": "^2.1.6",
"register-service-worker": "^1.7.1", "register-service-worker": "^1.7.1",
"vue": "^2.6.11" "vue": "^2.6.12"
}, },
"devDependencies": { "devDependencies": {
"@vue/cli-plugin-babel": "^4.4.6", "@vue/cli-plugin-babel": "^4.5.8",
"@vue/cli-plugin-pwa": "^4.4.6", "@vue/cli-plugin-pwa": "^4.5.8",
"@vue/cli-service": "^4.4.6", "@vue/cli-service": "^4.5.8",
"babel-plugin-component": "^1.1.1", "babel-plugin-component": "^1.1.1",
"vue-cli-plugin-element": "^1.0.1", "vue-cli-plugin-element": "^1.0.1",
"vue-template-compiler": "^2.6.11", "vue-template-compiler": "^2.6.12",
"workerize-loader": "^1.3.0" "workerize-loader": "^1.3.0"
} }
} }

View File

@@ -14,6 +14,7 @@
<meta content="音乐解锁 - 在任何设备上解锁已购的加密音乐!" name="description"/> <meta content="音乐解锁 - 在任何设备上解锁已购的加密音乐!" name="description"/>
<!--@formatter:off--> <!--@formatter:off-->
<style>#loader{position:absolute;left:50%;top:50%;z-index:1010;margin:-75px 0 0 -75px;border:16px solid #f3f3f3;border-radius:50%;border-top:16px solid #3498db;width:120px;height:120px;animation:spin 2s linear infinite}@keyframes spin{0%{transform:rotate(0deg)}100%{transform:rotate(360deg)}}#loader-mask{position:absolute;width:100%;height:100%;bottom:0;left:0;right:0;top:0;z-index:1009;background-color:rgba(242,246,252,0.88)}</style> <style>#loader{position:absolute;left:50%;top:50%;z-index:1010;margin:-75px 0 0 -75px;border:16px solid #f3f3f3;border-radius:50%;border-top:16px solid #3498db;width:120px;height:120px;animation:spin 2s linear infinite}@keyframes spin{0%{transform:rotate(0deg)}100%{transform:rotate(360deg)}}#loader-mask{position:absolute;width:100%;height:100%;bottom:0;left:0;right:0;top:0;z-index:1009;background-color:rgba(242,246,252,0.88)}</style>
<!--深色模式--><style>@media(prefers-color-scheme:dark){#loader-mask,html,.el-notification,.el-upload-dragger,.el-icon-upload,.el-table,.el-table__expanded-cell,.el-table th,.el-table tr{background-color:#111 !important;}html,.el-notification__content,.el-notification__title,#app,.el-upload__text,.el-table,.el-table__expanded-cell,.el-table th,.el-table tr{color:#DDD !important;}.el-upload__tip,.el-radio__label,.el-checkbox__label,.el-table__empty-text{color:#AAA !important;}.el-table tr:hover,.el-table tr:hover td{background-color:#555 !important;transition:background-color .4s !important;}}</style>
<!--@formatter:on--> <!--@formatter:on-->
</head> </head>
<body> <body>
@@ -29,12 +30,12 @@
<h3 id="loader-source"> 请勿直接运行源代码! </h3> <h3 id="loader-source"> 请勿直接运行源代码! </h3>
<div hidden id="loader-tips-outdated"> <div hidden id="loader-tips-outdated">
<h2>您可能在使用不受支持的<span style="color:#f00;">过时</span>浏览器,这可能导致此应用无法正常工作。</h2> <h2>您可能在使用不受支持的<span style="color:#f00;">过时</span>浏览器,这可能导致此应用无法正常工作。</h2>
<h3>如果您使用双核浏览器,您可以尝试切换<span style="color:#f00;">“极速模式”</span>解决此问题。</h3> <h3>如果您使用双核浏览器,您可以尝试切换<span style="color:#f00;">“极速模式”</span> 解决此问题。</h3>
<h3>或者,您可以尝试更换下方的几个浏览器之一。</h3> <h3>或者,您可以尝试更换下方的几个浏览器之一。</h3>
</div> </div>
<h3 hidden id="loader-tips-timeout"> <h3 hidden id="loader-tips-timeout">
音乐解锁采用了一些新特性!建议使用 音乐解锁采用了一些新特性!建议使用
<a href="https://www.microsoftedgeinsider.com/zh-cn/download" target="_blank">Microsoft Edge Chromium</a> <a href="https://www.microsoft.com/zh-cn/edge" target="_blank">Microsoft Edge Chromium</a>
<a href="https://www.google.cn/chrome/" target="_blank">Google Chrome</a> <a href="https://www.google.cn/chrome/" target="_blank">Google Chrome</a>
<a href="https://www.firefox.com.cn/" target="_blank">Mozilla Firefox</a> <a href="https://www.firefox.com.cn/" target="_blank">Mozilla Firefox</a>
| <a href="https://github.com/ix64/unlock-music/wiki/使用提示" target="_blank">使用提示</a> | <a href="https://github.com/ix64/unlock-music/wiki/使用提示" target="_blank">使用提示</a>
@@ -69,4 +70,4 @@
})(); })();
</script> </script>
</body> </body>
</html> </html>

View File

@@ -101,15 +101,15 @@
} }
if ((!!updateInfo && process.env.NODE_ENV === 'production') && (!!updateInfo.HttpsFound || if ((!!updateInfo && process.env.NODE_ENV === 'production') && (!!updateInfo.HttpsFound ||
(!!updateInfo.Found && window.location.protocol !== "https:"))) { (!!updateInfo.Found && window.location.protocol !== "https:"))) {
this.$notify.warning({ this.$notify.warning({
title: '发现更新', title: '发现更新',
message: '发现新版本 v' + updateInfo.Version + message: '发现新版本 v' + updateInfo.Version +
'<br/>更新详情:' + updateInfo.Detail + '<br/>更新详情:' + updateInfo.Detail +
'<br/><a target="_blank" href="' + updateInfo.URL + '">获取更新</a>', '<br/><a target="_blank" href="' + updateInfo.URL + '">获取更新</a>',
dangerouslyUseHTMLString: true, dangerouslyUseHTMLString: true,
duration: 15000, duration: 15000,
position: 'top-left' position: 'top-left'
}); });
} else { } else {
this.$notify.info({ this.$notify.info({
title: '离线使用', title: '离线使用',

View File

@@ -47,12 +47,12 @@
this.idle_workers.push(0); this.idle_workers.push(0);
// delay to optimize for first loading // delay to optimize for first loading
setTimeout(() => { setTimeout(() => {
for (let i = 1; i < this.thread_num; i++) { for (let i = 1; i < this.thread_num; i++) {
// noinspection JSValidateTypes,JSUnresolvedVariable // noinspection JSValidateTypes,JSUnresolvedVariable
this.workers.push(worker().CommonDecrypt); this.workers.push(worker().CommonDecrypt);
this.idle_workers.push(i); this.idle_workers.push(i);
} }
}, 1000); }, 5000);
} else { } else {
const dec = require('../decrypt/common'); const dec = require('../decrypt/common');
this.workers.push(dec.CommonDecrypt); this.workers.push(dec.CommonDecrypt);

View File

@@ -41,6 +41,11 @@ export async function CommonDecrypt(file) {
case "bkcflac"://Moo Music Flac case "bkcflac"://Moo Music Flac
case "mflac"://QQ Music Desktop Flac case "mflac"://QQ Music Desktop Flac
case "mgg": //QQ Music Desktop Ogg case "mgg": //QQ Music Desktop Ogg
case "666c6163"://QQ Music Weiyun Flac
case "6d7033"://QQ Music Weiyun Mp3
case "6f6767"://QQ Music Weiyun Ogg
case "6d3461"://QQ Music Weiyun M4a
case "776176"://QQ Music Weiyun Wav
rt_data = await QmcDecrypt.Decrypt(file.raw, raw_filename, raw_ext); rt_data = await QmcDecrypt.Decrypt(file.raw, raw_filename, raw_ext);
break; break;
case "tm2":// QQ Music IOS M4a case "tm2":// QQ Music IOS M4a
@@ -58,6 +63,6 @@ export async function CommonDecrypt(file) {
if (!rt_data.rawExt) rt_data.rawExt = raw_ext; if (!rt_data.rawExt) rt_data.rawExt = raw_ext;
if (!rt_data.rawFilename) rt_data.rawFilename = raw_filename; if (!rt_data.rawFilename) rt_data.rawFilename = raw_filename;
console.log(rt_data);
return rt_data; return rt_data;
} }

View File

@@ -10,9 +10,17 @@ const KgmHeader = [
const VprMaskDiff = [0x25, 0xDF, 0xE8, 0xA6, 0x75, 0x1E, 0x75, 0x0E, const VprMaskDiff = [0x25, 0xDF, 0xE8, 0xA6, 0x75, 0x1E, 0x75, 0x0E,
0x2F, 0x80, 0xF3, 0x2D, 0xB8, 0xB6, 0xE3, 0x11, 0x2F, 0x80, 0xF3, 0x2D, 0xB8, 0xB6, 0xE3, 0x11,
0x00] 0x00]
const PreDefinedKey = "MoOtOiTvINGwd2E6n0E1i7L5t2IoOoNk"
export async function Decrypt(file, raw_filename, raw_ext) { export async function Decrypt(file, raw_filename, raw_ext) {
try {
if (window.location.protocol === "file:") {
return {
status: false,
message: "请使用<a target='_blank' href='https://github.com/ix64/unlock-music/wiki/其他音乐格式工具'>CLI版本</a>进行解锁"
}
}
} catch {
}
const oriData = new Uint8Array(await GetArrayBuffer(file)); const oriData = new Uint8Array(await GetArrayBuffer(file));
if (raw_ext === "vpr") { if (raw_ext === "vpr") {
if (!IsBytesEqual(VprHeader, oriData.slice(0, 0x10))) if (!IsBytesEqual(VprHeader, oriData.slice(0, 0x10)))

View File

@@ -3,6 +3,9 @@ const MetaFlac = require('metaflac-js');
const CORE_KEY = CryptoJS.enc.Hex.parse("687a4852416d736f356b496e62617857"); const CORE_KEY = CryptoJS.enc.Hex.parse("687a4852416d736f356b496e62617857");
const META_KEY = CryptoJS.enc.Hex.parse("2331346C6A6B5F215C5D2630553C2728"); const META_KEY = CryptoJS.enc.Hex.parse("2331346C6A6B5F215C5D2630553C2728");
const MagicHeader = [0x43, 0x54, 0x45, 0x4E, 0x46, 0x44, 0x41, 0x4D]; const MagicHeader = [0x43, 0x54, 0x45, 0x4E, 0x46, 0x44, 0x41, 0x4D];
const musicMetadata = require("music-metadata-browser");
import jimp from 'jimp';
import { import {
AudioMimeType, AudioMimeType,
DetectAudioExt, DetectAudioExt,
@@ -35,29 +38,45 @@ export async function Decrypt(file, raw_filename, raw_ext) {
const artists = []; const artists = [];
if (!!musicMeta.artist) musicMeta.artist.forEach(arr => artists.push(arr[0])); if (!!musicMeta.artist) musicMeta.artist.forEach(arr => artists.push(arr[0]));
const info = GetFileInfo(artists.join(" & "), musicMeta.musicName, raw_filename); const info = GetFileInfo(artists.join("; "), musicMeta.musicName, raw_filename);
if (artists.length === 0) artists.push(info.artist); if (artists.length === 0) artists.push(info.artist);
if (musicMeta.format === undefined) musicMeta.format = DetectAudioExt(audioData, "mp3"); if (musicMeta.format === undefined) musicMeta.format = DetectAudioExt(audioData, "mp3");
console.log(musicMeta)
const imageInfo = await GetWebImage(musicMeta.albumPic); const imageInfo = await GetWebImage(musicMeta.albumPic);
if (musicMeta.format === "mp3") { while (!!imageInfo.buffer && imageInfo.buffer.byteLength >= 16 * 1024 * 1024) {
audioData = await WriteMp3Meta( let img = await jimp.read(imageInfo.buffer)
audioData, artists, info.title, musicMeta.album, imageInfo.buffer, musicMeta.albumPic); await img.resize(Math.round(img.getHeight() / 2), jimp.AUTO)
} else if (musicMeta.format === "flac") { imageInfo.buffer = await img.getBufferAsync("image/jpeg")
const writer = new MetaFlac(Buffer.from(audioData))
writer.setTag("TITLE=" + info.title);
writer.setTag("ALBUM=" + musicMeta.album);
artists.forEach(artist => writer.setTag("ARTIST=" + artist));
writer.importPictureFromBuffer(Buffer.from(imageInfo.buffer))
audioData = writer.save()
} }
console.log(imageInfo) console.log(imageInfo)
const mime = AudioMimeType[musicMeta.format]
try {
let musicBlob = new Blob([audioData], {type: mime});
const originalMeta = await musicMetadata.parseBlob(musicBlob);
let shouldWrite = !originalMeta.common.album && !originalMeta.common.artists && !originalMeta.common.title
if (musicMeta.format === "mp3") {
audioData = await WriteMp3Meta(
audioData, artists, info.title, musicMeta.album, imageInfo.buffer, musicMeta.albumPic, !shouldWrite)
} else if (musicMeta.format === "flac") {
const writer = new MetaFlac(Buffer.from(audioData))
if (shouldWrite) {
writer.setTag("TITLE=" + info.title)
writer.setTag("ALBUM=" + musicMeta.album)
writer.removeTag("ARTIST")
artists.forEach(artist => writer.setTag("ARTIST=" + artist))
}
writer.importPictureFromBuffer(Buffer.from(imageInfo.buffer))
audioData = writer.save()
}
} catch (e) {
console.warn("Error while appending cover image to file " + e)
}
const mime = AudioMimeType[musicMeta.format]; const musicData = new Blob([audioData], {type: mime})
const musicData = new Blob([audioData], {type: mime});
let x = { return {
status: true, status: true,
title: info.title, title: info.title,
artist: info.artist, artist: info.artist,
@@ -66,9 +85,7 @@ export async function Decrypt(file, raw_filename, raw_ext) {
picture: imageInfo.url, picture: imageInfo.url,
file: URL.createObjectURL(musicData), file: URL.createObjectURL(musicData),
mime: mime mime: mime
}; }
console.log(x)
return x;
} }
@@ -153,7 +170,9 @@ function getMetaData(dataView, fileBuffer, offset) {
if (plainText.slice(0, labelIndex) === "dj") { if (plainText.slice(0, labelIndex) === "dj") {
result = result.mainMusic; result = result.mainMusic;
} }
if (!!result.albumPic) result.albumPic = result.albumPic.replace("http://", "https://"); if (!!result.albumPic && result.albumPic !== "")
result.albumPic = result.albumPic.replace("http://", "https://") + "?param=500y500";
return {data: result, offset: offset}; return {data: result, offset: offset};
} }

View File

@@ -29,7 +29,12 @@ const HandlerMap = {
"qmcflac": {handler: QmcMaskGetDefault, ext: "flac", detect: false}, "qmcflac": {handler: QmcMaskGetDefault, ext: "flac", detect: false},
"bkcmp3": {handler: QmcMaskGetDefault, ext: "mp3", detect: false}, "bkcmp3": {handler: QmcMaskGetDefault, ext: "mp3", detect: false},
"bkcflac": {handler: QmcMaskGetDefault, ext: "flac", detect: false}, "bkcflac": {handler: QmcMaskGetDefault, ext: "flac", detect: false},
"tkm": {handler: QmcMaskGetDefault, ext: "m4a", detect: false} "tkm": {handler: QmcMaskGetDefault, ext: "m4a", detect: false},
"666c6163": {handler: QmcMaskGetDefault, ext: "flac", detect: false},
"6d7033": {handler: QmcMaskGetDefault, ext: "mp3", detect: false},
"6f6767": {handler: QmcMaskGetDefault, ext: "ogg", detect: false},
"6d3461": {handler: QmcMaskGetDefault, ext: "m4a", detect: false},
"776176": {handler: QmcMaskGetDefault, ext: "wav", detect: false}
}; };
export async function Decrypt(file, raw_filename, raw_ext) { export async function Decrypt(file, raw_filename, raw_ext) {
@@ -60,6 +65,7 @@ export async function Decrypt(file, raw_filename, raw_ext) {
const musicMeta = await musicMetadata.parseBlob(musicBlob); const musicMeta = await musicMetadata.parseBlob(musicBlob);
for (let metaIdx in musicMeta.native) { for (let metaIdx in musicMeta.native) {
if (musicMeta.native[metaIdx].some(item => item.id === "TCON" && item.value === "(12)")) { if (musicMeta.native[metaIdx].some(item => item.id === "TCON" && item.value === "(12)")) {
console.warn("The metadata is using gbk encoding")
musicMeta.common.artist = decode(musicMeta.common.artist, "gbk"); musicMeta.common.artist = decode(musicMeta.common.artist, "gbk");
musicMeta.common.title = decode(musicMeta.common.title, "gbk"); musicMeta.common.title = decode(musicMeta.common.title, "gbk");
musicMeta.common.album = decode(musicMeta.common.album, "gbk"); musicMeta.common.album = decode(musicMeta.common.album, "gbk");
@@ -77,21 +83,25 @@ export async function Decrypt(file, raw_filename, raw_ext) {
const imageInfo = await GetWebImage(imgUrl); const imageInfo = await GetWebImage(imgUrl);
if (imageInfo.url !== "") { if (imageInfo.url !== "") {
imgUrl = imageInfo.url imgUrl = imageInfo.url
if (ext === "mp3") { try {
let writer = new ID3Writer(musicDecoded) if (ext === "mp3") {
writer.setFrame('APIC', { let writer = new ID3Writer(musicDecoded)
type: 3, writer.setFrame('APIC', {
data: imageInfo.buffer, type: 3,
description: "Cover", data: imageInfo.buffer,
}) description: "Cover",
writer.addTag(); })
musicDecoded = writer.arrayBuffer writer.addTag();
musicBlob = new Blob([musicDecoded], {type: mime}); musicDecoded = writer.arrayBuffer
} else if (ext === 'flac') { musicBlob = new Blob([musicDecoded], {type: mime});
const writer = new MetaFlac(Buffer.from(musicDecoded)) } else if (ext === 'flac') {
writer.importPictureFromBuffer(Buffer.from(imageInfo.buffer)) const writer = new MetaFlac(Buffer.from(musicDecoded))
musicDecoded = writer.save() writer.importPictureFromBuffer(Buffer.from(imageInfo.buffer))
musicBlob = new Blob([musicDecoded], {type: mime}); musicDecoded = writer.save()
musicBlob = new Blob([musicDecoded], {type: mime});
}
} catch (e) {
console.warn("Error while appending cover image to file " + e)
} }
} }
} }

View File

@@ -1,4 +1,5 @@
const ID3Writer = require("browser-id3-writer"); const ID3Writer = require("browser-id3-writer");
const musicMetadata = require("music-metadata-browser");
export const FLAC_HEADER = [0x66, 0x4C, 0x61, 0x43]; export const FLAC_HEADER = [0x66, 0x4C, 0x61, 0x43];
export const MP3_HEADER = [0x49, 0x44, 0x33]; export const MP3_HEADER = [0x49, 0x44, 0x33];
export const OGG_HEADER = [0x4F, 0x67, 0x67, 0x53]; export const OGG_HEADER = [0x4F, 0x67, 0x67, 0x53];
@@ -85,18 +86,16 @@ export async function GetWebImage(pic_url) {
let buf = await resp.arrayBuffer(); let buf = await resp.arrayBuffer();
let objBlob = new Blob([buf], {type: mime}); let objBlob = new Blob([buf], {type: mime});
let objUrl = URL.createObjectURL(objBlob); let objUrl = URL.createObjectURL(objBlob);
return {"buffer": buf, "url": objUrl, "type": mime}; return {"buffer": buf, "src": pic_url, "url": objUrl, "type": mime};
} }
} catch (e) { } catch (e) {
} }
return {"buffer": null, "url": "", "type": ""} return {"buffer": null, "src": pic_url, "url": "", "type": ""}
} }
export function WriteMp3Meta(audioData, artistList, title, album, pictureData = null, pictureDesc = "Cover") { export async function WriteMp3Meta(audioData, artistList, title, album, pictureData = null, pictureDesc = "Cover", cover_only = true) {
const writer = new ID3Writer(audioData); const writer = new ID3Writer(audioData);
writer.setFrame("TPE1", artistList) if (!cover_only) writer.setFrame("TPE1", artistList).setFrame("TIT2", title).setFrame("TALB", album);
.setFrame("TIT2", title)
.setFrame("TALB", album);
if (pictureData !== null) { if (pictureData !== null) {
writer.setFrame('APIC', { writer.setFrame('APIC', {
type: 3, type: 3,
@@ -108,20 +107,3 @@ export function WriteMp3Meta(audioData, artistList, title, album, pictureData =
return writer.arrayBuffer; return writer.arrayBuffer;
} }
export function RequestJsonp(url, callback_name = "callback") {
return new Promise((resolve, reject) => {
let node;
window[callback_name] = function (data) {
delete window[callback_name];
if (node.parentNode) node.parentNode.removeChild(node);
resolve(data)
};
node = document.createElement('script');
node.type = "text/javascript";
node.src = url;
node.addEventListener('error', msg => {
reject(msg);
});
document.head.appendChild(node);
});
}

View File

@@ -1,4 +1,4 @@
import {AudioMimeType, DetectAudioExt, GetArrayBuffer, GetFileInfo, GetMetaCoverURL, IsBytesEqual} from "./util"; import {AudioMimeType, GetArrayBuffer, GetFileInfo, GetMetaCoverURL, IsBytesEqual} from "./util";
import {Decrypt as RawDecrypt} from "./raw"; import {Decrypt as RawDecrypt} from "./raw";
@@ -29,25 +29,26 @@ export async function Decrypt(file, raw_filename, raw_ext) {
} }
let key = oriData[0xf] let key = oriData[0xf]
let dataOffset = oriData[0xc] | oriData[0xd] << 8 let dataOffset = oriData[0xc] | oriData[0xd] << 8 | oriData[0xe] << 16
let audioData = oriData.slice(0x10); let audioData = oriData.slice(0x10);
let lenAudioData = audioData.length; let lenAudioData = audioData.length;
for (let cur = dataOffset; cur < lenAudioData; ++cur) for (let cur = dataOffset; cur < lenAudioData; ++cur)
audioData[cur] = (audioData[cur] - key) ^ 0xff; audioData[cur] = (audioData[cur] - key) ^ 0xff;
const ext = DetectAudioExt(audioData, "mp3"); const ext = FileTypeMap[typeText];
const mime = AudioMimeType[ext]; const mime = AudioMimeType[ext];
let musicBlob = new Blob([audioData], {type: mime}); let musicBlob = new Blob([audioData], {type: mime});
const musicMeta = await musicMetadata.parseBlob(musicBlob); const musicMeta = await musicMetadata.parseBlob(musicBlob);
if (ext === "wav") { if (ext === "wav") {
//todo:未知的编码方式 //todo:未知的编码方式
console.log(musicMeta.common)
musicMeta.common.album = ""; musicMeta.common.album = "";
musicMeta.common.artist = ""; musicMeta.common.artist = "";
musicMeta.common.title = ""; musicMeta.common.title = "";
} }
let _sep = raw_filename.indexOf("_") === -1 ? "-" : "_"
const info = GetFileInfo(musicMeta.common.artist, musicMeta.common.title, raw_filename, "_"); const info = GetFileInfo(musicMeta.common.artist, musicMeta.common.title, raw_filename, _sep);
const imgUrl = GetMetaCoverURL(musicMeta); const imgUrl = GetMetaCoverURL(musicMeta);