Skip to content

Multi artist support#548

Merged
theovilardo merged 22 commits intomasterfrom
multi-artist-support
Dec 17, 2025
Merged

Multi artist support#548
theovilardo merged 22 commits intomasterfrom
multi-artist-support

Conversation

@lostf1sh
Copy link
Copy Markdown
Collaborator

This pull request introduces comprehensive support for multi-artist relationships in the music database, enabling each song to have multiple associated artists and each artist to be linked with multiple songs. The migration is carefully handled to maintain backward compatibility and ensure efficient queries. The changes span database schema updates, model enhancements, and new DAO methods, as well as the addition of related user preferences.

Database schema and migration for multi-artist support:

  • Added a new SongArtistCrossRef junction table to represent the many-to-many relationship between songs and artists, including a flag for the primary artist. The database version is incremented, and a migration is provided to populate this table from existing data. (app/src/main/java/com/theveloper/pixelplay/data/database/SongArtistCrossRef.kt, app/src/main/java/com/theveloper/pixelplay/data/database/PixelPlayDatabase.kt) [1] [2] [3]
  • Updated the SongEntity model to add an albumArtist column and adjusted conversion functions to handle the new structure. (app/src/main/java/com/theveloper/pixelplay/data/database/SongEntity.kt) [1] [2] [3] [4] [5]
  • Enhanced the MusicDao interface with new methods for managing and querying the song-artist cross-references, including batch inserts, deletions, and retrievals for both songs and artists. Transactional methods are provided for bulk operations. (app/src/main/java/com/theveloper/pixelplay/data/database/MusicDao.kt)

Model and API enhancements:

  • Introduced the ArtistRef data class for simplified artist references and updated the Song model to include a list of ArtistRef objects, an albumArtist field, and helper properties for display and primary artist access. (app/src/main/java/com/theveloper/pixelplay/data/model/LibraryModels.kt, app/src/main/java/com/theveloper/pixelplay/data/model/Song.kt) [1] [2] [3]

User preferences for multi-artist features:

  • Added new preference keys to support multi-artist configuration, such as artist separation, delimiters, and grouping by album artist. (app/src/main/java/com/theveloper/pixelplay/data/preferences/UserPreferencesRepository.kt)

IDE and build configuration:

  • Updated Gradle settings for the project, including JVM and Gradle home configuration. (.idea/gradle.xml)

Database changes:
- Add SongArtistCrossRef junction table for many-to-many relationships
- Add SongWithArtists and ArtistWithSongs relation classes
- Add albumArtist field to SongEntity
- Add ArtistRef model and update Song model with artists list
- Database migration 9→10 with junction table creation

Preferences:
- Add ARTIST_SEPARATION_ENABLED for toggling artist splitting
- Add ARTIST_DELIMITERS for custom delimiter list (default: /, ;, ,, +, &)
- Add GROUP_BY_ALBUM_ARTIST for album organization
- Add ARTIST_SETTINGS_RESCAN_REQUIRED for pending library rescan

Parsing logic:
- Add splitArtistsByDelimiters() extension in Extensions.kt
- Backslash escape support (\\) to preserve delimiters in names

SyncWorker & DAO:
- Query ALBUM_ARTIST from MediaStore
- Split artists when enabled and create cross-references
- Batch insert cross-refs in chunks of 500
- Efficient queries with JOINs for artist-song relationships

UI Screens:
- ArtistSettingsScreen: Toggles, delimiter config nav, info/examples cards
- DelimiterConfigScreen: Add/remove delimiters with chips, reset option
- Rescan warning banner when settings change
- Navigation from Settings → Artists sub-screen
Copilot AI review requested due to automatic review settings December 16, 2025 20:57
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This pull request introduces comprehensive multi-artist support to the music player, allowing songs to have multiple associated artists through a many-to-many relationship. The implementation includes database schema updates with a junction table, intelligent artist parsing with configurable delimiters, and new UI screens for managing multi-artist settings.

Key changes:

  • Database migration (v9→v10) with SongArtistCrossRef junction table for many-to-many artist-song relationships
  • Artist name parsing with configurable delimiters (/, ;, ,, +, &) and escape sequence support
  • New settings screens for configuring multi-artist behavior and delimiter customization
  • Updated data models to support multiple artists per song while maintaining backward compatibility

Reviewed changes

Copilot reviewed 20 out of 21 changed files in this pull request and generated 15 comments.

