feat: 扩展数据层与播放能力

This commit is contained in:
alger
2026-02-04 20:10:28 +08:00
parent a44addef22
commit 3a3820cf52
29 changed files with 1111 additions and 675 deletions
+50
View File
@@ -0,0 +1,50 @@
import { useLocalStorage } from '@vueuse/core';
import { ref, watch } from 'vue';
import type { DjProgram } from '@/types/podcast';
export const usePodcastHistory = () => {
const podcastHistory = useLocalStorage<DjProgram[]>('podcastHistory', []);
const addPodcast = (program: DjProgram) => {
const index = podcastHistory.value.findIndex((item) => item.id === program.id);
if (index !== -1) {
podcastHistory.value.unshift(podcastHistory.value.splice(index, 1)[0]);
} else {
podcastHistory.value.unshift(program);
}
if (podcastHistory.value.length > 100) {
podcastHistory.value.pop();
}
};
const delPodcast = (program: DjProgram) => {
const index = podcastHistory.value.findIndex((item) => item.id === program.id);
if (index !== -1) {
podcastHistory.value.splice(index, 1);
}
};
const clearPodcastHistory = () => {
podcastHistory.value = [];
};
const podcastList = ref(podcastHistory.value);
watch(
() => podcastHistory.value,
() => {
podcastList.value = podcastHistory.value;
},
{ deep: true }
);
return {
podcastHistory,
podcastList,
addPodcast,
delPodcast,
clearPodcastHistory
};
};
@@ -0,0 +1,66 @@
import { useLocalStorage } from '@vueuse/core';
import { ref, watch } from 'vue';
export type PodcastRadioHistoryItem = {
id: number;
name: string;
picUrl: string;
desc?: string;
dj?: {
nickname: string;
userId: number;
};
count?: number;
lastPlayTime?: number;
type?: string;
};
export const usePodcastRadioHistory = () => {
const podcastRadioHistory = useLocalStorage<PodcastRadioHistoryItem[]>('podcastRadioHistory', []);
const addPodcastRadio = (radio: PodcastRadioHistoryItem) => {
const index = podcastRadioHistory.value.findIndex((item) => item.id === radio.id);
const now = Date.now();
if (index !== -1) {
const existing = podcastRadioHistory.value.splice(index, 1)[0];
existing.count = (existing.count || 0) + 1;
existing.lastPlayTime = now;
podcastRadioHistory.value.unshift(existing);
} else {
podcastRadioHistory.value.unshift({
...radio,
count: 1,
lastPlayTime: now
});
}
if (podcastRadioHistory.value.length > 100) {
podcastRadioHistory.value.pop();
}
};
const delPodcastRadio = (radio: PodcastRadioHistoryItem) => {
const index = podcastRadioHistory.value.findIndex((item) => item.id === radio.id);
if (index !== -1) {
podcastRadioHistory.value.splice(index, 1);
}
};
const podcastRadioList = ref(podcastRadioHistory.value);
watch(
() => podcastRadioHistory.value,
() => {
podcastRadioList.value = podcastRadioHistory.value;
},
{ deep: true }
);
return {
podcastRadioHistory,
podcastRadioList,
addPodcastRadio,
delPodcastRadio
};
};
+94
View File
@@ -0,0 +1,94 @@
import { computed, onMounted, ref } from 'vue';
import { useRouter } from 'vue-router';
const downloadList = ref<any[]>([]);
const isInitialized = ref(false);
export const useDownloadStatus = () => {
const router = useRouter();
const downloadingCount = computed(() => {
return downloadList.value.filter((item) => item.status === 'downloading').length;
});
const navigateToDownloads = () => {
router.push('/downloads');
};
const initDownloadListeners = () => {
if (isInitialized.value) return;
if (!window.electron?.ipcRenderer) return;
window.electron.ipcRenderer.on('music-download-progress', (_, data) => {
const existingItem = downloadList.value.find((item) => item.filename === data.filename);
if (data.progress === 100) {
data.status = 'completed';
}
if (existingItem) {
Object.assign(existingItem, {
...data,
songInfo: data.songInfo || existingItem.songInfo
});
if (data.status === 'completed') {
downloadList.value = downloadList.value.filter((item) => item.filename !== data.filename);
}
} else {
downloadList.value.push({
...data,
songInfo: data.songInfo
});
}
});
window.electron.ipcRenderer.on('music-download-complete', async (_, data) => {
if (data.success) {
downloadList.value = downloadList.value.filter((item) => item.filename !== data.filename);
} else {
const existingItem = downloadList.value.find((item) => item.filename === data.filename);
if (existingItem) {
Object.assign(existingItem, {
status: 'error',
error: data.error,
progress: 0
});
setTimeout(() => {
downloadList.value = downloadList.value.filter(
(item) => item.filename !== data.filename
);
}, 3000);
}
}
});
window.electron.ipcRenderer.on('music-download-queued', (_, data) => {
const existingItem = downloadList.value.find((item) => item.filename === data.filename);
if (!existingItem) {
downloadList.value.push({
filename: data.filename,
progress: 0,
loaded: 0,
total: 0,
path: '',
status: 'downloading',
songInfo: data.songInfo
});
}
});
isInitialized.value = true;
};
onMounted(() => {
initDownloadListeners();
});
return {
downloadList,
downloadingCount,
navigateToDownloads
};
};
+1 -57
View File
@@ -2,7 +2,6 @@ import { cloneDeep } from 'lodash';
import { createDiscreteApi } from 'naive-ui';
import i18n from '@/../i18n/renderer';
import { getBilibiliAudioUrl } from '@/api/bilibili';
import { getMusicLrc, getMusicUrl, getParsingMusicUrl } from '@/api/music';
import { playbackRequestManager } from '@/services/playbackRequestManager';
import { SongSourceConfigManager } from '@/services/SongSourceConfigManager';
@@ -39,28 +38,6 @@ export const getSongUrl = async (
return songData.playMusicUrl;
}
if (songData.source === 'bilibili' && songData.bilibiliData) {
console.log('加载B站音频URL');
if (!songData.playMusicUrl && songData.bilibiliData.bvid && songData.bilibiliData.cid) {
try {
songData.playMusicUrl = await getBilibiliAudioUrl(
songData.bilibiliData.bvid,
songData.bilibiliData.cid
);
// 验证请求
if (requestId && !playbackRequestManager.isRequestValid(requestId)) {
console.log(`[getSongUrl] 获取B站URL后请求已失效: ${requestId}`);
throw new Error('Request cancelled');
}
return songData.playMusicUrl;
} catch (error) {
console.error('重启后获取B站音频URL失败:', error);
return '';
}
}
return songData.playMusicUrl || '';
}
// ==================== 自定义API最优先 ====================
const globalSources = settingsStore.setData.enabledMusicSources || [];
const useCustomApiGlobally = globalSources.includes('custom');
@@ -108,7 +85,7 @@ export const getSongUrl = async (
}
// 如果有自定义音源设置,直接使用getParsingMusicUrl获取URL
if (songConfig && songData.source !== 'bilibili') {
if (songConfig) {
try {
console.log(`使用自定义音源解析歌曲 ID: ${id}`);
const res = await getParsingMusicUrl(numericId, cloneDeep(songData));
@@ -239,15 +216,6 @@ const parseLyrics = (lyricsString: string): { lyrics: ILyricText[]; times: numbe
* 加载歌词(独立函数)
*/
export const loadLrc = async (id: string | number): Promise<ILyric> => {
if (typeof id === 'string' && id.includes('--')) {
console.log('B站音频,无需加载歌词');
return {
lrcTimeArray: [],
lrcArray: [],
hasWordByWord: false
};
}
try {
const numericId = typeof id === 'string' ? parseInt(id, 10) : id;
const { data } = await getMusicLrc(numericId);
@@ -346,30 +314,6 @@ export const useSongDetail = () => {
throw new Error('Request cancelled');
}
if (playMusic.source === 'bilibili') {
try {
if (!playMusic.playMusicUrl && playMusic.bilibiliData) {
playMusic.playMusicUrl = await getBilibiliAudioUrl(
playMusic.bilibiliData.bvid,
playMusic.bilibiliData.cid
);
}
// 验证请求
if (requestId && !playbackRequestManager.isRequestValid(requestId)) {
console.log(`[getSongDetail] B站URL获取后请求已失效: ${requestId}`);
throw new Error('Request cancelled');
}
playMusic.playLoading = false;
return { ...playMusic } as SongResult;
} catch (error) {
console.error('获取B站音频详情失败:', error);
playMusic.playLoading = false;
throw error;
}
}
if (playMusic.expiredAt && playMusic.expiredAt < Date.now()) {
console.info(`歌曲已过期,重新获取: ${playMusic.name}`);
playMusic.playMusicUrl = undefined;