Skip to content

Commit 09585ae

Browse files
authored
Merge pull request #837 from imsyy/dev-fix-02
修复桌面歌词杂项 颜色、封面修复、显示翻译罗马音
2 parents 51c2993 + 5ce90d1 commit 09585ae

14 files changed

Lines changed: 165 additions & 81 deletions

File tree

electron/main/ipc/ipc-taskbar.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,10 @@ const initTaskbarIpc = () => {
5454
taskbarLyricWindow.updateLayout(true);
5555
});
5656

57+
ipcMain.on("taskbar:set-theme-color", (_event, color: { light: string; dark: string } | null) => {
58+
taskbarLyricWindow.send("taskbar:update-theme-color", color);
59+
});
60+
5761
ipcMain.on("taskbar:broadcast-settings", (_event, settings: unknown) => {
5862
taskbarLyricWindow.send("taskbar:update-settings", settings);
5963
});

src/components/Card/SongCard.vue

Lines changed: 0 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@
2525
:key="song.cover"
2626
:src="song.path ? song.cover : song.coverSize?.s || song.cover"
2727
class="cover"
28-
@update:show="localCover"
2928
/>
3029
<!-- 信息 -->
3130
<n-flex size="small" class="info" vertical>
@@ -209,8 +208,6 @@ import { toLikeSong } from "@/utils/auth";
209208
import { isObject } from "lodash-es";
210209
import { formatTimestamp, msToTime } from "@/utils/time";
211210
import { usePlayerController } from "@/core/player/PlayerController";
212-
import { isElectron } from "@/utils/env";
213-
import { useBlobURLManager } from "@/core/resource/BlobURLManager";
214211
import { useMobile } from "@/composables/useMobile";
215212
import { EXPLICIT_CONTENT_MARK } from "@/utils/meta";
216213
@@ -237,7 +234,6 @@ const statusStore = useStatusStore();
237234
const settingStore = useSettingStore();
238235
239236
const player = usePlayerController();
240-
const blobURLManager = useBlobURLManager();
241237
242238
// 歌曲数据
243239
const song = toRef(props, "song");
@@ -256,36 +252,6 @@ const albumName = computed(() => {
256252
const name = isObject(album) ? album.name : album;
257253
return (settingStore.hideBracketedContent ? removeBrackets(name) : name) || "未知专辑";
258254
});
259-
260-
// 加载本地歌曲封面
261-
const localCover = async (show: boolean) => {
262-
if (!isElectron || !show) return;
263-
// 本地路径
264-
const path = song.value.path;
265-
if (!path || song.value.type === "streaming") return;
266-
// 当前封面
267-
const currentCover = song.value.cover;
268-
// 直接复用
269-
if (
270-
currentCover &&
271-
currentCover !== "/images/song.jpg?asset" &&
272-
!currentCover.startsWith("blob:")
273-
) {
274-
return;
275-
}
276-
// 缓存生效
277-
if (blobURLManager.hasBlobURL(path)) return;
278-
// 请求路径
279-
const requestPath = path;
280-
// 获取封面
281-
const coverData = await window.electron.ipcRenderer.invoke("get-music-cover", requestPath);
282-
if (song.value.path !== requestPath || !coverData) return;
283-
const { data, format } = coverData;
284-
const blobURL = blobURLManager.createBlobURL(data, format, requestPath);
285-
if (blobURL && song.value.path === requestPath) {
286-
song.value.cover = blobURL;
287-
}
288-
};
289255
</script>
290256

291257
<style lang="scss" scoped>

