From d57b1b160480100f644cf659c6ad3b3e870b882a Mon Sep 17 00:00:00 2001 From: SDCOM-0415 <127208666+SDCOM-0415@users.noreply.github.com> Date: Wed, 8 Jul 2026 19:44:55 +0800 Subject: [PATCH 1/2] =?UTF-8?q?feat(player-core):=20=E6=96=B0=E5=A2=9E?= =?UTF-8?q?=E7=BD=91=E7=BB=9C=E9=9F=B3=E9=A2=91=E6=96=87=E4=BB=B6=E6=92=AD?= =?UTF-8?q?=E6=94=BE=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 添加reqwest依赖,支持通过http/https URL加载音频资源,同时保留本地文件播放能力 --- packages/player-core/Cargo.toml | 1 + packages/player-core/src/player.rs | 19 +++++++++++++++---- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/packages/player-core/Cargo.toml b/packages/player-core/Cargo.toml index fadc87bd..996bc2ef 100644 --- a/packages/player-core/Cargo.toml +++ b/packages/player-core/Cargo.toml @@ -33,6 +33,7 @@ parking_lot = "0.12" ffmpeg_audio = { git = "https://github.com/apoint123/ffmpeg-audio.git", rev = "b77e5edea026712c42d58a9dce68e531b812c63f" } now-playing-controls = { git = "https://github.com/apoint123/now-playing-controls.git" } tokio-util = "0.7.18" +reqwest = { version = "0.13", default-features = false, features = ["rustls", "stream"] } [dev-dependencies] tracing-subscriber = "0.3" diff --git a/packages/player-core/src/player.rs b/packages/player-core/src/player.rs index 8c01642f..c0f36e6c 100644 --- a/packages/player-core/src/player.rs +++ b/packages/player-core/src/player.rs @@ -1,7 +1,7 @@ use std::{ fmt::Debug, fs::File, - io::{Read, Seek}, + io::{Cursor, Read, Seek}, sync::{ Arc, atomic::{AtomicBool, AtomicU32, AtomicU64, Ordering}, @@ -454,9 +454,20 @@ impl AudioPlayer { let song_data = self.current_song.clone().context("没有当前歌曲可播放")?; - let file = File::open(&song_data.file_path) - .with_context(|| format!("打开 {} 失败", song_data.file_path))?; - let source_stream: Box = Box::new(file); + let source_stream: Box = + if song_data.file_path.starts_with("http://") || song_data.file_path.starts_with("https://") { + let bytes = reqwest::get(&song_data.file_path) + .await + .with_context(|| format!("下载 {} 失败", song_data.file_path))? + .bytes() + .await + .with_context(|| format!("读取 {} 响应失败", song_data.file_path))?; + Box::new(Cursor::new(bytes.to_vec())) + } else { + let file = File::open(&song_data.file_path) + .with_context(|| format!("打开 {} 失败", song_data.file_path))?; + Box::new(file) + }; let target_channels = self.target_channels; let target_sample_rate = self.target_sample_rate; From c7ddf3ca34a1f2f786ac994a1ea4afbba0a9cf36 Mon Sep 17 00:00:00 2001 From: SDCOM-0415 <127208666+SDCOM-0415@users.noreply.github.com> Date: Fri, 10 Jul 2026 01:30:37 +0800 Subject: [PATCH 2/2] =?UTF-8?q?fix:=20=E9=80=82=E9=85=8D=E7=BD=91=E7=BB=9C?= =?UTF-8?q?=E5=9B=BE=E7=89=87=E5=B0=81=E9=9D=A2=E7=9A=84=E5=A4=84=E7=90=86?= =?UTF-8?q?=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 扩展了封面路径的处理逻辑,区分本地文件路径和网络URL,不再对网络链接使用convertFileSrc进行转换,同时更新了多处相关的代码逻辑,包括播放列表卡片、本地音乐上下文、播放列表封面组件以及歌曲封面工具函数,新增reqwest依赖用于相关网络处理支持 --- Cargo.lock | 1 + .../src/components/LocalMusicContext/index.tsx | 9 +++++++-- .../player/src/components/NowPlaylistCard/index.tsx | 6 +++++- .../player/src/components/PlaylistCover/index.tsx | 13 +++++++++++-- packages/player/src/utils/use-song-cover.ts | 10 ++++++++-- 5 files changed, 32 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a691b5d9..b6657d49 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -161,6 +161,7 @@ dependencies = [ "now-playing-controls", "parking_lot", "rb", + "reqwest 0.13.4", "ringbuf", "segmap", "serde", diff --git a/packages/player/src/components/LocalMusicContext/index.tsx b/packages/player/src/components/LocalMusicContext/index.tsx index 7cde5290..5b48795c 100644 --- a/packages/player/src/components/LocalMusicContext/index.tsx +++ b/packages/player/src/components/LocalMusicContext/index.tsx @@ -291,10 +291,15 @@ export const LocalMusicContext: FC = () => { ); if (songFromDb.coverPath) { - store.set(musicCoverAtom, convertFileSrc(songFromDb.coverPath)); + const coverPath = songFromDb.coverPath; + if (coverPath.startsWith("http://") || coverPath.startsWith("https://")) { + store.set(musicCoverAtom, coverPath); + } else { + store.set(musicCoverAtom, convertFileSrc(coverPath)); + } store.set( musicCoverIsVideoAtom, - songFromDb.coverPath.endsWith(".mp4"), + coverPath.endsWith(".mp4"), ); } else { store.set( diff --git a/packages/player/src/components/NowPlaylistCard/index.tsx b/packages/player/src/components/NowPlaylistCard/index.tsx index 2cd8e1c1..34bd4ff2 100644 --- a/packages/player/src/components/NowPlaylistCard/index.tsx +++ b/packages/player/src/components/NowPlaylistCard/index.tsx @@ -22,7 +22,11 @@ const PlaylistSongItem: FC< const playlistIndex = useAtomValue(queueCurrentIndexAtom); const queueManager = useAtomValue(queueManagerAtom); - const cover = song.coverPath ? convertFileSrc(song.coverPath) : ""; + const cover = song.coverPath + ? (song.coverPath.startsWith("http://") || song.coverPath.startsWith("https://") + ? song.coverPath + : convertFileSrc(song.coverPath)) + : ""; const name = song.songName || "未知歌曲"; const artists = song.songArtists || "未知艺术家"; diff --git a/packages/player/src/components/PlaylistCover/index.tsx b/packages/player/src/components/PlaylistCover/index.tsx index 9ed9e9ff..21498888 100644 --- a/packages/player/src/components/PlaylistCover/index.tsx +++ b/packages/player/src/components/PlaylistCover/index.tsx @@ -35,14 +35,23 @@ export const PlaylistCover: FC< useEffect(() => { if (playlist?.coverPath) { - setPlaylistImgs([convertFileSrc(playlist.coverPath)]); + setPlaylistImgs([ + playlist.coverPath.startsWith("http://") || playlist.coverPath.startsWith("https://") + ? playlist.coverPath + : convertFileSrc(playlist.coverPath) + ]); return; } if (songs && songs.length > 0) { const imgs = songs .slice(0, 4) // biome-ignore lint/style/noNonNullAssertion: filter() 检查了 coverPath 的存在 - .map((s) => convertFileSrc(s.coverPath!)); + .map((s) => { + const path = s.coverPath!; + return path.startsWith("http://") || path.startsWith("https://") + ? path + : convertFileSrc(path); + }); setPlaylistImgs(imgs); } else { setPlaylistImgs([]); diff --git a/packages/player/src/utils/use-song-cover.ts b/packages/player/src/utils/use-song-cover.ts index df3bf7e0..a1bc465e 100644 --- a/packages/player/src/utils/use-song-cover.ts +++ b/packages/player/src/utils/use-song-cover.ts @@ -27,7 +27,11 @@ export const useSongCover = (song?: Song) => { // 3. 从 dexie 迁移导入时,使用前端的 mime type // 所以可以确保封面路径总是 jpg 或 mp4 文件,方便判断封面是图片或者视频 if (!song.coverPath.endsWith(".mp4")) { - setSongImgUrl(convertFileSrc(song.coverPath)); + if (song.coverPath.startsWith("http://") || song.coverPath.startsWith("https://")) { + setSongImgUrl(song.coverPath); + } else { + setSongImgUrl(convertFileSrc(song.coverPath)); + } return; } @@ -39,7 +43,9 @@ export const useSongCover = (song?: Song) => { setSongImgUrl(""); const coverPath = song.coverPath; - const videoSrc = convertFileSrc(coverPath); + const videoSrc = coverPath.startsWith("http://") || coverPath.startsWith("https://") + ? coverPath + : convertFileSrc(coverPath); getVideoThumbnail(videoSrc) .then((blob) => { const url = URL.createObjectURL(blob);