Skip to content

Commit 27d5bd8

Browse files
committed
🐞 fix: 修复下载管理 切换tab程序卡死问题
1 parent c5da42b commit 27d5bd8

File tree

4 files changed

+104
-86
lines changed

4 files changed

+104
-86
lines changed

src/i18n/lang/en-US/download.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,5 +44,6 @@ export default {
4444
message: {
4545
downloadComplete: '{filename} download completed',
4646
downloadFailed: '{filename} download failed: {error}'
47-
}
47+
},
48+
loading: 'Loading...'
4849
};

src/i18n/lang/zh-CN/download.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,5 +43,6 @@ export default {
4343
message: {
4444
downloadComplete: '{filename} 下载完成',
4545
downloadFailed: '{filename} 下载失败: {error}'
46-
}
46+
},
47+
loading: '加载中...'
4748
};

src/main/modules/fileManager.ts

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -122,20 +122,37 @@ export function initializeFileManager() {
122122
});
123123

124124
// 获取已下载音乐列表
125-
ipcMain.handle('get-downloaded-music', () => {
125+
ipcMain.handle('get-downloaded-music', async () => {
126126
try {
127127
const store = new Store();
128128
const songInfos = store.get('downloadedSongs', {}) as Record<string, any>;
129129

130-
// 过滤出实际存在的文件
131-
const validSongs = Object.entries(songInfos)
132-
.filter(([path]) => fs.existsSync(path))
133-
.map(([_, info]) => info)
130+
// 异步处理文件存在性检查
131+
const entriesArray = Object.entries(songInfos);
132+
const validEntriesPromises = await Promise.all(
133+
entriesArray.map(async ([path, info]) => {
134+
try {
135+
const exists = await fs.promises.access(path)
136+
.then(() => true)
137+
.catch(() => false);
138+
return exists ? info : null;
139+
} catch (error) {
140+
console.error('Error checking file existence:', error);
141+
return null;
142+
}
143+
})
144+
);
145+
146+
// 过滤有效的歌曲并排序
147+
const validSongs = validEntriesPromises
148+
.filter(song => song !== null)
134149
.sort((a, b) => (b.downloadTime || 0) - (a.downloadTime || 0));
135150

136151
// 更新存储,移除不存在的文件记录
137152
const newSongInfos = validSongs.reduce((acc, song) => {
138-
acc[song.path] = song;
153+
if (song && song.path) {
154+
acc[song.path] = song;
155+
}
139156
return acc;
140157
}, {});
141158
store.set('downloadedSongs', newSongInfos);
@@ -175,6 +192,13 @@ export function initializeFileManager() {
175192
downloadStore.set('history', []);
176193
});
177194

195+
// 添加清除已下载音乐记录的处理函数
196+
ipcMain.handle('clear-downloaded-music', () => {
197+
const store = new Store();
198+
store.set('downloadedSongs', {});
199+
return true;
200+
});
201+
178202
// 添加清除音频缓存的处理函数
179203
ipcMain.on('clear-audio-cache', () => {
180204
audioCacheStore.set('cache', {});

src/renderer/components/common/DownloadDrawer.vue

Lines changed: 70 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,11 @@
9090
<!-- 已下载列表 -->
9191
<n-tab-pane name="downloaded" :tab="t('download.tabs.downloaded')" class="h-full">
9292
<div class="downloaded-list">
93-
<div v-if="downloadedList.length === 0" class="empty-tip">
93+
<div v-if="isLoadingDownloaded" class="loading-tip">
94+
<n-spin size="medium" />
95+
<span class="loading-text">{{ t('download.loading') }}</span>
96+
</div>
97+
<div v-else-if="downloadedList.length === 0" class="empty-tip">
9498
<n-empty :description="t('download.empty.noDownloaded')" />
9599
</div>
96100
<div v-else class="downloaded-content">
@@ -262,9 +266,7 @@ const downloadedList = ref<DownloadedItem[]>(
262266
JSON.parse(localStorage.getItem('downloadedList') || '[]')
263267
);
264268
265-
const downList = computed(() => {
266-
return (downloadedList.value as DownloadedItem[]).reverse();
267-
});
269+
const downList = computed(() => downloadedList.value);
268270
269271
// 计算下载中的任务数量
270272
const downloadingCount = computed(() => {
@@ -350,38 +352,25 @@ const handleDelete = (item: DownloadedItem) => {
350352
351353
// 确认删除
352354
const confirmDelete = async () => {
353-
if (!itemToDelete.value) return;
355+
const item = itemToDelete.value;
356+
if (!item) return;
354357
355358
try {
356359
const success = await window.electron.ipcRenderer.invoke(
357360
'delete-downloaded-music',
358-
itemToDelete.value.path
359-
);
360-
361-
// 无论删除文件是否成功,都从记录中移除
362-
localStorage.setItem(
363-
'downloadedList',
364-
JSON.stringify(
365-
downloadedList.value.filter((item) => item.id !== (itemToDelete.value as DownloadedItem).id)
366-
)
361+
item.path
367362
);
368-
await refreshDownloadedList();
369363
370364
if (success) {
365+
const newList = downloadedList.value.filter(i => i.id !== item.id);
366+
downloadedList.value = newList;
367+
localStorage.setItem('downloadedList', JSON.stringify(newList));
371368
message.success(t('download.delete.success'));
372369
} else {
373370
message.warning(t('download.delete.fileNotFound'));
374371
}
375372
} catch (error) {
376373
console.error('Failed to delete music:', error);
377-
// 即使删除文件出错,也从记录中移除
378-
localStorage.setItem(
379-
'downloadedList',
380-
JSON.stringify(
381-
downloadedList.value.filter((item) => item.id !== (itemToDelete.value as DownloadedItem).id)
382-
)
383-
);
384-
await refreshDownloadedList();
385374
message.warning(t('download.delete.recordRemoved'));
386375
} finally {
387376
showDeleteConfirm.value = false;
@@ -393,11 +382,18 @@ const confirmDelete = async () => {
393382
const showClearConfirm = ref(false);
394383
395384
// 清空下载记录
396-
const clearDownloadRecords = () => {
397-
localStorage.setItem('downloadedList', '[]');
398-
downloadedList.value = [];
399-
message.success(t('download.clear.success'));
400-
showClearConfirm.value = false;
385+
const clearDownloadRecords = async () => {
386+
try {
387+
downloadedList.value = [];
388+
localStorage.setItem('downloadedList', '[]');
389+
await window.electron.ipcRenderer.invoke('clear-downloaded-music');
390+
message.success(t('download.clear.success'));
391+
} catch (error) {
392+
console.error('Failed to clear download records:', error);
393+
message.error(t('download.clear.failed'));
394+
} finally {
395+
showClearConfirm.value = false;
396+
}
401397
};
402398
403399
// 播放音乐
@@ -407,65 +403,64 @@ const clearDownloadRecords = () => {
407403
// playerStore.setIsPlay(true);
408404
// };
409405
406+
// 添加加载状态
407+
const isLoadingDownloaded = ref(false);
408+
410409
// 获取已下载音乐列表
411410
const refreshDownloadedList = async () => {
411+
if (isLoadingDownloaded.value) return; // 防止重复加载
412+
412413
try {
413-
let saveList: any = [];
414+
isLoadingDownloaded.value = true;
414415
const list = await window.electron.ipcRenderer.invoke('get-downloaded-music');
416+
415417
if (!Array.isArray(list) || list.length === 0) {
416-
saveList = [];
418+
downloadedList.value = [];
419+
localStorage.setItem('downloadedList', '[]');
417420
return;
418421
}
419422
420-
const songIds = list.filter((item) => item.id).map((item) => item.id);
421-
422-
// 如果有歌曲ID,获取详细信息
423-
if (songIds.length > 0) {
424-
try {
425-
const detailRes = await getMusicDetail(songIds);
426-
const songDetails = detailRes.data.songs.reduce((acc, song) => {
427-
acc[song.id] = song;
428-
return acc;
429-
}, {});
430-
431-
saveList = list.map((item) => {
432-
const songDetail = songDetails[item.id];
433-
return {
434-
...item,
435-
picUrl: songDetail?.al?.picUrl || item.picUrl || '/images/default_cover.png',
436-
ar: songDetail?.ar || item.ar || [{ name: t('download.localMusic') }]
437-
};
438-
});
439-
} catch (detailError) {
440-
console.error('Failed to get music details:', detailError);
441-
saveList = list;
442-
}
443-
} else {
444-
saveList = list;
423+
const songIds = list.filter(item => item.id).map(item => item.id);
424+
if (songIds.length === 0) {
425+
downloadedList.value = list;
426+
localStorage.setItem('downloadedList', JSON.stringify(list));
427+
return;
428+
}
429+
430+
try {
431+
const detailRes = await getMusicDetail(songIds);
432+
const songDetails = detailRes.data.songs.reduce((acc, song) => {
433+
acc[song.id] = song;
434+
return acc;
435+
}, {});
436+
437+
const updatedList = list.map(item => ({
438+
...item,
439+
picUrl: songDetails[item.id]?.al?.picUrl || item.picUrl || '/images/default_cover.png',
440+
ar: songDetails[item.id]?.ar || item.ar || [{ name: t('download.localMusic') }]
441+
}));
442+
443+
downloadedList.value = updatedList;
444+
localStorage.setItem('downloadedList', JSON.stringify(updatedList));
445+
} catch (error) {
446+
console.error('Failed to get music details:', error);
447+
downloadedList.value = list;
448+
localStorage.setItem('downloadedList', JSON.stringify(list));
445449
}
446-
setLocalDownloadedList(saveList);
447450
} catch (error) {
448451
console.error('Failed to get downloaded music list:', error);
449452
downloadedList.value = [];
453+
localStorage.setItem('downloadedList', '[]');
454+
} finally {
455+
isLoadingDownloaded.value = false;
450456
}
451457
};
452458
453-
const setLocalDownloadedList = (list: DownloadedItem[]) => {
454-
const localList = localStorage.getItem('downloadedList');
455-
// 合并 去重
456-
const saveList = [...(localList ? JSON.parse(localList) : []), ...list];
457-
const uniqueList = saveList.filter(
458-
(item, index, self) => index === self.findIndex((t) => t.id === item.id)
459-
);
460-
localStorage.setItem('downloadedList', JSON.stringify(uniqueList));
461-
downloadedList.value = uniqueList;
462-
};
463-
464459
// 监听抽屉显示状态
465460
watch(
466461
() => showDrawer.value,
467462
(newVal) => {
468-
if (newVal) {
463+
if (newVal && !isLoadingDownloaded.value) {
469464
refreshDownloadedList();
470465
}
471466
}
@@ -503,28 +498,25 @@ onMounted(() => {
503498
});
504499
505500
// 监听下载完成
506-
window.electron.ipcRenderer.on('music-download-complete', (_, data) => {
501+
window.electron.ipcRenderer.on('music-download-complete', async (_, data) => {
507502
if (data.success) {
508-
// 从下载列表中移除
509-
downloadList.value = downloadList.value.filter((item) => item.filename !== data.filename);
510-
// 刷新已下载列表
511-
refreshDownloadedList();
503+
downloadList.value = downloadList.value.filter(item => item.filename !== data.filename);
504+
// 延迟刷新已下载列表,避免文件系统未完全写入
505+
setTimeout(() => refreshDownloadedList(), 500);
512506
message.success(t('download.message.downloadComplete', { filename: data.filename }));
513507
} else {
514-
const existingItem = downloadList.value.find((item) => item.filename === data.filename);
508+
const existingItem = downloadList.value.find(item => item.filename === data.filename);
515509
if (existingItem) {
516510
Object.assign(existingItem, {
517511
status: 'error',
518512
error: data.error,
519513
progress: 0
520514
});
521515
setTimeout(() => {
522-
downloadList.value = downloadList.value.filter((item) => item.filename !== data.filename);
516+
downloadList.value = downloadList.value.filter(item => item.filename !== data.filename);
523517
}, 3000);
524518
}
525-
message.error(
526-
t('download.message.downloadFailed', { filename: data.filename, error: data.error })
527-
);
519+
message.error(t('download.message.downloadFailed', { filename: data.filename, error: data.error }));
528520
}
529521
});
530522

0 commit comments

Comments
 (0)