mirror of
https://github.com/Psychotoxical/psysonic.git
synced 2026-07-22 07:15:47 +00:00
refactor(ipc): complete tauri-specta typed-IPC cutover, contract guards, and layering cleanup (#1230)
This commit is contained in:
+7
-1
@@ -26,6 +26,7 @@ pub fn resolve_hot_cache_root(
|
||||
/// Returns true if the current Linux system is Arch-based
|
||||
/// (checks /etc/arch-release and /etc/os-release).
|
||||
#[tauri::command]
|
||||
#[specta::specta]
|
||||
pub fn check_arch_linux() -> bool {
|
||||
#[cfg(target_os = "linux")]
|
||||
{
|
||||
@@ -57,6 +58,7 @@ pub struct UpdateDownloadProgress {
|
||||
/// Emits `update:download:progress` events with `{ bytes, total }` every 250 ms.
|
||||
/// Returns the final absolute file path on success.
|
||||
#[tauri::command]
|
||||
#[specta::specta]
|
||||
pub async fn download_update(url: String, filename: String, app: tauri::AppHandle) -> Result<String, String> {
|
||||
use futures_util::StreamExt;
|
||||
use std::time::{Duration, Instant};
|
||||
@@ -130,6 +132,7 @@ pub async fn download_update(url: String, filename: String, app: tauri::AppHandl
|
||||
/// Performs a track search, then fetches the LRC string for the best match.
|
||||
/// Returns `None` if no match or no lyrics are found.
|
||||
#[tauri::command]
|
||||
#[specta::specta]
|
||||
pub async fn fetch_netease_lyrics(artist: String, title: String) -> Result<Option<String>, String> {
|
||||
let client = reqwest::Client::builder()
|
||||
.user_agent(subsonic_wire_user_agent())
|
||||
@@ -183,6 +186,7 @@ pub async fn fetch_netease_lyrics(artist: String, title: String) -> Result<Optio
|
||||
/// Errors are silenced and mapped to `None` so the frontend falls through to the
|
||||
/// next lyrics source without crashing.
|
||||
#[tauri::command]
|
||||
#[specta::specta]
|
||||
pub fn get_embedded_lyrics(path: String) -> Option<String> {
|
||||
use lofty::file::FileType;
|
||||
use lofty::prelude::*;
|
||||
@@ -282,6 +286,7 @@ pub fn get_embedded_lyrics(path: String) -> Option<String> {
|
||||
/// Uses platform-specific process spawning — tauri-plugin-shell's open() only
|
||||
/// allows https:// URLs per the capability scope and fails silently for paths.
|
||||
#[tauri::command]
|
||||
#[specta::specta]
|
||||
pub fn open_folder(path: String) -> Result<(), String> {
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
@@ -323,6 +328,7 @@ pub struct ZipProgress {
|
||||
/// live MB-counter without holding any binary data in the WebView process.
|
||||
/// Returns the final destination path on success.
|
||||
#[tauri::command]
|
||||
#[specta::specta]
|
||||
pub async fn download_zip(
|
||||
id: String,
|
||||
url: String,
|
||||
@@ -409,7 +415,7 @@ pub async fn download_zip(
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(serde::Serialize)]
|
||||
#[derive(serde::Serialize, specta::Type)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct HotCacheDownloadResult {
|
||||
pub path: String,
|
||||
|
||||
@@ -10,6 +10,7 @@ use super::downloads::{resolve_hot_cache_root, HotCacheDownloadResult};
|
||||
use super::offline::enqueue_analysis_seed_from_file;
|
||||
|
||||
#[tauri::command]
|
||||
#[specta::specta]
|
||||
pub async fn download_track_hot_cache(
|
||||
track_id: String,
|
||||
server_id: String,
|
||||
@@ -129,6 +130,7 @@ pub async fn download_track_hot_cache(
|
||||
/// Promotes bytes captured by the manual streaming path into hot cache on disk.
|
||||
/// Returns `Ok(None)` when no completed stream cache is available for this URL.
|
||||
#[tauri::command]
|
||||
#[specta::specta]
|
||||
pub async fn promote_stream_cache_to_hot_cache(
|
||||
track_id: String,
|
||||
server_id: String,
|
||||
@@ -244,6 +246,7 @@ pub async fn promote_stream_cache_to_hot_cache(
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
#[specta::specta]
|
||||
pub async fn get_hot_cache_size(custom_dir: Option<String>, app: tauri::AppHandle) -> u64 {
|
||||
resolve_hot_cache_root(custom_dir, &app)
|
||||
.map(|root| super::fs_utils::dir_size_recursive(&root))
|
||||
@@ -251,6 +254,7 @@ pub async fn get_hot_cache_size(custom_dir: Option<String>, app: tauri::AppHandl
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
#[specta::specta]
|
||||
pub async fn delete_hot_cache_track(
|
||||
local_path: String,
|
||||
custom_dir: Option<String>,
|
||||
@@ -282,6 +286,7 @@ pub async fn delete_hot_cache_track(
|
||||
|
||||
/// Removes the entire hot cache root (`psysonic-hot-cache` for the active location).
|
||||
#[tauri::command]
|
||||
#[specta::specta]
|
||||
pub async fn purge_hot_cache(custom_dir: Option<String>, app: tauri::AppHandle) -> Result<(), String> {
|
||||
let root = resolve_hot_cache_root(custom_dir, &app)?;
|
||||
if !root.exists() {
|
||||
|
||||
+17
-4
@@ -44,7 +44,7 @@ pub fn resolve_media_dir(custom_media_dir: Option<&str>, app: &AppHandle) -> Res
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(serde::Serialize)]
|
||||
#[derive(serde::Serialize, specta::Type)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct LocalTrackDownloadResult {
|
||||
pub path: String,
|
||||
@@ -52,7 +52,7 @@ pub struct LocalTrackDownloadResult {
|
||||
pub layout_fingerprint: String,
|
||||
}
|
||||
|
||||
#[derive(serde::Serialize)]
|
||||
#[derive(serde::Serialize, specta::Type)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct LibraryTrackProbeResult {
|
||||
pub path: String,
|
||||
@@ -61,7 +61,7 @@ pub struct LibraryTrackProbeResult {
|
||||
pub exists: bool,
|
||||
}
|
||||
|
||||
#[derive(serde::Serialize)]
|
||||
#[derive(serde::Serialize, specta::Type)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct LibraryTierDiskHit {
|
||||
pub track_id: String,
|
||||
@@ -230,6 +230,7 @@ async fn local_track_hit_if_exists(
|
||||
/// `TRACK_NOT_INDEXED` when the row is missing. Disk scope uses `server_index_key`;
|
||||
/// SQL lookup uses `library_server_id`.
|
||||
#[tauri::command]
|
||||
#[specta::specta]
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub async fn download_track_local(
|
||||
tier: String,
|
||||
@@ -409,6 +410,7 @@ pub async fn download_track_local(
|
||||
/// Scan library-tier bytes on disk and match them to known candidates only
|
||||
/// (`track_offline.local_path` + canonical paths for `candidate_track_ids`).
|
||||
#[tauri::command]
|
||||
#[specta::specta]
|
||||
pub async fn discover_library_tier_on_disk(
|
||||
server_index_key: String,
|
||||
library_server_id: String,
|
||||
@@ -517,6 +519,7 @@ pub async fn discover_library_tier_on_disk(
|
||||
/// Resolve the canonical `library/` path for a track and report on-disk presence only
|
||||
/// (no download, no analysis seed).
|
||||
#[tauri::command]
|
||||
#[specta::specta]
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub async fn probe_library_track_local(
|
||||
track_id: String,
|
||||
@@ -580,6 +583,7 @@ async fn prune_orphan_files_under_root(root: &Path, keep_paths: &[String]) -> Ve
|
||||
|
||||
/// Remove library-tier files under `{server_index_key}` that are not listed in `keep_paths`.
|
||||
#[tauri::command]
|
||||
#[specta::specta]
|
||||
pub async fn prune_orphan_library_tier_files(
|
||||
server_index_key: String,
|
||||
keep_paths: Vec<String>,
|
||||
@@ -654,6 +658,7 @@ async fn evict_orphan_files_under_root_to_fit(
|
||||
|
||||
/// Evict unindexed ephemeral cache files (oldest first) until tier size ≤ `max_bytes`.
|
||||
#[tauri::command]
|
||||
#[specta::specta]
|
||||
pub async fn evict_ephemeral_cache_orphans_to_fit(
|
||||
keep_paths: Vec<String>,
|
||||
max_bytes: u64,
|
||||
@@ -667,6 +672,7 @@ pub async fn evict_ephemeral_cache_orphans_to_fit(
|
||||
|
||||
/// Remove ephemeral-tier files under `{media}/cache/` not listed in `keep_paths`.
|
||||
#[tauri::command]
|
||||
#[specta::specta]
|
||||
pub async fn prune_orphan_ephemeral_cache_files(
|
||||
keep_paths: Vec<String>,
|
||||
media_dir: Option<String>,
|
||||
@@ -679,6 +685,7 @@ pub async fn prune_orphan_ephemeral_cache_files(
|
||||
|
||||
/// Batch existence probe for reconcile (index rows without on-disk bytes).
|
||||
#[tauri::command]
|
||||
#[specta::specta]
|
||||
pub fn probe_media_files(local_paths: Vec<String>) -> Vec<bool> {
|
||||
local_paths
|
||||
.iter()
|
||||
@@ -696,6 +703,7 @@ fn resolve_media_tier_root(
|
||||
|
||||
/// Recursive byte size under `{media}/{cache|library}/`.
|
||||
#[tauri::command]
|
||||
#[specta::specta]
|
||||
pub async fn get_media_tier_size(
|
||||
tier: String,
|
||||
media_dir: Option<String>,
|
||||
@@ -712,6 +720,7 @@ pub async fn get_media_tier_size(
|
||||
|
||||
/// Deletes the entire `{cache|library}/` subtree under the media root.
|
||||
#[tauri::command]
|
||||
#[specta::specta]
|
||||
pub async fn purge_media_tier(
|
||||
tier: String,
|
||||
media_dir: Option<String>,
|
||||
@@ -749,6 +758,7 @@ fn prune_parents_after_media_file_delete(
|
||||
|
||||
/// Deletes one media file and prunes empty parents up to the tier root.
|
||||
#[tauri::command]
|
||||
#[specta::specta]
|
||||
pub async fn delete_media_file(
|
||||
local_path: String,
|
||||
media_dir: Option<String>,
|
||||
@@ -766,6 +776,7 @@ pub async fn delete_media_file(
|
||||
|
||||
/// Removes empty directories under `{media}/{cache|library}/` (post-eviction sweep).
|
||||
#[tauri::command]
|
||||
#[specta::specta]
|
||||
pub async fn prune_empty_media_tier_dirs(
|
||||
tier: String,
|
||||
media_dir: Option<String>,
|
||||
@@ -780,6 +791,7 @@ pub async fn prune_empty_media_tier_dirs(
|
||||
|
||||
/// Promotes stream-cache bytes into `{media}/cache/…` using library-index paths.
|
||||
#[tauri::command]
|
||||
#[specta::specta]
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub async fn promote_stream_cache_to_local(
|
||||
track_id: String,
|
||||
@@ -905,7 +917,7 @@ pub async fn promote_stream_cache_to_local(
|
||||
}))
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, serde::Serialize)]
|
||||
#[derive(Debug, Clone, serde::Serialize, specta::Type)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct LegacyOfflineMigrationResult {
|
||||
pub track_id: String,
|
||||
@@ -1228,6 +1240,7 @@ async fn relocate_legacy_track_file(
|
||||
/// Scan `psysonic-offline/{segment}/{trackId}.ext`, verify each id in the library
|
||||
/// index, and relocate live tracks into `{media}/library/…`.
|
||||
#[tauri::command]
|
||||
#[specta::specta]
|
||||
pub async fn migrate_legacy_offline_disk(
|
||||
media_dir: Option<String>,
|
||||
custom_offline_dir: Option<String>,
|
||||
|
||||
@@ -88,6 +88,7 @@ pub(crate) fn resolve_offline_cache_dir(
|
||||
/// Returns the absolute file path so TypeScript can store it and later
|
||||
/// construct a `psysonic-local://<path>` URL for the audio engine.
|
||||
#[tauri::command]
|
||||
#[specta::specta]
|
||||
#[allow(clippy::too_many_arguments)] // Tauri command surface — args map 1:1 to the JS call.
|
||||
pub async fn download_track_offline(
|
||||
track_id: String,
|
||||
@@ -160,6 +161,7 @@ pub async fn download_track_offline(
|
||||
/// boundary; ones still parked on the download semaphore bail as soon as they
|
||||
/// acquire a slot. Mirrors `cancel_device_sync` for the device-sync side.
|
||||
#[tauri::command]
|
||||
#[specta::specta]
|
||||
pub fn cancel_offline_downloads(download_ids: Vec<String>) {
|
||||
if let Ok(mut flags) = offline_cancel_flags().lock() {
|
||||
for id in download_ids {
|
||||
@@ -175,6 +177,7 @@ pub fn cancel_offline_downloads(download_ids: Vec<String>) {
|
||||
/// across a long session. The frontend calls this once an album/playlist
|
||||
/// download settles (completed or cancelled).
|
||||
#[tauri::command]
|
||||
#[specta::specta]
|
||||
pub fn clear_offline_cancel(download_id: String) {
|
||||
if let Ok(mut flags) = offline_cancel_flags().lock() {
|
||||
flags.remove(&download_id);
|
||||
@@ -435,6 +438,7 @@ mod tests {
|
||||
|
||||
/// Returns the total size in bytes of all files in the offline cache directory (and optional custom dir).
|
||||
#[tauri::command]
|
||||
#[specta::specta]
|
||||
pub async fn get_offline_cache_size(custom_dir: Option<String>, app: tauri::AppHandle) -> u64 {
|
||||
let default_dir = match app.path().app_data_dir() {
|
||||
Ok(d) => d.join("psysonic-offline"),
|
||||
@@ -474,6 +478,7 @@ pub(crate) async fn delete_offline_track_with_boundary(
|
||||
/// After deleting the file, empty parent directories up to (but not including)
|
||||
/// `base_dir` are pruned using `remove_dir` (never `remove_dir_all`).
|
||||
#[tauri::command]
|
||||
#[specta::specta]
|
||||
pub async fn delete_offline_track(
|
||||
local_path: String,
|
||||
base_dir: Option<String>,
|
||||
|
||||
Reference in New Issue
Block a user