music: re-add library find functionality
This commit is contained in:
parent
9d9f810356
commit
0ba5ddce51
6 changed files with 83 additions and 89 deletions
|
@ -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.
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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 } }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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() },
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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(
|
||||||
|
|
Loading…
Reference in a new issue