music: re-add library find functionality

This commit is contained in:
Alexander Capehart 2024-11-26 10:08:07 -07:00
parent 9d9f810356
commit 0ba5ddce51
No known key found for this signature in database
GPG key ID: 37DBE3621FE9AD47
6 changed files with 83 additions and 89 deletions

View file

@ -34,7 +34,6 @@ import org.oxycblt.auxio.music.metadata.Separators
import org.oxycblt.auxio.music.stack.Indexer import org.oxycblt.auxio.music.stack.Indexer
import org.oxycblt.auxio.music.stack.interpret.Interpretation import org.oxycblt.auxio.music.stack.interpret.Interpretation
import org.oxycblt.auxio.music.stack.interpret.model.MutableLibrary import org.oxycblt.auxio.music.stack.interpret.model.MutableLibrary
import kotlin.math.exp
import timber.log.Timber as L import timber.log.Timber as L
/** /**
@ -366,23 +365,24 @@ constructor(private val indexer: Indexer, private val musicSettings: MusicSettin
var explored = 0 var explored = 0
var loaded = 0 var loaded = 0
val newLibrary = indexer.run(listOf(), Interpretation(nameFactory, separators)) { val newLibrary =
when (it) { indexer.run(listOf(), Interpretation(nameFactory, separators)) {
is Indexer.Event.Discovered -> { when (it) {
explored = it.amount is Indexer.Event.Discovered -> {
emitIndexingProgress(IndexingProgress.Songs(loaded, explored)) explored = it.amount
} emitIndexingProgress(IndexingProgress.Songs(loaded, explored))
is Indexer.Event.Extracted -> { }
loaded = it.amount is Indexer.Event.Extracted -> {
emitIndexingProgress(IndexingProgress.Songs(loaded, explored)) loaded = it.amount
} emitIndexingProgress(IndexingProgress.Songs(loaded, explored))
is Indexer.Event.Interpret -> { }
if (explored == loaded) { is Indexer.Event.Interpret -> {
emitIndexingProgress(IndexingProgress.Indeterminate) if (explored == loaded) {
emitIndexingProgress(IndexingProgress.Indeterminate)
}
} }
} }
} }
}
// We want to make sure that all reads and writes are synchronized due to the sheer // We want to make sure that all reads and writes are synchronized due to the sheer
// amount of consumers of MusicRepository. // amount of consumers of MusicRepository.

View file

@ -31,27 +31,31 @@ import org.oxycblt.auxio.music.stack.interpret.Interpreter
import org.oxycblt.auxio.music.stack.interpret.model.MutableLibrary import org.oxycblt.auxio.music.stack.interpret.model.MutableLibrary
interface Indexer { interface Indexer {
suspend fun run(uris: List<Uri>, interpretation: Interpretation, eventHandler: suspend (Event) -> Unit = {}): MutableLibrary suspend fun run(
uris: List<Uri>,
interpretation: Interpretation,
eventHandler: suspend (Event) -> Unit = {}
): MutableLibrary
sealed interface Event { sealed interface Event {
data class Discovered( data class Discovered(
val amount: Int, val amount: Int,
) : Event ) : Event
data class Extracted( data class Extracted(val amount: Int) : Event
val amount: Int
) : Event
data class Interpret( data class Interpret(val amount: Int) : Event
val amount: Int
) : Event
} }
} }
class IndexerImpl class IndexerImpl
@Inject @Inject
constructor(private val explorer: Explorer, private val interpreter: Interpreter) : Indexer { constructor(private val explorer: Explorer, private val interpreter: Interpreter) : Indexer {
override suspend fun run(uris: List<Uri>, interpretation: Interpretation, eventHandler: suspend (Event) -> Unit) = coroutineScope { override suspend fun run(
uris: List<Uri>,
interpretation: Interpretation,
eventHandler: suspend (Event) -> Unit
) = coroutineScope {
val files = explorer.explore(uris, eventHandler) val files = explorer.explore(uris, eventHandler)
val audioFiles = files.audios.flowOn(Dispatchers.IO).buffer() val audioFiles = files.audios.flowOn(Dispatchers.IO).buffer()
val playlistFiles = files.playlists.flowOn(Dispatchers.IO).buffer() val playlistFiles = files.playlists.flowOn(Dispatchers.IO).buffer()

View file

@ -60,13 +60,15 @@ constructor(
@OptIn(ExperimentalCoroutinesApi::class) @OptIn(ExperimentalCoroutinesApi::class)
override fun explore(uris: List<Uri>, eventHandler: suspend (Indexer.Event) -> Unit): Files { override fun explore(uris: List<Uri>, eventHandler: suspend (Indexer.Event) -> Unit): Files {
var discovered = 0 var discovered = 0
val deviceFiles = deviceFiles.explore(uris.asFlow()) val deviceFiles =
.onEach { deviceFiles
discovered++ .explore(uris.asFlow())
eventHandler(Indexer.Event.Discovered(discovered)) .onEach {
} discovered++
.flowOn(Dispatchers.IO) eventHandler(Indexer.Event.Discovered(discovered))
.buffer() }
.flowOn(Dispatchers.IO)
.buffer()
val tagRead = tagCache.read(deviceFiles).flowOn(Dispatchers.IO).buffer() val tagRead = tagCache.read(deviceFiles).flowOn(Dispatchers.IO).buffer()
val (uncachedDeviceFiles, cachedAudioFiles) = tagRead.results() val (uncachedDeviceFiles, cachedAudioFiles) = tagRead.results()
val extractedAudioFiles = val extractedAudioFiles =
@ -77,10 +79,11 @@ constructor(
.flattenMerge() .flattenMerge()
val writtenAudioFiles = tagCache.write(extractedAudioFiles).flowOn(Dispatchers.IO).buffer() val writtenAudioFiles = tagCache.write(extractedAudioFiles).flowOn(Dispatchers.IO).buffer()
var loaded = 0 var loaded = 0
val audioFiles = merge(cachedAudioFiles, writtenAudioFiles).onEach { val audioFiles =
loaded++ merge(cachedAudioFiles, writtenAudioFiles).onEach {
eventHandler(Indexer.Event.Extracted(loaded)) loaded++
} eventHandler(Indexer.Event.Extracted(loaded))
}
val playlistFiles = storedPlaylists.read() val playlistFiles = storedPlaylists.read()
return Files(audioFiles, playlistFiles) return Files(audioFiles, playlistFiles)
} }
@ -97,8 +100,7 @@ constructor(
val indexed = withIndex() val indexed = withIndex()
val shared = val shared =
indexed.shareIn( indexed.shareIn(
CoroutineScope(Dispatchers.Main), SharingStarted.WhileSubscribed(), replay = 0 CoroutineScope(Dispatchers.Main), SharingStarted.WhileSubscribed(), replay = 0)
)
return Array(n) { shared.filter { it.index % n == 0 }.map { it.value } } return Array(n) { shared.filter { it.index % n == 0 }.map { it.value } }
} }
} }

View file

@ -83,7 +83,7 @@ class InterpreterImpl @Inject constructor(private val preparer: Preparer) : Inte
.register(artistLinkedSongs) .register(artistLinkedSongs)
.flowOn(Dispatchers.Main) .flowOn(Dispatchers.Main)
.onEach { .onEach {
interpreted++; interpreted++
eventHandler(Indexer.Event.Interpret(interpreted)) eventHandler(Indexer.Event.Interpret(interpreted))
} }
.map { LinkedSongImpl(it) } .map { LinkedSongImpl(it) }
@ -91,16 +91,17 @@ class InterpreterImpl @Inject constructor(private val preparer: Preparer) : Inte
val albums = albumLinker.resolve() val albums = albumLinker.resolve()
val uidMap = mutableMapOf<Music.UID, SongImpl>() val uidMap = mutableMapOf<Music.UID, SongImpl>()
val songs = albumLinkedSongs.mapNotNull { val songs =
val uid = it.preSong.computeUid() albumLinkedSongs.mapNotNull {
val other = uidMap[uid] val uid = it.preSong.computeUid()
if (other == null) { val other = uidMap[uid]
SongImpl(it) if (other == null) {
} else { SongImpl(it)
L.d("Song @ $uid already exists at ${other.path}, ignoring") } else {
null L.d("Song @ $uid already exists at ${other.path}, ignoring")
null
}
} }
}
return LibraryImpl( return LibraryImpl(
songs, songs,
albums.onEach { it.finalize() }, albums.onEach { it.finalize() },

View file

@ -18,9 +18,6 @@
package org.oxycblt.auxio.music.stack.interpret.model package org.oxycblt.auxio.music.stack.interpret.model
import org.oxycblt.auxio.music.Album
import org.oxycblt.auxio.music.Artist
import org.oxycblt.auxio.music.Genre
import org.oxycblt.auxio.music.Library import org.oxycblt.auxio.music.Library
import org.oxycblt.auxio.music.Music import org.oxycblt.auxio.music.Music
import org.oxycblt.auxio.music.Playlist import org.oxycblt.auxio.music.Playlist
@ -47,53 +44,43 @@ class LibraryImpl(
) : MutableLibrary { ) : MutableLibrary {
override val playlists = emptySet<Playlist>() override val playlists = emptySet<Playlist>()
private val songUidMap = songs.associ { it.uid } private val songUidMap = songs.associateBy { it.uid }
private val albumUidMap = albums.associateBy { it.uid }
private val artistUidMap = artists.associateBy { it.uid }
private val genreUidMap = genres.associateBy { it.uid }
private val playlistUidMap = playlists.associateBy { it.uid }
override fun findSong(uid: Music.UID): Song? { override fun findSong(uid: Music.UID) = songUidMap[uid]
TODO("Not yet implemented")
}
override fun findSongByPath(path: Path): Song? { override fun findSongByPath(path: Path) = songs.find { it.path == path }
TODO("Not yet implemented")
}
override fun findAlbum(uid: Music.UID): Album? { override fun findAlbum(uid: Music.UID) = albumUidMap[uid]
TODO("Not yet implemented")
}
override fun findArtist(uid: Music.UID): Artist? { override fun findArtist(uid: Music.UID) = artistUidMap[uid]
TODO("Not yet implemented")
}
override fun findGenre(uid: Music.UID): Genre? { override fun findGenre(uid: Music.UID) = genreUidMap[uid]
TODO("Not yet implemented")
}
override fun findPlaylist(uid: Music.UID): Playlist? { override fun findPlaylist(uid: Music.UID) = playlistUidMap[uid]
TODO("Not yet implemented")
}
override fun findPlaylistByName(name: String): Playlist? { override fun findPlaylistByName(name: String) = playlists.find { it.name.raw == name }
TODO("Not yet implemented")
}
override suspend fun createPlaylist(name: String, songs: List<Song>): MutableLibrary { override suspend fun createPlaylist(name: String, songs: List<Song>): MutableLibrary {
TODO("Not yet implemented") return this
} }
override suspend fun renamePlaylist(playlist: Playlist, name: String): MutableLibrary { override suspend fun renamePlaylist(playlist: Playlist, name: String): MutableLibrary {
TODO("Not yet implemented") return this
} }
override suspend fun addToPlaylist(playlist: Playlist, songs: List<Song>): MutableLibrary { override suspend fun addToPlaylist(playlist: Playlist, songs: List<Song>): MutableLibrary {
TODO("Not yet implemented") return this
} }
override suspend fun rewritePlaylist(playlist: Playlist, songs: List<Song>): MutableLibrary { override suspend fun rewritePlaylist(playlist: Playlist, songs: List<Song>): MutableLibrary {
TODO("Not yet implemented") return this
} }
override suspend fun deletePlaylist(playlist: Playlist): MutableLibrary { override suspend fun deletePlaylist(playlist: Playlist): MutableLibrary {
TODO("Not yet implemented") return this
} }
} }

View file

@ -54,20 +54,20 @@ data class PreSong(
) { ) {
fun computeUid() = fun computeUid() =
musicBrainzId?.let { Music.UID.musicBrainz(MusicType.SONGS, it) } musicBrainzId?.let { Music.UID.musicBrainz(MusicType.SONGS, it) }
?: Music.UID.auxio(MusicType.SONGS) { ?: Music.UID.auxio(MusicType.SONGS) {
// Song UIDs are based on the raw data without parsing so that they remain // 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 // consistent across music setting changes. Parents are not held up to the
// same standard since grouping is already inherently linked to settings. // same standard since grouping is already inherently linked to settings.
update(rawName) update(rawName)
update(preAlbum.rawName) update(preAlbum.rawName)
update(date) update(date)
update(track) update(track)
update(disc?.number) update(disc?.number)
update(preArtists.map { artist -> artist.rawName }) update(preArtists.map { artist -> artist.rawName })
update(preAlbum.preArtists.map { artist -> artist.rawName }) update(preAlbum.preArtists.map { artist -> artist.rawName })
} }
} }
data class PreAlbum( data class PreAlbum(