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.Album
|
||||||
import org.oxycblt.auxio.music.Music
|
import org.oxycblt.auxio.music.Music
|
||||||
import org.oxycblt.auxio.music.Song
|
import org.oxycblt.auxio.music.Song
|
||||||
import org.oxycblt.auxio.music.fs.DeviceFile
|
import org.oxycblt.auxio.music.stack.fs.DeviceFile
|
||||||
import org.oxycblt.auxio.music.fs.Path
|
|
||||||
import org.oxycblt.auxio.music.info.Date
|
import org.oxycblt.auxio.music.info.Date
|
||||||
import org.oxycblt.auxio.music.info.ReleaseType
|
import org.oxycblt.auxio.music.info.ReleaseType
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,10 @@ import dagger.Binds
|
||||||
import dagger.Module
|
import dagger.Module
|
||||||
import dagger.hilt.InstallIn
|
import dagger.hilt.InstallIn
|
||||||
import dagger.hilt.components.SingletonComponent
|
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
|
@Module
|
||||||
@InstallIn(SingletonComponent::class)
|
@InstallIn(SingletonComponent::class)
|
||||||
|
|
|
@ -26,6 +26,7 @@ import kotlin.math.min
|
||||||
import org.oxycblt.auxio.image.extractor.CoverExtractor
|
import org.oxycblt.auxio.image.extractor.CoverExtractor
|
||||||
import org.oxycblt.auxio.music.device.RawSong
|
import org.oxycblt.auxio.music.device.RawSong
|
||||||
import org.oxycblt.auxio.music.info.Date
|
import org.oxycblt.auxio.music.info.Date
|
||||||
|
import org.oxycblt.auxio.music.stack.extractor.TextTags
|
||||||
import org.oxycblt.auxio.util.nonZeroOrNull
|
import org.oxycblt.auxio.util.nonZeroOrNull
|
||||||
import timber.log.Timber as L
|
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/>.
|
* 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 android.content.Context
|
||||||
import androidx.room.Room
|
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.flow
|
|
||||||
import kotlinx.coroutines.flow.transform
|
import kotlinx.coroutines.flow.transform
|
||||||
import org.oxycblt.auxio.music.device.RawSong
|
import org.oxycblt.auxio.music.device.RawSong
|
||||||
import org.oxycblt.auxio.music.fs.DeviceFile
|
import org.oxycblt.auxio.music.stack.fs.DeviceFile
|
||||||
import org.oxycblt.auxio.music.metadata.TagResult
|
import org.oxycblt.auxio.music.stack.extractor.TagResult
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
interface TagCache {
|
interface TagCache {
|
|
@ -16,7 +16,7 @@
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* 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.Dao
|
||||||
import androidx.room.Database
|
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 android.os.HandlerThread
|
||||||
import androidx.media3.common.MediaItem
|
import androidx.media3.common.MediaItem
|
||||||
|
@ -9,7 +9,7 @@ import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.FlowCollector
|
import kotlinx.coroutines.flow.FlowCollector
|
||||||
import kotlinx.coroutines.flow.flow
|
import kotlinx.coroutines.flow.flow
|
||||||
import org.oxycblt.auxio.music.device.RawSong
|
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 java.util.concurrent.Future
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import timber.log.Timber as L
|
import timber.log.Timber as L
|
|
@ -16,7 +16,7 @@
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* 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.core.text.isDigitsOnly
|
||||||
import androidx.media3.exoplayer.MetadataRetriever
|
import androidx.media3.exoplayer.MetadataRetriever
|
||||||
|
@ -24,6 +24,8 @@ import javax.inject.Inject
|
||||||
import org.oxycblt.auxio.image.extractor.CoverExtractor
|
import org.oxycblt.auxio.image.extractor.CoverExtractor
|
||||||
import org.oxycblt.auxio.music.device.RawSong
|
import org.oxycblt.auxio.music.device.RawSong
|
||||||
import org.oxycblt.auxio.music.info.Date
|
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
|
import org.oxycblt.auxio.util.nonZeroOrNull
|
||||||
|
|
||||||
/**
|
/**
|
|
@ -16,12 +16,13 @@
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* 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.common.Metadata
|
||||||
import androidx.media3.extractor.metadata.id3.InternalFrame
|
import androidx.media3.extractor.metadata.id3.InternalFrame
|
||||||
import androidx.media3.extractor.metadata.id3.TextInformationFrame
|
import androidx.media3.extractor.metadata.id3.TextInformationFrame
|
||||||
import androidx.media3.extractor.metadata.vorbis.VorbisComment
|
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.
|
* 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/>.
|
* 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.ContentResolver
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
@ -31,6 +31,9 @@ import kotlinx.coroutines.flow.emitAll
|
||||||
import kotlinx.coroutines.flow.flatMapMerge
|
import kotlinx.coroutines.flow.flatMapMerge
|
||||||
import kotlinx.coroutines.flow.flattenMerge
|
import kotlinx.coroutines.flow.flattenMerge
|
||||||
import kotlinx.coroutines.flow.flow
|
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 {
|
interface DeviceFiles {
|
||||||
fun explore(uris: Flow<Uri>): Flow<DeviceFile>
|
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.junit.Test
|
||||||
import org.oxycblt.auxio.music.device.RawSong
|
import org.oxycblt.auxio.music.device.RawSong
|
||||||
import org.oxycblt.auxio.music.info.Date
|
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 {
|
class CacheRepositoryTest {
|
||||||
@Test
|
@Test
|
||||||
|
|
|
@ -27,6 +27,7 @@ import androidx.media3.extractor.metadata.vorbis.VorbisComment
|
||||||
import org.junit.Assert.assertEquals
|
import org.junit.Assert.assertEquals
|
||||||
import org.junit.Assert.assertTrue
|
import org.junit.Assert.assertTrue
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
|
import org.oxycblt.auxio.music.stack.extractor.TextTags
|
||||||
|
|
||||||
class TextTagsTest {
|
class TextTagsTest {
|
||||||
@Test
|
@Test
|
||||||
|
|
Loading…
Reference in a new issue