mirror of
https://github.com/imsyy/SPlayer.git
synced 2025-11-25 03:14:57 +08:00
🐞 fix: 修复本地歌词翻译显示异常 #121
This commit is contained in:
@@ -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,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",
|
||||
|
||||
@@ -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);
|
||||
},
|
||||
},
|
||||
|
||||
@@ -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: [],
|
||||
|
||||
@@ -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,7 @@
|
||||
// BlobUrl
|
||||
let lastSongBlobUrl = null;
|
||||
let lastCoverBlobUrl = null;
|
||||
|
||||
/**
|
||||
* 判断当前运行环境
|
||||
*/
|
||||
@@ -59,27 +63,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`;
|
||||
@@ -359,12 +351,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 +367,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"
|
||||
>
|
||||
|
||||
Reference in New Issue
Block a user