143 Commits
Author SHA1 Message Date
zarzet 92adce43ef refactor(native): share errorJson and retire Go-era names in native comments
MainActivitySafIo and SafDownloadHandler each defined an identical
errorJson. Keep one internal top-level function in MainActivitySafIo
and drop the private copy. errorJson, isMediaStoreUri, extFromFileName
and extFromMimeType never used their MainActivity receiver, so they
become plain top-level functions; call sites are unchanged.

Rust is the only backend. Rename the iOS invokeGoMethod, which
dispatches the OS-owned channel methods, to invokeOsMethod, and reword
the comments that still described Go calls, the Go progress stream, the
Go metadata reader or a "native migration boundary" so they describe
the current backend. Channel method names and reply keys are unchanged.
2026-10-18 23:01:04 +07:00
zarzet 8aca5e4ef2 refactor(android): drop unreachable dispatch cases and dead channel handlers
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.
2026-10-18 23:01:04 +07:00
zarzet ca950dc35a refactor(android): remove backend switches that became constants
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.
2026-10-18 23:01:04 +07:00
zarzet 70718b7ed1 refactor(ios): share locked backend manager snapshots 2026-10-18 00:46:53 +07:00
zarzet 0a9013c92e refactor(ios): simplify supported OAuth session handling 2026-10-18 00:46:09 +07:00
zarzet 11c6cf9915 feat(player): synchronize music haptics with local playback 2026-10-17 16:18:37 +07:00
zarzet 40f65d3a4d feat(player): show audio source processing and output details 2026-10-17 16:18:32 +07:00
zarzet 5caa2ed588 chore(flutter): update SDK and packages with glass and navigation compatibility 2026-10-11 14:32:03 +07:00
zarzet 00cff9b3c3 fix(ios): restore lock screen controls across app transitions 2026-10-09 18:08:12 +07:00
zarzet 2ded1cff68 perf(downloads): batch queue cancellation and progress cleanup 2026-10-08 08:47:47 +07:00
zarzet c67d2a2dd2 perf(downloads): batch cancellation resets for retry 2026-10-08 06:55:26 +07:00
zarzet d1de833184 perf(library): index scans and snapshots with native SQLite workers
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.
2026-10-06 23:29:33 +07:00
zarzet 867371cb67 feat(native): add cancellable data workers and streamed file hashing
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.
2026-10-06 23:25:39 +07:00
zarzet 7ebaef3df3 refactor(ios): remove unreachable legacy bridge routes 2026-10-04 01:41:56 +07:00
zarzet 5f0577837d perf(widget): publish artwork only for installed consumers 2026-10-04 01:41:55 +07:00
zarzet 04ee6bc6f2 fix(lyrics): grant scoped access to selected audio files
Open temporary media directory grants for reading, embedding and exporting lyrics on iOS and Android. Resolve native file aliases consistently while retaining extension storage restrictions.

Validate with 4 iOS integration tests, 42 Flutter tests, 6 Rust file capability tests, and Android Kotlin compilation. Refs #631.
2026-10-03 12:19:03 +07:00
zarzet 84cac92983 feat(network): add SMB and WebDAV libraries with streaming and uploads 2026-10-03 04:27:47 +07:00
zarzet 0b2cf0e4e5 feat(discord): add opt-in mobile rich presence with track artwork 2026-10-03 02:14:11 +07:00
zarzet 70f8bd7f35 feat(extensions): support personal account forms and direct downloads 2026-10-02 19:21:58 +07:00
zarzet fe36f08918 feat(backend): hold library scans in place while paused
Path and SAF scans wait at their next file checkpoint while paused and keep their position in memory. Cancel, backend shutdown and released request leases still end a paused scan.
2026-10-01 21:00:42 +07:00
zarzet 6e6d9dd00e fix(background): keep library scans and update downloads running with the screen off
Android froze the process once the activity stopped, because only track downloads held a foreground service. A dataSync BackgroundWorkService with a partial wake lock now covers library scans and in-app APK downloads, and stops when the last task ends or the system timeout fires. Its notification shows the existing localized progress, so no duplicate appears. If Android refuses the service, work continues as before.

On iOS a scan holds its own background task; on expiry it cancels the scan cleanly instead of being suspended.
2026-10-01 18:46:21 +07:00
zarzet fe88048db2 refactor(backend): retire unused native bridge entry points 2026-09-30 21:47:15 +07:00
zarzet c5789df87d fix(storage): retain document access for library metadata and covers 2026-09-30 21:42:53 +07:00
zarzet 8aa8d598d8 feat(lyrics): keep the screen awake while viewing lyrics 2026-09-30 20:21:21 +07:00
zarzet e3bec31b36 fix(library): preserve indexed tracks when a full scan is incomplete 2026-09-28 12:13:17 +07:00
zarzet 5259c86365 feat(player): add Mornye media notification controls
Sync notification favorites with Library, add Mornye transport icons and an output action on legacy Android, and expose the iOS favorite command. Preserve playback position when controls refresh.

Vendor audio_service 0.18.19 with a small Android patch for custom notification actions. Modern Android keeps its system output switcher and transport layout.
2026-09-26 03:07:38 +07:00
zarzet abb8dc4a42 feat(concerts): show artwork-themed event details in app 2026-09-25 12:00:28 +07:00
zarzet 1d6ff18d06 feat(player): add native audio output controls 2026-09-25 11:27:49 +07:00
zarzet 040c328479 feat(widgets): add native Android and iOS home screen players
Publish artwork, metadata, and transport state through a shared Dart bridge. Add configurable Android RemoteViews and iOS WidgetKit players with controls and links into the full player.

