Skip to content

Commit 06005af

Browse files
committed
feat: release v4.0.1 — Deezer downloads, Spotify lyrics, clickable metadata, and notification love action
- Add Deezer as built-in download provider with fallback support - Add Spotify Lyrics API provider with rate limiting and cooldown handling - Add clickable artist/album name navigation across all screens - Add love/unlove toggle action in media notification controls - Add external LRC file writing for lyrics mode external/both - Propagate artistId/albumId through Track model, search, queue, and playback flows - Refactor playlist picker into stateful multi-select with explicit Done action - Harden CSV import with single-flight guard and safer dialog navigator scope - Switch Amazon API host to amzn.afkarxyz.fun with fallback - Remove Qobuz Jumo/legacy fallback path, simplify quality cascade - Pin CI Go toolchain to 1.25.7 - Fix Vorbis-to-ID3 tag mapping, update checker arm64 targeting, and queue touch handling
1 parent abca21f commit 06005af

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+1893
-717
lines changed

.github/workflows/release.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ jobs:
7171
- name: Setup Go
7272
uses: actions/setup-go@v6
7373
with:
74-
go-version: "1.26"
74+
go-version: "1.25.7"
7575
cache-dependency-path: go_backend/go.sum
7676

7777
# Cache Gradle for faster builds
@@ -174,7 +174,7 @@ jobs:
174174
- name: Setup Go
175175
uses: actions/setup-go@v6
176176
with:
177-
go-version: "1.26"
177+
go-version: "1.25.7"
178178
cache-dependency-path: go_backend/go.sum
179179

180180
# Cache CocoaPods

AndroidManifest.xml

16.5 KB
Binary file not shown.

CHANGELOG.md

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,34 @@
11
# Changelog
22

3+
## [4.0.1] - 2026-02-25
4+
5+
### Added
6+
- **Clickable Metadata Navigation**: Added reusable `ClickableArtistName` and `ClickableAlbumName`
7+
- **Love Action in Media Notification**: Added custom notification action (`toggle_love`) with new Android favorite/favorite-border status icons
8+
9+
### Changed
10+
11+
- **Track Metadata Model Expansion**: `Track` now carries `artistId` and `albumId`, propagated across search, queue, playback, CSV import, and extension mapping flows
12+
- **Full-Screen Player UX**: Top bar now supports swipe-down dismiss; artist/album text is now tappable; and in-player love toggle is available next to track metadata
13+
- **Playlist Picker Flow Refactor**: Reworked playlist picker sheet into stateful multi-select flow with explicit Done action and improved create-playlist handling
14+
- **CSV Import Interaction Flow**: Added single-flight import guard, more reliable progress dialog lifecycle, and safer local navigator usage
15+
- **Amazon API Host Fallback**: Amazon metadata fetch now tries `amazon.afkarxyz.fun` and falls back to `amzn.afkarxyz.fun`
16+
- **Qobuz URL Resolution Strategy**: Removed legacy/Jumo fallback path; now uses standard API pool (deeb)
17+
- **Update Checker Asset Targeting**: Update selection now prioritizes arm64/universal assets only
18+
- **Donate Page Supporters**: Updated highlighted donor/supporter list entries
19+
20+
### Fixed
21+
22+
- **FLAC External Lyrics Output**: External `.lrc` writing now works consistently for lyrics mode `external`/`both`, with SAF conversion paths avoiding duplicate writes
23+
- **Loved-State Notification Sync**: Playback notification controls now refresh correctly when loved state changes
24+
- **Queue Selection Touch Handling**: Selection overlays/check indicators no longer block tap gestures in queue and playlist selection modes
25+
- **Vorbis-to-ID3 Tag Mapping Robustness**: FFmpeg metadata conversion now normalizes keys and handles aliases like `TRCK` and `TPOS`
26+
- **Nested Dialog Navigation Safety**: Adjusted dialog navigator scope in CSV import and track-delete flows to prevent navigator mismatch issues
27+
- **Artist/Album Routing Reliability**: Track metadata routing now reuses resolved artist/album IDs across album/artist/home/search/queue/player surfaces
28+
- **Release Workflow Go Toolchain**: Pinned CI release workflow Go version to `1.25.7` for consistent build behavior
29+
30+
---
31+
332
## [4.0.0] - 2026-02-22
433

