native_lyrics.rs kept only the shared types, has_usable_timing and the
execute entry point. Lexing and timing helpers move to text, the LRC
parser to lrc, the TTML parser and XML normalization to ttml, credit
extraction (with its regex statics) to credits, and the tests to
tests.rs. Function bodies are unchanged; only visibility on moved items
and use lines differ. The fixture include_str path gains one parent
segment for the new file depth.
Inline test modules of tags/write.rs, lyrics/payloads.rs and
extensions/src/files.rs become sibling tests.rs files, and the
cfg(test) reference decoder of media/hires moves into hires/reference.rs.
The artist-mode helpers of tags/write.rs move to write/artists.rs.
No behavior or public API change; the supplement_tests module of
payloads.rs is now named tests.
RustCoreBackend.invokeApplication handled readFileMetadata,
editFileMetadata and cancelNativeDataJob, but MainActivity intercepts
all three first: the metadata methods are in nativeBackendMethods and
dispatchBackendApplication answers cancelNativeDataJob before it calls
invokeApplication. No Kotlin code calls invokeApplication with those
names.
Dart never sends these channel methods, so their handlers go on every
platform:
- shareMultipleContentUris (Android) and getAvailableLyricsProviders
(Android and iOS) have no Dart caller.
- scanLibraryFolderIncrementalFromSnapshot and
scanSafTreeIncrementalFromSnapshot are called only from the Dart host
branch of the library scan, which runs only when
supportsCoreBackend is false, that is never on Android or iOS. The
unreachable Dart wrappers are removed separately.
The allowlist and the when cases in MainActivity stay in sync (70
names). The UniFFI exports get_available_lyrics_providers_json and
scan_library_folder_incremental_from_snapshot were used only by the
removed handlers; the backend methods behind them stay.
Rust is the only backend, so several single-backend switches had become
constants and their other branches could never run:
- CoreBackend.supportsOutputDescriptors was always false. The
descriptor branch in SafDownloadHandler and its pfd/detachFd cleanup
never ran, and the Rust planner rejects output_fd anyway.
- routesApplication was always true on Android and iOS. Drop the member,
the "go" value of getBackendImplementations (the reply keys and the
"rust" value Dart receives are unchanged), the iOS routing guard and
the interface defaults that threw for unrouted methods.
- cleanupExtensions(), waitForDownloadProgressDelta and
scanCueForLibrary had no production caller. Dart reaches cleanup
through the cleanupExtensions channel method, and RustCoreBackend
overrides openDownloadProgress, so the polling default goes too.
The UniFFI exports DownloadState.wait_progress_delta and
ExtensionManager.scan_cue_file_for_library were used only by those
Kotlin overrides. Bindings are generated at build time by
scripts/build_rust_backend.sh; no generated binding is tracked.
DeezerClient::search_all was reachable only through
Backend::search_deezer, which had no production caller: no UniFFI
export, platform channel or extension API wraps it, and no built-in
Deezer catalogue search is planned. Remove the chain together with the
items that existed only for it: the core search result types, the Deezer
search page types and their API error wrappers, the cached Search value,
and the latency test that exercised the search fan-out.
The Search cache bucket stays because the coalesced Deezer lookups use
it. The shared error context helper moves into the deezer module now
that search.rs is gone. searchDeezerByISRC is a separate, live method
and is untouched.
check_from_deezer and the lyrics cache_size accessor are only called by
unit tests in their own crate, so compile them only for tests and narrow
them to the crate.
call_download is only used by the extensions integration tests, which
can only reach public items; the download manager goes through
call_download_provider. Keep it public but hide it from the docs.
RequestLease::wait_cancelled stays public: the extensions crate's unit
tests call it across the crate boundary and a later coalescer change is
expected to use it.
with_credits runs for every parse_lyrics data job and compiled up to
seven regexes per call. Hoist them into LazyLock statics next to the
other lyrics patterns, with the same pattern text, so each is compiled
once per process. Every other regex in the workspace is already static.
storage_failure also lowercased the error message once per probe on the
download failure path; lowercase it once and reuse it.
NetworkService::set_network_compatibility_options ignored the
insecure_tls flag by design because TLS verification is never disabled.
Remove the parameter from the network service and the extension
environment so the dead flag no longer travels through internal layers.
The UniFFI method keeps its signature: Kotlin and Swift still pass the
flag, and the mobile binding accepts and ignores it until the binding
and channel cleanup lands separately.
The table is pinned to Unicode 15.0.0 for matching parity and its generator
is no longer in the repository. Say so in the header and mark the file as
generated for linguist.
Rust is the only backend and routes the app on Android and iOS. Rewrite
the migration-era crate, script and comment wording that implied Go still
runs, and note in each crate doc that Go mentions describe the retired
backend whose observable behavior remains a compatibility contract.
Remove tokio from spotiflac-extensions and thiserror and the direct hyper
entry from spotiflac-network (hyper stays resolved through hyper-util).
Move roxmltree, rusqlite and libc into workspace.dependencies. libc already
resolves to 0.2.190, so the workspace pin uses that exact version. No
resolved version or enabled feature changes.
The desktop search CGI now answers HTTP 500 for every query, so QQ Music
never reached its lyrics. Search through the suggestion box instead, rank
title and artist matches, and check the length of the best few through
the song detail CGI so a live or remixed cut is skipped. The lyrics CGI
is unchanged and still answers.
Add two keyless providers that serve Apple Music TTML: lrc.red, matched by
recording, and BetterLyrics, which answers songs it has already cached.
TTML is read into the first-party Apple line structure, so vocal sides,
backing groups, word timing, translations and romanization share one eLRC
formatter. The XML depth guard moves to core so both readers use it.
SpotiFLAC-Mobile Lyrics becomes its own provider instead of a fallback
inside Apple Music. New defaults are Apple Music, lrc.red, SpotiFLAC-Mobile
Lyrics, LyricsPlus and LRCLIB. Migration 13 moves untouched defaults to the
new list and keeps the former fallback right after Apple Music in custom
orders. Replace the dead LyricsPlus Workers mirror.
Match decoded path components once in Dart and Rust while rejecting encoded separators and paths outside the selected folder. Add folder scan diagnostics, shared parser fixtures, a synthetic 1806-track traversal and native Android regression coverage.
Keep installed extensions visible with an error and disable execution when their encrypted settings cannot be read. Preserve settings and credentials so restoring the correct key remains possible.
Validation: 80 extension Rust tests passed (2 ignored), Clippy and formatting; Android profile integration verified repository initialization and preservation of encrypted settings.
Export, import, and split legacy history backups in Rust while preserving schema 15, all history columns, path keys, and atomic restore behavior. Keep provider refresh and pending history edits intact.
Move full and incremental ingestion, source snapshots, path keys, and download exclusion to native transactions. Preserve schema 17, FTS, WAL coordination, cancellation, and Android SAF timestamp backfill.
Move playlist parsing and serialization off the UI isolate, preserving field mapping, identifiers, durations, and export entries. Extend the shared parser contract tests with playlist fixtures.
Port LRC, enhanced LRC, and TTML parsing plus ID3v2.3 lyrics writing and picture encoding. Keep legacy contracts and deep XML fallback; guard asynchronous player updates against stale track results.
Move PCM beat analysis and spectrum cutoff estimation to cancellable Rust workers with the existing algorithms and isolated Dart fallbacks. Keep visual rendering and playback behavior unchanged.
Port the pinned Material quantizer and Dart RNG compatibility to Rust while keeping Material Score and final color schemes in Dart. Include exact palette fixtures, licenses, and Kotlin worker ABI coverage.
Introduce Android, iOS, and Dart worker dispatch with queued cancellation, binary inputs, and background JSON codecs. Move APK verification to streamed native SHA256 while retaining the isolated fallback.