musikr: refactor model

This commit is contained in:
Alexander Capehart 2024-12-09 08:44:40 -07:00
parent 1d0ad641d5
commit 501c79d23c
No known key found for this signature in database
GPG key ID: 37DBE3621FE9AD47
12 changed files with 470 additions and 371 deletions

View file

@ -29,9 +29,9 @@ import org.oxycblt.musikr.Indexer
import org.oxycblt.musikr.IndexingProgress import org.oxycblt.musikr.IndexingProgress
import org.oxycblt.musikr.Library import org.oxycblt.musikr.Library
import org.oxycblt.musikr.Music import org.oxycblt.musikr.Music
import org.oxycblt.musikr.MutableLibrary
import org.oxycblt.musikr.Playlist import org.oxycblt.musikr.Playlist
import org.oxycblt.musikr.Song import org.oxycblt.musikr.Song
import org.oxycblt.musikr.model.MutableLibrary
import org.oxycblt.musikr.tag.Interpretation import org.oxycblt.musikr.tag.Interpretation
import org.oxycblt.musikr.tag.Name import org.oxycblt.musikr.tag.Name
import org.oxycblt.musikr.tag.interpret.Separators import org.oxycblt.musikr.tag.interpret.Separators

View file

@ -26,7 +26,6 @@ import kotlinx.coroutines.flow.onCompletion
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.flow.onStart
import org.oxycblt.musikr.fs.MusicLocation import org.oxycblt.musikr.fs.MusicLocation
import org.oxycblt.musikr.model.MutableLibrary
import org.oxycblt.musikr.pipeline.EvaluateStep import org.oxycblt.musikr.pipeline.EvaluateStep
import org.oxycblt.musikr.pipeline.ExploreStep import org.oxycblt.musikr.pipeline.ExploreStep
import org.oxycblt.musikr.pipeline.ExtractStep import org.oxycblt.musikr.pipeline.ExtractStep

View file

@ -61,6 +61,18 @@ interface Library {
fun findPlaylistByName(name: String): Playlist? fun findPlaylistByName(name: String): Playlist?
} }
interface MutableLibrary : Library {
suspend fun createPlaylist(name: String, songs: List<Song>): MutableLibrary
suspend fun renamePlaylist(playlist: Playlist, name: String): MutableLibrary
suspend fun addToPlaylist(playlist: Playlist, songs: List<Song>): MutableLibrary
suspend fun rewritePlaylist(playlist: Playlist, songs: List<Song>): MutableLibrary
suspend fun deletePlaylist(playlist: Playlist): MutableLibrary
}
/** /**
* Abstract music data. This contains universal information about all concrete music * Abstract music data. This contains universal information about all concrete music
* implementations, such as identification information and names. * implementations, such as identification information and names.

View file

@ -0,0 +1,77 @@
/*
* Copyright (c) 2024 Auxio Project
* AlbumImpl.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.model
import org.oxycblt.auxio.music.MusicType
import org.oxycblt.auxio.util.update
import org.oxycblt.musikr.Album
import org.oxycblt.musikr.Artist
import org.oxycblt.musikr.Music
import org.oxycblt.musikr.Song
import org.oxycblt.musikr.cover.Cover
import org.oxycblt.musikr.tag.Date
import org.oxycblt.musikr.tag.interpret.PreAlbum
interface AlbumCore {
val preAlbum: PreAlbum
val songs: List<Song>
fun resolveArtists(): List<Artist>
}
/**
* Library-backed implementation of [Album].
*
* @author Alexander Capehart (OxygenCobalt)
*/
class AlbumImpl(private val core: AlbumCore) : Album {
private val preAlbum = core.preAlbum
override val uid =
// Attempt to use a MusicBrainz ID first before falling back to a hashed UID.
preAlbum.musicBrainzId?.let { Music.UID.musicBrainz(MusicType.ALBUMS, it) }
?: Music.UID.auxio(MusicType.ALBUMS) {
// Hash based on only names despite the presence of a date to increase stability.
// I don't know if there is any situation where an artist will have two albums with
// the exact same name, but if there is, I would love to know.
update(preAlbum.rawName)
update(preAlbum.preArtists.map { it.rawName })
}
override val name = preAlbum.name
override val releaseType = preAlbum.releaseType
override val durationMs = core.songs.sumOf { it.durationMs }
override val dateAdded = core.songs.minOf { it.dateAdded }
override val cover = Cover.multi(core.songs)
override val dates: Date.Range? =
core.songs.mapNotNull { it.date }.ifEmpty { null }?.run { Date.Range(min(), max()) }
override val artists: List<Artist>
get() = core.resolveArtists()
override val songs = core.songs
private val hashCode = 31 * (31 * uid.hashCode() + preAlbum.hashCode()) + songs.hashCode()
override fun hashCode() = hashCode
override fun equals(other: Any?) =
other is AlbumImpl && uid == other.uid && preAlbum == other.preAlbum && songs == other.songs
override fun toString() = "Album(uid=$uid, name=$name)"
}

