mirror of
https://github.com/clangen/musikcube.git
synced 2024-10-02 13:02:35 +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.app.*
|
||||||
import android.content.*
|
import android.content.*
|
||||||
import android.graphics.Bitmap
|
import android.graphics.Bitmap
|
||||||
|
import android.graphics.drawable.Drawable
|
||||||
import android.media.AudioManager
|
import android.media.AudioManager
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.IBinder
|
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.Application
|
||||||
import io.casey.musikcube.remote.MainActivity
|
import io.casey.musikcube.remote.MainActivity
|
||||||
import io.casey.musikcube.remote.R
|
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.extension.fallback
|
||||||
import io.casey.musikcube.remote.ui.model.albumart.Size
|
import io.casey.musikcube.remote.ui.model.albumart.Size
|
||||||
import io.casey.musikcube.remote.ui.view.GlideApp
|
import io.casey.musikcube.remote.ui.view.GlideApp
|
||||||
@ -175,10 +177,8 @@ class SystemService : Service() {
|
|||||||
private fun updateMediaSessionPlaybackState() {
|
private fun updateMediaSessionPlaybackState() {
|
||||||
var mediaSessionState = PlaybackStateCompat.STATE_STOPPED
|
var mediaSessionState = PlaybackStateCompat.STATE_STOPPED
|
||||||
|
|
||||||
var title = "-"
|
|
||||||
var album = "-"
|
|
||||||
var artist = "-"
|
|
||||||
var duration = 0
|
var duration = 0
|
||||||
|
var playing: ITrack? = null
|
||||||
|
|
||||||
if (playback != null) {
|
if (playback != null) {
|
||||||
when (playback?.playbackState) {
|
when (playback?.playbackState) {
|
||||||
@ -188,15 +188,14 @@ class SystemService : Service() {
|
|||||||
else -> { }
|
else -> { }
|
||||||
}
|
}
|
||||||
|
|
||||||
val playing = playback!!.playingTrack
|
playing = playback!!.playingTrack
|
||||||
title = fallback(playing.title, "-")
|
|
||||||
album = fallback(playing.album, "-")
|
|
||||||
artist = fallback(playing.artist, "-")
|
|
||||||
duration = ((playback?.duration ?: 0.0) * 1000).toInt()
|
duration = ((playback?.duration ?: 0.0) * 1000).toInt()
|
||||||
}
|
}
|
||||||
|
|
||||||
updateMetadata(title, artist, album, duration)
|
Log.e(TAG, String.format("updatePlaybackState: %s", playing))
|
||||||
updateNotification(title, artist, album, mediaSessionState)
|
|
||||||
|
updateMetadata(playing, duration)
|
||||||
|
updateNotification(playing, mediaSessionState)
|
||||||
|
|
||||||
mediaSession?.setPlaybackState(PlaybackStateCompat.Builder()
|
mediaSession?.setPlaybackState(PlaybackStateCompat.Builder()
|
||||||
.setState(mediaSessionState, 0, 0f)
|
.setState(mediaSessionState, 0, 0f)
|
||||||
@ -204,60 +203,73 @@ class SystemService : Service() {
|
|||||||
.build())
|
.build())
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun downloadAlbumArt(title: String, artist: String, album: String, duration: Int) {
|
private fun downloadAlbumArt(track: ITrack, duration: Int) {
|
||||||
albumArt.reset()
|
if (!albumArt.same(track) || albumArt.request == null) {
|
||||||
albumArt.url = getAlbumArtUrl(artist, album, Size.Mega)
|
if (track.artist.isNotBlank() && track.album.isNotBlank()) {
|
||||||
|
val url = getAlbumArtUrl(track, Size.Mega)
|
||||||
|
|
||||||
val originalRequest = GlideApp.with(applicationContext)
|
val originalRequest = GlideApp.with(applicationContext)
|
||||||
.asBitmap().load(albumArt.url).apply(BITMAP_OPTIONS)
|
.asBitmap().load(url).apply(BITMAP_OPTIONS)
|
||||||
|
|
||||||
|
albumArt.reset(track)
|
||||||
albumArt.request = originalRequest
|
albumArt.request = originalRequest
|
||||||
|
|
||||||
albumArt.target = originalRequest.into(object : SimpleTarget<Bitmap>(Target.SIZE_ORIGINAL, Target.SIZE_ORIGINAL) {
|
albumArt.target = originalRequest.into(object : SimpleTarget<Bitmap>(Target.SIZE_ORIGINAL, Target.SIZE_ORIGINAL) {
|
||||||
override fun onResourceReady(bitmap: Bitmap?, transition: Transition<in Bitmap>?) {
|
override fun onResourceReady(bitmap: Bitmap?, transition: Transition<in Bitmap>?) {
|
||||||
/* make sure the instance's current request is the same as this request. it's
|
/* 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 */
|
possible we had another download request come in before this one finished */
|
||||||
if (albumArt.request == originalRequest) {
|
if (albumArt.request == originalRequest) {
|
||||||
albumArt.bitmap = bitmap
|
albumArt.bitmap = bitmap
|
||||||
updateMetadata(title, artist, album, duration)
|
|
||||||
}
|
|
||||||
albumArt.request = null
|
albumArt.request = null
|
||||||
|
updateMetadata(track, duration)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
var currentImage: Bitmap? = null
|
||||||
|
|
||||||
val albumArtEnabledInSettings = this.prefs.getBoolean(
|
val albumArtEnabledInSettings = this.prefs.getBoolean(
|
||||||
Prefs.Key.ALBUM_ART_ENABLED, Prefs.Default.ALBUM_ART_ENABLED)
|
Prefs.Key.ALBUM_ART_ENABLED, Prefs.Default.ALBUM_ART_ENABLED)
|
||||||
|
|
||||||
if (albumArtEnabledInSettings) {
|
if (albumArtEnabledInSettings && track != null && albumArt.same(track)) {
|
||||||
val url = getAlbumArtUrl(artist, album, Size.Mega)
|
if (albumArt.bitmap != null) {
|
||||||
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)
|
|
||||||
}
|
|
||||||
currentImage = albumArt.bitmap
|
currentImage = albumArt.bitmap
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (track != null && currentImage == null) {
|
||||||
|
downloadAlbumArt(track, duration)
|
||||||
|
}
|
||||||
|
|
||||||
mediaSession?.setMetadata(MediaMetadataCompat.Builder()
|
mediaSession?.setMetadata(MediaMetadataCompat.Builder()
|
||||||
.putString(MediaMetadataCompat.METADATA_KEY_ARTIST, artist)
|
.putString(MediaMetadataCompat.METADATA_KEY_ARTIST, track?.artist ?: "-")
|
||||||
.putString(MediaMetadataCompat.METADATA_KEY_ALBUM, album)
|
.putString(MediaMetadataCompat.METADATA_KEY_ALBUM, track?.album ?: "-")
|
||||||
.putString(MediaMetadataCompat.METADATA_KEY_TITLE, title)
|
.putString(MediaMetadataCompat.METADATA_KEY_TITLE, track?.title ?: "-")
|
||||||
.putLong(MediaMetadataCompat.METADATA_KEY_DURATION, duration.toLong())
|
.putLong(MediaMetadataCompat.METADATA_KEY_DURATION, duration.toLong())
|
||||||
.putBitmap(MediaMetadataCompat.METADATA_KEY_ALBUM_ART, currentImage)
|
.putBitmap(MediaMetadataCompat.METADATA_KEY_ALBUM_ART, currentImage)
|
||||||
.build())
|
.build())
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateNotification(title: String, artist: String, album: String, state: Int) {
|
private fun updateNotification(track: ITrack?, state: Int) {
|
||||||
val contentIntent = PendingIntent.getActivity(
|
val contentIntent = PendingIntent.getActivity(
|
||||||
applicationContext, 1, MainActivity.getStartIntent(this), 0)
|
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)
|
val notification = NotificationCompat.Builder(this, NOTIFICATION_CHANNEL)
|
||||||
.setSmallIcon(R.drawable.ic_notification)
|
.setSmallIcon(R.drawable.ic_notification)
|
||||||
.setContentTitle(title)
|
.setContentTitle(title)
|
||||||
@ -461,19 +473,23 @@ class SystemService : Service() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private class AlbumArt {
|
private class AlbumArt {
|
||||||
var url: String? = null
|
|
||||||
var target: SimpleTarget<Bitmap>? = null
|
var target: SimpleTarget<Bitmap>? = null
|
||||||
var request: GlideRequest<Bitmap>? = null
|
var request: GlideRequest<Bitmap>? = null
|
||||||
var bitmap: Bitmap? = null
|
var bitmap: Bitmap? = null
|
||||||
|
var track: ITrack? = null
|
||||||
|
|
||||||
fun reset() {
|
fun reset(t: ITrack? = null) {
|
||||||
if (target != null && target?.request != null) {
|
if (target != null && target?.request != null) {
|
||||||
target?.request?.clear()
|
target?.request?.clear()
|
||||||
}
|
}
|
||||||
url = null
|
|
||||||
bitmap = null
|
bitmap = null
|
||||||
request = null
|
request = null
|
||||||
target = 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
|
package io.casey.musikcube.remote.ui.model.albumart
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.SharedPreferences
|
||||||
import android.util.LruCache
|
import android.util.LruCache
|
||||||
|
import io.casey.musikcube.remote.Application
|
||||||
import io.casey.musikcube.remote.data.IAlbum
|
import io.casey.musikcube.remote.data.IAlbum
|
||||||
import io.casey.musikcube.remote.data.ITrack
|
import io.casey.musikcube.remote.data.ITrack
|
||||||
import io.casey.musikcube.remote.util.NetworkUtil
|
import io.casey.musikcube.remote.util.NetworkUtil
|
||||||
import io.casey.musikcube.remote.util.Strings
|
import io.casey.musikcube.remote.util.Strings
|
||||||
|
import io.casey.musikcube.remote.websocket.Prefs
|
||||||
import okhttp3.*
|
import okhttp3.*
|
||||||
import org.json.JSONException
|
import org.json.JSONException
|
||||||
import org.json.JSONObject
|
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? {
|
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? {
|
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? {
|
fun getUrl(artist: String = "", album: String = "", size: Size = Size.Small): String? {
|
||||||
@ -172,26 +199,20 @@ fun intercept(req: Request): Request? {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
/* used to strip extraneous tags */
|
private val prefs by lazy {
|
||||||
private val badPatterns = arrayOf(
|
Application.instance!!.getSharedPreferences(Prefs.NAME, Context.MODE_PRIVATE)
|
||||||
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
|
private fun getThumbnailUrl(id: Long): String? {
|
||||||
put our key in the code */
|
if (id > 0) {
|
||||||
private val lastFmFormatUrl =
|
val host = prefs.getString(Prefs.Key.ADDRESS, Prefs.Default.ADDRESS)
|
||||||
"http://ws.audioscrobbler.com/2.0/?method=album.getinfo&api_key=" +
|
val port = prefs.getInt(Prefs.Key.AUDIO_PORT, Prefs.Default.MAIN_PORT)
|
||||||
"502c69bd3f9946e8e0beee4fcb28c4cd&artist=%s&album=%s&format=json&size=%s"
|
val ssl = prefs.getBoolean(Prefs.Key.SSL_ENABLED, Prefs.Default.SSL_ENABLED)
|
||||||
|
val scheme = if (ssl) "https" else "http"
|
||||||
private val urlCache = LruCache<String, String>(500)
|
return "$scheme://$host:$port/thumbnail/$id"
|
||||||
private val badUrlCache = LruCache<String, Boolean>(100)
|
}
|
||||||
private val inFlight = mutableMapOf<String, CountDownLatch>()
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
private fun dejunk(album: String): String {
|
private fun dejunk(album: String): String {
|
||||||
var result = album
|
var result = album
|
||||||
|
@ -142,7 +142,7 @@ class MainMetadataView : FrameLayout {
|
|||||||
setMetadataDisplayMode(DisplayMode.NoArtwork)
|
setMetadataDisplayMode(DisplayMode.NoArtwork)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
val newUrl = getAlbumArtUrl(artist, album, Size.Mega) ?: ""
|
val newUrl = getAlbumArtUrl(playing, Size.Mega) ?: ""
|
||||||
if (newUrl != loadedAlbumArtUrl) {
|
if (newUrl != loadedAlbumArtUrl) {
|
||||||
updateAlbumArt(newUrl)
|
updateAlbumArt(newUrl)
|
||||||
}
|
}
|
||||||
@ -226,19 +226,6 @@ class MainMetadataView : FrameLayout {
|
|||||||
artistAndAlbumWithArt.text = builder
|
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 = "") {
|
private fun updateAlbumArt(albumArtUrl: String = "") {
|
||||||
if (playbackService.playbackState == PlaybackState.Stopped) {
|
if (playbackService.playbackState == PlaybackState.Stopped) {
|
||||||
setMetadataDisplayMode(DisplayMode.NoArtwork)
|
setMetadataDisplayMode(DisplayMode.NoArtwork)
|
||||||
|
Loading…
Reference in New Issue
Block a user