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