diff --git a/app/src/main/java/org/oxycblt/auxio/music/stack/interpret/Interpreter.kt b/app/src/main/java/org/oxycblt/auxio/music/stack/interpret/Interpreter.kt index f89eb9d2f..a3f46a731 100644 --- a/app/src/main/java/org/oxycblt/auxio/music/stack/interpret/Interpreter.kt +++ b/app/src/main/java/org/oxycblt/auxio/music/stack/interpret/Interpreter.kt @@ -26,6 +26,7 @@ import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.toList +import org.oxycblt.auxio.music.Music import org.oxycblt.auxio.music.stack.Indexer import org.oxycblt.auxio.music.stack.explore.AudioFile import org.oxycblt.auxio.music.stack.explore.PlaylistFile @@ -42,6 +43,7 @@ import org.oxycblt.auxio.music.stack.interpret.model.MutableLibrary import org.oxycblt.auxio.music.stack.interpret.model.SongImpl import org.oxycblt.auxio.music.stack.interpret.prepare.PreSong import org.oxycblt.auxio.music.stack.interpret.prepare.Preparer +import timber.log.Timber as L interface Interpreter { suspend fun interpret( @@ -88,8 +90,22 @@ class InterpreterImpl @Inject constructor(private val preparer: Preparer) : Inte .toList() val albums = albumLinker.resolve() - val songs = albumLinkedSongs.map { SongImpl(it) } - return LibraryImpl(songs, albums, artists, genres) + val uidMap = mutableMapOf() + val songs = albumLinkedSongs.mapNotNull { + val uid = it.preSong.computeUid() + val other = uidMap[uid] + if (other == null) { + SongImpl(it) + } else { + L.d("Song @ $uid already exists at ${other.path}, ignoring") + null + } + } + return LibraryImpl( + songs, + albums.onEach { it.finalize() }, + artists.onEach { it.finalize() }, + genres.onEach { it.finalize() }) } private data class LinkedSongImpl(private val albumLinkedSong: AlbumLinker.LinkedSong) : diff --git a/app/src/main/java/org/oxycblt/auxio/music/stack/interpret/model/DeviceMusicImpl.kt b/app/src/main/java/org/oxycblt/auxio/music/stack/interpret/model/DeviceMusicImpl.kt index ae16c744f..dd3d0e30b 100644 --- a/app/src/main/java/org/oxycblt/auxio/music/stack/interpret/model/DeviceMusicImpl.kt +++ b/app/src/main/java/org/oxycblt/auxio/music/stack/interpret/model/DeviceMusicImpl.kt @@ -43,23 +43,7 @@ import org.oxycblt.auxio.util.update class SongImpl(linkedSong: LinkedSong) : Song { private val preSong = linkedSong.preSong - override val uid = - // Attempt to use a MusicBrainz ID first before falling back to a hashed UID. - preSong.musicBrainzId?.let { Music.UID.musicBrainz(MusicType.SONGS, it) } - ?: Music.UID.auxio(MusicType.SONGS) { - // Song UIDs are based on the raw data without parsing so that they remain - // consistent across music setting changes. Parents are not held up to the - // same standard since grouping is already inherently linked to settings. - update(preSong.rawName) - update(preSong.preAlbum.rawName) - update(preSong.date) - - update(preSong.track) - update(preSong.disc?.number) - - update(preSong.preArtists.map { it.rawName }) - update(preSong.preAlbum.preArtists.map { it.rawName }) - } + override val uid = preSong.computeUid() override val name = preSong.name override val track = preSong.track override val disc = preSong.disc diff --git a/app/src/main/java/org/oxycblt/auxio/music/stack/interpret/model/Library.kt b/app/src/main/java/org/oxycblt/auxio/music/stack/interpret/model/Library.kt index 90ea8110f..f7c9e8863 100644 --- a/app/src/main/java/org/oxycblt/auxio/music/stack/interpret/model/Library.kt +++ b/app/src/main/java/org/oxycblt/auxio/music/stack/interpret/model/Library.kt @@ -47,6 +47,8 @@ class LibraryImpl( ) : MutableLibrary { override val playlists = emptySet() + private val songUidMap = songs.associ { it.uid } + override fun findSong(uid: Music.UID): Song? { TODO("Not yet implemented") } diff --git a/app/src/main/java/org/oxycblt/auxio/music/stack/interpret/prepare/PreMusic.kt b/app/src/main/java/org/oxycblt/auxio/music/stack/interpret/prepare/PreMusic.kt index ccb023ad5..e713037c3 100644 --- a/app/src/main/java/org/oxycblt/auxio/music/stack/interpret/prepare/PreMusic.kt +++ b/app/src/main/java/org/oxycblt/auxio/music/stack/interpret/prepare/PreMusic.kt @@ -21,6 +21,8 @@ package org.oxycblt.auxio.music.stack.interpret.prepare import android.net.Uri import java.util.UUID import org.oxycblt.auxio.image.extractor.Cover +import org.oxycblt.auxio.music.Music +import org.oxycblt.auxio.music.MusicType import org.oxycblt.auxio.music.info.Date import org.oxycblt.auxio.music.info.Disc import org.oxycblt.auxio.music.info.Name @@ -29,6 +31,7 @@ import org.oxycblt.auxio.music.stack.explore.PlaylistHandle import org.oxycblt.auxio.music.stack.explore.fs.MimeType import org.oxycblt.auxio.music.stack.explore.fs.Path import org.oxycblt.auxio.playback.replaygain.ReplayGainAdjustment +import org.oxycblt.auxio.util.update data class PreSong( val musicBrainzId: UUID?, @@ -48,7 +51,24 @@ data class PreSong( val preAlbum: PreAlbum, val preArtists: List, val preGenres: List -) +) { + fun computeUid() = + musicBrainzId?.let { Music.UID.musicBrainz(MusicType.SONGS, it) } + ?: Music.UID.auxio(MusicType.SONGS) { + // Song UIDs are based on the raw data without parsing so that they remain + // consistent across music setting changes. Parents are not held up to the + // same standard since grouping is already inherently linked to settings. + update(rawName) + update(preAlbum.rawName) + update(date) + + update(track) + update(disc?.number) + + update(preArtists.map { artist -> artist.rawName }) + update(preAlbum.preArtists.map { artist -> artist.rawName }) + } +} data class PreAlbum( val musicBrainzId: UUID?,