mirror of
https://github.com/imsyy/SPlayer.git
synced 2025-11-25 19:37:35 +08:00
Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
adbda459ba | ||
|
|
984d747179 |
@@ -23,7 +23,7 @@
|
||||
- 仅对移动端做了基础适配,**不保证功能全部可用**
|
||||
|
||||
> 请注意,本程序不打算开发移动端,也不会对移动端进行完美适配,仅保证基础可用性
|
||||
|
||||
|
||||
- 欢迎各位大佬 `Star` 😍
|
||||
|
||||
## 👀 Demo
|
||||
@@ -137,8 +137,11 @@ docker-compose up -d
|
||||
### 在线部署
|
||||
|
||||
```bash
|
||||
# 拉取
|
||||
# 从 Docker Hub 拉取
|
||||
docker pull imsyy/splayer:latest
|
||||
# 从 GitHub ghcr 拉取
|
||||
docker pull ghcr.io/imsyy/splayer:latest
|
||||
|
||||
# 运行
|
||||
docker run -d --name SPlayer -p 7899:7899 imsyy/splayer:latest
|
||||
```
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { ipcMain, dialog, app, clipboard, shell } from "electron";
|
||||
import { File, Picture, Id3v2Settings } from "node-taglib-sharp";
|
||||
import { readDirAsync } from "@main/utils/readDirAsync";
|
||||
import { parseFile } from "music-metadata";
|
||||
import { download } from "electron-dl";
|
||||
import getNeteaseMusicUrl from "@main/utils/getNeteaseMusicUrl";
|
||||
import NodeID3 from "node-id3";
|
||||
import axios from "axios";
|
||||
import fs from "fs/promises";
|
||||
|
||||
@@ -193,37 +193,42 @@ const mainIpcMain = (win) => {
|
||||
});
|
||||
|
||||
// 下载文件至指定目录
|
||||
ipcMain.handle("downloadFile", async (_, data, song, songName, songType, path) => {
|
||||
ipcMain.handle("downloadFile", async (_, songData, options) => {
|
||||
try {
|
||||
const { url, data, lyric, name, type } = JSON.parse(songData);
|
||||
const { path, downloadMeta, downloadCover, downloadLyrics } = JSON.parse(options);
|
||||
if (fs.access(path)) {
|
||||
const songData = JSON.parse(song);
|
||||
console.info("开始下载:", songData, data);
|
||||
console.info("开始下载:", name, url);
|
||||
// 下载歌曲
|
||||
const songDownload = await download(win, data.url, {
|
||||
const songDownload = await download(win, url, {
|
||||
directory: path,
|
||||
filename: `${songName}.${songType}`,
|
||||
filename: `${name}.${type}`,
|
||||
});
|
||||
// 若不为 mp3,则不进行元信息写入
|
||||
if (songType !== "mp3") return true;
|
||||
// 若关闭,则不进行元信息写入
|
||||
if (!downloadMeta) return true;
|
||||
// 下载封面
|
||||
const coverDownload = await download(win, songData.cover, {
|
||||
const coverDownload = await download(win, data.cover, {
|
||||
directory: path,
|
||||
filename: `${songName}.jpg`,
|
||||
filename: `${name}.jpg`,
|
||||
});
|
||||
// 生成歌曲文件的元数据
|
||||
const songTag = {
|
||||
title: songData.name,
|
||||
artist: Array.isArray(songData.artists)
|
||||
? songData.artists.map((ar) => ar.name).join(" / ")
|
||||
: songData.artists || "未知歌手",
|
||||
album: songData.album?.name || songData.album,
|
||||
image: coverDownload.getSavePath(),
|
||||
};
|
||||
// 读取歌曲文件
|
||||
const songFile = File.createFromPath(songDownload.getSavePath());
|
||||
// 生成图片信息
|
||||
const songCover = Picture.fromPath(coverDownload.getSavePath());
|
||||
// 保存修改后的元数据
|
||||
const isSuccess = NodeID3.write(songTag, songDownload.getSavePath());
|
||||
Id3v2Settings.forceDefaultVersion = true;
|
||||
Id3v2Settings.defaultVersion = 3;
|
||||
songFile.tag.title = data.name || "未知曲目";
|
||||
songFile.tag.album = data.album?.name || "未知专辑";
|
||||
songFile.tag.performers = data?.artists?.map((ar) => ar.name) || ["未知艺术家"];
|
||||
if (downloadLyrics) songFile.tag.lyrics = lyric;
|
||||
if (downloadCover) songFile.tag.pictures = [songCover];
|
||||
// 保存元信息
|
||||
songFile.save();
|
||||
songFile.dispose();
|
||||
// 删除封面
|
||||
await fs.unlink(coverDownload.getSavePath());
|
||||
return isSuccess;
|
||||
return true;
|
||||
} else {
|
||||
console.log(`目录不存在:${path}`);
|
||||
return false;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { dialog, shell } from "electron";
|
||||
import { dialog } from "electron";
|
||||
import { is } from "@electron-toolkit/utils";
|
||||
import pkg from "electron-updater";
|
||||
|
||||
@@ -10,23 +10,56 @@ const hasNewVersion = (info) => {
|
||||
.showMessageBox({
|
||||
title: "发现新版本 v" + info.version,
|
||||
message: "发现新版本 v" + info.version,
|
||||
detail: "是否前往 GitHub 下载新版本安装包?",
|
||||
buttons: ["前往", "取消"],
|
||||
detail: "是否立即下载并安装新版本?",
|
||||
buttons: ["立即下载", "取消"],
|
||||
type: "question",
|
||||
noLink: true,
|
||||
})
|
||||
.then((result) => {
|
||||
if (result.response === 0) {
|
||||
shell.openExternal("https://github.com/imsyy/SPlayer/releases");
|
||||
// 触发手动下载
|
||||
autoUpdater.downloadUpdate();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export const configureAutoUpdater = () => {
|
||||
if (is.dev) return false;
|
||||
autoUpdater.checkForUpdatesAndNotify();
|
||||
|
||||
// 监听下载进度事件
|
||||
autoUpdater.on("download-progress", (progressObj) => {
|
||||
console.log(`更新下载进度: ${progressObj.percent}%`);
|
||||
});
|
||||
|
||||
// 下载完成
|
||||
autoUpdater.on("update-downloaded", () => {
|
||||
// 显示安装弹窗
|
||||
dialog
|
||||
.showMessageBox({
|
||||
title: "下载完成",
|
||||
message: "新版本已下载完成,是否现在安装?",
|
||||
buttons: ["是", "稍后"],
|
||||
type: "question",
|
||||
})
|
||||
.then((result) => {
|
||||
if (result.response === 0) {
|
||||
// 安装更新
|
||||
autoUpdater.quitAndInstall();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// 下载失败
|
||||
autoUpdater.on("error", (err) => {
|
||||
console.error("下载更新失败:", err);
|
||||
dialog.showErrorBox("下载更新失败", "请检查网络连接并稍后重试!");
|
||||
});
|
||||
|
||||
// 若有更新
|
||||
autoUpdater.on("update-available", (info) => {
|
||||
hasNewVersion(info);
|
||||
});
|
||||
|
||||
// 检查更新
|
||||
autoUpdater.checkForUpdatesAndNotify();
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "splayer",
|
||||
"version": "2.0.0",
|
||||
"version": "2.0.1",
|
||||
"description": "A minimalist music player",
|
||||
"main": "./out/main/index.js",
|
||||
"author": "imsyy",
|
||||
@@ -41,7 +41,7 @@
|
||||
"js-cookie": "^3.0.5",
|
||||
"localforage": "^1.10.0",
|
||||
"music-metadata": "7.14.0",
|
||||
"node-id3": "^0.2.6",
|
||||
"node-taglib-sharp": "^5.2.3",
|
||||
"pinia": "^2.1.7",
|
||||
"pinia-plugin-persistedstate": "^3.2.1",
|
||||
"plyr": "^3.7.8",
|
||||
|
||||
182
pnpm-lock.yaml
generated
182
pnpm-lock.yaml
generated
@@ -50,9 +50,9 @@ dependencies:
|
||||
music-metadata:
|
||||
specifier: 7.14.0
|
||||
version: 7.14.0
|
||||
node-id3:
|
||||
specifier: ^0.2.6
|
||||
version: 0.2.6
|
||||
node-taglib-sharp:
|
||||
specifier: ^5.2.3
|
||||
version: 5.2.3
|
||||
pinia:
|
||||
specifier: ^2.1.7
|
||||
version: 2.1.7(vue@3.4.4)
|
||||
@@ -2972,6 +2972,17 @@ packages:
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/cross-spawn@6.0.5:
|
||||
resolution: {integrity: sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==}
|
||||
engines: {node: '>=4.8'}
|
||||
dependencies:
|
||||
nice-try: 1.0.5
|
||||
path-key: 2.0.1
|
||||
semver: 5.7.2
|
||||
shebang-command: 1.2.0
|
||||
which: 1.3.1
|
||||
dev: false
|
||||
|
||||
/cross-spawn@7.0.3:
|
||||
resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==}
|
||||
engines: {node: '>= 8'}
|
||||
@@ -3055,6 +3066,10 @@ packages:
|
||||
'@babel/runtime': 7.23.8
|
||||
dev: true
|
||||
|
||||
/dateformat@3.0.3:
|
||||
resolution: {integrity: sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q==}
|
||||
dev: false
|
||||
|
||||
/debounce-fn@4.0.0:
|
||||
resolution: {integrity: sha512-8pYCQiL9Xdcg0UPSD3d+0KMlOjp+KGU5EPwYddgzQ7DATsg4fuUDjQtsYLmWjnk2obnNHgV3vE2Y4jejSOJVBQ==}
|
||||
engines: {node: '>=10'}
|
||||
@@ -3703,6 +3718,19 @@ packages:
|
||||
resolution: {integrity: sha512-qaeGN5bx63s/AXgQo8gj6fBkxge+OoLddLniox5qtLAEY5HSnuSlISXVPxnSae1dWblvTh4/HoMIB+mbMsvZzw==}
|
||||
dev: true
|
||||
|
||||
/execa@1.0.0:
|
||||
resolution: {integrity: sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==}
|
||||
engines: {node: '>=6'}
|
||||
dependencies:
|
||||
cross-spawn: 6.0.5
|
||||
get-stream: 4.1.0
|
||||
is-stream: 1.1.0
|
||||
npm-run-path: 2.0.2
|
||||
p-finally: 1.0.0
|
||||
signal-exit: 3.0.7
|
||||
strip-eof: 1.0.0
|
||||
dev: false
|
||||
|
||||
/express-fileupload@1.4.3:
|
||||
resolution: {integrity: sha512-vRzZo2YELm68DfR/CX8RMXgeK9BTAANxigrKACPjCXFGEzkCt/QWbqaIXP3W61uaX/hLj0CAo3/EVelpSQXkqA==}
|
||||
engines: {node: '>=12.0.0'}
|
||||
@@ -4074,6 +4102,13 @@ packages:
|
||||
through: 2.3.8
|
||||
dev: false
|
||||
|
||||
/get-stream@4.1.0:
|
||||
resolution: {integrity: sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==}
|
||||
engines: {node: '>=6'}
|
||||
dependencies:
|
||||
pump: 3.0.0
|
||||
dev: false
|
||||
|
||||
/get-stream@5.2.0:
|
||||
resolution: {integrity: sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==}
|
||||
engines: {node: '>=8'}
|
||||
@@ -4361,19 +4396,11 @@ packages:
|
||||
safer-buffer: 2.1.2
|
||||
dev: false
|
||||
|
||||
/iconv-lite@0.6.2:
|
||||
resolution: {integrity: sha512-2y91h5OpQlolefMPmUlivelittSWy0rP+oYVpn6A7GwVHNE8AWzoYOBNmlwks3LobaJxgHCYZAnyNo2GgpNRNQ==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
dependencies:
|
||||
safer-buffer: 2.1.2
|
||||
dev: false
|
||||
|
||||
/iconv-lite@0.6.3:
|
||||
resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
dependencies:
|
||||
safer-buffer: 2.1.2
|
||||
dev: true
|
||||
|
||||
/idb@7.1.1:
|
||||
resolution: {integrity: sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ==}
|
||||
@@ -4427,6 +4454,11 @@ packages:
|
||||
side-channel: 1.0.4
|
||||
dev: true
|
||||
|
||||
/invert-kv@3.0.1:
|
||||
resolution: {integrity: sha512-CYdFeFexxhv/Bcny+Q0BfOV+ltRlJcd4BBZBYFX/O0u4npJrgZtIcjokegtiSMAvlMTJ+Koq0GBCc//3bueQxw==}
|
||||
engines: {node: '>=8'}
|
||||
dev: false
|
||||
|
||||
/iota-array@1.0.0:
|
||||
resolution: {integrity: sha512-pZ2xT+LOHckCatGQ3DcG/a+QuEqvoxqkiL7tvE8nn3uuu+f6i1TtpB5/FtWFbxUuVr5PZCx8KskuGatbJDXOWA==}
|
||||
dev: false
|
||||
@@ -4578,6 +4610,11 @@ packages:
|
||||
call-bind: 1.0.5
|
||||
dev: true
|
||||
|
||||
/is-stream@1.1.0:
|
||||
resolution: {integrity: sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
dev: false
|
||||
|
||||
/is-stream@2.0.1:
|
||||
resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==}
|
||||
engines: {node: '>=8'}
|
||||
@@ -4630,12 +4667,15 @@ packages:
|
||||
|
||||
/isexe@2.0.0:
|
||||
resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
|
||||
dev: true
|
||||
|
||||
/isstream@0.1.2:
|
||||
resolution: {integrity: sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==}
|
||||
dev: false
|
||||
|
||||
/itiriri@2.0.1:
|
||||
resolution: {integrity: sha512-XutdsL9Nm9ejgRlFwOBdWW78Txvs9ehhMFeGCp3GjGIWSeQNgOzOfo+PjuQSmaFgE1ol6Pr2g5Jg7W4BYIploQ==}
|
||||
dev: false
|
||||
|
||||
/jackspeak@2.3.6:
|
||||
resolution: {integrity: sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==}
|
||||
engines: {node: '>=14'}
|
||||
@@ -4767,6 +4807,13 @@ packages:
|
||||
/lazy-val@1.0.5:
|
||||
resolution: {integrity: sha512-0/BnGCCfyUMkBpeDgWihanIAF9JmZhHBgUhEqzvf+adhNGLoP6TaiI5oF8oyb3I45P+PcnrqihSf01M0l0G5+Q==}
|
||||
|
||||
/lcid@3.1.1:
|
||||
resolution: {integrity: sha512-M6T051+5QCGLBQb8id3hdvIW8+zeFV2FyBGFS9IEK5H9Wt4MueD4bW1eWikpHgZp+5xR3l5c8pZUkQsIA0BFZg==}
|
||||
engines: {node: '>=8'}
|
||||
dependencies:
|
||||
invert-kv: 3.0.1
|
||||
dev: false
|
||||
|
||||
/leven@3.1.0:
|
||||
resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==}
|
||||
engines: {node: '>=6'}
|
||||
@@ -4892,6 +4939,13 @@ packages:
|
||||
dependencies:
|
||||
'@jridgewell/sourcemap-codec': 1.4.15
|
||||
|
||||
/map-age-cleaner@0.1.3:
|
||||
resolution: {integrity: sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==}
|
||||
engines: {node: '>=6'}
|
||||
dependencies:
|
||||
p-defer: 1.0.0
|
||||
dev: false
|
||||
|
||||
/matcher@3.0.0:
|
||||
resolution: {integrity: sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng==}
|
||||
engines: {node: '>=10'}
|
||||
@@ -4918,6 +4972,15 @@ packages:
|
||||
engines: {node: '>= 0.8'}
|
||||
dev: false
|
||||
|
||||
/mem@5.1.1:
|
||||
resolution: {integrity: sha512-qvwipnozMohxLXG1pOqoLiZKNkC4r4qqRucSoDwXowsNGDSULiqFTRUF05vcZWnwJSG22qTsynQhxbaMtnX9gw==}
|
||||
engines: {node: '>=8'}
|
||||
dependencies:
|
||||
map-age-cleaner: 0.1.3
|
||||
mimic-fn: 2.1.0
|
||||
p-is-promise: 2.1.0
|
||||
dev: false
|
||||
|
||||
/merge-descriptors@1.0.1:
|
||||
resolution: {integrity: sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==}
|
||||
dev: false
|
||||
@@ -5139,6 +5202,10 @@ packages:
|
||||
engines: {node: '>= 0.4.0'}
|
||||
dev: false
|
||||
|
||||
/nice-try@1.0.5:
|
||||
resolution: {integrity: sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==}
|
||||
dev: false
|
||||
|
||||
/node-addon-api@1.7.2:
|
||||
resolution: {integrity: sha512-ibPK3iA+vaY1eEjESkQkM0BbCqFOaZMiXRTtdB0u7b4djtY6JnsjvPdUHVMg6xQt3B8fpTTWHI9A+ADjM9frzg==}
|
||||
requiresBuild: true
|
||||
@@ -5155,16 +5222,21 @@ packages:
|
||||
engines: {node: '>= 6.13.0'}
|
||||
dev: false
|
||||
|
||||
/node-id3@0.2.6:
|
||||
resolution: {integrity: sha512-w8GuKXLlPpDjTxLowCt/uYMhRQzED3cg2GdSG1i6RSGKeDzPvxlXeLQuQInKljahPZ0aDnmyX7FX8BbJOM7REg==}
|
||||
dependencies:
|
||||
iconv-lite: 0.6.2
|
||||
dev: false
|
||||
|
||||
/node-releases@2.0.14:
|
||||
resolution: {integrity: sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==}
|
||||
dev: true
|
||||
|
||||
/node-taglib-sharp@5.2.3:
|
||||
resolution: {integrity: sha512-cqomQ+pD/4HPYpVS5UpDkyN3YQwX67YHSXFk9udSI0VgFKdHSG9/PJFKLehsuwWUr5HKVqRVgSJjBIeOvyLajA==}
|
||||
engines: {node: '>=12.16.1'}
|
||||
dependencies:
|
||||
dateformat: 3.0.3
|
||||
iconv-lite: 0.6.3
|
||||
itiriri: 2.0.1
|
||||
os-locale: 4.0.0
|
||||
uuid: 8.3.2
|
||||
dev: false
|
||||
|
||||
/normalize-path@3.0.0:
|
||||
resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
@@ -5174,6 +5246,13 @@ packages:
|
||||
resolution: {integrity: sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==}
|
||||
engines: {node: '>=10'}
|
||||
|
||||
/npm-run-path@2.0.2:
|
||||
resolution: {integrity: sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw==}
|
||||
engines: {node: '>=4'}
|
||||
dependencies:
|
||||
path-key: 2.0.1
|
||||
dev: false
|
||||
|
||||
/nth-check@2.1.1:
|
||||
resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==}
|
||||
dependencies:
|
||||
@@ -5236,10 +5315,34 @@ packages:
|
||||
type-check: 0.4.0
|
||||
dev: true
|
||||
|
||||
/os-locale@4.0.0:
|
||||
resolution: {integrity: sha512-HsSR1+2l6as4Wp2SGZxqLnuFHxVvh1Ir9pvZxyujsC13egZVe7P0YeBLN0ijQzM/twrO5To3ia3jzBXAvpMTEA==}
|
||||
engines: {node: '>=8'}
|
||||
dependencies:
|
||||
execa: 1.0.0
|
||||
lcid: 3.1.1
|
||||
mem: 5.1.1
|
||||
dev: false
|
||||
|
||||
/p-cancelable@2.1.1:
|
||||
resolution: {integrity: sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
/p-defer@1.0.0:
|
||||
resolution: {integrity: sha512-wB3wfAxZpk2AzOfUMJNL+d36xothRSyj8EXOa4f6GMqYDN9BJaaSISbsk+wS9abmnebVw95C2Kb5t85UmpCxuw==}
|
||||
engines: {node: '>=4'}
|
||||
dev: false
|
||||
|
||||
/p-finally@1.0.0:
|
||||
resolution: {integrity: sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==}
|
||||
engines: {node: '>=4'}
|
||||
dev: false
|
||||
|
||||
/p-is-promise@2.1.0:
|
||||
resolution: {integrity: sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg==}
|
||||
engines: {node: '>=6'}
|
||||
dev: false
|
||||
|
||||
/p-limit@2.3.0:
|
||||
resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==}
|
||||
engines: {node: '>=6'}
|
||||
@@ -5337,6 +5440,11 @@ packages:
|
||||
engines: {node: '>=0.10.0'}
|
||||
dev: true
|
||||
|
||||
/path-key@2.0.1:
|
||||
resolution: {integrity: sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==}
|
||||
engines: {node: '>=4'}
|
||||
dev: false
|
||||
|
||||
/path-key@3.1.1:
|
||||
resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==}
|
||||
engines: {node: '>=8'}
|
||||
@@ -5896,6 +6004,11 @@ packages:
|
||||
requiresBuild: true
|
||||
optional: true
|
||||
|
||||
/semver@5.7.2:
|
||||
resolution: {integrity: sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==}
|
||||
hasBin: true
|
||||
dev: false
|
||||
|
||||
/semver@6.3.1:
|
||||
resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==}
|
||||
hasBin: true
|
||||
@@ -5980,6 +6093,13 @@ packages:
|
||||
resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==}
|
||||
dev: false
|
||||
|
||||
/shebang-command@1.2.0:
|
||||
resolution: {integrity: sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
dependencies:
|
||||
shebang-regex: 1.0.0
|
||||
dev: false
|
||||
|
||||
/shebang-command@2.0.0:
|
||||
resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
|
||||
engines: {node: '>=8'}
|
||||
@@ -5987,6 +6107,11 @@ packages:
|
||||
shebang-regex: 3.0.0
|
||||
dev: true
|
||||
|
||||
/shebang-regex@1.0.0:
|
||||
resolution: {integrity: sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
dev: false
|
||||
|
||||
/shebang-regex@3.0.0:
|
||||
resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==}
|
||||
engines: {node: '>=8'}
|
||||
@@ -5999,6 +6124,10 @@ packages:
|
||||
get-intrinsic: 1.2.2
|
||||
object-inspect: 1.13.1
|
||||
|
||||
/signal-exit@3.0.7:
|
||||
resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==}
|
||||
dev: false
|
||||
|
||||
/signal-exit@4.1.0:
|
||||
resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==}
|
||||
engines: {node: '>=14'}
|
||||
@@ -6211,6 +6340,11 @@ packages:
|
||||
engines: {node: '>=10'}
|
||||
dev: true
|
||||
|
||||
/strip-eof@1.0.0:
|
||||
resolution: {integrity: sha512-7FCwGGmx8mD5xQd3RPUvnSpUXHM3BWuzjtpD4TXsfcZ9EL4azvVVUscFYwD9nx8Kh+uCBC00XBtAykoMHwTh8Q==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
dev: false
|
||||
|
||||
/strip-json-comments@3.1.1:
|
||||
resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==}
|
||||
engines: {node: '>=8'}
|
||||
@@ -6677,6 +6811,11 @@ packages:
|
||||
hasBin: true
|
||||
dev: false
|
||||
|
||||
/uuid@8.3.2:
|
||||
resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==}
|
||||
hasBin: true
|
||||
dev: false
|
||||
|
||||
/vary@1.1.2:
|
||||
resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==}
|
||||
engines: {node: '>= 0.8'}
|
||||
@@ -6909,6 +7048,13 @@ packages:
|
||||
has-tostringtag: 1.0.0
|
||||
dev: true
|
||||
|
||||
/which@1.3.1:
|
||||
resolution: {integrity: sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==}
|
||||
hasBin: true
|
||||
dependencies:
|
||||
isexe: 2.0.0
|
||||
dev: false
|
||||
|
||||
/which@2.0.2:
|
||||
resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
|
||||
engines: {node: '>= 8'}
|
||||
|
||||
@@ -698,7 +698,7 @@ onBeforeUnmount(() => {
|
||||
border-color: var(--main-color);
|
||||
a,
|
||||
span,
|
||||
.play {
|
||||
.num {
|
||||
color: var(--main-color) !important;
|
||||
}
|
||||
.artist {
|
||||
|
||||
@@ -59,7 +59,7 @@
|
||||
@click="
|
||||
() => {
|
||||
drawerShow = false;
|
||||
playMode = 'song';
|
||||
playMode = 'normal';
|
||||
addSongToNext(songData);
|
||||
}
|
||||
"
|
||||
|
||||
@@ -147,7 +147,7 @@ const openDropdown = (e, data, song, index, sourceId, type) => {
|
||||
show: isSong && playMode.value !== "dj" && music.getPlaySongData?.id !== song.id && !isFm,
|
||||
props: {
|
||||
onClick: () => {
|
||||
playMode.value = "song";
|
||||
playMode.value = "normal";
|
||||
addSongToNext(song);
|
||||
},
|
||||
},
|
||||
|
||||
@@ -43,8 +43,9 @@
|
||||
<n-button
|
||||
:disabled="!downloadChoose"
|
||||
:loading="downloadStatus"
|
||||
:focusable="false"
|
||||
type="primary"
|
||||
@click="toSongDownload(songData, downloadChoose)"
|
||||
@click="toSongDownload(songData, lyricData, downloadChoose)"
|
||||
>
|
||||
下载
|
||||
</n-button>
|
||||
@@ -58,7 +59,7 @@ import { storeToRefs } from "pinia";
|
||||
import { isLogin } from "@/utils/auth";
|
||||
import { useRouter } from "vue-router";
|
||||
import { siteData, siteSettings } from "@/stores";
|
||||
import { getSongDetail, getSongDownload } from "@/api/song";
|
||||
import { getSongDetail, getSongDownload, getSongLyric } from "@/api/song";
|
||||
import { downloadFile, checkPlatform } from "@/utils/helper";
|
||||
import formatData from "@/utils/formatData";
|
||||
|
||||
@@ -66,11 +67,12 @@ const router = useRouter();
|
||||
const data = siteData();
|
||||
const settings = siteSettings();
|
||||
const { userData } = storeToRefs(data);
|
||||
const { downloadPath } = storeToRefs(settings);
|
||||
const { downloadPath, downloadMeta, downloadCover, downloadLyrics } = storeToRefs(settings);
|
||||
|
||||
// 歌曲下载数据
|
||||
const songId = ref(null);
|
||||
const songData = ref(null);
|
||||
const lyricData = ref(null);
|
||||
const downloadStatus = ref(false);
|
||||
const downloadSongShow = ref(false);
|
||||
const downloadChoose = ref(null);
|
||||
@@ -79,11 +81,13 @@ const downloadLevel = ref(null);
|
||||
// 获取歌曲详情
|
||||
const getMusicDetailData = async (id) => {
|
||||
try {
|
||||
const result = await getSongDetail(id);
|
||||
const songResult = await getSongDetail(id);
|
||||
const lyricResult = await getSongLyric(id);
|
||||
// 获取歌曲详情
|
||||
songData.value = formatData(result?.songs?.[0], "song")[0];
|
||||
songData.value = formatData(songResult?.songs?.[0], "song")[0];
|
||||
lyricData.value = lyricResult?.lrc?.lyric || null;
|
||||
// 生成音质列表
|
||||
generateLists(result);
|
||||
generateLists(songResult);
|
||||
} catch (error) {
|
||||
closeDownloadModal();
|
||||
console.error("歌曲信息获取失败:", error);
|
||||
@@ -91,26 +95,42 @@ const getMusicDetailData = async (id) => {
|
||||
};
|
||||
|
||||
// 歌曲下载
|
||||
const toSongDownload = async (song, br) => {
|
||||
console.log(song, br);
|
||||
downloadStatus.value = true;
|
||||
// 获取下载数据
|
||||
const result = await getSongDownload(song?.id, br);
|
||||
// 开始下载
|
||||
if (!downloadPath.value && checkPlatform.electron()) {
|
||||
$notification["warning"]({
|
||||
content: "缺少配置",
|
||||
meta: "请前往设置页配置默认下载目录",
|
||||
duration: 3000,
|
||||
const toSongDownload = async (song, lyric, br) => {
|
||||
try {
|
||||
console.log(song, lyric, br);
|
||||
downloadStatus.value = true;
|
||||
// 获取下载数据
|
||||
const result = await getSongDownload(song?.id, br);
|
||||
// 开始下载
|
||||
if (!downloadPath.value && checkPlatform.electron()) {
|
||||
$notification["warning"]({
|
||||
content: "缺少配置",
|
||||
meta: "请前往设置页配置默认下载目录",
|
||||
duration: 3000,
|
||||
});
|
||||
}
|
||||
if (!result.data?.url) {
|
||||
downloadStatus.value = false;
|
||||
return $message.error("下载失败,请重试");
|
||||
}
|
||||
// 获取下载结果
|
||||
const isDownloaded = await downloadFile(result.data, song, lyric, {
|
||||
path: downloadPath.value,
|
||||
downloadMeta: downloadMeta.value,
|
||||
downloadCover: downloadCover.value,
|
||||
downloadLyrics: downloadLyrics.value,
|
||||
});
|
||||
}
|
||||
const isDownloaded = await downloadFile(result.data, song, downloadPath.value);
|
||||
if (isDownloaded) {
|
||||
$message.success("下载完成");
|
||||
closeDownloadModal();
|
||||
} else {
|
||||
downloadStatus.value = false;
|
||||
$message.error("下载失败,请重试");
|
||||
console.log(lyric);
|
||||
if (isDownloaded) {
|
||||
$message.success("下载完成");
|
||||
closeDownloadModal();
|
||||
} else {
|
||||
downloadStatus.value = false;
|
||||
$message.error("下载失败,请重试");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("歌曲下载出错:", error);
|
||||
$message.error("歌曲下载失败,请重试");
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -19,11 +19,20 @@ const useMusicDataStore = defineStore("musicData", {
|
||||
playSongSource: 0,
|
||||
// 当前歌曲歌词数据
|
||||
playSongLyric: {
|
||||
lrc: [],
|
||||
yrc: [],
|
||||
hasTran: false,
|
||||
hasRoma: false,
|
||||
// 是否具有普通翻译
|
||||
hasLrcTran: false,
|
||||
// 是否具有普通音译
|
||||
hasLrcRoma: false,
|
||||
// 是否具有逐字歌词
|
||||
hasYrc: false,
|
||||
// 是否具有逐字翻译
|
||||
hasYrcTran: false,
|
||||
// 是否具有逐字音译
|
||||
hasYrcRoma: false,
|
||||
// 普通歌词数组
|
||||
lrc: [],
|
||||
// 逐字歌词数据
|
||||
yrc: [],
|
||||
},
|
||||
// 本地歌曲目录
|
||||
localSongPath: [],
|
||||
|
||||
@@ -48,6 +48,9 @@ const useSiteSettingsStore = defineStore("siteSettings", {
|
||||
showRoma: true, // 是否显示歌词音译
|
||||
// 下载部分
|
||||
downloadPath: null, // 默认下载路径
|
||||
downloadMeta: true, // 同时下载元信息
|
||||
downloadCover: true, // 同时下载封面
|
||||
downloadLyrics: true, // 同时下载歌词
|
||||
};
|
||||
},
|
||||
getters: {},
|
||||
|
||||
@@ -6,7 +6,7 @@ import { decode as base642Buffer } from "@/utils/base64";
|
||||
import { getSongPlayTime } from "@/utils/timeTools";
|
||||
import { getCoverGradient } from "@/utils/cover-color";
|
||||
import { isLogin } from "@/utils/auth";
|
||||
import parseLyric from "@/utils/parseLyric";
|
||||
import { parseLyric, parseLocalLrc } from "@/utils/parseLyric";
|
||||
|
||||
// 全局播放器
|
||||
let player;
|
||||
@@ -46,6 +46,7 @@ export const initPlayer = async (playNow = false) => {
|
||||
playSongData.id = playMode === "dj" ? playSongData.mainTrackId : playSongData.id;
|
||||
// 是否为本地歌曲
|
||||
const isLocalSong = playSongData?.path ? true : false;
|
||||
console.log("当前为本地歌曲");
|
||||
// 获取封面
|
||||
if (isLocalSong) {
|
||||
music.playSongData.localCover = await getLocalCoverData(playSongData?.path);
|
||||
@@ -597,17 +598,20 @@ const getSongLyricData = async (islocal, data) => {
|
||||
const music = musicData();
|
||||
const setDefaults = () => {
|
||||
music.playSongLyric = {
|
||||
hasLrcTran: false,
|
||||
hasLrcRoma: false,
|
||||
hasYrc: false,
|
||||
hasYrcTran: false,
|
||||
hasYrcRoma: false,
|
||||
lrc: [],
|
||||
yrc: [],
|
||||
hasTran: false,
|
||||
hasRoma: false,
|
||||
hasYrc: false,
|
||||
};
|
||||
};
|
||||
if (islocal) {
|
||||
const lyricData = await electron.ipcRenderer.invoke("getMusicLyric", data?.path);
|
||||
if (lyricData) {
|
||||
music.playSongLyric = parseLyric({ lrc: { lyric: lyricData } });
|
||||
const result = parseLocalLrc(lyricData);
|
||||
music.playSongLyric = result ? (music.playSongLyric = result) : setDefaults();
|
||||
} else {
|
||||
console.log("该歌曲暂无歌词");
|
||||
setDefaults();
|
||||
@@ -616,7 +620,8 @@ const getSongLyricData = async (islocal, data) => {
|
||||
const lyricResponse = await getSongLyric(data?.id);
|
||||
const lyricData = lyricResponse?.lrc;
|
||||
if (lyricData) {
|
||||
music.playSongLyric = parseLyric(lyricResponse);
|
||||
const result = parseLyric(lyricResponse);
|
||||
result ? (music.playSongLyric = result) : setDefaults();
|
||||
} else {
|
||||
console.log("该歌曲暂无歌词");
|
||||
setDefaults();
|
||||
@@ -812,6 +817,10 @@ export const playAllSongs = async (playlist, mode = "normal") => {
|
||||
// 播放
|
||||
fadePlayOrPause();
|
||||
}
|
||||
// 获取封面
|
||||
if (music.getPlaySongData?.path) {
|
||||
music.playSongData.localCover = await getLocalCoverData(music.getPlaySongData?.path);
|
||||
}
|
||||
$message.info("已开始播放", { showIcon: false });
|
||||
} catch (error) {
|
||||
console.error("播放全部歌曲出错:", error);
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
// BlobUrl
|
||||
let lastSongBlobUrl = null;
|
||||
let lastCoverBlobUrl = null;
|
||||
let lastDownloadBlobUrl = null;
|
||||
|
||||
/**
|
||||
* 判断当前运行环境
|
||||
*/
|
||||
@@ -59,27 +64,15 @@ export const getCacheData = async (key, time, request, params) => {
|
||||
*/
|
||||
export const getLocalCoverData = async (path, isAlbum = false) => {
|
||||
try {
|
||||
let blobUrl = null;
|
||||
// 清理过期的 Blob 链接
|
||||
if (lastCoverBlobUrl) URL.revokeObjectURL(lastCoverBlobUrl);
|
||||
const coverData = await electron.ipcRenderer.invoke("getMusicCover", path);
|
||||
if (coverData) {
|
||||
// 将 Uint8Array 数据转换为 Blob
|
||||
const blob = new Blob([coverData.coverData], { type: `image/${coverData.coverFormat}` });
|
||||
// 生成Blob URL
|
||||
blobUrl = URL.createObjectURL(blob);
|
||||
// 检查当前path是否与上次不一致
|
||||
const previousPath = sessionStorage.getItem("localCoverPath");
|
||||
if (previousPath && previousPath !== path) {
|
||||
// 清除上次的内容
|
||||
const previousBlobUrl = sessionStorage.getItem("localCoverBlobUrl");
|
||||
if (previousBlobUrl) {
|
||||
URL.revokeObjectURL(previousBlobUrl);
|
||||
sessionStorage.removeItem("localCoverBlobUrl");
|
||||
}
|
||||
}
|
||||
// 存储当前path和Blob URL
|
||||
sessionStorage.setItem("localCoverPath", path);
|
||||
sessionStorage.setItem("localCoverBlobUrl", blobUrl);
|
||||
return blobUrl;
|
||||
lastCoverBlobUrl = URL.createObjectURL(blob);
|
||||
return lastCoverBlobUrl;
|
||||
} else {
|
||||
// 如果没有封面数据
|
||||
return `/images/pic/${isAlbum ? "album" : "song"}.jpg?assest`;
|
||||
@@ -299,35 +292,40 @@ export const generateId = (fileName) => {
|
||||
* @param {String} songName - 歌曲名称
|
||||
* @returns {number} - 生成的数字ID
|
||||
*/
|
||||
export const downloadFile = async (data, song, path = null) => {
|
||||
export const downloadFile = async (data, song, lyric, options) => {
|
||||
try {
|
||||
const isElectron = checkPlatform.electron();
|
||||
const songType = data.type.toLowerCase();
|
||||
// 歌曲名称
|
||||
const songName =
|
||||
song.name +
|
||||
" - " +
|
||||
"-" +
|
||||
(Array.isArray(song.artists)
|
||||
? song.artists.map((ar) => ar.name).join(" / ")
|
||||
? song.artists.map((ar) => ar.name).join("&")
|
||||
: song.artists || "未知歌手");
|
||||
if (isElectron && path) {
|
||||
console.log("开始下载:", data, song, songName, songType, path);
|
||||
if (isElectron && options.path) {
|
||||
console.log("开始下载:", data, song, songName, songType, options.path);
|
||||
return await electron.ipcRenderer.invoke(
|
||||
"downloadFile",
|
||||
data,
|
||||
JSON.stringify(song),
|
||||
songName,
|
||||
songType,
|
||||
path,
|
||||
JSON.stringify({
|
||||
url: data?.url,
|
||||
data: song,
|
||||
lyric: lyric,
|
||||
name: songName,
|
||||
type: songType,
|
||||
}),
|
||||
JSON.stringify(options),
|
||||
);
|
||||
} else {
|
||||
const songRes = await fetch(data.url.replace(/^http:/, "https:"));
|
||||
// 清理过期的 Blob 链接
|
||||
if (lastDownloadBlobUrl) URL.revokeObjectURL(lastDownloadBlobUrl);
|
||||
const songRes = await fetch(data?.url.replace(/^http:/, "https:"));
|
||||
if (!songRes.ok) throw new Error("下载出错,请重试");
|
||||
const blob = await songRes.blob();
|
||||
const url = window.URL.createObjectURL(blob);
|
||||
lastDownloadBlobUrl = URL.createObjectURL(blob);
|
||||
// 下载数据
|
||||
const a = document.createElement("a");
|
||||
a.href = url;
|
||||
a.href = lastDownloadBlobUrl;
|
||||
a.download = `${songName}.${songType}`;
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
@@ -359,12 +357,10 @@ export const formatBytes = (bytes, decimals = 2) => {
|
||||
* 获取音频文件的 Blob 链接
|
||||
* @param {string} url - 音频文件的网络链接
|
||||
*/
|
||||
// 上次生成的 BlobUrl
|
||||
let lastBlobUrl = null;
|
||||
export const getBlobUrlFromUrl = async (url) => {
|
||||
try {
|
||||
// 清理过期的 Blob 链接
|
||||
if (lastBlobUrl) URL.revokeObjectURL(lastBlobUrl);
|
||||
if (lastSongBlobUrl) URL.revokeObjectURL(lastSongBlobUrl);
|
||||
// 是否为网络链接
|
||||
if (!url.startsWith("http://") && !url.startsWith("https://") && !url.startsWith("blob:")) {
|
||||
return url;
|
||||
@@ -377,8 +373,8 @@ export const getBlobUrlFromUrl = async (url) => {
|
||||
}
|
||||
const blob = await response.blob();
|
||||
// 转换为本地 Blob 链接
|
||||
lastBlobUrl = URL.createObjectURL(blob);
|
||||
return lastBlobUrl;
|
||||
lastSongBlobUrl = URL.createObjectURL(blob);
|
||||
return lastSongBlobUrl;
|
||||
} catch (error) {
|
||||
console.error("获取 Blob 链接遇到错误:" + error);
|
||||
throw error;
|
||||
|
||||
@@ -4,60 +4,113 @@
|
||||
* @param {string} data 接口数据
|
||||
* @returns {Array} 对应数据
|
||||
*/
|
||||
const parseLyric = (data) => {
|
||||
// 判断是否具有内容
|
||||
const checkLyric = (lyric) => (lyric ? (lyric.lyric ? true : false) : false);
|
||||
// 初始化数据
|
||||
const { lrc, tlyric, romalrc, yrc, ytlrc, yromalrc } = data;
|
||||
const lrcData = {
|
||||
lrc: lrc?.lyric || null,
|
||||
tlyric: tlyric?.lyric || null,
|
||||
romalrc: romalrc?.lyric || null,
|
||||
yrc: yrc?.lyric || null,
|
||||
ytlrc: ytlrc?.lyric || null,
|
||||
yromalrc: yromalrc?.lyric || null,
|
||||
};
|
||||
// 初始化输出结果
|
||||
const result = {
|
||||
// 是否具有普通翻译
|
||||
hasLrcTran: checkLyric(tlyric),
|
||||
// 是否具有普通音译
|
||||
hasLrcRoma: checkLyric(romalrc),
|
||||
// 是否具有逐字歌词
|
||||
hasYrc: checkLyric(yrc),
|
||||
// 是否具有逐字翻译
|
||||
hasYrcTran: checkLyric(ytlrc),
|
||||
// 是否具有逐字音译
|
||||
hasYrcRoma: checkLyric(yromalrc),
|
||||
// 普通歌词数组
|
||||
lrc: [],
|
||||
// 逐字歌词数据
|
||||
yrc: [],
|
||||
};
|
||||
// 普通歌词
|
||||
if (lrcData.lrc) {
|
||||
result.lrc = parseLrc(lrcData.lrc);
|
||||
//判断是否有其他翻译
|
||||
result.lrc = lrcData.tlyric
|
||||
? parseOtherLrc(result.lrc, parseLrc(lrcData.tlyric), "tran")
|
||||
: result.lrc;
|
||||
result.lrc = lrcData.romalrc
|
||||
? parseOtherLrc(result.lrc, parseLrc(lrcData.romalrc), "roma")
|
||||
: result.lrc;
|
||||
export const parseLyric = (data) => {
|
||||
try {
|
||||
// 判断是否具有内容
|
||||
const checkLyric = (lyric) => (lyric ? (lyric.lyric ? true : false) : false);
|
||||
// 初始化数据
|
||||
const { lrc, tlyric, romalrc, yrc, ytlrc, yromalrc } = data;
|
||||
const lrcData = {
|
||||
lrc: lrc?.lyric || null,
|
||||
tlyric: tlyric?.lyric || null,
|
||||
romalrc: romalrc?.lyric || null,
|
||||
yrc: yrc?.lyric || null,
|
||||
ytlrc: ytlrc?.lyric || null,
|
||||
yromalrc: yromalrc?.lyric || null,
|
||||
};
|
||||
// 初始化输出结果
|
||||
const result = {
|
||||
// 是否具有普通翻译
|
||||
hasLrcTran: checkLyric(tlyric),
|
||||
// 是否具有普通音译
|
||||
hasLrcRoma: checkLyric(romalrc),
|
||||
// 是否具有逐字歌词
|
||||
hasYrc: checkLyric(yrc),
|
||||
// 是否具有逐字翻译
|
||||
hasYrcTran: checkLyric(ytlrc),
|
||||
// 是否具有逐字音译
|
||||
hasYrcRoma: checkLyric(yromalrc),
|
||||
// 普通歌词数组
|
||||
lrc: [],
|
||||
// 逐字歌词数据
|
||||
yrc: [],
|
||||
};
|
||||
// 普通歌词
|
||||
if (lrcData.lrc) {
|
||||
result.lrc = parseLrc(lrcData.lrc);
|
||||
//判断是否有其他翻译
|
||||
result.lrc = lrcData.tlyric
|
||||
? parseOtherLrc(result.lrc, parseLrc(lrcData.tlyric), "tran")
|
||||
: result.lrc;
|
||||
result.lrc = lrcData.romalrc
|
||||
? parseOtherLrc(result.lrc, parseLrc(lrcData.romalrc), "roma")
|
||||
: result.lrc;
|
||||
}
|
||||
// 逐字歌词
|
||||
if (lrcData.yrc) {
|
||||
result.yrc = parseYrc(lrcData.yrc);
|
||||
//判断是否有其他翻译
|
||||
result.yrc = lrcData.ytlrc
|
||||
? parseOtherLrc(result.yrc, parseLrc(lrcData.ytlrc), "tran")
|
||||
: result.yrc;
|
||||
result.yrc = lrcData.yromalrc
|
||||
? parseOtherLrc(result.yrc, parseLrc(lrcData.yromalrc, false), "roma")
|
||||
: result.yrc;
|
||||
}
|
||||
console.log(result);
|
||||
return result;
|
||||
} catch (error) {
|
||||
console.error("解析歌词时出现错误:", error);
|
||||
return false;
|
||||
}
|
||||
// 逐字歌词
|
||||
if (lrcData.yrc) {
|
||||
result.yrc = parseYrc(lrcData.yrc);
|
||||
//判断是否有其他翻译
|
||||
result.yrc = lrcData.ytlrc
|
||||
? parseOtherLrc(result.yrc, parseLrc(lrcData.ytlrc), "tran")
|
||||
: result.yrc;
|
||||
result.yrc = lrcData.yromalrc
|
||||
? parseOtherLrc(result.yrc, parseLrc(lrcData.yromalrc, false), "roma")
|
||||
: result.yrc;
|
||||
};
|
||||
|
||||
/**
|
||||
* 解析本地歌词数据
|
||||
* @param {string} data - 歌词字符串
|
||||
* @returns {Object} - 包含解析后的歌词信息的对象
|
||||
*/
|
||||
export const parseLocalLrc = (data) => {
|
||||
try {
|
||||
const lyric = parseLrc(data);
|
||||
const parsedLyrics = [];
|
||||
// 初始化输出结果
|
||||
const result = {
|
||||
hasLrcTran: false,
|
||||
hasLrcRoma: false,
|
||||
hasYrc: false,
|
||||
hasYrcTran: false,
|
||||
hasYrcRoma: false,
|
||||
lrc: [],
|
||||
yrc: [],
|
||||
};
|
||||
// 遍历本地歌词数据
|
||||
for (let i = 0; i < lyric.length; i++) {
|
||||
// 当前歌词
|
||||
let currentObj = lyric[i];
|
||||
// 是否有相同时间
|
||||
let existingObj = parsedLyrics.find((v) => Number(v.time) === Number(currentObj.time));
|
||||
// 如果存在翻译
|
||||
if (existingObj) {
|
||||
result.hasLrcTran = true;
|
||||
existingObj.tran = currentObj.content;
|
||||
}
|
||||
// 若不存在翻译
|
||||
else {
|
||||
parsedLyrics.push({
|
||||
time: currentObj.time,
|
||||
content: currentObj.content,
|
||||
});
|
||||
}
|
||||
}
|
||||
// 改变输出结果
|
||||
result.lrc = parsedLyrics;
|
||||
console.log(result);
|
||||
return result;
|
||||
} catch (error) {
|
||||
console.error("解析本地歌词时出现错误:", error);
|
||||
return false;
|
||||
}
|
||||
console.log(result);
|
||||
return result;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -199,5 +252,3 @@ const parseYrc = (lyrics) => {
|
||||
return [];
|
||||
}
|
||||
};
|
||||
|
||||
export default parseLyric;
|
||||
|
||||
@@ -71,7 +71,7 @@
|
||||
v-model:value="searchValue"
|
||||
:input-props="{ autoComplete: false }"
|
||||
class="search"
|
||||
placeholder="搜索"
|
||||
placeholder="模糊搜索"
|
||||
clearable
|
||||
@input="localSearch"
|
||||
>
|
||||
|
||||
@@ -516,6 +516,21 @@
|
||||
</n-button>
|
||||
</n-flex>
|
||||
</n-card>
|
||||
<n-card class="set-item">
|
||||
<div class="name">
|
||||
同时下载歌曲元信息
|
||||
<n-text class="tip">为当前下载歌曲附加封面及歌词等元信息</n-text>
|
||||
</div>
|
||||
<n-switch v-model:value="downloadMeta" :round="false" />
|
||||
</n-card>
|
||||
<n-card class="set-item">
|
||||
<div class="name">下载歌曲时同时下载封面</div>
|
||||
<n-switch v-model:value="downloadCover" :disabled="!downloadMeta" :round="false" />
|
||||
</n-card>
|
||||
<n-card class="set-item">
|
||||
<div class="name">下载歌曲时同时下载歌词</div>
|
||||
<n-switch v-model:value="downloadLyrics" :disabled="!downloadMeta" :round="false" />
|
||||
</n-card>
|
||||
</div>
|
||||
<!-- 其他 -->
|
||||
<div class="set-type">
|
||||
@@ -611,6 +626,9 @@ const {
|
||||
showSpectrums,
|
||||
siderShowCover,
|
||||
useMusicCache,
|
||||
downloadMeta,
|
||||
downloadCover,
|
||||
downloadLyrics,
|
||||
} = storeToRefs(settings);
|
||||
|
||||
// 标签页数据
|
||||
|
||||
Reference in New Issue
Block a user