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 0ee54e513..b73fbe642 100644 --- a/app/src/main/java/org/oxycblt/auxio/music/MusicRepository.kt +++ b/app/src/main/java/org/oxycblt/auxio/music/MusicRepository.kt @@ -39,6 +39,8 @@ import org.oxycblt.musikr.Storage import org.oxycblt.musikr.cache.Cache import org.oxycblt.musikr.cache.CacheDatabase import org.oxycblt.musikr.cover.StoredCovers +import org.oxycblt.musikr.playlist.db.PlaylistDatabase +import org.oxycblt.musikr.playlist.db.StoredPlaylists import org.oxycblt.musikr.tag.interpret.Naming import org.oxycblt.musikr.tag.interpret.Separators import timber.log.Timber as L @@ -213,6 +215,7 @@ constructor( private val musikr: Musikr, @ApplicationContext private val context: Context, private val cacheDatabase: CacheDatabase, + private val playlistDatabase: PlaylistDatabase, private val musicSettings: MusicSettings ) : MusicRepository { private val updateListeners = mutableListOf() @@ -361,10 +364,10 @@ constructor( val storage = if (withCache) { - Storage(Cache.full(cacheDatabase), StoredCovers.from(context, "covers")) + Storage(Cache.full(cacheDatabase), StoredCovers.from(context, "covers"), StoredPlaylists.from(playlistDatabase)) } else { - // TODO: Revisioned covers - Storage(Cache.writeOnly(cacheDatabase), StoredCovers.from(context, "covers")) + // TODO: Revisioned cache (as a stateful extension of musikr) + Storage(Cache.writeOnly(cacheDatabase), StoredCovers.from(context, "covers"), StoredPlaylists.from(playlistDatabase)) } val newLibrary = musikr.run( diff --git a/musikr/src/main/java/org/oxycblt/musikr/Config.kt b/musikr/src/main/java/org/oxycblt/musikr/Config.kt index 3885eb54c..637dba9cf 100644 --- a/musikr/src/main/java/org/oxycblt/musikr/Config.kt +++ b/musikr/src/main/java/org/oxycblt/musikr/Config.kt @@ -20,9 +20,10 @@ package org.oxycblt.musikr import org.oxycblt.musikr.cache.Cache import org.oxycblt.musikr.cover.StoredCovers +import org.oxycblt.musikr.playlist.db.StoredPlaylists import org.oxycblt.musikr.tag.interpret.Naming import org.oxycblt.musikr.tag.interpret.Separators -data class Storage(val cache: Cache, val storedCovers: StoredCovers) +data class Storage(val cache: Cache, val storedCovers: StoredCovers, val storedPlaylists: StoredPlaylists) data class Interpretation(val naming: Naming, val separators: Separators) diff --git a/musikr/src/main/java/org/oxycblt/musikr/Musikr.kt b/musikr/src/main/java/org/oxycblt/musikr/Musikr.kt index 4828b522e..5838ecf68 100644 --- a/musikr/src/main/java/org/oxycblt/musikr/Musikr.kt +++ b/musikr/src/main/java/org/oxycblt/musikr/Musikr.kt @@ -70,7 +70,7 @@ private class MusikrImpl( var extractedCount = 0 val explored = exploreStep - .explore(locations) + .explore(locations, storage) .buffer(Channel.UNLIMITED) .onStart { onProgress(IndexingProgress.Songs(0, 0)) } .onEach { onProgress(IndexingProgress.Songs(extractedCount, ++exploredCount)) } diff --git a/musikr/src/main/java/org/oxycblt/musikr/pipeline/ExploreStep.kt b/musikr/src/main/java/org/oxycblt/musikr/pipeline/ExploreStep.kt index a011d3f98..fd9499da5 100644 --- a/musikr/src/main/java/org/oxycblt/musikr/pipeline/ExploreStep.kt +++ b/musikr/src/main/java/org/oxycblt/musikr/pipeline/ExploreStep.kt @@ -15,42 +15,64 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ - + package org.oxycblt.musikr.pipeline import android.content.Context import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.asFlow +import kotlinx.coroutines.flow.buffer +import kotlinx.coroutines.flow.emitAll +import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.flowOn +import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.mapNotNull +import kotlinx.coroutines.flow.merge +import org.oxycblt.musikr.Storage import org.oxycblt.musikr.fs.MusicLocation import org.oxycblt.musikr.fs.DeviceFile import org.oxycblt.musikr.fs.query.DeviceFiles +import org.oxycblt.musikr.playlist.PlaylistFile +import org.oxycblt.musikr.playlist.db.StoredPlaylists import org.oxycblt.musikr.playlist.m3u.M3U internal interface ExploreStep { - fun explore(locations: List): Flow + fun explore(locations: List, storage: Storage): Flow companion object { - fun from(context: Context): ExploreStep = ExploreStepImpl(DeviceFiles.from(context)) + fun from(context: Context): ExploreStep = + ExploreStepImpl(DeviceFiles.from(context)) } } -private class ExploreStepImpl(private val deviceFiles: DeviceFiles) : ExploreStep { - override fun explore(locations: List) = - deviceFiles - .explore(locations.asFlow()) - .mapNotNull { - when { - it.mimeType == M3U.MIME_TYPE -> null - it.mimeType.startsWith("audio/") -> ExploreNode.Audio(it) - else -> null +private class ExploreStepImpl( + private val deviceFiles: DeviceFiles +) : ExploreStep { + override fun explore(locations: List, storage: Storage): Flow { + val audios = + deviceFiles + .explore(locations.asFlow()) + .mapNotNull { + when { + it.mimeType == M3U.MIME_TYPE -> null + it.mimeType.startsWith("audio/") -> ExploreNode.Audio(it) + else -> null + } } - } - .flowOn(Dispatchers.IO) + .flowOn(Dispatchers.IO) + .buffer() + val playlists = + flow { emitAll(storage.storedPlaylists.read().asFlow()) } + .map { ExploreNode.Playlist(it) } + .flowOn(Dispatchers.IO) + .buffer() + return merge(audios, playlists) + } } internal sealed interface ExploreNode { data class Audio(val file: DeviceFile) : ExploreNode + + data class Playlist(val file: PlaylistFile) : ExploreNode } diff --git a/musikr/src/main/java/org/oxycblt/musikr/playlist/PlaylistFile.kt b/musikr/src/main/java/org/oxycblt/musikr/playlist/PlaylistFile.kt index a973e2e68..231705cb7 100644 --- a/musikr/src/main/java/org/oxycblt/musikr/playlist/PlaylistFile.kt +++ b/musikr/src/main/java/org/oxycblt/musikr/playlist/PlaylistFile.kt @@ -24,7 +24,7 @@ import org.oxycblt.musikr.Song data class PlaylistFile( val name: String, val songPointers: List, - val editor: PlaylistHandle + val handle: PlaylistHandle ) sealed interface SongPointer { diff --git a/musikr/src/main/java/org/oxycblt/musikr/playlist/db/StoredPlaylistHandle.kt b/musikr/src/main/java/org/oxycblt/musikr/playlist/db/StoredPlaylistHandle.kt new file mode 100644 index 000000000..a025ed4df --- /dev/null +++ b/musikr/src/main/java/org/oxycblt/musikr/playlist/db/StoredPlaylistHandle.kt @@ -0,0 +1,36 @@ +package org.oxycblt.musikr.playlist.db + +import org.oxycblt.musikr.Music +import org.oxycblt.musikr.Song +import org.oxycblt.musikr.playlist.PlaylistHandle +import java.util.UUID + +internal class StoredPlaylistHandle( + private val playlistInfo: PlaylistInfo, + private val playlistDao: PlaylistDao +) : PlaylistHandle { + override val uid = playlistInfo.playlistUid + + override suspend fun rename(name: String) { + playlistDao.replacePlaylistInfo(playlistInfo.copy(name = name)) + } + + override suspend fun rewrite(songs: List) { + playlistDao.replacePlaylistSongs( + uid, + songs.map { PlaylistSong(it.uid) } + ) + } + + override suspend fun add(songs: List) { + playlistDao.insertPlaylistSongs( + uid, + songs.map { PlaylistSong(it.uid) } + ) + } + + override suspend fun delete() { + playlistDao.deletePlaylist(uid) + } + +} \ No newline at end of file diff --git a/musikr/src/main/java/org/oxycblt/musikr/playlist/db/StoredPlaylists.kt b/musikr/src/main/java/org/oxycblt/musikr/playlist/db/StoredPlaylists.kt index 8bcf39475..682cfaadc 100644 --- a/musikr/src/main/java/org/oxycblt/musikr/playlist/db/StoredPlaylists.kt +++ b/musikr/src/main/java/org/oxycblt/musikr/playlist/db/StoredPlaylists.kt @@ -18,15 +18,11 @@ package org.oxycblt.musikr.playlist.db -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.asFlow -import kotlinx.coroutines.flow.emitAll -import kotlinx.coroutines.flow.flow -import kotlinx.coroutines.flow.map import org.oxycblt.musikr.playlist.PlaylistFile +import org.oxycblt.musikr.playlist.SongPointer interface StoredPlaylists { - fun read(): Flow + suspend fun read(): List companion object { fun from(database: PlaylistDatabase): StoredPlaylists = @@ -35,5 +31,11 @@ interface StoredPlaylists { } private class StoredPlaylistsImpl(private val playlistDao: PlaylistDao) : StoredPlaylists { - override fun read() = flow { emitAll(playlistDao.readRawPlaylists().asFlow().map { TODO() }) } + override suspend fun read() = playlistDao.readRawPlaylists().map { + PlaylistFile( + it.playlistInfo.name, + it.songs.map { song -> SongPointer.UID(song.songUid) }, + StoredPlaylistHandle(it.playlistInfo, playlistDao) + ) + } }