diff --git a/CHANGELOG.md b/CHANGELOG.md index ec8b7254..9916536f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -334,7 +334,7 @@ Thank you for your understanding and continued support. This decision was made t - Routing priority: YouTube service -> extension fallback -> built-in fallback -> direct service - New Android method channel handler: `"downloadByStrategy"` -> `Gobackend.downloadByStrategy(...)` - SpotFetch metadata fallback integration for Spotify-blocked regions - - New backend client for `spotify.afkarxyz.fun/api` + - New backend client for `sp.afkarxyz.qzz.io/api` - Automatic fallback in Spotify metadata fetch path when primary source fails - Lyrics extraction now supports MP3 (ID3v2) and Opus/OGG (Vorbis comments) in addition to FLAC - Includes heuristic detection of lyrics stored in Comment fields @@ -349,7 +349,7 @@ Thank you for your understanding and continued support. This decision was made t - Legacy Dart bridge methods (`downloadTrack`, `downloadWithFallback`, `downloadWithExtensions`, `downloadFromYouTube`) are now thin wrappers and marked `@Deprecated` - Qobuz downloader updated to latest Jumo API contract (`/get` endpoint, required headers) - Amazon download flow now returns `decryption_key` from Go and performs decryption in Flutter (local file + SAF paths) -- Amazon now uses the new `amazon.afkarxyz.fun` API flow (ASIN-based track endpoint + legacy fallback) with encrypted stream support +- Amazon now uses the new `amzn.afkarxyz.qzz.io` API flow (ASIN-based track endpoint + legacy fallback) with encrypted stream support - Amazon ASIN extraction rewritten with robust URL/query-param parsing and regex fallback - Amazon provider re-enabled in download service picker and download settings (alongside Tidal, Qobuz, and YouTube picker flow) - Track Metadata cover UI now refreshes from the embedded file after Edit Metadata/Re-enrich, so the displayed art matches actual file tags diff --git a/go_backend/qobuz.go b/go_backend/qobuz.go index ec0489ff..e354f479 100644 --- a/go_backend/qobuz.go +++ b/go_backend/qobuz.go @@ -54,7 +54,7 @@ const ( qobuzDownloadAPIURL = "https://www.musicdl.me/api/qobuz/download" qobuzDabMusicAPIURL = "https://dabmusic.xyz/api/stream?trackId=" qobuzDeebAPIURL = "https://dab.yeet.su/api/stream?trackId=" - qobuzAfkarAPIURL = "https://qbz.afkarxyz.fun/api/track/" + qobuzAfkarAPIURL = "https://qbz.afkarxyz.qzz.io/api/track/" qobuzSquidAPIURL = "https://qobuz.squid.wtf/api/download-music?country=US&track_id=" qobuzDebugKeyXORMask = byte(0x5A) ) diff --git a/go_backend/spotfetch_api.go b/go_backend/spotfetch_api.go index d6bfab19..e12804a4 100644 --- a/go_backend/spotfetch_api.go +++ b/go_backend/spotfetch_api.go @@ -10,7 +10,7 @@ import ( "time" ) -const DefaultSpotFetchAPIBaseURL = "https://spotify.afkarxyz.fun/api" +const DefaultSpotFetchAPIBaseURL = "https://sp.afkarxyz.qzz.io/api" // GetSpotifyDataWithAPI fetches Spotify metadata through SpotFetch-compatible API. // This is used as a fallback when direct Spotify API access is blocked/limited. diff --git a/lib/screens/settings/download_settings_page.dart b/lib/screens/settings/download_settings_page.dart index b4a6d1a7..fb583df7 100644 --- a/lib/screens/settings/download_settings_page.dart +++ b/lib/screens/settings/download_settings_page.dart @@ -1310,8 +1310,27 @@ class _DownloadSettingsPageState extends ConsumerState { subtitle: Text(context.l10n.setupChooseFromFilesSubtitle), onTap: () async { Navigator.pop(ctx); + if (Platform.isIOS) { + await Future.delayed(const Duration(milliseconds: 250)); + } + // Note: iOS requires folder to have at least one file to be selectable - final result = await FilePicker.platform.getDirectoryPath(); + String? result; + try { + result = await FilePicker.platform.getDirectoryPath(); + } catch (e) { + if (ctx.mounted) { + ScaffoldMessenger.of(ctx).showSnackBar( + SnackBar( + content: Text('Failed to open folder picker: $e'), + backgroundColor: Theme.of(ctx).colorScheme.error, + duration: const Duration(seconds: 4), + ), + ); + } + return; + } + if (result != null) { // iOS: Validate the selected path is writable (not iCloud or container root) if (Platform.isIOS) { diff --git a/lib/screens/setup_screen.dart b/lib/screens/setup_screen.dart index 7c74d92e..6d12825d 100644 --- a/lib/screens/setup_screen.dart +++ b/lib/screens/setup_screen.dart @@ -321,7 +321,26 @@ class _SetupScreenState extends ConsumerState { title: Text(context.l10n.setupChooseFromFiles), onTap: () async { Navigator.pop(ctx); - final result = await FilePicker.platform.getDirectoryPath(); + if (Platform.isIOS) { + await Future.delayed(const Duration(milliseconds: 250)); + } + + String? result; + try { + result = await FilePicker.platform.getDirectoryPath(); + } catch (e) { + if (mounted) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text('Failed to open folder picker: $e'), + backgroundColor: Theme.of(context).colorScheme.error, + duration: const Duration(seconds: 4), + ), + ); + } + return; + } + if (result != null) { // iOS: Validate the selected path is writable if (Platform.isIOS) {