music: refactor saf loader into new module
This commit is contained in:
parent
01a5e87a77
commit
b651a3be03
14 changed files with 100 additions and 13 deletions
|
@ -22,8 +22,7 @@ import java.util.UUID
|
|||
import org.oxycblt.auxio.music.Album
|
||||
import org.oxycblt.auxio.music.Music
|
||||
import org.oxycblt.auxio.music.Song
|
||||
import org.oxycblt.auxio.music.fs.DeviceFile
|
||||
import org.oxycblt.auxio.music.fs.Path
|
||||
import org.oxycblt.auxio.music.stack.fs.DeviceFile
|
||||
import org.oxycblt.auxio.music.info.Date
|
||||
import org.oxycblt.auxio.music.info.ReleaseType
|
||||
|
||||
|
|
|
@ -22,6 +22,10 @@ import dagger.Binds
|
|||
import dagger.Module
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import org.oxycblt.auxio.music.stack.extractor.ExoPlayerTagExtractor
|
||||
import org.oxycblt.auxio.music.stack.extractor.ExoPlayerTagExtractorImpl
|
||||
import org.oxycblt.auxio.music.stack.extractor.TagInterpreter2
|
||||
import org.oxycblt.auxio.music.stack.extractor.TagInterpreter2Impl
|
||||
|
||||
@Module
|
||||
@InstallIn(SingletonComponent::class)
|
||||
|
|
|
@ -26,6 +26,7 @@ import kotlin.math.min
|
|||
import org.oxycblt.auxio.image.extractor.CoverExtractor
|
||||
import org.oxycblt.auxio.music.device.RawSong
|
||||
import org.oxycblt.auxio.music.info.Date
|
||||
import org.oxycblt.auxio.music.stack.extractor.TextTags
|
||||
import org.oxycblt.auxio.util.nonZeroOrNull
|
||||
import timber.log.Timber as L
|
||||
|
||||
|
|
66
app/src/main/java/org/oxycblt/auxio/music/stack/Indexer.kt
Normal file
66
app/src/main/java/org/oxycblt/auxio/music/stack/Indexer.kt
Normal file
|
@ -0,0 +1,66 @@
|
|||
package org.oxycblt.auxio.music.stack
|
||||
|
||||
import android.net.Uri
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.coroutineScope
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.asFlow
|
||||
import kotlinx.coroutines.flow.buffer
|
||||
import kotlinx.coroutines.flow.filterIsInstance
|
||||
import kotlinx.coroutines.flow.flowOn
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.merge
|
||||
import kotlinx.coroutines.flow.shareIn
|
||||
import org.oxycblt.auxio.music.device.DeviceLibrary
|
||||
import org.oxycblt.auxio.music.device.RawSong
|
||||
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 javax.inject.Inject
|
||||
|
||||
interface Indexer {
|
||||
suspend fun run(uris: List<Uri>): DeviceLibrary
|
||||
}
|
||||
|
||||
class IndexerImpl @Inject constructor(
|
||||
private val deviceFiles: DeviceFiles,
|
||||
private val tagCache: TagCache,
|
||||
private val tagExtractor: ExoPlayerTagExtractor,
|
||||
private val interpreter: Interpreter
|
||||
) : Indexer {
|
||||
override suspend fun run(uris: List<Uri>) = coroutineScope {
|
||||
val deviceFiles = deviceFiles.explore(uris.asFlow())
|
||||
.flowOn(Dispatchers.IO)
|
||||
.buffer()
|
||||
val tagRead = tagCache.read(deviceFiles)
|
||||
.flowOn(Dispatchers.IO)
|
||||
.buffer()
|
||||
val (cacheFiles, cacheSongs) = tagRead.split()
|
||||
val tagExtractor =
|
||||
tagExtractor.process(cacheFiles)
|
||||
.flowOn(Dispatchers.IO)
|
||||
.buffer()
|
||||
val (_, extractorSongs) = tagExtractor.split()
|
||||
val sharedExtractorSongs = extractorSongs.shareIn(
|
||||
CoroutineScope(Dispatchers.Main),
|
||||
started = SharingStarted.WhileSubscribed(),
|
||||
replay = Int.MAX_VALUE
|
||||
)
|
||||
val tagWrite = async { tagCache.write(merge(cacheSongs, sharedExtractorSongs)) }
|
||||
val deviceLibrary = async { interpreter.interpret(sharedExtractorSongs) }.await()
|
||||
tagWrite.await()
|
||||
deviceLibrary
|
||||
}
|
||||
|
||||
private fun Flow<TagResult>.split(): Pair<Flow<DeviceFile>, Flow<RawSong>> {
|
||||
val files = filterIsInstance<TagResult.Miss>().map { it.file }
|
||||
val songs = filterIsInstance<TagResult.Hit>().map { it.rawSong }
|
||||
return files to songs
|
||||
}
|
||||
}
|
|
@ -16,7 +16,7 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.oxycblt.auxio.music.cache
|
||||
package org.oxycblt.auxio.music.stack.cache
|
||||
|
||||
import android.content.Context
|
||||
import androidx.room.Room
|
|
@ -1,11 +1,10 @@
|
|||
package org.oxycblt.auxio.music.cache
|
||||
package org.oxycblt.auxio.music.stack.cache
|
||||
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.flow
|
||||
import kotlinx.coroutines.flow.transform
|
||||
import org.oxycblt.auxio.music.device.RawSong
|
||||
import org.oxycblt.auxio.music.fs.DeviceFile
|
||||
import org.oxycblt.auxio.music.metadata.TagResult
|
||||
import org.oxycblt.auxio.music.stack.fs.DeviceFile
|
||||
import org.oxycblt.auxio.music.stack.extractor.TagResult
|
||||
import javax.inject.Inject
|
||||
|
||||
interface TagCache {
|
|
@ -16,7 +16,7 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.oxycblt.auxio.music.cache
|
||||
package org.oxycblt.auxio.music.stack.cache
|
||||
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Database
|
|
@ -1,4 +1,4 @@
|
|||
package org.oxycblt.auxio.music.metadata
|
||||
package org.oxycblt.auxio.music.stack.extractor
|
||||
|
||||
import android.os.HandlerThread
|
||||
import androidx.media3.common.MediaItem
|
||||
|
@ -9,7 +9,7 @@ import kotlinx.coroutines.flow.Flow
|
|||
import kotlinx.coroutines.flow.FlowCollector
|
||||
import kotlinx.coroutines.flow.flow
|
||||
import org.oxycblt.auxio.music.device.RawSong
|
||||
import org.oxycblt.auxio.music.fs.DeviceFile
|
||||
import org.oxycblt.auxio.music.stack.fs.DeviceFile
|
||||
import java.util.concurrent.Future
|
||||
import javax.inject.Inject
|
||||
import timber.log.Timber as L
|
|
@ -16,7 +16,7 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.oxycblt.auxio.music.metadata
|
||||
package org.oxycblt.auxio.music.stack.extractor
|
||||
|
||||
import androidx.core.text.isDigitsOnly
|
||||
import androidx.media3.exoplayer.MetadataRetriever
|
||||
|
@ -24,6 +24,8 @@ import javax.inject.Inject
|
|||
import org.oxycblt.auxio.image.extractor.CoverExtractor
|
||||
import org.oxycblt.auxio.music.device.RawSong
|
||||
import org.oxycblt.auxio.music.info.Date
|
||||
import org.oxycblt.auxio.music.metadata.parseId3v2PositionField
|
||||
import org.oxycblt.auxio.music.metadata.parseVorbisPositionField
|
||||
import org.oxycblt.auxio.util.nonZeroOrNull
|
||||
|
||||
/**
|
|
@ -16,12 +16,13 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.oxycblt.auxio.music.metadata
|
||||
package org.oxycblt.auxio.music.stack.extractor
|
||||
|
||||
import androidx.media3.common.Metadata
|
||||
import androidx.media3.extractor.metadata.id3.InternalFrame
|
||||
import androidx.media3.extractor.metadata.id3.TextInformationFrame
|
||||
import androidx.media3.extractor.metadata.vorbis.VorbisComment
|
||||
import org.oxycblt.auxio.music.metadata.correctWhitespace
|
||||
|
||||
/**
|
||||
* Processing wrapper for [Metadata] that allows organized access to text-based audio tags.
|
|
@ -16,7 +16,7 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.oxycblt.auxio.music.fs
|
||||
package org.oxycblt.auxio.music.stack.fs
|
||||
|
||||
import android.content.ContentResolver
|
||||
import android.content.Context
|
||||
|
@ -31,6 +31,9 @@ import kotlinx.coroutines.flow.emitAll
|
|||
import kotlinx.coroutines.flow.flatMapMerge
|
||||
import kotlinx.coroutines.flow.flattenMerge
|
||||
import kotlinx.coroutines.flow.flow
|
||||
import org.oxycblt.auxio.music.fs.Components
|
||||
import org.oxycblt.auxio.music.fs.contentResolverSafe
|
||||
import org.oxycblt.auxio.music.fs.useQuery
|
||||
|
||||
interface DeviceFiles {
|
||||
fun explore(uris: Flow<Uri>): Flow<DeviceFile>
|
|
@ -0,0 +1,9 @@
|
|||
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<RawSong>): DeviceLibrary
|
||||
}
|
|
@ -33,6 +33,8 @@ import org.junit.Assert.assertTrue
|
|||
import org.junit.Test
|
||||
import org.oxycblt.auxio.music.device.RawSong
|
||||
import org.oxycblt.auxio.music.info.Date
|
||||
import org.oxycblt.auxio.music.stack.cache.TagDao
|
||||
import org.oxycblt.auxio.music.stack.cache.Tags
|
||||
|
||||
class CacheRepositoryTest {
|
||||
@Test
|
||||
|
|
|
@ -27,6 +27,7 @@ import androidx.media3.extractor.metadata.vorbis.VorbisComment
|
|||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Test
|
||||
import org.oxycblt.auxio.music.stack.extractor.TextTags
|
||||
|
||||
class TextTagsTest {
|
||||
@Test
|
||||
|
|
Loading…
Reference in a new issue