Emmm Monster
4 years ago
9 changed files with 230 additions and 164 deletions
@ -0,0 +1,56 @@ |
|||||
|
import {fromByteArray as Base64Encode} from "base64-js"; |
||||
|
|
||||
|
export const IXAREA_API_ENDPOINT = "https://stats.ixarea.com/apis" |
||||
|
|
||||
|
export interface UpdateInfo { |
||||
|
Found: boolean |
||||
|
HttpsFound: boolean |
||||
|
Version: string |
||||
|
URL: string |
||||
|
Detail: string |
||||
|
} |
||||
|
|
||||
|
export async function checkUpdate(version: string): Promise<UpdateInfo> { |
||||
|
const resp = await fetch(IXAREA_API_ENDPOINT + "/music/app-version", { |
||||
|
method: "POST", |
||||
|
headers: {"Content-Type": "application/json"}, |
||||
|
body: JSON.stringify({"Version": version}) |
||||
|
}); |
||||
|
return await resp.json(); |
||||
|
} |
||||
|
|
||||
|
export function reportKeyUsage(keyData: Uint8Array, maskData: number[], filename: string, format: string, title: string, artist?: string, album?: string) { |
||||
|
return fetch(IXAREA_API_ENDPOINT + "/qmcmask/usage", { |
||||
|
method: "POST", |
||||
|
headers: {"Content-Type": "application/json"}, |
||||
|
body: JSON.stringify({ |
||||
|
Mask: Base64Encode(new Uint8Array(maskData)), Key: Base64Encode(keyData), |
||||
|
Artist: artist, Title: title, Album: album, Filename: filename, Format: format |
||||
|
}), |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
interface KeyInfo { |
||||
|
Matrix44: string |
||||
|
} |
||||
|
|
||||
|
export async function queryKeyInfo(keyData: Uint8Array, filename: string, format: string): Promise<KeyInfo> { |
||||
|
const resp = await fetch(IXAREA_API_ENDPOINT + "/qmcmask/query", { |
||||
|
method: "POST", |
||||
|
headers: {"Content-Type": "application/json"}, |
||||
|
body: JSON.stringify({Format: format, Key: Base64Encode(keyData), Filename: filename, Type: 44}), |
||||
|
}); |
||||
|
return await resp.json(); |
||||
|
} |
||||
|
|
||||
|
export interface CoverInfo { |
||||
|
Id: string |
||||
|
Type: number |
||||
|
} |
||||
|
|
||||
|
export async function queryAlbumCover(title: string, artist?: string, album?: string): Promise<CoverInfo> { |
||||
|
const endpoint = IXAREA_API_ENDPOINT + "/music/qq-cover" |
||||
|
const params = new URLSearchParams([["Title", title], ["Artist", artist ?? ""], ["Album", album ?? ""]]) |
||||
|
const resp = await fetch(`${endpoint}?${params.toString()}`) |
||||
|
return await resp.json() |
||||
|
} |
@ -1,20 +1,35 @@ |
|||||
import {DecryptResult} from "@/decrypt/entity"; |
import {DecryptResult} from "@/decrypt/entity"; |
||||
|
|
||||
export function DownloadBlobMusic(data: DecryptResult, format: string) {//todo: use enum
|
export enum FilenamePolicy { |
||||
|
ArtistAndTitle, |
||||
|
TitleOnly, |
||||
|
TitleAndArtist, |
||||
|
SameAsOriginal, |
||||
|
} |
||||
|
|
||||
|
export const FilenamePolicies: { key: FilenamePolicy, text: string }[] = [ |
||||
|
{key: FilenamePolicy.ArtistAndTitle, text: "歌手-歌曲名"}, |
||||
|
{key: FilenamePolicy.TitleOnly, text: "歌曲名"}, |
||||
|
{key: FilenamePolicy.TitleAndArtist, text: "歌曲名-歌手"}, |
||||
|
{key: FilenamePolicy.SameAsOriginal, text: "同源文件名"}, |
||||
|
] |
||||
|
|
||||
|
|
||||
|
export function DownloadBlobMusic(data: DecryptResult, policy: FilenamePolicy) { |
||||
const a = document.createElement('a'); |
const a = document.createElement('a'); |
||||
a.href = data.file; |
a.href = data.file; |
||||
switch (format) { |
switch (policy) { |
||||
default: |
default: |
||||
case "1": |
case FilenamePolicy.ArtistAndTitle: |
||||
a.download = data.artist + " - " + data.title + "." + data.ext; |
a.download = data.artist + " - " + data.title + "." + data.ext; |
||||
break; |
break; |
||||
case "2": |
case FilenamePolicy.TitleOnly: |
||||
a.download = data.title + "." + data.ext; |
a.download = data.title + "." + data.ext; |
||||
break; |
break; |
||||
case "3": |
case FilenamePolicy.TitleAndArtist: |
||||
a.download = data.title + " - " + data.artist + "." + data.ext; |
a.download = data.title + " - " + data.artist + "." + data.ext; |
||||
break; |
break; |
||||
case "4": |
case FilenamePolicy.SameAsOriginal: |
||||
a.download = data.rawFilename + "." + data.ext; |
a.download = data.rawFilename + "." + data.ext; |
||||
break; |
break; |
||||
} |
} |
@ -0,0 +1,111 @@ |
|||||
|
<template> |
||||
|
<div> |
||||
|
<file-selector @error="showFail" @success="showSuccess"/> |
||||
|
|
||||
|
<div id="app-control"> |
||||
|
<el-row class="mb-3"> |
||||
|
<span>歌曲命名格式:</span> |
||||
|
<el-radio v-for="k in FilenamePolicies" :key="k.key" |
||||
|
v-model="filename_policy" :label="k.key"> |
||||
|
{{ k.text }} |
||||
|
</el-radio> |
||||
|
</el-row> |
||||
|
<el-row> |
||||
|
<el-button icon="el-icon-download" plain @click="handleDownloadAll">下载全部</el-button> |
||||
|
<el-button icon="el-icon-delete" plain type="danger" @click="handleDeleteAll">清除全部</el-button> |
||||
|
|
||||
|
<el-tooltip class="item" effect="dark" placement="top-start"> |
||||
|
<div slot="content"> |
||||
|
当您使用此工具进行大量文件解锁的时候,建议开启此选项。<br/> |
||||
|
开启后,解锁结果将不会存留于浏览器中,防止内存不足。 |
||||
|
</div> |
||||
|
<el-checkbox v-model="instant_download" border class="ml-2">立即保存</el-checkbox> |
||||
|
</el-tooltip> |
||||
|
</el-row> |
||||
|
</div> |
||||
|
|
||||
|
<audio :autoplay="playing_auto" :src="playing_url" controls/> |
||||
|
|
||||
|
<PreviewTable :policy="filename_policy" :table-data="tableData" @play="changePlaying"/> |
||||
|
</div> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
|
||||
|
import FileSelector from "../component/FileSelector" |
||||
|
import PreviewTable from "../component/PreviewTable" |
||||
|
import {DownloadBlobMusic, FilenamePolicy, FilenamePolicies, RemoveBlobMusic} from "@/utils/utils" |
||||
|
|
||||
|
export default { |
||||
|
name: 'Home', |
||||
|
components: { |
||||
|
FileSelector, |
||||
|
PreviewTable |
||||
|
}, |
||||
|
data() { |
||||
|
return { |
||||
|
activeIndex: '1', |
||||
|
tableData: [], |
||||
|
playing_url: "", |
||||
|
playing_auto: false, |
||||
|
filename_policy: FilenamePolicy.ArtistAndTitle, |
||||
|
instant_download: false, |
||||
|
FilenamePolicies |
||||
|
} |
||||
|
}, |
||||
|
methods: { |
||||
|
showSuccess(data) { |
||||
|
if (this.instant_download) { |
||||
|
DownloadBlobMusic(data, this.filename_policy); |
||||
|
RemoveBlobMusic(data); |
||||
|
} else { |
||||
|
this.tableData.push(data); |
||||
|
this.$notify.success({ |
||||
|
title: '解锁成功', |
||||
|
message: '成功解锁 ' + data.title, |
||||
|
duration: 3000 |
||||
|
}); |
||||
|
} |
||||
|
if (process.env.NODE_ENV === 'production') { |
||||
|
let _rp_data = [data.title, data.artist, data.album]; |
||||
|
window._paq.push(["trackEvent", "Unlock", data.rawExt + "," + data.mime, JSON.stringify(_rp_data)]); |
||||
|
} |
||||
|
}, |
||||
|
showFail(errInfo, filename) { |
||||
|
console.error(errInfo, filename) |
||||
|
this.$notify.error({ |
||||
|
title: '出现问题', |
||||
|
message: errInfo + "," + filename + |
||||
|
',参考<a target="_blank" href="https://github.com/ix64/unlock-music/wiki/使用提示">使用提示</a>', |
||||
|
dangerouslyUseHTMLString: true, |
||||
|
duration: 6000 |
||||
|
}); |
||||
|
if (process.env.NODE_ENV === 'production') { |
||||
|
window._paq.push(["trackEvent", "Error", errInfo, filename]); |
||||
|
} |
||||
|
}, |
||||
|
changePlaying(url) { |
||||
|
this.playing_url = url; |
||||
|
this.playing_auto = true; |
||||
|
}, |
||||
|
handleDeleteAll() { |
||||
|
this.tableData.forEach(value => { |
||||
|
RemoveBlobMusic(value); |
||||
|
}); |
||||
|
this.tableData = []; |
||||
|
}, |
||||
|
handleDownloadAll() { |
||||
|
let index = 0; |
||||
|
let c = setInterval(() => { |
||||
|
if (index < this.tableData.length) { |
||||
|
DownloadBlobMusic(this.tableData[index], this.filename_policy); |
||||
|
index++; |
||||
|
} else { |
||||
|
clearInterval(c); |
||||
|
} |
||||
|
}, 300); |
||||
|
} |
||||
|
}, |
||||
|
} |
||||
|
</script> |
||||
|
|
Loading…
Reference in new issue