Add Tag Edit Function & Wasm for Qmc & Kgm #5

Merged
um-dev merged 17 commits from nullptr-0/web:master into master 2022-11-24 07:32:59 +00:00
10 changed files with 94 additions and 11 deletions
Showing only changes of commit b5cc9a7404 - Show all commits

7
.gitignore vendored
View File

@ -20,3 +20,10 @@ yarn-error.log*
*.njsproj
*.sln
*.sw?
/src/KgmWasm/build
/src/KgmWasm/build
um-dev marked this conversation as resolved
Review

/src/KgmWasm/build -> /src/QmcWasm/build

`/src/KgmWasm/build` -> `/src/QmcWasm/build`
/src/KgmWasm/*.js
/src/KgmWasm/*.wasm
/src/QmcWasm/*.js
/src/QmcWasm/*.wasm

View File

@ -1,3 +1,4 @@
import { Decrypt as Mg3dDecrypt } from '@/decrypt/mg3d';
import { Decrypt as NcmDecrypt } from '@/decrypt/ncm';
import { Decrypt as NcmCacheDecrypt } from '@/decrypt/ncmcache';
import { Decrypt as XmDecrypt } from '@/decrypt/xm';
@ -22,6 +23,9 @@ export async function Decrypt(file: FileInfo, config: Record<string, any>): Prom
const raw = SplitFilename(file.name);
let rt_data: DecryptResult;
switch (raw.ext) {
case 'mg3d': // Migu Wav
rt_data = await Mg3dDecrypt(file.raw, raw.name);
break;
case 'ncm': // Netease Mp3/Flac
rt_data = await NcmDecrypt(file.raw, raw.name, raw.ext);
break;

View File

@ -63,7 +63,7 @@ export async function Decrypt(file: File, raw_filename: string, raw_ext: string)
const mime = AudioMimeType[ext];
let musicBlob = new Blob([musicDecoded], { type: mime });
const musicMeta = await metaParseBlob(musicBlob);
const { title, artist } = GetMetaFromFile(raw_filename, musicMeta.common.title, musicMeta.common.artists == undefined ? musicMeta.common.artist : musicMeta.common.artists.toString());
const { title, artist } = GetMetaFromFile(raw_filename, musicMeta.common.title, String(musicMeta.common.artists || musicMeta.common.artist || ""));
return {
album: musicMeta.common.album,
picture: GetCoverFromFile(musicMeta),

View File

@ -38,7 +38,7 @@ export async function Decrypt(file: File, raw_filename: string, _: string): Prom
let musicBlob = new Blob([audioData], { type: mime });
const musicMeta = await metaParseBlob(musicBlob);
const { title, artist } = GetMetaFromFile(raw_filename, musicMeta.common.title, musicMeta.common.artists == undefined ? musicMeta.common.artist : musicMeta.common.artists.toString());
const { title, artist } = GetMetaFromFile(raw_filename, musicMeta.common.title, String(musicMeta.common.artists || musicMeta.common.artist || ""));
nullptr-0 marked this conversation as resolved Outdated
  • musicMeta.common.artists == undefined ? musicMeta.common.artist : musicMeta.common.artists.toString() 可以替换为 String(musicMeta.common.artists || musicMeta.common.artist)
- `musicMeta.common.artists == undefined ? musicMeta.common.artist : musicMeta.common.artists.toString()` 可以替换为 `String(musicMeta.common.artists || musicMeta.common.artist)`。
return {
album: musicMeta.common.album,
picture: GetCoverFromFile(musicMeta),

71
src/decrypt/mg3d.ts Normal file
View File

@ -0,0 +1,71 @@
import { Decrypt as RawDecrypt } from './raw';
import { GetArrayBuffer } from '@/decrypt/utils';
import { DecryptResult } from '@/decrypt/entity';
const segmentSize = 0x20;
function isPrintableAsciiChar(ch: number) {
return ch >= 0x20 && ch <= 0x7E;
}
function isUpperHexChar(ch: number) {
return (ch >= 0x30 && ch <= 0x39) || (ch >= 0x41 && ch <= 0x46);
}
/**
* @param {Buffer} data
* @param {Buffer} key
* @param {boolean} copy
* @returns Buffer
*/
function decryptSegment(data: Uint8Array, key: Uint8Array) {
for (let i = 0; i < data.byteLength; i++) {
data[i] -= key[i % segmentSize];
}
return Buffer.from(data);
}
export async function Decrypt(file: File, raw_filename: string): Promise<DecryptResult> {
const buf = new Uint8Array(await GetArrayBuffer(file));
// 咪咕编码的 WAV 文件有很多“空洞”内容,尝试密钥。
const header = buf.slice(0, 0x100);
const bytesRIFF = Buffer.from('RIFF', 'ascii');
const bytesWaveFormat = Buffer.from('WAVEfmt ', 'ascii');
const possibleKeys = [];
for (let i = segmentSize; i < segmentSize * 20; i += segmentSize) {
const possibleKey = buf.slice(i, i + segmentSize);
if (!possibleKey.every(isUpperHexChar)) continue;
const tempHeader = decryptSegment(header, possibleKey);
if (tempHeader.slice(0, 4).compare(bytesRIFF)) continue;
if (tempHeader.slice(8, 16).compare(bytesWaveFormat)) continue;
// fmt chunk 大小可以是 16 / 18 / 40。
const fmtChunkSize = tempHeader.readUInt32LE(0x10);
if (![16, 18, 40].includes(fmtChunkSize)) continue;
// 下一个 chunk
const firstDataChunkOffset = 0x14 + fmtChunkSize;
const chunkName = tempHeader.slice(firstDataChunkOffset, firstDataChunkOffset + 4);
if (!chunkName.every(isPrintableAsciiChar)) continue;
const secondDataChunkOffset = firstDataChunkOffset + 8 + tempHeader.readUInt32LE(firstDataChunkOffset + 4);
if (secondDataChunkOffset <= header.byteLength) {
const secondChunkName = tempHeader.slice(secondDataChunkOffset, secondDataChunkOffset + 4);
if (!secondChunkName.every(isPrintableAsciiChar)) continue;
}
possibleKeys.push(Buffer.from(possibleKey).toString('ascii'));
}
if (possibleKeys.length <= 0) {
throw new Error(`ERROR: no suitable key discovered`);
}
const decryptionKey = Buffer.from(possibleKeys[0], 'ascii');
decryptSegment(buf, decryptionKey);
const musicData = new Blob([buf], { type: 'audio/x-wav' });
return await RawDecrypt(musicData, raw_filename, 'wav', false);
}

View File

@ -13,7 +13,7 @@ export async function Decrypt(file: Blob, raw_filename: string, raw_ext: string)
const ext = SniffAudioExt(buffer, raw_ext);
if (ext !== raw_ext) file = new Blob([buffer], { type: AudioMimeType[ext] });
const tag = await metaParseBlob(file);
const { title, artist } = GetMetaFromFile(raw_filename, tag.common.title, tag.common.artists == undefined ? tag.common.artist : tag.common.artists.toString());
const { title, artist } = GetMetaFromFile(raw_filename, tag.common.title, String(tag.common.artists || tag.common.artist || ""));
nullptr-0 marked this conversation as resolved Outdated

同上,

  • tag.common.artists == undefined ? tag.common.artist : tag.common.artists.toString() 可以替换为 String(tag.common.artists || tag.common.artist)
同上, - `tag.common.artists == undefined ? tag.common.artist : tag.common.artists.toString()` 可以替换为 `String(tag.common.artists || tag.common.artist)`。
return {
title,

View File

@ -54,7 +54,7 @@ export async function Decrypt(file: Blob, raw_filename: string, raw_ext: string)
throw '不支持的QQ音乐缓存格式';
}
const tag = await metaParseBlob(audioBlob);
const { title, artist } = GetMetaFromFile(raw_filename, tag.common.title, String(tag.common.artists || tag.common.artist));
const { title, artist } = GetMetaFromFile(raw_filename, tag.common.title, String(tag.common.artists || tag.common.artist || ""));
nullptr-0 marked this conversation as resolved Outdated
  • tag.common.artists == undefined ? tag.common.artist : tag.common.artists.toString() 可以替换为 String(tag.common.artists || tag.common.artist)
- `tag.common.artists == undefined ? tag.common.artist : tag.common.artists.toString()` 可以替换为 `String(tag.common.artists || tag.common.artist)`。
return {
title,

View File

@ -17,7 +17,7 @@ export async function Decrypt(
if (ext !== raw_ext) file = new Blob([buffer], { type: AudioMimeType[ext] });
}
const tag = await metaParseBlob(file);
const { title, artist } = GetMetaFromFile(raw_filename, tag.common.title, String(tag.common.artists || tag.common.artist));
const { title, artist } = GetMetaFromFile(raw_filename, tag.common.title, String(tag.common.artists || tag.common.artist || ''));
nullptr-0 marked this conversation as resolved Outdated
  • tag.common.artists == undefined ? tag.common.artist : tag.common.artists.toString() 可以替换为 String(tag.common.artists || tag.common.artist)
- `tag.common.artists == undefined ? tag.common.artist : tag.common.artists.toString()` 可以替换为 `String(tag.common.artists || tag.common.artist)`。
return {
title,

View File

@ -49,7 +49,7 @@ export async function Decrypt(file: File, raw_filename: string, raw_ext: string)
const { title, artist } = GetMetaFromFile(
raw_filename,
musicMeta.common.title,
String(musicMeta.common.artists || musicMeta.common.artist),
String(musicMeta.common.artists || musicMeta.common.artist || ""),
nullptr-0 marked this conversation as resolved Outdated
  • 可以替换为 String(musicMeta.common.artists || musicMeta.common.artist)
- 可以替换为 `String(musicMeta.common.artists || musicMeta.common.artist)`。
raw_filename.indexOf('_') === -1 ? '-' : '_',
);

View File

@ -20,6 +20,8 @@ interface MetaResult {
blob: Blob;
}
const fromGBK = (text?: string) => iconv.decode(new Buffer(text || ''), 'gbk');
/**
*
* @param musicBlob
@ -41,14 +43,13 @@ export async function extractQQMusicMeta(
console.warn('try using gbk encoding to decode meta');
nullptr-0 marked this conversation as resolved Outdated
  • 不需要 == undefined 部分
  • 如果需要检查 undefined 值,应使用 === 操作符。
- 不需要 `== undefined` 部分 - 如果需要检查 `undefined` 值,应使用 `===` 操作符。
musicMeta.common.artist = '';
if (!musicMeta.common.artists) {
musicMeta.common.artist = iconv.decode(new Buffer(musicMeta.common.artist ?? ''), 'gbk');
musicMeta.common.artist = fromGBK(musicMeta.common.artist);
}
else {
nullptr-0 marked this conversation as resolved
Review
  • 可以替换为 String(musicMeta.common.artists || musicMeta.common.artist)
- 可以替换为 `String(musicMeta.common.artists || musicMeta.common.artist)`。
Review

但是这样改就不是gbk编码了吧

但是这样改就不是gbk编码了吧
Review

确实忽略了 GBK 的情况,请无视之前的建议。

// 文件顶部定义
const fromGBK = (text) => iconv.decode(new Buffer(text || ''), 'gbk')

// 第 47、48 行更换为
musicMeta.common.artist = musicMeta.common.artists.map(fromGBK).join();

下面的 iconv.decode 调用也可以更改为调用这个新的包装函数。

确实忽略了 GBK 的情况,请无视之前的建议。 ```js // 文件顶部定义 const fromGBK = (text) => iconv.decode(new Buffer(text || ''), 'gbk') // 第 47、48 行更换为 musicMeta.common.artist = musicMeta.common.artists.map(fromGBK).join(); ``` 下面的 `iconv.decode` 调用也可以更改为调用这个新的包装函数。
musicMeta.common.artists.forEach((artist) => artist = iconv.decode(new Buffer(artist ?? ''), 'gbk'));
musicMeta.common.artist = musicMeta.common.artists.toString();
musicMeta.common.artist = musicMeta.common.artists.map(fromGBK).join();
}
musicMeta.common.title = iconv.decode(new Buffer(musicMeta.common.title ?? ''), 'gbk');
musicMeta.common.album = iconv.decode(new Buffer(musicMeta.common.album ?? ''), 'gbk');
musicMeta.common.title = fromGBK(musicMeta.common.title);
musicMeta.common.album = fromGBK(musicMeta.common.album);
}
}
jixunmoe marked this conversation as resolved Outdated

为什么一开始不传入一个整数值呢

为什么一开始不传入一个整数值呢

wasm版本返回的songId是string,'0'也和0一样代表无效的id

wasm版本返回的songId是string,'0'也和0一样代表无效的id

建议改一下 wasm 的返回值或 js 接收这个值的时候做类型转换。

建议改一下 wasm 的返回值或 js 接收这个值的时候做类型转换。

id本身的类型就是number | string,所以我觉得两种都应该支持吧

id本身的类型就是number | string,所以我觉得两种都应该支持吧

也行

也行