View file

@ -0,0 +1,73 @@
/*
* Copyright (c) 2024 Auxio Project
* ArtistImpl.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.model
import org.oxycblt.auxio.music.MusicType
import org.oxycblt.auxio.util.update
import org.oxycblt.musikr.Album
import org.oxycblt.musikr.Artist
import org.oxycblt.musikr.Genre
import org.oxycblt.musikr.Music
import org.oxycblt.musikr.Song
import org.oxycblt.musikr.cover.Cover
import org.oxycblt.musikr.tag.interpret.PreArtist
interface ArtistCore {
val preArtist: PreArtist
val songs: Set<Song>
val albums: Set<Album>
fun resolveGenres(): Set<Genre>
}
/**
* Library-backed implementation of [Artist].
*
* @author Alexander Capehart (OxygenCobalt)
*/
class ArtistImpl(private val core: ArtistCore) : Artist {
override val uid =
// Attempt to use a MusicBrainz ID first before falling back to a hashed UID.
core.preArtist.musicBrainzId?.let { Music.UID.musicBrainz(MusicType.ARTISTS, it) }
?: Music.UID.auxio(MusicType.ARTISTS) { update(core.preArtist.rawName) }
override val name = core.preArtist.name
override val songs = core.songs
override var explicitAlbums = core.albums
override var implicitAlbums = core.songs.mapTo(mutableSetOf()) { it.album } - core.albums
override val genres: List<Genre>
get() = core.resolveGenres().toList()
override val durationMs = core.songs.sumOf { it.durationMs }
override val cover = Cover.multi(core.songs)
private val hashCode =
31 * (31 * uid.hashCode() + core.preArtist.hashCode()) * core.songs.hashCode()
override fun hashCode() = hashCode
override fun equals(other: Any?) =
other is ArtistImpl &&
uid == other.uid &&
core.preArtist == other.core.preArtist &&
songs == other.songs
override fun toString() = "Artist(uid=$uid, name=$name)"
}

View file

@ -1,210 +0,0 @@
/*
* Copyright (c) 2023 Auxio Project
* DeviceMusicImpl.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.model
import org.oxycblt.auxio.music.MusicType
import org.oxycblt.auxio.util.update
import org.oxycblt.musikr.Album
import org.oxycblt.musikr.Artist
import org.oxycblt.musikr.Genre
import org.oxycblt.musikr.Music
import org.oxycblt.musikr.Song
import org.oxycblt.musikr.cover.Cover
import org.oxycblt.musikr.tag.Date
import org.oxycblt.musikr.tag.interpret.PreAlbum
import org.oxycblt.musikr.tag.interpret.PreArtist
import org.oxycblt.musikr.tag.interpret.PreGenre
import org.oxycblt.musikr.tag.interpret.PreSong
interface SongHandle {
val preSong: PreSong
fun resolveAlbum(): Album
fun resolveArtists(): List<Artist>
fun resolveGenres(): List<Genre>
}
/**
* Library-backed implementation of [Song].
*
* @author Alexander Capehart (OxygenCobalt)
*/
class SongImpl(private val handle: SongHandle) : Song {
private val preSong = handle.preSong
override val uid = preSong.computeUid()
override val name = preSong.name
override val track = preSong.track
override val disc = preSong.disc
override val date = preSong.date
override val uri = preSong.uri
override val path = preSong.path
override val mimeType = preSong.mimeType
override val size = preSong.size
override val durationMs = preSong.durationMs
override val replayGainAdjustment = preSong.replayGainAdjustment
override val lastModified = preSong.lastModified
override val dateAdded = preSong.dateAdded
override val cover = Cover.single(this)
override val album: Album
get() = handle.resolveAlbum()
override val artists: List<Artist>
get() = handle.resolveArtists()
override val genres: List<Genre>
get() = handle.resolveGenres()
private val hashCode = 31 * uid.hashCode() + preSong.hashCode()
override fun hashCode() = hashCode
override fun equals(other: Any?) =
other is SongImpl && uid == other.uid && preSong == other.preSong
override fun toString() = "Song(uid=$uid, name=$name)"
}
interface AlbumHandle {
val preAlbum: PreAlbum
val songs: List<Song>
fun resolveArtists(): List<Artist>
}
/**
* Library-backed implementation of [Album].
*
* @author Alexander Capehart (OxygenCobalt)
*/
class AlbumImpl(private val handle: AlbumHandle) : Album {
private val preAlbum = handle.preAlbum
override val uid =
// Attempt to use a MusicBrainz ID first before falling back to a hashed UID.
preAlbum.musicBrainzId?.let { Music.UID.musicBrainz(MusicType.ALBUMS, it) }
?: Music.UID.auxio(MusicType.ALBUMS) {
// Hash based on only names despite the presence of a date to increase stability.
// I don't know if there is any situation where an artist will have two albums with
// the exact same name, but if there is, I would love to know.
update(preAlbum.rawName)
update(preAlbum.preArtists.map { it.rawName })
}
override val name = preAlbum.name
override val releaseType = preAlbum.releaseType
override val durationMs = handle.songs.sumOf { it.durationMs }
override val dateAdded = handle.songs.minOf { it.dateAdded }
override val cover = Cover.multi(handle.songs)
override val dates: Date.Range? =
handle.songs.mapNotNull { it.date }.ifEmpty { null }?.run { Date.Range(min(), max()) }
override val artists: List<Artist>
get() = handle.resolveArtists()
override val songs = handle.songs
private val hashCode = 31 * (31 * uid.hashCode() + preAlbum.hashCode()) + songs.hashCode()
override fun hashCode() = hashCode
override fun equals(other: Any?) =
other is AlbumImpl && uid == other.uid && preAlbum == other.preAlbum && songs == other.songs
override fun toString() = "Album(uid=$uid, name=$name)"
}
interface ArtistHandle {
val preArtist: PreArtist
val songs: Set<Song>
val albums: Set<Album>
fun resolveGenres(): Set<Genre>
}
/**
* Library-backed implementation of [Artist].
*
* @author Alexander Capehart (OxygenCobalt)
*/
class ArtistImpl(private val handle: ArtistHandle) : Artist {
override val uid =
// Attempt to use a MusicBrainz ID first before falling back to a hashed UID.
handle.preArtist.musicBrainzId?.let { Music.UID.musicBrainz(MusicType.ARTISTS, it) }
?: Music.UID.auxio(MusicType.ARTISTS) { update(handle.preArtist.rawName) }
override val name = handle.preArtist.name
override val songs = handle.songs
override var explicitAlbums = handle.albums
override var implicitAlbums = handle.songs.mapTo(mutableSetOf()) { it.album } - handle.albums
override val genres: List<Genre>
get() = handle.resolveGenres().toList()
override val durationMs = handle.songs.sumOf { it.durationMs }
override val cover = Cover.multi(handle.songs)
private val hashCode =
31 * (31 * uid.hashCode() + handle.preArtist.hashCode()) * handle.songs.hashCode()
override fun hashCode() = hashCode
override fun equals(other: Any?) =
other is ArtistImpl &&
uid == other.uid &&
handle.preArtist == other.handle.preArtist &&
songs == other.songs
override fun toString() = "Artist(uid=$uid, name=$name)"
}
interface GenreHandle {
val preGenre: PreGenre
val songs: Set<Song>
val artists: Set<Artist>
}
/**
* Library-backed implementation of [Genre].
*
* @author Alexander Capehart (OxygenCobalt)
*/
class GenreImpl(private val handle: GenreHandle) : Genre {
override val uid = Music.UID.auxio(MusicType.GENRES) { update(handle.preGenre.rawName) }
override val name = handle.preGenre.name
override val songs = mutableSetOf<Song>()
override val artists = mutableSetOf<Artist>()
override val durationMs = handle.songs.sumOf { it.durationMs }
override val cover = Cover.multi(handle.songs)
private val hashCode =
31 * (31 * uid.hashCode() + handle.preGenre.hashCode()) + songs.hashCode()
override fun hashCode() = hashCode
override fun equals(other: Any?) =
other is GenreImpl &&
uid == other.uid &&
handle.preGenre == other.handle.preGenre &&
songs == other.songs
override fun toString() = "Genre(uid=$uid, name=$name)"
}

