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"
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";
5555import { useMobile } from " @/composables/useMobile" ;
5656import { useSettingStore , useStatusStore , useMusicStore } from " @/stores" ;
5757import { isLogin } from " @/utils/auth" ;
58+ import { isElectron } from " @/utils/env" ;
5859import { isEmpty } from " lodash-es" ;
5960
6061const musicStore = useMusicStore ();
@@ -63,13 +64,21 @@ const settingStore = useSettingStore();
6364
6465const { isTablet } = useMobile ();
6566
67+ // 本地歌曲高清封面(Data URL)
68+ const localCoverDataUrl = ref <string >(" " );
69+
6670// 动态封面
6771const dynamicCover = ref <string >(" " );
6872const dynamicCoverLoaded = ref <boolean >(false );
6973
7074// 视频元素
7175const videoRef = ref <HTMLVideoElement | null >(null );
7276
77+ // 清理本地封面资源
78+ const cleanupLocalCover = () => {
79+ localCoverDataUrl .value = " " ;
80+ };
81+
7382// 清理动态封面资源
7483const 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// 获取动态封面
95136const 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+
120169watch (
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
127186onBeforeUnmount (() => {
128187 // 停止定时器
129188 dynamicCoverStop ();
130189 // 清理动态封面资源
131190 cleanupDynamicCover ();
191+ // 清理本地封面资源
192+ cleanupLocalCover ();
132193});
133194 </script >
134195
0 commit comments