🐞 fix: 修复下载管理 切换tab程序卡死问题

This commit is contained in:
algerkong
2025-04-21 20:38:05 +08:00
parent c5da42b67d
commit 27d5bd8f81
4 changed files with 104 additions and 86 deletions
+2 -1
View File
@@ -44,5 +44,6 @@ export default {
message: { message: {
downloadComplete: '{filename} download completed', downloadComplete: '{filename} download completed',
downloadFailed: '{filename} download failed: {error}' downloadFailed: '{filename} download failed: {error}'
} },
loading: 'Loading...'
}; };
+2 -1
View File
@@ -43,5 +43,6 @@ export default {
message: { message: {
downloadComplete: '{filename} 下载完成', downloadComplete: '{filename} 下载完成',
downloadFailed: '{filename} 下载失败: {error}' downloadFailed: '{filename} 下载失败: {error}'
} },
loading: '加载中...'
}; };
+30 -6
View File
@@ -122,20 +122,37 @@ export function initializeFileManager() {
}); });
// 获取已下载音乐列表 // 获取已下载音乐列表
ipcMain.handle('get-downloaded-music', () => { ipcMain.handle('get-downloaded-music', async () => {
try { try {
const store = new Store(); const store = new Store();
const songInfos = store.get('downloadedSongs', {}) as Record<string, any>; const songInfos = store.get('downloadedSongs', {}) as Record<string, any>;
// 过滤出实际存在的文件 // 异步处理文件存在性检查
const validSongs = Object.entries(songInfos) const entriesArray = Object.entries(songInfos);
.filter(([path]) => fs.existsSync(path)) const validEntriesPromises = await Promise.all(
.map(([_, info]) => info) entriesArray.map(async ([path, info]) => {
try {
const exists = await fs.promises.access(path)
.then(() => true)
.catch(() => false);
return exists ? info : null;
} catch (error) {
console.error('Error checking file existence:', error);
return null;
}
})
);
// 过滤有效的歌曲并排序
const validSongs = validEntriesPromises
.filter(song => song !== null)
.sort((a, b) => (b.downloadTime || 0) - (a.downloadTime || 0)); .sort((a, b) => (b.downloadTime || 0) - (a.downloadTime || 0));
// 更新存储,移除不存在的文件记录 // 更新存储,移除不存在的文件记录
const newSongInfos = validSongs.reduce((acc, song) => { const newSongInfos = validSongs.reduce((acc, song) => {
acc[song.path] = song; if (song && song.path) {
acc[song.path] = song;
}
return acc; return acc;
}, {}); }, {});
store.set('downloadedSongs', newSongInfos); store.set('downloadedSongs', newSongInfos);
@@ -175,6 +192,13 @@ export function initializeFileManager() {
downloadStore.set('history', []); downloadStore.set('history', []);
}); });
// 添加清除已下载音乐记录的处理函数
ipcMain.handle('clear-downloaded-music', () => {
const store = new Store();
store.set('downloadedSongs', {});
return true;
});
// 添加清除音频缓存的处理函数 // 添加清除音频缓存的处理函数
ipcMain.on('clear-audio-cache', () => { ipcMain.on('clear-audio-cache', () => {
audioCacheStore.set('cache', {}); audioCacheStore.set('cache', {});
@@ -90,7 +90,11 @@
<!-- 已下载列表 --> <!-- 已下载列表 -->
<n-tab-pane name="downloaded" :tab="t('download.tabs.downloaded')" class="h-full"> <n-tab-pane name="downloaded" :tab="t('download.tabs.downloaded')" class="h-full">
<div class="downloaded-list"> <div class="downloaded-list">
<div v-if="downloadedList.length === 0" class="empty-tip"> <div v-if="isLoadingDownloaded" class="loading-tip">
<n-spin size="medium" />
<span class="loading-text">{{ t('download.loading') }}</span>
</div>
<div v-else-if="downloadedList.length === 0" class="empty-tip">
<n-empty :description="t('download.empty.noDownloaded')" /> <n-empty :description="t('download.empty.noDownloaded')" />
</div> </div>
<div v-else class="downloaded-content"> <div v-else class="downloaded-content">
@@ -262,9 +266,7 @@ const downloadedList = ref<DownloadedItem[]>(
JSON.parse(localStorage.getItem('downloadedList') || '[]') JSON.parse(localStorage.getItem('downloadedList') || '[]')
); );
const downList = computed(() => { const downList = computed(() => downloadedList.value);
return (downloadedList.value as DownloadedItem[]).reverse();
});
// 计算下载中的任务数量 // 计算下载中的任务数量
const downloadingCount = computed(() => { const downloadingCount = computed(() => {
@@ -350,38 +352,25 @@ const handleDelete = (item: DownloadedItem) => {
// 确认删除 // 确认删除
const confirmDelete = async () => { const confirmDelete = async () => {
if (!itemToDelete.value) return; const item = itemToDelete.value;
if (!item) return;
try { try {
const success = await window.electron.ipcRenderer.invoke( const success = await window.electron.ipcRenderer.invoke(
'delete-downloaded-music', 'delete-downloaded-music',
itemToDelete.value.path item.path
); );
// 无论删除文件是否成功,都从记录中移除
localStorage.setItem(
'downloadedList',
JSON.stringify(
downloadedList.value.filter((item) => item.id !== (itemToDelete.value as DownloadedItem).id)
)
);
await refreshDownloadedList();
if (success) { if (success) {
const newList = downloadedList.value.filter(i => i.id !== item.id);
downloadedList.value = newList;
localStorage.setItem('downloadedList', JSON.stringify(newList));
message.success(t('download.delete.success')); message.success(t('download.delete.success'));
} else { } else {
message.warning(t('download.delete.fileNotFound')); message.warning(t('download.delete.fileNotFound'));
} }
} catch (error) { } catch (error) {
console.error('Failed to delete music:', error); console.error('Failed to delete music:', error);
// 即使删除文件出错,也从记录中移除
localStorage.setItem(
'downloadedList',
JSON.stringify(
downloadedList.value.filter((item) => item.id !== (itemToDelete.value as DownloadedItem).id)
)
);
await refreshDownloadedList();
message.warning(t('download.delete.recordRemoved')); message.warning(t('download.delete.recordRemoved'));
} finally { } finally {
showDeleteConfirm.value = false; showDeleteConfirm.value = false;
@@ -393,11 +382,18 @@ const confirmDelete = async () => {
const showClearConfirm = ref(false); const showClearConfirm = ref(false);
// 清空下载记录 // 清空下载记录
const clearDownloadRecords = () => { const clearDownloadRecords = async () => {
localStorage.setItem('downloadedList', '[]'); try {
downloadedList.value = []; downloadedList.value = [];
message.success(t('download.clear.success')); localStorage.setItem('downloadedList', '[]');
showClearConfirm.value = false; await window.electron.ipcRenderer.invoke('clear-downloaded-music');
message.success(t('download.clear.success'));
} catch (error) {
console.error('Failed to clear download records:', error);
message.error(t('download.clear.failed'));
} finally {
showClearConfirm.value = false;
}
}; };
// 播放音乐 // 播放音乐
@@ -407,65 +403,64 @@ const clearDownloadRecords = () => {
// playerStore.setIsPlay(true); // playerStore.setIsPlay(true);
// }; // };
// 添加加载状态
const isLoadingDownloaded = ref(false);
// 获取已下载音乐列表 // 获取已下载音乐列表
const refreshDownloadedList = async () => { const refreshDownloadedList = async () => {
if (isLoadingDownloaded.value) return; // 防止重复加载
try { try {
let saveList: any = []; isLoadingDownloaded.value = true;
const list = await window.electron.ipcRenderer.invoke('get-downloaded-music'); const list = await window.electron.ipcRenderer.invoke('get-downloaded-music');
if (!Array.isArray(list) || list.length === 0) { if (!Array.isArray(list) || list.length === 0) {
saveList = []; downloadedList.value = [];
localStorage.setItem('downloadedList', '[]');
return; return;
} }
const songIds = list.filter((item) => item.id).map((item) => item.id); const songIds = list.filter(item => item.id).map(item => item.id);
if (songIds.length === 0) {
// 如果有歌曲ID,获取详细信息 downloadedList.value = list;
if (songIds.length > 0) { localStorage.setItem('downloadedList', JSON.stringify(list));
try { return;
const detailRes = await getMusicDetail(songIds); }
const songDetails = detailRes.data.songs.reduce((acc, song) => {
acc[song.id] = song; try {
return acc; const detailRes = await getMusicDetail(songIds);
}, {}); const songDetails = detailRes.data.songs.reduce((acc, song) => {
acc[song.id] = song;
saveList = list.map((item) => { return acc;
const songDetail = songDetails[item.id]; }, {});
return {
...item, const updatedList = list.map(item => ({
picUrl: songDetail?.al?.picUrl || item.picUrl || '/images/default_cover.png', ...item,
ar: songDetail?.ar || item.ar || [{ name: t('download.localMusic') }] picUrl: songDetails[item.id]?.al?.picUrl || item.picUrl || '/images/default_cover.png',
}; ar: songDetails[item.id]?.ar || item.ar || [{ name: t('download.localMusic') }]
}); }));
} catch (detailError) {
console.error('Failed to get music details:', detailError); downloadedList.value = updatedList;
saveList = list; localStorage.setItem('downloadedList', JSON.stringify(updatedList));
} } catch (error) {
} else { console.error('Failed to get music details:', error);
saveList = list; downloadedList.value = list;
localStorage.setItem('downloadedList', JSON.stringify(list));
} }
setLocalDownloadedList(saveList);
} catch (error) { } catch (error) {
console.error('Failed to get downloaded music list:', error); console.error('Failed to get downloaded music list:', error);
downloadedList.value = []; downloadedList.value = [];
localStorage.setItem('downloadedList', '[]');
} finally {
isLoadingDownloaded.value = false;
} }
}; };
const setLocalDownloadedList = (list: DownloadedItem[]) => {
const localList = localStorage.getItem('downloadedList');
// 合并 去重
const saveList = [...(localList ? JSON.parse(localList) : []), ...list];
const uniqueList = saveList.filter(
(item, index, self) => index === self.findIndex((t) => t.id === item.id)
);
localStorage.setItem('downloadedList', JSON.stringify(uniqueList));
downloadedList.value = uniqueList;
};
// 监听抽屉显示状态 // 监听抽屉显示状态
watch( watch(
() => showDrawer.value, () => showDrawer.value,
(newVal) => { (newVal) => {
if (newVal) { if (newVal && !isLoadingDownloaded.value) {
refreshDownloadedList(); refreshDownloadedList();
} }
} }
@@ -503,15 +498,14 @@ onMounted(() => {
}); });
// 监听下载完成 // 监听下载完成
window.electron.ipcRenderer.on('music-download-complete', (_, data) => { window.electron.ipcRenderer.on('music-download-complete', async (_, data) => {
if (data.success) { if (data.success) {
// 从下载列表中移除 downloadList.value = downloadList.value.filter(item => item.filename !== data.filename);
downloadList.value = downloadList.value.filter((item) => item.filename !== data.filename); // 延迟刷新已下载列表,避免文件系统未完全写入
// 刷新已下载列表 setTimeout(() => refreshDownloadedList(), 500);
refreshDownloadedList();
message.success(t('download.message.downloadComplete', { filename: data.filename })); message.success(t('download.message.downloadComplete', { filename: data.filename }));
} else { } else {
const existingItem = downloadList.value.find((item) => item.filename === data.filename); const existingItem = downloadList.value.find(item => item.filename === data.filename);
if (existingItem) { if (existingItem) {
Object.assign(existingItem, { Object.assign(existingItem, {
status: 'error', status: 'error',
@@ -519,12 +513,10 @@ onMounted(() => {
progress: 0 progress: 0
}); });
setTimeout(() => { setTimeout(() => {
downloadList.value = downloadList.value.filter((item) => item.filename !== data.filename); downloadList.value = downloadList.value.filter(item => item.filename !== data.filename);
}, 3000); }, 3000);
} }
message.error( message.error(t('download.message.downloadFailed', { filename: data.filename, error: data.error }));
t('download.message.downloadFailed', { filename: data.filename, error: data.error })
);
} }
}); });