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.interpret.Interpretation
|
||||
import org.oxycblt.auxio.music.stack.interpret.model.MutableLibrary
|
||||
import kotlin.math.exp
|
||||
import timber.log.Timber as L
|
||||
|
||||
/**
|
||||
|
@ -366,23 +365,24 @@ constructor(private val indexer: Indexer, private val musicSettings: MusicSettin
|
|||
|
||||
var explored = 0
|
||||
var loaded = 0
|
||||
val newLibrary = indexer.run(listOf(), Interpretation(nameFactory, separators)) {
|
||||
when (it) {
|
||||
is Indexer.Event.Discovered -> {
|
||||
explored = it.amount
|
||||
emitIndexingProgress(IndexingProgress.Songs(loaded, explored))
|
||||
}
|
||||
is Indexer.Event.Extracted -> {
|
||||
loaded = it.amount
|
||||
emitIndexingProgress(IndexingProgress.Songs(loaded, explored))
|
||||
}
|
||||
is Indexer.Event.Interpret -> {
|
||||
if (explored == loaded) {
|
||||
emitIndexingProgress(IndexingProgress.Indeterminate)
|
||||
val newLibrary =
|
||||
indexer.run(listOf(), Interpretation(nameFactory, separators)) {
|
||||
when (it) {
|
||||
is Indexer.Event.Discovered -> {
|
||||
explored = it.amount
|
||||
emitIndexingProgress(IndexingProgress.Songs(loaded, explored))
|
||||
}
|
||||
is Indexer.Event.Extracted -> {
|
||||
loaded = it.amount
|
||||
emitIndexingProgress(IndexingProgress.Songs(loaded, explored))
|
||||
}
|
||||
is Indexer.Event.Interpret -> {
|
||||
if (explored == loaded) {
|
||||
emitIndexingProgress(IndexingProgress.Indeterminate)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We want to make sure that all reads and writes are synchronized due to the sheer
|
||||
// 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
|
||||
|
||||
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 {
|
||||
data class Discovered(
|
||||
val amount: Int,
|
||||
) : Event
|
||||
|
||||
data class Extracted(
|
||||
val amount: Int
|
||||
) : Event
|
||||
data class Extracted(val amount: Int) : Event
|
||||
|
||||
data class Interpret(
|
||||
val amount: Int
|
||||
) : Event
|
||||
data class Interpret(val amount: Int) : Event
|
||||
}
|
||||
}
|
||||
|
||||
class IndexerImpl
|
||||
@Inject
|
||||
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 audioFiles = files.audios.flowOn(Dispatchers.IO).buffer()
|
||||
val playlistFiles = files.playlists.flowOn(Dispatchers.IO).buffer()
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
package org.oxycblt.auxio.music.stack.explore
|
||||
|
||||
import android.net.Uri
|
||||
|
@ -60,13 +60,15 @@ constructor(
|
|||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
override fun explore(uris: List<Uri>, eventHandler: suspend (Indexer.Event) -> Unit): Files {
|
||||
var discovered = 0
|
||||
val deviceFiles = deviceFiles.explore(uris.asFlow())
|
||||
.onEach {
|
||||
discovered++
|
||||
eventHandler(Indexer.Event.Discovered(discovered))
|
||||
}
|
||||
.flowOn(Dispatchers.IO)
|
||||
.buffer()
|
||||
val deviceFiles =
|
||||
deviceFiles
|
||||
.explore(uris.asFlow())
|
||||
.onEach {
|
||||
discovered++
|
||||
eventHandler(Indexer.Event.Discovered(discovered))
|
||||
}
|
||||
.flowOn(Dispatchers.IO)
|
||||
.buffer()
|
||||
val tagRead = tagCache.read(deviceFiles).flowOn(Dispatchers.IO).buffer()
|
||||
val (uncachedDeviceFiles, cachedAudioFiles) = tagRead.results()
|
||||
val extractedAudioFiles =
|
||||
|
@ -77,10 +79,11 @@ constructor(
|
|||
.flattenMerge()
|
||||
val writtenAudioFiles = tagCache.write(extractedAudioFiles).flowOn(Dispatchers.IO).buffer()
|
||||
var loaded = 0
|
||||
val audioFiles = merge(cachedAudioFiles, writtenAudioFiles).onEach {
|
||||
loaded++
|
||||
eventHandler(Indexer.Event.Extracted(loaded))
|
||||
}
|
||||
val audioFiles =
|
||||
merge(cachedAudioFiles, writtenAudioFiles).onEach {
|
||||
loaded++
|
||||
eventHandler(Indexer.Event.Extracted(loaded))
|
||||
}
|
||||
val playlistFiles = storedPlaylists.read()
|
||||
return Files(audioFiles, playlistFiles)
|
||||
}
|
||||
|
@ -97,8 +100,7 @@ constructor(
|
|||
val indexed = withIndex()
|
||||
val shared =
|
||||
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 } }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -83,7 +83,7 @@ class InterpreterImpl @Inject constructor(private val preparer: Preparer) : Inte
|
|||
.register(artistLinkedSongs)
|
||||
.flowOn(Dispatchers.Main)
|
||||
.onEach {
|
||||
interpreted++;
|
||||
interpreted++
|
||||
eventHandler(Indexer.Event.Interpret(interpreted))
|
||||
}
|
||||
.map { LinkedSongImpl(it) }
|
||||
|
@ -91,16 +91,17 @@ class InterpreterImpl @Inject constructor(private val preparer: Preparer) : Inte
|
|||
val albums = albumLinker.resolve()
|
||||
|
||||
val uidMap = mutableMapOf<Music.UID, SongImpl>()
|
||||
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
|
||||
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() },
|
||||
|
|
|
@ -18,9 +18,6 @@
|
|||
|
||||
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.Music
|
||||
import org.oxycblt.auxio.music.Playlist
|
||||
|
@ -47,53 +44,43 @@ class LibraryImpl(
|
|||
) : MutableLibrary {
|
||||
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? {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
override fun findSong(uid: Music.UID) = songUidMap[uid]
|
||||
|
||||
override fun findSongByPath(path: Path): Song? {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
override fun findSongByPath(path: Path) = songs.find { it.path == path }
|
||||
|
||||
override fun findAlbum(uid: Music.UID): Album? {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
override fun findAlbum(uid: Music.UID) = albumUidMap[uid]
|
||||
|
||||
override fun findArtist(uid: Music.UID): Artist? {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
override fun findArtist(uid: Music.UID) = artistUidMap[uid]
|
||||
|
||||
override fun findGenre(uid: Music.UID): Genre? {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
override fun findGenre(uid: Music.UID) = genreUidMap[uid]
|
||||
|
||||
override fun findPlaylist(uid: Music.UID): Playlist? {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
override fun findPlaylist(uid: Music.UID) = playlistUidMap[uid]
|
||||
|
||||
override fun findPlaylistByName(name: String): Playlist? {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
override fun findPlaylistByName(name: String) = playlists.find { it.name.raw == name }
|
||||
|
||||
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 {
|
||||
TODO("Not yet implemented")
|
||||
return this
|
||||
}
|
||||
|
||||
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 {
|
||||
TODO("Not yet implemented")
|
||||
return this
|
||||
}
|
||||
|
||||
override suspend fun deletePlaylist(playlist: Playlist): MutableLibrary {
|
||||
TODO("Not yet implemented")
|
||||
return this
|
||||
}
|
||||
}
|
||||
|
|
|
@ -54,20 +54,20 @@ data class PreSong(
|
|||
) {
|
||||
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)
|
||||
?: 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(track)
|
||||
update(disc?.number)
|
||||
|
||||
update(preArtists.map { artist -> artist.rawName })
|
||||
update(preAlbum.preArtists.map { artist -> artist.rawName })
|
||||
}
|
||||
update(preArtists.map { artist -> artist.rawName })
|
||||
update(preAlbum.preArtists.map { artist -> artist.rawName })
|
||||
}
|
||||
}
|
||||
|
||||
data class PreAlbum(
|
||||
|
|
Loading…
Reference in a new issue