mirror of
				https://git.unlock-music.dev/um/web.git
				synced 2025-11-01 06:43:29 +08:00 
			
		
		
		
	Use Universal Decoder for Qmc,Mgg,Mflac
This commit is contained in:
		
							parent
							
								
									47cea6eae9
								
							
						
					
					
						commit
						41a45176be
					
				| @ -1,8 +1,6 @@ | ||||
| const NcmDecrypt = require("./ncm"); | ||||
| const QmcDecrypt = require("./qmc"); | ||||
| const RawDecrypt = require("./raw"); | ||||
| const MFlacDecrypt = require("./mflac"); | ||||
| const MggDecrypt = require("./mgg"); | ||||
| const TmDecrypt = require("./tm"); | ||||
| 
 | ||||
| 
 | ||||
| @ -31,13 +29,9 @@ export async function CommonDecrypt(file) { | ||||
|         case "tkm"://QQ Music Accompaniment M4a
 | ||||
|         case "bkcmp3"://Moo Music Mp3
 | ||||
|         case "bkcflac"://Moo Music Flac
 | ||||
|             rt_data = await QmcDecrypt.Decrypt(file.raw, raw_filename, raw_ext); | ||||
|             break; | ||||
|         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); | ||||
|         case "mgg": //QQ Music Desktop Ogg
 | ||||
|             rt_data = await QmcDecrypt.Decrypt(file.raw, raw_filename, raw_ext); | ||||
|             break; | ||||
|         case "tm2":// QQ Music IOS M4a
 | ||||
|         case "tm6":// QQ Music IOS M4a
 | ||||
|  | ||||
| @ -1,55 +0,0 @@ | ||||
| const musicMetadata = require("music-metadata-browser"); | ||||
| import {GetArrayBuffer, GetCoverURL, GetFileInfo} from "./util" | ||||
| 
 | ||||
| import * as mask from "./qmcmask" | ||||
| 
 | ||||
