The track metadata screen's _performConversion re-implemented the batch
conversion pipeline step by step (metadata merge, lyrics, cover temp file,
SAF temp copy, FFmpeg, SAF publish, history or library update, temp
cleanup). Both now call convertLibraryTrack in the batch controller, which
returns a LibraryTrackConversionResult that each caller maps to its own
reporting: batch failure summaries and log lines, or the screen's
existing snackbars.
Caller differences stay as parameters: the cover temp prefix
(batch_cover_ or convert_cover_), whether an empty SAF temp path fails,
the history SAF file name fallback, the screen's eager temp deletes, its
provider reload right after the row is written, its warnings, its cleanup
log text and its lazy artistTagMode read.
One deliberate difference: the screen now reads the track path, item and
audio fields once before the first await instead of re-reading widget state
after each await. Swiping to another track mid-conversion previously published
and recorded the output against the newly shown track; it now stays on the original.
The completion bridge maps, the precache set, the History row matcher and the
bodies of the queue lookup and History index listeners lived on the Library
tab State. CompletionBridgeTracker in lib/controllers now owns them. The two
ref.listen registrations stay in build at the same positions and call into
the tracker; the State forwards the old field names as getters so the part
files read the same collections.
Guards, the setState that drops removed items, the file check refresh order
(file cache, then playable probe), the offset reset and the mounted rebuild
after a History index change are unchanged.
The Library tab State held the page offsets, the load-more guard, the count
and page snapshot caches, the blank-library repair throttle and their trim
and scope rules inline. QueueLibraryPager in lib/controllers now owns that
state and logic; the State keeps one _pager field and forwards, passing
setState, mounted and the provider reloads as callbacks.
The page request and page data types implement two small interfaces so the
pager can compare scopes and test emptiness without seeing the private
types. Provider reads, page size, trim limits, the repair throttle and the
order of cache writes and setState calls are unchanged.
Search bucketing, refresh policy, folder data, history snapshot and the
selection and collapsing-header mixins hold no widgets, so they move to
controllers; the settings search catalog is pure presentation metadata and
moves to utils. Files with many importers keep a re-export in screens.
library_database_models and ffmpeg_models are plain data types, so they live in
models (re-exports stay at the old paths). deleted_library_files uses providers
and download_progress uses provider state, so both move to providers, and
ffmpeg_reenrich is a service. models no longer import from services for these.
Batch actions, clickable metadata, playback, auth launcher, action forms and
the glass warmup drive UI flows, so they belong with the controllers. Files
with more than three importers (or an importer that must stay untouched) keep
a one-line re-export at the old path.
The empty lyrics page offers Search online, which asks the enabled lyrics
providers in their configured order. The result is shown in the player
only and never written to the file; a late result is dropped after the
track changes, and a miss says so.
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.