[Android] Allow browsing the genres in the remote access
Nicolas Pomepuy
git at videolan.org
Thu Apr 25 11:58:56 UTC 2024
vlc-android | branch: master | Nicolas Pomepuy <nicolas at videolabs.io> | Fri Apr 12 10:29:41 2024 +0200| [6f2de3d9afa76e139f2e8707d5bbeeb30aa97e43] | committer: Duncan McNamara
Allow browsing the genres in the remote access
> https://code.videolan.org/videolan/vlc-android/commit/6f2de3d9afa76e139f2e8707d5bbeeb30aa97e43
---
.../videolan/vlc/webserver/RemoteAccessRouting.kt | 25 +++++++
.../src/components/MediaItem.vue | 6 +-
.../src/pages/GenreDetails.vue | 83 ++++++++++++++++++++++
.../network-sharing-server/src/plugins/api.js | 6 ++
.../network-sharing-server/src/plugins/vlcUtils.js | 2 +
buildsystem/network-sharing-server/src/routes.js | 2 +
6 files changed, 122 insertions(+), 2 deletions(-)
diff --git a/application/webserver/src/main/java/org/videolan/vlc/webserver/RemoteAccessRouting.kt b/application/webserver/src/main/java/org/videolan/vlc/webserver/RemoteAccessRouting.kt
index f125e1d970..c8c4fa974f 100644
--- a/application/webserver/src/main/java/org/videolan/vlc/webserver/RemoteAccessRouting.kt
+++ b/application/webserver/src/main/java/org/videolan/vlc/webserver/RemoteAccessRouting.kt
@@ -517,6 +517,25 @@ fun Route.setupRouting(appContext: Context, scope: CoroutineScope) {
val gson = Gson()
call.respondText(gson.toJson(result))
}
+ // Get a genre details
+ get("/genre") {
+ verifyLogin(settings)
+ if (!settings.serveAudios(appContext)) {
+ call.respond(HttpStatusCode.Forbidden)
+ return at get
+ }
+ val id = call.request.queryParameters["id"]?.toLong() ?: 0L
+
+ val genre = appContext.getFromMl { getGenre(id) }
+
+ val list = ArrayList<RemoteAccessServer.PlayQueueItem>()
+ genre.tracks.forEach { track ->
+ list.add(track.toPlayQueueItem())
+ }
+ val result= RemoteAccessServer.AlbumResult(list, genre.title)
+ val gson = Gson()
+ call.respondText(gson.toJson(result))
+ }
// Get an playlist details
get("/playlist") {
verifyLogin(settings)
@@ -860,6 +879,12 @@ fun Route.setupRouting(appContext: Context, scope: CoroutineScope) {
album.tracks
}
}
+ "genre" -> {
+ id?.let { id ->
+ val genre = getGenre(id.toLong())
+ genre.tracks
+ }
+ }
else -> getAudio(Medialibrary.SORT_DEFAULT, false, false, false)
}
}
diff --git a/buildsystem/network-sharing-server/src/components/MediaItem.vue b/buildsystem/network-sharing-server/src/components/MediaItem.vue
index 5273834305..6b34412b25 100644
--- a/buildsystem/network-sharing-server/src/components/MediaItem.vue
+++ b/buildsystem/network-sharing-server/src/components/MediaItem.vue
@@ -112,7 +112,7 @@ export default {
return (this.mediaType == 'album' || this.mediaType == 'artist')
},
isOpenable() {
- return ['video-group', 'video-folder', 'artist', 'album', 'playlist'].includes(this.mediaType)
+ return ['video-group', 'video-folder', 'artist', 'album', 'playlist', 'genre'].includes(this.mediaType)
},
getDescription() {
if (this.mediaType == 'video') {
@@ -123,7 +123,9 @@ export default {
}
},
manageClick() {
- if (this.mediaType == 'playlist') {
+ if (this.mediaType == 'genre') {
+ this.$router.push({ name: 'GenreDetails', params: { genreId: this.media.id } })
+ } else if (this.mediaType == 'playlist') {
this.$router.push({ name: 'PlaylistDetails', params: { playlistId: this.media.id } })
} else if (this.mediaType == 'album') {
this.$router.push({ name: 'AlbumDetails', params: { albumId: this.media.id } })
diff --git a/buildsystem/network-sharing-server/src/pages/GenreDetails.vue b/buildsystem/network-sharing-server/src/pages/GenreDetails.vue
new file mode 100644
index 0000000000..ab63440adf
--- /dev/null
+++ b/buildsystem/network-sharing-server/src/pages/GenreDetails.vue
@@ -0,0 +1,83 @@
+<template>
+ <div v-if="loaded && this.tracks.length !== 0" class="container">
+ <div v-if="this.appStore.displayType[this.$route.name]" class="row gx-3 gy-3 media-list">
+ <template v-for="track in tracks" :key="track.id">
+ <MediaItem :isCard="false" :media="track" :mediaType="'track'" />
+ </template>
+ </div>
+ <div v-else class="row gx-3 gy-3 media-content">
+ <div class="col-md-3 col-lg-2 col-sm-4 col-6" v-for="track in tracks" :key="track.id">
+ <MediaItem :isCard="true" :media="track" :mediaType="'track'" />
+ </div>
+ </div>
+ </div>
+ <div v-else-if="loaded" class="empty-view-container">
+ <EmptyView :message="getEmptyText()" />
+ </div>
+</template>
+
+<script>
+
+import { useAppStore } from '../stores/AppStore'
+import { mapStores } from 'pinia'
+import http from '../plugins/auth'
+import { vlcApi } from '../plugins/api.js'
+import MediaItem from '../components/MediaItem.vue'
+import EmptyView from '../components/EmptyView.vue'
+
+export default {
+ computed: {
+ ...mapStores(useAppStore)
+ },
+ components: {
+ MediaItem,
+ EmptyView,
+ },
+ data() {
+ return {
+ tracks: [],
+ loaded: false,
+ forbidden: false,
+ }
+ },
+ methods: {
+ fetchTracks() {
+ let component = this
+ component.appStore.loading = true
+ let genreId = this.$route.params.genreId
+ this.$log.log(`Loading artist: ${genreId}`)
+ http.get(vlcApi.genreDetails(genreId))
+ .catch(function (error) {
+ if (error.response !== undefined && error.response.status == 403) {
+ component.forbidden = true;
+ }
+ })
+ .then((response) => {
+ this.loaded = true;
+ if (response) {
+ component.forbidden = false;
+ this.tracks = response.data.tracks
+ }
+ component.appStore.loading = false
+ component.appStore.title = response.data.name
+ });
+
+ },
+ getEmptyText() {
+ if (this.forbidden) return this.$t('FORBIDDEN')
+ return this.$t('NO_MEDIA')
+ }
+ },
+ created: function () {
+ this.fetchTracks();
+ },
+ unmounted: function() {
+ console.log("unmounted")
+ this.appStore.title = ''
+ }
+}
+</script>
+
+<style lang='scss'>
+ at import '../scss/colors.scss';
+</style>
diff --git a/buildsystem/network-sharing-server/src/plugins/api.js b/buildsystem/network-sharing-server/src/plugins/api.js
index cd0a01025f..0922ed909e 100644
--- a/buildsystem/network-sharing-server/src/plugins/api.js
+++ b/buildsystem/network-sharing-server/src/plugins/api.js
@@ -88,6 +88,12 @@ export const vlcApi = {
albumDetails: (albumId) => {
return`${API_URL}album?id=${albumId}`
},
+ /**
+ * Retrieve the genre details API URL
+ */
+ genreDetails: (genreId) => {
+ return`${API_URL}genre?id=${genreId}`
+ },
/**
* Retrieve the playlist details API URL
*/
diff --git a/buildsystem/network-sharing-server/src/plugins/vlcUtils.js b/buildsystem/network-sharing-server/src/plugins/vlcUtils.js
index ae0becdf28..a43c3e6ed8 100644
--- a/buildsystem/network-sharing-server/src/plugins/vlcUtils.js
+++ b/buildsystem/network-sharing-server/src/plugins/vlcUtils.js
@@ -61,6 +61,8 @@ export default {
break
case "album": id = route.params.albumId
break
+ case "genre": id = route.params.genreId
+ break
case "playlist": id = route.params.playlistId
break
default: id = 0
diff --git a/buildsystem/network-sharing-server/src/routes.js b/buildsystem/network-sharing-server/src/routes.js
index 3aa22dc999..b1b3393917 100644
--- a/buildsystem/network-sharing-server/src/routes.js
+++ b/buildsystem/network-sharing-server/src/routes.js
@@ -12,6 +12,7 @@ import LoginPage from './pages/LoginPage'
import SslPage from './pages/SslPage'
import ArtistDetails from './pages/ArtistDetails'
import AlbumDetails from './pages/AlbumDetails'
+import GenreDetails from './pages/GenreDetails'
import PlaylistDetails from './pages/PlaylistDetails'
const routes = [
@@ -33,6 +34,7 @@ const routes = [
{ path: 'genres', component: AudioGenres, name: 'AudioGenres', meta: { showDisplayBar: true, isAudio: true, showResume: true, showGrouping: false } },
{ path: 'artist/:artistId', component: ArtistDetails, name: 'ArtistDetails', meta: { showDisplayBar: true, isAudio: false, showResume: false, showGrouping: false, showFAB: true, playAllType: "artist", icon:"ic_no_artist" } },
{ path: 'album/:albumId', component: AlbumDetails, name: 'AlbumDetails', meta: { showDisplayBar: true, isAudio: false, showResume: false, showGrouping: false, showFAB: true, playAllType: "album", icon:"ic_album" } },
+ { path: 'genre/:genreId', component: GenreDetails, name: 'GenreDetails', meta: { showDisplayBar: true, isAudio: false, showResume: false, showGrouping: false, showFAB: true, playAllType: "genre", icon:"ic_genre" } },
]
},
{
More information about the Android
mailing list