mirror of
https://github.com/clangen/musikcube.git
synced 2025-01-02 11:58:27 +00:00
Load album art from the musikcube server by default, if thumbnails
exist. Otherwise farm out to lastfm.
This commit is contained in:
parent
850664a519
commit
35c3e4b534
@ -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<Bitmap>(Target.SIZE_ORIGINAL, Target.SIZE_ORIGINAL) {
|
||||
override fun onResourceReady(bitmap: Bitmap?, transition: Transition<in Bitmap>?) {
|
||||
/* 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<Bitmap>(Target.SIZE_ORIGINAL, Target.SIZE_ORIGINAL) {
|
||||
override fun onResourceReady(bitmap: Bitmap?, transition: Transition<in Bitmap>?) {
|
||||
/* 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<Bitmap>? = null
|
||||
var request: GlideRequest<Bitmap>? = 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
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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<String, String>(500)
|
||||
private val badUrlCache = LruCache<String, Boolean>(100)
|
||||
private val inFlight = mutableMapOf<String, CountDownLatch>()
|
||||
|
||||
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<String, String>(500)
|
||||
private val badUrlCache = LruCache<String, Boolean>(100)
|
||||
private val inFlight = mutableMapOf<String, CountDownLatch>()
|
||||
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
|
||||
|
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user