musikr: add date added support
Through a new `Tracker` interface. Tracker is kind of a generic name. It's set up in the case that I have to wind up associating more post-extraction metadata with songs.
This commit is contained in:
parent
c42ac644eb
commit
ca6388b28d
12 changed files with 158 additions and 29 deletions
|
@ -28,6 +28,7 @@ import dagger.hilt.components.SingletonComponent
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
import org.oxycblt.musikr.cache.Cache
|
import org.oxycblt.musikr.cache.Cache
|
||||||
import org.oxycblt.musikr.playlist.db.StoredPlaylists
|
import org.oxycblt.musikr.playlist.db.StoredPlaylists
|
||||||
|
import org.oxycblt.musikr.track.Tracker
|
||||||
|
|
||||||
@Module
|
@Module
|
||||||
@InstallIn(SingletonComponent::class)
|
@InstallIn(SingletonComponent::class)
|
||||||
|
@ -42,6 +43,8 @@ interface MusicModule {
|
||||||
class MusikrShimModule {
|
class MusikrShimModule {
|
||||||
@Singleton @Provides fun cache(@ApplicationContext context: Context) = Cache.from(context)
|
@Singleton @Provides fun cache(@ApplicationContext context: Context) = Cache.from(context)
|
||||||
|
|
||||||
|
@Singleton @Provides fun tracker(@ApplicationContext context: Context) = Tracker.from(context)
|
||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
@Provides
|
@Provides
|
||||||
fun storedPlaylists(@ApplicationContext context: Context) = StoredPlaylists.from(context)
|
fun storedPlaylists(@ApplicationContext context: Context) = StoredPlaylists.from(context)
|
||||||
|
|
|
@ -42,6 +42,7 @@ import org.oxycblt.musikr.cache.WriteOnlyCache
|
||||||
import org.oxycblt.musikr.playlist.db.StoredPlaylists
|
import org.oxycblt.musikr.playlist.db.StoredPlaylists
|
||||||
import org.oxycblt.musikr.tag.interpret.Naming
|
import org.oxycblt.musikr.tag.interpret.Naming
|
||||||
import org.oxycblt.musikr.tag.interpret.Separators
|
import org.oxycblt.musikr.tag.interpret.Separators
|
||||||
|
import org.oxycblt.musikr.track.Tracker
|
||||||
import timber.log.Timber as L
|
import timber.log.Timber as L
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -215,6 +216,7 @@ class MusicRepositoryImpl
|
||||||
constructor(
|
constructor(
|
||||||
@ApplicationContext private val context: Context,
|
@ApplicationContext private val context: Context,
|
||||||
private val cache: Cache,
|
private val cache: Cache,
|
||||||
|
private val tracker: Tracker,
|
||||||
private val storedPlaylists: StoredPlaylists,
|
private val storedPlaylists: StoredPlaylists,
|
||||||
private val musicSettings: MusicSettings
|
private val musicSettings: MusicSettings
|
||||||
) : MusicRepository {
|
) : MusicRepository {
|
||||||
|
@ -365,7 +367,7 @@ constructor(
|
||||||
val newRevision = currentRevision?.takeIf { withCache } ?: UUID.randomUUID()
|
val newRevision = currentRevision?.takeIf { withCache } ?: UUID.randomUUID()
|
||||||
val cache = if (withCache) cache else WriteOnlyCache(cache)
|
val cache = if (withCache) cache else WriteOnlyCache(cache)
|
||||||
val covers = MutableRevisionedStoredCovers(context, newRevision)
|
val covers = MutableRevisionedStoredCovers(context, newRevision)
|
||||||
val storage = Storage(cache, covers, storedPlaylists)
|
val storage = Storage(cache, tracker, covers, storedPlaylists)
|
||||||
val interpretation = Interpretation(nameFactory, separators)
|
val interpretation = Interpretation(nameFactory, separators)
|
||||||
|
|
||||||
val newLibrary =
|
val newLibrary =
|
||||||
|
|
|
@ -23,9 +23,11 @@ import org.oxycblt.musikr.cover.MutableStoredCovers
|
||||||
import org.oxycblt.musikr.playlist.db.StoredPlaylists
|
import org.oxycblt.musikr.playlist.db.StoredPlaylists
|
||||||
import org.oxycblt.musikr.tag.interpret.Naming
|
import org.oxycblt.musikr.tag.interpret.Naming
|
||||||
import org.oxycblt.musikr.tag.interpret.Separators
|
import org.oxycblt.musikr.tag.interpret.Separators
|
||||||
|
import org.oxycblt.musikr.track.Tracker
|
||||||
|
|
||||||
data class Storage(
|
data class Storage(
|
||||||
val cache: Cache,
|
val cache: Cache,
|
||||||
|
val tracker: Tracker,
|
||||||
val storedCovers: MutableStoredCovers,
|
val storedCovers: MutableStoredCovers,
|
||||||
val storedPlaylists: StoredPlaylists
|
val storedPlaylists: StoredPlaylists
|
||||||
)
|
)
|
||||||
|
|
|
@ -24,7 +24,7 @@ import org.oxycblt.musikr.playlist.interpret.PrePlaylist
|
||||||
import org.oxycblt.musikr.tag.interpret.PreAlbum
|
import org.oxycblt.musikr.tag.interpret.PreAlbum
|
||||||
import org.oxycblt.musikr.tag.interpret.PreArtist
|
import org.oxycblt.musikr.tag.interpret.PreArtist
|
||||||
import org.oxycblt.musikr.tag.interpret.PreGenre
|
import org.oxycblt.musikr.tag.interpret.PreGenre
|
||||||
import org.oxycblt.musikr.tag.interpret.PreSong
|
import org.oxycblt.musikr.track.TrackedSong
|
||||||
import org.oxycblt.musikr.util.unlikelyToBeNull
|
import org.oxycblt.musikr.util.unlikelyToBeNull
|
||||||
|
|
||||||
internal data class MusicGraph(
|
internal data class MusicGraph(
|
||||||
|
@ -35,7 +35,7 @@ internal data class MusicGraph(
|
||||||
val playlistVertex: Set<PlaylistVertex>
|
val playlistVertex: Set<PlaylistVertex>
|
||||||
) {
|
) {
|
||||||
interface Builder {
|
interface Builder {
|
||||||
fun add(preSong: PreSong)
|
fun add(trackedSong: TrackedSong)
|
||||||
|
|
||||||
fun add(prePlaylist: PrePlaylist)
|
fun add(prePlaylist: PrePlaylist)
|
||||||
|
|
||||||
|
@ -54,7 +54,8 @@ private class MusicGraphBuilderImpl : MusicGraph.Builder {
|
||||||
private val genreVertices = mutableMapOf<PreGenre, GenreVertex>()
|
private val genreVertices = mutableMapOf<PreGenre, GenreVertex>()
|
||||||
private val playlistVertices = mutableSetOf<PlaylistVertex>()
|
private val playlistVertices = mutableSetOf<PlaylistVertex>()
|
||||||
|
|
||||||
override fun add(preSong: PreSong) {
|
override fun add(trackedSong: TrackedSong) {
|
||||||
|
val preSong = trackedSong.preSong
|
||||||
val uid = preSong.uid
|
val uid = preSong.uid
|
||||||
if (songVertices.containsKey(uid)) {
|
if (songVertices.containsKey(uid)) {
|
||||||
return
|
return
|
||||||
|
@ -88,7 +89,7 @@ private class MusicGraphBuilderImpl : MusicGraph.Builder {
|
||||||
|
|
||||||
val songVertex =
|
val songVertex =
|
||||||
SongVertex(
|
SongVertex(
|
||||||
preSong,
|
trackedSong,
|
||||||
albumVertex,
|
albumVertex,
|
||||||
songArtistVertices.toMutableList(),
|
songArtistVertices.toMutableList(),
|
||||||
songGenreVertices.toMutableList())
|
songGenreVertices.toMutableList())
|
||||||
|
@ -311,7 +312,7 @@ private class MusicGraphBuilderImpl : MusicGraph.Builder {
|
||||||
}
|
}
|
||||||
|
|
||||||
internal class SongVertex(
|
internal class SongVertex(
|
||||||
val preSong: PreSong,
|
val trackedSong: TrackedSong,
|
||||||
var albumVertex: AlbumVertex,
|
var albumVertex: AlbumVertex,
|
||||||
var artistVertices: MutableList<ArtistVertex>,
|
var artistVertices: MutableList<ArtistVertex>,
|
||||||
var genreVertices: MutableList<GenreVertex>
|
var genreVertices: MutableList<GenreVertex>
|
||||||
|
|
|
@ -75,7 +75,7 @@ private class LibraryFactoryImpl() : LibraryFactory {
|
||||||
}
|
}
|
||||||
|
|
||||||
private class SongVertexCore(private val vertex: SongVertex) : SongCore {
|
private class SongVertexCore(private val vertex: SongVertex) : SongCore {
|
||||||
override val preSong = vertex.preSong
|
override val trackedSong = vertex.trackedSong
|
||||||
|
|
||||||
override fun resolveAlbum() = vertex.albumVertex.tag as Album
|
override fun resolveAlbum() = vertex.albumVertex.tag as Album
|
||||||
|
|
||||||
|
|
|
@ -22,10 +22,10 @@ import org.oxycblt.musikr.Album
|
||||||
import org.oxycblt.musikr.Artist
|
import org.oxycblt.musikr.Artist
|
||||||
import org.oxycblt.musikr.Genre
|
import org.oxycblt.musikr.Genre
|
||||||
import org.oxycblt.musikr.Song
|
import org.oxycblt.musikr.Song
|
||||||
import org.oxycblt.musikr.tag.interpret.PreSong
|
import org.oxycblt.musikr.track.TrackedSong
|
||||||
|
|
||||||
internal interface SongCore {
|
internal interface SongCore {
|
||||||
val preSong: PreSong
|
val trackedSong: TrackedSong
|
||||||
|
|
||||||
fun resolveAlbum(): Album
|
fun resolveAlbum(): Album
|
||||||
|
|
||||||
|
@ -40,7 +40,7 @@ internal interface SongCore {
|
||||||
* @author Alexander Capehart (OxygenCobalt)
|
* @author Alexander Capehart (OxygenCobalt)
|
||||||
*/
|
*/
|
||||||
internal class SongImpl(private val handle: SongCore) : Song {
|
internal class SongImpl(private val handle: SongCore) : Song {
|
||||||
private val preSong = handle.preSong
|
private val preSong = handle.trackedSong.preSong
|
||||||
|
|
||||||
override val uid = preSong.uid
|
override val uid = preSong.uid
|
||||||
override val name = preSong.name
|
override val name = preSong.name
|
||||||
|
@ -56,7 +56,7 @@ internal class SongImpl(private val handle: SongCore) : Song {
|
||||||
override val sampleRateHz = preSong.sampleRateHz
|
override val sampleRateHz = preSong.sampleRateHz
|
||||||
override val replayGainAdjustment = preSong.replayGainAdjustment
|
override val replayGainAdjustment = preSong.replayGainAdjustment
|
||||||
override val lastModified = preSong.lastModified
|
override val lastModified = preSong.lastModified
|
||||||
override val dateAdded = preSong.dateAdded
|
override val dateAdded = handle.trackedSong.dateAdded
|
||||||
override val cover = preSong.cover
|
override val cover = preSong.cover
|
||||||
override val album: Album
|
override val album: Album
|
||||||
get() = handle.resolveAlbum()
|
get() = handle.resolveAlbum()
|
||||||
|
|
|
@ -19,10 +19,12 @@
|
||||||
package org.oxycblt.musikr.pipeline
|
package org.oxycblt.musikr.pipeline
|
||||||
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
import kotlinx.coroutines.channels.Channel
|
import kotlinx.coroutines.channels.Channel
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.buffer
|
import kotlinx.coroutines.flow.buffer
|
||||||
import kotlinx.coroutines.flow.collect
|
import kotlinx.coroutines.flow.collect
|
||||||
|
import kotlinx.coroutines.flow.flattenMerge
|
||||||
import kotlinx.coroutines.flow.flowOn
|
import kotlinx.coroutines.flow.flowOn
|
||||||
import kotlinx.coroutines.flow.map
|
import kotlinx.coroutines.flow.map
|
||||||
import kotlinx.coroutines.flow.merge
|
import kotlinx.coroutines.flow.merge
|
||||||
|
@ -35,6 +37,7 @@ import org.oxycblt.musikr.model.LibraryFactory
|
||||||
import org.oxycblt.musikr.playlist.db.StoredPlaylists
|
import org.oxycblt.musikr.playlist.db.StoredPlaylists
|
||||||
import org.oxycblt.musikr.playlist.interpret.PlaylistInterpreter
|
import org.oxycblt.musikr.playlist.interpret.PlaylistInterpreter
|
||||||
import org.oxycblt.musikr.tag.interpret.TagInterpreter
|
import org.oxycblt.musikr.tag.interpret.TagInterpreter
|
||||||
|
import org.oxycblt.musikr.track.Tracker
|
||||||
|
|
||||||
internal interface EvaluateStep {
|
internal interface EvaluateStep {
|
||||||
suspend fun evaluate(extractedMusic: Flow<ExtractedMusic>): MutableLibrary
|
suspend fun evaluate(extractedMusic: Flow<ExtractedMusic>): MutableLibrary
|
||||||
|
@ -43,14 +46,17 @@ internal interface EvaluateStep {
|
||||||
fun new(storage: Storage, interpretation: Interpretation): EvaluateStep =
|
fun new(storage: Storage, interpretation: Interpretation): EvaluateStep =
|
||||||
EvaluateStepImpl(
|
EvaluateStepImpl(
|
||||||
TagInterpreter.new(interpretation),
|
TagInterpreter.new(interpretation),
|
||||||
|
storage.tracker,
|
||||||
PlaylistInterpreter.new(interpretation),
|
PlaylistInterpreter.new(interpretation),
|
||||||
storage.storedPlaylists,
|
storage.storedPlaylists,
|
||||||
LibraryFactory.new())
|
LibraryFactory.new())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalCoroutinesApi::class)
|
||||||
private class EvaluateStepImpl(
|
private class EvaluateStepImpl(
|
||||||
private val tagInterpreter: TagInterpreter,
|
private val tagInterpreter: TagInterpreter,
|
||||||
|
private val tracker: Tracker,
|
||||||
private val playlistInterpreter: PlaylistInterpreter,
|
private val playlistInterpreter: PlaylistInterpreter,
|
||||||
private val storedPlaylists: StoredPlaylists,
|
private val storedPlaylists: StoredPlaylists,
|
||||||
private val libraryFactory: LibraryFactory
|
private val libraryFactory: LibraryFactory
|
||||||
|
@ -69,6 +75,13 @@ private class EvaluateStepImpl(
|
||||||
.map { wrap(it, tagInterpreter::interpret) }
|
.map { wrap(it, tagInterpreter::interpret) }
|
||||||
.flowOn(Dispatchers.Default)
|
.flowOn(Dispatchers.Default)
|
||||||
.buffer(Channel.UNLIMITED)
|
.buffer(Channel.UNLIMITED)
|
||||||
|
val trackDistributedFlow = preSongs.distribute(8)
|
||||||
|
val trackedSongs =
|
||||||
|
merge(
|
||||||
|
trackDistributedFlow.manager,
|
||||||
|
trackDistributedFlow.flows
|
||||||
|
.map { flow -> flow.map { wrap(it, tracker::track) } }
|
||||||
|
.flattenMerge())
|
||||||
val prePlaylists =
|
val prePlaylists =
|
||||||
filterFlow.left
|
filterFlow.left
|
||||||
.map { wrap(it, playlistInterpreter::interpret) }
|
.map { wrap(it, playlistInterpreter::interpret) }
|
||||||
|
@ -78,7 +91,7 @@ private class EvaluateStepImpl(
|
||||||
val graphBuild =
|
val graphBuild =
|
||||||
merge(
|
merge(
|
||||||
filterFlow.manager,
|
filterFlow.manager,
|
||||||
preSongs.onEach { wrap(it, graphBuilder::add) },
|
trackedSongs.onEach { wrap(it, graphBuilder::add) },
|
||||||
prePlaylists.onEach { wrap(it, graphBuilder::add) })
|
prePlaylists.onEach { wrap(it, graphBuilder::add) })
|
||||||
graphBuild.collect()
|
graphBuild.collect()
|
||||||
val graph = graphBuilder.build()
|
val graph = graphBuilder.build()
|
||||||
|
|
|
@ -22,6 +22,7 @@ import org.oxycblt.musikr.fs.DeviceFile
|
||||||
import org.oxycblt.musikr.playlist.PlaylistFile
|
import org.oxycblt.musikr.playlist.PlaylistFile
|
||||||
import org.oxycblt.musikr.playlist.interpret.PrePlaylist
|
import org.oxycblt.musikr.playlist.interpret.PrePlaylist
|
||||||
import org.oxycblt.musikr.tag.interpret.PreSong
|
import org.oxycblt.musikr.tag.interpret.PreSong
|
||||||
|
import org.oxycblt.musikr.track.TrackedSong
|
||||||
|
|
||||||
class PipelineException(val processing: WhileProcessing, val error: Exception) : Exception() {
|
class PipelineException(val processing: WhileProcessing, val error: Exception) : Exception() {
|
||||||
override val cause = error
|
override val cause = error
|
||||||
|
@ -46,6 +47,11 @@ sealed interface WhileProcessing {
|
||||||
override fun toString() = "Pre Song @ ${preSong.path}"
|
override fun toString() = "Pre Song @ ${preSong.path}"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class ATrackedSong internal constructor(private val trackedSong: TrackedSong) :
|
||||||
|
WhileProcessing {
|
||||||
|
override fun toString() = "Tracked Song @ ${trackedSong.preSong.path}"
|
||||||
|
}
|
||||||
|
|
||||||
class APrePlaylist internal constructor(private val prePlaylist: PrePlaylist) :
|
class APrePlaylist internal constructor(private val prePlaylist: PrePlaylist) :
|
||||||
WhileProcessing {
|
WhileProcessing {
|
||||||
override fun toString() = "Pre Playlist @ ${prePlaylist.name}"
|
override fun toString() = "Pre Playlist @ ${prePlaylist.name}"
|
||||||
|
@ -80,6 +86,13 @@ internal suspend fun <R> wrap(song: PreSong, block: suspend (PreSong) -> R): R =
|
||||||
throw PipelineException(WhileProcessing.APreSong(song), e)
|
throw PipelineException(WhileProcessing.APreSong(song), e)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal suspend fun <R> wrap(song: TrackedSong, block: suspend (TrackedSong) -> R): R =
|
||||||
|
try {
|
||||||
|
block(song)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
throw PipelineException(WhileProcessing.ATrackedSong(song), e)
|
||||||
|
}
|
||||||
|
|
||||||
internal suspend fun <R> wrap(playlist: PrePlaylist, block: suspend (PrePlaylist) -> R): R =
|
internal suspend fun <R> wrap(playlist: PrePlaylist, block: suspend (PrePlaylist) -> R): R =
|
||||||
try {
|
try {
|
||||||
block(playlist)
|
block(playlist)
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* 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.musikr.tag.interpret
|
package org.oxycblt.musikr.tag.interpret
|
||||||
|
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
|
@ -47,27 +47,27 @@ internal data class PreSong(
|
||||||
val sampleRateHz: Int,
|
val sampleRateHz: Int,
|
||||||
val replayGainAdjustment: ReplayGainAdjustment,
|
val replayGainAdjustment: ReplayGainAdjustment,
|
||||||
val lastModified: Long,
|
val lastModified: Long,
|
||||||
val dateAdded: Long,
|
|
||||||
val cover: Cover?,
|
val cover: Cover?,
|
||||||
val preAlbum: PreAlbum,
|
val preAlbum: PreAlbum,
|
||||||
val preArtists: List<PreArtist>,
|
val preArtists: List<PreArtist>,
|
||||||
val preGenres: List<PreGenre>
|
val preGenres: List<PreGenre>
|
||||||
) {
|
) {
|
||||||
val uid = musicBrainzId?.let { Music.UID.musicBrainz(Music.UID.Item.SONG, it) }
|
val uid =
|
||||||
?: Music.UID.auxio(Music.UID.Item.SONG) {
|
musicBrainzId?.let { Music.UID.musicBrainz(Music.UID.Item.SONG, it) }
|
||||||
// Song UIDs are based on the raw data without parsing so that they remain
|
?: Music.UID.auxio(Music.UID.Item.SONG) {
|
||||||
// consistent across music setting changes. Parents are not held up to the
|
// Song UIDs are based on the raw data without parsing so that they remain
|
||||||
// same standard since grouping is already inherently linked to settings.
|
// consistent across music setting changes. Parents are not held up to the
|
||||||
update(rawName)
|
// same standard since grouping is already inherently linked to settings.
|
||||||
update(preAlbum.rawName)
|
update(rawName)
|
||||||
update(date)
|
update(preAlbum.rawName)
|
||||||
|
update(date)
|
||||||
|
|
||||||
update(track)
|
update(track)
|
||||||
update(disc?.number)
|
update(disc?.number)
|
||||||
|
|
||||||
update(preArtists.map { artist -> artist.rawName })
|
update(preArtists.map { artist -> artist.rawName })
|
||||||
update(preAlbum.preArtists.map { artist -> artist.rawName })
|
update(preAlbum.preArtists.map { artist -> artist.rawName })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal data class PreAlbum(
|
internal data class PreAlbum(
|
||||||
|
|
|
@ -65,8 +65,6 @@ private class TagInterpreterImpl(private val interpretation: Interpretation) : T
|
||||||
size = song.file.size,
|
size = song.file.size,
|
||||||
format = Format.infer(song.file.mimeType, song.properties.mimeType),
|
format = Format.infer(song.file.mimeType, song.properties.mimeType),
|
||||||
lastModified = song.file.lastModified,
|
lastModified = song.file.lastModified,
|
||||||
// TODO: Figure out what to do with date added
|
|
||||||
dateAdded = song.file.lastModified,
|
|
||||||
musicBrainzId = song.tags.musicBrainzId?.toUuidOrNull(),
|
musicBrainzId = song.tags.musicBrainzId?.toUuidOrNull(),
|
||||||
name = interpretation.naming.name(song.tags.name, song.tags.sortName),
|
name = interpretation.naming.name(song.tags.name, song.tags.sortName),
|
||||||
rawName = song.tags.name,
|
rawName = song.tags.name,
|
||||||
|
|
43
musikr/src/main/java/org/oxycblt/musikr/track/Tracker.kt
Normal file
43
musikr/src/main/java/org/oxycblt/musikr/track/Tracker.kt
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2024 Auxio Project
|
||||||
|
* Tracker.kt is part of Auxio.
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.oxycblt.musikr.track
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import org.oxycblt.musikr.tag.interpret.PreSong
|
||||||
|
|
||||||
|
abstract class Tracker {
|
||||||
|
internal abstract suspend fun track(preSong: PreSong): TrackedSong
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun from(context: Context): Tracker =
|
||||||
|
TrackerImpl(TrackerDatabase.from(context).trackedSongsDao())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal data class TrackedSong(val preSong: PreSong, val dateAdded: Long)
|
||||||
|
|
||||||
|
private class TrackerImpl(private val dao: TrackedSongsDao) : Tracker() {
|
||||||
|
override suspend fun track(preSong: PreSong): TrackedSong {
|
||||||
|
val currentTime = System.currentTimeMillis()
|
||||||
|
val entity = TrackedSongEntity(uid = preSong.uid.toString(), dateAdded = currentTime)
|
||||||
|
dao.insertSong(entity)
|
||||||
|
val trackedEntity = dao.selectSong(preSong.uid.toString())
|
||||||
|
return TrackedSong(preSong = preSong, dateAdded = trackedEntity?.dateAdded ?: currentTime)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,54 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2024 Auxio Project
|
||||||
|
* TrackerDatabase.kt is part of Auxio.
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.oxycblt.musikr.track
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import androidx.room.Dao
|
||||||
|
import androidx.room.Database
|
||||||
|
import androidx.room.Entity
|
||||||
|
import androidx.room.Insert
|
||||||
|
import androidx.room.OnConflictStrategy
|
||||||
|
import androidx.room.PrimaryKey
|
||||||
|
import androidx.room.Query
|
||||||
|
import androidx.room.Room
|
||||||
|
import androidx.room.RoomDatabase
|
||||||
|
|
||||||
|
@Database(entities = [TrackedSongEntity::class], version = 1, exportSchema = false)
|
||||||
|
internal abstract class TrackerDatabase : RoomDatabase() {
|
||||||
|
abstract fun trackedSongsDao(): TrackedSongsDao
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun from(context: Context) =
|
||||||
|
Room.databaseBuilder(
|
||||||
|
context.applicationContext, TrackerDatabase::class.java, "tracked_songs.db")
|
||||||
|
.fallbackToDestructiveMigration()
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity internal data class TrackedSongEntity(@PrimaryKey val uid: String, val dateAdded: Long)
|
||||||
|
|
||||||
|
@Dao
|
||||||
|
internal interface TrackedSongsDao {
|
||||||
|
@Query("SELECT * FROM TrackedSongEntity WHERE uid = :uid")
|
||||||
|
suspend fun selectSong(uid: String): TrackedSongEntity?
|
||||||
|
|
||||||
|
@Insert(onConflict = OnConflictStrategy.IGNORE)
|
||||||
|
suspend fun insertSong(trackedSong: TrackedSongEntity)
|
||||||
|
}
|
Loading…
Reference in a new issue