Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Fix: Artist selection by storing artist name in URI

Problem

The artist() method queries by MediaStore artist ID, but artists() generates synthetic URIs. They don't match, causing artist selection to fail.

Solution

Store the artist name in the URI path so artist() can extract and use it directly.

Implementation

Replace the artists() method with:

override fun artists(
    providerIdentifier: ProviderIdentifier,
    sortingRule: SortingRule,
) = providersManager.flatMapWithInstanceOf(providerIdentifier) {
    // Get all audios to extract album artist information
    contentResolver.queryFlow(
        audiosUri,
        audiosProjection,
    ).mapEachRowToAudio().mapLatest { audios ->
        // Group by album artist, falling back to track artist
        val artistGroups = audios
            .groupBy { audio ->
                audio.albumArtist ?: audio.artistName ?: "Unknown Artist"
            }
        
        // Sort based on sorting rule
        val sortedArtistNames = when (sortingRule.strategy) {
            SortingStrategy.NAME -> {
                if (sortingRule.reverse) {
                    artistGroups.keys.sortedDescending()
                } else {
                    artistGroups.keys.sorted()
                }
            }
            else -> artistGroups.keys.sorted()
        }
        
        // Create Artist objects with custom URIs
        sortedArtistNames.map { artistName ->
            // Create a custom URI with artist name encoded
            val customUri = Uri.Builder()
                .scheme("content")
                .authority("org.lineageos.twelve.artist")
                .appendPath(artistName)
                .build()
            
            Artist.Builder(customUri)
                .setName(artistName)
                .build()
        }
    }.mapLatest {
        Result.Success(it)
    }
}

Replace the artist() method with:

override fun artist(artistUri: Uri) = withVolumeName(artistUri) { volumeName ->
    // Extract artist name from custom URI
    val artistName = artistUri.lastPathSegment ?: return@withVolumeName flowOf(
        Result.Error(Error.NOT_FOUND)
    )
    
    // Get all audios
    contentResolver.queryFlow(
        getAudiosUri(volumeName),
        audiosProjection,
    ).mapEachRowToAudio(volumeName).mapLatest { audios ->
        // Filter audios by album artist or track artist
        val matchingAudios = audios.filter { audio ->
            (audio.albumArtist ?: audio.artistName ?: "Unknown Artist") == artistName
        }
        
        if (matchingAudios.isEmpty()) {
            return@mapLatest Result.Error<Pair<Artist, ArtistWorks>, Error>(Error.NOT_FOUND)
        }
        
        // Get unique albums
        val albumIds = matchingAudios
            .map { it.albumUri }
            .distinct()
            .map { ContentUris.parseId(it).toString() }
        
        // Query albums
        contentResolver.queryFlow(
            getAlbumsUri(volumeName),
            albumsProjection,
            bundleOf(
                ContentResolver.QUERY_ARG_SQL_SELECTION to query {
                    MediaStore.Audio.AudioColumns._ID `in` List(albumIds.size) {
                        Query.ARG
                    }
                },
                ContentResolver.QUERY_ARG_SQL_SELECTION_ARGS to albumIds.toTypedArray(),
            )
        ).mapEachRowToAlbum(volumeName).mapLatest { albums ->
            val artist = Artist.Builder(artistUri)
                .setName(artistName)
                .build()
            
            val artistWorks = ArtistWorks(
                albums,
                listOf(),
                listOf(),
            )
            
            Result.Success(artist to artistWorks)
        }
    }.flatMapLatest { it }
}

How it works

  1. artists() creates URIs with the artist name in the path
  2. artist() extracts the artist name from the URI
  3. Filters audios by album artist (with fallback to track artist)
  4. Returns albums for that artist

Testing

After implementing:

  1. Rebuild APK
  2. Tap on an artist - should now display correctly
  3. Verify compilation albums are grouped
  4. Check that artist details show all albums