534
> **Major update warning:** This release introduces a large streaming-focused refactor and broad cross-app behavior changes.
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<vector xmlns:android="http://schemas.android.com/apk/res/android"
2+
android:width="24dp"
3+
android:height="24dp"
4+
android:viewportWidth="24"
5+
android:viewportHeight="24">
6+
<path
7+
android:fillColor="#FFFFFFFF"
8+
android:pathData="M12,21.35l-1.45,-1.32C5.4,15.36 2,12.28 2,8.5 2,5.42 4.42,3 7.5,3c1.74,0 3.41,0.81 4.5,2.09C13.09,3.81 14.76,3 16.5,3 19.58,3 22,5.42 22,8.5c0,3.78 -3.4,6.86 -8.55,11.54L12,21.35z"/>
9+
</vector>
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<vector xmlns:android="http://schemas.android.com/apk/res/android"
2+
android:width="24dp"
3+
android:height="24dp"
4+
android:viewportWidth="24"
5+
android:viewportHeight="24">
6+
<path
7+
android:fillColor="#FFFFFFFF"
8+
android:pathData="M16.5,3C14.76,3 13.09,3.81 12,5.09C10.91,3.81 9.24,3 7.5,3C4.42,3 2,5.42 2,8.5C2,12.28 5.4,15.36 10.55,20.03L12,21.35L13.45,20.03C18.6,15.36 22,12.28 22,8.5C22,5.42 19.58,3 16.5,3ZM12.1,18.55L12,18.65L11.9,18.55C7.14,14.24 4,11.39 4,8.5C4,6.5 5.5,5 7.5,5C9.04,5 10.54,5.99 11.07,7.36H12.94C13.46,5.99 14.96,5 16.5,5C18.5,5 20,6.5 20,8.5C20,11.39 16.86,14.24 12.1,18.55Z"/>
9+
</vector>

go_backend/amazon.go

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ type AfkarXYZResponse struct {
4545
} `json:"data"`
4646
}
4747

48-
// AmazonStreamResponse is the new response format from amazon.afkarxyz.fun/api/track/{asin}
48+
// AmazonStreamResponse is the new response format from amzn.afkarxyz.fun/api/track/{asin}
4949
type AmazonStreamResponse struct {
5050
StreamURL string `json:"streamUrl"`
5151
DecryptionKey string `json:"decryptionKey"`
@@ -179,7 +179,7 @@ func (a *AmazonDownloader) doAfkarXYZRequestNew(asin string) (string, string, st
179179
ctx, cancel := context.WithTimeout(context.Background(), amazonAPITimeoutMobile)
180180
defer cancel()
181181

182-
apiURL := fmt.Sprintf("https://amazon.afkarxyz.fun/api/track/%s", asin)
182+
apiURL := fmt.Sprintf("https://amzn.afkarxyz.fun/api/track/%s", asin)
183183
req, err := http.NewRequestWithContext(ctx, "GET", apiURL, nil)
184184
if err != nil {
185185
return "", "", "", fmt.Errorf("failed to create request: %w", err)
@@ -193,13 +193,13 @@ func (a *AmazonDownloader) doAfkarXYZRequestNew(asin string) (string, string, st
193193
}
194194
defer resp.Body.Close()
195195

196-
if resp.StatusCode != 200 {
197-
return "", "", "", fmt.Errorf("Amazon API returned status %d", resp.StatusCode)
196+
body, readErr := io.ReadAll(resp.Body)
197+
if readErr != nil {
198+
return "", "", "", fmt.Errorf("failed to read response: %w", readErr)
198199
}
199200

200-
body, err := io.ReadAll(resp.Body)
201-
if err != nil {
202-
return "", "", "", fmt.Errorf("failed to read response: %w", err)
201+
if resp.StatusCode != 200 {
202+
return "", "", "", fmt.Errorf("Amazon API returned status %d", resp.StatusCode)
203203
}
204204

205205
var apiResp AmazonStreamResponse
@@ -219,7 +219,7 @@ func (a *AmazonDownloader) doAfkarXYZRequestLegacy(amazonURL string) (string, st
219219
ctx, cancel := context.WithTimeout(context.Background(), amazonAPITimeoutMobile)
220220
defer cancel()
221221

222-
apiURL := "https://amazon.afkarxyz.fun/convert?url=" + url.QueryEscape(amazonURL)
222+
apiURL := "https://amzn.afkarxyz.fun/convert?url=" + url.QueryEscape(amazonURL)
223223
req, err := http.NewRequestWithContext(ctx, "GET", apiURL, nil)
224224
if err != nil {
225225
return "", "", "", fmt.Errorf("failed to create legacy request: %w", err)

go_backend/deezer.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,8 @@ func (c *DeezerClient) convertTrack(track deezerTrack) TrackMetadata {
235235
DiscNumber: track.DiskNumber,
236236
ExternalURL: track.Link,
237237
ISRC: track.ISRC,
238+
AlbumID: fmt.Sprintf("deezer:%d", track.Album.ID),
239+
ArtistID: fmt.Sprintf("deezer:%d", track.Artist.ID),
238240
}
239241
}
240242

0 commit comments

Comments
 (0)