From f76eafc9d461a9ee9e99ecba4f95ae3bc4d43cee Mon Sep 17 00:00:00 2001 From: Alexander Capehart Date: Tue, 19 Nov 2024 15:17:05 -0700 Subject: [PATCH] music: connect saf indexer to libraries Largely temporary, to be replaced with Interpreter --- .../oxycblt/auxio/music/MusicRepository.kt | 2 +- .../auxio/music/device/DeviceLibrary.kt | 48 +++++++++---------- .../auxio/music/device/DeviceMusicImpl.kt | 2 +- .../interpreter => metadata}/Separators.kt | 4 +- .../auxio/music/metadata/SeparatorsDialog.kt | 1 - .../org/oxycblt/auxio/music/stack/Indexer.kt | 22 ++++++--- .../music/stack/interpreter/Interpreter.kt | 9 ---- .../auxio/music/metadata/SeparatorsTest.kt | 1 - 8 files changed, 41 insertions(+), 48 deletions(-) rename app/src/main/java/org/oxycblt/auxio/music/{stack/interpreter => metadata}/Separators.kt (92%) delete mode 100644 app/src/main/java/org/oxycblt/auxio/music/stack/interpreter/Interpreter.kt diff --git a/app/src/main/java/org/oxycblt/auxio/music/MusicRepository.kt b/app/src/main/java/org/oxycblt/auxio/music/MusicRepository.kt index e2cbfd68b..34d21ed43 100644 --- a/app/src/main/java/org/oxycblt/auxio/music/MusicRepository.kt +++ b/app/src/main/java/org/oxycblt/auxio/music/MusicRepository.kt @@ -37,7 +37,7 @@ import org.oxycblt.auxio.music.cache.CacheRepository import org.oxycblt.auxio.music.device.DeviceLibrary import org.oxycblt.auxio.music.device.RawSong import org.oxycblt.auxio.music.info.Name -import org.oxycblt.auxio.music.stack.interpreter.Separators +import org.oxycblt.auxio.music.metadata.Separators import org.oxycblt.auxio.music.user.MutableUserLibrary import org.oxycblt.auxio.music.user.UserLibrary import org.oxycblt.auxio.util.DEFAULT_TIMEOUT diff --git a/app/src/main/java/org/oxycblt/auxio/music/device/DeviceLibrary.kt b/app/src/main/java/org/oxycblt/auxio/music/device/DeviceLibrary.kt index 121d93419..cbd4a272f 100644 --- a/app/src/main/java/org/oxycblt/auxio/music/device/DeviceLibrary.kt +++ b/app/src/main/java/org/oxycblt/auxio/music/device/DeviceLibrary.kt @@ -24,6 +24,7 @@ import android.provider.OpenableColumns import java.util.UUID import javax.inject.Inject import kotlinx.coroutines.channels.Channel +import kotlinx.coroutines.flow.Flow import org.oxycblt.auxio.music.Album import org.oxycblt.auxio.music.Artist import org.oxycblt.auxio.music.Genre @@ -35,7 +36,7 @@ import org.oxycblt.auxio.music.stack.fs.Path import org.oxycblt.auxio.music.stack.fs.contentResolverSafe import org.oxycblt.auxio.music.stack.fs.useQuery import org.oxycblt.auxio.music.info.Name -import org.oxycblt.auxio.music.stack.interpreter.Separators +import org.oxycblt.auxio.music.metadata.Separators import org.oxycblt.auxio.util.forEachWithTimeout import org.oxycblt.auxio.util.sendWithTimeout import org.oxycblt.auxio.util.unlikelyToBeNull @@ -123,8 +124,8 @@ interface DeviceLibrary { * the instance. */ suspend fun create( - rawSongs: Channel, - processedSongs: Channel, + rawSongs: Flow, + onSongProcessed: () -> Unit, separators: Separators, nameFactory: Name.Known.Factory ): DeviceLibraryImpl @@ -133,8 +134,8 @@ interface DeviceLibrary { class DeviceLibraryFactoryImpl @Inject constructor() : DeviceLibrary.Factory { override suspend fun create( - rawSongs: Channel, - processedSongs: Channel, + rawSongs: Flow, + onSongProcessed: () -> Unit, separators: Separators, nameFactory: Name.Known.Factory ): DeviceLibraryImpl { @@ -144,7 +145,7 @@ class DeviceLibraryFactoryImpl @Inject constructor() : DeviceLibrary.Factory { val genreGrouping = mutableMapOf>() // All music information is grouped as it is indexed by other components. - rawSongs.forEachWithTimeout { rawSong -> + rawSongs.collect { rawSong -> val song = SongImpl(rawSong, nameFactory, separators) // At times the indexer produces duplicate songs, try to filter these. Comparing by // UID is sufficient for something like this, and also prevents collisions from @@ -153,11 +154,8 @@ class DeviceLibraryFactoryImpl @Inject constructor() : DeviceLibrary.Factory { L.w( "Duplicate song found: ${song.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.sendWithTimeout(rawSong) - return@forEachWithTimeout + onSongProcessed() + return@collect } songGrouping[song.uid] = song @@ -180,7 +178,7 @@ class DeviceLibraryFactoryImpl @Inject constructor() : DeviceLibrary.Factory { appendToNameTree(song, rawGenre, genreGrouping) { old, new -> new.name < old.name } } - processedSongs.sendWithTimeout(rawSong) + onSongProcessed() } // Now that all songs are processed, also process albums and group them into their @@ -343,7 +341,7 @@ class DeviceLibraryImpl( ) : DeviceLibrary { // Use a mapping to make finding information based on it's UID much faster. private val songUidMap = buildMap { songs.forEach { put(it.uid, it.finalize()) } } - private val songPathMap = buildMap { songs.forEach { put(it.path, it) } } +// private val songPathMap = buildMap { songs.forEach { put(it.path, it) } } private val albumUidMap = buildMap { albums.forEach { put(it.uid, it.finalize()) } } private val artistUidMap = buildMap { artists.forEach { put(it.uid, it.finalize()) } } private val genreUidMap = buildMap { genres.forEach { put(it.uid, it.finalize()) } } @@ -365,17 +363,17 @@ class DeviceLibraryImpl( override fun findGenre(uid: Music.UID): Genre? = genreUidMap[uid] - override fun findSongByPath(path: Path) = songPathMap[path] + override fun findSongByPath(path: Path) = null - override fun findSongForUri(context: Context, uri: Uri) = - context.contentResolverSafe.useQuery( - uri, arrayOf(OpenableColumns.DISPLAY_NAME, OpenableColumns.SIZE)) { cursor -> - cursor.moveToFirst() - // We are weirdly limited to DISPLAY_NAME and SIZE when trying to locate a - // song. Do what we can to hopefully find the song the user wanted to open. - val displayName = - cursor.getString(cursor.getColumnIndexOrThrow(OpenableColumns.DISPLAY_NAME)) - val size = cursor.getLong(cursor.getColumnIndexOrThrow(OpenableColumns.SIZE)) - songs.find { it.path.name == displayName && it.size == size } - } + override fun findSongForUri(context: Context, uri: Uri) = null +// context.contentResolverSafe.useQuery( +// uri, arrayOf(OpenableColumns.DISPLAY_NAME, OpenableColumns.SIZE)) { cursor -> +// cursor.moveToFirst() +// // We are weirdly limited to DISPLAY_NAME and SIZE when trying to locate a +// // song. Do what we can to hopefully find the song the user wanted to open. +// val displayName = +// cursor.getString(cursor.getColumnIndexOrThrow(OpenableColumns.DISPLAY_NAME)) +// val size = cursor.getLong(cursor.getColumnIndexOrThrow(OpenableColumns.SIZE)) +// songs.find { it.path.name == displayName && it.size == size } +// } } diff --git a/app/src/main/java/org/oxycblt/auxio/music/device/DeviceMusicImpl.kt b/app/src/main/java/org/oxycblt/auxio/music/device/DeviceMusicImpl.kt index 2c2deb17a..71f0bea40 100644 --- a/app/src/main/java/org/oxycblt/auxio/music/device/DeviceMusicImpl.kt +++ b/app/src/main/java/org/oxycblt/auxio/music/device/DeviceMusicImpl.kt @@ -36,7 +36,7 @@ import org.oxycblt.auxio.music.info.Date import org.oxycblt.auxio.music.info.Disc import org.oxycblt.auxio.music.info.Name import org.oxycblt.auxio.music.info.ReleaseType -import org.oxycblt.auxio.music.stack.interpreter.Separators +import org.oxycblt.auxio.music.metadata.Separators import org.oxycblt.auxio.music.stack.extractor.parseId3GenreNames import org.oxycblt.auxio.playback.replaygain.ReplayGainAdjustment import org.oxycblt.auxio.util.positiveOrNull diff --git a/app/src/main/java/org/oxycblt/auxio/music/stack/interpreter/Separators.kt b/app/src/main/java/org/oxycblt/auxio/music/metadata/Separators.kt similarity index 92% rename from app/src/main/java/org/oxycblt/auxio/music/stack/interpreter/Separators.kt rename to app/src/main/java/org/oxycblt/auxio/music/metadata/Separators.kt index 27c542843..9713faede 100644 --- a/app/src/main/java/org/oxycblt/auxio/music/stack/interpreter/Separators.kt +++ b/app/src/main/java/org/oxycblt/auxio/music/metadata/Separators.kt @@ -1,7 +1,5 @@ -package org.oxycblt.auxio.music.stack.interpreter +package org.oxycblt.auxio.music.metadata -import org.oxycblt.auxio.music.metadata.CharSeparators -import org.oxycblt.auxio.music.metadata.NoSeparators import org.oxycblt.auxio.music.stack.extractor.correctWhitespace import org.oxycblt.auxio.music.stack.extractor.splitEscaped diff --git a/app/src/main/java/org/oxycblt/auxio/music/metadata/SeparatorsDialog.kt b/app/src/main/java/org/oxycblt/auxio/music/metadata/SeparatorsDialog.kt index 3dfd91c73..49cbca524 100644 --- a/app/src/main/java/org/oxycblt/auxio/music/metadata/SeparatorsDialog.kt +++ b/app/src/main/java/org/oxycblt/auxio/music/metadata/SeparatorsDialog.kt @@ -29,7 +29,6 @@ import org.oxycblt.auxio.BuildConfig import org.oxycblt.auxio.R import org.oxycblt.auxio.databinding.DialogSeparatorsBinding import org.oxycblt.auxio.music.MusicSettings -import org.oxycblt.auxio.music.stack.interpreter.Separators import org.oxycblt.auxio.ui.ViewBindingMaterialDialogFragment import timber.log.Timber as L diff --git a/app/src/main/java/org/oxycblt/auxio/music/stack/Indexer.kt b/app/src/main/java/org/oxycblt/auxio/music/stack/Indexer.kt index 326cfcdb9..981a977c3 100644 --- a/app/src/main/java/org/oxycblt/auxio/music/stack/Indexer.kt +++ b/app/src/main/java/org/oxycblt/auxio/music/stack/Indexer.kt @@ -14,27 +14,33 @@ import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.merge import kotlinx.coroutines.flow.shareIn +import kotlinx.coroutines.flow.toList import org.oxycblt.auxio.music.device.DeviceLibrary import org.oxycblt.auxio.music.device.RawSong +import org.oxycblt.auxio.music.info.Name +import org.oxycblt.auxio.music.metadata.Separators import org.oxycblt.auxio.music.stack.cache.TagCache import org.oxycblt.auxio.music.stack.extractor.ExoPlayerTagExtractor import org.oxycblt.auxio.music.stack.extractor.TagResult import org.oxycblt.auxio.music.stack.fs.DeviceFile import org.oxycblt.auxio.music.stack.fs.DeviceFiles -import org.oxycblt.auxio.music.stack.interpreter.Interpreter +import org.oxycblt.auxio.music.user.UserLibrary import javax.inject.Inject interface Indexer { - suspend fun run(uris: List): DeviceLibrary + suspend fun run(uris: List, separators: Separators, nameFactory: Name.Known.Factory): LibraryResult } +data class LibraryResult(val deviceLibrary: DeviceLibrary, val userLibrary: UserLibrary) + class IndexerImpl @Inject constructor( private val deviceFiles: DeviceFiles, private val tagCache: TagCache, private val tagExtractor: ExoPlayerTagExtractor, - private val interpreter: Interpreter + private val deviceLibraryFactory: DeviceLibrary.Factory, + private val userLibraryFactory: UserLibrary.Factory ) : Indexer { - override suspend fun run(uris: List) = coroutineScope { + override suspend fun run(uris: List, separators: Separators, nameFactory: Name.Known.Factory) = coroutineScope { val deviceFiles = deviceFiles.explore(uris.asFlow()) .flowOn(Dispatchers.IO) .buffer() @@ -52,10 +58,12 @@ class IndexerImpl @Inject constructor( started = SharingStarted.WhileSubscribed(), replay = Int.MAX_VALUE ) - val tagWrite = async { tagCache.write(merge(cacheSongs, sharedExtractorSongs)) } - val deviceLibrary = async { interpreter.interpret(sharedExtractorSongs) }.await() + val tagWrite = async(Dispatchers.IO) { tagCache.write(merge(cacheSongs, sharedExtractorSongs)) } + val rawPlaylists = async(Dispatchers.IO) { userLibraryFactory.query() } + val deviceLibrary = deviceLibraryFactory.create(merge(cacheSongs, sharedExtractorSongs), {}, separators, nameFactory) + val userLibrary = userLibraryFactory.create(rawPlaylists.await(), deviceLibrary, nameFactory) tagWrite.await() - deviceLibrary + LibraryResult(deviceLibrary, userLibrary) } private fun Flow.split(): Pair, Flow> { diff --git a/app/src/main/java/org/oxycblt/auxio/music/stack/interpreter/Interpreter.kt b/app/src/main/java/org/oxycblt/auxio/music/stack/interpreter/Interpreter.kt deleted file mode 100644 index fb2d42bae..000000000 --- a/app/src/main/java/org/oxycblt/auxio/music/stack/interpreter/Interpreter.kt +++ /dev/null @@ -1,9 +0,0 @@ -package org.oxycblt.auxio.music.stack.interpreter - -import kotlinx.coroutines.flow.Flow -import org.oxycblt.auxio.music.device.DeviceLibrary -import org.oxycblt.auxio.music.device.RawSong - -interface Interpreter { - suspend fun interpret(rawSong: Flow): DeviceLibrary -} \ No newline at end of file diff --git a/app/src/test/java/org/oxycblt/auxio/music/metadata/SeparatorsTest.kt b/app/src/test/java/org/oxycblt/auxio/music/metadata/SeparatorsTest.kt index da8b755c3..440f044e3 100644 --- a/app/src/test/java/org/oxycblt/auxio/music/metadata/SeparatorsTest.kt +++ b/app/src/test/java/org/oxycblt/auxio/music/metadata/SeparatorsTest.kt @@ -20,7 +20,6 @@ package org.oxycblt.auxio.music.metadata import org.junit.Assert.assertEquals import org.junit.Test -import org.oxycblt.auxio.music.stack.interpreter.Separators class SeparatorsTest { @Test