From 7768d986328dd780161f621e13ba5a7a48639c2b Mon Sep 17 00:00:00 2001 From: Alexander Capehart Date: Tue, 24 Dec 2024 14:43:48 -0500 Subject: [PATCH] musikr.cover: refactor cover Instead of using a weird sealed class, instead go for a Cover/CoverCollection system instead that removes some implicit design dependence in musikr. --- .../java/org/oxycblt/auxio/image/CoverView.kt | 14 +-- .../oxycblt/auxio/image/coil/CoilModule.kt | 13 ++- ...omponents.kt => CoverCollectionFetcher.kt} | 40 +++---- .../oxycblt/auxio/image/coil/CoverFetcher.kt | 110 ++++++++++++++++++ .../org/oxycblt/auxio/image/coil/Keyers.kt | 34 ++++++ .../oxycblt/auxio/music/RevisionedLibrary.kt | 2 +- .../src/main/java/org/oxycblt/musikr/Music.kt | 11 +- .../java/org/oxycblt/musikr/cover/Cover.kt | 31 ++--- .../org/oxycblt/musikr/cover/StoredCovers.kt | 6 +- .../org/oxycblt/musikr/model/AlbumImpl.kt | 4 +- .../org/oxycblt/musikr/model/ArtistImpl.kt | 4 +- .../org/oxycblt/musikr/model/GenreImpl.kt | 4 +- .../org/oxycblt/musikr/model/PlaylistImpl.kt | 4 +- .../oxycblt/musikr/pipeline/ExtractStep.kt | 2 +- .../oxycblt/musikr/tag/interpret/PreMusic.kt | 2 +- 15 files changed, 205 insertions(+), 76 deletions(-) rename app/src/main/java/org/oxycblt/auxio/image/coil/{Components.kt => CoverCollectionFetcher.kt} (82%) create mode 100644 app/src/main/java/org/oxycblt/auxio/image/coil/CoverFetcher.kt create mode 100644 app/src/main/java/org/oxycblt/auxio/image/coil/Keyers.kt diff --git a/app/src/main/java/org/oxycblt/auxio/image/CoverView.kt b/app/src/main/java/org/oxycblt/auxio/image/CoverView.kt index 704378c11..eb402545c 100644 --- a/app/src/main/java/org/oxycblt/auxio/image/CoverView.kt +++ b/app/src/main/java/org/oxycblt/auxio/image/CoverView.kt @@ -64,7 +64,7 @@ import org.oxycblt.musikr.Artist import org.oxycblt.musikr.Genre import org.oxycblt.musikr.Playlist import org.oxycblt.musikr.Song -import org.oxycblt.musikr.cover.Cover +import org.oxycblt.musikr.cover.CoverCollection /** * Auxio's extension of [ImageView] that enables cover art loading and playing indicator and @@ -327,7 +327,7 @@ constructor(context: Context, attrs: AttributeSet? = null, @AttrRes defStyleAttr */ fun bind(album: Album) = bindImpl( - album.cover, + album.covers, context.getString(R.string.desc_album_cover, album.name), R.drawable.ic_album_24) @@ -338,7 +338,7 @@ constructor(context: Context, attrs: AttributeSet? = null, @AttrRes defStyleAttr */ fun bind(artist: Artist) = bindImpl( - artist.cover, + artist.covers, context.getString(R.string.desc_artist_image, artist.name), R.drawable.ic_artist_24) @@ -349,7 +349,7 @@ constructor(context: Context, attrs: AttributeSet? = null, @AttrRes defStyleAttr */ fun bind(genre: Genre) = bindImpl( - genre.cover, + genre.covers, context.getString(R.string.desc_genre_image, genre.name), R.drawable.ic_genre_24) @@ -360,7 +360,7 @@ constructor(context: Context, attrs: AttributeSet? = null, @AttrRes defStyleAttr */ fun bind(playlist: Playlist) = bindImpl( - playlist.cover, + playlist.covers, context.getString(R.string.desc_playlist_image, playlist.name), R.drawable.ic_playlist_24) @@ -372,9 +372,9 @@ constructor(context: Context, attrs: AttributeSet? = null, @AttrRes defStyleAttr * @param errorRes The resource of the error drawable to use if the cover cannot be loaded. */ fun bind(songs: List, desc: String, @DrawableRes errorRes: Int) = - bindImpl(Cover.multi(songs), desc, errorRes) + bindImpl(CoverCollection.from(songs.mapNotNull { it.cover }), desc, errorRes) - private fun bindImpl(cover: Cover?, desc: String, @DrawableRes errorRes: Int) { + private fun bindImpl(cover: Any?, desc: String, @DrawableRes errorRes: Int) { val request = ImageRequest.Builder(context) .data(cover) diff --git a/app/src/main/java/org/oxycblt/auxio/image/coil/CoilModule.kt b/app/src/main/java/org/oxycblt/auxio/image/coil/CoilModule.kt index 3766ce5fb..59f904d66 100644 --- a/app/src/main/java/org/oxycblt/auxio/image/coil/CoilModule.kt +++ b/app/src/main/java/org/oxycblt/auxio/image/coil/CoilModule.kt @@ -36,14 +36,17 @@ class CoilModule { @Provides fun imageLoader( @ApplicationContext context: Context, - keyer: CoverKeyer, - factory: CoverFetcher.Factory + coverKeyer: CoverKeyer, + coverFactory: CoverFetcher.Factory, + coverCollectionKeyer: CoverCollectionKeyer, + coverCollectionFactory: CoverCollectionFetcher.Factory ) = ImageLoader.Builder(context) .components { - // Add fetchers for Music components to make them usable with ImageRequest - add(keyer) - add(factory) + add(coverKeyer) + add(coverFactory) + add(coverCollectionKeyer) + add(coverCollectionFactory) } // Use our own crossfade with error drawable support .transitionFactory(ErrorCrossfadeTransitionFactory()) diff --git a/app/src/main/java/org/oxycblt/auxio/image/coil/Components.kt b/app/src/main/java/org/oxycblt/auxio/image/coil/CoverCollectionFetcher.kt similarity index 82% rename from app/src/main/java/org/oxycblt/auxio/image/coil/Components.kt rename to app/src/main/java/org/oxycblt/auxio/image/coil/CoverCollectionFetcher.kt index 12d69d87a..147721fbb 100644 --- a/app/src/main/java/org/oxycblt/auxio/image/coil/Components.kt +++ b/app/src/main/java/org/oxycblt/auxio/image/coil/CoverCollectionFetcher.kt @@ -1,6 +1,6 @@ /* - * Copyright (c) 2021 Auxio Project - * Components.kt is part of Auxio. + * Copyright (c) 2024 Auxio Project + * CoverCollectionFetcher.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 @@ -31,7 +31,6 @@ import coil3.fetch.FetchResult import coil3.fetch.Fetcher import coil3.fetch.ImageFetchResult import coil3.fetch.SourceFetchResult -import coil3.key.Keyer import coil3.request.Options import coil3.size.Dimension import coil3.size.Size @@ -39,36 +38,24 @@ import coil3.size.pxOrElse import java.io.InputStream import javax.inject.Inject import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.asFlow +import kotlinx.coroutines.flow.mapNotNull +import kotlinx.coroutines.flow.take +import kotlinx.coroutines.flow.toList import kotlinx.coroutines.withContext import okio.FileSystem import okio.buffer import okio.source -import org.oxycblt.musikr.cover.Cover +import org.oxycblt.musikr.cover.CoverCollection -class CoverKeyer @Inject constructor() : Keyer { - override fun key(data: Cover, options: Options) = "${data.id}&${options.size}" -} - -class CoverFetcher +class CoverCollectionFetcher private constructor( private val context: Context, - private val cover: Cover, + private val covers: CoverCollection, private val size: Size, ) : Fetcher { override suspend fun fetch(): FetchResult? { - val streams = - when (val cover = cover) { - is Cover.Single -> listOfNotNull(cover.open()) - is Cover.Multi -> - buildList { - for (single in cover.all) { - single.open()?.let { add(it) } - if (size == 4) { - break - } - } - } - } + val streams = covers.covers.asFlow().mapNotNull { it.open() }.take(4).toList() // We don't immediately check for mosaic feasibility from album count alone, as that // does not factor in InputStreams failing to load. Instead, only check once we // definitely have image data to use. @@ -79,6 +66,7 @@ private constructor( withContext(Dispatchers.IO) { streams.forEach(InputStream::close) } } } + // Not enough covers for a mosaic, take the first one (if that even exists) val first = streams.firstOrNull() ?: return null @@ -146,8 +134,8 @@ private constructor( return if (size.mod(2) > 0) size + 1 else size } - class Factory @Inject constructor() : Fetcher.Factory { - override fun create(data: Cover, options: Options, imageLoader: ImageLoader) = - CoverFetcher(options.context, data, options.size) + class Factory @Inject constructor() : Fetcher.Factory { + override fun create(data: CoverCollection, options: Options, imageLoader: ImageLoader) = + CoverCollectionFetcher(options.context, data, options.size) } } diff --git a/app/src/main/java/org/oxycblt/auxio/image/coil/CoverFetcher.kt b/app/src/main/java/org/oxycblt/auxio/image/coil/CoverFetcher.kt new file mode 100644 index 000000000..29bbb430b --- /dev/null +++ b/app/src/main/java/org/oxycblt/auxio/image/coil/CoverFetcher.kt @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2024 Auxio Project + * CoverFetcher.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 . + */ + +package org.oxycblt.auxio.image.coil + +import android.content.Context +import android.graphics.Bitmap +import android.graphics.BitmapFactory +import android.graphics.Canvas +import androidx.core.graphics.drawable.toDrawable +import coil3.ImageLoader +import coil3.asImage +import coil3.decode.DataSource +import coil3.decode.ImageSource +import coil3.fetch.FetchResult +import coil3.fetch.Fetcher +import coil3.fetch.ImageFetchResult +import coil3.fetch.SourceFetchResult +import coil3.request.Options +import coil3.size.Dimension +import coil3.size.Size +import coil3.size.pxOrElse +import java.io.InputStream +import javax.inject.Inject +import okio.FileSystem +import okio.buffer +import okio.source +import org.oxycblt.musikr.cover.Cover + +class CoverFetcher private constructor(private val context: Context, private val cover: Cover) : + Fetcher { + override suspend fun fetch(): FetchResult? { + val stream = cover.open() ?: return null + return SourceFetchResult( + source = ImageSource(stream.source().buffer(), FileSystem.SYSTEM, null), + mimeType = null, + dataSource = DataSource.DISK) + } + + /** Derived from phonograph: https://github.com/kabouzeid/Phonograph */ + private suspend fun createMosaic(streams: List, size: Size): FetchResult { + // Use whatever size coil gives us to create the mosaic. + val mosaicSize = android.util.Size(size.width.mosaicSize(), size.height.mosaicSize()) + val mosaicFrameSize = + Size(Dimension(mosaicSize.width / 2), Dimension(mosaicSize.height / 2)) + + val mosaicBitmap = + Bitmap.createBitmap(mosaicSize.width, mosaicSize.height, Bitmap.Config.ARGB_8888) + val canvas = Canvas(mosaicBitmap) + + var x = 0 + var y = 0 + + // For each stream, create a bitmap scaled to 1/4th of the mosaics combined size + // and place it on a corner of the canvas. + for (stream in streams) { + if (y == mosaicSize.height) { + break + } + + // Crop the bitmap down to a square so it leaves no empty space + // TODO: Work around this + val bitmap = + SquareCropTransformation.INSTANCE.transform( + BitmapFactory.decodeStream(stream), mosaicFrameSize) + canvas.drawBitmap(bitmap, x.toFloat(), y.toFloat(), null) + + x += bitmap.width + if (x == mosaicSize.width) { + x = 0 + y += bitmap.height + } + } + + // It's way easier to map this into a drawable then try to serialize it into an + // BufferedSource. Just make sure we mark it as "sampled" so Coil doesn't try to + // load low-res mosaics into high-res ImageViews. + return ImageFetchResult( + image = mosaicBitmap.toDrawable(context.resources).asImage(), + isSampled = true, + dataSource = DataSource.DISK) + } + + private fun Dimension.mosaicSize(): Int { + // Since we want the mosaic to be perfectly divisible into two, we need to round any + // odd image sizes upwards to prevent the mosaic creation from failing. + val size = pxOrElse { 512 } + return if (size.mod(2) > 0) size + 1 else size + } + + class Factory @Inject constructor() : Fetcher.Factory { + override fun create(data: Cover, options: Options, imageLoader: ImageLoader) = + CoverFetcher(options.context, data) + } +} diff --git a/app/src/main/java/org/oxycblt/auxio/image/coil/Keyers.kt b/app/src/main/java/org/oxycblt/auxio/image/coil/Keyers.kt new file mode 100644 index 000000000..614b1bdf1 --- /dev/null +++ b/app/src/main/java/org/oxycblt/auxio/image/coil/Keyers.kt @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2021 Auxio Project + * Keyers.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 . + */ + +package org.oxycblt.auxio.image.coil + +import coil3.key.Keyer +import coil3.request.Options +import javax.inject.Inject +import org.oxycblt.musikr.cover.Cover +import org.oxycblt.musikr.cover.CoverCollection + +class CoverKeyer @Inject constructor() : Keyer { + override fun key(data: Cover, options: Options) = "${data.id}&${options.size}" +} + +class CoverCollectionKeyer @Inject constructor() : Keyer { + override fun key(data: CoverCollection, options: Options) = + "multi:${data.hashCode()}&${options.size}" +} diff --git a/app/src/main/java/org/oxycblt/auxio/music/RevisionedLibrary.kt b/app/src/main/java/org/oxycblt/auxio/music/RevisionedLibrary.kt index 069e2efe3..d35e5f3e5 100644 --- a/app/src/main/java/org/oxycblt/auxio/music/RevisionedLibrary.kt +++ b/app/src/main/java/org/oxycblt/auxio/music/RevisionedLibrary.kt @@ -70,7 +70,7 @@ class MutableRevisionedStoredCovers(context: Context, private val revision: UUID } } -class RevisionedCover(private val revision: UUID, val inner: Cover.Single) : Cover.Single by inner { +class RevisionedCover(private val revision: UUID, val inner: Cover) : Cover by inner { override val id: String get() = "${inner.id}@${revision}" } diff --git a/musikr/src/main/java/org/oxycblt/musikr/Music.kt b/musikr/src/main/java/org/oxycblt/musikr/Music.kt index 2985f0bf8..011cfc3a6 100644 --- a/musikr/src/main/java/org/oxycblt/musikr/Music.kt +++ b/musikr/src/main/java/org/oxycblt/musikr/Music.kt @@ -26,6 +26,7 @@ import java.util.UUID import kotlinx.parcelize.IgnoredOnParcel import kotlinx.parcelize.Parcelize import org.oxycblt.musikr.cover.Cover +import org.oxycblt.musikr.cover.CoverCollection import org.oxycblt.musikr.fs.Format import org.oxycblt.musikr.fs.Path import org.oxycblt.musikr.tag.Date @@ -276,7 +277,7 @@ interface Song : Music { /** The date the audio file was added to the device, as a unix epoch timestamp. */ val dateAdded: Long /** Useful information to quickly obtain the album cover. */ - val cover: Cover.Single? + val cover: Cover? /** * The parent [Album]. If the metadata did not specify an album, it's parent directory is used * instead. @@ -310,7 +311,7 @@ interface Album : MusicParent { */ val releaseType: ReleaseType /** Cover information from album's songs. */ - val cover: Cover + val covers: CoverCollection /** The duration of all songs in the album, in milliseconds. */ val durationMs: Long /** The earliest date a song in this album was added, as a unix epoch timestamp. */ @@ -340,7 +341,7 @@ interface Artist : MusicParent { */ val durationMs: Long? /** Useful information to quickly obtain a (single) cover for a Genre. */ - val cover: Cover + val covers: CoverCollection /** The [Genre]s of this artist. */ val genres: List } @@ -356,7 +357,7 @@ interface Genre : MusicParent { /** The total duration of the songs in this genre, in milliseconds. */ val durationMs: Long /** Useful information to quickly obtain a (single) cover for a Genre. */ - val cover: Cover + val covers: CoverCollection } /** @@ -370,5 +371,5 @@ interface Playlist : MusicParent { /** The total duration of the songs in this genre, in milliseconds. */ val durationMs: Long /** Useful information to quickly obtain a (single) cover for a Genre. */ - val cover: Cover + val covers: CoverCollection } diff --git a/musikr/src/main/java/org/oxycblt/musikr/cover/Cover.kt b/musikr/src/main/java/org/oxycblt/musikr/cover/Cover.kt index 4344f5787..3be0d33d3 100644 --- a/musikr/src/main/java/org/oxycblt/musikr/cover/Cover.kt +++ b/musikr/src/main/java/org/oxycblt/musikr/cover/Cover.kt @@ -19,29 +19,22 @@ package org.oxycblt.musikr.cover import java.io.InputStream -import org.oxycblt.musikr.Song -sealed interface Cover { +interface Cover { val id: String - interface Single : Cover { - suspend fun open(): InputStream? - } - - class Multi(val all: List) : Cover { - override val id = "multi@${all.hashCode()}" - } + suspend fun open(): InputStream? +} +class CoverCollection private constructor(val covers: List) { companion object { - fun multi(songs: Collection) = order(songs).run { Multi(this) } - - private fun order(songs: Collection) = - songs - .mapNotNull { it.cover } - .groupBy { it.id } - .entries - .sortedByDescending { it.key } - .sortedByDescending { it.value.size } - .map { it.value.first() } + fun from(covers: Collection) = + CoverCollection( + covers + .groupBy { it.id } + .entries + .sortedByDescending { it.key } + .sortedByDescending { it.value.size } + .map { it.value.first() }) } } diff --git a/musikr/src/main/java/org/oxycblt/musikr/cover/StoredCovers.kt b/musikr/src/main/java/org/oxycblt/musikr/cover/StoredCovers.kt index c3d1a3395..841271ec8 100644 --- a/musikr/src/main/java/org/oxycblt/musikr/cover/StoredCovers.kt +++ b/musikr/src/main/java/org/oxycblt/musikr/cover/StoredCovers.kt @@ -21,7 +21,7 @@ package org.oxycblt.musikr.cover import android.content.Context interface StoredCovers { - suspend fun obtain(id: String): Cover.Single? + suspend fun obtain(id: String): Cover? companion object { fun from(context: Context, path: String, format: CoverFormat): MutableStoredCovers = @@ -30,7 +30,7 @@ interface StoredCovers { } interface MutableStoredCovers : StoredCovers { - suspend fun write(data: ByteArray): Cover.Single? + suspend fun write(data: ByteArray): Cover? } private class FileStoredCovers( @@ -45,6 +45,6 @@ private class FileStoredCovers( } } -private class FileCover(override val id: String, private val coverFile: CoverFile) : Cover.Single { +private class FileCover(override val id: String, private val coverFile: CoverFile) : Cover { override suspend fun open() = coverFile.open() } diff --git a/musikr/src/main/java/org/oxycblt/musikr/model/AlbumImpl.kt b/musikr/src/main/java/org/oxycblt/musikr/model/AlbumImpl.kt index 19be4885c..10b7aabc3 100644 --- a/musikr/src/main/java/org/oxycblt/musikr/model/AlbumImpl.kt +++ b/musikr/src/main/java/org/oxycblt/musikr/model/AlbumImpl.kt @@ -22,7 +22,7 @@ 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.cover.CoverCollection import org.oxycblt.musikr.tag.Date import org.oxycblt.musikr.tag.interpret.PreAlbum import org.oxycblt.musikr.util.update @@ -56,7 +56,7 @@ internal class AlbumImpl(private val core: AlbumCore) : Album { 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 covers = CoverCollection.from(core.songs.mapNotNull { it.cover }) override val dates: Date.Range? = core.songs.mapNotNull { it.date }.ifEmpty { null }?.run { Date.Range(min(), max()) } diff --git a/musikr/src/main/java/org/oxycblt/musikr/model/ArtistImpl.kt b/musikr/src/main/java/org/oxycblt/musikr/model/ArtistImpl.kt index 4a2ca3990..e05740401 100644 --- a/musikr/src/main/java/org/oxycblt/musikr/model/ArtistImpl.kt +++ b/musikr/src/main/java/org/oxycblt/musikr/model/ArtistImpl.kt @@ -23,7 +23,7 @@ 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.cover.CoverCollection import org.oxycblt.musikr.tag.interpret.PreArtist import org.oxycblt.musikr.util.update @@ -55,7 +55,7 @@ internal class ArtistImpl(private val core: ArtistCore) : Artist { get() = core.resolveGenres().toList() override val durationMs = core.songs.sumOf { it.durationMs } - override val cover = Cover.multi(core.songs) + override val covers = CoverCollection.from(core.songs.mapNotNull { it.cover }) private val hashCode = 31 * (31 * uid.hashCode() + core.preArtist.hashCode()) * core.songs.hashCode() diff --git a/musikr/src/main/java/org/oxycblt/musikr/model/GenreImpl.kt b/musikr/src/main/java/org/oxycblt/musikr/model/GenreImpl.kt index 203c6c60a..0805f284b 100644 --- a/musikr/src/main/java/org/oxycblt/musikr/model/GenreImpl.kt +++ b/musikr/src/main/java/org/oxycblt/musikr/model/GenreImpl.kt @@ -22,7 +22,7 @@ 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.cover.CoverCollection import org.oxycblt.musikr.tag.interpret.PreGenre import org.oxycblt.musikr.util.update @@ -44,7 +44,7 @@ internal class GenreImpl(private val core: GenreCore) : Genre { override val songs = core.songs override val artists = core.artists override val durationMs = core.songs.sumOf { it.durationMs } - override val cover = Cover.multi(core.songs) + override val covers = CoverCollection.from(core.songs.mapNotNull { it.cover }) private val hashCode = 31 * (31 * uid.hashCode() + core.preGenre.hashCode()) + songs.hashCode() diff --git a/musikr/src/main/java/org/oxycblt/musikr/model/PlaylistImpl.kt b/musikr/src/main/java/org/oxycblt/musikr/model/PlaylistImpl.kt index 602e10f77..a92197df5 100644 --- a/musikr/src/main/java/org/oxycblt/musikr/model/PlaylistImpl.kt +++ b/musikr/src/main/java/org/oxycblt/musikr/model/PlaylistImpl.kt @@ -20,7 +20,7 @@ package org.oxycblt.musikr.model import org.oxycblt.musikr.Playlist import org.oxycblt.musikr.Song -import org.oxycblt.musikr.cover.Cover +import org.oxycblt.musikr.cover.CoverCollection import org.oxycblt.musikr.playlist.interpret.PrePlaylistInfo import org.oxycblt.musikr.tag.Name @@ -33,7 +33,7 @@ internal class PlaylistImpl(val core: PlaylistCore) : Playlist { override val uid = core.prePlaylist.handle.uid override val name: Name.Known = core.prePlaylist.name override val durationMs = core.songs.sumOf { it.durationMs } - override val cover = Cover.multi(core.songs) + override val covers = CoverCollection.from(core.songs.mapNotNull { it.cover }) override val songs = core.songs private var hashCode = diff --git a/musikr/src/main/java/org/oxycblt/musikr/pipeline/ExtractStep.kt b/musikr/src/main/java/org/oxycblt/musikr/pipeline/ExtractStep.kt index 4ac823ea8..042469261 100644 --- a/musikr/src/main/java/org/oxycblt/musikr/pipeline/ExtractStep.kt +++ b/musikr/src/main/java/org/oxycblt/musikr/pipeline/ExtractStep.kt @@ -163,7 +163,7 @@ internal data class RawSong( val file: DeviceFile, val properties: Properties, val tags: ParsedTags, - val cover: Cover.Single? + val cover: Cover? ) internal sealed interface ExtractedMusic { diff --git a/musikr/src/main/java/org/oxycblt/musikr/tag/interpret/PreMusic.kt b/musikr/src/main/java/org/oxycblt/musikr/tag/interpret/PreMusic.kt index 6049b3d68..1632c7547 100644 --- a/musikr/src/main/java/org/oxycblt/musikr/tag/interpret/PreMusic.kt +++ b/musikr/src/main/java/org/oxycblt/musikr/tag/interpret/PreMusic.kt @@ -48,7 +48,7 @@ internal data class PreSong( val replayGainAdjustment: ReplayGainAdjustment, val lastModified: Long, val dateAdded: Long, - val cover: Cover.Single?, + val cover: Cover?, val preAlbum: PreAlbum, val preArtists: List, val preGenres: List