musikr: initial root documentation
This commit is contained in:
parent
b6d80189ca
commit
c9d4b01f9f
3 changed files with 181 additions and 7 deletions
|
@ -24,10 +24,34 @@ import org.oxycblt.musikr.playlist.db.StoredPlaylists
|
|||
import org.oxycblt.musikr.tag.interpret.Naming
|
||||
import org.oxycblt.musikr.tag.interpret.Separators
|
||||
|
||||
/** Side-effect laden [Storage] for use during music loading and [MutableLibrary] operation. */
|
||||
data class Storage(
|
||||
/**
|
||||
* A factory producing a repository of cached metadata to read and write from over the course of
|
||||
* music loading. This will only be used during music loading.
|
||||
*/
|
||||
val cache: Cache.Factory,
|
||||
|
||||
/**
|
||||
* A repository of cover images to for re-use during music loading. Should be kept in lock-step
|
||||
* with the cache for best performance. This will be used during music loading and when
|
||||
* retrieving cover information from the library.
|
||||
*/
|
||||
val storedCovers: MutableCovers,
|
||||
|
||||
/**
|
||||
* A repository of user-created playlists that should also be loaded into the library. This will
|
||||
* be used during music loading and mutated when creating, renaming, or deleting playlists in
|
||||
* the library.
|
||||
*/
|
||||
val storedPlaylists: StoredPlaylists
|
||||
)
|
||||
|
||||
data class Interpretation(val naming: Naming, val separators: Separators)
|
||||
/** Configuration for how to interpret and extrapolate certain audio tags. */
|
||||
data class Interpretation(
|
||||
/** How to construct names from audio tags. */
|
||||
val naming: Naming,
|
||||
|
||||
/** What separators delimit multi-value audio tags. */
|
||||
val separators: Separators
|
||||
)
|
||||
|
|
|
@ -20,6 +20,11 @@ package org.oxycblt.musikr
|
|||
|
||||
import org.oxycblt.musikr.fs.Path
|
||||
|
||||
/**
|
||||
* An immutable music library.
|
||||
*
|
||||
* No operations here will create side effects.
|
||||
*/
|
||||
interface Library {
|
||||
val songs: Collection<Song>
|
||||
val albums: Collection<Album>
|
||||
|
@ -27,31 +32,131 @@ interface Library {
|
|||
val genres: Collection<Genre>
|
||||
val playlists: Collection<Playlist>
|
||||
|
||||
/**
|
||||
* Whether this library is empty (i.e no songs, which means no other music item)
|
||||
*
|
||||
* @return true if this library is empty, false otherwise
|
||||
*/
|
||||
fun empty(): Boolean
|
||||
|
||||
/**
|
||||
* Find a [Song] by it's [Music.UID]
|
||||
*
|
||||
* @param uid the [Music.UID] of the song
|
||||
* @return the song if found, null otherwise
|
||||
*/
|
||||
fun findSong(uid: Music.UID): Song?
|
||||
|
||||
/**
|
||||
* Find a [Song] by it's [Path]
|
||||
*
|
||||
* @param path the [Path] of the song
|
||||
* @return the song if found, null otherwise
|
||||
*/
|
||||
fun findSongByPath(path: Path): Song?
|
||||
|
||||
/**
|
||||
* Find an [Album] by it's [Music.UID]
|
||||
*
|
||||
* @param uid the [Music.UID] of the album
|
||||
* @return the album if found, null otherwise
|
||||
*/
|
||||
fun findAlbum(uid: Music.UID): Album?
|
||||
|
||||
/**
|
||||
* Find an [Artist] by it's [Music.UID]
|
||||
*
|
||||
* @param uid the [Music.UID] of the artist
|
||||
* @return the artist if found, null otherwise
|
||||
*/
|
||||
fun findArtist(uid: Music.UID): Artist?
|
||||
|
||||
/**
|
||||
* Find a [Genre] by it's [Music.UID]
|
||||
*
|
||||
* @param uid the [Music.UID] of the genre
|
||||
* @return the genre if found, null otherwise
|
||||
*/
|
||||
fun findGenre(uid: Music.UID): Genre?
|
||||
|
||||
/**
|
||||
* Find a [Playlist] by it's [Music.UID]
|
||||
*
|
||||
* @param uid the [Music.UID] of the playlist
|
||||
* @return the playlist if found, null otherwise
|
||||
*/
|
||||
fun findPlaylist(uid: Music.UID): Playlist?
|
||||
|
||||
/**
|
||||
* Find a [Playlist] by it's name
|
||||
*
|
||||
* @param name the name of the playlist
|
||||
* @return the playlist if found, null otherwise
|
||||
*/
|
||||
fun findPlaylistByName(name: String): Playlist?
|
||||
}
|
||||
|
||||
/**
|
||||
* A mutable extension of [Library].
|
||||
*
|
||||
* Operations here will cause side-effects within the [Storage] used when this library was loaded.
|
||||
* However, it won't actually mutate the [Library] itself, rather return a cloned instance with the
|
||||
* changes applied. It is up to the client to update their reference to the library within their
|
||||
* state handling.
|
||||
*/
|
||||
interface MutableLibrary : Library {
|
||||
/**
|
||||
* Create a new [Playlist] with the given name and songs.
|
||||
*
|
||||
* This will commit the new playlist to the stored playlists in the [Storage] used to load the
|
||||
* library.
|
||||
*
|
||||
* @param name the name of the playlist
|
||||
* @param songs the songs to add to the playlist
|
||||
* @return a new [MutableLibrary] with the new playlist
|
||||
*/
|
||||
suspend fun createPlaylist(name: String, songs: List<Song>): MutableLibrary
|
||||
|
||||
/**
|
||||
* Rename a [Playlist].
|
||||
*
|
||||
* This will commit to whatever playlist source the given [Playlist] was loaded from.
|
||||
*
|
||||
* @param playlist the playlist to rename
|
||||
* @param name the new name of the playlist
|
||||
* @return a new [MutableLibrary] with the renamed playlist
|
||||
*/
|
||||
suspend fun renamePlaylist(playlist: Playlist, name: String): MutableLibrary
|
||||
|
||||
/**
|
||||
* Add songs to a [Playlist].
|
||||
*
|
||||
* This will commit to whatever playlist source the given [Playlist] was loaded from.
|
||||
*
|
||||
* @param playlist the playlist to add songs to
|
||||
* @param songs the songs to add to the playlist
|
||||
* @return a new [MutableLibrary] with the edited playlist
|
||||
*/
|
||||
suspend fun addToPlaylist(playlist: Playlist, songs: List<Song>): MutableLibrary
|
||||
|
||||
/**
|
||||
* Remove songs from a [Playlist].
|
||||
*
|
||||
* This will commit to whatever playlist source the given [Playlist] was loaded from.
|
||||
*
|
||||
* @param playlist the playlist to remove songs from
|
||||
* @param songs the songs to remove from the playlist
|
||||
* @return a new [MutableLibrary] with the edited playlist
|
||||
*/
|
||||
suspend fun rewritePlaylist(playlist: Playlist, songs: List<Song>): MutableLibrary
|
||||
|
||||
/**
|
||||
* Remove a [Playlist].
|
||||
*
|
||||
* This will commit to whatever playlist source the given [Playlist] was loaded from.
|
||||
*
|
||||
* @param playlist the playlist to delete
|
||||
* @return a new [MutableLibrary] with the edited playlist
|
||||
*/
|
||||
suspend fun deletePlaylist(playlist: Playlist): MutableLibrary
|
||||
}
|
||||
|
|
|
@ -30,13 +30,44 @@ import org.oxycblt.musikr.pipeline.EvaluateStep
|
|||
import org.oxycblt.musikr.pipeline.ExploreStep
|
||||
import org.oxycblt.musikr.pipeline.ExtractStep
|
||||
|
||||
/**
|
||||
* A highly opinionated, multi-threaded device music library.
|
||||
*
|
||||
* Use this to load music with [run].
|
||||
*
|
||||
* Note the following:
|
||||
* 1. Musikr's API surface is intended to be primarily "stateless", with side-effects mostly
|
||||
* contained within [Storage]. It's your job to manage long-term state.
|
||||
* 2. There are no "defaults" in Musikr. You should think carefully about the parameters you are
|
||||
* specifying and know consider they are desirable or not.
|
||||
* 3. Musikr is currently not extendable, so if you're embedding this elsewhere you should be ready
|
||||
* to fork and modify the source code.
|
||||
*/
|
||||
interface Musikr {
|
||||
/**
|
||||
* Start loading music from the given [locations] and the configuration provided earlier.
|
||||
*
|
||||
* @param locations The [MusicLocation]s to search for music in.
|
||||
* @param onProgress Optional callback to receive progress on the current status of the music
|
||||
* pipeline. Warning: These events will be rapid-fire.
|
||||
* @return A handle to the newly created library alongside further cleanup.
|
||||
*/
|
||||
suspend fun run(
|
||||
locations: List<MusicLocation>,
|
||||
onProgress: suspend (IndexingProgress) -> Unit = {}
|
||||
): LibraryResult
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* Create a new instance from the given configuration.
|
||||
*
|
||||
* @param context The context to use for loading resources.
|
||||
* @param storage Side-effect laden storage for use within the music loader **and** when
|
||||
* mutating [MutableLibrary]. You should take responsibility for managing their long-term
|
||||
* state.
|
||||
* @param interpretation The configuration to use for interpreting certain vague tags. This
|
||||
* should be configured by the user, if possible.
|
||||
*/
|
||||
fun new(context: Context, storage: Storage, interpretation: Interpretation): Musikr =
|
||||
MusikrImpl(
|
||||
storage,
|
||||
|
@ -46,20 +77,35 @@ interface Musikr {
|
|||
}
|
||||
}
|
||||
|
||||
/** Simple library handle returned by [Musikr.run]. */
|
||||
interface LibraryResult {
|
||||
val library: MutableLibrary
|
||||
|
||||
/**
|
||||
* Clean up expired resources. This should be done as soon as possible after music loading to
|
||||
* reduce storage use.
|
||||
*
|
||||
* This may have unexpected results if previous [Library]s are in circulation across your app,
|
||||
* so use it once you've fully updated your state.
|
||||
*/
|
||||
suspend fun cleanup()
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the current progress of music loading.
|
||||
*
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*/
|
||||
/** Music loading progress as reported by the music pipeline. */
|
||||
sealed interface IndexingProgress {
|
||||
/**
|
||||
* Currently indexing and extracting tags from device music.
|
||||
*
|
||||
* @param explored The amount of music currently found from the given [MusicLocation]s.
|
||||
* @param loaded The amount of music that has had metadata extracted and parsed.
|
||||
*/
|
||||
data class Songs(val loaded: Int, val explored: Int) : IndexingProgress
|
||||
|
||||
/**
|
||||
* Currently creating the music graph alongside I/O finalization.
|
||||
*
|
||||
* There is no way to measure progress on these events.
|
||||
*/
|
||||
data object Indeterminate : IndexingProgress
|
||||
}
|
||||
|
||||
|
@ -96,7 +142,6 @@ private class LibraryResultImpl(
|
|||
private val storage: Storage,
|
||||
override val library: MutableLibrary
|
||||
) : LibraryResult {
|
||||
|
||||
override suspend fun cleanup() {
|
||||
storage.storedCovers.cleanup(library.songs.mapNotNull { it.cover })
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue