mirror of
https://git.um-react.app/um/um-react.git
synced 2025-11-28 03:23:02 +00:00
102 lines
2.7 KiB
TypeScript
102 lines
2.7 KiB
TypeScript
import { getFileName } from './pathHelper';
|
|
import { SQLDatabase, SQLStatic, loadSQL } from './sqlite';
|
|
import { KuGou } from '@unlock-music/crypto';
|
|
|
|
export interface QMAndroidKeyEntry {
|
|
name: string;
|
|
ekey: string;
|
|
}
|
|
|
|
export type KugouKeyEntry = {
|
|
audioHash: string;
|
|
ekey: string;
|
|
};
|
|
|
|
export class DatabaseKeyExtractor {
|
|
private static _instance: DatabaseKeyExtractor;
|
|
|
|
static async getInstance() {
|
|
if (!DatabaseKeyExtractor._instance) {
|
|
DatabaseKeyExtractor._instance = new DatabaseKeyExtractor(await loadSQL());
|
|
}
|
|
return DatabaseKeyExtractor._instance;
|
|
}
|
|
|
|
constructor(private SQL: SQLStatic) {}
|
|
|
|
private hasTable(db: SQLDatabase, name: string): boolean {
|
|
const tables = db.exec('SELECT name FROM sqlite_master WHERE type="table"')[0].values.map((x) => x[0]);
|
|
return tables.includes(name);
|
|
}
|
|
|
|
extractQmcV2KeysFromSqliteDb(buffer: ArrayBuffer): null | QMAndroidKeyEntry[] {
|
|
let db: SQLDatabase | null = null;
|
|
|
|
try {
|
|
db = new this.SQL.Database(new Uint8Array(buffer));
|
|
|
|
let sql: undefined | string;
|
|
if (this.hasTable(db, 'audio_file_ekey_table')) {
|
|
sql = 'select file_path, ekey from audio_file_ekey_table';
|
|
} else if (this.hasTable(db, 'EKeyFileInfo')) {
|
|
sql = 'select filePath, eKey from EKeyFileInfo';
|
|
}
|
|
if (!sql) return null;
|
|
|
|
const result = db.exec(sql);
|
|
if (result.length === 0) {
|
|
return [];
|
|
}
|
|
|
|
const keys = result[0].values;
|
|
return keys.map(([path, ekey]) => ({
|
|
// strip dir name
|
|
name: getFileName(String(path)),
|
|
ekey: String(ekey),
|
|
}));
|
|
} finally {
|
|
db?.close();
|
|
}
|
|
}
|
|
|
|
extractKugouKeyFromEncryptedDb(buffer: ArrayBuffer): null | KugouKeyEntry[] {
|
|
const dbBuffer = new Uint8Array(buffer);
|
|
let db: SQLDatabase | null = null;
|
|
|
|
try {
|
|
KuGou.decryptDatabase(dbBuffer);
|
|
db = new this.SQL.Database(dbBuffer);
|
|
|
|
let sql: undefined | string;
|
|
if (this.hasTable(db, 'ShareFileItems')) {
|
|
sql = `
|
|
select H, K from (
|
|
select EncryptionKeyId as H, EncryptionKey as K from ShareFileItems
|
|
union all
|
|
select EnHash as H, EnKey as K from DownloadItem
|
|
) t
|
|
where
|
|
t.H is not null and t.H != ''
|
|
and t.K is not null and t.K != ''
|
|
group by t.H
|
|
`;
|
|
}
|
|
if (!sql) return null;
|
|
|
|
const result = db.exec(sql);
|
|
if (result.length === 0) {
|
|
return [];
|
|
}
|
|
|
|
const keys = result[0].values;
|
|
return keys.map(([audioHash, ekey]) => ({
|
|
// strip dir name
|
|
audioHash: String(audioHash).normalize(),
|
|
ekey: String(ekey).normalize(),
|
|
}));
|
|
} finally {
|
|
db?.close();
|
|
}
|
|
}
|
|
}
|