This commit is contained in:
2026-09-12 18:38:30 +08:00
parent 4f574cb5fa
commit 80d764ed51
11 changed files with 206 additions and 29 deletions
+9 -7
View File
@@ -44,13 +44,14 @@ use monitor_kernel::{
};
use music::{
feiniu_activate_connection, feiniu_cache_clear, feiniu_cache_fetch, feiniu_cache_status,
feiniu_delete_connection, feiniu_delete_local, feiniu_fnconnect_resolve, feiniu_get_config,
feiniu_list_connections, feiniu_list_tracks, feiniu_login, feiniu_logout, feiniu_lyric,
feiniu_media_prefix, feiniu_save_connection, feiniu_scan_local, feiniu_test_connection,
music_cancel_runtime_install, music_download, music_download_cancel, music_env_status,
music_get_settings, music_get_sources, music_install_runtime, music_parse_playlist, music_ping,
music_resolve, music_save_settings, music_search, music_stop_bridge, webdav_delete,
webdav_get_secret, webdav_save_secret, webdav_test, webdav_upload, MusicManager,
feiniu_cover_data, feiniu_delete_connection, feiniu_delete_local, feiniu_fnconnect_resolve,
feiniu_get_config, feiniu_list_connections, feiniu_list_tracks, feiniu_login, feiniu_logout,
feiniu_lyric, feiniu_media_prefix, feiniu_save_connection, feiniu_scan_local,
feiniu_test_connection, music_cancel_runtime_install, music_download, music_download_cancel,
music_env_status, music_get_settings, music_get_sources, music_install_runtime,
music_parse_playlist, music_ping, music_resolve, music_save_settings, music_search,
music_stop_bridge, webdav_delete, webdav_get_secret, webdav_save_secret, webdav_test,
webdav_upload, MusicManager,
};
use network_monitor::network_status;
use osd_window::{
@@ -281,6 +282,7 @@ pub fn run() {
feiniu_get_config,
feiniu_list_tracks,
feiniu_lyric,
feiniu_cover_data,
feiniu_media_prefix,
feiniu_scan_local,
feiniu_cache_status,
+22
View File
@@ -380,6 +380,28 @@ pub async fn feiniu_lyric(
Ok(json!({ "lyric": text }))
}
/// 封面 data URLWindows SMTC / MediaSession artwork 用):
/// Rust 侧取 NAS 封面字节转 base64,规避 WebView 对 artwork 的 CORS 要求。
/// 返回 Value 且未标注 specta:前端直接按 JSON 使用(同 music_ping 豁免模式)。
#[tauri::command]
pub async fn feiniu_cover_data(
state: State<'_, MusicManager>,
cover_id: String,
size: Option<u32>,
) -> Result<serde_json::Value, String> {
state.feiniu.sync_with_settings(&state.load_settings());
let (bytes, ct) = state
.feiniu
.cover_bytes(&cover_id, size.unwrap_or(320))
.await?;
use base64::Engine;
let data = format!(
"data:{ct};base64,{}",
base64::engine::general_purpose::STANDARD.encode(bytes)
);
Ok(json!({ "data": data }))
}
/// 本地媒体地址前缀:{ mediaPrefix }(首次调用惰性启动本地流代理)。
#[tauri::command]
pub async fn feiniu_media_prefix(
+38
View File
@@ -315,6 +315,44 @@ impl Feiniu {
Ok(extract_lyric_text(&v))
}
/// 封面二进制(MediaSession artwork 用):`GET /music/api/v1/static/cover`。
/// 返回 (字节, content-type)。与流代理同款认证注入,但不走代理——
/// 前端拿到的是 data URL,规避 WebView 对 artwork 的 CORS 要求。
pub async fn cover_bytes(&self, cover_id: &str, size: u32) -> Result<(Vec<u8>, String), String> {
let (base, token, access_code) = self.auth_triple();
if base.is_empty() || token.is_empty() {
return Err("未登录".into());
}
let (_, client) = self.conn_client();
let query = [
("coverId".to_string(), cover_id.to_string()),
("size".to_string(), size.to_string()),
];
let mut rb = client.get(format!("{base}/music/api/v1/static/cover")).query(&query);
rb = rb.header("cookie", format!("music-token={token}"));
if !access_code.is_empty() {
use base64::Engine;
rb = rb
.header(
"x-access-code",
base64::engine::general_purpose::STANDARD.encode(access_code.as_bytes()),
)
.header("x-access-source", "app");
}
let resp = rb.send().await.map_err(|e| format!("请求失败: {e}"))?;
if !resp.status().is_success() {
return Err(format!("请求失败(HTTP {}", resp.status().as_u16()));
}
let ct = resp
.headers()
.get(reqwest::header::CONTENT_TYPE)
.and_then(|v| v.to_str().ok())
.unwrap_or("image/jpeg")
.to_string();
let bytes = resp.bytes().await.map_err(|e| format!("读取失败: {e}"))?;
Ok((bytes.to_vec(), ct))
}
/// 本地媒体地址前缀:`http://127.0.0.1:<port>/feiniu`。首次调用惰性启动代理。
pub async fn media_prefix(&self) -> Result<String, String> {
let port = self.ensure_proxy().await?;
+8 -7
View File
@@ -24,13 +24,14 @@ pub use feiniu::{extract_fn_id, normalize_base_url, resolve_base_url, Feiniu, Fe
pub use commands::{
feiniu_activate_connection, feiniu_cache_clear, feiniu_cache_fetch, feiniu_cache_status,
feiniu_delete_connection, feiniu_delete_local, feiniu_fnconnect_resolve, feiniu_get_config,
feiniu_list_connections, feiniu_list_tracks, feiniu_login, feiniu_logout, feiniu_lyric,
feiniu_media_prefix, feiniu_save_connection, feiniu_scan_local, feiniu_test_connection,
music_cancel_runtime_install, music_download, music_download_cancel, music_env_status,
music_get_settings, music_get_sources, music_install_runtime, music_parse_playlist, music_ping,
music_resolve, music_save_settings, music_search, music_stop_bridge, webdav_delete,
webdav_get_secret, webdav_save_secret, webdav_test, webdav_upload,
feiniu_cover_data, feiniu_delete_connection, feiniu_delete_local, feiniu_fnconnect_resolve,
feiniu_get_config, feiniu_list_connections, feiniu_list_tracks, feiniu_login, feiniu_logout,
feiniu_lyric, feiniu_media_prefix, feiniu_save_connection, feiniu_scan_local,
feiniu_test_connection, music_cancel_runtime_install, music_download, music_download_cancel,
music_env_status, music_get_settings, music_get_sources, music_install_runtime,
music_parse_playlist, music_ping, music_resolve, music_save_settings, music_search,
music_stop_bridge, webdav_delete, webdav_get_secret, webdav_save_secret, webdav_test,
webdav_upload,
};
use serde::{Deserialize, Serialize};
+1 -1
View File
@@ -24,7 +24,7 @@
}
],
"security": {
"csp": "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob: asset: http://asset.localhost; font-src 'self' data:; connect-src ipc: http://ipc.localhost; media-src 'self' data: blob:"
"csp": "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob: asset: http://asset.localhost http: https:; font-src 'self' data:; connect-src ipc: http://ipc.localhost http: https:; media-src 'self' data: blob: file: http: https:"
}
},
"bundle": {