src/components/List/CommentList.vue

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ const props = defineProps<{
115115
// 透明
116116
transparent?: boolean;
117117
// 资源 ID
118-
resId: number;
118+
resId: number | string;
119119
hiddenCover?: boolean;
120120
}>();
121121
@@ -169,10 +169,11 @@ const handleDoubleClick = debounce(async (item: CommentType) => {
169169
openUserLogin();
170170
return;
171171
}
172+
// 本地歌曲不支持抱一抱
173+
if (typeof props.resId !== "number") return;
172174
try {
173175
const result = await hugComment(userStore.userData.userId, item.id, props.resId);
174176
if (result.code === 200) {
175-
// 获取抱一抱列表以得到总数
176177
// 获取抱一抱列表以得到总数
177178
try {
178179
const listResult = await getCommentHugList(

src/components/Player/PlayerComponents/PlayerComment.vue

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ const commentScroll = ref<InstanceType<typeof NScrollbar> | null>(null);
9999
const isShowComment = computed<boolean>(() => statusStore.showPlayerComment);
100100
101101
// 歌曲 id
102-
const songId = computed<number>(() => musicStore.playSong.id);
102+
const songId = computed<number | string>(() => musicStore.playSong.id);
103103
104104
// 歌曲类型
105105
const songType = computed<0 | 1 | 7 | 2 | 3 | 4 | 5 | 6>(() =>
@@ -147,7 +147,8 @@ const filteredCommentHotData = computed(() => filterComments(commentHotData.valu
147147
148148
// 获取热门评论
149149
const getHotCommentData = async () => {
150-
if (!songId.value) return;
150+
// 本地歌曲无法获取评论
151+
if (!songId.value || typeof songId.value !== "number") return;
151152
// 获取评论
152153
const result = await getHotComment(songId.value);
153154
// 处理数据
@@ -159,7 +160,8 @@ const getHotCommentData = async () => {
159160
160161
// 获取歌曲评论
161162
const getAllComment = async () => {
162-
if (!songId.value) return;
163+
// 本地歌曲无法获取评论
164+
if (!songId.value || typeof songId.value !== "number") return;
163165
commentLoading.value = true;
164166
// 分页参数
165167
const cursor =

src/components/Player/PlayerMeta/PlayerCover.vue

Lines changed: 65 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
:style="{ '--gradient-percent': settingStore.playerFullscreenGradient + '%' }"
77
>
88
<s-image
9-
:src="musicStore.getSongCover('xl')"
9+
:src="getCoverUrl('xl')"
1010
:alt="musicStore.playSong.name"
1111
:title="musicStore.playSong.name"
1212
:lazy="false"
@@ -28,8 +28,8 @@
2828
/>
2929
<!-- 专辑图片 -->
3030
<s-image
31-
:key="musicStore.getSongCover()"
32-
:src="musicStore.getSongCover('l')"
31+
:key="getCoverUrl('l')"
32+
:src="getCoverUrl('l')"
3333
:observe-visibility="false"
3434
object-fit="cover"
3535
class="cover-img"
@@ -55,6 +55,7 @@ import { songDynamicCover } from "@/api/song";
5555
import { useMobile } from "@/composables/useMobile";
5656
import { useSettingStore, useStatusStore, useMusicStore } from "@/stores";
5757
import { isLogin } from "@/utils/auth";
58+
import { isElectron } from "@/utils/env";
5859
import { isEmpty } from "lodash-es";
5960
6061
const musicStore = useMusicStore();
@@ -63,13 +64,21 @@ const settingStore = useSettingStore();
6364
6465
const { isTablet } = useMobile();
6566
67+
// 本地歌曲高清封面(Data URL)
68+
const localCoverDataUrl = ref<string>("");
69+
6670
// 动态封面
6771
const dynamicCover = ref<string>("");
6872
const dynamicCoverLoaded = ref<boolean>(false);
6973
7074
// 视频元素
7175
const videoRef = ref<HTMLVideoElement | null>(null);
7276
77+
// 清理本地封面资源
78+
const cleanupLocalCover = () => {
79+
localCoverDataUrl.value = "";
80+
};
81+
7382
// 清理动态封面资源
7483
const cleanupDynamicCover = () => {
7584
if (videoRef.value) {
@@ -91,6 +100,38 @@ const { start: dynamicCoverStart, stop: dynamicCoverStop } = useTimeoutFn(
91100
{ immediate: false },
92101
);
93102
103+
// 获取本地歌曲高清封面
104+
const getLocalCover = async () => {
105+
// 非本地歌曲或非 Electron 环境,清理并返回
106+
if (!isElectron || !musicStore.playSong.path || musicStore.playSong.type === "streaming") {
107+
cleanupLocalCover();
108+
return;
109+
}
110+
try {
111+
const coverData = await window.electron.ipcRenderer.invoke(
112+
"get-music-cover",
113+
musicStore.playSong.path,
114+
);
115+
if (coverData) {
116+
// 使用 Data URL,确保跨窗口可用
117+
const blob = new Blob([coverData.data], { type: coverData.format });
118+
const reader = new FileReader();
119+
const dataUrl = await new Promise<string>((resolve, reject) => {
120+
reader.onload = () => resolve(reader.result as string);
121+
reader.onerror = reject;
122+
reader.onabort = reject;
123+
reader.readAsDataURL(blob);
124+
});
125+
localCoverDataUrl.value = dataUrl;
126+
} else {
127+
localCoverDataUrl.value = "";
128+
}
129+
} catch (error) {
130+
console.error("获取本地封面失败:", error);
131+
localCoverDataUrl.value = "";
132+
}
133+
};
134+
94135
// 获取动态封面
95136
const getDynamicCover = async () => {
96137
if (
@@ -117,18 +158,38 @@ const dynamicCoverEnded = () => {
117158
dynamicCoverStart();
118159
};
119160
161+
// 获取封面 URL
162+
const getCoverUrl = (size: "s" | "m" | "l" | "xl" = "l") => {
163+
if (localCoverDataUrl.value) {
164+
return localCoverDataUrl.value;
165+
}
166+
return musicStore.getSongCover(size);
167+
};
168+
120169
watch(
121170
() => [musicStore.playSong.id, settingStore.dynamicCover, settingStore.playerType],
122171
() => getDynamicCover(),
123172
);
124173
125-
onMounted(getDynamicCover);
174+
// 监听歌曲切换,获取/清理本地封面
175+
watch(
176+
() => musicStore.playSong.path,
177+
() => getLocalCover(),
178+
{ immediate: true },
179+
);
180+
181+
onMounted(() => {
182+
getDynamicCover();
183+
getLocalCover();
184+
});
126185
127186
onBeforeUnmount(() => {
128187
// 停止定时器
129188
dynamicCoverStop();
130189
// 清理动态封面资源
131190
cleanupDynamicCover();
191+
// 清理本地封面资源
192+
cleanupLocalCover();
132193
});
133194
</script>
134195

src/components/Setting/config/lyric.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -792,6 +792,13 @@ export const useLyricSettings = (): SettingConfig => {
792792
description: "是否在暂停播放时显示任务栏歌词",
793793
value: toRef(settingStore, "taskbarLyricShowWhenPaused"),
794794
},
795+
{
796+
key: "taskbarLyricUseThemeColor",
797+
label: "跟随主题色",
798+
type: "switch",
799+
description: "开启后任务栏歌词颜色将跟随应用主题,下一曲生效",
800+
value: toRef(settingStore, "taskbarLyricUseThemeColor"),
801+
},
795802
{
796803
key: "taskbarLyricShowCover",
797804
label: "显示封面",

src/composables/useInit.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,8 @@ export const useInit = () => {
116116
settingStore.LyricFont,
117117
settingStore.globalFont,
118118
settingStore.taskbarLyricFontWeight,
119+
settingStore.showTran,
120+
settingStore.showRoma,
119121
],
120122
() => {
121123
window.electron.ipcRenderer.send("taskbar:broadcast-settings", {
@@ -124,6 +126,8 @@ export const useInit = () => {
124126
lyricFont: settingStore.LyricFont,
125127
globalFont: settingStore.globalFont,
126128
fontWeight: settingStore.taskbarLyricFontWeight,
129+
showTran: settingStore.showTran,
130+
showRoma: settingStore.showRoma,
127131
});
128132
},
129133
{ deep: true },

src/core/player/PlayerController.ts

Lines changed: 1 addition & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import { AudioErrorCode } from "@/core/audio-player/BaseAudioPlayer";
2-
import { useBlobURLManager } from "@/core/resource/BlobURLManager";
32
import { useDataStore, useMusicStore, useSettingStore, useStatusStore } from "@/stores";
43
import type { SongType } from "@/types/main";
54
import type { RepeatModeType, ShuffleModeType } from "@/types/shared";
@@ -188,7 +187,7 @@ class PlayerController {
188187
}
189188
// 更新任务栏歌词窗口的元数据
190189
const { name, artist } = getPlayerInfoObj() || {};
191-
const coverUrl = playSongData.cover || "";
190+
const coverUrl = playSongData.coverSize?.s || playSongData.cover || "";
192191
playerIpc.sendTaskbarMetadata({
193192
title: name || "",
194193
artist: artist || "",
@@ -401,23 +400,6 @@ class PlayerController {
401400
const musicStore = useMusicStore();
402401
if (musicStore.playSong.type === "streaming") return;
403402
const statusStore = useStatusStore();
404-
const blobURLManager = useBlobURLManager();
405-
406-
// Blob URL 清理
407-
const oldCover = musicStore.playSong.cover;
408-
if (oldCover && oldCover.startsWith("blob:")) {
409-
blobURLManager.revokeBlobURL(musicStore.playSong.path || "");
410-
}
411-
412-
// 获取封面数据
413-
const coverData = await window.electron.ipcRenderer.invoke("get-music-cover", path);
414-
if (coverData) {
415-
const blobURL = blobURLManager.createBlobURL(coverData.data, coverData.format, path);
416-
if (blobURL) musicStore.playSong.cover = blobURL;
417-
} else {
418-
musicStore.playSong.cover = "/images/song.jpg?asset";
419-
}
420-
421403
// 获取元数据
422404
const infoData = await window.electron.ipcRenderer.invoke("get-music-metadata", path);
423405
statusStore.songQuality = handleSongQuality(infoData.format?.bitrate ?? 0, "local");

src/core/player/PlayerIpc.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,15 @@ export const sendTaskbarLyrics = (lyrics: SongLyric) => {
133133
window.electron.ipcRenderer.send("taskbar:update-lyrics", payload);
134134
};
135135

136+
/**
137+
* 发送任务栏歌词主题色
138+
* @param color 主题色
139+
*/
140+
export const sendTaskbarThemeColor = (color: { light: string; dark: string } | null) => {
141+
if (!isElectron) return;
142+
window.electron.ipcRenderer.send("taskbar:set-theme-color", color);
143+
};
144+
136145
export interface TaskbarProgressPayload {
137146
currentTime: number;
138147
duration: number;

src/stores/setting.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,8 @@ export interface SettingState {
5858
taskbarLyricAnimationMode: "slide-blur" | "left-sm";
5959
/** 任务栏歌词单行模式 */
6060
taskbarLyricSingleLineMode: boolean;
61+
/** 任务栏歌词跟随主题色 */
62+
taskbarLyricUseThemeColor: boolean;
6163
/** 任务栏歌词字重 */
6264
taskbarLyricFontWeight: number;
6365
/** 是否使用在线服务 */
@@ -505,6 +507,7 @@ export const useSettingStore = defineStore("setting", {
505507
taskbarLyricShowWhenPaused: true,
506508
taskbarLyricAnimationMode: "slide-blur",
507509
taskbarLyricSingleLineMode: false,
510+
taskbarLyricUseThemeColor: false,
508511
taskbarLyricFontWeight: 400,
509512
checkUpdateOnStart: true,
510513
updateChannel: "stable",

0 commit comments

Comments
 (0)