Serialize widget commands and session restoration, and share one headless-capable iOS Flutter engine with the foreground scene. Include native integration tests, Dart bridge tests, and widget setup documentation.
2026-09-25 01:22:58 +07:00
zarzet b7d4ceb7e1 Merge pull request #589 with lossless replacement safeguards
Keep automatic Hi-Res replacement off by default. Require exact full-duration PCM equivalence across every channel before and after processing. Retain the original through publication and history persistence, restore it on failure or cancellation, and bound WAV format parsing.

Validated with 30 Rust tests, 46 Dart tests, clean analyzer/Clippy checks, Android ARM64 and iOS builds, and iOS simulator startup.
2026-09-25 01:02:39 +07:00
Bartolo c16f14f75b Update AppDelegate.swift 2026-09-23 23:02:25 +02:00
Bartolo 14ce4bc868 Add Hi-Res authenticity check
Introduce a per-file Hi-Res authenticity checker and optional auto re-download for flagged fake Hi-Res downloads. Adds Rust checker modules (fft, evidence, FLAC/WAV decoding, tests) and a mobile API (check_hires_authenticity). Wire platform bindings for Android/iOS and PlatformBridge. Add Dart HiResCheckService, UI HiResCheckCard, settings toggle (redownloadFakeHiRes) and integration in the download pipeline to quarantine and replace suspicious files with LOSSLESS when safe. Adds claxon FLAC dependency.
2026-09-23 22:29:15 +02:00
Bartolo fb8a9d98ea Update Podfile.lock 2026-09-23 19:13:07 +02:00
Bartolo d957dde639 fix(ios): adopt the UIScene lifecycle required on iOS 27
On iOS 27 an app without a scene manifest launches to a blank launch
screen (and trips UIApplicationEvaluateRuntimeIssueForNoSceneLifecycleAdoption
under the debugger). Add the manifest with a FlutterSceneDelegate subclass.

Under scenes the app delegate has no window at launch, so the backend
method/event channels and plugins are now registered in
didInitializeImplicitFlutterEngine. SceneDelegate forwards what UIKit now
sends to the scene instead: extension OAuth/session-grant redirect URLs
and the background/foreground transitions that keep downloads alive. The
folder picker and ASWebAuthenticationSession anchor use the active scene's
window.
2026-09-23 18:34:28 +02:00
zarzet d5ac292d36 fix(ios): keep release feed bundle identity in sync
Use com.zarz.spotiflac for the app and matching test targets. Read the bundle identifier from the built IPA when updating apps.json so the release feed matches the distributed application.

Validation: Xcode project plist lint and binary IPA bundle identifier extraction passed.
2026-09-19 12:10:25 +07:00
zarzet 1ebdac8645 style: remove trailing whitespace 2026-09-18 00:31:27 +07:00
zarzet e7cf7e6335 chore(deps): update Flutter and iOS plugin dependencies 2026-09-17 02:59:14 +07:00
zarzet aa99726439 feat(backend): migrate production backend to Rust
Replace the Go backend with the Rust workspace and route Android/iOS through
UniFFI bindings. Include the migrated extension runtime, network, providers,
media metadata, downloads and library operations, with version 5.0.0+147.

Remove Go sources, adapters, native build selection and CI dependencies.
Build Rust unconditionally and lock the iOS Rust pod using a relative path.
Retain legacy source and migration evidence in a local ignored archive.

Validation: Android Kotlin compile and 53 native tests; Swift Rust-branch
equivalence and syntax; CocoaPods install; workflow, shell, Ruby, plist and
diff checks. Reuse the preceding 100 Rust tests, fmt/Clippy and five-ABI
build checkpoint; no new APK or iOS application build for this cleanup.

Known follow-up: URL/URLSearchParams globals are missing from the Rust JS
runtime. A controlled extension replay confirms a URL-resolution regression;
this commit does not fix that runtime gap.
2026-09-14 22:58:27 +07:00
zarzet 0e71003e12 test: decouple app fixtures from extension identities 2026-09-06 16:03:11 +07:00
zarzet b32bdbc291 perf(metadata): read complete SAF tags through descriptors
Add complete metadata reads with a display-name hint in Go and both native bridges. Read seekable SAF descriptors directly and use temporary copies only when direct reading fails.

Add format parity tests for MP3, FLAC, M4A, and WAV plus a Dart bridge hint regression.
2026-09-06 14:04:04 +07:00
zarzet 857d146c55 fix(ios): isolate download progress subscriptions
Give every subscription its own cursor, payload, and worker queue so an old blocked waiter cannot advance a replacement listener. Serialize lifecycle handling and guard cancellation.

Add a restart regression with an injected waiter; verified with Thread Sanitizer.
2026-09-06 14:03:49 +07:00
zarzet 83beccbb63 build(ios): require iOS 16 and restore local builds 2026-09-04 12:39:03 +07:00
zarzet 7be959e7e9 fix(extensions): preserve verification callback retries 2026-09-04 12:39:03 +07:00
zarzet e1e3ffe0b0 fix(ios): handle non-optional callback binding 2026-09-01 15:11:53 +07:00
zarzet f1fb01604d perf(download): reduce progress wakeups and harden retries 2026-08-29 23:47:58 +07:00
zarzet 63d29d2ed4 fix(security): validate extension callback state 2026-08-29 18:53:33 +07:00
zarzet 29db940d63 fix(security): encrypt extension storage at rest 2026-08-29 18:52:16 +07:00
zarzet 887a8ede12 feat(metadata): limit embedded cover resolution 2026-08-28 13:21:05 +07:00
zarzet 45d9bbd7b2 perf: harden download and persistence lifecycle 2026-08-28 12:51:03 +07:00
zarzet aafddde6bb fix(ios): handle library scan count output 2026-08-25 01:24:30 +07:00