Repository navigation
Add "Hide duplicate songs" option to Music Management - #2877
ImanMontajabi wants to merge 3 commits into
Conversation
Cloud songs (Telegram, Netease, Navidrome) were shown next to the same track already on the device, e.g. after adding a Telegram channel whose songs were downloaded earlier. With the new option enabled, the cloud sync skips songs that match a higher-priority source (local > Telegram > Netease > Navidrome) by normalized title, duration and artist, and ignores file extensions and untagged artists. Co-authored-by: Cursor <cursoragent@cursor.com>
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
Unbounded artist substring matching can hide legitimate songs by different artists.
Review effort: Balanced
Findings: 1
What changed in this PR
Adds configurable cross-source song deduplication using normalized metadata and source priority.
Changes:
- Adds the setting and persisted preference.
- Filters Telegram, Netease, and Navidrome sync results.
- Adds duplicate-matching tests.
| File | Description |
|---|---|
DuplicateSongMatcherTest.kt |
Tests matching behavior. |
strings_settings.xml |
Adds setting labels. |
DuplicateSongMatcher.kt |
Implements duplicate detection. |
SettingsViewModel.kt |
Exposes setting and triggers resync. |
SettingsCategoryScreen.kt |
Adds the filtering switch. |
SyncWorker.kt |
Filters Telegram and Netease songs. |
UserPreferencesRepository.kt |
Persists the preference. |
NavidromeRepository.kt |
Filters Navidrome songs. |
MusicDao.kt |
Loads higher-priority song fingerprints. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| if (a == b || a.contains(b) || b.contains(a)) return true | ||
| // e.g. title "Alaki (BLH Remix)" with artist tag "BLH Remix". | ||
| return titleKey.isNotEmpty() && | ||
| (titleKey.contains(a) || titleKey.contains(b)) |
Substring checks let artists such as "Queen" and "Queens of the Stone Age" match, hiding legitimate songs. Artists are now split into credits and must share a full credit; the title fallback only considers bracketed segments like "(BLH Remix)". Co-authored-by: Cursor <cursoragent@cursor.com>
|
Addressed the Copilot review in b77dbb3. Artists are now compared by whole credits (split on feat./ft./x/&/,/etc.) and must share at least one full credit, so "Queen" no longer matches "Queens of the Stone Age". The title fallback only looks at bracketed segments such as "(BLH Remix)" instead of arbitrary containment. Added tests for both cases (14 matcher tests pass). |
The hide-duplicates setting now uses one filter for every cloud source, including the Netease path that could reinsert songs after SyncWorker hid them. Hidden copies move their favorite flag and playlist entries to the song that stays, and matching requires a second signal besides title so unknown artists without duration no longer collide. Co-authored-by: Cursor <cursoragent@cursor.com>
|
Follow-up cleanup in f0137c8 after a full review of the first implementation:
|

Summary
Song.mp3ignore the extension and the artist, since the artist is unreliable there.Notes
Test plan
DuplicateSongMatcherTestunit tests (10 cases) pass./gradlew :app:assembleDebugsucceedsMade with Cursor