From 327384ace5b3aa6d49b23b2e454e128b0e88acf3 Mon Sep 17 00:00:00 2001 From: algerkong Date: Fri, 2 May 2025 22:39:47 +0800 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20feat:=20=E6=B7=BB=E5=8A=A0=20B?= =?UTF-8?q?=E7=AB=99=E8=A7=86=E9=A2=91=20ID=20=E5=8C=B9=E9=85=8D=E9=80=BB?= =?UTF-8?q?=E8=BE=91=EF=BC=8C=E4=BC=98=E5=8C=96=E6=94=B6=E8=97=8F=E5=8A=9F?= =?UTF-8?q?=E8=83=BD=E4=BB=A5=E6=94=AF=E6=8C=81=20B=E7=AB=99=E8=A7=86?= =?UTF-8?q?=E9=A2=91=EF=BC=8C=E7=A1=AE=E4=BF=9D=E6=94=B6=E8=97=8F=E5=88=97?= =?UTF-8?q?=E8=A1=A8=E4=B8=80=E8=87=B4=E6=80=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/player/MiniPlayBar.vue | 27 ++-- src/renderer/components/player/PlayBar.vue | 32 +++-- src/renderer/store/modules/player.ts | 68 +++++++++- .../views/bilibili/BilibiliPlayer.vue | 8 +- src/renderer/views/favorite/index.vue | 128 +++++++++++++++--- src/renderer/views/history/index.vue | 2 +- 6 files changed, 219 insertions(+), 46 deletions(-) diff --git a/src/renderer/components/player/MiniPlayBar.vue b/src/renderer/components/player/MiniPlayBar.vue index 636b273..51d3584 100644 --- a/src/renderer/components/player/MiniPlayBar.vue +++ b/src/renderer/components/player/MiniPlayBar.vue @@ -123,7 +123,7 @@ import SongItem from '@/components/common/SongItem.vue'; import { allTime, artistList, nowTime, playMusic } from '@/hooks/MusicHook'; import { useArtist } from '@/hooks/useArtist'; import { audioService } from '@/services/audioService'; -import { usePlayerStore, useSettingsStore } from '@/store'; +import { isBilibiliIdMatch, usePlayerStore, useSettingsStore } from '@/store'; import type { SongResult } from '@/type/music'; import { getImgUrl } from '@/utils'; @@ -185,20 +185,31 @@ const mute = () => { // 收藏相关 const isFavorite = computed(() => { - const numericId = - typeof playMusic.value.id === 'string' ? parseInt(playMusic.value.id, 10) : playMusic.value.id; - return playerStore.favoriteList.includes(numericId); + // 对于B站视频,使用ID匹配函数 + if (playMusic.value.source === 'bilibili' && playMusic.value.bilibiliData?.bvid) { + return playerStore.favoriteList.some(id => isBilibiliIdMatch(id, playMusic.value.id)); + } + + // 非B站视频直接比较ID + return playerStore.favoriteList.includes(playMusic.value.id); }); const toggleFavorite = async (e: Event) => { e.stopPropagation(); - const numericId = - typeof playMusic.value.id === 'string' ? parseInt(playMusic.value.id, 10) : playMusic.value.id; + + // 处理B站视频的收藏ID + let favoriteId = playMusic.value.id; + if (playMusic.value.source === 'bilibili' && playMusic.value.bilibiliData?.bvid) { + // 如果当前播放的是B站视频,且已有ID不包含--格式,则需要构造完整ID + if (!String(favoriteId).includes('--')) { + favoriteId = `${playMusic.value.bilibiliData.bvid}--${playMusic.value.song?.ar?.[0]?.id || 0}--${playMusic.value.bilibiliData.cid}`; + } + } if (isFavorite.value) { - playerStore.removeFromFavorite(numericId); + playerStore.removeFromFavorite(favoriteId); } else { - playerStore.addToFavorite(numericId); + playerStore.addToFavorite(favoriteId); } }; diff --git a/src/renderer/components/player/PlayBar.vue b/src/renderer/components/player/PlayBar.vue index faa8782..91a2ce0 100644 --- a/src/renderer/components/player/PlayBar.vue +++ b/src/renderer/components/player/PlayBar.vue @@ -206,7 +206,7 @@ import { import { useArtist } from '@/hooks/useArtist'; import MusicFull from '@/layout/components/MusicFull.vue'; import { audioService } from '@/services/audioService'; -import { usePlayerStore } from '@/store/modules/player'; +import { isBilibiliIdMatch, usePlayerStore } from '@/store/modules/player'; import { useSettingsStore } from '@/store/modules/settings'; import type { SongResult } from '@/type/music'; import { getImgUrl, isElectron, isMobile, secondToMinute, setAnimationClass } from '@/utils'; @@ -417,22 +417,32 @@ const scrollToPlayList = (val: boolean) => { }; const isFavorite = computed(() => { - // 将id转换为number,兼容B站视频ID - const numericId = - typeof playMusic.value.id === 'string' ? parseInt(playMusic.value.id, 10) : playMusic.value.id; - return playerStore.favoriteList.includes(numericId); + // 对于B站视频,使用ID匹配函数 + if (playMusic.value.source === 'bilibili' && playMusic.value.bilibiliData?.bvid) { + return playerStore.favoriteList.some(id => isBilibiliIdMatch(id, playMusic.value.id)); + } + + // 非B站视频直接比较ID + return playerStore.favoriteList.includes(playMusic.value.id); }); const toggleFavorite = async (e: Event) => { + console.log('playMusic.value', playMusic.value); e.stopPropagation(); - // 将id转换为number,兼容B站视频ID - const numericId = - typeof playMusic.value.id === 'string' ? parseInt(playMusic.value.id, 10) : playMusic.value.id; - + + // 处理B站视频的收藏ID + let favoriteId = playMusic.value.id; + if (playMusic.value.source === 'bilibili' && playMusic.value.bilibiliData?.bvid) { + // 如果当前播放的是B站视频,且已有ID不包含--格式,则需要构造完整ID + if (!String(favoriteId).includes('--')) { + favoriteId = `${playMusic.value.bilibiliData.bvid}--${playMusic.value.song?.ar?.[0]?.id || 0}--${playMusic.value.bilibiliData.cid}`; + } + } + if (isFavorite.value) { - playerStore.removeFromFavorite(numericId); + playerStore.removeFromFavorite(favoriteId); } else { - playerStore.addToFavorite(numericId); + playerStore.addToFavorite(favoriteId); } }; diff --git a/src/renderer/store/modules/player.ts b/src/renderer/store/modules/player.ts index 71046a1..633dc99 100644 --- a/src/renderer/store/modules/player.ts +++ b/src/renderer/store/modules/player.ts @@ -29,6 +29,50 @@ function getLocalStorageItem(key: string, defaultValue: T): T { } } +// 比较B站视频ID的辅助函数 +export const isBilibiliIdMatch = (id1: string | number, id2: string | number): boolean => { + const str1 = String(id1); + const str2 = String(id2); + + // 如果两个ID都不包含--分隔符,直接比较 + if (!str1.includes('--') && !str2.includes('--')) { + return str1 === str2; + } + + // 处理B站视频ID + if (str1.includes('--') || str2.includes('--')) { + // 尝试从ID中提取bvid和cid + const extractBvIdAndCid = (str: string) => { + if (!str.includes('--')) return { bvid: '', cid: '' }; + const parts = str.split('--'); + if (parts.length >= 3) { + // bvid--pid--cid格式 + return { bvid: parts[0], cid: parts[2] }; + } else if (parts.length === 2) { + // 旧格式或其他格式 + return { bvid: '', cid: parts[1] }; + } + return { bvid: '', cid: '' }; + }; + + const { bvid: bvid1, cid: cid1 } = extractBvIdAndCid(str1); + const { bvid: bvid2, cid: cid2 } = extractBvIdAndCid(str2); + + // 如果两个ID都有bvid,比较bvid和cid + if (bvid1 && bvid2) { + return bvid1 === bvid2 && cid1 === cid2; + } + + // 其他情况,只比较cid部分 + if (cid1 && cid2) { + return cid1 === cid2; + } + } + + // 默认情况,直接比较完整ID + return str1 === str2; +}; + // 提取公共函数:获取B站视频URL export const getSongUrl = async ( @@ -288,7 +332,7 @@ export const usePlayerStore = defineStore('player', () => { const playListIndex = ref(getLocalStorageItem('playListIndex', 0)); const playMode = ref(getLocalStorageItem('playMode', 0)); const musicFull = ref(false); - const favoriteList = ref(getLocalStorageItem('favoriteList', [])); + const favoriteList = ref>(getLocalStorageItem('favoriteList', [])); const savedPlayProgress = ref(); const currentSong = computed(() => playMusic.value); @@ -587,19 +631,31 @@ export const usePlayerStore = defineStore('player', () => { localStorage.setItem('playMode', JSON.stringify(playMode.value)); }; - const addToFavorite = async (id: number) => { - if (!favoriteList.value.includes(id)) { + const addToFavorite = async (id: number | string) => { + // 检查是否已存在相同的ID或内容相同的B站视频 + const isAlreadyInList = favoriteList.value.some(existingId => + typeof id === 'string' && id.includes('--') + ? isBilibiliIdMatch(existingId, id) + : existingId === id + ); + + if (!isAlreadyInList) { favoriteList.value.push(id); localStorage.setItem('favoriteList', JSON.stringify(favoriteList.value)); } }; - const removeFromFavorite = async (id: number) => { - favoriteList.value = favoriteList.value.filter((item) => item !== id); + const removeFromFavorite = async (id: number | string) => { + // 对于B站视频,需要根据bvid和cid来匹配 + if (typeof id === 'string' && id.includes('--')) { + favoriteList.value = favoriteList.value.filter(existingId => !isBilibiliIdMatch(existingId, id)); + } else { + favoriteList.value = favoriteList.value.filter(existingId => existingId !== id); + } localStorage.setItem('favoriteList', JSON.stringify(favoriteList.value)); }; - const removeFromPlayList = (id: number) => { + const removeFromPlayList = (id: number | string) => { const index = playList.value.findIndex((item) => item.id === id); if (index === -1) return; diff --git a/src/renderer/views/bilibili/BilibiliPlayer.vue b/src/renderer/views/bilibili/BilibiliPlayer.vue index 5dcd7ea..6e2b7aa 100644 --- a/src/renderer/views/bilibili/BilibiliPlayer.vue +++ b/src/renderer/views/bilibili/BilibiliPlayer.vue @@ -233,7 +233,7 @@ const loadVideoSource = async () => { // 其他分P创建占位对象,稍后按需加载 return { - id: `${videoDetail.value!.aid}--${page.cid}`, // 使用aid+cid作为唯一ID + id: `${bvid.value}--${page.page}--${page.cid}`, // 使用bvid--pid--cid作为唯一ID name: `${page.part || ''} - ${videoDetail.value!.title}`, picUrl: getBilibiliProxyUrl(videoDetail.value!.pic), type: 0, @@ -242,7 +242,7 @@ const loadVideoSource = async () => { source: 'bilibili', // 设置来源为B站 song: { name: `${page.part || ''} - ${videoDetail.value!.title}`, - id: `${videoDetail.value!.aid}--${page.cid}`, + id: `${bvid.value}--${page.page}--${page.cid}`, ar: [ { name: videoDetail.value!.owner.name, @@ -286,7 +286,7 @@ const createSongFromBilibiliVideo = (): SongResult => { const title = `${pageName} - ${videoDetail.value.title}`; return { - id: `${videoDetail.value.aid}--${currentPage.value.cid}`, // 使用aid+cid作为唯一ID + id: `${bvid.value}--${currentPage.value.page}--${currentPage.value.cid}`, // 使用bvid--pid--cid作为唯一ID name: title, picUrl: getBilibiliProxyUrl(videoDetail.value.pic), type: 0, @@ -297,7 +297,7 @@ const createSongFromBilibiliVideo = (): SongResult => { // playMusicUrl属性稍后通过loadSongUrl函数添加 song: { name: title, - id: `${videoDetail.value.aid}--${currentPage.value.cid}`, + id: `${bvid.value}--${currentPage.value.page}--${currentPage.value.cid}`, ar: [ { name: videoDetail.value.owner.name, diff --git a/src/renderer/views/favorite/index.vue b/src/renderer/views/favorite/index.vue index ad18f95..9014b6d 100644 --- a/src/renderer/views/favorite/index.vue +++ b/src/renderer/views/favorite/index.vue @@ -59,7 +59,7 @@ v-for="(song, index) in favoriteSongs" :key="song.id" :item="song" - :favorite="!isComponent" + :favorite="false" :class="setAnimationClass('animate__bounceInLeft')" :style="getItemAnimationDelay(index)" :selectable="isSelecting" @@ -90,6 +90,7 @@ import { useI18n } from 'vue-i18n'; import { useRouter } from 'vue-router'; import { getMusicDetail } from '@/api/music'; +import { getBilibiliProxyUrl, getBilibiliVideoDetail } from '@/api/bilibili'; import SongItem from '@/components/common/SongItem.vue'; import { getSongUrl } from '@/hooks/MusicListHook'; import { usePlayerStore } from '@/store'; @@ -231,6 +232,7 @@ const props = defineProps({ const getCurrentPageIds = () => { const startIndex = (currentPage.value - 1) * pageSize; const endIndex = startIndex + pageSize; + // 返回原始ID,不进行类型转换 return favoriteList.value.slice(startIndex, endIndex); }; @@ -248,23 +250,117 @@ const getFavoriteSongs = async () => { loading.value = true; try { const currentIds = getCurrentPageIds(); - const res = await getMusicDetail(currentIds); - if (res.data.songs) { - const newSongs = res.data.songs.map((song: SongResult) => ({ - ...song, - picUrl: song.al?.picUrl || '' - })); - - // 追加新数据而不是替换 - if (currentPage.value === 1) { - favoriteSongs.value = newSongs; - } else { - favoriteSongs.value = [...favoriteSongs.value, ...newSongs]; + console.log('currentIds', currentIds); + + // 分离网易云音乐ID和B站视频ID + const musicIds = currentIds.filter((id) => typeof id === 'number') as number[]; + // B站ID可能是字符串格式(包含"--")或特定数字ID,如113911642789603 + const bilibiliIds = currentIds.filter((id) => typeof id === 'string'); + + console.log('处理数据:', { + musicIds, + bilibiliIds + }); + + // 处理网易云音乐数据 + let neteaseSongs: SongResult[] = []; + if (musicIds.length > 0) { + const res = await getMusicDetail(musicIds); + if (res.data.songs) { + neteaseSongs = res.data.songs.map((song: SongResult) => ({ + ...song, + picUrl: song.al?.picUrl || '', + source: 'netease' + })); } - - // 判断是否还有更多数据 - noMore.value = favoriteSongs.value.length >= favoriteList.value.length; } + + // 处理B站视频数据 + const bilibiliSongs: SongResult[] = []; + for (const biliId of bilibiliIds) { + const strBiliId = String(biliId); + console.log(`处理B站ID: ${strBiliId}`); + + if (strBiliId.includes('--')) { + // 从ID中提取B站视频信息 (bvid--pid--cid格式) + try { + const [bvid, pid, cid] = strBiliId.split('--'); + if (!bvid || !pid || !cid) { + console.warn(`B站ID格式错误: ${strBiliId}, 正确格式应为 bvid--pid--cid`); + continue; + } + + const res = await getBilibiliVideoDetail(bvid); + const videoDetail = res.data; + + // 找到对应的分P + const page = videoDetail.pages.find(p => p.cid === Number(cid)); + if (!page) { + console.warn(`未找到对应的分P: cid=${cid}`); + continue; + } + + const songData = { + id: strBiliId, + name: `${page.part || ''} - ${videoDetail.title}`, + picUrl: getBilibiliProxyUrl(videoDetail.pic), + ar: [ + { + name: videoDetail.owner.name, + id: videoDetail.owner.mid + } + ], + al: { + name: videoDetail.title, + picUrl: getBilibiliProxyUrl(videoDetail.pic) + }, + source: 'bilibili', + bilibiliData: { + bvid, + cid: Number(cid) + } + } as SongResult; + + bilibiliSongs.push(songData); + } catch (error) { + console.error(`获取B站视频详情失败 (${strBiliId}):`, error); + } + } + } + + console.log('获取数据统计:', { + neteaseSongs: neteaseSongs.length, + bilibiliSongs: bilibiliSongs.length + }); + + // 合并数据,保持原有顺序 + const newSongs = currentIds + .map(id => { + const strId = String(id); + + // 查找B站视频 + if (typeof id === 'string') { + const found = bilibiliSongs.find(song => String(song.id) === strId); + if (found) return found; + } + + // 查找网易云音乐 + const found = neteaseSongs.find(song => String(song.id) === strId); + return found; + }) + .filter((song): song is SongResult => !!song); + + console.log(`最终歌曲列表: ${newSongs.length}首`); + + // 追加新数据而不是替换 + if (currentPage.value === 1) { + favoriteSongs.value = newSongs; + } else { + favoriteSongs.value = [...favoriteSongs.value, ...newSongs]; + } + + // 判断是否还有更多数据 + noMore.value = favoriteSongs.value.length >= favoriteList.value.length; } catch (error) { console.error('获取收藏歌曲失败:', error); } finally { diff --git a/src/renderer/views/history/index.vue b/src/renderer/views/history/index.vue index ce30cd9..998d487 100644 --- a/src/renderer/views/history/index.vue +++ b/src/renderer/views/history/index.vue @@ -109,7 +109,7 @@ const getHistorySongs = async () => { if (!page) continue; bilibiliSongs.push({ - id: `${videoDetail.aid}--${page.cid}`, + id: `${bvid}--${page.page}--${page.cid}`, name: `${page.part || ''} - ${videoDetail.title}`, picUrl: getBilibiliProxyUrl(videoDetail.pic), ar: [