17 Commits

Author SHA1 Message Date
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
12 changed files with 1640 additions and 1218 deletions

19
.github/ISSUE_TEMPLATE/---.md vendored Normal file
View File

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

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

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

View File

@@ -40,14 +40,18 @@ jobs:
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:
tag_name: ${{ github.ref }}
release_name: "Build $(date %Y/%m/%d)"
release_name: "Build ${{ steps.date.outputs.date }}"
draft: true
- name: Upload Release Assets - legacy.tar.gz

View File

@@ -1,5 +1,5 @@
# 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的CLI版本正在开发中。
- 我们新建了Telegram群组欢迎加入[https://t.me/unlock_music_chat](https://t.me/unlock_music_chat)
@@ -34,6 +34,7 @@
# 使用方法
## 使用已构建版本
- 从[GitHub Release](https://github.com/ix64/unlock-music/releases/latest)下载已构建的版本
- 本地使用请下载`legacy版本``modern版本`只能通过**http/https协议**访问)
- 解压缩后即可部署或本地使用(**请勿直接运行源代码**
## 自行构建

2699
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,7 @@
{
"name": "unlock-music",
"version": "1.6.1",
"updateInfo": "修复flac文件的meta写入",
"version": "1.6.3",
"updateInfo": ".ncm和.kgm解锁的一些优化",
"license": "MIT",
"description": "Unlock encrypted music file in browser.",
"repository": {
@@ -21,18 +21,19 @@
"crypto-js": "^4.0.0",
"element-ui": "^2.13.0",
"iconv-lite": "^0.5.1",
"jimp": "^0.14.0",
"metaflac-js": "^1.0.5",
"music-metadata-browser": "^2.0.5",
"music-metadata-browser": "^2.1.2",
"register-service-worker": "^1.7.1",
"vue": "^2.6.11"
"vue": "^2.6.12"
},
"devDependencies": {
"@vue/cli-plugin-babel": "^4.4.6",
"@vue/cli-plugin-pwa": "^4.4.6",
"@vue/cli-service": "^4.4.6",
"@vue/cli-plugin-babel": "^4.5.4",
"@vue/cli-plugin-pwa": "^4.5.4",
"@vue/cli-service": "^4.5.4",
"babel-plugin-component": "^1.1.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"
}
}

View File

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

View File

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

View File

@@ -3,6 +3,8 @@ const MetaFlac = require('metaflac-js');
const CORE_KEY = CryptoJS.enc.Hex.parse("687a4852416d736f356b496e62617857");
const META_KEY = CryptoJS.enc.Hex.parse("2331346C6A6B5F215C5D2630553C2728");
const MagicHeader = [0x43, 0x54, 0x45, 0x4E, 0x46, 0x44, 0x41, 0x4D];
import jimp from 'jimp';
import {
AudioMimeType,
DetectAudioExt,
@@ -35,13 +37,18 @@ export async function Decrypt(file, raw_filename, raw_ext) {
const artists = [];
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 (musicMeta.format === undefined) musicMeta.format = DetectAudioExt(audioData, "mp3");
console.log(musicMeta)
const imageInfo = await GetWebImage(musicMeta.albumPic);
while (!!imageInfo.buffer && imageInfo.buffer.byteLength >= 16 * 1024 * 1024) {
let img = await jimp.read(imageInfo.buffer)
await img.resize(Math.round(img.getHeight() / 2), jimp.AUTO)
imageInfo.buffer = await img.getBufferAsync("image/jpeg")
}
console.log(imageInfo)
try {
if (musicMeta.format === "mp3") {
@@ -49,9 +56,10 @@ export async function Decrypt(file, raw_filename, raw_ext) {
audioData, artists, info.title, musicMeta.album, imageInfo.buffer, musicMeta.albumPic);
} else if (musicMeta.format === "flac") {
const writer = new MetaFlac(Buffer.from(audioData))
//writer.setTag("TITLE=" + info.title);
//writer.setTag("ALBUM=" + musicMeta.album);
//artists.forEach(artist => writer.setTag("ARTIST=" + artist));
if (writer.getTag("TITLE") === "") writer.setTag("TITLE=" + info.title)
if (writer.getTag("ALBUM") === "") writer.setTag("ALBUM=" + musicMeta.album)
writer.removeTag("ARTIST")
artists.forEach(artist => writer.setTag("ARTIST=" + artist))
writer.importPictureFromBuffer(Buffer.from(imageInfo.buffer))
audioData = writer.save()
}
@@ -156,7 +164,9 @@ function getMetaData(dataView, fileBuffer, offset) {
if (plainText.slice(0, labelIndex) === "dj") {
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};
}

View File

@@ -60,7 +60,7 @@ export async function Decrypt(file, raw_filename, raw_ext) {
const musicMeta = await musicMetadata.parseBlob(musicBlob);
for (let metaIdx in musicMeta.native) {
if (musicMeta.native[metaIdx].some(item => item.id === "TCON" && item.value === "(12)")) {
console.log("The metadata is using gbk encoding")
console.warn("The metadata is using gbk encoding")
musicMeta.common.artist = decode(musicMeta.common.artist, "gbk");
musicMeta.common.title = decode(musicMeta.common.title, "gbk");
musicMeta.common.album = decode(musicMeta.common.album, "gbk");

View File

@@ -85,11 +85,11 @@ export async function GetWebImage(pic_url) {
let buf = await resp.arrayBuffer();
let objBlob = new Blob([buf], {type: mime});
let objUrl = URL.createObjectURL(objBlob);
return {"buffer": buf, "url": objUrl, "type": mime};
return {"buffer": buf, "src": pic_url, "url": objUrl, "type": mime};
}
} 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") {
@@ -108,20 +108,3 @@ export function WriteMp3Meta(audioData, artistList, title, album, pictureData =
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";
@@ -29,25 +29,26 @@ export async function Decrypt(file, raw_filename, raw_ext) {
}
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 lenAudioData = audioData.length;
for (let cur = dataOffset; cur < lenAudioData; ++cur)
audioData[cur] = (audioData[cur] - key) ^ 0xff;
const ext = DetectAudioExt(audioData, "mp3");
const ext = FileTypeMap[typeText];
const mime = AudioMimeType[ext];
let musicBlob = new Blob([audioData], {type: mime});
const musicMeta = await musicMetadata.parseBlob(musicBlob);
if (ext === "wav") {
//todo:未知的编码方式
console.log(musicMeta.common)
musicMeta.common.album = "";
musicMeta.common.artist = "";
musicMeta.common.title = "";
}
const info = GetFileInfo(musicMeta.common.artist, musicMeta.common.title, raw_filename, "_");
let _sep = raw_filename.indexOf("_") === -1 ? "-" : "_"
const info = GetFileInfo(musicMeta.common.artist, musicMeta.common.title, raw_filename, _sep);
const imgUrl = GetMetaCoverURL(musicMeta);