View file

@ -0,0 +1,61 @@
/*
* Copyright (c) 2023 Auxio Project
* GenreImpl.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.model
import org.oxycblt.auxio.music.MusicType
import org.oxycblt.auxio.util.update
import org.oxycblt.musikr.Artist
import org.oxycblt.musikr.Genre
import org.oxycblt.musikr.Music
import org.oxycblt.musikr.Song
import org.oxycblt.musikr.cover.Cover
import org.oxycblt.musikr.tag.interpret.PreGenre
interface GenreCore {
val preGenre: PreGenre
val songs: Set<Song>
val artists: Set<Artist>
}
/**
* Library-backed implementation of [Genre].
*
* @author Alexander Capehart (OxygenCobalt)
*/
class GenreImpl(private val core: GenreCore) : Genre {
override val uid = Music.UID.auxio(MusicType.GENRES) { update(core.preGenre.rawName) }
override val name = core.preGenre.name
override val songs = mutableSetOf<Song>()
override val artists = mutableSetOf<Artist>()
override val durationMs = core.songs.sumOf { it.durationMs }
override val cover = Cover.multi(core.songs)
private val hashCode = 31 * (31 * uid.hashCode() + core.preGenre.hashCode()) + songs.hashCode()
override fun hashCode() = hashCode
override fun equals(other: Any?) =
other is GenreImpl &&
uid == other.uid &&
core.preGenre == other.core.preGenre &&
songs == other.songs
override fun toString() = "Genre(uid=$uid, name=$name)"
}

View file

