From 227a25c16eface6307973b112d17c24ad13b7cc5 Mon Sep 17 00:00:00 2001 From: Nicolas Pomepuy Date: Fri, 14 Apr 2023 09:20:37 +0200 Subject: [PATCH] Webserver: Implement the genres tab --- .../src/org/videolan/vlc/ArtworkProvider.kt | 23 ++++++ .../vlc/webserver/HttpSharingServer.kt | 13 ++++ .../src/pages/AudioAlbums.vue | 4 - .../src/pages/AudioArtists.vue | 4 - .../src/pages/AudioGenres.vue | 78 ++++++++++++++++++- .../src/pages/AudioTracks.vue | 4 - .../network-sharing-server/src/scss/app.scss | 5 ++ 7 files changed, 116 insertions(+), 15 deletions(-) diff --git a/application/vlc-android/src/org/videolan/vlc/ArtworkProvider.kt b/application/vlc-android/src/org/videolan/vlc/ArtworkProvider.kt index 5d3a26803..b9c85b759 100644 --- a/application/vlc-android/src/org/videolan/vlc/ArtworkProvider.kt +++ b/application/vlc-android/src/org/videolan/vlc/ArtworkProvider.kt @@ -120,6 +120,7 @@ class ArtworkProvider : ContentProvider() { ALBUM -> getCategoryImage(ctx, ALBUM, ContentUris.parseId(uri)) ARTIST -> getCategoryImage(ctx, ARTIST, ContentUris.parseId(uri)) REMOTE -> getRemoteImage(ctx, uri.getQueryParameter(PATH)) + GENRE -> getGenreImage(ctx, ContentUris.parseId(uri)) PLAY_ALL -> getPlayAllImage(ctx, uriSegments[1], ContentUris.parseId(uri), uri.getBooleanQueryParameter(SHUFFLE, false)) else -> throw FileNotFoundException("Uri is not supported: $uri") @@ -232,6 +233,28 @@ class ArtworkProvider : ContentProvider() { return getPFDFromByteArray(image) } + /** + * Get the genre image and cache it. Without overlay + * + * @param ctx the context + * @param id the genre id + * @return a ParcelFileDescriptor containing the genre image + */ + private fun getGenreImage(ctx: Context, id: Long): ParcelFileDescriptor? { + val bitmap = runBlocking(Dispatchers.IO) { + val tracks = ctx.getFromMl { getGenre(id)?.albums?.flatMap { it.tracks.toList() } } + val cover = tracks?.let { + + ThumbnailsProvider.getPlaylistOrGenreImage("genre:${id}_256", tracks, 256) + } + return@runBlocking when { + cover != null -> cover + else -> ctx.getBitmapFromDrawable(R.drawable.ic_auto_genre) + } + } + return getPFDFromBitmap(bitmap) + } + private fun getPlayAllImage(ctx: Context, type: String, id: Long, shuffle: Boolean): ParcelFileDescriptor { val bitmap = runBlocking(Dispatchers.IO) { val tracks = when (type) { diff --git a/application/webserver/src/main/java/org/videolan/vlc/webserver/HttpSharingServer.kt b/application/webserver/src/main/java/org/videolan/vlc/webserver/HttpSharingServer.kt index b58ba9c53..a7b0c0d41 100644 --- a/application/webserver/src/main/java/org/videolan/vlc/webserver/HttpSharingServer.kt +++ b/application/webserver/src/main/java/org/videolan/vlc/webserver/HttpSharingServer.kt @@ -355,6 +355,17 @@ class HttpSharingServer(context: Context) : PlaybackService.Callback { val gson = Gson() call.respondText(gson.toJson(list)) } + get("/genre-list") { + val genres = context.getFromMl { getGenres( false, false) } + + val list = ArrayList() + genres.forEach { genre -> + list.add(PlayQueueItem(genre.id, genre.title, context.resources.getQuantityString(R.plurals.track_quantity, genre.tracksCount, genre.tracksCount), 0, genre.artworkMrl + ?: "", false, "")) + } + val gson = Gson() + call.respondText(gson.toJson(list)) + } get("/play") { val type = call.request.queryParameters["type"] ?: "media" val append = call.request.queryParameters["append"] == "true" @@ -364,6 +375,7 @@ class HttpSharingServer(context: Context) : PlaybackService.Callback { when (type) { "album" -> getAlbum(id.toLong()).tracks "artist" -> getArtist(id.toLong()).tracks + "genre" -> getGenre(id.toLong()).tracks else -> arrayOf(getMedia(id.toLong())) } } @@ -417,6 +429,7 @@ class HttpSharingServer(context: Context) : PlaybackService.Callback { "video" -> ArtworkProvider.VIDEO "album" -> ArtworkProvider.ALBUM "artist" -> ArtworkProvider.ARTIST + "genre" -> ArtworkProvider.GENRE else -> ArtworkProvider.MEDIA } cr.openInputStream(Uri.parse("content://${context.applicationContext.packageName}.artwork/$mediaType/0/$it"))?.let { inputStream -> diff --git a/buildsystem/network-sharing-server/src/pages/AudioAlbums.vue b/buildsystem/network-sharing-server/src/pages/AudioAlbums.vue index 3ec4ad426..7fdd00612 100644 --- a/buildsystem/network-sharing-server/src/pages/AudioAlbums.vue +++ b/buildsystem/network-sharing-server/src/pages/AudioAlbums.vue @@ -91,9 +91,5 @@ export default { diff --git a/buildsystem/network-sharing-server/src/pages/AudioArtists.vue b/buildsystem/network-sharing-server/src/pages/AudioArtists.vue index e3f7866b8..ab5cc0da0 100644 --- a/buildsystem/network-sharing-server/src/pages/AudioArtists.vue +++ b/buildsystem/network-sharing-server/src/pages/AudioArtists.vue @@ -91,9 +91,5 @@ export default { diff --git a/buildsystem/network-sharing-server/src/pages/AudioGenres.vue b/buildsystem/network-sharing-server/src/pages/AudioGenres.vue index 3bb8ca7c6..f646d1eb8 100644 --- a/buildsystem/network-sharing-server/src/pages/AudioGenres.vue +++ b/buildsystem/network-sharing-server/src/pages/AudioGenres.vue @@ -1,6 +1,38 @@ @@ -8,6 +40,9 @@ import { playerStore } from '../stores/PlayerStore' import { mapStores } from 'pinia' +import axios from 'axios' +import { API_URL } from '../config.js' + export default { computed: { ...mapStores(playerStore) @@ -17,8 +52,45 @@ export default { }, unmounted: function () { this.playerStore.currentTab = ""; + }, + data() { + return { + genres: [], + loaded: false, + } + }, + methods: { + getImageUrl(media) { + return API_URL + "/artwork?artwork=" + media.artworkURL + "&id=" + media.id + "&type=genre" + }, + fetchGenres() { + let component = this + component.playerStore.loading = true + axios.get(API_URL + "genre-list").then((response) => { + this.loaded = true; + this.genres = response.data + component.playerStore.loading = false + }); + }, + play(media, append, asAudio) { + let component = this + axios.get(API_URL + "play?id=" + media.id + "&append=" + append + "&audio=" + asAudio + "&type=genre") + .catch(function (error) { + console.log(error.toJSON()); + if (error.response.status != 200) { + component.playerStore.warning = { type: "warning", message: error.response.data } + } + }) + } + }, + created: function () { + this.fetchGenres(); } } - + diff --git a/buildsystem/network-sharing-server/src/pages/AudioTracks.vue b/buildsystem/network-sharing-server/src/pages/AudioTracks.vue index d3f5a4a7b..a54c9b3ab 100644 --- a/buildsystem/network-sharing-server/src/pages/AudioTracks.vue +++ b/buildsystem/network-sharing-server/src/pages/AudioTracks.vue @@ -91,9 +91,5 @@ export default { diff --git a/buildsystem/network-sharing-server/src/scss/app.scss b/buildsystem/network-sharing-server/src/scss/app.scss index d0cc968cd..4c821f90e 100644 --- a/buildsystem/network-sharing-server/src/scss/app.scss +++ b/buildsystem/network-sharing-server/src/scss/app.scss @@ -201,4 +201,9 @@ body { .media-content { padding-top: 16px; +} + +.audio-img-container { + background-color: $dark-background; + border-radius: 7px; } \ No newline at end of file