refactor(backend): retire unused native bridge entry points

This commit is contained in:
zarzet committed 2026-09-30 21:47:15 +07:00
1 parent b8e3a4c166
commit fe88048db2
45 files changed
+20 -1832

No files matched your search

@@ -97,23 +97,6 @@ impl AuthRegistry {
self.edit(id, |record| record.code = Zeroizing::new(code.to_owned()))
}
pub fn set_tokens(
&self,
id: &str,
access: &str,
refresh: &str,
expires_in: i64,
) -> Result<(), String> {
let expires = (expires_in > 0)
.then(|| self.now() + i128::from(expires_in.wrapping_mul(1_000_000_000)));
self.edit(id, |record| {
record.access_token = Zeroizing::new(access.to_owned());
record.refresh_token = Zeroizing::new(refresh.to_owned());
record.authenticated = !access.is_empty();
record.expires_at = expires;
})
}
pub fn code(&self, id: &str) -> Option<String> {
self.state
.lock()
@@ -251,16 +234,6 @@ impl AuthRegistry {
})
}
pub fn all_pending(&self) -> Vec<PendingAuthRequest> {
self.state
.lock()
.expect("auth registry lock")
.pending
.values()
.cloned()
.collect()
}
pub fn resolve_callback(&self, nonce: &str, consume: bool) -> Result<String, String> {
let nonce = nonce.trim();
if nonce.is_empty() {
@@ -184,22 +184,6 @@ impl Backend {
Arc::clone(&self.availability)
}
pub fn get_app_version(&self) -> Result<String, String> {
let _operation = self.enter()?;
self.manager
.environment()
.get_app_version()
.map_err(|error| error.to_string())
}
pub fn set_app_version(&self, version: &str) -> Result<(), String> {
let _operation = self.enter()?;
self.manager
.environment()
.set_app_version(version)
.map_err(|error| error.to_string())
}
pub fn get_extension_pending_auth_json(&self, id: &str) -> Result<String, String> {
let _operation = self.enter()?;
let id = id.trim();
@@ -391,13 +391,6 @@ impl Backend {
Ok(output.display())
}
pub fn clear_cover_memory_cache(&self) -> Result<(), String> {
let _operation = self.enter()?;
self.cover.clear();
spotiflac_core::cover::clear_library_thumbnail_cache();
Ok(())
}
pub fn download_cover_to_file_sized(
&self,
url: &str,
@@ -60,17 +60,6 @@ impl Backend {
.unwrap_or_else(|error| native_error_response(&error).to_string()))
}
pub fn download_with_extensions_json(
&self,
raw: &str,
check: &Check<'_>,
) -> Result<String, String> {
let _operation = self.enter()?;
let request =
DownloadRequest::parse(raw).map_err(|error| format!("invalid request: {error}"))?;
self.download_request(request, check)
}
fn download_request(
&self,
mut request: DownloadRequest,
@@ -75,21 +75,6 @@ impl Backend {
Ok(())
}
pub fn scan_library_folder(&self, folder: &str, check: Check<'_>) -> Result<Value, String> {
let mut tracks = Vec::new();
self.scan_library(
folder,
None,
true,
&mut |value| {
tracks.push(value);
Ok(())
},
check,
)?;
Ok(tracks.into())
}
pub fn scan_library_folder_incremental(
&self,
folder: &str,
@@ -118,11 +118,6 @@ impl Backend {
Ok(response)
}
pub fn get_lyrics_providers_json(&self) -> Result<String, String> {
let _operation = self.enter()?;
serde_json::to_string(&self.lyrics.providers()).map_err(|error| error.to_string())
}
pub fn set_lyrics_providers_json(&self, raw: &str) -> Result<(), String> {
let _operation = self.enter()?;
let providers = config::decode_providers(raw).map_err(|error| error.to_string())?;
@@ -136,11 +131,6 @@ impl Backend {
serde_json::to_string(&config::available_providers()).map_err(|error| error.to_string())
}
pub fn get_lyrics_fetch_options_json(&self) -> Result<String, String> {
let _operation = self.enter()?;
serde_json::to_string(&self.lyrics.options()).map_err(|error| error.to_string())
}
pub fn set_lyrics_fetch_options_json(&self, raw: &str) -> Result<(), String> {
let _operation = self.enter()?;
let _settings = self.lyrics_settings.lock().expect("lyrics settings lock");
@@ -2,7 +2,7 @@ use super::Backend;
use serde::Serialize;
use serde_json::{Value, json};
use spotiflac_core::metadata::TrackMetadata;
use spotiflac_providers::deezer::{self, MetadataLookup};
use spotiflac_providers::deezer::MetadataLookup;
use spotiflac_providers::musicbrainz::MusicBrainzOptions;
use spotiflac_providers::resolver::{Check, ResolverError, ResolverOptions};
use std::time::{Duration, Instant};
@@ -97,13 +97,6 @@ impl Backend {
.map_err(|error| error.to_string())
}
pub fn get_song_link_region(&self) -> Result<String, String> {
let _operation = self.enter()?;
self.availability
.region()
.map_err(|error| error.to_string())
}
// The retired Settings track-ID cache is distinct from catalog caches.
pub fn get_track_cache_size(&self) -> Result<u64, String> {
let _operation = self.enter()?;
@@ -115,11 +108,6 @@ impl Backend {
Ok(())
}
pub fn parse_deezer_url(&self, url: &str) -> Result<(String, String), String> {
let _operation = self.enter()?;
deezer::parse_url(url).map_err(|error| error.to_string())
}
pub fn search_deezer(
&self,
query: &str,
@@ -137,15 +125,6 @@ impl Backend {
})
}
pub fn get_deezer_metadata(
&self,
kind: &str,
id: &str,
check: &Check<'_>,
) -> Result<String, String> {
self.metadata_operation(30, check, |check| self.deezer_metadata(kind, id, check))
}
pub(super) fn deezer_metadata(
&self,
kind: &str,
@@ -175,10 +154,6 @@ impl Backend {
})
}
pub fn search_deezer_by_isrc(&self, isrc: &str, check: &Check<'_>) -> Result<String, String> {
self.search_deezer_by_isrc_for_item_id(isrc, "", check)
}
pub fn search_deezer_by_isrc_for_item_id(
&self,
isrc: &str,
@@ -246,69 +221,6 @@ impl Backend {
})
}
pub fn check_track_availability_json(
&self,
id: &str,
isrc: &str,
check: &Check<'_>,
) -> Result<String, String> {
self.metadata_operation(30, check, |check| {
encode(&self.availability.check_track(id, isrc, check)?)
})
}
pub fn check_album_availability_json(
&self,
id: &str,
check: &Check<'_>,
) -> Result<String, String> {
self.metadata_operation(30, check, |check| {
encode(&self.availability.check_album(id, check)?)
})
}
pub fn check_availability_from_deezer_json(
&self,
id: &str,
check: &Check<'_>,
) -> Result<String, String> {
self.metadata_operation(30, check, |check| {
encode(&self.availability.check_from_deezer(id, check)?)
})
}
pub fn check_availability_by_platform_json(
&self,
platform: &str,
kind: &str,
id: &str,
check: &Check<'_>,
) -> Result<String, String> {
self.metadata_operation(30, check, |check| {
encode(
&self
.availability
.check_by_platform(platform, kind, id, check)?,
)
})
}
pub fn check_availability_from_url_json(
&self,
url: &str,
check: &Check<'_>,
) -> Result<String, String> {
self.metadata_operation(30, check, |check| {
encode(&self.availability.check_from_url(url, check)?)
})
}
pub fn get_streaming_urls_json(&self, id: &str, check: &Check<'_>) -> Result<String, String> {
self.metadata_operation(30, check, |check| {
encode(&self.availability.streaming_urls(id, check)?)
})
}
pub fn get_deezer_id_from_spotify(
&self,
id: &str,
@@ -316,66 +228,6 @@ impl Backend {
) -> Result<String, String> {
self.metadata_operation(30, check, |check| self.deezer_id_from_spotify(id, check))
}
pub fn get_deezer_album_id_from_spotify(
&self,
id: &str,
check: &Check<'_>,
) -> Result<String, String> {
self.metadata_operation(30, check, |check| {
self.availability.deezer_album_id(id, check)
})
}
pub fn get_youtube_url_from_spotify(
&self,
id: &str,
check: &Check<'_>,
) -> Result<String, String> {
self.metadata_operation(30, check, |check| {
self.availability.youtube_url_from_spotify(id, check)
})
}
pub fn get_spotify_id_from_deezer_track(
&self,
id: &str,
check: &Check<'_>,
) -> Result<String, String> {
self.metadata_operation(30, check, |check| {
self.availability.platform_from_deezer(id, "spotify", check)
})
}
pub fn get_tidal_url_from_deezer_track(
&self,
id: &str,
check: &Check<'_>,
) -> Result<String, String> {
self.metadata_operation(30, check, |check| {
self.availability.platform_from_deezer(id, "tidal", check)
})
}
pub fn get_amazon_url_from_deezer_track(
&self,
id: &str,
check: &Check<'_>,
) -> Result<String, String> {
self.metadata_operation(30, check, |check| {
self.availability.platform_from_deezer(id, "amazon", check)
})
}
pub fn get_youtube_url_from_deezer_track(
&self,
id: &str,
check: &Check<'_>,
) -> Result<String, String> {
self.metadata_operation(30, check, |check| {
self.availability.platform_from_deezer(id, "youtube", check)
})
}
}
#[cfg(test)]
@@ -10,13 +10,6 @@ use std::time::Duration;
mod value_tests;
impl Backend {
pub fn enrich_track_json(&self, id: &str, track_json: &str) -> Result<String, String> {
let _operation = self.enter()?;
self.manager
.enrich_track_export(id, track_json)
.map_err(|error| error.to_string())
}
pub fn handle_url_json(&self, url: &str) -> Result<String, String> {
let _operation = self.enter()?;
let id = self
@@ -11,24 +11,6 @@ use std::io::Write;
use std::sync::Mutex;
impl Backend {
/// Resolve proposed tags without reading audio, downloading covers or writing files.
pub fn preview_reenrich_file(
&self,
request_json: &str,
check: &Check<'_>,
) -> Result<String, String> {
let _operation = self.enter()?;
let check = || {
self.check()?;
check()
};
check()?;
let request = self.resolve_reenrich_request(request_json, &check)?;
serde_json::to_string(&json!({"method":"preview","success":true,
"enriched_metadata":request.result_metadata()}))
.map_err(|error| error.to_string())
}
/// Execute FLAC enrichment or return the existing native FFmpeg plan.
/// Returned cover files belong to the caller, which removes them after use.
pub fn reenrich_file(&self, request_json: &str, check: &Check<'_>) -> Result<String, String> {
@@ -207,11 +207,6 @@ impl ExtensionEnvironment {
Ok(self.store(id)?.set(StoreKind::Settings, key, value)?)
}
pub fn remove_setting(&self, id: &str, key: &str) -> Result<(), EnvironmentError> {
let _operation = self.enter()?;
Ok(self.store(id)?.remove(StoreKind::Settings, key)?)
}
pub fn set_allow_private_network(&self, allow: bool) -> Result<(), EnvironmentError> {
let _operation = self.enter()?;
self.network.set_allow_private_network(allow);
@@ -247,13 +242,6 @@ impl ExtensionEnvironment {
Ok(self.app_version.get())
}
pub fn set_app_version(&self, version: &str) -> Result<(), EnvironmentError> {
let _operation = self.enter()?;
self.app_version
.set(version)
.map_err(|error| EnvironmentError::Gate(error.to_string()))
}
pub(crate) fn shared_app_version(&self) -> AppVersion {
self.app_version.clone()
}
@@ -622,17 +610,6 @@ impl ExtensionEnvironment {
Ok(self.auth.pending(id.trim()))
}
pub fn all_pending_auth(&self) -> Result<Vec<PendingAuthRequest>, EnvironmentError> {
let _operation = self.enter()?;
Ok(self.auth.all_pending())
}
pub fn clear_pending_auth(&self, id: &str) -> Result<(), EnvironmentError> {
let _operation = self.enter()?;
self.auth.clear_pending(id);
Ok(())
}
pub fn resolve_callback_state(
&self,
state: &str,
@@ -648,24 +625,6 @@ impl ExtensionEnvironment {
let _operation = self.enter()?;
self.auth.set_code(id, code).map_err(EnvironmentError::Auth)
}
pub fn set_auth_tokens(
&self,
id: &str,
access: &str,
refresh: &str,
expires_in: i64,
) -> Result<(), EnvironmentError> {
let _operation = self.enter()?;
self.auth
.set_tokens(id, access, refresh, expires_in)
.map_err(EnvironmentError::Auth)
}
pub fn is_authenticated(&self, id: &str) -> Result<bool, EnvironmentError> {
let _operation = self.enter()?;
Ok(self.auth.authenticated(id))
}
}
fn network_timeout(value: Option<&Value>) -> Duration {
@@ -1,5 +1,5 @@
use super::{EnvironmentError, ExtensionEnvironment};
use spotiflac_core::isrc::{IndexCache, IndexFiles, NativeFiles, parse_tracks};
use spotiflac_core::isrc::{IndexCache, NativeFiles};
use std::sync::atomic::Ordering;
type Check<'a> = &'a (dyn Fn() -> Result<(), String> + Sync);
@@ -27,27 +27,6 @@ impl ExtensionEnvironment {
/// Trusted native entry points share the SDK's index, while JavaScript uses
/// a scoped IndexFiles adapter and revalidates native directory grants.
pub fn prebuild_isrc_index(
&self,
directory: &str,
check: Check<'_>,
) -> Result<(), EnvironmentError> {
self.with_index(check, |cache, files, check| {
cache.prebuild(directory, files, check)
})
}
pub fn check_isrc_exists(
&self,
directory: &str,
isrc: &str,
check: Check<'_>,
) -> Result<String, EnvironmentError> {
self.with_index(check, |cache, files, check| {
cache.check(directory, isrc, files, check)
})
}
pub fn add_to_isrc_index(
&self,
directory: &str,
@@ -60,33 +39,9 @@ impl ExtensionEnvironment {
})
}
pub fn check_files_exist_parallel(
&self,
directory: &str,
tracks_json: &str,
check: Check<'_>,
) -> Result<String, EnvironmentError> {
self.with_index(check, |cache, files, check| {
if tracks_json.len() > 8 * 1024 * 1024 {
return Err("tracks JSON exceeds 8 MiB limit".into());
}
let tracks = parse_tracks(tracks_json)?;
let results = cache.check_batch(directory, &tracks, files, check)?;
serde_json::to_string(&results).map_err(|error| error.to_string())
})
}
pub fn invalidate_isrc_cache(&self, directory: &str) -> Result<(), EnvironmentError> {
let _operation = self.enter()?;
self.isrc.invalidate(directory);
Ok(())
}
pub fn check_file_exists(&self, path: &str) -> Result<bool, EnvironmentError> {
let _operation = self.enter()?;
NativeFiles
.stat(path)
.map(|stamp| stamp.is_some_and(|stamp| !stamp.directory && stamp.size > 0))
.map_err(EnvironmentError::Index)
}
}
+3 -11
View File
@@ -56,12 +56,6 @@ impl CommandRegistry {
Ok(state.commands.get(id).map(|entry| entry.command.clone()))
}
pub fn pending_json(&self) -> Result<String, RegistryClosed> {
let mut state = self.state.lock().expect("FFmpeg state lock");
state.check()?;
Ok(state.pending(false))
}
/// Claims each command at most once, even with multiple native pumps.
/// A zero/negative timeout performs a nonblocking claim. Shutdown wakes waits.
pub fn wait_pending_json(&self, timeout_ms: i64) -> Result<String, RegistryClosed> {
@@ -70,7 +64,7 @@ impl CommandRegistry {
let mut state = self.state.lock().expect("FFmpeg state lock");
loop {
state.check()?;
let commands = state.pending(true);
let commands = state.claim_pending();
let remaining = timeout.saturating_sub(started.elapsed());
if commands != "[]" || remaining.is_zero() {
return Ok(commands);
@@ -185,7 +179,7 @@ impl State {
}
}
fn pending(&mut self, claim: bool) -> String {
fn claim_pending(&mut self) -> String {
#[derive(Serialize)]
struct PendingCommand<'a> {
command_id: &'a str,
@@ -198,9 +192,7 @@ impl State {
.values_mut()
.filter(|entry| !entry.claimed && entry.result.is_none())
.map(|entry| {
if claim {
entry.claimed = true;
}
entry.claimed = true;
PendingCommand {
command_id: &entry.command.command_id,
extension_id: &entry.command.extension_id,
@@ -590,12 +590,6 @@ impl ExtensionManager {
.map_err(|e| error(e.to_string()))
}
pub fn unload(&self, id: &str) -> Result<(), ManagerError> {
let _metadata = MetadataChange(&self.metadata_revision);
let _mutation = self.mutation.lock().expect("extension mutation lock");
self.unload_locked(id)
}
fn unload_locked(&self, id: &str) -> Result<(), ManagerError> {
self.check()?;
let entry = self
@@ -612,23 +606,6 @@ impl ExtensionManager {
Ok(())
}
pub fn unload_all(&self) -> Result<(), ManagerError> {
let _metadata = MetadataChange(&self.metadata_revision);
let _mutation = self.mutation.lock().expect("extension mutation lock");
self.check()?;
let ids: Vec<_> = self
.entries
.lock()
.expect("extension manager lock")
.keys()
.cloned()
.collect();
for id in ids {
self.unload_locked(&id)?;
}
Ok(())
}
pub fn remove(&self, id: &str) -> Result<(), ManagerError> {
let _metadata = MetadataChange(&self.metadata_revision);
let _mutation = self.mutation.lock().expect("extension mutation lock");
@@ -32,16 +32,6 @@ impl ExtensionManager {
.map_err(|failure| error(failure.to_string()))
}
pub fn check_availability(
&self,
id: &str,
request: ProviderAvailabilityRequest,
timeout_ms: u64,
) -> Result<String, ManagerError> {
let lease = self.item_lease(&request.item_id)?;
self.check_availability_with_lease(id, request, timeout_ms, lease)
}
pub(crate) fn check_availability_with_lease(
&self,
id: &str,
@@ -70,52 +60,6 @@ impl ExtensionManager {
)
}
/// Best-effort enrichment preserves the original provider attribution.
/// Cancellation remains an error, allowing the download worker to stop.
pub fn enrich_track(
&self,
id: &str,
track_json: &str,
item_id: &str,
timeout_ms: u64,
) -> Result<String, ManagerError> {
self.enrich_track_with_lease(
id,
track_json,
item_id,
timeout_ms,
self.item_lease(item_id)?,
)
}
pub(crate) fn enrich_track_export(
&self,
id: &str,
track_json: &str,
) -> Result<String, ManagerError> {
self.check()?;
let Ok(entry) = self.get(id) else {
return Ok(track_json.into());
};
if !entry.manifest.has_type("metadata_provider") {
return Ok(track_json.into());
}
// This export unmarshals into a value struct, unlike the nullable
// provider argument used by the internal download API.
let input = if track_json.trim() == "null" {
"{}"
} else {
track_json
};
self.enrich_track(id, input, "", 30_000).map_err(|failure| {
if let Some(message) = failure.0.strip_prefix("invalid track: ") {
error(format!("failed to parse track: {message}"))
} else {
failure
}
})
}
pub(crate) fn enrich_track_with_lease(
&self,
id: &str,
@@ -258,12 +258,6 @@ impl ExtensionRepository {
Ok(())
}
pub fn clear_cache(&self) -> Result<(), RepositoryError> {
self.check()?;
self.clear_locked(&mut self.state.lock().expect("repository state lock"));
Ok(())
}
fn clear_locked(&self, state: &mut State) {
state.generation = state.generation.wrapping_add(1);
state.cache = None;
@@ -420,13 +414,6 @@ impl ExtensionRepository {
)
}
pub fn categories(&self) -> Result<Vec<String>, RepositoryError> {
self.check()?;
Ok(["metadata", "download", "utility", "lyrics", "integration"]
.map(str::to_owned)
.to_vec())
}
pub fn download(&self, id: &str, directory: &Path) -> Result<PathBuf, RepositoryError> {
let _operation = self.enter()?;
let generation = self.state.lock().expect("repository state lock").generation;
@@ -478,16 +478,6 @@ impl ExtensionRuntime {
/// Read typed provider fields in the owning VM. Go's provider wrappers do
/// not await a returned Promise; the provider object itself is parsed.
pub fn call_provider(
&self,
method: &str,
arguments: &str,
lease: Option<Arc<RequestLease>>,
timeout_ms: u64,
) -> Result<String, ExtensionError> {
self.call_provider_operation(method, arguments, lease, timeout_ms, String::new())
}
pub(crate) fn call_provider_operation(
&self,
method: &str,
@@ -596,33 +586,6 @@ impl ExtensionRuntime {
)
}
/// The manager starts progress before resolution and completes it after
/// finalization. This call acquires another reference to the same item and
/// binds its identity to the queued command, never to mutable runtime state.
pub fn call_download_for_item(
&self,
method: &str,
arguments_json: &str,
item_id: &str,
resolution_timeout_ms: u64,
) -> Result<String, ExtensionError> {
if self.control.is_closed() {
return Err(ExtensionError::Closed);
}
let lease = Arc::new(
self.downloads
.acquire(item_id)
.map_err(ExtensionError::Cancelled)?,
);
self.call_download_operation(
method,
arguments_json,
Some(lease),
resolution_timeout_ms,
item_id.trim().to_owned(),
)
}
pub fn download_state(&self) -> Arc<spotiflac_core::downloads::DownloadState> {
Arc::clone(&self.downloads)
}