@ -1,158 +0,0 @@
/*
* Copyright (c) 2024 Auxio Project
* Library.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.model
import javax.inject.Inject
import org.oxycblt.musikr.Album
import org.oxycblt.musikr.Artist
import org.oxycblt.musikr.Genre
import org.oxycblt.musikr.Library
import org.oxycblt.musikr.Music
import org.oxycblt.musikr.Playlist
import org.oxycblt.musikr.Song
import org.oxycblt.musikr.fs.Path
import org.oxycblt.musikr.graph.AlbumVertex
import org.oxycblt.musikr.graph.ArtistVertex
import org.oxycblt.musikr.graph.GenreVertex
import org.oxycblt.musikr.graph.MusicGraph
import org.oxycblt.musikr.graph.SongVertex
interface MutableLibrary : Library {
suspend fun createPlaylist(name: String, songs: List<Song>): MutableLibrary
suspend fun renamePlaylist(playlist: Playlist, name: String): MutableLibrary
suspend fun addToPlaylist(playlist: Playlist, songs: List<Song>): MutableLibrary
suspend fun rewritePlaylist(playlist: Playlist, songs: List<Song>): MutableLibrary
suspend fun deletePlaylist(playlist: Playlist): MutableLibrary
}
interface LibraryFactory {
fun create(graph: MusicGraph): MutableLibrary
}
class LibraryFactoryImpl @Inject constructor() : LibraryFactory {
override fun create(graph: MusicGraph): MutableLibrary {
val songs =
graph.songVertex.mapTo(mutableSetOf()) { vertex ->
SongImpl(SongVertexHandle(vertex)).also { vertex.tag = it }
}
val albums =
graph.albumVertex.mapTo(mutableSetOf()) { vertex ->
AlbumImpl(AlbumVertexHandle(vertex)).also { vertex.tag = it }
}
val artists =
graph.artistVertex.mapTo(mutableSetOf()) { vertex ->
ArtistImpl(ArtistVertexHandle(vertex)).also { vertex.tag = it }
}
val genres =
graph.genreVertex.mapTo(mutableSetOf()) { vertex ->
GenreImpl(GenreVertexHandle(vertex)).also { vertex.tag = it }
}
return LibraryImpl(songs, albums, artists, genres)
}
private class SongVertexHandle(private val vertex: SongVertex) : SongHandle {
override val preSong = vertex.preSong
override fun resolveAlbum() = vertex.albumVertex.tag as Album
override fun resolveArtists() = vertex.artistVertices.map { it.tag as Artist }
override fun resolveGenres() = vertex.genreVertices.map { it.tag as Genre }
}
private class AlbumVertexHandle(private val vertex: AlbumVertex) : AlbumHandle {
override val preAlbum = vertex.preAlbum
override val songs = vertex.songVertices.map { SongImpl(SongVertexHandle(it)) }
override fun resolveArtists() = vertex.artistVertices.map { it.tag as Artist }
}
private class ArtistVertexHandle(private val vertex: ArtistVertex) : ArtistHandle {
override val preArtist = vertex.preArtist
override val songs = vertex.songVertices.mapTo(mutableSetOf()) { it.tag as Song }
override val albums = vertex.albumVertices.mapTo(mutableSetOf()) { it.tag as Album }
override fun resolveGenres() =
vertex.genreVertices.mapTo(mutableSetOf()) { it.tag as Genre }
}
private class GenreVertexHandle(vertex: GenreVertex) : GenreHandle {
override val preGenre = vertex.preGenre
override val songs = vertex.songVertices.mapTo(mutableSetOf()) { it.tag as Song }
override val artists = vertex.artistVertices.mapTo(mutableSetOf()) { it.tag as Artist }
}
}
class LibraryImpl(
override val songs: Collection<SongImpl>,
override val albums: Collection<AlbumImpl>,
override val artists: Collection<ArtistImpl>,
override val genres: Collection<GenreImpl>
) : MutableLibrary {
override val playlists = emptySet<Playlist>()
private val songUidMap = songs.associateBy { it.uid }
private val albumUidMap = albums.associateBy { it.uid }
private val artistUidMap = artists.associateBy { it.uid }
private val genreUidMap = genres.associateBy { it.uid }
private val playlistUidMap = playlists.associateBy { it.uid }
override fun findSong(uid: Music.UID) = songUidMap[uid]
override fun findSongByPath(path: Path) = songs.find { it.path == path }
override fun findAlbum(uid: Music.UID) = albumUidMap[uid]
override fun findArtist(uid: Music.UID) = artistUidMap[uid]
override fun findGenre(uid: Music.UID) = genreUidMap[uid]
override fun findPlaylist(uid: Music.UID) = playlistUidMap[uid]
override fun findPlaylistByName(name: String) = playlists.find { it.name.raw == name }
override suspend fun createPlaylist(name: String, songs: List<Song>): MutableLibrary {
return this
}
override suspend fun renamePlaylist(playlist: Playlist, name: String): MutableLibrary {
return this
}
override suspend fun addToPlaylist(playlist: Playlist, songs: List<Song>): MutableLibrary {
return this
}
override suspend fun rewritePlaylist(playlist: Playlist, songs: List<Song>): MutableLibrary {
return this
}
override suspend fun deletePlaylist(playlist: Playlist): MutableLibrary {
return this
}
}

View file

@ -0,0 +1,94 @@
/*
* Copyright (c) 2024 Auxio Project
* LibraryFactory.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.model
import javax.inject.Inject
import org.oxycblt.musikr.Album
import org.oxycblt.musikr.Artist
import org.oxycblt.musikr.Genre
import org.oxycblt.musikr.MutableLibrary
import org.oxycblt.musikr.Song
import org.oxycblt.musikr.graph.AlbumVertex
import org.oxycblt.musikr.graph.ArtistVertex
import org.oxycblt.musikr.graph.GenreVertex
import org.oxycblt.musikr.graph.MusicGraph
import org.oxycblt.musikr.graph.SongVertex
interface LibraryFactory {
fun create(graph: MusicGraph): MutableLibrary
}
class LibraryFactoryImpl @Inject constructor() : LibraryFactory {
override fun create(graph: MusicGraph): MutableLibrary {
val songs =
graph.songVertex.mapTo(mutableSetOf()) { vertex ->
SongImpl(SongVertexCore(vertex)).also { vertex.tag = it }
}
val albums =
graph.albumVertex.mapTo(mutableSetOf()) { vertex ->
AlbumImpl(AlbumVertexCore(vertex)).also { vertex.tag = it }
}
val artists =
graph.artistVertex.mapTo(mutableSetOf()) { vertex ->
ArtistImpl(ArtistVertexCore(vertex)).also { vertex.tag = it }
}
val genres =
graph.genreVertex.mapTo(mutableSetOf()) { vertex ->
GenreImpl(GenreVertexCore(vertex)).also { vertex.tag = it }
}
return LibraryImpl(songs, albums, artists, genres)
}
private class SongVertexCore(private val vertex: SongVertex) : SongCore {
override val preSong = vertex.preSong
override fun resolveAlbum() = vertex.albumVertex.tag as Album
override fun resolveArtists() = vertex.artistVertices.map { it.tag as Artist }
override fun resolveGenres() = vertex.genreVertices.map { it.tag as Genre }
}
private class AlbumVertexCore(private val vertex: AlbumVertex) : AlbumCore {
override val preAlbum = vertex.preAlbum
override val songs = vertex.songVertices.map { SongImpl(SongVertexCore(it)) }
override fun resolveArtists() = vertex.artistVertices.map { it.tag as Artist }
}
private class ArtistVertexCore(private val vertex: ArtistVertex) : ArtistCore {
override val preArtist = vertex.preArtist
override val songs = vertex.songVertices.mapTo(mutableSetOf()) { it.tag as Song }
override val albums = vertex.albumVertices.mapTo(mutableSetOf()) { it.tag as Album }
override fun resolveGenres() =
vertex.genreVertices.mapTo(mutableSetOf()) { it.tag as Genre }
}
private class GenreVertexCore(vertex: GenreVertex) : GenreCore {
override val preGenre = vertex.preGenre
override val songs = vertex.songVertices.mapTo(mutableSetOf()) { it.tag as Song }
override val artists = vertex.artistVertices.mapTo(mutableSetOf()) { it.tag as Artist }
}
}

View file

@ -0,0 +1,74 @@
/*
* Copyright (c) 2024 Auxio Project
* LibraryImpl.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.model
import org.oxycblt.musikr.Music
import org.oxycblt.musikr.MutableLibrary
import org.oxycblt.musikr.Playlist
import org.oxycblt.musikr.Song
import org.oxycblt.musikr.fs.Path
class LibraryImpl(
override val songs: Collection<SongImpl>,
override val albums: Collection<AlbumImpl>,
override val artists: Collection<ArtistImpl>,
override val genres: Collection<GenreImpl>
) : MutableLibrary {
override val playlists = emptySet<Playlist>()
private val songUidMap = songs.associateBy { it.uid }
private val albumUidMap = albums.associateBy { it.uid }
private val artistUidMap = artists.associateBy { it.uid }
private val genreUidMap = genres.associateBy { it.uid }
private val playlistUidMap = playlists.associateBy { it.uid }
override fun findSong(uid: Music.UID) = songUidMap[uid]
override fun findSongByPath(path: Path) = songs.find { it.path == path }
override fun findAlbum(uid: Music.UID) = albumUidMap[uid]
override fun findArtist(uid: Music.UID) = artistUidMap[uid]
override fun findGenre(uid: Music.UID) = genreUidMap[uid]
override fun findPlaylist(uid: Music.UID) = playlistUidMap[uid]
override fun findPlaylistByName(name: String) = playlists.find { it.name.raw == name }
override suspend fun createPlaylist(name: String, songs: List<Song>): MutableLibrary {
return this
}
override suspend fun renamePlaylist(playlist: Playlist, name: String): MutableLibrary {
return this
}
override suspend fun addToPlaylist(playlist: Playlist, songs: List<Song>): MutableLibrary {
return this
}
override suspend fun rewritePlaylist(playlist: Playlist, songs: List<Song>): MutableLibrary {
return this
}
override suspend fun deletePlaylist(playlist: Playlist): MutableLibrary {
return this
}
}

View file

@ -0,0 +1,77 @@
/*
* Copyright (c) 2024 Auxio Project
* SongImpl.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.model
import org.oxycblt.musikr.Album
import org.oxycblt.musikr.Artist
import org.oxycblt.musikr.Genre
import org.oxycblt.musikr.Song
import org.oxycblt.musikr.cover.Cover
import org.oxycblt.musikr.tag.interpret.PreSong
interface SongCore {
val preSong: PreSong
fun resolveAlbum(): Album
fun resolveArtists(): List<Artist>
fun resolveGenres(): List<Genre>
}
/**
* Library-backed implementation of [Song].
*
* @author Alexander Capehart (OxygenCobalt)
*/
class SongImpl(private val handle: SongCore) : Song {
private val preSong = handle.preSong
override val uid = preSong.computeUid()
override val name = preSong.name
override val track = preSong.track
override val disc = preSong.disc
override val date = preSong.date
override val uri = preSong.uri
override val path = preSong.path
override val mimeType = preSong.mimeType
override val size = preSong.size
override val durationMs = preSong.durationMs
override val replayGainAdjustment = preSong.replayGainAdjustment
override val lastModified = preSong.lastModified
override val dateAdded = preSong.dateAdded
override val cover = Cover.single(this)
override val album: Album
get() = handle.resolveAlbum()
override val artists: List<Artist>
get() = handle.resolveArtists()
override val genres: List<Genre>
get() = handle.resolveGenres()
private val hashCode = 31 * uid.hashCode() + preSong.hashCode()
override fun hashCode() = hashCode
override fun equals(other: Any?) =
other is SongImpl && uid == other.uid && preSong == other.preSong
override fun toString() = "Song(uid=$uid, name=$name)"
}

View file

@ -26,9 +26,9 @@ import kotlinx.coroutines.flow.buffer
import kotlinx.coroutines.flow.filterIsInstance import kotlinx.coroutines.flow.filterIsInstance
import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
import org.oxycblt.musikr.MutableLibrary
import org.oxycblt.musikr.graph.MusicGraph import org.oxycblt.musikr.graph.MusicGraph
import org.oxycblt.musikr.model.LibraryFactory import org.oxycblt.musikr.model.LibraryFactory
import org.oxycblt.musikr.model.MutableLibrary
import org.oxycblt.musikr.tag.Interpretation import org.oxycblt.musikr.tag.Interpretation
import org.oxycblt.musikr.tag.interpret.TagInterpreter import org.oxycblt.musikr.tag.interpret.TagInterpreter