Skip to content

Commit

Permalink
feat: support refreshing release cover arts
Browse files Browse the repository at this point in the history
fixes #1082
  • Loading branch information
lydavid committed Sep 12, 2024
1 parent 6abe4a7 commit b92d9da
Show file tree
Hide file tree
Showing 7 changed files with 53 additions and 31 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,42 +3,60 @@ package ly.david.musicsearch.data.coverart
import io.ktor.client.plugins.ClientRequestException
import io.ktor.http.HttpStatusCode
import ly.david.musicsearch.core.logging.Logger
import ly.david.musicsearch.shared.domain.image.ImageUrlDao
import ly.david.musicsearch.shared.domain.image.ImageUrls
import ly.david.musicsearch.data.coverart.api.CoverArtArchiveApi
import ly.david.musicsearch.data.coverart.api.toImageUrlsList
import ly.david.musicsearch.shared.domain.image.ImageUrlDao
import ly.david.musicsearch.shared.domain.image.ImageUrls
import ly.david.musicsearch.shared.domain.release.ReleaseImageRepository

internal class ReleaseImageRepositoryImpl(
private val coverArtArchiveApi: CoverArtArchiveApi,
private val imageUrlDao: ImageUrlDao,
private val logger: Logger,
) : ReleaseImageRepository {
override suspend fun getReleaseCoverArtUrlsFromNetworkAndSave(

override suspend fun getReleaseImageUrl(
releaseId: String,
thumbnail: Boolean,
forceRefresh: Boolean,
): String {
if (forceRefresh) {
imageUrlDao.deleteAllUrlsById(releaseId)
}

val cachedImageUrls = imageUrlDao.getAllUrls(releaseId)
return if (cachedImageUrls.isNotEmpty()) {
val frontCoverArt = cachedImageUrls.first()
return if (thumbnail) frontCoverArt.thumbnailUrl else frontCoverArt.largeUrl
} else {
getReleaseImageUrlFromNetwork(releaseId, thumbnail)
}
}

private suspend fun getReleaseImageUrlFromNetwork(
releaseId: String,
thumbnail: Boolean,
): String {
return try {
val coverArts = coverArtArchiveApi.getReleaseCoverArts(releaseId)
val imageUrls: MutableList<ImageUrls> = coverArts.toImageUrlsList().toMutableList()

// We use an empty ImageUrls to represent that we've searched but failed to find any images.
if (imageUrls.isEmpty()) {
imageUrls.add(ImageUrls())
}

imageUrlDao.saveUrls(
mbid = releaseId,
imageUrls = imageUrls,
)
return if (thumbnail) imageUrls.first().thumbnailUrl else imageUrls.first().largeUrl
val frontCoverArt = imageUrls.first()
return if (thumbnail) frontCoverArt.thumbnailUrl else frontCoverArt.largeUrl
} catch (ex: ClientRequestException) {
if (ex.response.status == HttpStatusCode.NotFound) {
imageUrlDao.saveUrls(
mbid = releaseId,
imageUrls = listOf(
ImageUrls(
thumbnailUrl = "",
largeUrl = "",
),
),
imageUrls = listOf(ImageUrls()),
)
} else {
logger.e(ex)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,6 @@ class ArtistImageRepositoryImpl(
private val logger: Logger,
) : ArtistImageRepository {

/**
* Returns a url to the artist image.
* Empty if none found.
*
* Also saves it to db.
*/
override suspend fun getArtistImageUrl(
artistDetailsModel: ArtistDetailsModel,
forceRefresh: Boolean,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
package ly.david.musicsearch.shared.domain.artist

interface ArtistImageRepository {

/**
* Returns a url to the artist image.
* Empty if none found.
*
* Also saves it to db.
*/
suspend fun getArtistImageUrl(
artistDetailsModel: ArtistDetailsModel,
forceRefresh: Boolean,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,10 @@ interface ReleaseImageRepository {
*
* Make sure to handle non-404 errors at call site.
*/
suspend fun getReleaseCoverArtUrlsFromNetworkAndSave(
suspend fun getReleaseImageUrl(
releaseId: String,
thumbnail: Boolean,
forceRefresh: Boolean,
): String

fun getAllUrls(mbid: String): List<ImageUrls>
Expand Down
1 change: 1 addition & 0 deletions shared/feature/details/config/baseline.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@
<CurrentIssues>
<ID>CyclomaticComplexMethod:AreaPresenter.kt$AreaPresenter$@Composable override fun present(): AreaUiState</ID>
<ID>CyclomaticComplexMethod:ArtistPresenter.kt$ArtistPresenter$@Composable override fun present(): ArtistUiState</ID>
<ID>CyclomaticComplexMethod:ReleasePresenter.kt$ReleasePresenter$@Composable override fun present(): ReleaseUiState</ID>
</CurrentIssues>
</SmellBaseline>
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,13 @@ import com.slack.circuit.runtime.presenter.Presenter
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.toPersistentList
import ly.david.musicsearch.core.logging.Logger
import ly.david.musicsearch.data.common.network.RecoverableNetworkException
import ly.david.musicsearch.shared.domain.artist.getDisplayNames
import ly.david.musicsearch.shared.domain.getNameWithDisambiguation
import ly.david.musicsearch.shared.domain.history.LookupHistory
import ly.david.musicsearch.shared.domain.history.usecase.IncrementLookupHistory
import ly.david.musicsearch.shared.domain.network.MusicBrainzEntity
import ly.david.musicsearch.shared.domain.release.ReleaseDetailsModel
import ly.david.musicsearch.data.common.network.RecoverableNetworkException
import ly.david.musicsearch.shared.domain.history.usecase.IncrementLookupHistory
import ly.david.musicsearch.shared.domain.release.ReleaseImageRepository
import ly.david.musicsearch.shared.domain.release.ReleaseRepository
import ly.david.musicsearch.ui.common.artist.ArtistsByEntityPresenter
Expand Down Expand Up @@ -87,7 +87,6 @@ internal class ReleasePresenter(
title = releaseDetailsModel.getNameWithDisambiguation()
subtitle = "Release by ${releaseDetailsModel.artistCredits.getDisplayNames()}"
release = releaseDetailsModel
imageUrl = fetchReleaseImage(releaseDetailsModel)
isError = false
} catch (ex: RecoverableNetworkException) {
logger.e(ex)
Expand All @@ -105,6 +104,16 @@ internal class ReleasePresenter(
}
}

LaunchedEffect(forceRefreshDetails, release) {
release?.let { release ->
imageUrl = releaseImageRepository.getReleaseImageUrl(
releaseId = release.id,
thumbnail = false,
forceRefresh = forceRefreshDetails,
)
}
}

LaunchedEffect(
key1 = query,
key2 = selectedTab,
Expand Down Expand Up @@ -203,16 +212,6 @@ internal class ReleasePresenter(
eventSink = ::eventSink,
)
}

private suspend fun fetchReleaseImage(
releaseDetailsModel: ReleaseDetailsModel,
): String {
val imageUrl = releaseDetailsModel.imageUrl
return imageUrl ?: releaseImageRepository.getReleaseCoverArtUrlsFromNetworkAndSave(
releaseId = releaseDetailsModel.id,
thumbnail = false,
)
}
}

@Stable
Expand Down Expand Up @@ -241,5 +240,6 @@ internal sealed interface ReleaseUiEvent : CircuitUiEvent {
val id: String,
val title: String?,
) : ReleaseUiEvent

data object ClickImage : ReleaseUiEvent
}
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,10 @@ class ReleasesByEntityPresenter(
is ReleasesByEntityUiEvent.RequestForMissingCoverArtUrl -> {
scope.launch {
try {
releaseImageRepository.getReleaseCoverArtUrlsFromNetworkAndSave(
releaseImageRepository.getReleaseImageUrl(
releaseId = event.entityId,
thumbnail = true,
forceRefresh = false,
)
} catch (ex: Exception) {
logger.e(ex)
Expand Down

0 comments on commit b92d9da

Please sign in to comment.