Skip to content

Commit

Permalink
Feat: Add My Sonos & version info
Browse files Browse the repository at this point in the history
- Add feature to browse & play Sonos Favorites & Sonos Playlists
- Add feature to search Sonos Playlists
- Add package.json app version to navigation panel
  • Loading branch information
Nicholas Villarreal committed Jan 31, 2019
1 parent fb4cb19 commit 451441d
Show file tree
Hide file tree
Showing 16 changed files with 264 additions and 22 deletions.
2 changes: 1 addition & 1 deletion client/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "sonos-web",
"version": "0.1.0",
"version": "0.8.0",
"private": true,
"scripts": {
"serve": "vue-cli-service serve",
Expand Down
10 changes: 10 additions & 0 deletions client/src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@
</v-list-tile>
</template>
</v-list>
<div class="version-text subtitle pa-3 grey--text text--darken-2">
{{ APP_VERSION }}
</div>
</v-navigation-drawer>
<v-container fill-height>
<v-layout>
Expand All @@ -49,6 +52,7 @@

<script>
import NowPlayingBar from '@/components/NowPlayingBar.vue';
import { version } from '../package.json';
export default {
components: { NowPlayingBar },
Expand All @@ -61,6 +65,7 @@ export default {
{ icon: 'library_music', text: 'Music Library', path: '/library' },
{ icon: 'star', text: 'My Sonos', path: '/sonos' },
],
APP_VERSION: `v${version}`,
}),
computed: {
discoveringSonos() {
Expand All @@ -74,6 +79,11 @@ export default {
</script>

<style>
.version-text {
position: absolute;
bottom: 90px;
left: 0px;
}
.theme--dark.v-list {
background: #282828;
}
Expand Down
15 changes: 12 additions & 3 deletions client/src/components/SongList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,13 @@
<v-list-tile @click="playNow(selectedSongURI)">
<v-list-tile-title>Play Now</v-list-tile-title>
</v-list-tile>
<v-list-tile @click="playNext(selectedSongURI)">
<v-list-tile @click="playNext(selectedSongURI)" v-if="!isRadio">
<v-list-tile-title>Play Next</v-list-tile-title>
</v-list-tile>
<v-list-tile @click="addToEndOfQueue(selectedSongURI)">
<v-list-tile @click="addToEndOfQueue(selectedSongURI)" v-if="!isRadio">
<v-list-tile-title>Add to End of Queue</v-list-tile-title>
</v-list-tile>
<v-list-tile @click="replaceQueueAndPlay(selectedSongURI)">
<v-list-tile @click="replaceQueueAndPlay(selectedSongURI)" v-if="!isRadio">
<v-list-tile-title>Replace Queue</v-list-tile-title>
</v-list-tile>
</v-list>
Expand Down Expand Up @@ -84,6 +84,15 @@ export default {
activeZoneGroupId() {
return this.$store.state.activeZoneGroupId;
},
isRadio() {
if (this.selectedSongURI) {
if (this.selectedSongURI.startsWith('x-sonosapi-radio:')) {
return true;
}
return false;
}
return false;
},
},
};
</script>
Expand Down
15 changes: 14 additions & 1 deletion client/src/router.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ const Share = () => import(/* webpackChunkName: "Share" */ './views/library/deta

const MySonos = () => import(/* webpackChunkName: "My Sonos" */ './views/MySonos.vue');
const SonosPlaylists = () => import(/* webpackChunkName: "Sonos Playlists" */ './views/sonos/SonosPlaylists.vue');
const SonosFavorites = () => import(/* webpackChunkName: "Sonos Favorites" */ './views/sonos/SonosFavorites.vue');

const PlayQueue = () => import(/* webpackChunkName: "Play Queue" */ './views/PlayQueue.vue');

Expand Down Expand Up @@ -79,6 +80,12 @@ const router = new Router({
component: Playlists,
props: { search: true },
},
{
path: '/search/sp/*',
name: 'SearchSonosPlaylists',
component: SonosPlaylists,
props: { search: true },
},
],
},
{
Expand Down Expand Up @@ -164,6 +171,12 @@ const router = new Router({
name: 'Share',
component: Share,
},
{
path: '/sp/*',
name: 'Sonos Playlist',
component: Album,
props: { isSonosPlaylist: true },
},
{
path: '/queue',
name: 'PlayQueue',
Expand All @@ -186,7 +199,7 @@ const router = new Router({
{
path: '/sonos/favorites',
name: 'SonosFavorites',
component: Playlists,
component: SonosFavorites,
},
],
},
Expand Down
3 changes: 3 additions & 0 deletions client/src/services/API/musicLibrary.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ export default {
getSonosPlaylists(options) {
return axios.post('/sonos_playlists', options);
},
getSonosFavorites(options) {
return axios.post('/favorites', options);
},
getShares(options) {
return axios.post('/share', options);
},
Expand Down
1 change: 1 addition & 0 deletions client/src/views/Search.vue
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ export default {
{ title: 'Songs', link: `/search/songs/${this.encodedSearchInput}` },
{ title: 'Genres', link: `/search/genres/${this.encodedSearchInput}` },
{ title: 'Playlists', link: `/search/playlists/${this.encodedSearchInput}` },
{ title: 'Sonos Playlists', link: `/search/sp/${this.encodedSearchInput}` },
];
},
encodedSearchInput() {
Expand Down
16 changes: 16 additions & 0 deletions client/src/views/SearchTopResults.vue
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,19 @@
:item="item" toPrefix="/playlist"></library-item>
</v-layout>
</v-flex>
<v-flex xs12 v-if="playlists" pb-4>
<v-layout justify-center>
<router-link
:to="`/search/sp/${searchTerm}`"
class="top-result-heading">
Sonos Playlists
</router-link>
</v-layout>
<v-layout row wrap pt-2>
<library-item v-for="item in sonosPlaylists" :key="item.uri"
:item="item" toPrefix="/sp"></library-item>
</v-layout>
</v-flex>
<v-flex xs12 v-if="genres" pb-4>
<v-layout justify-center>
<router-link
Expand Down Expand Up @@ -123,6 +136,9 @@ export default {
playlists() {
return this.topResults.playlists ? this.topResults.playlists.items : null;
},
sonosPlaylists() {
return this.topResults.sonos_playlists ? this.topResults.sonos_playlists.items : null;
},
genres() {
return this.topResults.genres ? this.topResults.genres.items : null;
},
Expand Down
13 changes: 11 additions & 2 deletions client/src/views/library/detail/Album.vue
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,10 @@ export default {
type: Boolean,
default: false,
},
isSonosPlaylist: {
type: Boolean,
default: false,
},
isGenrePlaylist: {
type: Boolean,
default: false,
Expand Down Expand Up @@ -175,9 +179,14 @@ export default {
uriData() {
if (this.allAlbum) {
return { artistPath: `${this.albumName}/` };
} if (this.isNormalPlaylist) {
}
if (this.isNormalPlaylist) {
return { playlistName: this.albumName };
} if (this.isGenrePlaylist) {
}
if (this.isSonosPlaylist) {
return { sonosPlaylistName: this.albumName };
}
if (this.isGenrePlaylist) {
return { genrePath: `${this.albumName}//` };
}
return { artistPath: `${this.artistName}/${this.albumName}` };
Expand Down
55 changes: 55 additions & 0 deletions client/src/views/sonos/SonosFavorites.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<template>
<v-layout>
<load-library-on-scroll
@loading-error="loadingError"
@loaded-items="loadedItems"
:asyncLoadMethod="loadMethod"
:libraryItem="sonosFavorites">
</load-library-on-scroll>
<ErrorView v-if="error" absolute :message="errorMessage"></ErrorView>
<LoadingView v-else-if="loading" absolute message="Loading..."></LoadingView>
<v-layout row wrap v-else>
<library-item-count :total="sonosFavorites.total"
label="Favorites"></library-item-count>
<v-flex xs12>
<song-list :songs="items"></song-list>
</v-flex>
</v-layout>
</v-layout>
</template>

<script>
import deepmerge from 'deepmerge';
import MusicLibraryAPI from '@/services/API/musicLibrary';
import SongList from '@/components/SongList.vue';
import LibraryItemCount from '@/components/LibraryItemCount.vue';
import LoadLibraryOnScroll from '@/components/LoadLibraryOnScroll.vue';
export default {
name: 'SonosFavorites',
components: { SongList, LibraryItemCount, LoadLibraryOnScroll },
data: () => ({
sonosFavorites: {},
loading: true,
error: false,
errorMessage: null,
}),
methods: {
loadMethod: MusicLibraryAPI.getSonosFavorites,
loadedItems(data) {
this.loading = false;
this.sonosFavorites = deepmerge(this.sonosFavorites, data);
},
loadingError(error) {
this.loading = false;
this.error = true;
this.errorMessage = `${error.response.status}: ${error.response.data}`;
},
},
computed: {
items() {
return this.sonosFavorites.items || [];
},
},
};
</script>
1 change: 0 additions & 1 deletion client/src/views/sonos/SonosPlaylists.vue
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@ export default {
loadedItems(data) {
this.loading = false;
this.sonosPlaylists = deepmerge(this.sonosPlaylists, data);
console.log(this.sonosPlaylists);
},
loadingError(error) {
this.loading = false;
Expand Down
4 changes: 2 additions & 2 deletions server/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions server/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "sonos-web",
"version": "0.7.1",
"version": "0.8.0",
"description": "Sonos Web Controller",
"author": "Nicholas Villarreal",
"license": "GPL-3.0",
Expand All @@ -21,7 +21,7 @@
"js-base64": "^2.5.0",
"serve-static": "^1.13.2",
"socket.io": "^2.2.0",
"sonos": "git+https://github.com/Villarrealized/node-sonos.git#master"
"sonos": "git+https://github.com/Villarrealized/node-sonos.git#feature/add-soundcloud-support"
},
"devDependencies": {
"eslint": "^5.10.0",
Expand Down
15 changes: 15 additions & 0 deletions server/src/routes/libraryDetail.js
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,21 @@ module.exports = function LibraryDetail(sonosNetwork) {
this.handleError(error, res);
}
});
this.router.post('/sp/:name', async (req, res) => {
try {
const playlistName = Base64.decode(req.params.name);
const playlistURI = await this.sonosNetwork.musicLibrary.getSonosPlaylistId(playlistName);
const songs = await this.sonosNetwork.musicLibrary.browse({
searchCategory: 'sonos_playlists',
searchTerm: playlistURI,
searchOptions: { start: req.body.start, total: req.body.total },
search: true,
});
res.status(200).send(songs);
} catch (error) {
this.handleError(error, res);
}
});

this.handleError = function handleError(error, res) {
const { message } = error;
Expand Down
21 changes: 16 additions & 5 deletions server/src/routes/musicLibrary.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,20 @@ module.exports = function MusicLibrary(sonosNetwork) {
this.router.post('/search/:libraryItem', async (req, res) => {
try {
const { libraryItem } = req.params;

if (libraryItem !== 'results') {
const results = await this.sonosNetwork.musicLibrary.browse({
if (libraryItem === 'results') {
const results = await this.sonosNetwork.musicLibrary.getTopResults({
searchCategory: libraryItem,
searchOptions: { start: req.body.start, total: req.body.total },
searchTerm: req.body.searchTerm,
search: true,
});
res.status(200).send(results);
} else if (libraryItem === 'sonos_playlists') {
const results = await this.sonosNetwork.musicLibrary.searchSonosPlaylists({ searchTerm: req.body.searchTerm });
res.status(200).send(results);
} else {
const results = await this.sonosNetwork.musicLibrary.getTopResults({
const results = await this.sonosNetwork.musicLibrary.browse({
searchCategory: libraryItem,
searchOptions: { start: req.body.start, total: req.body.total },
searchTerm: req.body.searchTerm,
search: true,
});
Expand All @@ -29,6 +31,15 @@ module.exports = function MusicLibrary(sonosNetwork) {
}
});

this.router.post('/favorites', async (req, res) => {
try {
const results = await this.sonosNetwork.musicLibrary.getFavorites();
res.status(200).send(results);
} catch (error) {
res.status(500).send(error.message);
}
});

this.router.post('/:libraryItem', async (req, res) => {
try {
const { libraryItem } = req.params;
Expand Down
Loading

0 comments on commit 451441d

Please sign in to comment.