From 35c3e4b534d7618992c1358c10f1970d0267e1c8 Mon Sep 17 00:00:00 2001 From: casey langen Date: Mon, 13 Nov 2017 00:02:49 -0800 Subject: [PATCH] Load album art from the musikcube server by default, if thumbnails exist. Otherwise farm out to lastfm. --- .../remote/playback/SystemService.kt | 100 ++++++++++-------- .../remote/ui/model/AlbumArtLookup.kt | 63 +++++++---- .../remote/ui/view/MainMetadataView.kt | 15 +-- 3 files changed, 101 insertions(+), 77 deletions(-) diff --git a/src/musikdroid/app/src/main/java/io/casey/musikcube/remote/playback/SystemService.kt b/src/musikdroid/app/src/main/java/io/casey/musikcube/remote/playback/SystemService.kt index c12577c61..d80644944 100644 --- a/src/musikdroid/app/src/main/java/io/casey/musikcube/remote/playback/SystemService.kt +++ b/src/musikdroid/app/src/main/java/io/casey/musikcube/remote/playback/SystemService.kt @@ -3,6 +3,7 @@ package io.casey.musikcube.remote.playback import android.app.* import android.content.* import android.graphics.Bitmap +import android.graphics.drawable.Drawable import android.media.AudioManager import android.os.Build import android.os.IBinder @@ -22,6 +23,7 @@ import com.bumptech.glide.request.transition.Transition import io.casey.musikcube.remote.Application import io.casey.musikcube.remote.MainActivity import io.casey.musikcube.remote.R +import io.casey.musikcube.remote.data.ITrack import io.casey.musikcube.remote.ui.extension.fallback import io.casey.musikcube.remote.ui.model.albumart.Size import io.casey.musikcube.remote.ui.view.GlideApp @@ -175,10 +177,8 @@ class SystemService : Service() { private fun updateMediaSessionPlaybackState() { var mediaSessionState = PlaybackStateCompat.STATE_STOPPED - var title = "-" - var album = "-" - var artist = "-" var duration = 0 + var playing: ITrack? = null if (playback != null) { when (playback?.playbackState) { @@ -188,15 +188,14 @@ class SystemService : Service() { else -> { } } - val playing = playback!!.playingTrack - title = fallback(playing.title, "-") - album = fallback(playing.album, "-") - artist = fallback(playing.artist, "-") + playing = playback!!.playingTrack duration = ((playback?.duration ?: 0.0) * 1000).toInt() } - updateMetadata(title, artist, album, duration) - updateNotification(title, artist, album, mediaSessionState) + Log.e(TAG, String.format("updatePlaybackState: %s", playing)) + + updateMetadata(playing, duration) + updateNotification(playing, mediaSessionState) mediaSession?.setPlaybackState(PlaybackStateCompat.Builder() .setState(mediaSessionState, 0, 0f) @@ -204,60 +203,73 @@ class SystemService : Service() { .build()) } - private fun downloadAlbumArt(title: String, artist: String, album: String, duration: Int) { - albumArt.reset() - albumArt.url = getAlbumArtUrl(artist, album, Size.Mega) + private fun downloadAlbumArt(track: ITrack, duration: Int) { + if (!albumArt.same(track) || albumArt.request == null) { + if (track.artist.isNotBlank() && track.album.isNotBlank()) { + val url = getAlbumArtUrl(track, Size.Mega) - val originalRequest = GlideApp.with(applicationContext) - .asBitmap().load(albumArt.url).apply(BITMAP_OPTIONS) + val originalRequest = GlideApp.with(applicationContext) + .asBitmap().load(url).apply(BITMAP_OPTIONS) - albumArt.request = originalRequest + albumArt.reset(track) + albumArt.request = originalRequest + albumArt.target = originalRequest.into(object : SimpleTarget(Target.SIZE_ORIGINAL, Target.SIZE_ORIGINAL) { + override fun onResourceReady(bitmap: Bitmap?, transition: Transition?) { + /* make sure the instance's current request is the same as this request. it's + possible we had another download request come in before this one finished */ + if (albumArt.request == originalRequest) { + albumArt.bitmap = bitmap + albumArt.request = null + updateMetadata(track, duration) + } + } - albumArt.target = originalRequest.into(object: SimpleTarget(Target.SIZE_ORIGINAL, Target.SIZE_ORIGINAL) { - override fun onResourceReady(bitmap: Bitmap?, transition: Transition?) { - /* make sure the instance's current request is the samea s this request. it's - possible we had another download request come in before this one finished */ - if (albumArt.request == originalRequest) { - albumArt.bitmap = bitmap - updateMetadata(title, artist, album, duration) - } - albumArt.request = null + override fun onLoadFailed(errorDrawable: Drawable?) { + if (albumArt.request == originalRequest) { + albumArt.request = null + } + } + }) } - }) + } + else { + Log.d(TAG, "downloadAlbumArt already in flight") + } } - private fun updateMetadata(title: String, artist: String, album: String, duration: Int) { + private fun updateMetadata(track: ITrack?, duration: Int) { var currentImage: Bitmap? = null val albumArtEnabledInSettings = this.prefs.getBoolean( Prefs.Key.ALBUM_ART_ENABLED, Prefs.Default.ALBUM_ART_ENABLED) - if (albumArtEnabledInSettings) { - val url = getAlbumArtUrl(artist, album, Size.Mega) - if ("-" != artist && "-" != album && url != albumArt.url) { - downloadAlbumArt(title, artist, album, duration) - } - else if (albumArt.url == url) { - if (albumArt.bitmap == null) { - downloadAlbumArt(title, artist, album, duration) - } + if (albumArtEnabledInSettings && track != null && albumArt.same(track)) { + if (albumArt.bitmap != null) { currentImage = albumArt.bitmap } } + if (track != null && currentImage == null) { + downloadAlbumArt(track, duration) + } + mediaSession?.setMetadata(MediaMetadataCompat.Builder() - .putString(MediaMetadataCompat.METADATA_KEY_ARTIST, artist) - .putString(MediaMetadataCompat.METADATA_KEY_ALBUM, album) - .putString(MediaMetadataCompat.METADATA_KEY_TITLE, title) + .putString(MediaMetadataCompat.METADATA_KEY_ARTIST, track?.artist ?: "-") + .putString(MediaMetadataCompat.METADATA_KEY_ALBUM, track?.album ?: "-") + .putString(MediaMetadataCompat.METADATA_KEY_TITLE, track?.title ?: "-") .putLong(MediaMetadataCompat.METADATA_KEY_DURATION, duration.toLong()) .putBitmap(MediaMetadataCompat.METADATA_KEY_ALBUM_ART, currentImage) .build()) } - private fun updateNotification(title: String, artist: String, album: String, state: Int) { + private fun updateNotification(track: ITrack?, state: Int) { val contentIntent = PendingIntent.getActivity( applicationContext, 1, MainActivity.getStartIntent(this), 0) + val title = fallback(track?.title, "-") + val artist = fallback(track?.artist, "-") + val album = fallback(track?.album, "-") + val notification = NotificationCompat.Builder(this, NOTIFICATION_CHANNEL) .setSmallIcon(R.drawable.ic_notification) .setContentTitle(title) @@ -461,19 +473,23 @@ class SystemService : Service() { } private class AlbumArt { - var url: String? = null var target: SimpleTarget? = null var request: GlideRequest? = null var bitmap: Bitmap? = null + var track: ITrack? = null - fun reset() { + fun reset(t: ITrack? = null) { if (target != null && target?.request != null) { target?.request?.clear() } - url = null bitmap = null request = null target = null + track = t + } + + fun same(other: ITrack?): Boolean { + return track != null && other != null && other.externalId == track?.externalId } } diff --git a/src/musikdroid/app/src/main/java/io/casey/musikcube/remote/ui/model/AlbumArtLookup.kt b/src/musikdroid/app/src/main/java/io/casey/musikcube/remote/ui/model/AlbumArtLookup.kt index 3fe99aa8b..f233a38a1 100644 --- a/src/musikdroid/app/src/main/java/io/casey/musikcube/remote/ui/model/AlbumArtLookup.kt +++ b/src/musikdroid/app/src/main/java/io/casey/musikcube/remote/ui/model/AlbumArtLookup.kt @@ -1,10 +1,14 @@ package io.casey.musikcube.remote.ui.model.albumart +import android.content.Context +import android.content.SharedPreferences import android.util.LruCache +import io.casey.musikcube.remote.Application import io.casey.musikcube.remote.data.IAlbum import io.casey.musikcube.remote.data.ITrack import io.casey.musikcube.remote.util.NetworkUtil import io.casey.musikcube.remote.util.Strings +import io.casey.musikcube.remote.websocket.Prefs import okhttp3.* import org.json.JSONException import org.json.JSONObject @@ -27,12 +31,35 @@ enum class Size constructor(internal val key: String, internal val order: Int) { } } +/* used to strip extraneous tags */ +private val badPatterns = arrayOf( + Pattern.compile("(?i)^" + Pattern.quote("[") + "CD" + Pattern.quote("]")), + Pattern.compile("(?i)" + Pattern.quote("(") + "disc \\d*" + Pattern.quote(")") + "$"), + Pattern.compile("(?i)" + Pattern.quote("[") + "disc \\d*" + Pattern.quote("]") + "$"), + Pattern.compile("(?i)" + Pattern.quote("(+") + "video" + Pattern.quote(")") + "$"), + Pattern.compile("(?i)" + Pattern.quote("[+") + "video" + Pattern.quote("]") + "$"), + Pattern.compile("(?i)" + Pattern.quote("(") + "explicit" + Pattern.quote(")") + "$"), + Pattern.compile("(?i)" + Pattern.quote("[") + "explicit" + Pattern.quote("]") + "$"), + Pattern.compile("(?i)" + Pattern.quote("[+") + "digital booklet" + Pattern.quote("]") + "$")) + +/* http://www.last.fm/group/Last.fm+Web+Services/forum/21604/_/522900 -- it's ok to +put our key in the code */ +private val lastFmFormatUrl = + "http://ws.audioscrobbler.com/2.0/?method=album.getinfo&api_key=" + + "502c69bd3f9946e8e0beee4fcb28c4cd&artist=%s&album=%s&format=json&size=%s" + +private val urlCache = LruCache(500) +private val badUrlCache = LruCache(100) +private val inFlight = mutableMapOf() + fun getUrl(album: IAlbum, size: Size = Size.Small): String? { - return getUrl(album.albumArtist, album.name, size) + return getThumbnailUrl(album.thumbnailId) + ?: getUrl(album.albumArtist, album.name, size) } fun getUrl(track: ITrack, size: Size = Size.Small): String? { - return getUrl(track.artist, track.album, size) + return getThumbnailUrl(track.thumbnailId) + ?: getUrl(track.artist, track.album, size) } fun getUrl(artist: String = "", album: String = "", size: Size = Size.Small): String? { @@ -172,26 +199,20 @@ fun intercept(req: Request): Request? { return result } -/* used to strip extraneous tags */ -private val badPatterns = arrayOf( - Pattern.compile("(?i)^" + Pattern.quote("[") + "CD" + Pattern.quote("]")), - Pattern.compile("(?i)" + Pattern.quote("(") + "disc \\d*" + Pattern.quote(")") + "$"), - Pattern.compile("(?i)" + Pattern.quote("[") + "disc \\d*" + Pattern.quote("]") + "$"), - Pattern.compile("(?i)" + Pattern.quote("(+") + "video" + Pattern.quote(")") + "$"), - Pattern.compile("(?i)" + Pattern.quote("[+") + "video" + Pattern.quote("]") + "$"), - Pattern.compile("(?i)" + Pattern.quote("(") + "explicit" + Pattern.quote(")") + "$"), - Pattern.compile("(?i)" + Pattern.quote("[") + "explicit" + Pattern.quote("]") + "$"), - Pattern.compile("(?i)" + Pattern.quote("[+") + "digital booklet" + Pattern.quote("]") + "$")) +private val prefs by lazy { + Application.instance!!.getSharedPreferences(Prefs.NAME, Context.MODE_PRIVATE) +} -/* http://www.last.fm/group/Last.fm+Web+Services/forum/21604/_/522900 -- it's ok to -put our key in the code */ -private val lastFmFormatUrl = - "http://ws.audioscrobbler.com/2.0/?method=album.getinfo&api_key=" + - "502c69bd3f9946e8e0beee4fcb28c4cd&artist=%s&album=%s&format=json&size=%s" - -private val urlCache = LruCache(500) -private val badUrlCache = LruCache(100) -private val inFlight = mutableMapOf() +private fun getThumbnailUrl(id: Long): String? { + if (id > 0) { + val host = prefs.getString(Prefs.Key.ADDRESS, Prefs.Default.ADDRESS) + val port = prefs.getInt(Prefs.Key.AUDIO_PORT, Prefs.Default.MAIN_PORT) + val ssl = prefs.getBoolean(Prefs.Key.SSL_ENABLED, Prefs.Default.SSL_ENABLED) + val scheme = if (ssl) "https" else "http" + return "$scheme://$host:$port/thumbnail/$id" + } + return null +} private fun dejunk(album: String): String { var result = album diff --git a/src/musikdroid/app/src/main/java/io/casey/musikcube/remote/ui/view/MainMetadataView.kt b/src/musikdroid/app/src/main/java/io/casey/musikcube/remote/ui/view/MainMetadataView.kt index a251abf60..7ba5bbc77 100644 --- a/src/musikdroid/app/src/main/java/io/casey/musikcube/remote/ui/view/MainMetadataView.kt +++ b/src/musikdroid/app/src/main/java/io/casey/musikcube/remote/ui/view/MainMetadataView.kt @@ -142,7 +142,7 @@ class MainMetadataView : FrameLayout { setMetadataDisplayMode(DisplayMode.NoArtwork) } else { - val newUrl = getAlbumArtUrl(artist, album, Size.Mega) ?: "" + val newUrl = getAlbumArtUrl(playing, Size.Mega) ?: "" if (newUrl != loadedAlbumArtUrl) { updateAlbumArt(newUrl) } @@ -226,19 +226,6 @@ class MainMetadataView : FrameLayout { artistAndAlbumWithArt.text = builder } -// private val thumbnailUrl: String -// get() { -// val playing = playbackService.playingTrack -// if (playing.thumbnailId > 0) { -// val host = prefs.getString(Prefs.Key.ADDRESS, Prefs.Default.ADDRESS) -// val port = prefs.getInt(Prefs.Key.AUDIO_PORT, Prefs.Default.MAIN_PORT) -// val ssl = prefs.getBoolean(Prefs.Key.SSL_ENABLED, Prefs.Default.SSL_ENABLED) -// val scheme = if (ssl) "https" else "http" -// return "$scheme://$host:$port/thumbnail/${playing.thumbnailId}" -// } -// return "" -// } - private fun updateAlbumArt(albumArtUrl: String = "") { if (playbackService.playbackState == PlaybackState.Stopped) { setMetadataDisplayMode(DisplayMode.NoArtwork)