music: cleanup

Clean up parts of the music loader.
This commit is contained in:
Alexander Capehart 2023-06-07 20:08:57 -06:00
parent 77d01a0c97
commit a9a6d1ccc1
No known key found for this signature in database
GPG key ID: 37DBE3621FE9AD47
3 changed files with 26 additions and 16 deletions

View file

@ -372,7 +372,6 @@ constructor(
// Do the initial query of the cache and media databases in parallel. // Do the initial query of the cache and media databases in parallel.
logD("Starting MediaStore query") logD("Starting MediaStore query")
val mediaStoreQueryJob = worker.scope.tryAsync { mediaStoreExtractor.query() } val mediaStoreQueryJob = worker.scope.tryAsync { mediaStoreExtractor.query() }
val userLibraryQueryJob = worker.scope.tryAsync { userLibraryFactory.query() }
val cache = val cache =
if (withCache) { if (withCache) {
logD("Reading cache") logD("Reading cache")
@ -431,6 +430,7 @@ constructor(
logD("Discovered ${rawSongs.size} songs, starting finalization") logD("Discovered ${rawSongs.size} songs, starting finalization")
emitLoading(IndexingProgress.Indeterminate) emitLoading(IndexingProgress.Indeterminate)
logD("Starting UserLibrary query") logD("Starting UserLibrary query")
val userLibraryQueryJob = worker.scope.tryAsync { userLibraryFactory.query() }
if (cache == null || cache.invalidated) { if (cache == null || cache.invalidated) {
logD("Writing cache [why=${cache?.invalidated}]") logD("Writing cache [why=${cache?.invalidated}]")
cacheRepository.writeCache(rawSongs) cacheRepository.writeCache(rawSongs)

View file

@ -97,6 +97,14 @@ interface DeviceLibrary {
/** Constructs a [DeviceLibrary] implementation in an asynchronous manner. */ /** Constructs a [DeviceLibrary] implementation in an asynchronous manner. */
interface Factory { interface Factory {
/**
* Creates a new [DeviceLibrary] instance asynchronously based on the incoming stream of
* [RawSong] instances.
*
* @param rawSongs A stream of [RawSong] instances to process.
* @param processedSongs A stream of [RawSong] instances that will have been processed by
* the instance.
*/
suspend fun create( suspend fun create(
rawSongs: Channel<RawSong>, rawSongs: Channel<RawSong>,
processedSongs: Channel<RawSong> processedSongs: Channel<RawSong>
@ -123,8 +131,11 @@ class DeviceLibraryFactoryImpl @Inject constructor(private val musicSettings: Mu
// causing severe issues elsewhere. // causing severe issues elsewhere.
if (songGrouping.containsKey(song.uid)) { if (songGrouping.containsKey(song.uid)) {
logW( logW(
"Duplicate song found: ${song.path} in " + "Duplicate song found: ${song.path} " +
"collides with ${unlikelyToBeNull(songGrouping[song.uid]).path}") "collides with ${unlikelyToBeNull(songGrouping[song.uid]).path}")
// We still want to say that we "processed" the song so that the user doesn't
// get confused at why the bar was only partly filled by the end of the loading
// process.
processedSongs.send(rawSong) processedSongs.send(rawSong)
continue continue
} }
@ -140,9 +151,9 @@ class DeviceLibraryFactoryImpl @Inject constructor(private val musicSettings: Mu
// use for album information to ensure consistent metadata and UIDs. Fall back to // use for album information to ensure consistent metadata and UIDs. Fall back to
// the name otherwise. // the name otherwise.
val trackLower = val trackLower =
song.track != null && (prioritized.track == null || song.track < prioritized.track) song.track != null &&
val nameLower = (prioritized.track == null || song.track < prioritized.track)
song.name < prioritized.name val nameLower = song.name < prioritized.name
if (trackLower || nameLower) { if (trackLower || nameLower) {
albumBody.raw = PrioritizedRaw(song.rawAlbum, song) albumBody.raw = PrioritizedRaw(song.rawAlbum, song)
} }
@ -192,8 +203,7 @@ class DeviceLibraryFactoryImpl @Inject constructor(private val musicSettings: Mu
// Now that all songs are processed, also process albums and group them into their // Now that all songs are processed, also process albums and group them into their
// respective artists. // respective artists.
val albums = val albums = albumGrouping.values.map { AlbumImpl(it.raw.inner, musicSettings, it.music) }
albumGrouping.values.map { AlbumImpl(it.raw.inner, musicSettings, it.music) }
for (album in albums) { for (album in albums) {
for (rawArtist in album.rawArtists) { for (rawArtist in album.rawArtists) {
val key = RawArtist.Key(rawArtist) val key = RawArtist.Key(rawArtist)
@ -204,13 +214,14 @@ class DeviceLibraryFactoryImpl @Inject constructor(private val musicSettings: Mu
// Immediately replace any songs that initially held the priority position. // Immediately replace any songs that initially held the priority position.
is SongImpl -> body.raw = PrioritizedRaw(rawArtist, album) is SongImpl -> body.raw = PrioritizedRaw(rawArtist, album)
is AlbumImpl -> { is AlbumImpl -> {
// Album information from later dates is prioritized, as it is more likely to // Album information from later dates is prioritized, as it is more
// likely to
// contain the "modern" name of the artist if the information really is // contain the "modern" name of the artist if the information really is
// in-consistent. Fall back to the name otherwise. // in-consistent. Fall back to the name otherwise.
val dateEarlier = val dateEarlier =
album.dates != null && (prioritized.dates == null || album.dates < prioritized.dates) album.dates != null &&
val nameLower = (prioritized.dates == null || album.dates < prioritized.dates)
album.name < prioritized.name val nameLower = album.name < prioritized.name
if (dateEarlier || nameLower) { if (dateEarlier || nameLower) {
body.raw = PrioritizedRaw(rawArtist, album) body.raw = PrioritizedRaw(rawArtist, album)
} }
@ -228,8 +239,7 @@ class DeviceLibraryFactoryImpl @Inject constructor(private val musicSettings: Mu
// Artists and genres do not need to be grouped and can be processed immediately. // Artists and genres do not need to be grouped and can be processed immediately.
val artists = val artists =
artistGrouping.values.map { ArtistImpl(it.raw.inner, musicSettings, it.music) } artistGrouping.values.map { ArtistImpl(it.raw.inner, musicSettings, it.music) }
val genres = val genres = genreGrouping.values.map { GenreImpl(it.raw.inner, musicSettings, it.music) }
genreGrouping.values.map { GenreImpl(it.raw.inner, musicSettings, it.music) }
return DeviceLibraryImpl(songGrouping.values, albums, artists, genres) return DeviceLibraryImpl(songGrouping.values, albums, artists, genres)
} }
@ -259,7 +269,7 @@ class DeviceLibraryImpl(
override fun hashCode() = songs.hashCode() override fun hashCode() = songs.hashCode()
override fun toString() = override fun toString() =
"DeviceLibrary(songs=${songs.size}, albums=${albums.size}, " + "DeviceLibrary(songs=${songs.size}, albums=${albums.size}, " +
"artists=${artists.size}, genres=${genres.size})" "artists=${artists.size}, genres=${genres.size})"
override fun findSong(uid: Music.UID): Song? = songUidMap[uid] override fun findSong(uid: Music.UID): Song? = songUidMap[uid]
override fun findAlbum(uid: Music.UID): Album? = albumUidMap[uid] override fun findAlbum(uid: Music.UID): Album? = albumUidMap[uid]

View file

@ -36,9 +36,9 @@ import org.oxycblt.auxio.util.logE
* is also not backed by library information, rather an app database with in-memory caching. It is * is also not backed by library information, rather an app database with in-memory caching. It is
* generally not expected to create this yourself, and instead rely on MusicRepository. * generally not expected to create this yourself, and instead rely on MusicRepository.
* *
* TODO: Communicate errors
*
* @author Alexander Capehart * @author Alexander Capehart
*
* TODO: Communicate errors
*/ */
interface UserLibrary { interface UserLibrary {
/** The current user-defined playlists. */ /** The current user-defined playlists. */