diff --git a/src/App.vue b/src/App.vue
index 32fa547..593d64e 100644
--- a/src/App.vue
+++ b/src/App.vue
@@ -75,7 +75,7 @@
this.$notify.info({
title: '离线使用',
message: '我们使用PWA技术,无网络也能使用
' +
- '最近更新:支持bkcmp3/bkcflac/tkm
' +
+ '最近更新:提供实验性mgg支持
' +
'点击查看 使用提示',
dangerouslyUseHTMLString: true,
duration: 10000,
diff --git a/src/decrypt/common.js b/src/decrypt/common.js
index 16d9d07..29fc926 100644
--- a/src/decrypt/common.js
+++ b/src/decrypt/common.js
@@ -2,6 +2,7 @@ const NcmDecrypt = require("./ncm");
const QmcDecrypt = require("./qmc");
const RawDecrypt = require("./raw");
const MFlacDecrypt = require("./mflac");
+const MggDecrypt = require("./mgg");
const TmDecrypt = require("./tm");
@@ -35,6 +36,9 @@ export async function CommonDecrypt(file) {
case "mflac"://QQ Music Desktop Flac
rt_data = await MFlacDecrypt.Decrypt(file.raw, raw_filename, raw_ext);
break;
+ case "mgg":
+ rt_data = await MggDecrypt.Decrypt(file.raw, raw_filename, raw_ext);
+ break;
case "tm2":// QQ Music IOS M4a
case "tm6":// QQ Music IOS M4a
rt_data = await TmDecrypt.Decrypt(file.raw, raw_filename);
diff --git a/src/decrypt/mgg.js b/src/decrypt/mgg.js
new file mode 100644
index 0000000..5a8ad1e
--- /dev/null
+++ b/src/decrypt/mgg.js
@@ -0,0 +1,84 @@
+const musicMetadata = require("music-metadata-browser");
+const util = require("./util");
+export {Decrypt}
+const FLAC_HEADER = [0x4F, 0x67, 0x67, 0x53, 0x00];
+
+async function Decrypt(file, raw_filename, raw_ext) {
+ // 获取扩展名
+ if (raw_ext !== "mgg") return {
+ status: false,
+ message: "File type is incorrect!",
+ };
+ // 读取文件
+ const fileBuffer = await util.GetArrayBuffer(file);
+ const audioData = new Uint8Array(fileBuffer.slice(0, -0x170));
+ const audioDataLen = audioData.length;
+ const keyData = new Uint8Array(fileBuffer.slice(-0x170));
+ const headData = new Uint8Array(fileBuffer.slice(0, 170));
+ let seed;
+ try {
+ let resp = await queryKeyInfo(keyData, headData, raw_filename);
+ let data = await resp.json();
+ seed = new Mask(data.Mask);
+
+ } catch (e) {
+ return {
+ status: false,
+ message: "此音乐无法解锁,目前mgg格式仅提供试验性支持",
+ };
+ }
+
+ for (let cur = 0; cur < audioDataLen; ++cur) {
+ audioData[cur] ^= seed.NextMask();
+ }
+ // 导出
+ const musicData = new Blob([audioData], {type: "audio/ogg"});
+
+ // 读取Meta
+ let tag = await musicMetadata.parseBlob(musicData);
+ const info = util.GetFileInfo(tag.common.artist, tag.common.title, raw_filename);
+
+ // 返回
+ return {
+ status: true,
+ title: info.title,
+ artist: info.artist,
+ ext: 'ogg',
+ album: tag.common.album,
+ picture: util.GetCoverURL(tag),
+ file: URL.createObjectURL(musicData),
+ mime: "audio/ogg"
+ }
+
+}
+
+class Mask {
+ constructor(mask) {
+ this.index = -1;
+ this.mask_index = -1;
+ this.mask128 = mask;
+ }
+
+ NextMask() {
+ this.index++;
+ this.mask_index++;
+ if (this.index === 0x8000 || (this.index > 0x8000 && (this.index + 1) % 0x8000 === 0)) {
+ this.index++;
+ this.mask_index++;
+ }
+ if (this.mask_index >= 128) {
+ this.mask_index -= 128;
+ }
+ return this.mask128[this.mask_index]
+ }
+
+}
+
+
+function queryKeyInfo(keyData, headData, filename) {
+ return fetch("https://stats.ixarea.com/collect/mgg/query", {
+ method: "POST",
+ headers: {"Content-Type": "application/json"},
+ body: JSON.stringify({Key: Array.from(keyData), Head: Array.from(headData), Filename: filename}),
+ })
+}