Jixun
3 years ago
committed by
MengYX
13 changed files with 207 additions and 16 deletions
@ -0,0 +1,53 @@ |
|||||
|
<template> |
||||
|
<el-dialog fullscreen @close="cancel()" title="解密设定" :visible="show" width="30%" center> |
||||
|
<el-form ref="form" :model="form" label-width="80px"> |
||||
|
<el-form-item label="Joox UUID"> |
||||
|
<el-input type="text" placeholder="UUID" v-model="form.jooxUUID" clearable maxlength="32" show-word-limit> |
||||
|
</el-input> |
||||
|
</el-form-item> |
||||
|
</el-form> |
||||
|
<span slot="footer" class="dialog-footer"> |
||||
|
<el-button type="primary" :loading="saving" @click="emitConfirm()">确 定</el-button> |
||||
|
</span> |
||||
|
</el-dialog> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
import storage from '../utils/storage'; |
||||
|
|
||||
|
export default { |
||||
|
components: {}, |
||||
|
props: { |
||||
|
show: { type: Boolean, required: true }, |
||||
|
}, |
||||
|
data() { |
||||
|
return { |
||||
|
saving: false, |
||||
|
form: { |
||||
|
jooxUUID: '', |
||||
|
}, |
||||
|
centerDialogVisible: false, |
||||
|
}; |
||||
|
}, |
||||
|
async mounted() { |
||||
|
await this.resetForm(); |
||||
|
}, |
||||
|
methods: { |
||||
|
async resetForm() { |
||||
|
this.form.jooxUUID = await storage.loadJooxUUID(); |
||||
|
}, |
||||
|
|
||||
|
async cancel() { |
||||
|
await this.resetForm(); |
||||
|
this.$emit('done'); |
||||
|
}, |
||||
|
|
||||
|
async emitConfirm() { |
||||
|
this.saving = true; |
||||
|
await storage.saveJooxUUID(this.form.jooxUUID); |
||||
|
this.saving = false; |
||||
|
this.$emit('done'); |
||||
|
}, |
||||
|
}, |
||||
|
}; |
||||
|
</script> |
@ -0,0 +1,34 @@ |
|||||
|
import { DecryptResult } from './entity'; |
||||
|
import { AudioMimeType, GetArrayBuffer, SniffAudioExt } from './utils'; |
||||
|
|
||||
|
import jooxFactory from '@unlock-music-gh/joox-crypto'; |
||||
|
import storage from '@/utils/storage'; |
||||
|
import { MergeUint8Array } from '@/utils/MergeUint8Array'; |
||||
|
|
||||
|
export async function Decrypt(file: Blob, raw_filename: string, raw_ext: string): Promise<DecryptResult> { |
||||
|
const uuid = await storage.loadJooxUUID(''); |
||||
|
if (!uuid || uuid.length !== 32) { |
||||
|
throw new Error('请在“解密设定”填写应用 Joox 应用的 UUID。'); |
||||
|
} |
||||
|
|
||||
|
const fileBuffer = new Uint8Array(await GetArrayBuffer(file)); |
||||
|
const decryptor = jooxFactory(fileBuffer, uuid); |
||||
|
if (!decryptor) { |
||||
|
throw new Error('不支持的 joox 加密格式'); |
||||
|
} |
||||
|
|
||||
|
const musicDecoded = MergeUint8Array(decryptor.decryptFile(fileBuffer)); |
||||
|
const ext = SniffAudioExt(musicDecoded); |
||||
|
const mime = AudioMimeType[ext]; |
||||
|
const musicBlob = new Blob([musicDecoded], { type: mime }); |
||||
|
|
||||
|
return { |
||||
|
title: raw_filename.replace(/\.[^\.]+$/, ''), |
||||
|
artist: '未知', |
||||
|
album: '未知', |
||||
|
file: URL.createObjectURL(musicBlob), |
||||
|
blob: musicBlob, |
||||
|
mime: mime, |
||||
|
ext: ext, |
||||
|
}; |
||||
|
} |
@ -0,0 +1,15 @@ |
|||||
|
export function MergeUint8Array(array: Uint8Array[]): Uint8Array { |
||||
|
let length = 0; |
||||
|
array.forEach((item) => { |
||||
|
length += item.length; |
||||
|
}); |
||||
|
|
||||
|
let mergedArray = new Uint8Array(length); |
||||
|
let offset = 0; |
||||
|
array.forEach((item) => { |
||||
|
mergedArray.set(item, offset); |
||||
|
offset += item.length; |
||||
|
}); |
||||
|
|
||||
|
return mergedArray; |
||||
|
} |
@ -0,0 +1,7 @@ |
|||||
|
import BaseStorage from './storage/BaseStorage'; |
||||
|
import BrowserNativeStorage from './storage/BrowserNativeStorage'; |
||||
|
import ChromeExtensionStorage from './storage/ChromeExtensionStorage'; |
||||
|
|
||||
|
const storage: BaseStorage = ChromeExtensionStorage.works ? new ChromeExtensionStorage() : new BrowserNativeStorage(); |
||||
|
|
||||
|
export default storage; |
@ -0,0 +1,14 @@ |
|||||
|
const KEY_JOOX_UUID = 'joox.uuid'; |
||||
|
|
||||
|
export default abstract class BaseStorage { |
||||
|
protected abstract save<T>(name: string, value: T): Promise<void>; |
||||
|
protected abstract load<T>(name: string, defaultValue: T): Promise<T>; |
||||
|
|
||||
|
public saveJooxUUID(uuid: string): Promise<void> { |
||||
|
return this.save(KEY_JOOX_UUID, uuid); |
||||
|
} |
||||
|
|
||||
|
public loadJooxUUID(defaultValue: string = ''): Promise<string> { |
||||
|
return this.load(KEY_JOOX_UUID, defaultValue); |
||||
|
} |
||||
|
} |
@ -0,0 +1,15 @@ |
|||||
|
import BaseStorage from './BaseStorage'; |
||||
|
|
||||
|
export default class BrowserNativeStorage extends BaseStorage { |
||||
|
protected async load<T>(name: string, defaultValue: T): Promise<T> { |
||||
|
const result = localStorage.getItem(name); |
||||
|
if (result === null) { |
||||
|
return defaultValue; |
||||
|
} |
||||
|
return JSON.parse(result); |
||||
|
} |
||||
|
|
||||
|
protected async save<T>(name: string, value: T): Promise<void> { |
||||
|
localStorage.setItem(name, JSON.stringify(value)); |
||||
|
} |
||||
|
} |
@ -0,0 +1,21 @@ |
|||||
|
import BaseStorage from './BaseStorage'; |
||||
|
|
||||
|
declare var chrome: any; |
||||
|
|
||||
|
export default class ChromeExtensionStorage extends BaseStorage { |
||||
|
static get works(): boolean { |
||||
|
return Boolean(chrome?.storage?.local?.set); |
||||
|
} |
||||
|
|
||||
|
protected async load<T>(name: string, defaultValue: T): Promise<T> { |
||||
|
const result = await chrome.storage.local.get({ [name]: defaultValue }); |
||||
|
if (Object.prototype.hasOwnProperty.call(result, name)) { |
||||
|
return result[name]; |
||||
|
} |
||||
|
return defaultValue; |
||||
|
} |
||||
|
|
||||
|
protected async save<T>(name: string, value: T): Promise<void> { |
||||
|
return chrome.storage.local.set({ [name]: value }); |
||||
|
} |
||||
|
} |
Loading…
Reference in new issue