Browse Source

Webserver: Implement the genres tab

transifex
Nicolas Pomepuy 3 years ago
parent
commit
227a25c16e
  1. 23
      application/vlc-android/src/org/videolan/vlc/ArtworkProvider.kt
  2. 13
      application/webserver/src/main/java/org/videolan/vlc/webserver/HttpSharingServer.kt
  3. 4
      buildsystem/network-sharing-server/src/pages/AudioAlbums.vue
  4. 4
      buildsystem/network-sharing-server/src/pages/AudioArtists.vue
  5. 78
      buildsystem/network-sharing-server/src/pages/AudioGenres.vue
  6. 4
      buildsystem/network-sharing-server/src/pages/AudioTracks.vue
  7. 5
      buildsystem/network-sharing-server/src/scss/app.scss

23
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) {

13
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<PlayQueueItem>()
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 ->

4
buildsystem/network-sharing-server/src/pages/AudioAlbums.vue

@ -91,9 +91,5 @@ export default {
<style lang='scss'>
@import '../scss/colors.scss';
.audio-img-container {
background-color: $dark-background;
border-radius: 7px;
}
</style>

4
buildsystem/network-sharing-server/src/pages/AudioArtists.vue

@ -91,9 +91,5 @@ export default {
<style lang='scss'>
@import '../scss/colors.scss';
.audio-img-container {
background-color: $dark-background;
border-radius: 7px;
}
</style>

78
buildsystem/network-sharing-server/src/pages/AudioGenres.vue

@ -1,6 +1,38 @@
<template>
<div class="container">
<p>Genres</p>
<div v-if="loaded" class="container">
<div v-if="this.genres.length !== 0" class="row gx-3 gy-3 media-content">
<div class="col-md-3 col-lg-2 col-sm-4 col-xs-6" v-for="genre in genres" :key="genre.id">
<div class="ratio ratio-1x1 media-img-container audio-img-container">
<img :src="getImageUrl(genre)" class="media-img-top">
<div v-on:click="play(genre)" class="media-overlay">
<button class="btn btn-lg overlay-play text-white" type="button">
<span class="material-symbols-outlined">play_circle</span>
</button>
</div>
</div>
<div class="d-flex">
<div class="card-body media-text flex1">
<h6 class="card-title text-truncate">{{ genre.title }}</h6>
<p class="card-text text-truncate">{{ genre.artist }}</p>
</div>
<div class="dropdown dropstart overlay-more-container">
<button class="btn btn-lg nav-button overlay-more " type="button" id="dropdownMenuButton1"
data-bs-toggle="dropdown" aria-expanded="false">
<span class="material-symbols-outlined">more_vert</span>
</button>
<ul class="dropdown-menu media-more" aria-labelledby="dropdownMenuButton1">
<li> <span v-on:click="play(genre, false, false)" class="dropdown-item">Play</span> </li>
<li> <span v-on:click="play(genre, true, false)" class="dropdown-item">Append</span> </li>
</ul>
</div>
</div>
</div>
</div>
<div v-else>
<EmptyView />
</div>
</div>
</template>
@ -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();
}
}
</script>
<style></style>
<style lang='scss'>
@import '../scss/colors.scss';
</style>

4
buildsystem/network-sharing-server/src/pages/AudioTracks.vue

@ -91,9 +91,5 @@ export default {
<style lang='scss'>
@import '../scss/colors.scss';
.audio-img-container {
background-color: $dark-background;
border-radius: 7px;
}
</style>

5
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;
}
Loading…
Cancel
Save