Fixed crash on coverless albums.

This commit is contained in:
Antoine POPINEAU 2019-10-22 21:56:33 +02:00
parent 6139591bd1
commit 78468167ca
No known key found for this signature in database
GPG Key ID: A78AC64694F84063
18 changed files with 81 additions and 33 deletions

View File

@ -224,13 +224,13 @@ class MainActivity : AppCompatActivity() {
now_playing_details_progress.progress = 0 now_playing_details_progress.progress = 0
Picasso.get() Picasso.get()
.load(normalizeUrl(track.album.cover.original)) .maybeLoad(maybeNormalizeUrl(track.album.cover.original))
.fit() .fit()
.centerCrop() .centerCrop()
.into(now_playing_cover) .into(now_playing_cover)
Picasso.get() Picasso.get()
.load(normalizeUrl(track.album.cover.original)) .maybeLoad(maybeNormalizeUrl(track.album.cover.original))
.fit() .fit()
.centerCrop() .centerCrop()
.into(now_playing_details_cover) .into(now_playing_details_cover)

View File

@ -8,7 +8,8 @@ import androidx.recyclerview.widget.RecyclerView
import com.github.apognu.otter.R import com.github.apognu.otter.R
import com.github.apognu.otter.fragments.FunkwhaleAdapter import com.github.apognu.otter.fragments.FunkwhaleAdapter
import com.github.apognu.otter.utils.Album import com.github.apognu.otter.utils.Album
import com.github.apognu.otter.utils.normalizeUrl import com.github.apognu.otter.utils.maybeLoad
import com.github.apognu.otter.utils.maybeNormalizeUrl
import com.squareup.picasso.Picasso import com.squareup.picasso.Picasso
import jp.wasabeef.picasso.transformations.RoundedCornersTransformation import jp.wasabeef.picasso.transformations.RoundedCornersTransformation
import kotlinx.android.synthetic.main.row_album.view.* import kotlinx.android.synthetic.main.row_album.view.*
@ -33,9 +34,8 @@ class AlbumsAdapter(val context: Context?, val listener: OnAlbumClickListener) :
val album = data[position] val album = data[position]
Picasso.get() Picasso.get()
.load(normalizeUrl(album.cover.original)) .maybeLoad(maybeNormalizeUrl(album.cover.original))
.fit() .fit()
.placeholder(R.drawable.cover)
.transform(RoundedCornersTransformation(16, 0)) .transform(RoundedCornersTransformation(16, 0))
.into(holder.art) .into(holder.art)

View File

@ -8,7 +8,8 @@ import androidx.recyclerview.widget.RecyclerView
import com.github.apognu.otter.R import com.github.apognu.otter.R
import com.github.apognu.otter.fragments.FunkwhaleAdapter import com.github.apognu.otter.fragments.FunkwhaleAdapter
import com.github.apognu.otter.utils.Album import com.github.apognu.otter.utils.Album
import com.github.apognu.otter.utils.normalizeUrl import com.github.apognu.otter.utils.maybeLoad
import com.github.apognu.otter.utils.maybeNormalizeUrl
import com.squareup.picasso.Picasso import com.squareup.picasso.Picasso
import jp.wasabeef.picasso.transformations.RoundedCornersTransformation import jp.wasabeef.picasso.transformations.RoundedCornersTransformation
import kotlinx.android.synthetic.main.row_album_grid.view.* import kotlinx.android.synthetic.main.row_album_grid.view.*
@ -32,7 +33,7 @@ class AlbumsGridAdapter(val context: Context?, private val listener: OnAlbumClic
val album = data[position] val album = data[position]
Picasso.get() Picasso.get()
.load(normalizeUrl(album.cover.original)) .maybeLoad(maybeNormalizeUrl(album.cover.original))
.fit() .fit()
.placeholder(R.drawable.cover) .placeholder(R.drawable.cover)
.transform(RoundedCornersTransformation(24, 0)) .transform(RoundedCornersTransformation(24, 0))

View File

@ -8,7 +8,8 @@ import androidx.recyclerview.widget.RecyclerView
import com.github.apognu.otter.R import com.github.apognu.otter.R
import com.github.apognu.otter.fragments.FunkwhaleAdapter import com.github.apognu.otter.fragments.FunkwhaleAdapter
import com.github.apognu.otter.utils.Artist import com.github.apognu.otter.utils.Artist
import com.github.apognu.otter.utils.normalizeUrl import com.github.apognu.otter.utils.maybeLoad
import com.github.apognu.otter.utils.maybeNormalizeUrl
import com.squareup.picasso.Picasso import com.squareup.picasso.Picasso
import jp.wasabeef.picasso.transformations.RoundedCornersTransformation import jp.wasabeef.picasso.transformations.RoundedCornersTransformation
import kotlinx.android.synthetic.main.row_artist.view.* import kotlinx.android.synthetic.main.row_artist.view.*
@ -36,9 +37,8 @@ class ArtistsAdapter(val context: Context?, private val listener: OnArtistClickL
artist.albums?.let { albums -> artist.albums?.let { albums ->
if (albums.isNotEmpty()) { if (albums.isNotEmpty()) {
Picasso.get() Picasso.get()
.load(normalizeUrl(albums[0].cover.original)) .maybeLoad(maybeNormalizeUrl(albums[0].cover.original))
.fit() .fit()
.placeholder(R.drawable.cover)
.transform(RoundedCornersTransformation(16, 0)) .transform(RoundedCornersTransformation(16, 0))
.into(holder.art) .into(holder.art)
} }

View File

@ -44,7 +44,7 @@ class FavoritesAdapter(private val context: Context?, private val favoriteListen
val favorite = data[position] val favorite = data[position]
Picasso.get() Picasso.get()
.load(normalizeUrl(favorite.track.album.cover.original)) .maybeLoad(maybeNormalizeUrl(favorite.track.album.cover.original))
.fit() .fit()
.placeholder(R.drawable.cover) .placeholder(R.drawable.cover)
.transform(RoundedCornersTransformation(16, 0)) .transform(RoundedCornersTransformation(16, 0))

View File

@ -56,7 +56,7 @@ class PlaylistTracksAdapter(private val context: Context?, private val favoriteL
val track = data[position] val track = data[position]
Picasso.get() Picasso.get()
.load(normalizeUrl(track.track.album.cover.original)) .maybeLoad(maybeNormalizeUrl(track.track.album.cover.original))
.fit() .fit()
.placeholder(R.drawable.cover) .placeholder(R.drawable.cover)
.transform(RoundedCornersTransformation(16, 0)) .transform(RoundedCornersTransformation(16, 0))

View File

@ -56,9 +56,8 @@ class TracksAdapter(private val context: Context?, private val favoriteListener:
val track = data[position] val track = data[position]
Picasso.get() Picasso.get()
.load(normalizeUrl(track.album.cover.original)) .maybeLoad(maybeNormalizeUrl(track.album.cover.original))
.fit() .fit()
.placeholder(R.drawable.cover)
.transform(RoundedCornersTransformation(16, 0)) .transform(RoundedCornersTransformation(16, 0))
.into(holder.cover) .into(holder.cover)

View File

@ -11,9 +11,7 @@ import com.github.apognu.otter.R
import com.github.apognu.otter.activities.MainActivity import com.github.apognu.otter.activities.MainActivity
import com.github.apognu.otter.adapters.AlbumsAdapter import com.github.apognu.otter.adapters.AlbumsAdapter
import com.github.apognu.otter.repositories.AlbumsRepository import com.github.apognu.otter.repositories.AlbumsRepository
import com.github.apognu.otter.utils.Album import com.github.apognu.otter.utils.*
import com.github.apognu.otter.utils.AppContext
import com.github.apognu.otter.utils.Artist
import com.squareup.picasso.Picasso import com.squareup.picasso.Picasso
import kotlinx.android.synthetic.main.fragment_albums.* import kotlinx.android.synthetic.main.fragment_albums.*
@ -54,7 +52,7 @@ class AlbumsFragment : FunkwhaleFragment<Album, AlbumsAdapter>() {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
Picasso.get() Picasso.get()
.load(artistArt) .maybeLoad(maybeNormalizeUrl(artistArt))
.noFade() .noFade()
.fit() .fit()
.centerCrop() .centerCrop()

View File

@ -100,7 +100,7 @@ class PlaylistTracksFragment : FunkwhaleFragment<PlaylistTrack, PlaylistTracksAd
} }
Picasso.get() Picasso.get()
.load(normalizeUrl(url)) .maybeLoad(maybeNormalizeUrl(url))
.into(imageView) .into(imageView)
} }
} }

View File

@ -60,7 +60,7 @@ class TracksFragment : FunkwhaleFragment<Track, TracksAdapter>() {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
Picasso.get() Picasso.get()
.load(albumCover) .maybeLoad(maybeNormalizeUrl(albumCover))
.noFade() .noFade()
.fit() .fit()
.centerCrop() .centerCrop()

View File

@ -60,7 +60,12 @@ class MediaControlsManager(val context: Service, private val mediaSession: Media
.setShowActionsInCompactView(0, 1, 2) .setShowActionsInCompactView(0, 1, 2)
) )
.setSmallIcon(R.drawable.ottericon) .setSmallIcon(R.drawable.ottericon)
.setLargeIcon(Picasso.get().load(normalizeUrl(track.album.cover.original)).get()) .run {
val url = maybeNormalizeUrl(track.album.cover.original)
if (url != null) setLargeIcon(Picasso.get().load(url).get())
else this
}
.setContentTitle(track.title) .setContentTitle(track.title)
.setContentText(track.artist.name) .setContentText(track.artist.name)
.setContentIntent(openPendingIntent) .setContentIntent(openPendingIntent)

View File

@ -37,7 +37,7 @@ class QueueManager(val context: Context) {
val factory = factory() val factory = factory()
datasources.addMediaSources(metadata.map { track -> datasources.addMediaSources(metadata.map { track ->
val url = normalizeUrl(track.bestUpload()?.listen_url ?: "") val url = mustNormalizeUrl(track.bestUpload()?.listen_url ?: "")
ProgressiveMediaSource.Factory(factory).setTag(track.title).createMediaSource(Uri.parse(url)) ProgressiveMediaSource.Factory(factory).setTag(track.title).createMediaSource(Uri.parse(url))
}) })
@ -73,7 +73,7 @@ class QueueManager(val context: Context) {
val factory = factory() val factory = factory()
val sources = tracks.map { track -> val sources = tracks.map { track ->
val url = normalizeUrl(track.bestUpload()?.listen_url ?: "") val url = mustNormalizeUrl(track.bestUpload()?.listen_url ?: "")
ProgressiveMediaSource.Factory(factory).setTag(track.title).createMediaSource(Uri.parse(url)) ProgressiveMediaSource.Factory(factory).setTag(track.title).createMediaSource(Uri.parse(url))
} }
@ -92,7 +92,7 @@ class QueueManager(val context: Context) {
val tracks = tracks.filter { metadata.indexOf(it) == -1 } val tracks = tracks.filter { metadata.indexOf(it) == -1 }
val sources = tracks.map { track -> val sources = tracks.map { track ->
val url = normalizeUrl(track.bestUpload()?.listen_url ?: "") val url = mustNormalizeUrl(track.bestUpload()?.listen_url ?: "")
ProgressiveMediaSource.Factory(factory).createMediaSource(Uri.parse(url)) ProgressiveMediaSource.Factory(factory).createMediaSource(Uri.parse(url))
} }
@ -107,7 +107,7 @@ class QueueManager(val context: Context) {
fun insertNext(track: Track) { fun insertNext(track: Track) {
val factory = factory() val factory = factory()
val url = normalizeUrl(track.bestUpload()?.listen_url ?: "") val url = mustNormalizeUrl(track.bestUpload()?.listen_url ?: "")
if (metadata.indexOf(track) == -1) { if (metadata.indexOf(track) == -1) {
ProgressiveMediaSource.Factory(factory).createMediaSource(Uri.parse(url)).let { ProgressiveMediaSource.Factory(factory).createMediaSource(Uri.parse(url)).let {

View File

@ -31,7 +31,7 @@ class FavoritesRepository(override val context: Context?) : Repository<Favorite,
runBlocking(IO) { runBlocking(IO) {
Fuel Fuel
.post(normalizeUrl("/api/v1/favorites/tracks")) .post(mustNormalizeUrl("/api/v1/favorites/tracks"))
.header("Authorization", "Bearer $token") .header("Authorization", "Bearer $token")
.header("Content-Type", "application/json") .header("Content-Type", "application/json")
.body(Gson().toJson(body)) .body(Gson().toJson(body))
@ -45,7 +45,7 @@ class FavoritesRepository(override val context: Context?) : Repository<Favorite,
runBlocking(IO) { runBlocking(IO) {
Fuel Fuel
.post(normalizeUrl("/api/v1/favorites/tracks/remove/")) .post(mustNormalizeUrl("/api/v1/favorites/tracks/remove/"))
.header("Authorization", "Bearer $token") .header("Authorization", "Bearer $token")
.header("Content-Type", "application/json") .header("Content-Type", "application/json")
.body(Gson().toJson(body)) .body(Gson().toJson(body))

View File

@ -78,7 +78,7 @@ class HttpUpstream<D : Any, R : FunkwhaleResponse<D>>(private val behavior: Beha
val token = PowerPreference.getFileByName(AppContext.PREFS_CREDENTIALS).getString("access_token") val token = PowerPreference.getFileByName(AppContext.PREFS_CREDENTIALS).getString("access_token")
val (_, response, result) = Fuel val (_, response, result) = Fuel
.get(normalizeUrl(url)) .get(mustNormalizeUrl(url))
.header("Authorization", "Bearer $token") .header("Authorization", "Bearer $token")
.awaitObjectResponseResult(GenericDeserializer<R>(type)) .awaitObjectResponseResult(GenericDeserializer<R>(type))
@ -94,7 +94,7 @@ class HttpUpstream<D : Any, R : FunkwhaleResponse<D>>(private val behavior: Beha
val token = PowerPreference.getFileByName(AppContext.PREFS_CREDENTIALS).getString("access_token") val token = PowerPreference.getFileByName(AppContext.PREFS_CREDENTIALS).getString("access_token")
Fuel Fuel
.get(normalizeUrl(url)) .get(mustNormalizeUrl(url))
.header("Authorization", "Bearer $token") .header("Authorization", "Bearer $token")
.awaitObjectResult(GenericDeserializer(type)) .awaitObjectResult(GenericDeserializer(type))
} else { } else {

View File

@ -23,7 +23,7 @@ object HTTP {
"password" to PowerPreference.getFileByName(AppContext.PREFS_CREDENTIALS).getString("password") "password" to PowerPreference.getFileByName(AppContext.PREFS_CREDENTIALS).getString("password")
).toList() ).toList()
val result = Fuel.post(normalizeUrl("/api/v1/token"), body).awaitObjectResult(gsonDeserializerOf(FwCredentials::class.java)) val result = Fuel.post(mustNormalizeUrl("/api/v1/token"), body).awaitObjectResult(gsonDeserializerOf(FwCredentials::class.java))
return result.fold( return result.fold(
{ data -> { data ->
@ -39,7 +39,7 @@ object HTTP {
val token = PowerPreference.getFileByName(AppContext.PREFS_CREDENTIALS).getString("access_token") val token = PowerPreference.getFileByName(AppContext.PREFS_CREDENTIALS).getString("access_token")
val (_, response, result) = Fuel val (_, response, result) = Fuel
.get(normalizeUrl(url)) .get(mustNormalizeUrl(url))
.header("Authorization", "Bearer $token") .header("Authorization", "Bearer $token")
.awaitObjectResponseResult(gsonDeserializerOf(T::class.java)) .awaitObjectResponseResult(gsonDeserializerOf(T::class.java))
@ -55,7 +55,7 @@ object HTTP {
val token = PowerPreference.getFileByName(AppContext.PREFS_CREDENTIALS).getString("access_token") val token = PowerPreference.getFileByName(AppContext.PREFS_CREDENTIALS).getString("access_token")
Fuel Fuel
.get(normalizeUrl(url)) .get(mustNormalizeUrl(url))
.header("Authorization", "Bearer $token") .header("Authorization", "Bearer $token")
.awaitObjectResult(gsonDeserializerOf(T::class.java)) .awaitObjectResult(gsonDeserializerOf(T::class.java))
} else { } else {

View File

@ -1,11 +1,15 @@
package com.github.apognu.otter.utils package com.github.apognu.otter.utils
import android.annotation.SuppressLint
import android.content.Context import android.content.Context
import android.os.Build import android.os.Build
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import com.github.apognu.otter.R
import com.github.apognu.otter.fragments.BrowseFragment import com.github.apognu.otter.fragments.BrowseFragment
import com.github.apognu.otter.repositories.Repository import com.github.apognu.otter.repositories.Repository
import com.squareup.picasso.Picasso
import com.squareup.picasso.RequestCreator
import kotlinx.coroutines.Dispatchers.Main import kotlinx.coroutines.Dispatchers.Main
import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.channels.Channel
@ -73,4 +77,9 @@ fun <T> T.applyOnApi(api: Int, block: T.() -> T): T {
} else { } else {
this this
} }
}
fun Picasso.maybeLoad(url: String?): RequestCreator {
if (url == null) return load(R.drawable.cover)
else return load(url)
} }

View File

@ -16,7 +16,18 @@ fun Any.log(message: String) {
Log.d("FUNKWHALE", "${this.javaClass.simpleName}: $message") Log.d("FUNKWHALE", "${this.javaClass.simpleName}: $message")
} }
fun normalizeUrl(url: String): String { fun maybeNormalizeUrl(url: String?): String? {
if (url == null || url.isEmpty()) return null
val fallbackHost = PowerPreference.getFileByName(AppContext.PREFS_CREDENTIALS).getString("hostname")
val uri = URI(url).takeIf { it.host != null } ?: URI("$fallbackHost$url")
return uri.run {
URI("https", host, path, query, null)
}.toString()
}
fun mustNormalizeUrl(url: String): String {
val fallbackHost = PowerPreference.getFileByName(AppContext.PREFS_CREDENTIALS).getString("hostname") val fallbackHost = PowerPreference.getFileByName(AppContext.PREFS_CREDENTIALS).getString("hostname")
val uri = URI(url).takeIf { it.host != null } ?: URI("$fallbackHost$url") val uri = URI(url).takeIf { it.host != null } ?: URI("$fallbackHost$url")

25
publish.sh Executable file
View File

@ -0,0 +1,25 @@
#!/bin/sh
if [ $# -ne 2 ]; then
echo 'Usage: ./publish.sh <TAG> <MESSAGE>' >&2
exit 1
fi
if [ "$(git diff --stat)" != '' ]; then
echo 'ERROR: repository is dirty.' >&2
exit 1
fi
TAG="$1"
MESSAGE="$2"
if [ "$(git tag -l | grep $TAG)" != '' ]; then
echo "ERROR: tag $TAG already exists." >&2
exit 1
fi
git tag -a -m "$MESSAGE" "$TAG"
git push --tags
./gradlew publish