Skip to content

Commit ca51020

Browse files
committed
refactor: 将下载逻辑提取到useDownload hook中
1 parent 258828f commit ca51020

File tree

4 files changed

+262
-228
lines changed

4 files changed

+262
-228
lines changed

src/renderer/components/common/SongItem.vue

Lines changed: 3 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,7 @@ import { usePlayerStore } from '@/store';
144144
import type { SongResult } from '@/type/music';
145145
import { getImgUrl, isElectron } from '@/utils';
146146
import { getImageBackground } from '@/utils/linearColor';
147+
import { useDownload } from '@/hooks/useDownload';
147148
148149
const { t } = useI18n();
149150
@@ -191,8 +192,6 @@ const dropdownX = ref(0);
191192
const dropdownY = ref(0);
192193
const isHovering = ref(false);
193194
194-
const isDownloading = ref(false);
195-
196195
const openPlaylistDrawer = inject<(songId: number | string) => void>('openPlaylistDrawer');
197196
198197
const { navigateToArtist } = useArtist();
@@ -344,7 +343,7 @@ const handleMenuClick = (e: MouseEvent) => {
344343
const handleSelect = (key: string | number) => {
345344
showDropdown.value = false;
346345
if (key === 'download') {
347-
downloadMusic();
346+
downloadMusic(props.item);
348347
} else if (key === 'playNext') {
349348
handlePlayNext();
350349
} else if (key === 'addToPlaylist') {
@@ -359,74 +358,7 @@ const handleSelect = (key: string | number) => {
359358
};
360359
361360
// 下载音乐
362-
const downloadMusic = async () => {
363-
if (isDownloading.value) {
364-
message.warning(t('songItem.message.downloading'));
365-
return;
366-
}
367-
368-
try {
369-
isDownloading.value = true;
370-
371-
const data = (await getSongUrl(props.item.id as number, cloneDeep(props.item), true)) as any;
372-
if (!data || !data.url) {
373-
throw new Error(t('songItem.message.getUrlFailed'));
374-
}
375-
376-
// 构建文件名
377-
const artistNames = (props.item.ar || props.item.song?.artists)?.map((a) => a.name).join(',');
378-
const filename = `${props.item.name} - ${artistNames}`;
379-
console.log('props.item', props.item);
380-
381-
const songData = cloneDeep(props.item);
382-
songData.ar = songData.ar || songData.song?.artists;
383-
// 发送下载请求
384-
window.electron.ipcRenderer.send('download-music', {
385-
url: data.url,
386-
type: data.type,
387-
filename,
388-
songInfo: {
389-
...songData,
390-
downloadTime: Date.now()
391-
}
392-
});
393-
394-
message.success(t('songItem.message.downloadQueued'));
395-
396-
// 监听下载完成事件
397-
const handleDownloadComplete = (_, result) => {
398-
if (result.filename === filename) {
399-
isDownloading.value = false;
400-
removeListeners();
401-
}
402-
};
403-
404-
// 监听下载错误事件
405-
const handleDownloadError = (_, result) => {
406-
if (result.filename === filename) {
407-
isDownloading.value = false;
408-
removeListeners();
409-
}
410-
};
411-
412-
// 移除监听器函数
413-
const removeListeners = () => {
414-
window.electron.ipcRenderer.removeListener('music-download-complete', handleDownloadComplete);
415-
window.electron.ipcRenderer.removeListener('music-download-error', handleDownloadError);
416-
};
417-
418-
// 添加事件监听器
419-
window.electron.ipcRenderer.once('music-download-complete', handleDownloadComplete);
420-
window.electron.ipcRenderer.once('music-download-error', handleDownloadError);
421-
422-
// 30秒后自动清理监听器(以防下载过程中出现未知错误)
423-
setTimeout(removeListeners, 30000);
424-
} catch (error: any) {
425-
console.error('Download error:', error);
426-
isDownloading.value = false;
427-
message.error(error.message || t('songItem.message.downloadFailed'));
428-
}
429-
};
361+
const { isDownloading, downloadMusic } = useDownload();
430362
431363
const emits = defineEmits(['play', 'select', 'remove-song']);
432364
const songImageRef = useTemplateRef('songImg');

src/renderer/hooks/useDownload.ts

Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
import { cloneDeep } from 'lodash';
2+
import { ref } from 'vue';
3+
import { useI18n } from 'vue-i18n';
4+
import { useMessage } from 'naive-ui';
5+
6+
import { getSongUrl } from '@/store/modules/player';
7+
import type { SongResult } from '@/type/music';
8+
9+
export const useDownload = () => {
10+
const { t } = useI18n();
11+
const message = useMessage();
12+
const isDownloading = ref(false);
13+
14+
/**
15+
* 下载单首音乐
16+
* @param song 歌曲信息
17+
* @returns Promise<void>
18+
*/
19+
const downloadMusic = async (song: SongResult) => {
20+
if (isDownloading.value) {
21+
message.warning(t('songItem.message.downloading'));
22+
return;
23+
}
24+
25+
try {
26+
isDownloading.value = true;
27+
28+
const musicUrl = (await getSongUrl(song.id as number, cloneDeep(song), true)) as any;
29+
if (!musicUrl) {
30+
throw new Error(t('songItem.message.getUrlFailed'));
31+
}
32+
33+
// 构建文件名
34+
const artistNames = (song.ar || song.song?.artists)?.map((a) => a.name).join(',');
35+
const filename = `${song.name} - ${artistNames}`;
36+
37+
const songData = cloneDeep(song);
38+
songData.ar = songData.ar || songData.song?.artists;
39+
// 发送下载请求
40+
window.electron.ipcRenderer.send('download-music', {
41+
url: musicUrl,
42+
filename,
43+
songInfo: {
44+
...songData,
45+
downloadTime: Date.now()
46+
}
47+
});
48+
49+
message.success(t('songItem.message.downloadQueued'));
50+
51+
// 监听下载完成事件
52+
const handleDownloadComplete = (_, result) => {
53+
if (result.filename === filename) {
54+
isDownloading.value = false;
55+
removeListeners();
56+
}
57+
};
58+
59+
// 监听下载错误事件
60+
const handleDownloadError = (_, result) => {
61+
if (result.filename === filename) {
62+
isDownloading.value = false;
63+
removeListeners();
64+
}
65+
};
66+
67+
// 移除监听器函数
68+
const removeListeners = () => {
69+
window.electron.ipcRenderer.removeListener('music-download-complete', handleDownloadComplete);
70+
window.electron.ipcRenderer.removeListener('music-download-error', handleDownloadError);
71+
};
72+
73+
// 添加事件监听器
74+
window.electron.ipcRenderer.once('music-download-complete', handleDownloadComplete);
75+
window.electron.ipcRenderer.once('music-download-error', handleDownloadError);
76+
77+
// 30秒后自动清理监听器(以防下载过程中出现未知错误)
78+
setTimeout(removeListeners, 30000);
79+
} catch (error: any) {
80+
console.error('Download error:', error);
81+
isDownloading.value = false;
82+
message.error(error.message || t('songItem.message.downloadFailed'));
83+
}
84+
};
85+
86+
/**
87+
* 批量下载音乐
88+
* @param songs 歌曲列表
89+
* @returns Promise<void>
90+
*/
91+
const batchDownloadMusic = async (songs: SongResult[]) => {
92+
if (isDownloading.value) {
93+
message.warning(t('favorite.downloading'));
94+
return;
95+
}
96+
97+
if (songs.length === 0) {
98+
message.warning(t('favorite.selectSongsFirst'));
99+
return;
100+
}
101+
102+
try {
103+
isDownloading.value = true;
104+
message.success(t('favorite.downloading'));
105+
106+
// 移除旧的监听器
107+
window.electron.ipcRenderer.removeAllListeners('music-download-complete');
108+
109+
let successCount = 0;
110+
let failCount = 0;
111+
112+
// 添加新的监听器
113+
window.electron.ipcRenderer.on('music-download-complete', (_, result) => {
114+
if (result.success) {
115+
successCount++;
116+
} else {
117+
failCount++;
118+
}
119+
120+
// 当所有下载完成时
121+
if (successCount + failCount === songs.length) {
122+
isDownloading.value = false;
123+
message.success(t('favorite.downloadSuccess'));
124+
window.electron.ipcRenderer.removeAllListeners('music-download-complete');
125+
}
126+
});
127+
128+
// 并行获取所有歌曲的下载链接
129+
const downloadUrls = await Promise.all(
130+
songs.map(async (song) => {
131+
try {
132+
const data = (await getSongUrl(song.id, song, true)) as any;
133+
return { song, ...data };
134+
} catch (error) {
135+
console.error(`获取歌曲 ${song.name} 下载链接失败:`, error);
136+
return { song, url: null };
137+
}
138+
})
139+
);
140+
141+
// 开始下载有效的链接
142+
downloadUrls.forEach(({ song, url, type }) => {
143+
if (!url) {
144+
failCount++;
145+
return;
146+
}
147+
const songData = cloneDeep(song);
148+
const songInfo = {
149+
...songData,
150+
ar: songData.ar || songData.song?.artists,
151+
downloadTime: Date.now()
152+
};
153+
window.electron.ipcRenderer.send('download-music', {
154+
url,
155+
filename: `${song.name} - ${(song.ar || song.song?.artists)?.map((a) => a.name).join(',')}`,
156+
songInfo,
157+
type
158+
});
159+
});
160+
} catch (error) {
161+
console.error('下载失败:', error);
162+
isDownloading.value = false;
163+
message.destroyAll();
164+
message.error(t('favorite.downloadFailed'));
165+
}
166+
};
167+
168+
return {
169+
isDownloading,
170+
downloadMusic,
171+
batchDownloadMusic
172+
};
173+
};

0 commit comments

Comments
 (0)