Show a summary per file
File Description
Extensions.kt Adds splitArtistsByDelimiters() function with escape sequence support and joinArtistsForDisplay() helper
PlayerViewModel.kt Adds currentSongArtists StateFlow to expose all artists for the currently playing song
ArtistSettingsViewModel.kt New ViewModel managing multi-artist configuration, delimiters, and rescan requirements
SettingsScreen.kt Adds navigation entry to new Artist settings screen
DelimiterConfigScreen.kt New screen for managing artist delimiter configuration with add/remove functionality
ArtistSettingsScreen.kt New screen for multi-artist parsing settings, album artist grouping, and informational content
Screen.kt Adds routes for ArtistSettings and DelimiterConfig screens
AppNavigation.kt Registers navigation destinations for new artist configuration screens
FullPlayerContent.kt Adds artist picker bottom sheet for multi-artist songs and updates artist navigation
AppModule.kt Registers MIGRATION_9_10 in database builder
SyncWorker.kt Implements multi-artist preprocessing logic with delimiter-based splitting and junction table population
MusicRepositoryImpl.kt Adds getArtistsForSong() implementation and updates artist queries to use junction table
MusicRepository.kt Adds getArtistsForSong() interface method
UserPreferencesRepository.kt Adds preferences for artist separation, delimiters, album artist grouping, and rescan flags
Song.kt Extends model with artists list, albumArtist field, and convenience properties
LibraryModels.kt Adds ArtistRef data class for simplified artist references
SongEntity.kt Adds albumArtist column and toSongWithArtists() conversion function
SongArtistCrossRef.kt New junction table entity with supporting data classes for many-to-many queries
PixelPlayDatabase.kt Increments version to 10 and adds MIGRATION_9_10 for junction table creation
MusicDao.kt Adds extensive junction table query methods and transactional batch operations
.idea/gradle.xml Updates Gradle JVM and home configuration
Files not reviewed (1)
  • .idea/gradle.xml: Language not supported

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +106 to +111
if (current.size <= 1) return // Keep at least one delimiter

viewModelScope.launch {
userPreferencesRepository.setArtistDelimiters(current - delimiter)
}
}
Copy link

Copilot AI Dec 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment states 'Keep at least one delimiter', but the guard condition checks if size <= 1. This means if there's exactly 1 delimiter, the function returns early and doesn't remove it, which is correct. However, the logic in the UI (DelimiterConfigScreen lines 129-130) duplicates this check. If someone bypasses the ViewModel and calls setArtistDelimiters directly with an empty list, it would be allowed. Consider adding validation in setArtistDelimiters itself to ensure at least one delimiter is always maintained.

Copilot uses AI. Check for mistakes.
Comment on lines +162 to +182
songs.forEach { song ->
val artistsToProcess = if (artistSeparationEnabled) {
song.artistName.splitArtistsByDelimiters(artistDelimiters)
} else {
listOf(song.artistName)
}

artistsToProcess.forEach { artistName ->
val normalizedName = artistName.trim()
if (normalizedName.isNotEmpty() && !artistNameToId.containsKey(normalizedName)) {
// Check if this artist name matches the original artist in the song
// If so, use the original MediaStore ID
if (normalizedName == song.artistName) {
artistNameToId[normalizedName] = song.artistId
} else {
// Generate a new ID for split artists
artistNameToId[normalizedName] = nextArtistId.getAndIncrement()
}
}
}
}
Copy link

Copilot AI Dec 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the first pass through songs, you're calling splitArtistsByDelimiters for each song, and then calling it again for the same songs in the second pass. This duplicates the splitting work. Consider caching the split results from the first pass in a map (songId -> List of artist names) to avoid redundant string processing, especially for large libraries.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you fix that?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you fix that?

Comment on lines 297 to 298
@Suppress("unused")
private fun preProcessAndDeduplicate(songs: List<SongEntity>): Triple<List<SongEntity>, List<AlbumEntity>, List<ArtistEntity>> {
Copy link

Copilot AI Dec 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The @Suppress("unused") annotation on the legacy preProcessAndDeduplicate function suggests it's kept for reference only. If this function is truly unused and only kept as reference, consider removing it entirely to reduce code maintenance burden, or move it to documentation/comments if the implementation logic needs to be preserved for reference.

Copilot uses AI. Check for mistakes.
lostf1sh and others added 10 commits December 17, 2025 00:10
…ents/player/FullPlayerContent.kt

Co-authored-by: Copilot <[email protected]>
…ents/player/FullPlayerContent.kt

Co-authored-by: Copilot <[email protected]>
@lostf1sh lostf1sh requested a review from theovilardo December 16, 2025 21:17
@lostf1sh
Copy link
Copy Markdown
Collaborator Author

@copilot check again, i did the commits you requested.

Copy link
Copy Markdown
Contributor

Copilot AI commented Dec 16, 2025

@lostf1sh I've opened a new pull request, #549, to work on those changes. Once the pull request is ready, I'll request review from you.

Copy link
Copy Markdown
Owner

@theovilardo theovilardo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good

@theovilardo theovilardo merged commit 5e9afe5 into master Dec 17, 2025
@udaign
Copy link
Copy Markdown

udaign commented Jan 15, 2026

Great job! Thank you for this!

@lostf1sh lostf1sh deleted the multi-artist-support branch January 16, 2026 20:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants