mirror of
				https://git.unlock-music.dev/um/web.git
				synced 2025-11-04 09:03:38 +08:00 
			
		
		
		
	Add Detect Media Type by File
This commit is contained in:
		
							parent
							
								
									10f09958c4
								
							
						
					
					
						commit
						c314a251c2
					
				@ -3,7 +3,6 @@ const QmcDecrypt = require("./qmc");
 | 
			
		||||
const RawDecrypt = require("./raw");
 | 
			
		||||
const TmDecrypt = require("./tm");
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
export async function CommonDecrypt(file) {
 | 
			
		||||
    let raw_ext = file.name.substring(file.name.lastIndexOf(".") + 1, file.name.length).toLowerCase();
 | 
			
		||||
    let raw_filename = file.name.substring(0, file.name.lastIndexOf("."));
 | 
			
		||||
 | 
			
		||||
@ -2,7 +2,7 @@ const CryptoJS = require("crypto-js");
 | 
			
		||||
const ID3Writer = require("browser-id3-writer");
 | 
			
		||||
const CORE_KEY = CryptoJS.enc.Hex.parse("687a4852416d736f356b496e62617857");
 | 
			
		||||
const META_KEY = CryptoJS.enc.Hex.parse("2331346C6A6B5F215C5D2630553C2728");
 | 
			
		||||
import {AudioMimeType, GetArrayBuffer} from "./util"
 | 
			
		||||
import {AudioMimeType, DetectAudioExt, GetArrayBuffer} from "./util"
 | 
			
		||||
 | 
			
		||||
export async function Decrypt(file) {
 | 
			
		||||
 | 
			
		||||
@ -23,27 +23,16 @@ export async function Decrypt(file) {
 | 
			
		||||
    let audioOffset = musicMetaObj.offset + dataView.getUint32(musicMetaObj.offset + 5, true) + 13;
 | 
			
		||||
    let audioData = new Uint8Array(fileBuffer, audioOffset);
 | 
			
		||||
 | 
			
		||||
    for (let cur = 0; cur < audioData.length; ++cur) {
 | 
			
		||||
        audioData[cur] ^= keyBox[cur & 0xff];
 | 
			
		||||
    }
 | 
			
		||||
    for (let cur = 0; cur < audioData.length; ++cur) audioData[cur] ^= keyBox[cur & 0xff];
 | 
			
		||||
 | 
			
		||||
    if (musicMeta.format === undefined) musicMeta.format = DetectAudioExt(audioData, "mp3");
 | 
			
		||||
 | 
			
		||||
    if (musicMeta.format === undefined) {
 | 
			
		||||
        const [f, L, a, C] = audioData;
 | 
			
		||||
        if (f === 0x66 && L === 0x4c && a === 0x61 && C === 0x43) {
 | 
			
		||||
            musicMeta.format = "flac";
 | 
			
		||||
        } else {
 | 
			
		||||
            musicMeta.format = "mp3";
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    const mime = AudioMimeType[musicMeta.format];
 | 
			
		||||
 | 
			
		||||
    const artists = [];
 | 
			
		||||
    musicMeta.artist.forEach(arr => {
 | 
			
		||||
        artists.push(arr[0]);
 | 
			
		||||
    });
 | 
			
		||||
    if (musicMeta.format === "mp3") {
 | 
			
		||||
    musicMeta.artist.forEach(arr => artists.push(arr[0]));
 | 
			
		||||
    if (musicMeta.format === "mp3")
 | 
			
		||||
        audioData = await writeID3(audioData, artists, musicMeta.musicName, musicMeta.album, musicMeta.albumPic)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const musicData = new Blob([audioData], {type: mime});
 | 
			
		||||
    return {
 | 
			
		||||
 | 
			
		||||
@ -1,4 +1,4 @@
 | 
			
		||||
import {AudioMimeType, GetArrayBuffer, GetCoverURL, GetFileInfo} from "./util";
 | 
			
		||||
import {AudioMimeType, DetectAudioExt, GetArrayBuffer, GetCoverURL, GetFileInfo} from "./util";
 | 
			
		||||
import {QmcMaskCreate58, QmcMaskGetDefault, QmcMaskDetectMgg, QmcMaskDetectMflac} from "./qmcMask";
 | 
			
		||||
 | 
			
		||||
const musicMetadata = require("music-metadata-browser");
 | 
			
		||||
@ -15,7 +15,6 @@ const HandlerMap = {
 | 
			
		||||
    "tkm": {handler: 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 HandlerMap)) return {status: false, message: "File type is incorrect!"};
 | 
			
		||||
    const handler = HandlerMap[raw_ext];
 | 
			
		||||
@ -27,14 +26,16 @@ export async function Decrypt(file, raw_filename, raw_ext) {
 | 
			
		||||
        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: "此格式仅提供实验性支持!"};
 | 
			
		||||
        if (seed === undefined) return {status: false, message: raw_ext + "格式仅提供实验性支持!"};
 | 
			
		||||
    } else {
 | 
			
		||||
        audioData = fileData;
 | 
			
		||||
        seed = handler.handler(audioData);
 | 
			
		||||
    }
 | 
			
		||||
    const dec = seed.Decrypt(audioData);
 | 
			
		||||
 | 
			
		||||
    const mime = AudioMimeType[handler.ext];
 | 
			
		||||
    const ext = DetectAudioExt(dec, handler.ext);
 | 
			
		||||
    const mime = AudioMimeType[ext];
 | 
			
		||||
 | 
			
		||||
    const musicData = new Blob([dec], {type: mime});
 | 
			
		||||
 | 
			
		||||
    const tag = await musicMetadata.parseBlob(musicData);
 | 
			
		||||
@ -45,7 +46,7 @@ export async function Decrypt(file, raw_filename, raw_ext) {
 | 
			
		||||
        status: true,
 | 
			
		||||
        title: info.title,
 | 
			
		||||
        artist: info.artist,
 | 
			
		||||
        ext: handler.ext,
 | 
			
		||||
        ext: ext,
 | 
			
		||||
        album: tag.common.album,
 | 
			
		||||
        picture: GetCoverURL(tag),
 | 
			
		||||
        file: URL.createObjectURL(musicData),
 | 
			
		||||
 | 
			
		||||
@ -110,10 +110,7 @@ export function QmcMaskDetectMflac(data) {
 | 
			
		||||
    for (let block_idx = 0; block_idx < search_len; block_idx += 128) {
 | 
			
		||||
        try {
 | 
			
		||||
            mask = new QmcMask(data.slice(block_idx, block_idx + 128));
 | 
			
		||||
            if (!mask.Decrypt(data.slice(0, FLAC_HEADER.length))
 | 
			
		||||
                .every((val, idx) => {
 | 
			
		||||
                    return val === FLAC_HEADER[idx];
 | 
			
		||||
                })) break;
 | 
			
		||||
            if (!IsBytesEqual(FLAC_HEADER, mask.Decrypt(data.slice(0, FLAC_HEADER.length)))) break;
 | 
			
		||||
        } catch (e) {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -1,17 +1,23 @@
 | 
			
		||||
const musicMetadata = require("music-metadata-browser");
 | 
			
		||||
import {GetCoverURL, GetFileInfo, AudioMimeType} from "./util";
 | 
			
		||||
import {GetCoverURL, GetFileInfo, AudioMimeType, DetectAudioExt, GetArrayBuffer} from "./util";
 | 
			
		||||
 | 
			
		||||
export async function Decrypt(file, raw_filename, raw_ext) {
 | 
			
		||||
export async function Decrypt(file, raw_filename, raw_ext, detect = true) {
 | 
			
		||||
    let ext = raw_ext;
 | 
			
		||||
    if (detect) {
 | 
			
		||||
        const buffer = new Uint8Array(await GetArrayBuffer(file));
 | 
			
		||||
        ext = DetectAudioExt(buffer, raw_ext);
 | 
			
		||||
        if (ext !== raw_ext) file = new Blob([buffer], {type: AudioMimeType[ext]})
 | 
			
		||||
    }
 | 
			
		||||
    const tag = await musicMetadata.parseBlob(file);
 | 
			
		||||
    const info = GetFileInfo(tag.common.artist, tag.common.title, raw_filename);
 | 
			
		||||
    return {
 | 
			
		||||
        status: true,
 | 
			
		||||
        title: info.title,
 | 
			
		||||
        artist: info.artist,
 | 
			
		||||
        ext: raw_ext,
 | 
			
		||||
        ext: ext,
 | 
			
		||||
        album: tag.common.album,
 | 
			
		||||
        picture: GetCoverURL(tag),
 | 
			
		||||
        file: URL.createObjectURL(file),
 | 
			
		||||
        mime: AudioMimeType[raw_ext]
 | 
			
		||||
        mime: AudioMimeType[ext]
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -10,5 +10,5 @@ export async function Decrypt(file, raw_filename) {
 | 
			
		||||
        audioData[cur] = TM_HEADER[cur];
 | 
			
		||||
    }
 | 
			
		||||
    const musicData = new Blob([audioData], {type: "audio/mp4"});
 | 
			
		||||
    return await RawDecrypt(musicData, raw_filename, "m4a")
 | 
			
		||||
    return await RawDecrypt(musicData, raw_filename, "m4a", false)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -1,4 +1,7 @@
 | 
			
		||||
export const FLAC_HEADER = [0x66, 0x4C, 0x61, 0x43, 0x00];
 | 
			
		||||
export const FLAC_HEADER = [0x66, 0x4C, 0x61, 0x43];
 | 
			
		||||
export const MP3_HEADER = [0x49, 0x44, 0x33];
 | 
			
		||||
export const OGG_HEADER = [0x4F, 0x67, 0x67, 0x53];
 | 
			
		||||
export const M4A_HEADER = [0x66, 0x74, 0x79, 0x70];
 | 
			
		||||
export const AudioMimeType = {
 | 
			
		||||
    mp3: "audio/mpeg",
 | 
			
		||||
    flac: "audio/flac",
 | 
			
		||||
@ -45,7 +48,19 @@ export function GetCoverURL(metadata) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function IsBytesEqual(first, second) {
 | 
			
		||||
    // if want wholly check, should length first>=second
 | 
			
		||||
    return first.every((val, idx) => {
 | 
			
		||||
        return val === second[idx];
 | 
			
		||||
    })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @return {string}
 | 
			
		||||
 */
 | 
			
		||||
export function DetectAudioExt(data, fallbackExt) {
 | 
			
		||||
    if (IsBytesEqual(MP3_HEADER, data.slice(0, MP3_HEADER.length))) return "mp3";
 | 
			
		||||
    if (IsBytesEqual(FLAC_HEADER, data.slice(0, FLAC_HEADER.length))) return "flac";
 | 
			
		||||
    if (IsBytesEqual(OGG_HEADER, data.slice(0, OGG_HEADER.length))) return "ogg";
 | 
			
		||||
    if (IsBytesEqual(M4A_HEADER, data.slice(4, 8))) return "m4a";
 | 
			
		||||
    return fallbackExt;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user