| export async function Decrypt(file, raw_filename, raw_ext) { | ||||
|     // 获取扩展名
 | ||||
|     if (raw_ext !== "mflac") return { | ||||
|         status: false, | ||||
|         message: "File type is incorrect!", | ||||
|     }; | ||||
|     // 读取文件
 | ||||
|     const fileBuffer = await GetArrayBuffer(file); | ||||
|     const audioData = new Uint8Array(fileBuffer.slice(0, -0x170)); | ||||
|     //const audioDataLen = audioData.length;
 | ||||
| 
 | ||||
|     // 转换数据
 | ||||
|     const seed = mask.QmcMaskDetectMflac(audioData); | ||||
|     if (seed === undefined) return { | ||||
|         status: false, | ||||
|         message: "此音乐无法解锁,目前mflac格式不提供完整支持", | ||||
|     }; | ||||
|     const dec = seed.Decrypt(audioData); | ||||
|     // 导出
 | ||||
|     const musicData = new Blob([dec], {type: "audio/flac"}); | ||||
| 
 | ||||
|     // 读取Meta
 | ||||
|     let tag = await musicMetadata.parseBlob(musicData); | ||||
|     const info = GetFileInfo(tag.common.artist, tag.common.title, raw_filename); | ||||
|     //reportKeyInfo(new Uint8Array(fileBuffer.slice(-0x170)), seed.mask128,
 | ||||
|     //    info.artist, info.title, tag.common.album, raw_filename);
 | ||||
| 
 | ||||
|     // 返回
 | ||||
|     return { | ||||
|         status: true, | ||||
|         title: info.title, | ||||
|         artist: info.artist, | ||||
|         ext: 'flac', | ||||
|         album: tag.common.album, | ||||
|         picture: GetCoverURL(tag), | ||||
|         file: URL.createObjectURL(musicData), | ||||
|         mime: "audio/flac" | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| function reportKeyInfo(keyData, maskData, artist, title, album, filename) { | ||||
|     fetch("https://stats.ixarea.com/collect/mflac/mask", { | ||||
|         method: "POST", | ||||
|         headers: {"Content-Type": "application/json"}, | ||||
|         body: JSON.stringify({ | ||||
|             Mask: Array.from(maskData), Key: Array.from(keyData), | ||||
|             Artist: artist, Title: title, Album: album, Filename: filename | ||||
|         }), | ||||
|     }).then().catch() | ||||
| } | ||||
| @ -1,60 +0,0 @@ | ||||
| const musicMetadata = require("music-metadata-browser"); | ||||
| const util = require("./util"); | ||||
| 
 | ||||
| import * as mask from "./qmcmask" | ||||
| 
 | ||||
| //todo: combine qmc mflac mgg
 | ||||
| export 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 = mask.QmcMaskDetectMgg(headData); | ||||
|     if (seed === undefined) { | ||||
|         return { | ||||
|             status: false, | ||||
|             message: "此音乐无法解锁,目前mgg格式仅提供试验性支持", | ||||
|         }; | ||||
|         /*try { | ||||
|             let resp = await queryKeyInfo(keyData, headData, raw_filename); | ||||
|             let data = await resp.json(); | ||||
|             seed = mask.QmcMaskCreate128(data.Mask); | ||||
|         } catch (e) {}*/ | ||||
|     } | ||||
|     const dec = seed.Decrypt(audioData); | ||||
|     // 导出
 | ||||
|     const musicData = new Blob([dec], {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" | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| function queryKeyInfo(keyData, headData, filename) { | ||||
|     return fetch("http://127.0.0.1:6580/mgg/query", { | ||||
|         method: "POST", | ||||
|         headers: {"Content-Type": "application/json"}, | ||||
|         body: JSON.stringify({Key: Array.from(keyData), Head: Array.from(headData), Filename: filename}), | ||||
|     }) | ||||
| } | ||||
| @ -1,46 +1,78 @@ | ||||
| const musicMetadata = require("music-metadata-browser"); | ||||
| const util = require("./util"); | ||||
| import * as mask from "./qmcmask" | ||||
| import {AudioMimeType, GetArrayBuffer, GetCoverURL, GetFileInfo} from "./util"; | ||||
| import * as mask from "./qmcMask" | ||||
| 
 | ||||
| const OriginalExtMap = { | ||||
|     "qmc0": "mp3", | ||||
|     "qmc3": "mp3", | ||||
|     "qmcogg": "ogg", | ||||
|     "qmcflac": "flac", | ||||
|     "bkcmp3": "mp3", | ||||
|     "bkcflac": "flac", | ||||
|     "tkm": "m4a" | ||||
| const musicMetadata = require("music-metadata-browser"); | ||||
| 
 | ||||
| const HandlerMap = { | ||||
|     "mgg": {handler: mask.QmcMaskDetectMgg, ext: "ogg", detect: true}, | ||||
|     "mflac": {handler: mask.QmcMaskDetectMflac, ext: "flac", detect: true}, | ||||
|     "qmc0": {handler: mask.QmcMaskGetDefault, ext: "mp3", detect: false}, | ||||
|     "qmc3": {handler: mask.QmcMaskGetDefault, ext: "mp3", detect: false}, | ||||
|     "qmcogg": {handler: mask.QmcMaskGetDefault, ext: "ogg", detect: false}, | ||||
|     "qmcflac": {handler: mask.QmcMaskGetDefault, ext: "flac", detect: false}, | ||||
|     "bkcmp3": {handler: mask.QmcMaskGetDefault, ext: "mp3", detect: false}, | ||||
|     "bkcflac": {handler: mask.QmcMaskGetDefault, ext: "flac", detect: false}, | ||||
|     "tkm": {handler: mask.QmcMaskGetDefault, ext: "m4a", detect: false} | ||||
| }; | ||||
| 
 | ||||
| //todo: use header to detect media type
 | ||||
| export async function Decrypt(file, raw_filename, raw_ext) { | ||||
|     // 获取扩展名
 | ||||
|     if (!(raw_ext in OriginalExtMap)) { | ||||
|         return {status: false, message: "File type is incorrect!"} | ||||
|     } | ||||
|     const new_ext = OriginalExtMap[raw_ext]; | ||||
|     const mime = util.AudioMimeType[new_ext]; | ||||
|     // 读取文件
 | ||||
|     const fileBuffer = await util.GetArrayBuffer(file); | ||||
|     const audioData = new Uint8Array(fileBuffer); | ||||
|     // 转换数据
 | ||||
|     const seed = mask.QmcMaskGetDefault(); | ||||
|     const dec = seed.Decrypt(audioData); | ||||
|     // 导出
 | ||||
|     const musicData = new Blob([dec], {type: mime}); | ||||
|     // 读取Meta
 | ||||
|     const tag = await musicMetadata.parseBlob(musicData); | ||||
|     const info = util.GetFileInfo(tag.common.artist, tag.common.title, raw_filename); | ||||
|     if (!(raw_ext in HandlerMap)) return {status: false, message: "File type is incorrect!"}; | ||||
|     const handler = HandlerMap[raw_ext]; | ||||
| 
 | ||||
|     // 返回
 | ||||
|     const fileData = new Uint8Array(await GetArrayBuffer(file)); | ||||
|     let audioData, seed, keyData; | ||||
|     if (handler.detect) { | ||||
|         audioData = fileData.slice(0, -0x170); | ||||
|         seed = handler.handler(audioData); | ||||
|         keyData = fileData.slice(-0x170); | ||||
|         if (seed === undefined) seed = await queryKeyInfo(keyData, raw_filename, raw_ext); | ||||
|         if (seed === undefined) return {status: false, message: "此格式仅提供实验性支持!"}; | ||||
|     } else { | ||||
|         audioData = fileData; | ||||
|         seed = handler.handler(audioData); | ||||
|     } | ||||
|     const dec = seed.Decrypt(audioData); | ||||
| 
 | ||||
|     const mime = AudioMimeType[handler.ext]; | ||||
|     const musicData = new Blob([dec], {type: mime}); | ||||
| 
 | ||||
|     const tag = await musicMetadata.parseBlob(musicData); | ||||
|     const info = GetFileInfo(tag.common.artist, tag.common.title, raw_filename); | ||||
|     if (handler.detect) reportKeyUsage(keyData, seed.Matrix128, | ||||
|         info.artist, info.title, tag.common.album, raw_filename, raw_ext); | ||||
|     return { | ||||
|         status: true, | ||||
|         title: info.title, | ||||
|         artist: info.artist, | ||||
|         ext: new_ext, | ||||
|         ext: handler.ext, | ||||
|         album: tag.common.album, | ||||
|         picture: util.GetCoverURL(tag), | ||||
|         picture: GetCoverURL(tag), | ||||
|         file: URL.createObjectURL(musicData), | ||||
|         mime: mime | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| function reportKeyUsage(keyData, maskData, artist, title, album, filename, format) { | ||||
|     fetch("https://stats.ixarea.com/collect/qmcmask/usage", { | ||||
|         method: "POST", | ||||
|         headers: {"Content-Type": "application/json"}, | ||||
|         body: JSON.stringify({ | ||||
|             Mask: Array.from(maskData), Key: Array.from(keyData), | ||||
|             Artist: artist, Title: title, Album: album, Filename: filename, Format: format | ||||
|         }), | ||||
|     }).then().catch() | ||||
| } | ||||
| 
 | ||||
| async function queryKeyInfo(keyData, filename, format) { | ||||
|     try { | ||||
|         const resp = await fetch("https://stats.ixarea.com/collect/qmcmask/query", { | ||||
|             method: "POST", | ||||
|             headers: {"Content-Type": "application/json"}, | ||||
|             body: JSON.stringify({Format: format, Key: Array.from(keyData), Filename: filename}), | ||||
|         }); | ||||
|         let data = await resp.json(); | ||||
|         return mask.QmcMaskCreate58(data.Matrix58, data.Super58A, data.Super58B); | ||||
|     } catch (e) { | ||||
|     } | ||||
| } | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 MengYX
						MengYX