[vlc-devel] [PATCH 15/15] http: add new routes & components

David Loiret loiret.d at gmail.com
Tue Jun 23 14:42:50 CEST 2020


---
 share/Makefile.am                             |   48 +
 share/lua/http/body.html                      |   14 +-
 share/lua/http/head.html                      |    2 +-
 share/lua/http/index.html                     | 1064 +++++++++++++++--
 .../album-item/album-item-grid.component.html |   20 +
 .../album-item/album-item-grid.component.js   |   20 +
 .../album-item/album-item-list.component.html |   27 +
 .../album-item/album-item-list.component.js   |   16 +
 .../album-item/album-item.component.scss      |   17 +
 .../components/albums/albums.component.html   |   63 +
 .../src/components/albums/albums.component.js |   41 +
 .../components/albums/albums.component.scss   |   23 +
 .../artist-item-grid.component.html           |   15 +
 .../artist-item/artist-item-grid.component.js |   12 +
 .../artist-item-grid.component.scss           |   17 +
 .../components/artists/artists.component.html |   10 +
 .../components/artists/artists.component.js   |   25 +
 .../detail/artists-detail.component.html      |   21 +
 .../detail/artists-detail.component.js        |   25 +
 .../detail/artists-detail.component.scss      |   59 +
 .../continue-watching.component.html          |   11 +
 .../continue-watching.component.js            |   13 +
 .../controls/filebrowser.component.html       |   18 -
 .../controls/filebrowser.component.js         |   83 --
 .../controls/sidenav.component.html           |   31 -
 .../components/controls/sidenav.component.js  |   26 -
 .../controls/sidenav.component.scss           |   50 -
 .../enqueue-button.component.html             |    5 +
 .../enqueue-button.component.js               |   14 +
 .../enqueue-button.component.scss             |    4 +
 .../equalizer/equalizer.component.html        |    2 +-
 .../genre-item/genre-item-grid.component.html |   12 +
 .../genre-item/genre-item-grid.component.js   |   16 +
 .../components/genres/genres.component.html   |   10 +
 .../src/components/genres/genres.component.js |   25 +
 .../grid-item/grid-item.component.html        |   14 +
 .../grid-item/grid-item.component.js          |    5 +
 .../item-layout-button.component.html         |    6 +
 .../item-layout-button.component.js           |   16 +
 .../library-item/library-item.component.html  |   11 +
 .../library-item/library-item.component.js    |   15 +
 .../library-item/library-item.component.scss  |   26 +
 .../components/library/library.component.html |   17 +
 .../components/library/library.component.js   |    5 +
 .../components/library/library.component.scss |    3 +
 .../media-player/media-player.component.html  |   66 +
 .../media-player/media-player.component.js    |  268 +++++
 .../media-player/media-player.component.scss  |  133 +++
 .../play-button-secondary.component.html      |    5 +
 .../play-button-secondary.component.js        |   15 +
 .../play-button-secondary.component.scss      |   12 +
 .../play-button/play-button.component.html    |    5 +
 .../play-button/play-button.component.js      |   16 +
 .../play-button/play-button.component.scss    |   28 +
 .../components/player/player.component.scss   |   19 -
 .../components/player/plyr.lib.custom.scss    |  758 ------------
 .../src/components/player/plyr.lib.vars.scss  |   71 --
 .../src/components/player/plyr.methods.js     |   41 -
 .../playlist/buttons.playlist.component.html  |   18 +-
 .../playlist/buttons.playlist.component.js    |   39 +-
 .../playlist/buttons.playlist.component.scss  |   10 +
 .../playlist-audio-item.component.html        |   27 +
 .../playlist/playlist-audio-item.component.js |   23 +
 .../playlist-audio-item.component.scss        |   24 +
 .../playlist-video-item.component.html        |   25 +
 .../playlist/playlist-video-item.component.js |   31 +
 .../playlist-video-item.component.scss        |   24 +
 .../playlist/playlist.component.html          |   36 +-
 .../components/playlist/playlist.component.js |  108 +-
 .../playlist/playlist.component.scss          |   63 +-
 .../playlists-item.component.html             |   28 +
 .../playlists-item.component.js               |    7 +
 .../playlists-item.component.scss             |   10 +
 .../playlists/playlists.component.html        |   13 +
 .../playlists/playlists.component.js          |   19 +
 .../sot/stream.manager.component.html         |    2 +-
 .../svg-icon/svg-icon.component.scss          |    4 +-
 .../tracksync.component.html                  |    2 +-
 .../track-item-table.component.html           |   28 +
 .../track-item/track-item-table.component.js  |   16 +
 .../components/tracks/tracks.component.html   |   25 +
 .../src/components/tracks/tracks.component.js |   22 +
 .../video-item/video-item-grid.component.html |   33 +
 .../video-item/video-item-grid.component.js   |    9 +
 .../video-item/video-item-list.component.html |   20 +
 .../video-item/video-item-list.component.js   |    9 +
 .../video-item-poster.component.html          |   27 +
 .../video-item/video-item-poster.component.js |    4 +
 .../components/videos/videos.component.html   |   90 ++
 .../src/components/videos/videos.component.js |   22 +
 .../src/components/vlm/vlm.component.html     |    4 +-
 .../http/src/components/vlm/vlm.component.js  |   10 +-
 .../routes/browse/browse-view.component.html  |   51 +
 .../routes/browse/browse-view.component.js    |   19 +
 .../routes/browse/browse-view.component.scss  |    3 +
 .../discover/discover-view.component.html     |   37 +
 .../discover/discover-view.component.js       |    5 +
 .../home/discover-home-view.component.html    |    4 +
 .../home/discover-home-view.component.js      |    5 +
 .../discover-services-view.component.html     |   15 +
 .../discover-services-view.component.js       |   49 +
 .../discover-services-view.component.scss     |   33 +
 .../discover-services-tv-view.component.html  |   10 +
 .../tv/discover-services-tv-view.component.js |   11 +
 .../url/discover-url-view.component.html      |    4 +
 .../url/discover-url-view.component.js        |    5 +
 .../src/routes/main/main-view.component.html  |    3 +
 .../src/routes/main/main-view.component.js    |    5 +
 .../albums/music-albums-view.component.html   |    5 +
 .../albums/music-albums-view.component.js     |    6 +
 .../music-artists-detail-view.component.html  |    5 +
 .../music-artists-detail-view.component.js    |    5 +
 .../artists/music-artists-view.component.html |    5 +
 .../artists/music-artists-view.component.js   |    5 +
 .../genres/music-genres-view.component.html   |    5 +
 .../genres/music-genres-view.component.js     |    5 +
 .../routes/music/music-view.component.html    |   39 +
 .../src/routes/music/music-view.component.js  |    7 +
 .../music-playlists-view.component.html       |    5 +
 .../music-playlists-view.component.js         |    5 +
 .../tracks/music-tracks-view.component.html   |    5 +
 .../tracks/music-tracks-view.component.js     |    5 +
 .../network/network-view.component.html       |   31 +
 .../routes/network/network-view.component.js  |    4 +
 .../video/all/video-all-view.component.html   |    6 +
 .../video/all/video-all-view.component.js     |    5 +
 .../movies/video-movies-view.component.html   |    3 +
 .../movies/video-movies-view.component.js     |    5 +
 .../video-playlists-view.component.html       |    3 +
 .../video-playlists-view.component.js         |    5 +
 .../tvshows/video-tvshows-view.component.html |    3 +
 .../tvshows/video-tvshows-view.component.js   |    5 +
 .../routes/video/video-view.component.html    |   38 +
 .../src/routes/video/video-view.component.js  |    7 +
 .../routes/watch/watch-view.component.html    |   36 +
 .../src/routes/watch/watch-view.component.js  |    7 +
 share/lua/http/src/scss/components.scss       |   16 +
 .../http/src/services/initialize.service.js   |  207 +++-
 138 files changed, 3534 insertions(+), 1459 deletions(-)
 create mode 100644 share/lua/http/src/components/albums/album-item/album-item-grid.component.html
 create mode 100644 share/lua/http/src/components/albums/album-item/album-item-grid.component.js
 create mode 100644 share/lua/http/src/components/albums/album-item/album-item-list.component.html
 create mode 100644 share/lua/http/src/components/albums/album-item/album-item-list.component.js
 create mode 100644 share/lua/http/src/components/albums/album-item/album-item.component.scss
 create mode 100644 share/lua/http/src/components/albums/albums.component.html
 create mode 100644 share/lua/http/src/components/albums/albums.component.js
 create mode 100644 share/lua/http/src/components/albums/albums.component.scss
 create mode 100644 share/lua/http/src/components/artists/artist-item/artist-item-grid.component.html
 create mode 100644 share/lua/http/src/components/artists/artist-item/artist-item-grid.component.js
 create mode 100644 share/lua/http/src/components/artists/artist-item/artist-item-grid.component.scss
 create mode 100644 share/lua/http/src/components/artists/artists.component.html
 create mode 100644 share/lua/http/src/components/artists/artists.component.js
 create mode 100644 share/lua/http/src/components/artists/detail/artists-detail.component.html
 create mode 100644 share/lua/http/src/components/artists/detail/artists-detail.component.js
 create mode 100644 share/lua/http/src/components/artists/detail/artists-detail.component.scss
 create mode 100644 share/lua/http/src/components/continue-watching/continue-watching.component.html
 create mode 100644 share/lua/http/src/components/continue-watching/continue-watching.component.js
 delete mode 100644 share/lua/http/src/components/controls/filebrowser.component.html
 delete mode 100644 share/lua/http/src/components/controls/filebrowser.component.js
 delete mode 100644 share/lua/http/src/components/controls/sidenav.component.html
 delete mode 100644 share/lua/http/src/components/controls/sidenav.component.js
 delete mode 100644 share/lua/http/src/components/controls/sidenav.component.scss
 create mode 100644 share/lua/http/src/components/enqueue-button/enqueue-button.component.html
 create mode 100644 share/lua/http/src/components/enqueue-button/enqueue-button.component.js
 create mode 100644 share/lua/http/src/components/enqueue-button/enqueue-button.component.scss
 create mode 100644 share/lua/http/src/components/genres/genre-item/genre-item-grid.component.html
 create mode 100644 share/lua/http/src/components/genres/genre-item/genre-item-grid.component.js
 create mode 100644 share/lua/http/src/components/genres/genres.component.html
 create mode 100644 share/lua/http/src/components/genres/genres.component.js
 create mode 100644 share/lua/http/src/components/grid-item/grid-item.component.html
 create mode 100644 share/lua/http/src/components/grid-item/grid-item.component.js
 create mode 100644 share/lua/http/src/components/item-layout-button/item-layout-button.component.html
 create mode 100644 share/lua/http/src/components/item-layout-button/item-layout-button.component.js
 create mode 100644 share/lua/http/src/components/library/library-item/library-item.component.html
 create mode 100644 share/lua/http/src/components/library/library-item/library-item.component.js
 create mode 100644 share/lua/http/src/components/library/library-item/library-item.component.scss
 create mode 100644 share/lua/http/src/components/library/library.component.html
 create mode 100644 share/lua/http/src/components/library/library.component.js
 create mode 100644 share/lua/http/src/components/library/library.component.scss
 create mode 100644 share/lua/http/src/components/media-player/media-player.component.html
 create mode 100644 share/lua/http/src/components/media-player/media-player.component.js
 create mode 100644 share/lua/http/src/components/media-player/media-player.component.scss
 create mode 100644 share/lua/http/src/components/play-button/play-button-secondary.component.html
 create mode 100644 share/lua/http/src/components/play-button/play-button-secondary.component.js
 create mode 100644 share/lua/http/src/components/play-button/play-button-secondary.component.scss
 create mode 100644 share/lua/http/src/components/play-button/play-button.component.html
 create mode 100644 share/lua/http/src/components/play-button/play-button.component.js
 create mode 100644 share/lua/http/src/components/play-button/play-button.component.scss
 delete mode 100644 share/lua/http/src/components/player/player.component.scss
 delete mode 100644 share/lua/http/src/components/player/plyr.lib.custom.scss
 delete mode 100644 share/lua/http/src/components/player/plyr.lib.vars.scss
 delete mode 100644 share/lua/http/src/components/player/plyr.methods.js
 create mode 100644 share/lua/http/src/components/playlist/buttons.playlist.component.scss
 create mode 100644 share/lua/http/src/components/playlist/playlist-audio-item.component.html
 create mode 100644 share/lua/http/src/components/playlist/playlist-audio-item.component.js
 create mode 100644 share/lua/http/src/components/playlist/playlist-audio-item.component.scss
 create mode 100644 share/lua/http/src/components/playlist/playlist-video-item.component.html
 create mode 100644 share/lua/http/src/components/playlist/playlist-video-item.component.js
 create mode 100644 share/lua/http/src/components/playlist/playlist-video-item.component.scss
 create mode 100644 share/lua/http/src/components/playlists/playlists-item/playlists-item.component.html
 create mode 100644 share/lua/http/src/components/playlists/playlists-item/playlists-item.component.js
 create mode 100644 share/lua/http/src/components/playlists/playlists-item/playlists-item.component.scss
 create mode 100644 share/lua/http/src/components/playlists/playlists.component.html
 create mode 100644 share/lua/http/src/components/playlists/playlists.component.js
 create mode 100644 share/lua/http/src/components/tracks/track-item/track-item-table.component.html
 create mode 100644 share/lua/http/src/components/tracks/track-item/track-item-table.component.js
 create mode 100644 share/lua/http/src/components/tracks/tracks.component.html
 create mode 100644 share/lua/http/src/components/tracks/tracks.component.js
 create mode 100644 share/lua/http/src/components/videos/video-item/video-item-grid.component.html
 create mode 100644 share/lua/http/src/components/videos/video-item/video-item-grid.component.js
 create mode 100644 share/lua/http/src/components/videos/video-item/video-item-list.component.html
 create mode 100644 share/lua/http/src/components/videos/video-item/video-item-list.component.js
 create mode 100644 share/lua/http/src/components/videos/video-item/video-item-poster.component.html
 create mode 100644 share/lua/http/src/components/videos/video-item/video-item-poster.component.js
 create mode 100644 share/lua/http/src/components/videos/videos.component.html
 create mode 100644 share/lua/http/src/components/videos/videos.component.js
 create mode 100644 share/lua/http/src/routes/browse/browse-view.component.html
 create mode 100644 share/lua/http/src/routes/browse/browse-view.component.js
 create mode 100644 share/lua/http/src/routes/browse/browse-view.component.scss
 create mode 100644 share/lua/http/src/routes/discover/discover-view.component.html
 create mode 100644 share/lua/http/src/routes/discover/discover-view.component.js
 create mode 100644 share/lua/http/src/routes/discover/home/discover-home-view.component.html
 create mode 100644 share/lua/http/src/routes/discover/home/discover-home-view.component.js
 create mode 100644 share/lua/http/src/routes/discover/services/discover-services-view.component.html
 create mode 100644 share/lua/http/src/routes/discover/services/discover-services-view.component.js
 create mode 100644 share/lua/http/src/routes/discover/services/discover-services-view.component.scss
 create mode 100644 share/lua/http/src/routes/discover/services/tv/discover-services-tv-view.component.html
 create mode 100644 share/lua/http/src/routes/discover/services/tv/discover-services-tv-view.component.js
 create mode 100644 share/lua/http/src/routes/discover/url/discover-url-view.component.html
 create mode 100644 share/lua/http/src/routes/discover/url/discover-url-view.component.js
 create mode 100644 share/lua/http/src/routes/main/main-view.component.html
 create mode 100644 share/lua/http/src/routes/main/main-view.component.js
 create mode 100644 share/lua/http/src/routes/music/albums/music-albums-view.component.html
 create mode 100644 share/lua/http/src/routes/music/albums/music-albums-view.component.js
 create mode 100644 share/lua/http/src/routes/music/artists/detail/music-artists-detail-view.component.html
 create mode 100644 share/lua/http/src/routes/music/artists/detail/music-artists-detail-view.component.js
 create mode 100644 share/lua/http/src/routes/music/artists/music-artists-view.component.html
 create mode 100644 share/lua/http/src/routes/music/artists/music-artists-view.component.js
 create mode 100644 share/lua/http/src/routes/music/genres/music-genres-view.component.html
 create mode 100644 share/lua/http/src/routes/music/genres/music-genres-view.component.js
 create mode 100644 share/lua/http/src/routes/music/music-view.component.html
 create mode 100644 share/lua/http/src/routes/music/music-view.component.js
 create mode 100644 share/lua/http/src/routes/music/playlists/music-playlists-view.component.html
 create mode 100644 share/lua/http/src/routes/music/playlists/music-playlists-view.component.js
 create mode 100644 share/lua/http/src/routes/music/tracks/music-tracks-view.component.html
 create mode 100644 share/lua/http/src/routes/music/tracks/music-tracks-view.component.js
 create mode 100644 share/lua/http/src/routes/network/network-view.component.html
 create mode 100644 share/lua/http/src/routes/network/network-view.component.js
 create mode 100644 share/lua/http/src/routes/video/all/video-all-view.component.html
 create mode 100644 share/lua/http/src/routes/video/all/video-all-view.component.js
 create mode 100644 share/lua/http/src/routes/video/movies/video-movies-view.component.html
 create mode 100644 share/lua/http/src/routes/video/movies/video-movies-view.component.js
 create mode 100644 share/lua/http/src/routes/video/playlists/video-playlists-view.component.html
 create mode 100644 share/lua/http/src/routes/video/playlists/video-playlists-view.component.js
 create mode 100644 share/lua/http/src/routes/video/tvshows/video-tvshows-view.component.html
 create mode 100644 share/lua/http/src/routes/video/tvshows/video-tvshows-view.component.js
 create mode 100644 share/lua/http/src/routes/video/video-view.component.html
 create mode 100644 share/lua/http/src/routes/video/video-view.component.js
 create mode 100644 share/lua/http/src/routes/watch/watch-view.component.html
 create mode 100644 share/lua/http/src/routes/watch/watch-view.component.js

diff --git a/share/Makefile.am b/share/Makefile.am
index d74bb86f19..b591b70baf 100644
--- a/share/Makefile.am
+++ b/share/Makefile.am
@@ -20,13 +20,61 @@ JS_TARGETS = --js $(srcdir)/lua/http/src/services/initialize.service.js \
 --js $(srcdir)/lua/http/src/store/modules/playlist.js \
 --js $(srcdir)/lua/http/src/store/modules/status.js \
 --js $(srcdir)/lua/http/src/store/modules/vlm_cmd.js \
+--js $(srcdir)/lua/http/src/components/albums/album-item/album-item-grid.component.js \
+--js $(srcdir)/lua/http/src/components/albums/album-item/album-item-list.component.js \
+--js $(srcdir)/lua/http/src/components/artists/artist-item/artist-item-grid.component.js \
+--js $(srcdir)/lua/http/src/components/albums/albums.component.js \
+--js $(srcdir)/lua/http/src/components/artists/artists.component.js \
+--js $(srcdir)/lua/http/src/components/artists/detail/artists-detail.component.js \
+--js $(srcdir)/lua/http/src/components/continue-watching/continue-watching.component.js \
 --js $(srcdir)/lua/http/src/components/playlist/playlist.component.js \
+--js $(srcdir)/lua/http/src/components/playlist/playlist-video-item.component.js \
+--js $(srcdir)/lua/http/src/components/playlist/playlist-audio-item.component.js \
 --js $(srcdir)/lua/http/src/components/playlist/buttons.playlist.component.js \
+--js $(srcdir)/lua/http/src/components/playlists/playlists.component.js \
+--js $(srcdir)/lua/http/src/components/playlists/playlists-item/playlists-item.component.js \
 --js $(srcdir)/lua/http/src/components/vlm/vlm.component.js \
 --js $(srcdir)/lua/http/src/components/equalizer/equalizer.component.js \
+--js $(srcdir)/lua/http/src/components/genres/genres.component.js \
+--js $(srcdir)/lua/http/src/components/grid-item/grid-item.component.js \
+--js $(srcdir)/lua/http/src/components/item-layout-button/item-layout-button.component.js \
+--js $(srcdir)/lua/http/src/components/genres/genre-item/genre-item-grid.component.js \
+--js $(srcdir)/lua/http/src/components/library/library.component.js \
+--js $(srcdir)/lua/http/src/components/library/library-item/library-item.component.js \
+--js $(srcdir)/lua/http/src/components/media-player/media-player.component.js \
+--js $(srcdir)/lua/http/src/components/enqueue-button/enqueue-button.component.js \
+--js $(srcdir)/lua/http/src/components/play-button/play-button-secondary.component.js \
+--js $(srcdir)/lua/http/src/components/play-button/play-button.component.js \
+--js $(srcdir)/lua/http/src/routes/main/main-view.component.js \
+--js $(srcdir)/lua/http/src/routes/browse/browse-view.component.js \
+--js $(srcdir)/lua/http/src/routes/discover/discover-view.component.js \
+--js $(srcdir)/lua/http/src/routes/discover/home/discover-home-view.component.js \
+--js $(srcdir)/lua/http/src/routes/discover/services/discover-services-view.component.js \
+--js $(srcdir)/lua/http/src/routes/discover/services/tv/discover-services-tv-view.component.js \
+--js $(srcdir)/lua/http/src/routes/discover/url/discover-url-view.component.js \
+--js $(srcdir)/lua/http/src/routes/music/music-view.component.js \
+--js $(srcdir)/lua/http/src/routes/music/albums/music-albums-view.component.js \
+--js $(srcdir)/lua/http/src/routes/music/artists/music-artists-view.component.js \
+--js $(srcdir)/lua/http/src/routes/music/artists/detail/music-artists-detail-view.component.js \
+--js $(srcdir)/lua/http/src/routes/music/genres/music-genres-view.component.js \
+--js $(srcdir)/lua/http/src/routes/music/tracks/music-tracks-view.component.js \
+--js $(srcdir)/lua/http/src/routes/music/playlists/music-playlists-view.component.js \
+--js $(srcdir)/lua/http/src/routes/network/network-view.component.js \
+--js $(srcdir)/lua/http/src/routes/video/video-view.component.js \
+--js $(srcdir)/lua/http/src/routes/video/all/video-all-view.component.js \
+--js $(srcdir)/lua/http/src/routes/video/movies/video-movies-view.component.js \
+--js $(srcdir)/lua/http/src/routes/video/tvshows/video-tvshows-view.component.js \
+--js $(srcdir)/lua/http/src/routes/video/playlists/video-playlists-view.component.js \
+--js $(srcdir)/lua/http/src/routes/watch/watch-view.component.js \
 --js $(srcdir)/lua/http/src/components/track-synchronisation/tracksync.component.js \
 --js $(srcdir)/lua/http/src/components/sot/stream.manager.component.js \
 --js $(srcdir)/lua/http/src/components/svg-icon/svg-icon.component.js \
+--js $(srcdir)/lua/http/src/components/tracks/tracks.component.js \
+--js $(srcdir)/lua/http/src/components/videos/video-item/video-item-grid.component.js \
+--js $(srcdir)/lua/http/src/components/videos/video-item/video-item-list.component.js \
+--js $(srcdir)/lua/http/src/components/videos/video-item/video-item-poster.component.js \
+--js $(srcdir)/lua/http/src/components/videos/videos.component.js \
+--js $(srcdir)/lua/http/src/components/tracks/track-item/track-item-table.component.js \
 --js $(srcdir)/lua/http/src/assets/compiled-icons/*.js \
 --js_output_file $(srcdir)/lua/http/dist/js/script.min.js
 
diff --git a/share/lua/http/body.html b/share/lua/http/body.html
index 908104d101..9f3667534f 100644
--- a/share/lua/http/body.html
+++ b/share/lua/http/body.html
@@ -1,18 +1,12 @@
 <body>
     <div id="app">
-        <sidenav></sidenav>
-
-        <playlist></playlist>
-
+        <!-- <sidenav></sidenav> -->
+        <main-view></main-view>
         <!-- Modals -->
         <div id="vlmModal" class="modal fade" role="dialog">
             <vlm-modal></vlm-modal>
         </div>
 
-        <div id="fileModal" class="modal fade" role="dialog">
-            <file-modal></file-modal>
-        </div>
-
         <div id="equalizerModal" class="modal fade" role="dialog">
             <equalizer-modal></equalizer-modal>
         </div>
@@ -25,8 +19,4 @@
             <stream-manager-modal></stream-manager-modal>
         </div>
     </div>
-
-    <div id="videoPlayer">
-            <video></video>
-    </div>
 </body>
diff --git a/share/lua/http/head.html b/share/lua/http/head.html
index cb2f8e4e0d..9babc62bdf 100644
--- a/share/lua/http/head.html
+++ b/share/lua/http/head.html
@@ -10,7 +10,7 @@
     <link rel="icon" type="image/png" href="dist/assets/favicons/16.png" sizes="16x16"/>
     <link rel="icon" type="image/x-icon" href="dist/assets/favicons/favicon.ico"/>
     <link rel="stylesheet" href="dist/css/vendors.min.css">
-    <link rel="stylesheet" href="dist/css/style.min.css">
+    <link rel="stylesheet" href="dist/css/style.css">
     <script src="dist/js/vendors.min.js"></script>
     <script src="dist/js/script.min.js"></script>
 </head>
diff --git a/share/lua/http/index.html b/share/lua/http/index.html
index 69c2438938..e54fdab9cd 100644
--- a/share/lua/http/index.html
+++ b/share/lua/http/index.html
@@ -4,28 +4,25 @@
     <meta charset="utf-8">
     <meta name="viewport" content="initial-scale=1">
     <title>VLC media player - Web Interface</title>
-    <link href="favicon.ico" type="image/x-icon" rel="shortcut icon" />
+    <link rel="apple-touch-icon" sizes="180x180" href="dist/assets/favicons/apple-touch-icon.png">
+    <link rel="apple-touch-icon-precomposed" sizes="180x180" href="dist/assets/favicons/apple-touch-icon.png">
+    <link rel="icon" type="image/png" href="dist/assets/favicons/32.png" sizes="32x32"/>
+    <link rel="icon" type="image/png" href="dist/assets/favicons/16.png" sizes="16x16"/>
+    <link rel="icon" type="image/x-icon" href="dist/assets/favicons/favicon.ico"/>
     <link rel="stylesheet" href="dist/css/vendors.min.css">
-    <link rel="stylesheet" href="dist/css/style.min.css">
-    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
+    <link rel="stylesheet" href="dist/css/style.css">
     <script src="dist/js/vendors.min.js"></script>
     <script src="dist/js/script.min.js"></script>
 </head>
 <body>
     <div id="app">
-        <sidenav></sidenav>
-
-        <playlist></playlist>
-
+        <!-- <sidenav></sidenav> -->
+        <main-view></main-view>
         <!-- Modals -->
         <div id="vlmModal" class="modal fade" role="dialog">
             <vlm-modal></vlm-modal>
         </div>
 
-        <div id="fileModal" class="modal fade" role="dialog">
-            <file-modal></file-modal>
-        </div>
-
         <div id="equalizerModal" class="modal fade" role="dialog">
             <equalizer-modal></equalizer-modal>
         </div>
@@ -38,52 +35,144 @@
             <stream-manager-modal></stream-manager-modal>
         </div>
     </div>
-
-    <div id="videoPlayer">
-            <video></video>
-    </div>
 </body>
+<script type="text/x-template" id="continue-watching-template">
+    <div class="continue-watching-view container-fluid">
+        <!-- <div v-if="displayMode === 'list'" class="row">
+            <album-item-list v-for="(item, index) in items" :item="item" :key="index"></album-item-list>
+        </div> -->
+        <h5 class="separator-title">Continue watching</h5>
+        <div class="row" v-if="displayMode === 'grid'">
+            <video-item-grid class="col-12 col-sm-6 col-md-4 col-lg-4" v-for="(item, index) in items" :item="item" :key="index"></video-item-grid>
+        </div>
+    </div>
+</script>
 <!-- Templates -->
 <!-- Playlist Template -->
 <script type="text/x-template" id="playlist-template">
-    <div>
-        <div id="playlistNav" class="playlistNav">
-            <div class="container">
-                <playlist-buttons></playlist-buttons>
-                <div>
-                    <h1 class="playlistHeader">Playlist</h1>
-                </div>
-                <div id="playlist">
-                    <ol type="1">
-                        <li v-for="item in this.$parent.playlistItems">
-                            <button v-on:click="play(item.src, item.id)" class="playlistItem">{{ item.title }}</button>
-                            <button v-on:click="removeItem(item.id)" type="button" class="btn-circle col-xs">
-                                <svg-icon name="delete"></svg-icon>
-                            </button>
-                        </li>
-                    </ol>
-                </div>
-                <div class="playlistNavMobile" id="mobilePlaylistNavButton">
-                    <center><svg-icon name="format-list-bulleted"></svg-icon></center>
-                </div>
+    <div id="playlistNav" class="playlistNav h-100">
+        <h6 class="playlist-header">Playlist • <span class="text-muted">{{ ' ' + items.length }}{{ items.length ? ' elements' : ''}}</span></h6>
+        <div class="dropdown-divider"></div>
+        <div id="playlist">
+            <div v-for="(item, index) in items" :key="index">
+                <playlist-video-item
+                    v-if="item.type === 'leaf' || item.type === 'video'"
+                    :item="item"
+                    :index="index">
+                </playlist-video-item>
+                <playlist-audio-item
+                    v-if="item.type === 'audio'"
+                    :item="item"
+                    :index="index">
+                </playlist-audio-item>
             </div>
         </div>
+        <playlist-buttons></playlist-buttons>
     </div>
 </script>
 <!-- Button Template -->
 <script type="text/x-template" id="button-template">
-    <div>
-        <div class="playlistIcons">
-            <div class="row">
-                <button type="button" class="btn-circle col-xs" id="randomButton"><svg-icon name="shuffle-variant"></svg-icon></button>
-                <button type="button" class="btn-circle col-xs" id="repeatButton"><svg-icon name="replay"></svg-icon></button>
-                <button type="button" class="btn-circle col-xs" id="playButton"><svg-icon name="play-circle-outline"></svg-icon></button>
+    <div class="playlist-actions">
+        <div class="d-flex justify-content-between mx-2" :class="{ disabled: !playlist.activeItem }">
+            <button v-on:click="toggleRepeat()" type="button" class="btn-circle" id="repeatButton"><svg-icon name="repeat"></svg-icon></button>
+            <button v-on:click="toggleRandom()" type="button" class="btn-circle" id="randomButton"><svg-icon name="shuffle"></svg-icon></button>
+            <!-- <span>{{ playlist.items.length ? playlist.items.length + ' items': '' }}</span> -->
+            <button type="button" class="btn-circle" data-toggle="modal" data-target="#equalizerModal"><svg-icon name="sort"></svg-icon></button>
+            <button v-on:click="removeItem(playlist.activeItem.id)" type="button" class="btn-circle"><svg-icon name="delete_outline"></svg-icon></button>
+        </div>
+    </div>
+</script>
+<!-- Playlist item Template -->
+<script type="text/x-template" id="playlist-video-item-template">
+    <button v-on:dblclick="play(item.uri, item.id)" class="btn btn-link pl-2 playlist-video-item list-item d-flex justify-content-between align-items-start position-relative"
+        :class="{ current: item.current === 'current', active: item.active }">
+        <div class="d-flex align-items-start w-100 mr-n3">
+            <div class="item-img" v-on:click="setActiveItem(item)">
+                <img v-if="item.src" class="img-art" :src="item.src" @error="onImgError(item)">
+                <svg-icon v-if="!item.src" name="placeholder-album" class="placeholder-album-icon"></svg-icon>
+                <div class="overlay">
+                    <play-button :item="item" class="item-primary-action item-action"></play-button>
+                </div>
+            </div>
+            <span class="item-duration">{{ item.duration | formatDuration }}</span>
+            <!-- <span class="item-nb">{{ index + 1 }}</span> -->
+            <div class="item-body d-flex flex-column mx-2 pt-1">
+                <router-link to="" class="item-title text-truncate">{{ item.name }}</router-link>
+                <router-link to="" class="item-subtitle text-truncate">{{ item.subtitle }}</router-link>
+            </div>
+        </div>
+        <div class="d-flex item-action pt-1">
+            <svg-icon name="more_vert"></svg-icon>
+        </div>
+        <div class="item-progress-bar" :style="{width: item.progress}"></div>
+    </button>
+</script>
+<!-- Playlist item Template -->
+<script type="text/x-template" id="playlist-audio-item-template">
+    <button v-on:dblclick="setActiveItem(item)" class="btn btn-link pl-2 playlist-audio-item list-item d-flex justify-content-between align-items-start position-relative"
+        :class="{ current: item.current === 'current', active: item.active }">
+        <div class="d-flex align-items-start w-100 mr-n3">
+            <div class="item-img" v-on:click="setActiveItem(item)">
+                <img v-if="item.src" class="img-art" :src="item.src" @error="onImgError(item)">
+                <svg-icon v-if="!item.src" name="placeholder-album" class="placeholder-album-icon"></svg-icon>
+                <div class="overlay">
+                    <play-button class="item-primary-action item-action"></play-button>
+                </div>
             </div>
-            <div class="row">
-                <button type="button" class="btn-circle col-xs" data-toggle="modal" data-target="#equalizerModal"><svg-icon name="poll"></svg-icon></button>
-                <button type="button" class="btn-circle col-xs" data-toggle="modal" data-target="#fileModal"><svg-icon name="plus"></svg-icon></button>
+            <span class="item-duration">{{ item.duration | formatDuration }}</span>
+            <!-- <span class="item-nb">{{ index + 1 }}</span> -->
+            <div class="item-body d-flex flex-column mx-2 pt-1">
+                <router-link to="" class="item-title text-truncate">{{ item.name }}</router-link>
+                <!-- <span class="item-subtitle text-truncate d-flex">
+                    <router-link to="/artist">{{ item.subtitle }}</router-link> • <router-link to="/album">{{ item.album }}</router-link>
+                </span> -->
             </div>
         </div>
+        <div class="d-flex item-action pt-1">
+            <svg-icon name="more_vert"></svg-icon>
+        </div>
+        <!-- <div class="item-progress-bar" :style="{width: item.progress}"></div> -->
+    </button>
+</script>
+<script type="text/x-template" id="playlists-template">
+    <div class="playlists-view container-fluid">
+        <div class="row">
+            <div class="playlists-item grid-item col-12 col-sm-6 col-md-4 col-lg-3 col-xl-2">
+                <div class="create-item">
+                    Create a playlist
+                    <svg-icon name="add"></svg-icon>
+                </div>
+            </div>
+            <playlists-item v-for="item in items" :item="item"></playlists-item>
+        </div>
+    </div>
+</script>
+<script type="text/x-template" id="playlists-item-template">
+    <div class="playlists-item grid-item col-12 col-sm-6 col-md-4 col-lg-3 col-xl-2">
+        <div class="item-img">
+            <div>
+                <div class="d-flex">
+                    <img :src="item.images[0]">
+                    <img :src="item.images[1]">
+                </div>
+                <div class="d-flex">
+                    <img :src="item.images[2]">
+                    <img :src="item.images[3]">
+                </div>
+            </div>
+            <div class="overlay">
+                <play-button class="item-primary-action"></play-button>
+                <div class="item-secondary-action">
+                    <div class="icon-wrapper">
+                        <svg-icon name="more-horizontal"></svg-icon>
+                    </div>
+                </div>
+            </div>
+        </div>
+        <div class="item-body">
+            <router-link to="/album" class="item-title text-truncate">{{ item.name }}</router-link>
+            <router-link to="/artist" class="item-subtitle text-truncate">{{ item.nbItems }} files • {{ item.duration }}</router-link>
+        </div>
     </div>
 </script>
 <!-- equalizer modal Template -->
@@ -93,8 +182,8 @@
             <!-- Modal content-->
             <div class="modal-content">
                 <div class="modal-header">
-                    <button type="button" class="close" data-dismiss="modal">×</button>
                     <h4 class="modal-title">Equalizer</h4>
+                    <button type="button" class="close" data-dismiss="modal">×</button>
                 </div>
                 <div class="modal-body">
                     <input type="range" name="equalizerInput" id="equalizerInput" min="-20" max="20" step="0.1"/>
@@ -106,6 +195,184 @@
         </div>
     </div>
 </script>
+<script type="text/x-template" id="genre-item-grid-template">
+    <div class="item-people genre-item tag-item genre-item-grid-template col-12 col-sm-6 col-md-4 col-lg-3">
+        <div class="item-img">
+            <img :src="item.src">
+            <div class="item-body">
+                <span class="item-title text-truncate">{{ item.name }}</span>
+                <span class="item-subtitle text-truncate">{{ item.nbTracks }} tracks</span>
+            </div>
+            <div class="overlay"></div>
+        </div>
+    </div>
+</script>
+<script type="text/x-template" id="genres-template">
+    <div class="genres-view container-fluid">
+        <!-- <div v-if="displayMode === 'list'" class="row">
+            <artist-item-list v-for="item in items" :item="item"></artist-item-list>
+        </div> -->
+        <div class="row" v-if="layout.itemLayout === 'grid'">
+            <genre-item-grid v-for="(item, index) in music.genres" :item="item" :key="index"></genre-item-grid>
+        </div>
+    </div>
+</script>
+<script type="text/x-template" id="media-player-template">
+    <div id="media-player" class="media-player">
+        <audio class="d-none" controls="false" ref="player" :src="src"></audio>
+        <div class="seek-bar">
+            <div class="seek-bar-track">
+                <div ref="progressBar" class="seek-bar-progress slider-fill"></div>
+                <div ref="bufferBar" class="seek-bar-buffer slider-fill"></div>
+            </div>
+            <input ref="scrubber" class="seek-bar-scrubber" type="range" min="0" max="1000" value="0"></input>
+        </div>
+        <div class="d-flex align-items-center justify-content-center media-player-container">
+            <div class="metadata d-flex flex-1">
+                <!-- Image + info -->
+                <img class="img" v-if="imageSrc" :src="imageSrc">
+                <svg-icon v-if="!imageSrc" name="placeholder-album" class="placeholder-album-icon"></svg-icon>
+                <span class="ml-2 d-flex flex-column justify-content-center">
+                    <span class="title font-weight-bold">{{ status.information && status.information.category && status.information.category.meta ? status.information.category.meta.filename : '' }} </span>
+                    <span class="subtitle muted-opacity"></span>
+                    <span class="subtitle2 muted-opacity">{{ formattedCurrentTime }} / {{ formattedDuration }}</span>
+                </span>
+            </div>
+            <div class="primary-actions d-flex flex-1 justify-content-center">
+                <!-- Primary actions -->
+                <button v-on:click="toggleRandom()" class="btn btn-link p-0 media-action" :class="{ active: status.random }">
+                    <svg-icon name="shuffle"></svg-icon>
+                </button>
+                <button class="btn btn-link p-0 media-action" v-on:click="previous()">
+                    <svg-icon name="skip_previous"></svg-icon>
+                </button>
+                <button class="btn btn-link p-0 media-action play-action" v-on:click="playing ? pause() : play()">
+                    <svg-icon v-if="status.state === 'playing'" name="pause"></svg-icon>
+                    <svg-icon v-else name="play_arrow"></svg-icon>
+                </button>
+                <button class="btn btn-link p-0 media-action" v-on:click="next()">
+                    <svg-icon name="skip_next"></svg-icon>
+                </button>
+                <button v-on:click="toggleRepeat()" class="btn btn-link p-0 media-action" :class="{ active: status.repeat }">
+                    <svg-icon name="repeat"></svg-icon>
+                </button>
+            </div>
+            <div class="secondary-actions d-flex flex-1 justify-content-end">
+                <!-- Secondary actions -->
+                <div class="media-action volume-container">
+                    <button class="btn btn-link p-0 media-action" v-on:click="toggleMute()">
+                        <svg-icon v-if="volumeIconState === 'mute'" name="volume_off"></svg-icon>
+                        <svg-icon v-if="volumeIconState === 'up'" name="volume_up"></svg-icon>
+                        <svg-icon v-if="volumeIconState === 'down'" name="volume_down"></svg-icon>
+                    </button>
+                    <div class="volume-panel">
+                        <div class="volume-slider">
+                            <div class="volume-slider-track-default"></div>
+                            <div ref="volumeBar" class="volume-slider-track-active"></div>
+                            <input ref="volume" class="volume-slider-input" type="range">
+                        </div>
+                    </div>
+                </div>
+                <button v-on:click="toggleFullscreen()" class="btn btn-link p-0 media-action" :class="{ active: status.fullscreen }">
+                    <svg-icon name="Icon-Fullscreen"></svg-icon>
+                </button>
+                <div class="media-action">
+                    <svg-icon name="more-vertical"></svg-icon>
+                </div>
+            </div>
+        </div>
+    </div>
+</script>
+<script type="text/x-template" id="tracks-template">
+    <div class="tracks-view container-fluid">
+        <!-- <div v-if="displayMode === 'list'" class="row">
+            <artist-item-list v-for="item in items" :item="item"></artist-item-list>
+        </div> -->
+        <div class="row" v-if="displayMode === 'table'">
+            <table class="table table-hover table-item">
+                <thead>
+                  <tr>
+                    <th scope="col"></th>
+                    <th scope="col">Title</th>
+                    <th scope="col">Artist</th>
+                    <th scope="col">Album</th>
+                    <th scope="col">Duration</th>
+                    <th scope="col">Track</th>
+                    <th scope="col"></th>
+                  </tr>
+                </thead>
+                <tbody>
+                    <track-item-table v-for="(item, index) in music.tracks" :item="item" :key="index" :index="index"></track-item-table>
+                </tbody>
+            </table>
+        </div>
+    </div>
+</script>
+<script type="text/x-template" id="track-item-table-template">
+  <tr>
+    <th scope="row" class="d-flex justify-content-between">
+      <div class="item-img">
+        <img v-if="item.src" :src="item.src">
+        <svg-icon v-if="!item.src" name="placeholder-album" class="placeholder-album-icon"></svg-icon>
+        <play-button class="item-primary-action item-action" :item="item"></play-button>
+      </div>
+    </th>
+    <td>
+      <router-link to="">
+        <span>{{ item.title }}</span>
+        <span></span>
+      </router-link>
+    </td>
+    <td><router-link to="">{{ item.artist }}</router-link></td>
+    <td><router-link to="">{{ item.album }}</router-link></td>
+    <td class="text-muted">{{ item.duration | formatDuration }}</td>
+    <td class="text-muted">{{ index + 1 }}</td>
+    <td class="text-muted">
+      <div class="d-flex justify-content-between">
+        <div class="item-action">
+          <svg-icon name="more-horizontal"></svg-icon>
+        </div>
+      </div>
+    </td>
+  </tr>
+</script><script type="text/x-template" id="library-template">
+    <div class="library-view container-fluid">
+        <library-item></library-item>
+        <library-item></library-item>
+        <library-item></library-item>
+        <library-item></library-item>
+        <library-item></library-item>
+        <library-item></library-item>
+        <library-item></library-item>
+        <library-item></library-item>
+        <library-item></library-item>
+        <library-item></library-item>
+        <library-item></library-item>
+        <library-item></library-item>
+        <library-item></library-item>
+    </div>
+</script>
+<script type="text/x-template" id="library-item-template">
+    <div class="library-item">
+        <div class="library-item-img">
+            <img src="http://images.videolan.org/images/VLC-IconSmall.png">
+        </div>
+        <div class="library-item-body">
+            <div class="library-item-name text-truncate">{{ item.name }}</div>
+            <div class="library-item-artist text-truncate">{{ item.artist }}</div>
+        </div>
+    </div>
+</script>
+<script type="text/x-template" id="play-button-secondary-template">
+    <button v-on:click="playItem()" class="btn btn-sm play-button-secondary">
+        <svg-icon name="play_arrow"></svg-icon> Play
+    </button>
+</script>
+<script type="text/x-template" id="play-button-template">
+    <button v-on:click="playItem()" class="btn btn-link play-button">
+        <svg-icon name="play_arrow"></svg-icon>
+    </button>
+</script>
 <!-- vlm modal Template -->
 <script type="text/x-template" id="vlm-modal-template">
     <div>
@@ -113,8 +380,8 @@
             <!-- Modal content-->
             <div class="modal-content">
                 <div class="modal-header">
-                    <button type="button" class="close" data-dismiss="modal">×</button>
                     <h4 class="modal-title">Write your VLM commands here (seperated by new line)</h4>
+                    <button type="button" class="close" data-dismiss="modal">×</button>
                 </div>
                 <div class="modal-body">
                     <textarea id="vlmCommand"></textarea>
@@ -123,59 +390,180 @@
                     </div>
                 </div>
                 <div class="modal-footer">
-                    <button type="button" id="vlmButton" class="btn btn-default">Submit</button>
+                    <button type="button" v-on:click="executeVLM()" id="vlmButton" class="btn btn-default">Submit</button>
                     <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
                 </div>
             </div>
         </div>
     </div>
 </script>
-<!-- sideNav Template -->
-<script type="text/x-template" id="sidenav-template">
-    <div>
-        <div id="sideNav" class="sideNav">
-            <a href="javascript:void(0)" class="closebtn" id="closeNavButton">
-                Menu
-            </a>
-            <a href="#" data-toggle="modal" data-target="#fileModal">
-                Open File
-            </a>
-            <a href="#">
-                Local Network
-            </a>
-            <a href="#">
-                Podcast
-            </a>
-            <a href="#" data-toggle="modal" data-target="#streamManagerModal">
-                Manage Streams
-            </a>
-            <a href="#" data-toggle="modal" data-target="#trackSyncModal">
-                Track Synchronization
-            </a>
-            <a href="#" data-toggle="modal" data-target="#vlmModal">
-                VLM Batch Commands
-            </a>
-        </div>
-        <span id="openNavButton">
-            <svg-icon name="menu"></svg-icon>
-        </span>
-    </div>
-</script>
-<!-- file modal Template -->
-<script type="text/x-template" id="file-modal-template">
-    <div>
-        <div class="modal-dialog">
-            <!-- Modal content-->
-            <div class="modal-content">
-                <div class="modal-header">
-                    <button type="button" class="close" data-dismiss="modal">×</button>
-                    <h4 class="modal-title">Select your file</h4>
+<script type="text/x-template" id="videos-template">
+    <div class="videos-view container-fluid">
+        <!-- <div v-if="displayMode === 'list'" class="row">
+            <album-item-list v-for="(item, index) in items" :item="item" :key="index"></album-item-list>
+        </div> -->
+        <!-- <h5 class="separator-title d-flex align-items-center">
+            <span>Videos</span>
+        </h5> -->
+        <div id="collapse-group-grid" class="position-relative collapse-container" v-if="layout.itemLayout === 'grid'">
+            <!-- <div class="carousel-navigation d-flex align-items-center">
+                <svg-icon class="muted-opacity" name="keyboard_arrow_left"></svg-icon>
+                <svg-icon name="keyboard_arrow_right"></svg-icon>
+            </div> -->
+            <template v-for="(item, index) in videos">
+                <video-item-grid :key="index" data-toggle="collapse" v-bind:data-target="'#collapse-' + item.id" aria-expanded="false" aria-controls="collapse" class="col-12 col-sm-6 col-md-4 col-lg-3 collapse-item" :item="item">
+                </video-item-grid><div class="collapse collapse-detail" v-bind:id="'collapse-' + item.id" data-parent="#collapse-group-grid">
+                    <div class="d-flex ml-2">
+                        <button data-toggle="collapse" v-bind:data-target="'#collapse-' + item.id" aria-expanded="false" aria-controls="collapse" class="close">
+                            <svg-icon name="add"></svg-icon>
+                        </button>
+                        <div>
+                            <img width="260" class="embed-responsive-item" :src="item.src">
+                            <div class="mt-2 d-flex">
+                                <play-button-secondary :item="item"></play-button-secondary>
+                                <enqueue-button class="ml-2" :item="item"></enqueue-button>
+                            </div>
+                        </div>
+                        <div class="ml-2">
+                            <h4 class="title mb-0">{{ item.title }}</h4>
+                            <span class="caption">{{ item.duration | formatDuration }}</span>
+                            <div class="subtitle"><span class="font-weight-bold">File name:</span>{{ item.filename }}</div>
+                            <div class="subtitle"><span class="font-weight-bold">Path:</span>{{ item.mrl }}</div>
+                        </div>
+                    </div>
                 </div>
-                <div class="modal-body" id="file-tree">
+            </template>
+        </div>
+        <div id="collapse-group-list" class="position-relative collapse-container" v-if="layout.itemLayout === 'list'">
+            <!-- <div class="carousel-navigation d-flex align-items-center">
+                <svg-icon class="muted-opacity" name="keyboard_arrow_left"></svg-icon>
+                <svg-icon name="keyboard_arrow_right"></svg-icon>
+            </div> -->
+            <div class="tracks-view container-fluid list-table-view">
+                <!-- <div v-if="displayMode === 'list'" class="row">
+                    <artist-item-list v-for="item in items" :item="item"></artist-item-list>
+                </div> -->
+                <div class="d-flex list-header row">
+                    <span class="col-2 text-muted track-img">
+                        <svg-icon name="Icon-Vinyl"></svg-icon>
+                    </span>
+                    <span class="col-6 text-muted">Title</span>
+                    <span class="col-2 text-muted text-center">
+                        <svg-icon name="Icon-Time"></svg-icon>
+                    </span>
                 </div>
-                <div class="modal-footer">
+                <template v-for="(item, index) in videos">
+                    <video-item-list :key="index" data-toggle="collapse" v-bind:data-target="'#collapse-' + item.id" aria-expanded="false" aria-controls="collapse" :item="item">
+                    </video-item-list><div class="collapse collapse-detail" v-bind:id="'collapse-' + item.id" data-parent="#collapse-group-list">
+                        <div class="d-flex ml-2">
+                            <button data-toggle="collapse" v-bind:data-target="'#collapse-' + item.id" aria-expanded="false" aria-controls="collapse" class="close">
+                                <svg-icon name="add"></svg-icon>
+                            </button>
+                            <div>
+                                <img width="260" class="embed-responsive-item" :src="item.src">
+                                <div class="mt-2 d-flex">
+                                    <play-button-secondary :item="item"></play-button-secondary>
+                                    <enqueue-button class="ml-2" :item="item"></enqueue-button>
+                                </div>
+                            </div>
+                            <div class="ml-2">
+                                <h4 class="title mb-0">{{ item.title }}</h4>
+                                <span class="caption">{{ item.duration | formatDuration }}</span>
+                                <div class="subtitle"><span class="font-weight-bold">File name:</span>{{ item.filename }}</div>
+                                <div class="subtitle"><span class="font-weight-bold">Path:</span>{{ item.mrl }}</div>
+                            </div>
+                        </div>
+                    </div>
+                </template>
+
+            </div>
+        </div>
+        <div class="row position-relative" v-if="layout.itemLayout === 'poster'">
+            <!-- <div class="carousel-navigation d-flex align-items-center">
+                <svg-icon class="muted-opacity" name="keyboard_arrow_left"></svg-icon>
+                <svg-icon name="keyboard_arrow_right"></svg-icon>
+            </div> -->
+            <video-item-poster class="col-12 col-sm-6 col-md-4 col-lg-2" v-for="(item, index) in videos" :item="item" :key="index"></video-item-poster>
+        </div>
+    </div>
+</script>
+<script type="text/x-template" id="video-item-list-template">
+    <div class="row align-items-center list-table-item">
+      <div class="d-flex justify-content-between col-2">
+        <div class="item-img img-16by9 d-flex" :style="{'background-image': `url(${item.src})`}">
+          <play-button class="item-primary-action item-action m-auto" :item="item"></play-button>
+        </div>
+      </div>
+      <div class="col-6">
+        <span>{{ item.title }}</span>
+      </div>
+      <div class="col-2 text-center">{{ item.duration | formatDuration }}</div>
+      <div class="text-muted col-2">
+        <div class="d-flex justify-content-between">
+          <div class="item-action">
+            <svg-icon name="more-horizontal"></svg-icon>
+          </div>
+        </div>
+      </div>
+    </div>
+</script>
+<script type="text/x-template" id="video-item-grid-template">
+    <div class="video-item grid-item video-item-grid-template">
+        <div class="item-img embed-responsive embed-responsive-16by10">
+            <img class="embed-responsive-item" :src="item.src">
+            <div class="overlay">
+                <play-button :item="item" class="item-primary-action"></play-button>
+                <div class="item-secondary-action">
+                    <div class="icon-wrapper">
+                        <svg-icon name="more-horizontal"></svg-icon>
+                    </div>
+                </div>
+            </div>
+            <div v-if="item.quality" class="quality">
+                <span class="quality-tag">{{ item.quality }}</span>
+            </div>
+            <div v-if="item.user" class="user">
+                <img src="">
+            </div>
+            <div v-if="item.progress" class="item-progress-bar-container">
+                <div class="item-progress-bar" :style="{width: item.progress}"></div>
+            </div>
+        </div>
+        <div class="item-body px-2">
+            <span class="d-flex align-items-center justify-content-between">
+                <router-link to="/album" class="item-title text-truncate">
+                    <span>{{ item.title }}</span>
+                </router-link>
+                <span class="item-subtitle">{{ item.duration | formatDuration }}</span>
+            </span>
+            <router-link to="/" class="item-subtitle text-truncate text-left">{{ item.subtitle }}</router-link>
+        </div>
+    </div>
+</script>
+<script type="text/x-template" id="video-item-poster-template">
+    <div class="video-item grid-item poster-item video-item-poster-template">
+        <div class="item-img embed-responsive embed-responsive-4by6">
+            <img class="embed-responsive-item" :src="item.src">
+            <div class="overlay">
+                <play-button class="item-primary-action" :item="item"></play-button>
+                <div class="item-secondary-action">
+                    <div class="icon-wrapper">
+                        <svg-icon name="more-horizontal"></svg-icon>
+                    </div>
                 </div>
             </div>
+            <div v-if="item.progress" class="item-progress-bar-container">
+                <div class="item-progress-bar" :style="{width: item.progress}"></div>
+            </div>
+        </div>
+        <div class="item-body px-2">
+            <span class="d-flex align-items-center justify-content-between">
+                <router-link to="/album" class="item-title text-truncate">
+                    <span>{{ item.title }}</span>
+                </router-link>
+                <span class="item-subtitle">{{ item.duration | formatDuration }}</span>
+            </span>
+            <router-link to="/" class="item-subtitle text-truncate text-left">{{ item.subtitle }}</router-link>
         </div>
     </div>
 </script>
@@ -186,8 +574,8 @@
             <!-- Modal content-->
             <div class="modal-content">
                 <div class="modal-header">
-                    <button type="button" class="close" data-dismiss="modal">×</button>
                     <h4 class="modal-title">Set Track Synchronization</h4>
+                    <button type="button" class="close" data-dismiss="modal">×</button>
                 </div>
                 <div class="modal-body" id="">
                     Playback Rate
@@ -208,6 +596,77 @@
         </div>
     </div>
 </script>
+<script type="text/x-template" id="item-layout-button-template">
+    <button v-on:click="toggleItemLayout()" class="btn btn-circle active">
+        <svg-icon v-if="layout && layout.itemLayout === 'list'" name="format_list_bulleted"></svg-icon>
+        <svg-icon v-if="layout && layout.itemLayout === 'grid'" name="grid_view"></svg-icon>
+    </button>
+</script>
+<script type="text/x-template" id="grid-item-template">
+    <div class="grid-item discover-services-item-grid-template col-12 col-sm-6 col-md-4 col-lg-3 col-xl-2">
+        <div :class="item.additionalClass" class="item-img embed-responsive embed-responsive-1by1 img-contain">
+            <img v-if="item.src" class="embed-responsive-item p-4" :src="item.src">
+            <div v-else="item.icon" class="embed-responsive-item" :class="item.additionalItemClass">
+                <svg-icon :name="item.icon"></svg-icon>
+            </div>
+        </div>
+        <div class="item-body">
+            <router-link :to="item.titleRoute || '/album'" class="item-title text-truncate">{{ item.title }}</router-link>
+            <router-link v-if="item.subtitle" :to="item.subtitleRoute || '/artist'" class="item-subtitle text-truncate">{{ item.subtitle }}</router-link>
+        </div>
+    </div>
+</script>
+<script type="text/x-template" id="artists-template">
+    <div class="artits-view container-fluid">
+        <!-- <div v-if="displayMode === 'list'" class="row">
+            <artist-item-list v-for="item in items" :item="item"></artist-item-list>
+        </div> -->
+        <div class="row" v-if="layout.itemLayout === 'grid'">
+            <artist-item-grid v-for="(item, index) in music.artists" :item="item" :key="index"></artist-item-grid>
+        </div>
+    </div>
+</script>
+<script type="text/x-template" id="artist-item-grid-template">
+    <router-link :to="'/music/artists/' + item.id" class="item-people artist-item grid-item artist-item-grid-template col-12 col-sm-6 col-md-4 col-lg-3 col-xl-2">
+        <div class="item-img embed-responsive embed-responsive-1by1">
+            <img v-if="item.src" class="embed-responsive-item" :src="item.src">
+            <svg-icon v-if="!item.src" name="placeholder-artist" class="placeholder-artist-icon"></svg-icon>
+            <div class="overlay">
+                <play-button class="item-primary-action"></play-button>
+            </div>
+        </div>
+        <div class="item-body">
+            <router-link to="" class="item-title text-truncate">{{ item.name }}</router-link>
+            <span class="item-subtitle text-truncate">{{ item.nbTracks }} songs</span>
+        </div>
+    </router-link>
+</script>
+<script type="text/x-template" id="artists-detail-template">
+    <div class="artits-detail-view container-fluid">
+        <div class="row">
+            <div class="col-2 artists-sidebar pt-2">
+                <h5 class="font-weight-normal">Artists</h5>
+                <ul class="list-unstyled h-100">
+                    <li v-for="(artist, index) in music.artists" class="artist-list-item">
+                        <router-link :to="'/music/artists/' + artist.id" exact-active-class="active">
+                            <img v-if="artist.src" class="embed-responsive-item artist-img" :src="artist.src">
+                            <svg-icon v-if="!artist.src" name="placeholder-artist" class="placeholder-artist-icon artist-img"></svg-icon>
+                            <span class="flex-1">{{ artist.name }}</span>
+                        </router-link>
+                    </li>
+                </ul>
+            </div>
+            <div class="col-10 pt-4">
+                <albums :artistId="$route.params.id"></albums>
+            </div>
+        </div>
+    </div>
+</script>
+<script type="text/x-template" id="enqueue-button-template">
+    <button v-on:click="enqueueItem()" class="btn btn-sm enqueue-button">
+        <svg-icon name="playlist_add"></svg-icon> Enqueue
+    </button>
+</script>
 <script type="text/x-template" id="svg-icon">
     <svg version="1.1" :class="clazz" :viewBox="box" v-html="path" :style="style"></svg>
 </script>
@@ -218,8 +677,8 @@
             <!-- Modal content-->
             <div class="modal-content">
                 <div class="modal-header">
-                    <button type="button" class="close" data-dismiss="modal">×</button>
                     <h4 class="modal-title">Stream Manager</h4>
+                    <button type="button" class="close" data-dismiss="modal">×</button>
                 </div>
                 <div class="modal-body">
                 </div>
@@ -229,4 +688,427 @@
         </div>
     </div>
 </script>
+<script type="text/x-template" id="albums-template">
+    <div class="albums-view container-fluid">
+        <div v-if="layout.itemLayout === 'list'" class="row">
+            <!-- <album-item-list v-for="(item, index) in music.albums" :item="item" :key="index"></album-item-list> -->
+        </div>
+        <div id="album-collapse-group" class="position-relative collapse-container album-collapse-container" v-if="layout.itemLayout === 'grid'">
+            <template v-for="(item, index) in getAlbums()">
+                <album-item-grid :key="index" data-toggle="collapse" v-bind:data-target="'#collapse-album-' + item.id" aria-expanded="false" aria-controls="collapse" :item="item" class="col-12 col-sm-6 col-md-4 col-lg-3 col-xl-2 collapse-item"></album-item-grid><div class="collapse album-collapse-detail collapse-detail" v-bind:id="'collapse-album-' + item.id" data-parent="#album-collapse-group">
+                    <div class="d-flex ml-2">
+                        <!-- <button data-toggle="collapse" v-bind:data-target="'#collapse-album-' + item.id" aria-expanded="false" aria-controls="collapse" class="close">
+                            <svg-icon name="add"></svg-icon>
+                        </button> -->
+                        <div>
+                            <img v-if="item.src" width="171" height="171" class="embed-responsive-item item-img" :src="item.src">
+                            <svg-icon v-if="!item.src" name="placeholder-album" class="placeholder-album-icon item-img"></svg-icon>
+                            <div class="mt-2 d-flex">
+                                <play-button-secondary></play-button-secondary>
+                                <enqueue-button class="ml-2" :item="item"></enqueue-button>
+                            </div>
+                        </div>
+                        <div class="ml-2 w-100">
+                            <button data-toggle="collapse" v-bind:data-target="'#collapse-album-' + item.id" aria-expanded="false" aria-controls="collapse" class="close">
+                                <svg-icon name="add"></svg-icon>
+                            </button>
+                            <h4 class="title mb-0">{{ item.title }}</h4>
+                            <span class="caption">{{ item.duration | formatDuration }}</span>
+                            <table class="table table-hover table-item">
+                                <thead>
+                                  <tr>
+                                    <th scope="col">#</th>
+                                    <th scope="col">Title</th>
+                                    <th scope="col"></th>
+                                    <th scope="col"><svg-icon name="Icon-Time"></svg-icon></th>
+                                  </tr>
+                                </thead>
+                                <tbody>
+                                    <tr v-for="(track, index) in music.albumTracks[item.id]">
+                                        <td>
+                                            {{ index + 1 }}
+                                        </td>
+                                        <td>
+                                            {{ track.title }}
+                                        </td>
+                                        <td class="text-muted">
+                                          <div class="d-flex justify-content-between">
+                                            <div class="item-action">
+                                              <svg-icon name="more-horizontal"></svg-icon>
+                                            </div>
+                                          </div>
+                                        </td>
+                                        <td>
+                                            {{ track.duration | formatDuration }}
+                                        </td>
+                                      </tr>
+                                </tbody>
+                            </table>
+                        </div>
+                    </div>
+                </div>
+            </template>
+        </div>
+    </div>
+</script>
+<script type="text/x-template" id="album-item-grid-template">
+    <div v-on:click="toggleAlbumTracks(item.id)" class="album-item grid-item album-item-grid-template">
+        <div class="item-img embed-responsive embed-responsive-1by1">
+            <img v-if="item.src" class="embed-responsive-item" :src="item.src">
+            <svg-icon v-if="!item.src" name="placeholder-album" class="placeholder-album-icon"></svg-icon>
+            <div class="overlay">
+                <play-button class="item-primary-action"></play-button>
+                <div class="item-secondary-action">
+                    <div class="icon-wrapper">
+                        <svg-icon name="more-horizontal"></svg-icon>
+                    </div>
+                </div>
+            </div>
+        </div>
+        <div class="item-body">
+            <router-link to="" class="item-title text-truncate">{{ item.title }}</router-link>
+            <router-link to="" class="item-subtitle text-truncate">{{ item.artist }}</router-link>
+        </div>
+    </div>
+</script>
+<script type="text/x-template" id="album-item-list-template">
+    <div class="album-item list-item album-item-list-template col-12 col-sm-6 col-md-6 col-lg-4">
+        <div class="item-img item-border-radius">
+            <img :src="item.src">
+            <div class="overlay">
+                <play-button class="item-primary-action item-action"></play-button>
+            </div>
+        </div>
+        <div class="item-body">
+            <router-link to="/album" class="item-title text-truncate">{{ item.title }}</router-link>
+            <router-link to="/artist" class="item-subtitle text-truncate">{{ item.artist }}</router-link>
+            <div class="item-secondary-action">
+                <div v-on:click="toggleLiked()">
+                    <div v-if="!liked" class="icon-wrapper item-action">
+                        <svg-icon name="favorite_outline"></svg-icon>
+                    </div>
+                    <div v-if="liked" class="icon-wrapper item-action active">
+                        <svg-icon name="favorite"></svg-icon>
+                    </div>
+                </div>
+                <div class="icon-wrapper item-action">
+                    <svg-icon name="more-horizontal"></svg-icon>
+                </div>
+            </div>
+        </div>
+    </div>
+</script>
+<script type="text/x-template" id="music-view-template">
+    <div class="music-view h-100">
+        <div class="fixed-top secondary-navbar">
+            <div class="container-fluid d-flex h-100 position-relative show collapse justify-content-between align-items-center">
+                <div class="d-flex align-items-center flex-1 justify-content-start">
+                    <item-layout-button></item-layout-button>
+                    <button v-on:click="" class="btn btn-circle ml-2"><svg-icon name="sort"></svg-icon></button>
+                </div>
+                <div class="d-flex align-items-center justify-content-center">
+                    <router-link to="/music/albums" exact-active-class="active" class="btn btn-link">Albums</router-link>
+                    <router-link to="/music/artists" active-class="active" class="btn btn-link">Artists</router-link>
+                    <router-link to="/music/genres" exact-active-class="active" class="btn btn-link">Genres</router-link>
+                    <router-link to="/music/tracks" exact-active-class="active" class="btn btn-link">Tracks</router-link>
+                    <router-link to="/music/playlists" exact-active-class="active" class="btn btn-link">Playlists</router-link>
+                </div>
+                <div class="d-flex flex-1 justify-content-end h-100">
+                    <a data-toggle="collapse" href="#" data-target=".collapse" role="button" class="text-white toggler px-2 d-flex align-items-center">
+                        <svg-icon name="playlist_play"></svg-icon>
+                    </a>
+                </div>
+            </div>
+        </div>
+        <div class="container-fluid px-0 h-100" :class="'push'">
+            <div class="row collapse show no-gutters d-flex h-100 position-relative">
+                <div class="col container-view">
+                    <transition name="fade" mode="out-in">
+                        <router-view></router-view>
+                    </transition>
+                </div>
+                <div class="col-3 p-0 h-100 w-sidebar navbar-collapse collapse d-flex sidebar show">
+                    <!-- fixed sidebar -->
+                    <div class="navbar-dark align-self-start w-sidebar w-sidebar-content">
+                        <playlist></playlist>
+                    </div>
+                </div>
+            </div>
+        </div>
+    </div>
+</script>
+<script type="text/x-template" id="music-playlists-view-template">
+    <div class="music-playlists-view pt-4">
+        <playlists></playlists>
+    </div>
+</script>
+<script type="text/x-template" id="music-genres-view-template">
+    <div class="music-genres-view pt-4">
+        <genres></genres>
+    </div>
+</script>
+<script type="text/x-template" id="music-tracks-view-template">
+    <div class="music-tracks-view pt-4">
+        <tracks></tracks>
+    </div>
+</script>
+<script type="text/x-template" id="music-artists-detail-view-template">
+    <div class="music-artists-detail-view">
+        <artists-detail></artists-detail>
+    </div>
+</script>
+<script type="text/x-template" id="music-artists-view-template">
+    <div class="music-artists-view pt-4">
+        <artists></artists>
+    </div>
+</script>
+<script type="text/x-template" id="music-albums-view-template">
+    <div class="music-albums-view pt-4">
+        <albums></albums>
+    </div>
+</script>
+<script type="text/x-template" id="video-playlists-view-template">
+    <div class="video-playlists-view"></div>
+</script>
+<script type="text/x-template" id="video-all-view-template">
+    <div class="video-all-view">
+        <!-- <continue-watching></continue-watching> -->
+        <videos></videos>
+    </div>
+</script>
+<script type="text/x-template" id="video-tvshows-view-template">
+    <div class="video-tvshows-view"></div>
+</script>
+<script type="text/x-template" id="video-view-template">
+    <div class="video-view h-100 pt-4">
+        <div class="fixed-top secondary-navbar">
+            <div class="container-fluid d-flex h-100 position-relative show collapse justify-content-between align-items-center">
+                <div class="d-flex align-items-center flex-1 justify-content-start">
+                    <item-layout-button></item-layout-button>
+                    <button v-on:click="" class="btn btn-circle ml-2"><svg-icon name="sort"></svg-icon></button>
+                </div>
+                <div class="d-flex align-items-center justify-content-center">
+                    <router-link to="/video/all" exact-active-class="active" class="btn btn-link">All</router-link>
+                    <router-link to="/video/movies" exact-active-class="active" class="btn btn-link">Movies</router-link>
+                    <router-link to="/video/tvshows" exact-active-class="active" class="btn btn-link">Tv Shows</router-link>
+                    <router-link to="/video/playlists" exact-active-class="active" class="btn btn-link">Playlists</router-link>
+                </div>
+                <div class="d-flex flex-1 justify-content-end h-100">
+                    <a data-toggle="collapse" href="#" data-target=".collapse" role="button" class="text-white toggler px-2 d-flex align-items-center">
+                        <svg-icon name="playlist_play"></svg-icon>
+                    </a>
+                </div>
+            </div>
+        </div>
+        <div class="container-fluid px-0 h-100" :class="'push'">
+            <div class="row collapse show no-gutters d-flex h-100 position-relative">
+                <div class="col container-view">
+                    <transition name="fade" mode="out-in">
+                        <router-view></router-view>
+                    </transition>
+                </div>
+                <div class="col-3 p-0 h-100 w-sidebar navbar-collapse collapse d-flex sidebar show">
+                    <!-- fixed sidebar -->
+                    <div class="navbar-dark align-self-start w-sidebar w-sidebar-content">
+                        <playlist></playlist>
+                    </div>
+                </div>
+            </div>
+        </div>
+    </div>
+</script>
+<script type="text/x-template" id="video-movies-view-template">
+    <div class="video-movies-view"></div>
+</script>
+<script type="text/x-template" id="discover-home-view-template">
+    <div class="discover-home-view">
+    </div>
+</script>
+<script type="text/x-template" id="discover-view-template">
+    <div class="discover-view h-100 pt-4">
+        <div class="fixed-top secondary-navbar">
+            <div class="container-fluid d-flex h-100 position-relative show collapse justify-content-between align-items-center">
+                <div class="d-flex align-items-center flex-1 justify-content-start">
+                    <button v-on:click="" class="btn btn-circle"><svg-icon name="format_list_bulleted"></svg-icon></button>
+                    <button v-on:click="" class="btn btn-circle ml-2"><svg-icon name="sort"></svg-icon></button>
+                </div>
+                <div class="d-flex align-items-center justify-content-center">
+                    <router-link to="/discover/home" active-class="active" class="btn btn-link">Home</router-link>
+                    <router-link to="/discover/services" active-class="active" class="btn btn-link">Services</router-link>
+                    <router-link to="/discover/url" active-class="active" class="btn btn-link">URL</router-link>
+                </div>
+                <div class="d-flex flex-1 justify-content-end h-100">
+                    <a data-toggle="collapse" href="#" data-target=".collapse" role="button" class="text-white toggler px-2 d-flex align-items-center">
+                        <svg-icon name="playlist_play"></svg-icon>
+                    </a>
+                </div>
+            </div>
+        </div>
+        <div class="container-fluid px-0 h-100" :class="'push'">
+            <div class="row collapse show no-gutters d-flex h-100 position-relative">
+                <div class="col container-view">
+                    <transition name="fade" mode="out-in">
+                        <router-view></router-view>
+                    </transition>
+                </div>
+                <div class="col-3 p-0 h-100 w-sidebar navbar-collapse collapse d-flex sidebar show">
+                    <!-- fixed sidebar -->
+                    <div class="navbar-dark align-self-start w-sidebar w-sidebar-content">
+                        <playlist></playlist>
+                    </div>
+                </div>
+            </div>
+        </div>
+    </div>
+</script>
+<script type="text/x-template" id="discover-url-view-template">
+    <div class="discover-url-view">
+    </div>
+</script>
+<script type="text/x-template" id="discover-services-view-template">
+    <div class="discover-services-view">
+        <div class="row">
+            <grid-item :item="addService"></grid-item>
+            <template v-for="service in mainServices">
+                <grid-item :item="service"></grid-item>
+            </template>
+        </div>
+        <div class="row">
+            <template v-for="service in secondaryServices">
+                <grid-item :item="service"></grid-item>
+            </template>
+        </div>
+    </div>
+</script>
+<script type="text/x-template" id="discover-services-tv-view-template">
+    <div class="discover-services-tv-view container-fluid">
+        <div v-if="displayMode === 'list'" class="row">
+            <grid-list v-for="(item, index) in items" :item="item" :key="index"></grid-list>
+        </div>
+        <div class="row" v-if="displayMode === 'grid'">
+            <grid-item v-for="(item, index) in items" :item="item" :key="index"></grid-item>
+        </div>
+    </div>
+</script>
+<script type="text/x-template" id="network-view-template">
+    <div class="network-view h-100 pt-4">
+        <div class="fixed-top secondary-navbar">
+            <div class="container-fluid d-flex h-100 position-relative show collapse justify-content-between align-items-center">
+                <div class="d-flex align-items-center flex-1 justify-content-start">
+                    <button v-on:click="" class="btn btn-circle"><svg-icon name="format_list_bulleted"></svg-icon></button>
+                    <button v-on:click="" class="btn btn-circle ml-2"><svg-icon name="sort"></svg-icon></button>
+                </div>
+                <div class="d-flex align-items-center justify-content-center"></div>
+                <div class="d-flex flex-1 justify-content-end h-100">
+                    <a data-toggle="collapse" href="#" data-target=".collapse" role="button" class="text-white toggler px-2 d-flex align-items-center">
+                        <svg-icon name="playlist_play"></svg-icon>
+                    </a>
+                </div>
+            </div>
+        </div>
+        <div class="container-fluid px-0 h-100" :class="'push'">
+            <div class="row collapse show no-gutters d-flex h-100 position-relative">
+                <div class="col container-view">
+
+                </div>
+                <div class="col-3 p-0 h-100 w-sidebar navbar-collapse collapse d-flex sidebar show">
+                    <!-- fixed sidebar -->
+                    <div class="navbar-dark align-self-start w-sidebar w-sidebar-content">
+                        <playlist></playlist>
+                    </div>
+                </div>
+            </div>
+        </div>
+    </div>
+</script>
+<script type="text/x-template" id="watch-view-template">
+    <div class="watch-view h-100">
+        <div>
+            <div class="container-fluid fixed-top primary-navbar">
+                <div class="d-flex h-100 position-relative show collapse justify-content-between align-items-center">
+                    <div class="d-flex align-items-center flex-1 justify-content-start">
+                        <router-link to="/">
+                            <button class="btn btn-circle">
+                                <svg-icon name="keyboard_arrow_left"></svg-icon>
+                            </button>
+                        </router-link>
+                    </div>
+                    <div class="d-flex align-items-center justify-content-center"></div>
+                    <div class="d-flex flex-1 justify-content-end h-100">
+                        <a data-toggle="collapse" href="#" data-target=".collapse" role="button" class="text-white toggler px-2 d-flex align-items-center">
+                            <svg-icon name="playlist_play"></svg-icon>
+                        </a>
+                    </div>
+                </div>
+            </div>
+        </div>
+        <div class="container-fluid px-0" :class="'overlay'">
+            <div class="row collapse show no-gutters d-flex h-100 position-relative">
+                <div class="col">
+                    <media-player></media-player>
+                </div>
+                <div class="col-3 p-0 h-100 w-sidebar navbar-collapse collapse d-flex sidebar show">
+                    <!-- fixed sidebar -->
+                    <div class="navbar-dark align-self-start w-sidebar w-sidebar-content">
+                        <playlist></playlist>
+                    </div>
+                </div>
+            </div>
+        </div>
+    </div>
+</script>
+<script type="text/x-template" id="browse-view-template">
+    <div class="browse-view h-100" :class="{ 'media-player-opened': audioPlayerOpened }">
+        <div class="container-fluid fixed-top primary-navbar d-flex align-content-center align-items-center text-center justify-content-center">
+            <div class="d-flex align-items-center flex-1 justify-content-start">
+                <img width="23" height="26" src="http://images.videolan.org/images/VLC-IconSmall.png">
+                <span class="ml-2">VLC</span>
+            </div>
+            <ul class="nav d-flex align-items-center justify-content-center">
+                <li class="nav-item">
+                    <router-link title="Video" active-class="active" to="/video" class="btn btn-icon btn-link d-flex flex-column">
+                        <svg-icon name="local_movies_outline"></svg-icon>
+                        <span>Video</span>
+                    </router-link>
+                </li>
+                <li class="nav-item">
+                    <router-link title="Music" active-class="active" to="/music" class="btn btn-icon btn-link d-flex flex-column">
+                        <svg-icon name="audiotrack_outline"></svg-icon>
+                        <span>Music</span>
+                    </router-link>
+                </li>
+                <li class="nav-item">
+                    <router-link title="Network" active-class="active" to="/network" class="btn btn-icon btn-link d-flex flex-column">
+                        <svg-icon name="wifi"></svg-icon>
+                        <span>Browse</span>
+                    </router-link>
+                </li>
+                <li class="nav-item">
+                    <router-link title="Discover" active-class="active" to="/discover" class="btn btn-icon btn-link d-flex flex-column">
+                        <svg-icon name="explore"></svg-icon>
+                        <span>Discover</span>
+                    </router-link>
+                </li>
+            </ul>
+            <div class="d-flex flex-1 justify-content-end h-100">
+                <button v-on:click="" class="btn-circle mr-2"><svg-icon name="search"></svg-icon></button>
+                <button v-on:click="" class="btn-circle"><svg-icon name="more-horizontal"></svg-icon></button>
+            </div>
+        </div>
+        <transition name="fade" mode="out-in">
+            <router-view></router-view>
+        </transition>
+        <transition name="slide-fade">
+            <media-player
+                v-if="audioPlayerOpened"
+                canPlay="canPlay"
+                ended="ended"
+                src="">
+            </media-player>
+        </transition>
+    </div>
+</script>
+<script type="text/x-template" id="main-view-template">
+    <router-view></router-view>
+</script>
 </html>
diff --git a/share/lua/http/src/components/albums/album-item/album-item-grid.component.html b/share/lua/http/src/components/albums/album-item/album-item-grid.component.html
new file mode 100644
index 0000000000..6dd189c065
--- /dev/null
+++ b/share/lua/http/src/components/albums/album-item/album-item-grid.component.html
@@ -0,0 +1,20 @@
+<script type="text/x-template" id="album-item-grid-template">
+    <div v-on:click="toggleAlbumTracks(item.id)" class="album-item grid-item album-item-grid-template">
+        <div class="item-img embed-responsive embed-responsive-1by1">
+            <img v-if="item.src" class="embed-responsive-item" :src="item.src">
+            <svg-icon v-if="!item.src" name="placeholder-album" class="placeholder-album-icon"></svg-icon>
+            <div class="overlay">
+                <play-button class="item-primary-action"></play-button>
+                <div class="item-secondary-action">
+                    <div class="icon-wrapper">
+                        <svg-icon name="more-horizontal"></svg-icon>
+                    </div>
+                </div>
+            </div>
+        </div>
+        <div class="item-body">
+            <router-link to="" class="item-title text-truncate">{{ item.title }}</router-link>
+            <router-link to="" class="item-subtitle text-truncate">{{ item.artist }}</router-link>
+        </div>
+    </div>
+</script>
diff --git a/share/lua/http/src/components/albums/album-item/album-item-grid.component.js b/share/lua/http/src/components/albums/album-item/album-item-grid.component.js
new file mode 100644
index 0000000000..13076deeaa
--- /dev/null
+++ b/share/lua/http/src/components/albums/album-item/album-item-grid.component.js
@@ -0,0 +1,20 @@
+Vue.component('album-item-grid', {
+    template: '#album-item-grid-template',
+    props: ['item'],
+    data: function() {
+        return {
+            liked: this.item.liked
+        }
+    },
+    computed: {
+        ...Vuex.mapState({
+            music: state => state.music
+        }),
+    },
+    methods: {
+        toggleAlbumTracks(id) {
+            this.$store.dispatch('music/fetchAlbumTracks', id);
+        }
+    },
+    created() { }
+});
diff --git a/share/lua/http/src/components/albums/album-item/album-item-list.component.html b/share/lua/http/src/components/albums/album-item/album-item-list.component.html
new file mode 100644
index 0000000000..063644ebf5
--- /dev/null
+++ b/share/lua/http/src/components/albums/album-item/album-item-list.component.html
@@ -0,0 +1,27 @@
+<script type="text/x-template" id="album-item-list-template">
+    <div class="album-item list-item album-item-list-template col-12 col-sm-6 col-md-6 col-lg-4">
+        <div class="item-img item-border-radius">
+            <img :src="item.src">
+            <div class="overlay">
+                <play-button class="item-primary-action item-action"></play-button>
+            </div>
+        </div>
+        <div class="item-body">
+            <router-link to="/album" class="item-title text-truncate">{{ item.title }}</router-link>
+            <router-link to="/artist" class="item-subtitle text-truncate">{{ item.artist }}</router-link>
+            <div class="item-secondary-action">
+                <div v-on:click="toggleLiked()">
+                    <div v-if="!liked" class="icon-wrapper item-action">
+                        <svg-icon name="favorite_outline"></svg-icon>
+                    </div>
+                    <div v-if="liked" class="icon-wrapper item-action active">
+                        <svg-icon name="favorite"></svg-icon>
+                    </div>
+                </div>
+                <div class="icon-wrapper item-action">
+                    <svg-icon name="more-horizontal"></svg-icon>
+                </div>
+            </div>
+        </div>
+    </div>
+</script>
diff --git a/share/lua/http/src/components/albums/album-item/album-item-list.component.js b/share/lua/http/src/components/albums/album-item/album-item-list.component.js
new file mode 100644
index 0000000000..7eca71f703
--- /dev/null
+++ b/share/lua/http/src/components/albums/album-item/album-item-list.component.js
@@ -0,0 +1,16 @@
+Vue.component('album-item-list', {
+    template: '#album-item-list-template',
+    props: ['item'],
+    data: function() {
+        return {
+            liked: this.item.liked
+        }
+    },
+    computed: { },
+    methods: {
+        toggleLiked() {
+            this.liked = !this.liked;
+        }
+    },
+    created() { }
+});
diff --git a/share/lua/http/src/components/albums/album-item/album-item.component.scss b/share/lua/http/src/components/albums/album-item/album-item.component.scss
new file mode 100644
index 0000000000..54bdbf8403
--- /dev/null
+++ b/share/lua/http/src/components/albums/album-item/album-item.component.scss
@@ -0,0 +1,17 @@
+.album-item-grid-template.grid-item {
+    margin-bottom: 16px;
+}
+
+.album-item-list-template.list-item {
+    margin-bottom: 16px;
+    .item-img {
+        width: 90px;
+    }
+}
+
+.svg-icon {
+    &.placeholder-album-icon {
+        width: 3.5rem;
+        height: 3.5rem;
+    }
+}
diff --git a/share/lua/http/src/components/albums/albums.component.html b/share/lua/http/src/components/albums/albums.component.html
new file mode 100644
index 0000000000..441485136f
--- /dev/null
+++ b/share/lua/http/src/components/albums/albums.component.html
@@ -0,0 +1,63 @@
+<script type="text/x-template" id="albums-template">
+    <div class="albums-view container-fluid">
+        <div v-if="layout.itemLayout === 'list'" class="row">
+            <!-- <album-item-list v-for="(item, index) in music.albums" :item="item" :key="index"></album-item-list> -->
+        </div>
+        <div id="album-collapse-group" class="position-relative collapse-container album-collapse-container" v-if="layout.itemLayout === 'grid'">
+            <template v-for="(item, index) in getAlbums()">
+                <album-item-grid :key="index" data-toggle="collapse" v-bind:data-target="'#collapse-album-' + item.id" aria-expanded="false" aria-controls="collapse" :item="item" class="col-12 col-sm-6 col-md-4 col-lg-3 col-xl-2 collapse-item"></album-item-grid><div class="collapse album-collapse-detail collapse-detail" v-bind:id="'collapse-album-' + item.id" data-parent="#album-collapse-group">
+                    <div class="d-flex ml-2">
+                        <!-- <button data-toggle="collapse" v-bind:data-target="'#collapse-album-' + item.id" aria-expanded="false" aria-controls="collapse" class="close">
+                            <svg-icon name="add"></svg-icon>
+                        </button> -->
+                        <div>
+                            <img v-if="item.src" width="171" height="171" class="embed-responsive-item item-img" :src="item.src">
+                            <svg-icon v-if="!item.src" name="placeholder-album" class="placeholder-album-icon item-img"></svg-icon>
+                            <div class="mt-2 d-flex">
+                                <play-button-secondary></play-button-secondary>
+                                <enqueue-button class="ml-2" :item="item"></enqueue-button>
+                            </div>
+                        </div>
+                        <div class="ml-2 w-100">
+                            <button data-toggle="collapse" v-bind:data-target="'#collapse-album-' + item.id" aria-expanded="false" aria-controls="collapse" class="close">
+                                <svg-icon name="add"></svg-icon>
+                            </button>
+                            <h4 class="title mb-0">{{ item.title }}</h4>
+                            <span class="caption">{{ item.duration | formatDuration }}</span>
+                            <table class="table table-hover table-item">
+                                <thead>
+                                  <tr>
+                                    <th scope="col">#</th>
+                                    <th scope="col">Title</th>
+                                    <th scope="col"></th>
+                                    <th scope="col"><svg-icon name="Icon-Time"></svg-icon></th>
+                                  </tr>
+                                </thead>
+                                <tbody>
+                                    <tr v-for="(track, index) in music.albumTracks[item.id]">
+                                        <td>
+                                            {{ index + 1 }}
+                                        </td>
+                                        <td>
+                                            {{ track.title }}
+                                        </td>
+                                        <td class="text-muted">
+                                          <div class="d-flex justify-content-between">
+                                            <div class="item-action">
+                                              <svg-icon name="more-horizontal"></svg-icon>
+                                            </div>
+                                          </div>
+                                        </td>
+                                        <td>
+                                            {{ track.duration | formatDuration }}
+                                        </td>
+                                      </tr>
+                                </tbody>
+                            </table>
+                        </div>
+                    </div>
+                </div>
+            </template>
+        </div>
+    </div>
+</script>
diff --git a/share/lua/http/src/components/albums/albums.component.js b/share/lua/http/src/components/albums/albums.component.js
new file mode 100644
index 0000000000..61e7b5c46e
--- /dev/null
+++ b/share/lua/http/src/components/albums/albums.component.js
@@ -0,0 +1,41 @@
+Vue.component('albums', {
+    template: '#albums-template',
+    props: ['artistId'],
+    watch: {
+        artistId: function(newVal, oldVal) {
+            if (newVal !== oldVal) {
+                this.fetchArtistAlbums(newVal);
+            }
+        }
+    },
+    data: function() {
+        return {
+            displayMode: 'grid'
+        }
+    },
+    computed: {
+        ...Vuex.mapState({
+            music: state => state.music,
+            layout: state => state.layout
+        }),
+    },
+    methods: {
+        fetchAlbums() {
+            this.$store.dispatch('music/fetchAlbums');
+        },
+        fetchArtistAlbums(artistId) {
+            this.$store.dispatch('music/fetchArtistAlbums', artistId);
+        },
+        getAlbums() {
+            return !this.$route.params.id ? this.music.albums : this.music.artistAlbums[this.$route.params.id];
+        }
+    },
+    created() {
+        const artistId = this.$route.params.id;
+        if (artistId) {
+            this.fetchArtistAlbums(artistId);
+        } else {
+            this.fetchAlbums();
+        }
+    }
+});
diff --git a/share/lua/http/src/components/albums/albums.component.scss b/share/lua/http/src/components/albums/albums.component.scss
new file mode 100644
index 0000000000..91dd0deeb7
--- /dev/null
+++ b/share/lua/http/src/components/albums/albums.component.scss
@@ -0,0 +1,23 @@
+.albums-view {
+    ul > li {
+        display: inline-block;
+        list-style: none;
+    }
+    .expander {
+        top: auto;
+        position: absolute;
+        visibility: hidden;
+        left: 0;
+        margin-top: 20px;
+        transition: height 0.35s ease;
+        width: 100%;
+    }
+}
+.album-collapse-container {
+    .album-collapse-detail {
+        .item-img {
+            width: 171px;
+            height: 171px;
+        }
+    }
+}
diff --git a/share/lua/http/src/components/artists/artist-item/artist-item-grid.component.html b/share/lua/http/src/components/artists/artist-item/artist-item-grid.component.html
new file mode 100644
index 0000000000..a927d53712
--- /dev/null
+++ b/share/lua/http/src/components/artists/artist-item/artist-item-grid.component.html
@@ -0,0 +1,15 @@
+<script type="text/x-template" id="artist-item-grid-template">
+    <router-link :to="'/music/artists/' + item.id" class="item-people artist-item grid-item artist-item-grid-template col-12 col-sm-6 col-md-4 col-lg-3 col-xl-2">
+        <div class="item-img embed-responsive embed-responsive-1by1">
+            <img v-if="item.src" class="embed-responsive-item" :src="item.src">
+            <svg-icon v-if="!item.src" name="placeholder-artist" class="placeholder-artist-icon"></svg-icon>
+            <div class="overlay">
+                <play-button class="item-primary-action"></play-button>
+            </div>
+        </div>
+        <div class="item-body">
+            <router-link to="" class="item-title text-truncate">{{ item.name }}</router-link>
+            <span class="item-subtitle text-truncate">{{ item.nbTracks }} songs</span>
+        </div>
+    </router-link>
+</script>
diff --git a/share/lua/http/src/components/artists/artist-item/artist-item-grid.component.js b/share/lua/http/src/components/artists/artist-item/artist-item-grid.component.js
new file mode 100644
index 0000000000..8fe01f3a69
--- /dev/null
+++ b/share/lua/http/src/components/artists/artist-item/artist-item-grid.component.js
@@ -0,0 +1,12 @@
+Vue.component('artist-item-grid', {
+    template: '#artist-item-grid-template',
+    props: ['item'],
+    data: function() {
+        return {
+            liked: this.item.liked
+        }
+    },
+    computed: { },
+    methods: { },
+    created() { }
+});
diff --git a/share/lua/http/src/components/artists/artist-item/artist-item-grid.component.scss b/share/lua/http/src/components/artists/artist-item/artist-item-grid.component.scss
new file mode 100644
index 0000000000..03226f6b5e
--- /dev/null
+++ b/share/lua/http/src/components/artists/artist-item/artist-item-grid.component.scss
@@ -0,0 +1,17 @@
+.artist-item-grid-template.grid-item {
+    margin-bottom: 16px;
+}
+
+.svg-icon {
+    &.placeholder-artist-icon {
+        width: 5rem;
+        height: 5rem;
+        margin-top: 1.8rem;
+    }
+}
+
+.artist-item {
+    display: block;
+    text-decoration: none;
+    color: var(--primary-text-color);
+}
diff --git a/share/lua/http/src/components/artists/artists.component.html b/share/lua/http/src/components/artists/artists.component.html
new file mode 100644
index 0000000000..0f56e8b403
--- /dev/null
+++ b/share/lua/http/src/components/artists/artists.component.html
@@ -0,0 +1,10 @@
+<script type="text/x-template" id="artists-template">
+    <div class="artits-view container-fluid">
+        <!-- <div v-if="displayMode === 'list'" class="row">
+            <artist-item-list v-for="item in items" :item="item"></artist-item-list>
+        </div> -->
+        <div class="row" v-if="layout.itemLayout === 'grid'">
+            <artist-item-grid v-for="(item, index) in music.artists" :item="item" :key="index"></artist-item-grid>
+        </div>
+    </div>
+</script>
diff --git a/share/lua/http/src/components/artists/artists.component.js b/share/lua/http/src/components/artists/artists.component.js
new file mode 100644
index 0000000000..33b49ce48e
--- /dev/null
+++ b/share/lua/http/src/components/artists/artists.component.js
@@ -0,0 +1,25 @@
+Vue.component('artists', {
+    template: '#artists-template',
+    data: function() {
+        return {
+            displayMode: 'grid',
+            items: [
+
+            ]
+        }
+    },
+    computed: {
+        ...Vuex.mapState({
+            music: state => state.music,
+            layout: state => state.layout
+        }),
+    },
+    methods: {
+        fetchArtists() {
+            this.$store.dispatch('music/fetchArtists');
+        }
+    },
+    created() {
+        this.fetchArtists();
+    }
+});
diff --git a/share/lua/http/src/components/artists/detail/artists-detail.component.html b/share/lua/http/src/components/artists/detail/artists-detail.component.html
new file mode 100644
index 0000000000..28f883c172
--- /dev/null
+++ b/share/lua/http/src/components/artists/detail/artists-detail.component.html
@@ -0,0 +1,21 @@
+<script type="text/x-template" id="artists-detail-template">
+    <div class="artits-detail-view container-fluid">
+        <div class="row">
+            <div class="col-2 artists-sidebar pt-2">
+                <h5 class="font-weight-normal">Artists</h5>
+                <ul class="list-unstyled h-100">
+                    <li v-for="(artist, index) in music.artists" class="artist-list-item">
+                        <router-link :to="'/music/artists/' + artist.id" exact-active-class="active">
+                            <img v-if="artist.src" class="embed-responsive-item artist-img" :src="artist.src">
+                            <svg-icon v-if="!artist.src" name="placeholder-artist" class="placeholder-artist-icon artist-img"></svg-icon>
+                            <span class="flex-1">{{ artist.name }}</span>
+                        </router-link>
+                    </li>
+                </ul>
+            </div>
+            <div class="col-10 pt-4">
+                <albums :artistId="$route.params.id"></albums>
+            </div>
+        </div>
+    </div>
+</script>
diff --git a/share/lua/http/src/components/artists/detail/artists-detail.component.js b/share/lua/http/src/components/artists/detail/artists-detail.component.js
new file mode 100644
index 0000000000..ed775f54ff
--- /dev/null
+++ b/share/lua/http/src/components/artists/detail/artists-detail.component.js
@@ -0,0 +1,25 @@
+Vue.component('artists-detail', {
+    template: '#artists-detail-template',
+    data: function() {
+        return {
+            displayMode: 'grid',
+            items: [
+
+            ]
+        }
+    },
+    computed: {
+        ...Vuex.mapState({
+            music: state => state.music,
+            layout: state => state.layout
+        }),
+    },
+    methods: {
+        fetchArtists() {
+            this.$store.dispatch('music/fetchArtists');
+        }
+    },
+    created() {
+        this.fetchArtists();
+    }
+});
diff --git a/share/lua/http/src/components/artists/detail/artists-detail.component.scss b/share/lua/http/src/components/artists/detail/artists-detail.component.scss
new file mode 100644
index 0000000000..635fff7525
--- /dev/null
+++ b/share/lua/http/src/components/artists/detail/artists-detail.component.scss
@@ -0,0 +1,59 @@
+.artits-detail-view {
+    .artists-sidebar {
+        height: 100%;
+        background-color: var(--sidenav-bg-color);
+        overflow-y: auto;
+        box-shadow: 0 4px 12px 0 rgba(0, 0, 0, 0.15);
+        position: fixed;
+    }
+    .artist-list-item a {
+        $spacing: 15px;
+        display: flex;
+        align-items: center;
+        padding: 10px 0;
+        font-size: 12px;
+        text-decoration: none;
+        margin-left: -$spacing;
+        margin-right: -$spacing;
+        padding-left: $spacing;
+        padding-right: $spacing;
+        color: var(--primary-text-color);
+        &.active {
+            background-color: var(--playlist-active-bg-color);
+            .artist-img {
+                border: 1px solid var(--highlight-color);
+            }
+        }
+    }
+    .artist-img {
+        width: 24px;
+        height: 24px;
+        margin-top: 0;
+        margin-right: 10px;
+        border-radius: 50%;
+        border: 1px solid rgb(151, 151, 151);
+        &.svg-icon {
+            padding-top: 3px;
+        }
+    }
+}
+
+.artits-detail-view {
+    .col-10 {
+        margin-left: 16.6666666667%;
+    }
+}
+.collapse {
+    .artits-detail-view {
+        .col-10 {
+            margin-left: 16.6666666667%;
+        }
+    }
+    &.show {
+        .artits-detail-view {
+            .col-10 {
+                margin-left: 20%;
+            }
+        }
+    }
+}
diff --git a/share/lua/http/src/components/continue-watching/continue-watching.component.html b/share/lua/http/src/components/continue-watching/continue-watching.component.html
new file mode 100644
index 0000000000..040b31f0d9
--- /dev/null
+++ b/share/lua/http/src/components/continue-watching/continue-watching.component.html
@@ -0,0 +1,11 @@
+<script type="text/x-template" id="continue-watching-template">
+    <div class="continue-watching-view container-fluid">
+        <!-- <div v-if="displayMode === 'list'" class="row">
+            <album-item-list v-for="(item, index) in items" :item="item" :key="index"></album-item-list>
+        </div> -->
+        <h5 class="separator-title">Continue watching</h5>
+        <div class="row" v-if="displayMode === 'grid'">
+            <video-item-grid class="col-12 col-sm-6 col-md-4 col-lg-4" v-for="(item, index) in items" :item="item" :key="index"></video-item-grid>
+        </div>
+    </div>
+</script>
diff --git a/share/lua/http/src/components/continue-watching/continue-watching.component.js b/share/lua/http/src/components/continue-watching/continue-watching.component.js
new file mode 100644
index 0000000000..9df879ebf9
--- /dev/null
+++ b/share/lua/http/src/components/continue-watching/continue-watching.component.js
@@ -0,0 +1,13 @@
+Vue.component('continue-watching', {
+    template: '#continue-watching-template',
+    data: function() {
+        return {
+            displayMode: 'grid',
+            items: [
+
+            ]
+        }
+    },
+    computed: { },
+    methods: { }
+});
diff --git a/share/lua/http/src/components/controls/filebrowser.component.html b/share/lua/http/src/components/controls/filebrowser.component.html
deleted file mode 100644
index c1baa75181..0000000000
--- a/share/lua/http/src/components/controls/filebrowser.component.html
+++ /dev/null
@@ -1,18 +0,0 @@
-<!-- file modal Template -->
-<script type="text/x-template" id="file-modal-template">
-    <div>
-        <div class="modal-dialog">
-            <!-- Modal content-->
-            <div class="modal-content">
-                <div class="modal-header">
-                    <button type="button" class="close" data-dismiss="modal">×</button>
-                    <h4 class="modal-title">Select your file</h4>
-                </div>
-                <div class="modal-body" id="file-tree">
-                </div>
-                <div class="modal-footer">
-                </div>
-            </div>
-        </div>
-    </div>
-</script>
diff --git a/share/lua/http/src/components/controls/filebrowser.component.js b/share/lua/http/src/components/controls/filebrowser.component.js
deleted file mode 100644
index a156042176..0000000000
--- a/share/lua/http/src/components/controls/filebrowser.component.js
+++ /dev/null
@@ -1,83 +0,0 @@
-import { notifyBus } from '../../services/bus.service.js';
-import { VIDEO_TYPES, AUDIO_TYPES, PLAYLIST_TYPES } from '../../services/initialize.service.js';
-
-Vue.component('file-modal', {
-    template: '#file-modal-template',
-    methods: {
-        populateTree() {
-            $('#file-tree').jstree({
-                core: {
-                    multiple: false,
-                    animation: 100,
-                    check_callback: true,
-                    html_titles: true,
-                    load_open: true,
-                    themes: {
-                        variant: 'medium',
-                        dots: false
-                    },
-                    data: {
-                        url: (node) => {
-                            if (node.id === '#') {
-                                return 'requests/browse.json?dir=/';
-                            }
-                            return `requests/browse.json?dir=/${node.data.path}`;
-                        },
-                        dataType: 'json',
-                        dataFilter(rawData) {
-                            const data = JSON.parse(rawData);
-                            const result = data.element.map((d) => {
-                                const res = {
-                                    text: d.name,
-                                    data: {
-                                        path: d.path,
-                                        uri: d.uri,
-                                        type: d.type
-                                    },
-                                    type: d.type,
-                                    children: true
-                                };
-                                if (d.type === 'file') {
-                                    res.children = false;
-                                }
-                                return res;
-                            });
-                            return JSON.stringify(result);
-                        }
-                    }
-                },
-                types: {
-                    '#': {
-                        valid_children: ['root']
-                    },
-                    root: {
-                        valid_children: ['default']
-                    },
-                    default: {
-                        valid_children: ['default', 'file']
-                    }
-                },
-                plugins: [
-                    'themes',
-                    'json_data',
-                    'ui',
-                    'cookies',
-                    'crrm',
-                    'sort',
-                    'types'
-                ]
-            });
-
-            $('#file-tree').on('select_node.jstree', (e, data) => {
-                node = data.instance.get_node(data.selected[0]);
-                ext = (node.data.uri).substr(node.data.uri.lastIndexOf('.') + 1).toLowerCase();
-                if (node.data.type === 'file' && ($.inArray(ext, VIDEO_TYPES) !== -1 || $.inArray(ext, AUDIO_TYPES) !== -1 || $.inArray(ext, PLAYLIST_TYPES) !== -1)) {
-                    notifyBus('addItem', [1, '', node.data.uri,node.data.uri]);
-                }
-            }).jstree();
-        }
-    },
-    mounted() {
-        this.populateTree();
-    }
-});
diff --git a/share/lua/http/src/components/controls/sidenav.component.html b/share/lua/http/src/components/controls/sidenav.component.html
deleted file mode 100644
index acc5eca71c..0000000000
--- a/share/lua/http/src/components/controls/sidenav.component.html
+++ /dev/null
@@ -1,31 +0,0 @@
-<!-- sideNav Template -->
-<script type="text/x-template" id="sidenav-template">
-    <div>
-        <div id="sideNav" class="sideNav">
-            <a href="javascript:void(0)" class="closebtn" id="closeNavButton">
-                Menu
-            </a>
-            <a href="#" data-toggle="modal" data-target="#fileModal">
-                Open File
-            </a>
-            <a href="#">
-                Local Network
-            </a>
-            <a href="#">
-                Podcast
-            </a>
-            <a href="#" data-toggle="modal" data-target="#streamManagerModal">
-                Manage Streams
-            </a>
-            <a href="#" data-toggle="modal" data-target="#trackSyncModal">
-                Track Synchronization
-            </a>
-            <a href="#" data-toggle="modal" data-target="#vlmModal">
-                VLM Batch Commands
-            </a>
-        </div>
-        <span id="openNavButton">
-            <svg-icon name="menu"></svg-icon>
-        </span>
-    </div>
-</script>
diff --git a/share/lua/http/src/components/controls/sidenav.component.js b/share/lua/http/src/components/controls/sidenav.component.js
deleted file mode 100644
index 878388a3c9..0000000000
--- a/share/lua/http/src/components/controls/sidenav.component.js
+++ /dev/null
@@ -1,26 +0,0 @@
-import { bus } from '../../services/bus.service.js';
-
-Vue.component('sidenav', {
-    template: '#sidenav-template',
-    methods: {
-        openNav() {
-            if (window.screen.width <= 480) {
-                $('#sideNav').width('60%');
-            } else {
-                $('#sideNav').width('20%');
-            }
-        },
-        closeNav() {
-            $('#sideNav').width('0');
-        }
-    },
-    created() {
-        bus.$on('openNav', () => {
-            this.openNav();
-        });
-
-        bus.$on('closeNav', () => {
-            this.closeNav();
-        });
-    }
-});
diff --git a/share/lua/http/src/components/controls/sidenav.component.scss b/share/lua/http/src/components/controls/sidenav.component.scss
deleted file mode 100644
index fc7e7d7b13..0000000000
--- a/share/lua/http/src/components/controls/sidenav.component.scss
+++ /dev/null
@@ -1,50 +0,0 @@
-/* The side navigation menu */
-.sideNav {
-    height: 100%;
-    width: 0;
-    position: fixed;
-    z-index: 5;
-    top: 0;
-    left: 0;
-    background-color: orange;
-    overflow-x: hidden;
-    padding-top: 60px;
-    transition: 0.5s;
-}
-
-/* The navigation menu links */
-.sideNav a {
-    padding: 8px 8px 8px 32px;
-    text-decoration: none;
-    font-size: 25px;
-    color: #111;
-    display: block;
-    transition: 0.3s
-}
-
-/* When you mouse over the navigation links, change their color */
-.sideNav a:hover, .offcanvas a:focus{
-    color: #f1f1f1;
-}
-
-/* Position and style the close button (top right corner) */
-.sideNav .closebtn {
-    position: absolute;
-    top: 0;
-    font-size: 36px;
-}
-
-.sideNav .closespn {
-    font-size: 36px;
-    margin-left: 60%;
-}
-
-/* On smaller screens, where height is less than 450px, change the style of the sidenav (less padding and a smaller font size) */
- at media screen and (max-height: 450px) {
-    .sideNav {padding-top: 15px;}
-    .sideNav a {font-size: 18px;}
-}
-
-#openNavButton{
-   color: orange;
-}
diff --git a/share/lua/http/src/components/enqueue-button/enqueue-button.component.html b/share/lua/http/src/components/enqueue-button/enqueue-button.component.html
new file mode 100644
index 0000000000..f1b58c2cb6
--- /dev/null
+++ b/share/lua/http/src/components/enqueue-button/enqueue-button.component.html
@@ -0,0 +1,5 @@
+<script type="text/x-template" id="enqueue-button-template">
+    <button v-on:click="enqueueItem()" class="btn btn-sm enqueue-button">
+        <svg-icon name="playlist_add"></svg-icon> Enqueue
+    </button>
+</script>
diff --git a/share/lua/http/src/components/enqueue-button/enqueue-button.component.js b/share/lua/http/src/components/enqueue-button/enqueue-button.component.js
new file mode 100644
index 0000000000..21bf28bd07
--- /dev/null
+++ b/share/lua/http/src/components/enqueue-button/enqueue-button.component.js
@@ -0,0 +1,14 @@
+Vue.component('enqueue-button', {
+    template: '#enqueue-button-template',
+    props: ['item'],
+    methods: {
+        enqueueItem() {
+            if (this.item) {
+                // Check if in playlist else add it
+                if (this.item.mrl) {
+                    this.$store.dispatch('playlist/addItem', this.item.mrl);
+                }
+            }
+        }
+    }
+});
diff --git a/share/lua/http/src/components/enqueue-button/enqueue-button.component.scss b/share/lua/http/src/components/enqueue-button/enqueue-button.component.scss
new file mode 100644
index 0000000000..17be26a21c
--- /dev/null
+++ b/share/lua/http/src/components/enqueue-button/enqueue-button.component.scss
@@ -0,0 +1,4 @@
+.btn.enqueue-button {
+    display: flex;
+    align-items: center;
+}
diff --git a/share/lua/http/src/components/equalizer/equalizer.component.html b/share/lua/http/src/components/equalizer/equalizer.component.html
index 9ab4ffda7b..b825edd576 100644
--- a/share/lua/http/src/components/equalizer/equalizer.component.html
+++ b/share/lua/http/src/components/equalizer/equalizer.component.html
@@ -5,8 +5,8 @@
             <!-- Modal content-->
             <div class="modal-content">
                 <div class="modal-header">
-                    <button type="button" class="close" data-dismiss="modal">×</button>
                     <h4 class="modal-title">Equalizer</h4>
+                    <button type="button" class="close" data-dismiss="modal">×</button>
                 </div>
                 <div class="modal-body">
                     <input type="range" name="equalizerInput" id="equalizerInput" min="-20" max="20" step="0.1"/>
diff --git a/share/lua/http/src/components/genres/genre-item/genre-item-grid.component.html b/share/lua/http/src/components/genres/genre-item/genre-item-grid.component.html
new file mode 100644
index 0000000000..a3f2d899be
--- /dev/null
+++ b/share/lua/http/src/components/genres/genre-item/genre-item-grid.component.html
@@ -0,0 +1,12 @@
+<script type="text/x-template" id="genre-item-grid-template">
+    <div class="item-people genre-item tag-item genre-item-grid-template col-12 col-sm-6 col-md-4 col-lg-3">
+        <div class="item-img">
+            <img :src="item.src">
+            <div class="item-body">
+                <span class="item-title text-truncate">{{ item.name }}</span>
+                <span class="item-subtitle text-truncate">{{ item.nbTracks }} tracks</span>
+            </div>
+            <div class="overlay"></div>
+        </div>
+    </div>
+</script>
diff --git a/share/lua/http/src/components/genres/genre-item/genre-item-grid.component.js b/share/lua/http/src/components/genres/genre-item/genre-item-grid.component.js
new file mode 100644
index 0000000000..c65d4d3f64
--- /dev/null
+++ b/share/lua/http/src/components/genres/genre-item/genre-item-grid.component.js
@@ -0,0 +1,16 @@
+Vue.component('genre-item-grid', {
+    template: '#genre-item-grid-template',
+    props: ['item'],
+    data: function() {
+        return {
+            liked: this.item.liked
+        }
+    },
+    computed: { },
+    methods: {
+        toggleLiked() {
+            this.liked = !this.liked;
+        }
+    },
+    created() { }
+});
diff --git a/share/lua/http/src/components/genres/genres.component.html b/share/lua/http/src/components/genres/genres.component.html
new file mode 100644
index 0000000000..7b14a5eae9
--- /dev/null
+++ b/share/lua/http/src/components/genres/genres.component.html
@@ -0,0 +1,10 @@
+<script type="text/x-template" id="genres-template">
+    <div class="genres-view container-fluid">
+        <!-- <div v-if="displayMode === 'list'" class="row">
+            <artist-item-list v-for="item in items" :item="item"></artist-item-list>
+        </div> -->
+        <div class="row" v-if="layout.itemLayout === 'grid'">
+            <genre-item-grid v-for="(item, index) in music.genres" :item="item" :key="index"></genre-item-grid>
+        </div>
+    </div>
+</script>
diff --git a/share/lua/http/src/components/genres/genres.component.js b/share/lua/http/src/components/genres/genres.component.js
new file mode 100644
index 0000000000..b10f5af8e5
--- /dev/null
+++ b/share/lua/http/src/components/genres/genres.component.js
@@ -0,0 +1,25 @@
+Vue.component('genres', {
+    template: '#genres-template',
+    data: function() {
+        return {
+            displayMode: 'grid',
+            items: [
+
+            ]
+        }
+    },
+    computed: {
+        ...Vuex.mapState({
+            music: state => state.music,
+            layout: state => state.layout
+        }),
+    },
+    methods: {
+        fetchGenres() {
+            this.$store.dispatch('music/fetchGenres');
+        }
+    },
+    created() {
+        this.fetchGenres();
+    }
+});
diff --git a/share/lua/http/src/components/grid-item/grid-item.component.html b/share/lua/http/src/components/grid-item/grid-item.component.html
new file mode 100644
index 0000000000..d54b507924
--- /dev/null
+++ b/share/lua/http/src/components/grid-item/grid-item.component.html
@@ -0,0 +1,14 @@
+<script type="text/x-template" id="grid-item-template">
+    <div class="grid-item discover-services-item-grid-template col-12 col-sm-6 col-md-4 col-lg-3 col-xl-2">
+        <div :class="item.additionalClass" class="item-img embed-responsive embed-responsive-1by1 img-contain">
+            <img v-if="item.src" class="embed-responsive-item p-4" :src="item.src">
+            <div v-else="item.icon" class="embed-responsive-item" :class="item.additionalItemClass">
+                <svg-icon :name="item.icon"></svg-icon>
+            </div>
+        </div>
+        <div class="item-body">
+            <router-link :to="item.titleRoute || '/album'" class="item-title text-truncate">{{ item.title }}</router-link>
+            <router-link v-if="item.subtitle" :to="item.subtitleRoute || '/artist'" class="item-subtitle text-truncate">{{ item.subtitle }}</router-link>
+        </div>
+    </div>
+</script>
diff --git a/share/lua/http/src/components/grid-item/grid-item.component.js b/share/lua/http/src/components/grid-item/grid-item.component.js
new file mode 100644
index 0000000000..c8a06c8193
--- /dev/null
+++ b/share/lua/http/src/components/grid-item/grid-item.component.js
@@ -0,0 +1,5 @@
+Vue.component('grid-item', {
+    template: '#grid-item-template',
+    props: ['item'],
+    created() { }
+});
diff --git a/share/lua/http/src/components/item-layout-button/item-layout-button.component.html b/share/lua/http/src/components/item-layout-button/item-layout-button.component.html
new file mode 100644
index 0000000000..75f07346d9
--- /dev/null
+++ b/share/lua/http/src/components/item-layout-button/item-layout-button.component.html
@@ -0,0 +1,6 @@
+<script type="text/x-template" id="item-layout-button-template">
+    <button v-on:click="toggleItemLayout()" class="btn btn-circle active">
+        <svg-icon v-if="layout && layout.itemLayout === 'list'" name="format_list_bulleted"></svg-icon>
+        <svg-icon v-if="layout && layout.itemLayout === 'grid'" name="grid_view"></svg-icon>
+    </button>
+</script>
diff --git a/share/lua/http/src/components/item-layout-button/item-layout-button.component.js b/share/lua/http/src/components/item-layout-button/item-layout-button.component.js
new file mode 100644
index 0000000000..c58b5cb5b2
--- /dev/null
+++ b/share/lua/http/src/components/item-layout-button/item-layout-button.component.js
@@ -0,0 +1,16 @@
+Vue.component('item-layout-button', {
+    template: '#item-layout-button-template',
+    computed: {
+        ...Vuex.mapState({
+            layout: state => state.layout
+        }),
+    },
+    created() {
+        document.body.style['overflow-y'] = 'auto';
+    },
+    methods: {
+        toggleItemLayout() {
+            this.$store.dispatch('layout/toggleItemLayout');
+        }
+    }
+});
diff --git a/share/lua/http/src/components/library/library-item/library-item.component.html b/share/lua/http/src/components/library/library-item/library-item.component.html
new file mode 100644
index 0000000000..1ccde89882
--- /dev/null
+++ b/share/lua/http/src/components/library/library-item/library-item.component.html
@@ -0,0 +1,11 @@
+<script type="text/x-template" id="library-item-template">
+    <div class="library-item">
+        <div class="library-item-img">
+            <img src="http://images.videolan.org/images/VLC-IconSmall.png">
+        </div>
+        <div class="library-item-body">
+            <div class="library-item-name text-truncate">{{ item.name }}</div>
+            <div class="library-item-artist text-truncate">{{ item.artist }}</div>
+        </div>
+    </div>
+</script>
diff --git a/share/lua/http/src/components/library/library-item/library-item.component.js b/share/lua/http/src/components/library/library-item/library-item.component.js
new file mode 100644
index 0000000000..6737b2db93
--- /dev/null
+++ b/share/lua/http/src/components/library/library-item/library-item.component.js
@@ -0,0 +1,15 @@
+Vue.component('library-item', {
+    template: '#library-item-template',
+    computed: {
+
+    },
+    methods: {
+
+    },
+    created() {
+        this.item = {
+            name: 'Some name',
+            artist: 'Some artist'
+        }
+    }
+});
diff --git a/share/lua/http/src/components/library/library-item/library-item.component.scss b/share/lua/http/src/components/library/library-item/library-item.component.scss
new file mode 100644
index 0000000000..6ad6a56902
--- /dev/null
+++ b/share/lua/http/src/components/library/library-item/library-item.component.scss
@@ -0,0 +1,26 @@
+.library-item {
+    display: inline-block;
+    width: 150px;
+    margin: 10px;
+    .library-item-img {
+        display: flex;
+        align-items: center;
+        justify-content: center;
+        background: var(--library-item-bg-color);
+        height: 150px;
+    }
+    .library-item-body {
+        text-align: center;
+        overflow: hidden;
+        text-overflow: ellipsis;
+        margin-top: 5px;
+        .library-item-name {
+            font-weight: bold;
+        }
+        .library-item-artist {
+            font-weight: normal;
+            color: var(--secondary-text-color);
+            font-size: 0.8rem;
+        }
+    }
+}
\ No newline at end of file
diff --git a/share/lua/http/src/components/library/library.component.html b/share/lua/http/src/components/library/library.component.html
new file mode 100644
index 0000000000..12ca97ec88
--- /dev/null
+++ b/share/lua/http/src/components/library/library.component.html
@@ -0,0 +1,17 @@
+<script type="text/x-template" id="library-template">
+    <div class="library-view container-fluid">
+        <library-item></library-item>
+        <library-item></library-item>
+        <library-item></library-item>
+        <library-item></library-item>
+        <library-item></library-item>
+        <library-item></library-item>
+        <library-item></library-item>
+        <library-item></library-item>
+        <library-item></library-item>
+        <library-item></library-item>
+        <library-item></library-item>
+        <library-item></library-item>
+        <library-item></library-item>
+    </div>
+</script>
diff --git a/share/lua/http/src/components/library/library.component.js b/share/lua/http/src/components/library/library.component.js
new file mode 100644
index 0000000000..87dc6ea235
--- /dev/null
+++ b/share/lua/http/src/components/library/library.component.js
@@ -0,0 +1,5 @@
+Vue.component('library', {
+    template: '#library-template',
+    computed: { },
+    methods: { }
+});
diff --git a/share/lua/http/src/components/library/library.component.scss b/share/lua/http/src/components/library/library.component.scss
new file mode 100644
index 0000000000..823aa363f4
--- /dev/null
+++ b/share/lua/http/src/components/library/library.component.scss
@@ -0,0 +1,3 @@
+.library-view {
+    margin: 90px 20px 20px 8px;
+}
diff --git a/share/lua/http/src/components/media-player/media-player.component.html b/share/lua/http/src/components/media-player/media-player.component.html
new file mode 100644
index 0000000000..5260d59cc8
--- /dev/null
+++ b/share/lua/http/src/components/media-player/media-player.component.html
@@ -0,0 +1,66 @@
+<script type="text/x-template" id="media-player-template">
+    <div id="media-player" class="media-player">
+        <audio class="d-none" controls="false" ref="player" :src="src"></audio>
+        <div class="seek-bar">
+            <div class="seek-bar-track">
+                <div ref="progressBar" class="seek-bar-progress slider-fill"></div>
+                <div ref="bufferBar" class="seek-bar-buffer slider-fill"></div>
+            </div>
+            <input ref="scrubber" class="seek-bar-scrubber" type="range" min="0" max="1000" value="0"></input>
+        </div>
+        <div class="d-flex align-items-center justify-content-center media-player-container">
+            <div class="metadata d-flex flex-1">
+                <!-- Image + info -->
+                <img class="img" v-if="imageSrc" :src="imageSrc">
+                <svg-icon v-if="!imageSrc" name="placeholder-album" class="placeholder-album-icon"></svg-icon>
+                <span class="ml-2 d-flex flex-column justify-content-center">
+                    <span class="title font-weight-bold">{{ status.information && status.information.category && status.information.category.meta ? status.information.category.meta.filename : '' }} </span>
+                    <span class="subtitle muted-opacity"></span>
+                    <span class="subtitle2 muted-opacity">{{ formattedCurrentTime }} / {{ formattedDuration }}</span>
+                </span>
+            </div>
+            <div class="primary-actions d-flex flex-1 justify-content-center">
+                <!-- Primary actions -->
+                <button v-on:click="toggleRandom()" class="btn btn-link p-0 media-action" :class="{ active: status.random }">
+                    <svg-icon name="shuffle"></svg-icon>
+                </button>
+                <button class="btn btn-link p-0 media-action" v-on:click="previous()">
+                    <svg-icon name="skip_previous"></svg-icon>
+                </button>
+                <button class="btn btn-link p-0 media-action play-action" v-on:click="playing ? pause() : play()">
+                    <svg-icon v-if="status.state === 'playing'" name="pause"></svg-icon>
+                    <svg-icon v-else name="play_arrow"></svg-icon>
+                </button>
+                <button class="btn btn-link p-0 media-action" v-on:click="next()">
+                    <svg-icon name="skip_next"></svg-icon>
+                </button>
+                <button v-on:click="toggleRepeat()" class="btn btn-link p-0 media-action" :class="{ active: status.repeat }">
+                    <svg-icon name="repeat"></svg-icon>
+                </button>
+            </div>
+            <div class="secondary-actions d-flex flex-1 justify-content-end">
+                <!-- Secondary actions -->
+                <div class="media-action volume-container">
+                    <button class="btn btn-link p-0 media-action" v-on:click="toggleMute()">
+                        <svg-icon v-if="volumeIconState === 'mute'" name="volume_off"></svg-icon>
+                        <svg-icon v-if="volumeIconState === 'up'" name="volume_up"></svg-icon>
+                        <svg-icon v-if="volumeIconState === 'down'" name="volume_down"></svg-icon>
+                    </button>
+                    <div class="volume-panel">
+                        <div class="volume-slider">
+                            <div class="volume-slider-track-default"></div>
+                            <div ref="volumeBar" class="volume-slider-track-active"></div>
+                            <input ref="volume" class="volume-slider-input" type="range">
+                        </div>
+                    </div>
+                </div>
+                <button v-on:click="toggleFullscreen()" class="btn btn-link p-0 media-action" :class="{ active: status.fullscreen }">
+                    <svg-icon name="Icon-Fullscreen"></svg-icon>
+                </button>
+                <div class="media-action">
+                    <svg-icon name="more-vertical"></svg-icon>
+                </div>
+            </div>
+        </div>
+    </div>
+</script>
diff --git a/share/lua/http/src/components/media-player/media-player.component.js b/share/lua/http/src/components/media-player/media-player.component.js
new file mode 100644
index 0000000000..c753c10b96
--- /dev/null
+++ b/share/lua/http/src/components/media-player/media-player.component.js
@@ -0,0 +1,268 @@
+const formatTime = second => new Date(second * 1000).toISOString().substr(11, 8);
+Vue.component('media-player', {
+    template: '#media-player-template',
+    props: ['src', 'autoPlay', 'ended', 'canPlay'],
+    computed: {
+        ...Vuex.mapState({
+            activeItem: state => state.playlist.activeItem,
+            status: state => state.status.data
+        }),
+    },
+    data() {
+        return {
+            firstPlay: true,
+            isMuted: false,
+            loaded: false,
+            playing: false,
+            paused: false,
+            percentage: 0,
+            currentTime: '00:00:00',
+            audio: undefined,
+            totalDuration: 0,
+            duration: 0,
+            volumeIconState: 'up',
+            formattedDuration: formatTime(0),
+            formattedCurrentTime: formatTime(0),
+            imageSrc
+        }
+    },
+    methods: {
+        init: function() {
+            this.player.addEventListener('loadeddata', this._handleLoaded);
+            this.player.addEventListener('pause', this._handlePlayPause);
+            this.player.addEventListener('play', this._handlePlayPause);
+            this.player.addEventListener('seeked', this._handleSeeked);
+            this.player.addEventListener('progress', this._handleProgress);
+            this.player.addEventListener('timeupdate', this._handleTimeUpdate);
+            this.player.addEventListener('ended', this._handleEnded);
+            this.player.volume = 0.5;
+
+            this.scrubber.addEventListener('input', this._handleScrubberChange);
+            this.volume.addEventListener('input', this._handleVolumeChange);
+            this._handleVolumeChange();
+        },
+        fetchStatus() {
+            this.$store.dispatch('status/fetchStatus');
+        },
+        refreshStatus() {
+            if (this.interval) {
+                clearTimeout(this.interval);
+                this.interval = null;
+            }
+            this.fetchStatus();
+            this.interval = setInterval(() => {
+                this.fetchStatus();
+            }, 1000);
+        },
+        play(dispatch = true) {
+            this.previousState = null;
+            if (this.playing) {
+                return;
+            }
+            this.paused = false;
+            if (this.src) {
+                this.player.play().then(() => {
+                    this.playing = true;
+                });
+            } else {
+                this.playing = true;
+            }
+            if (dispatch) {
+                this.$store.dispatch('status/play', this.status.currentplid);
+            }
+        },
+        pause(dispatch = true) {
+            this.previousState = null;
+            this.paused = !this.paused;
+            if (this.src) {
+                this.paused ? this.player.pause() : this.player.play();
+            }
+            if (dispatch) {
+                this.$store.dispatch('status/pause', this.status.currentplid);
+            }
+        },
+        stop(dispatch = true) {
+            this.previousState = null;
+            this.paused = this.playing = false;
+            if (this.src) {
+                this.player.pause();
+            }
+            this.player.currentTime = 0;
+            if (dispatch) {
+                this.$store.dispatch('status/stop', this.status.currentplid);
+            }
+        },
+        setPosition() {
+            this.player.currentTime = parseInt(this.player.duration / 100 * this.percentage);
+        },
+        next() {
+            this.$store.dispatch('status/next');
+        },
+        previous() {
+            this.$store.dispatch('status/previous');
+        },
+        toggleRandom() {
+            this.$store.dispatch('status/toggleRandom');
+        },
+        toggleRepeat() {
+            this.$store.dispatch('status/toggleRepeat');
+        },
+        toggleFullscreen() {
+            this.$store.dispatch('status/toggleFullscreen');
+        },
+        toggleMute() {
+            if (parseInt(this.volume.value)) {
+                this.previousVolume = this.volume.value;
+                this.volume.value = 0;
+            } else {
+                this.volume.value = this.previousVolume;
+            }
+            this._handleVolumeChange();
+        },
+        handleState(state) {
+            if (this.previousState && this.previousState == state) {
+                return;
+            }
+            this.previousState = state;
+            switch (state) {
+                case 'playing':
+                    this.play(false);
+                    break;
+                case 'paused':
+                    this.pause(false);
+                    break;
+                case 'stopped':
+                    this.stop(false);
+                    break;
+            }
+        },
+        handleVolume(volume) {
+            this.volume.value = volume / 255 * 100;
+            this._handleVolumeChange(false);
+        },
+        handleCurrentTime(currentTime) {
+            this.player.currentTime = currentTime;
+            this._handleTimeUpdate();
+        },
+        _handleSeeked() {
+        },
+        _handleProgress: function() {
+            let duration = this.player.duration;
+            if (duration > 0) {
+                for (let i = 0; i < this.player.buffered.length; i++) {
+                    if (this.player.buffered.start(this.player.buffered - 1 - i) < this.player.currentTime) {
+                        let scaleX = this.player.buffered.end(this.player.buffered.length - 1 - i) / duration;
+                        this.bufferBar.style.transform = `scaleX(${scaleX})`;
+                        break;
+                    }
+                }
+            }
+        },
+        _handleTimeUpdate: function() {
+            this.duration = this.status.length * 1000;
+            const currentTime = this.status.time * 1000;
+            this.percentage = currentTime / this.duration * 100
+            this.formattedCurrentTime = formatTime(currentTime / 1000);
+            this.formattedDuration = formatTime(this.duration / 1000);
+            if (this.duration > 0) {
+                let scaleX = currentTime / this.duration;
+                this.progressBar.style.transform = `scaleX(${scaleX})`;
+                // Update scrubber
+                this.scrubber.value = scaleX * 1000;
+            }
+        },
+        _handleEnded() {
+            this.paused = this.playing = false;
+        },
+        _handlePlayPause: function(e) {
+            if (e.type === 'play' && this.firstPlay) {
+                this.player.currentTime = 0;
+                if (this.firstPlay) {
+                    this.firstPlay = false;
+                }
+            }
+            if (e.type === 'pause' && this.paused === false && this.playing === false) {
+                this.formattedCurrentTime = formatTime(0);
+            }
+        },
+        _handleLoaded: function() {
+            if (this.player.readyState >= 2) {
+                if (this.player.duration === Infinity) {
+                    this.player.currentTime = 1e101;
+                    this.player.ontimeupdate = () => {
+                        this.player.ontimeupdate = () => {}
+                        this.player.currentTime = 0
+                        this.totalDuration = parseInt(this.player.duration)
+                        this.loaded = true;
+                    }
+                } else {
+                    this.totalDuration = parseInt(this.player.duration);
+                    this.loaded = true;
+                }
+                // this.duration = formatTime(this.totalDuration);
+                if (this.autoPlay) {
+                    this.player.play();
+                }
+            } else {
+                throw new Error('Failed to load sound file')
+            }
+        },
+        _handleScrubberChange: function(e) {
+            this.progressBar.style.transition = 'none;';
+            this.progressBar.style.transform = `scaleX(${this.scrubber.value / 1000})`;
+            this.progressBar.style.transition = 'transform .1s;';
+            const cTime = this.scrubber.value / 1000 * (this.duration);
+            this.player.currentTime = cTime;
+            this.$store.dispatch('status/seek', (this.player.currentTime / 1000).toFixed());
+        },
+        _handleVolumeChange: function(dispatch = true) {
+            this.volumeBar.style.width = `${this.volume.value}%`;
+            this.player.volume = this.volume.value / 100;
+            if (dispatch) {
+                this.$store.dispatch('status/updateVolume', this.player.volume * 255);
+            }
+            if (this.volume.value >= 50) {
+                this.volumeIconState = 'up';
+            } else if (this.volume.value < 50 && this.volume.value > 0) {
+                this.volumeIconState = 'down';
+            } else {
+                this.volumeIconState = 'mute';
+            }
+        }
+    },
+    mounted() {
+        this.player = this.$refs.player;
+        this.scrubber = this.$refs.scrubber;
+        this.volume = this.$refs.volume;
+        this.progressBar = this.$refs.progressBar;
+        this.volumeBar = this.$refs.volumeBar;
+        this.bufferBar = this.$refs.bufferBar;
+        this.init();
+        this.$store.subscribe((mutation, payload) => {
+            switch (mutation.type) {
+                case 'status/setStatus':
+                    this.handleCurrentTime(payload.status.data.time);
+                    this.handleState(payload.status.data.state);
+                    this.handleVolume(payload.status.data.volume);
+                    break;
+                // case 'playlist/activeItem':
+            }
+        });
+    },
+    created() {
+        this.refreshStatus();
+    },
+    beforeDestroy() {
+        this.player.removeEventListener('loadeddata', this._handleLoaded);
+        this.player.removeEventListener('pause', this._handlePlayPause);
+        this.player.removeEventListener('play', this._handlePlayPause);
+        this.player.removeEventListener('seeked', this._handleSeeked);
+        this.player.removeEventListener('progress', this._handleProgress);
+        this.player.removeEventListener('timeupdate', this._handleTimeUpdate);
+        this.player.removeEventListener('ended', this._handleEnded);
+
+        this.scrubber.removeEventListener('input', this._handleScrubberChange);
+        this.volume.removeEventListener('input', this._handleVolumeChange);
+        clearTimeout(this.interval);
+    }
+});
diff --git a/share/lua/http/src/components/media-player/media-player.component.scss b/share/lua/http/src/components/media-player/media-player.component.scss
new file mode 100644
index 0000000000..b01d464101
--- /dev/null
+++ b/share/lua/http/src/components/media-player/media-player.component.scss
@@ -0,0 +1,133 @@
+.media-player {
+    position: fixed;
+    bottom: 0;
+    left: 0;
+    right: 0;
+    height: 86px;
+    background-color: var(--media-player-bg-color);
+    backdrop-filter: blur(10px);
+    z-index: 9999;
+    .img {
+        width: 70px;
+        height: 70px;
+        box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2);
+        border-radius: 3px;
+        overflow: hidden;
+    }
+    .seek-bar {
+        position: absolute;
+        width: 100%;
+        top: 0;
+    }
+    .media-player-container {
+        margin: 15px 10px 10px 10px;
+    }
+    .seek-bar-track {
+        height: 4px;
+        position: relative;
+        &::before {
+            position: absolute;
+            top: 0;
+            bottom: 0;
+            right: 0;
+            left: 0;
+            content: '';
+            background-color: rgba(0, 0, 0, 0.6);
+            transition: background-color .6s;
+            transition-delay: .2s;
+        }
+    }
+    .slider-fill {
+        position: absolute;
+        transition: transform .1s;
+        transform-origin: center left;
+        width: 100%;
+        height: 4px;
+    }
+    .seek-bar-progress {
+        background-color: var(--highlight-color);
+        transform: scaleX(0);
+    }
+    .seek-bar-buffer {
+        background-color: rgba(244, 139, 0, 0.45);
+        top: 0;
+        transform: scaleX(0);
+    }
+    input[type=range]::-webkit-slider-thumb {
+        -webkit-appearance: none;
+        border: none;
+        height: 16px;
+        width: 16px;
+        border-radius: 50%;
+        background: #ffffff;
+        cursor: pointer;
+        box-shadow: 0.5px 0.5px 0.5px #000000, 0px 0px 0.5px #0d0d0d;
+    }
+    .seek-bar-scrubber {
+        -webkit-appearance: none;
+        background: transparent;
+        cursor: pointer;
+        height: 8px;
+        right: 0;
+        position: absolute;
+        top: -2px;
+        width: 100%;
+        outline: none;
+    }
+    .media-action {
+        width: 2rem;
+        height: 2rem;
+        margin: 0 5px;
+    }
+    .volume-container {
+        display: flex;
+        align-items: center;
+        width: 100px;
+        .volume-panel {
+            position: relative;
+            height: 6px;
+        }
+        .volume-slider {
+            width: 70px;
+        }
+        .volume-slider-track-default {
+            position: absolute;
+            border-radius: 4px;
+            left: 0;
+            background-color: rgba(0, 0, 0, 0.6);
+            height: 6px;
+            width: 100%;
+        }
+        .volume-slider-track-active {
+            background-color: var(--highlight-color);
+            position: absolute;
+            height: 6px;
+            width: 50%;
+        }
+        .volume-slider-input {
+            -webkit-appearance: none;
+            background: rgba(0,0,0,0);
+            cursor: pointer;
+            height: 6px;
+            position: absolute;
+            width: 100%;
+            outline: none;
+        }
+    }
+    .media-action.play-action {
+        border-radius: 50%;
+        background: rgba(0, 0, 0, 0.7);
+        display: flex;
+        align-items: center;
+        justify-content: center;
+        .svg-icon {
+            color: #fff;
+        }
+    }
+    .subtitle {
+        font-size: 0.8rem;
+    }
+    .subtitle2 {
+        font-size: 0.8rem;
+    }
+}
diff --git a/share/lua/http/src/components/play-button/play-button-secondary.component.html b/share/lua/http/src/components/play-button/play-button-secondary.component.html
new file mode 100644
index 0000000000..68ee5face0
--- /dev/null
+++ b/share/lua/http/src/components/play-button/play-button-secondary.component.html
@@ -0,0 +1,5 @@
+<script type="text/x-template" id="play-button-secondary-template">
+    <button v-on:click="playItem()" class="btn btn-sm play-button-secondary">
+        <svg-icon name="play_arrow"></svg-icon> Play
+    </button>
+</script>
diff --git a/share/lua/http/src/components/play-button/play-button-secondary.component.js b/share/lua/http/src/components/play-button/play-button-secondary.component.js
new file mode 100644
index 0000000000..94d1953c0b
--- /dev/null
+++ b/share/lua/http/src/components/play-button/play-button-secondary.component.js
@@ -0,0 +1,15 @@
+Vue.component('play-button-secondary', {
+    template: '#play-button-secondary-template',
+    props: ['item'],
+    methods: {
+        playItem() {
+            if (this.item) {
+                // Check if in playlist else add it
+                if (this.item.mrl) {
+                    this.$store.dispatch('playlist/addItem', this.item.mrl);
+                }
+                this.$store.dispatch('status/play', this.item.id);
+            }
+        }
+    }
+});
diff --git a/share/lua/http/src/components/play-button/play-button-secondary.component.scss b/share/lua/http/src/components/play-button/play-button-secondary.component.scss
new file mode 100644
index 0000000000..0e58647cee
--- /dev/null
+++ b/share/lua/http/src/components/play-button/play-button-secondary.component.scss
@@ -0,0 +1,12 @@
+.btn.play-button-secondary {
+    background-color: var(--highlight-color);
+    color: #fff;
+    display: flex;
+    align-items: center;
+    .svg-icon {
+        color: #fff;
+    }
+    &:hover, &:active, &:focus {
+        color: #fff;
+    }
+}
diff --git a/share/lua/http/src/components/play-button/play-button.component.html b/share/lua/http/src/components/play-button/play-button.component.html
new file mode 100644
index 0000000000..883d3f84a2
--- /dev/null
+++ b/share/lua/http/src/components/play-button/play-button.component.html
@@ -0,0 +1,5 @@
+<script type="text/x-template" id="play-button-template">
+    <button v-on:click="playItem()" class="btn btn-link play-button">
+        <svg-icon name="play_arrow"></svg-icon>
+    </button>
+</script>
diff --git a/share/lua/http/src/components/play-button/play-button.component.js b/share/lua/http/src/components/play-button/play-button.component.js
new file mode 100644
index 0000000000..6810eb9a2e
--- /dev/null
+++ b/share/lua/http/src/components/play-button/play-button.component.js
@@ -0,0 +1,16 @@
+Vue.component('play-button', {
+    template: '#play-button-template',
+    props: ['item'],
+    methods: {
+        playItem() {
+            if (this.item) {
+                // Check if in playlist else add it
+                // Don't add if already added
+                if (this.item.mrl) {
+                    this.$store.dispatch('playlist/addItem', this.item.mrl);
+                }
+                this.$store.dispatch('status/play', this.item.id);
+            }
+        }
+    }
+});
diff --git a/share/lua/http/src/components/play-button/play-button.component.scss b/share/lua/http/src/components/play-button/play-button.component.scss
new file mode 100644
index 0000000000..ce8158dd81
--- /dev/null
+++ b/share/lua/http/src/components/play-button/play-button.component.scss
@@ -0,0 +1,28 @@
+.play-button {
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    border-radius: 50%;
+    transition: opacity 0.2s ease, visibility 0.2s, transform 0.2s;
+    border: none;
+    padding: 0;
+    margin: 0;
+    transition: opacity 0.2s ease, visibility 0.2s, transform 0.2s;
+    background: rgba(0, 0, 0, 0.4);
+    border: 2px solid #fff;
+    .svg-icon {
+        color: #fff;
+        &:hover,
+        &:focus {
+            color: #fff;
+        }
+    }
+    &:hover {
+        transform: scale3d(1.2,1.2,1.2);
+        -webkit-transform: scale3d(1.2,1.2,1.2);
+    }
+    &:active {
+        transform: scaleX(1);
+        -webkit-transform: scaleX(1);
+    }
+}
\ No newline at end of file
diff --git a/share/lua/http/src/components/player/player.component.scss b/share/lua/http/src/components/player/player.component.scss
deleted file mode 100644
index 753348e9c7..0000000000
--- a/share/lua/http/src/components/player/player.component.scss
+++ /dev/null
@@ -1,19 +0,0 @@
-#videoPlayer {
-    padding-right: 12%;
-    padding-left: 2%;
-    left:0;
-    top:0;
-    margin:0;
-    width:90%;
-}
-
- at media screen and (min-device-width: 320px) and (max-device-width: 480px) {
-    #videoPlayer {
-        padding-right: 12%;
-        padding-left: 2%;
-        top: initial;
-        left: initial;
-        margin:0 auto;
-        width:100%;
-    }
-}
diff --git a/share/lua/http/src/components/player/plyr.lib.custom.scss b/share/lua/http/src/components/player/plyr.lib.custom.scss
deleted file mode 100644
index 931e45c46b..0000000000
--- a/share/lua/http/src/components/player/plyr.lib.custom.scss
+++ /dev/null
@@ -1,758 +0,0 @@
-// ==========================================================================
-// Plyr styles
-// https://github.com/selz/plyr
-// ==========================================================================
-
- at import "plyr.lib.vars.scss";
- at import "../../scss/mixins.util.scss";
-
-// Animation
-// ---------------------------------------
- at keyframes plyr-progress {
-    to { background-position: $plyr-progress-loading-size 0; }
-}
-
-// Styles
-// -------------------------------
-// Base
-.plyr {
-    position: relative;
-    max-width: 100%;
-    min-width: 200px;
-    font-family: $plyr-font-family;
-    direction: ltr;
-
-    @if $plyr-border-box == true {
-        // border-box everything
-        // http://paulirish.com/2012/box-sizing-border-box-ftw/
-        &,
-        *,
-        *::after,
-        *::before {
-            box-sizing: border-box;
-        }
-    }
-
-    @if $plyr-touch-action == true {
-      // Fix 300ms delay
-      a, button, input, label {
-        touch-action: manipulation;
-      }
-    }
-
-    // Focus
-    &:focus {
-        outline: 0;
-    }
-
-    // Media elements
-    video,
-    audio {
-        width: 100%;
-        height: auto;
-        vertical-align: middle;
-        border-radius: inherit;
-    }
-
-    // Range inputs
-    // Specificity is for bootstrap compatibility
-    input[type='range'] {
-        display: block;
-        height: ($plyr-range-thumb-height * $plyr-range-thumb-active-scale);
-        width: 100%;
-        margin: 0;
-        padding: 0;
-        vertical-align: middle;
-
-        @include prefix(appearance, none);
-        cursor: pointer;
-        border: none;
-        background: transparent;
-
-        // WebKit
-        &::-webkit-slider-runnable-track {
-            @include plyr-range-track();
-        }
-        &::-webkit-slider-thumb {
-            @include prefix(appearance, none);
-            margin-top: -(($plyr-range-thumb-height - $plyr-range-track-height) / 2);
-            @include plyr-range-thumb();
-        }
-
-        // Mozilla
-        &::-moz-range-track {
-            @include plyr-range-track();
-        }
-        &::-moz-range-thumb {
-            @include plyr-range-thumb();
-        }
-
-        // Microsoft
-        &::-ms-track {
-            height: $plyr-range-track-height;
-            background: transparent;
-            border: 0;
-            color: transparent;
-        }
-        &::-ms-fill-upper {
-            @include plyr-range-track();
-        }
-        &::-ms-fill-lower {
-            @include plyr-range-track();
-            background: $plyr-range-selected-bg;
-        }
-        &::-ms-thumb {
-            @include plyr-range-thumb();
-            // For some reason, Edge uses the -webkit margin above
-            margin-top: 0;
-        }
-        &::-ms-tooltip {
-            display: none;
-        }
-
-        // Focus styles
-        &:focus {
-            outline: 0;
-        }
-        &::-moz-focus-outer {
-            border: 0;
-        }
-        &.tab-focus:focus {
-            outline-offset: 3px;
-        }
-
-        // Pressed styles
-        &:active {
-            &::-webkit-slider-thumb {
-                @include plyr-range-thumb-active();
-            }
-            &::-moz-range-thumb {
-                @include plyr-range-thumb-active();
-            }
-            &::-ms-thumb {
-                @include plyr-range-thumb-active();
-            }
-        }
-    }
-}
-
-// Video range inputs
-.plyr--video input[type='range'].tab-focus:focus {
-    outline: 1px dotted transparentize($plyr-video-control-color, .5);
-}
-
-// Audio range inputs
-.plyr--audio input[type='range'].tab-focus:focus {
-    outline: 1px dotted transparentize($plyr-audio-control-color, .5);
-}
-
-// Screen reader only elements
-.plyr__sr-only {
-    clip: rect(1px, 1px, 1px, 1px);
-    overflow: hidden;
-
-    // !important is not always needed
-    @if $plyr-sr-only-important == true {
-        position: absolute !important;
-        padding: 0 !important;
-        border: 0 !important;
-        height: 1px !important;
-        width: 1px !important;
-    } @else {
-        position: absolute;
-        padding: 0;
-        border: 0;
-        height: 1px;
-        width: 1px;
-    }
-}
-
-// Video
-.plyr__video-wrapper {
-    position: relative;
-    background: #000;
-    border-radius: inherit;
-}
-
-// Container for embeds
-.plyr__video-embed {
-    padding-bottom: 56.25%; /* 16:9 */
-    height: 0;
-    border-radius: inherit;
-
-    // Require overflow and z-index to force border-radius
-    overflow: hidden;
-    z-index: 0;
-
-    iframe {
-        position: absolute;
-        top: 0;
-        left: 0;
-        width: 100%;
-        height: 100%;
-        border: 0;
-        user-select: none;
-    }
-
-    // Vimeo hack
-    > div {
-        position: relative;
-        padding-bottom: 200%;
-        @include prefix(transform, translateY(-35.95%));
-    }
-}
-// To allow mouse events to be captured if full support
-.plyr .plyr__video-embed iframe {
-    pointer-events: none;
-}
-
-// Captions
-// --------------------------------------------------------------
-// Hide default captions
-.plyr video::-webkit-media-text-track-container {
-    display: none;
-}
-.plyr__captions {
-    display: none;
-    position: absolute;
-    bottom: 0;
-    left: 0;
-    width: 100%;
-    padding: ($plyr-control-spacing * 2);
-    @include prefix(transform, translateY(-($plyr-control-spacing * 6)));
-    transition: transform .3s ease;
-    color: $plyr-captions-color;
-    font-size: $plyr-font-size-captions-base;
-    text-align: center;
-    font-weight: 400;
-
-    span {
-        border-radius: 2px;
-        padding: floor($plyr-control-spacing / 3) $plyr-control-spacing;
-        background: $plyr-captions-bg;
-        box-decoration-break: clone;
-        line-height: 150%;
-    }
-    span:empty {
-        display: none;
-    }
-
-    @media (min-width: $plyr-bp-screen-md) {
-        font-size: $plyr-font-size-captions-medium;
-    }
-}
-.plyr--captions-active .plyr__captions {
-    display: block;
-}
-.plyr--hide-controls .plyr__captions {
-    @include prefix(transform, translateY(-($plyr-control-spacing * 2)));
-}
-// Large captions in full screen on larger screens
- at media (min-width: $plyr-bp-screen-lg) {
-    .plyr--fullscreen-active .plyr__captions {
-        font-size: $plyr-font-size-captions-large;
-    }
-}
-
-// Controls
-// --------------------------------------------------------------
-// Hide native controls
-.plyr ::-webkit-media-controls {
-    display: none;
-}
-
-// Playback controls
-.plyr__controls {
-    display: flex;
-    align-items: center;
-    line-height: 1;
-    text-align: center;
-
-    // Spacing
-    > button,
-    .plyr__progress,
-    .plyr__time {
-        margin-left: ($plyr-control-spacing / 2);
-
-        &:first-child {
-            margin-left: 0;
-        }
-    }
-    .plyr__volume {
-        margin-left: ($plyr-control-spacing / 2);
-    }
-    [data-plyr="pause"] {
-        margin-left: 0;
-    }
-
-    // Buttons
-    button {
-        position: relative;
-        display: inline-block;
-        flex-shrink: 0;
-        overflow: visible; // IE11
-        vertical-align: middle;
-        padding: ($plyr-control-spacing * .7);
-        border: 0;
-        background: transparent;
-        border-radius: 3px;
-        cursor: pointer;
-        transition: background .3s ease, color .3s ease, opacity .3s ease;
-        color: inherit;
-
-        svg {
-            width: $plyr-control-icon-size;
-            height: $plyr-control-icon-size;
-            display: block;
-            fill: currentColor;
-        }
-
-        // Default focus
-        &:focus {
-            outline: 0;
-        }
-    }
-
-    // Hide toggle icons by default
-    .icon--exit-fullscreen,
-    .icon--muted,
-    .icon--captions-on {
-        display: none;
-    }
-
-    @media (min-width: $plyr-bp-screen-sm) {
-        > button,
-        .plyr__progress,
-        .plyr__time {
-            margin-left: $plyr-control-spacing;
-        }
-    }
-}
-// Hide controls
-.plyr--hide-controls .plyr__controls {
-    opacity: 0;
-    pointer-events: none;
-}
-
-// Video controls
-.plyr--video .plyr__controls {
-    position: absolute;
-    left: 0;
-    right: 0;
-    bottom: 0;
-    padding: ($plyr-control-spacing * 5) $plyr-control-spacing $plyr-control-spacing;
-    background: linear-gradient(transparentize($plyr-video-controls-bg, 1), transparentize($plyr-video-controls-bg, .5));
-    border-bottom-left-radius: inherit;
-    border-bottom-right-radius: inherit;
-    color: $plyr-video-control-color;
-    transition: opacity .3s ease;
-
-    button {
-        // Hover and tab focus
-        &.tab-focus:focus,
-        &:hover {
-            background: $plyr-video-control-bg-hover;
-            color: $plyr-video-control-color-hover;
-        }
-    }
-}
-
-// Audio controls
-.plyr--audio .plyr__controls {
-    padding: $plyr-control-spacing;
-    border-radius: inherit;
-    background: $plyr-audio-controls-bg;
-    border: $plyr-audio-controls-border;
-    color: $plyr-audio-control-color;
-
-    button {
-        // Hover and tab focus
-        &.tab-focus:focus,
-        &:hover {
-            background: $plyr-audio-control-bg-hover;
-            color: $plyr-audio-control-color-hover;
-        }
-    }
-}
-
-// Large play button (video only)
-.plyr__play-large {
-    display: none;
-    position: absolute;
-    z-index: 1;
-    top: 50%;
-    left: 50%;
-    @include prefix(transform, translate(-50%, -50%));
-    padding: $plyr-control-spacing;
-    background: $plyr-video-control-bg-hover;
-    border: 4px solid currentColor;
-    border-radius: 100%;
-    box-shadow: 0 1px 1px transparentize(#000, .85);
-    color: $plyr-video-control-color;
-    transition: all .3s ease;
-
-    svg {
-        position: relative;
-        left: 2px;
-        width: 20px;
-        height: 20px;
-        display: block;
-        fill: currentColor;
-    }
-
-    &:focus {
-        outline: 1px dotted transparentize($plyr-video-control-color, .5);
-    }
-}
-.plyr .plyr__play-large {
-    display: inline-block;
-}
-.plyr--audio .plyr__play-large {
-    display: none;
-}
-.plyr--playing .plyr__play-large {
-    opacity: 0;
-    visibility: hidden;
-}
-
-// States
-.plyr__controls [data-plyr='pause'],
-.plyr--playing .plyr__controls [data-plyr='play'] {
-    display: none;
-}
-.plyr--playing .plyr__controls [data-plyr='pause'] {
-    display: inline-block;
-}
-
-// Change icons on state change
-.plyr--fullscreen-active .icon--exit-fullscreen,
-.plyr--muted .plyr__controls .icon--muted,
-.plyr--captions-active .plyr__controls .icon--captions-on {
-    display: block;
-
-    & + svg {
-        display: none;
-    }
-}
-
-// Some options are hidden by default
-.plyr [data-plyr='captions'],
-.plyr [data-plyr='fullscreen'] {
-    display: none;
-}
-.plyr--captions-enabled [data-plyr='captions'],
-.plyr--fullscreen-enabled [data-plyr='fullscreen'] {
-    display: inline-block;
-}
-
-// Tooltips
-// --------------------------------------------------------------
-.plyr__tooltip {
-    position: absolute;
-    z-index: 2;
-    bottom: 100%;
-    margin-bottom: ($plyr-tooltip-padding * 2);
-    padding: $plyr-tooltip-padding ($plyr-tooltip-padding * 1.5);
-    pointer-events: none;
-
-    opacity: 0;
-    background: $plyr-tooltip-bg;
-    border-radius: $plyr-tooltip-radius;
-
-    color: $plyr-tooltip-color;
-    font-size: $plyr-font-size-small;
-    line-height: 1.3;
-
-    @include prefix(transform, translate(-50%, 10px) scale(.8));
-    transform-origin: 50% 100%;
-    transition: transform .2s .1s ease, opacity .2s .1s ease;
-
-    &::before {
-        // Arrows
-        content: '';
-        position: absolute;
-        width: 0;
-        height: 0;
-        left: 50%;
-        @include prefix(transform, translateX(-50%));
-
-        // The background triangle
-        bottom: -$plyr-tooltip-arrow-size;
-        border-right: $plyr-tooltip-arrow-size solid transparent;
-        border-top: $plyr-tooltip-arrow-size solid $plyr-tooltip-bg;
-        border-left: $plyr-tooltip-arrow-size solid transparent;
-        z-index: 2;
-    }
-}
-.plyr button:hover .plyr__tooltip,
-.plyr button.tab-focus:focus .plyr__tooltip,
-.plyr__tooltip--visible {
-    opacity: 1;
-    @include prefix(transform, translate(-50%, 0) scale(1));
-}
-.plyr button:hover .plyr__tooltip {
-    z-index: 3;
-}
-
-// First tooltip
-.plyr__controls button:first-child .plyr__tooltip {
-    left: 0;
-    @include prefix(transform, translate(0, 10px) scale(.8));
-    transform-origin: 0 100%;
-
-    &::before {
-        left: ($plyr-control-icon-size / 2) + $plyr-control-padding;
-    }
-}
-
-// Last tooltip
-.plyr__controls button:last-child .plyr__tooltip {
-    right: 0;
-    @include prefix(transform, translate(0, 10px) scale(.8));
-    transform-origin: 100% 100%;
-
-    &::before {
-        left: auto;
-        right: ($plyr-control-icon-size / 2) + $plyr-control-padding;
-        @include prefix(transform, translateX(50%));
-    }
-}
-
-.plyr__controls button:first-child,
-.plyr__controls button:last-child {
-    &:hover .plyr__tooltip,
-    &.tab-focus:focus .plyr__tooltip,
-    .plyr__tooltip--visible {
-        @include prefix(transform, translate(0, 0) scale(1));
-    }
-}
-
-// Playback progress
-// --------------------------------------------------------------
-// <progress> element
-.plyr__progress {
-    display: none;
-    position: relative;
-    flex: 1;
-
-    input[type="range"] {
-        position: relative;
-        z-index: 2;
-
-        &::-webkit-slider-runnable-track {
-            background: transparent;
-        }
-        &::-moz-range-track {
-            background: transparent;
-        }
-        &::-ms-fill-upper {
-            background: transparent;
-        }
-    }
-
-    // Seek tooltip to show time
-    .plyr__tooltip {
-        left: 0;
-    }
-}
-.plyr .plyr__progress {
-    display: inline-block;
-}
-
-.plyr__progress--buffer,
-.plyr__progress--played,
-.plyr__volume--display {
-    position: absolute;
-    left: 0;
-    top: 50%;
-    width: 100%;
-    height: $plyr-range-track-height;
-    margin: -($plyr-range-track-height / 2) 0 0;
-    padding: 0;
-    vertical-align: top;
-    @include prefix(appearance, none);
-    border: none;
-    border-radius: 100px;
-
-    &::-webkit-progress-bar {
-        background: transparent;
-    }
-    &::-webkit-progress-value {
-        background: currentColor;
-        border-radius: 100px;
-        min-width: $plyr-range-track-height;
-    }
-    &::-moz-progress-bar {
-        background: currentColor;
-        border-radius: 100px;
-        min-width: $plyr-range-track-height;
-    }
-    &::-ms-fill {
-        border-radius: 100px;
-    }
-}
-.plyr__progress--played,
-.plyr__volume--display {
-    z-index: 1;
-    color: $plyr-range-selected-bg;
-    background: transparent;
-    transition: none;
-
-    &::-webkit-progress-value {
-        min-width: $plyr-range-track-height;
-        max-width: 99%;
-        border-top-right-radius: 0;
-        border-bottom-right-radius: 0;
-        transition: none;
-    }
-    &::-moz-progress-bar {
-        min-width: $plyr-range-track-height;
-        max-width: 99%;
-        border-top-right-radius: 0;
-        border-bottom-right-radius: 0;
-        transition: none;
-    }
-    &::-ms-fill {
-        display: none;
-    }
-}
-.plyr__progress--buffer {
-    &::-webkit-progress-value {
-        transition: width .2s ease;
-    }
-    &::-moz-progress-bar {
-        transition: width .2s ease;
-    }
-    &::-ms-fill {
-        transition: width .2s ease;
-    }
-}
-.plyr--video .plyr__progress--buffer,
-.plyr--video .plyr__volume--display {
-    background: $plyr-video-range-track-bg;
-}
-.plyr--video .plyr__progress--buffer {
-    color: $plyr-video-progress-buffered-bg;
-}
-.plyr--audio .plyr__progress--buffer,
-.plyr--audio .plyr__volume--display {
-    background: $plyr-audio-range-track-bg;
-}
-.plyr--audio .plyr__progress--buffer {
-    color: $plyr-audio-progress-buffered-bg;
-}
-
-// Loading state
-.plyr--loading .plyr__progress--buffer {
-    animation: plyr-progress 1s linear infinite;
-    background-size: $plyr-progress-loading-size $plyr-progress-loading-size;
-    background-repeat: repeat-x;
-    background-image: linear-gradient(
-        -45deg,
-        $plyr-progress-loading-bg 25%,
-        transparent 25%,
-        transparent 50%,
-        $plyr-progress-loading-bg 50%,
-        $plyr-progress-loading-bg 75%,
-        transparent 75%,
-        transparent);
-    color: transparent;
-}
-.plyr--video.plyr--loading .plyr__progress--buffer {
-    background-color: $plyr-video-progress-buffered-bg;
-}
-.plyr--audio.plyr--loading .plyr__progress--buffer {
-    background-color: $plyr-audio-progress-buffered-bg;
-}
-
-// Time
-// --------------------------------------------------------------
-.plyr__time {
-    display: inline-block;
-    vertical-align: middle;
-    font-size: $plyr-font-size-small;
-}
-// Media duration hidden on small screens
-.plyr__time + .plyr__time {
-    display: none;
-
-    @media (min-width: $plyr-bp-screen-md) {
-        display: inline-block;
-    }
-
-    // Add a slash in before
-    &::before {
-        content: '\2044';
-        margin-right: $plyr-control-spacing;
-    }
-}
-
-// Volume
-// --------------------------------------------------------------
-.plyr__volume {
-    display: none;
-}
-.plyr .plyr__volume {
-    flex: 1;
-    position: relative;
-
-    input[type="range"] {
-        position: relative;
-        z-index: 2;
-    }
-    @media (min-width: $plyr-bp-screen-sm) {
-        display: block;
-        max-width: 60px;
-    }
-    @media (min-width: $plyr-bp-screen-md) {
-        max-width: 100px;
-    }
-}
-
-// Hide sound controls on iOS
-// It's not supported to change volume using JavaScript:
-// https://developer.apple.com/library/safari/documentation/AudioVideo/Conceptual/Using_HTML5_Audio_Video/Device-SpecificConsiderations/Device-SpecificConsiderations.html
-.plyr--is-ios .plyr__volume,
-.plyr--is-ios [data-plyr='mute'] {
-    display: none !important;
-}
-
-// Fullscreen
-// --------------------------------------------------------------
-.plyr--fullscreen-active {
-    position: fixed;
-    top: 0;
-    left: 0;
-    right: 0;
-    bottom: 0;
-    height: 100%;
-    width: 100%;
-    z-index: 10000000;
-    background: #000;
-    border-radius: 0 !important;
-
-    video {
-        height: 100%;
-    }
-    .plyr__video-wrapper {
-        height: 100%;
-        width: 100%;
-    }
-    .plyr__video-embed {
-        // Revert overflow change
-        overflow: visible;
-    }
-    .plyr__controls {
-        position: absolute;
-        bottom: 0;
-        left: 0;
-        right: 0;
-    }
-
-    // Vimeo requires some different styling
-    &.plyr--vimeo .plyr__video-wrapper {
-        height: 0;
-        top: 50%;
-        @include prefix(transform, translateY(-50%));
-    }
-}
diff --git a/share/lua/http/src/components/player/plyr.lib.vars.scss b/share/lua/http/src/components/player/plyr.lib.vars.scss
deleted file mode 100644
index e8a3415f1c..0000000000
--- a/share/lua/http/src/components/player/plyr.lib.vars.scss
+++ /dev/null
@@ -1,71 +0,0 @@
-// ==========================================================================
-// Plyr variables
-// ==========================================================================
-
-// Settings
-$plyr-border-box:                       true !default;
-$plyr-touch-action:                     true !default;
-$plyr-sr-only-important:                true !default;
-
-// Colors
-$plyr-color-main:                       #FFA500 !default;
-
-// Font sizes
-$plyr-font-family:                      Avenir, 'Avenir Next', 'Helvetica Neue', 'Segoe UI', Helvetica, Arial, sans-serif !default;
-$plyr-font-size-small:                  14px !default;
-$plyr-font-size-base:                   16px !default;
-
-// Captions
-$plyr-captions-bg:                      transparentize(#000, .3) !default;
-$plyr-captions-color:                   #fff !default;
-$plyr-font-size-captions-base:          $plyr-font-size-base !default;
-$plyr-font-size-captions-medium:        ceil($plyr-font-size-base * 1.5) !default;
-$plyr-font-size-captions-large:         ($plyr-font-size-base * 2) !default;
-
-// Controls
-$plyr-control-icon-size:                18px !default;
-$plyr-control-spacing:                  10px !default;
-$plyr-control-padding:                  ($plyr-control-spacing * .7) !default;
-$plyr-video-controls-bg:                #000 !default;
-$plyr-video-control-color:              #fff !default;
-$plyr-video-control-color-hover:        #fff !default;
-$plyr-video-control-bg-hover:           $plyr-color-main !default;
-$plyr-audio-controls-bg:                #fff !default;
-$plyr-audio-controls-border:            1px solid #dbe3e8 !default;
-$plyr-audio-control-color:              #565D64 !default;
-$plyr-audio-control-color-hover:        #fff !default;
-$plyr-audio-control-bg-hover:           $plyr-color-main;
-
-// Tooltips
-$plyr-tooltip-bg:                       transparentize(#000, .3) !default;
-$plyr-tooltip-color:                    #fff !default;
-$plyr-tooltip-padding:                  ($plyr-control-spacing / 2) !default;
-$plyr-tooltip-arrow-size:               4px !default;
-$plyr-tooltip-radius:                   3px !default;
-
-// Progress
-$plyr-progress-loading-size:            25px !default;
-$plyr-progress-loading-bg:              transparentize(#000, .85) !default;
-$plyr-video-progress-bg:                transparentize(#fff, .75) !default;
-$plyr-video-progress-buffered-bg:       $plyr-video-progress-bg !default;
-$plyr-audio-progress-bg:                transparentize(#C6D6DB, .33) !default;
-$plyr-audio-progress-buffered-bg:       $plyr-audio-progress-bg !default;
-
-// Range sliders
-$plyr-range-track-height:               8px !default;
-$plyr-range-thumb-height:               floor($plyr-range-track-height * 2) !default;
-$plyr-range-thumb-width:                floor($plyr-range-track-height * 2) !default;
-$plyr-range-thumb-bg:                   #fff !default;
-$plyr-range-thumb-border:               2px solid transparent !default;
-$plyr-range-thumb-shadow:               0 1px 1px transparentize($plyr-video-controls-bg, .85), 0 0 0 1px transparentize(#000, .85) !default;
-$plyr-range-thumb-active-border-color:  #fff !default;
-$plyr-range-thumb-active-bg:            $plyr-video-control-bg-hover !default;
-$plyr-range-thumb-active-scale:         1.25 !default;
-$plyr-video-range-track-bg:             $plyr-video-progress-buffered-bg !default;
-$plyr-audio-range-track-bg:             $plyr-audio-progress-buffered-bg !default;
-$plyr-range-selected-bg:                $plyr-color-main !default;
-
-// Breakpoints
-$plyr-bp-screen-sm:                     480px !default;
-$plyr-bp-screen-md:                     768px !default;
-$plyr-bp-screen-lg:                     1024px !default;
diff --git a/share/lua/http/src/components/player/plyr.methods.js b/share/lua/http/src/components/player/plyr.methods.js
deleted file mode 100644
index b41d2dad39..0000000000
--- a/share/lua/http/src/components/player/plyr.methods.js
+++ /dev/null
@@ -1,41 +0,0 @@
-import { sendCommand } from '../../services/command.service.js';
-
-let player;
-
-export function plyrInit() {
-    player = plyr.setup({
-        showPosterOnEnd: true
-    });
-    // setVideo('S6IP_6HG2QE','youtube');
-    player[0].on('pause', () => {
-        sendCommand(0, 'command=pl_pause');
-    });
-
-    player[0].on('play', () => {
-        sendCommand(0, 'command=pl_play');
-    });
-
-    player[0].on('volumechange', (event) => {
-        const cmd = `command=volume&val=${event.detail.plyr.getVolume() * 255}`;
-        sendCommand(0, cmd);
-    });
-    return true;
-}
-
-export function setVideo(videoSrc, type) {
-    player[0].source({
-        type: 'video',
-        title: '',
-        sources: [{
-            src: videoSrc,
-            type
-        }],
-        poster: 'assets/vlc-icons/256x256/vlc.png'
-    });
-}
-
-export function playItem(src, id) {
-    setVideo(src, 'video/mp4', id);
-    player[0].play();
-    sendCommand(0, `command=pl_play&id=${id}`);
-}
diff --git a/share/lua/http/src/components/playlist/buttons.playlist.component.html b/share/lua/http/src/components/playlist/buttons.playlist.component.html
index fc0dae59d0..66c2a1e6ae 100644
--- a/share/lua/http/src/components/playlist/buttons.playlist.component.html
+++ b/share/lua/http/src/components/playlist/buttons.playlist.component.html
@@ -1,16 +1,12 @@
 <!-- Button Template -->
 <script type="text/x-template" id="button-template">
-    <div>
-        <div class="playlistIcons">
-            <div class="row">
-                <button type="button" class="btn-circle col-xs" id="randomButton"><svg-icon name="shuffle-variant"></svg-icon></button>
-                <button type="button" class="btn-circle col-xs" id="repeatButton"><svg-icon name="replay"></svg-icon></button>
-                <button type="button" class="btn-circle col-xs" id="playButton"><svg-icon name="play-circle-outline"></svg-icon></button>
-            </div>
-            <div class="row">
-                <button type="button" class="btn-circle col-xs" data-toggle="modal" data-target="#equalizerModal"><svg-icon name="poll"></svg-icon></button>
-                <button type="button" class="btn-circle col-xs" data-toggle="modal" data-target="#fileModal"><svg-icon name="plus"></svg-icon></button>
-            </div>
+    <div class="playlist-actions">
+        <div class="d-flex justify-content-between mx-2" :class="{ disabled: !playlist.activeItem }">
+            <button v-on:click="toggleRepeat()" type="button" class="btn-circle" id="repeatButton"><svg-icon name="repeat"></svg-icon></button>
+            <button v-on:click="toggleRandom()" type="button" class="btn-circle" id="randomButton"><svg-icon name="shuffle"></svg-icon></button>
+            <!-- <span>{{ playlist.items.length ? playlist.items.length + ' items': '' }}</span> -->
+            <button type="button" class="btn-circle" data-toggle="modal" data-target="#equalizerModal"><svg-icon name="sort"></svg-icon></button>
+            <button v-on:click="removeItem(playlist.activeItem.id)" type="button" class="btn-circle"><svg-icon name="delete_outline"></svg-icon></button>
         </div>
     </div>
 </script>
diff --git a/share/lua/http/src/components/playlist/buttons.playlist.component.js b/share/lua/http/src/components/playlist/buttons.playlist.component.js
index d5609d7424..e1256289e6 100644
--- a/share/lua/http/src/components/playlist/buttons.playlist.component.js
+++ b/share/lua/http/src/components/playlist/buttons.playlist.component.js
@@ -1,35 +1,26 @@
-import { bus, notifyBus } from '../../services/bus.service.js';
-import { sendCommand } from '../../services/command.service.js';
-
-let playlistItems;
-
 Vue.component('playlist-buttons', {
     template: '#button-template',
+    computed: {
+        ...Vuex.mapState({
+            status: state => state.status.data,
+            playlist: state => state.playlist
+        }),
+        ...Vuex.mapGetters('playlist', {
+            currentItem: 'getCurrentItem'
+        })
+    },
     methods: {
         toggleRepeat() {
-            sendCommand(0, 'command=pl_repeat');
+            this.$store.dispatch('status/toggleRepeat');
         },
         startPlaylist() {
-            playlistItems = this.$parent.$parent.$data.playlistItems;
-            if (playlistItems[0]) {
-                notifyBus('play', playlistItems[0].src,playlistItems[0].id);
-            }
+            this.$store.dispatch('status/play', this.status.currentplid);
         },
         toggleRandom() {
-            sendCommand(0, 'command=pl_random');
+            this.$store.dispatch('status/toggleRandom');
+        },
+        removeItem(id) {
+            this.$store.dispatch('playlist/removeItem', id);
         }
-    },
-    created() {
-        bus.$on('toggleRepeat', () => {
-            this.toggleRepeat();
-        });
-
-        bus.$on('startPlaylist', () => {
-            this.startPlaylist();
-        });
-
-        bus.$on('toggleRandom', () => {
-            this.toggleRandom();
-        });
     }
 });
diff --git a/share/lua/http/src/components/playlist/buttons.playlist.component.scss b/share/lua/http/src/components/playlist/buttons.playlist.component.scss
new file mode 100644
index 0000000000..5e44e510a3
--- /dev/null
+++ b/share/lua/http/src/components/playlist/buttons.playlist.component.scss
@@ -0,0 +1,10 @@
+.playlist-actions {
+    padding: 5px 8px;
+    background-color: var(--playlist-action-bg-color);
+    .disabled {
+        color: var(--playlist-action-text-color);
+        .svg-icon {
+            color: var(--playlist-action-text-color);
+        }
+    }
+}
\ No newline at end of file
diff --git a/share/lua/http/src/components/playlist/playlist-audio-item.component.html b/share/lua/http/src/components/playlist/playlist-audio-item.component.html
new file mode 100644
index 0000000000..72f240de4f
--- /dev/null
+++ b/share/lua/http/src/components/playlist/playlist-audio-item.component.html
@@ -0,0 +1,27 @@
+<!-- Playlist item Template -->
+<script type="text/x-template" id="playlist-audio-item-template">
+    <button v-on:dblclick="setActiveItem(item)" class="btn btn-link pl-2 playlist-audio-item list-item d-flex justify-content-between align-items-start position-relative"
+        :class="{ current: item.current === 'current', active: item.active }">
+        <div class="d-flex align-items-start w-100 mr-n3">
+            <div class="item-img" v-on:click="setActiveItem(item)">
+                <img v-if="item.src" class="img-art" :src="item.src" @error="onImgError(item)">
+                <svg-icon v-if="!item.src" name="placeholder-album" class="placeholder-album-icon"></svg-icon>
+                <div class="overlay">
+                    <play-button class="item-primary-action item-action"></play-button>
+                </div>
+            </div>
+            <span class="item-duration">{{ item.duration | formatDuration }}</span>
+            <!-- <span class="item-nb">{{ index + 1 }}</span> -->
+            <div class="item-body d-flex flex-column mx-2 pt-1">
+                <router-link to="" class="item-title text-truncate">{{ item.name }}</router-link>
+                <!-- <span class="item-subtitle text-truncate d-flex">
+                    <router-link to="/artist">{{ item.subtitle }}</router-link> • <router-link to="/album">{{ item.album }}</router-link>
+                </span> -->
+            </div>
+        </div>
+        <div class="d-flex item-action pt-1">
+            <svg-icon name="more_vert"></svg-icon>
+        </div>
+        <!-- <div class="item-progress-bar" :style="{width: item.progress}"></div> -->
+    </button>
+</script>
diff --git a/share/lua/http/src/components/playlist/playlist-audio-item.component.js b/share/lua/http/src/components/playlist/playlist-audio-item.component.js
new file mode 100644
index 0000000000..de82597d11
--- /dev/null
+++ b/share/lua/http/src/components/playlist/playlist-audio-item.component.js
@@ -0,0 +1,23 @@
+Vue.component('playlist-audio-item', {
+    template: '#playlist-audio-item-template',
+    props: ['item', 'index'],
+    methods: {
+        addItem(mode, id, title, src) {
+            this.$store.dispatch('playlist/addItem', src);
+        },
+        play(src, id) {
+            this.$store.dispatch('layout/openAudioPlayer');
+            this.$store.dispatch('status/play', id);
+        },
+        onImgError(item) {
+            item.src = '';
+        },
+        setActiveItem(item) {
+            this.$store.dispatch('layout/openAudioPlayer');
+            this.$store.dispatch('playlist/setActiveItem', item);
+        }
+    },
+    created() {
+
+    }
+});
diff --git a/share/lua/http/src/components/playlist/playlist-audio-item.component.scss b/share/lua/http/src/components/playlist/playlist-audio-item.component.scss
new file mode 100644
index 0000000000..7625d63a0e
--- /dev/null
+++ b/share/lua/http/src/components/playlist/playlist-audio-item.component.scss
@@ -0,0 +1,24 @@
+.playlist-audio-item.list-item {
+    text-align: center;
+    border: 0;
+    border-radius: 0;
+    margin-bottom: 8px;
+    width: 100%;
+    &.active {
+        background-color: var(--playlist-active-bg-color);
+        color: var(--playlist-active-color);
+    }
+    &:focus,
+    &:hover {
+        text-decoration: none;
+    }
+    .item-img {
+        width: 52px;
+        height: 52px;
+        border-radius: 6px;
+        overflow: hidden;
+    }
+    .item-duration {
+        bottom: 3px;
+    }
+}
diff --git a/share/lua/http/src/components/playlist/playlist-video-item.component.html b/share/lua/http/src/components/playlist/playlist-video-item.component.html
new file mode 100644
index 0000000000..7cd5b02121
--- /dev/null
+++ b/share/lua/http/src/components/playlist/playlist-video-item.component.html
@@ -0,0 +1,25 @@
+<!-- Playlist item Template -->
+<script type="text/x-template" id="playlist-video-item-template">
+    <button v-on:dblclick="play(item.uri, item.id)" class="btn btn-link pl-2 playlist-video-item list-item d-flex justify-content-between align-items-start position-relative"
+        :class="{ current: item.current === 'current', active: item.active }">
+        <div class="d-flex align-items-start w-100 mr-n3">
+            <div class="item-img" v-on:click="setActiveItem(item)">
+                <img v-if="item.src" class="img-art" :src="item.src" @error="onImgError(item)">
+                <svg-icon v-if="!item.src" name="placeholder-album" class="placeholder-album-icon"></svg-icon>
+                <div class="overlay">
+                    <play-button :item="item" class="item-primary-action item-action"></play-button>
+                </div>
+            </div>
+            <span class="item-duration">{{ item.duration | formatDuration }}</span>
+            <!-- <span class="item-nb">{{ index + 1 }}</span> -->
+            <div class="item-body d-flex flex-column mx-2 pt-1">
+                <router-link to="" class="item-title text-truncate">{{ item.name }}</router-link>
+                <router-link to="" class="item-subtitle text-truncate">{{ item.subtitle }}</router-link>
+            </div>
+        </div>
+        <div class="d-flex item-action pt-1">
+            <svg-icon name="more_vert"></svg-icon>
+        </div>
+        <div class="item-progress-bar" :style="{width: item.progress}"></div>
+    </button>
+</script>
diff --git a/share/lua/http/src/components/playlist/playlist-video-item.component.js b/share/lua/http/src/components/playlist/playlist-video-item.component.js
new file mode 100644
index 0000000000..7bef7bc539
--- /dev/null
+++ b/share/lua/http/src/components/playlist/playlist-video-item.component.js
@@ -0,0 +1,31 @@
+Vue.component('playlist-video-item', {
+    template: '#playlist-video-item-template',
+    props: ['item', 'index'],
+    computed: {
+        ...Vuex.mapState({
+            playlist: state => state.playlist
+        }),
+    },
+    methods: {
+        addItem(mode, id, title, src) {
+            this.$store.dispatch('playlist/addItem', src);
+        },
+        play(src, id) {
+            this.$store.dispatch('status/play', id);
+        },
+        onImgError(item) {
+            item.src = '';
+        },
+        setActiveItem(item) {
+            const path = '/watch'
+            if (this.$route.path !== path) {
+                this.$router.push(path);
+            }
+            this.$store.dispatch('layout/closeAudioPlayer');
+            this.$store.dispatch('playlist/setActiveItem', item);
+        }
+    },
+    created() {
+
+    }
+});
diff --git a/share/lua/http/src/components/playlist/playlist-video-item.component.scss b/share/lua/http/src/components/playlist/playlist-video-item.component.scss
new file mode 100644
index 0000000000..e37f18394f
--- /dev/null
+++ b/share/lua/http/src/components/playlist/playlist-video-item.component.scss
@@ -0,0 +1,24 @@
+.playlist-video-item {
+    text-align: center;
+    border: 0;
+    border-radius: 0;
+    margin-bottom: 8px;
+    width: 100%;
+    &.current {
+        // font-weight: bold;
+    }
+    &.active {
+        background-color: var(--playlist-active-bg-color);
+        color: var(--playlist-active-color);
+    }
+    &:focus,
+    &:hover {
+        text-decoration: none;
+        color: #fff;
+    }
+    .item-img {
+        width: 120px;
+        height: 70px;
+        object-fit: cover;
+    }
+}
diff --git a/share/lua/http/src/components/playlist/playlist.component.html b/share/lua/http/src/components/playlist/playlist.component.html
index 86137bf985..7b65192713 100644
--- a/share/lua/http/src/components/playlist/playlist.component.html
+++ b/share/lua/http/src/components/playlist/playlist.component.html
@@ -1,27 +1,23 @@
 <!-- Templates -->
 <!-- Playlist Template -->
 <script type="text/x-template" id="playlist-template">
-    <div>
-        <div id="playlistNav" class="playlistNav">
-            <div class="container">
-                <playlist-buttons></playlist-buttons>
-                <div>
-                    <h1 class="playlistHeader">Playlist</h1>
-                </div>
-                <div id="playlist">
-                    <ol type="1">
-                        <li v-for="item in this.$parent.playlistItems">
-                            <button v-on:click="play(item.src, item.id)" class="playlistItem">{{ item.title }}</button>
-                            <button v-on:click="removeItem(item.id)" type="button" class="btn-circle col-xs">
-                                <svg-icon name="delete"></svg-icon>
-                            </button>
-                        </li>
-                    </ol>
-                </div>
-                <div class="playlistNavMobile" id="mobilePlaylistNavButton">
-                    <center><svg-icon name="format-list-bulleted"></svg-icon></center>
-                </div>
+    <div id="playlistNav" class="playlistNav h-100">
+        <h6 class="playlist-header">Playlist • <span class="text-muted">{{ ' ' + items.length }}{{ items.length ? ' elements' : ''}}</span></h6>
+        <div class="dropdown-divider"></div>
+        <div id="playlist">
+            <div v-for="(item, index) in items" :key="index">
+                <playlist-video-item
+                    v-if="item.type === 'leaf' || item.type === 'video'"
+                    :item="item"
+                    :index="index">
+                </playlist-video-item>
+                <playlist-audio-item
+                    v-if="item.type === 'audio'"
+                    :item="item"
+                    :index="index">
+                </playlist-audio-item>
             </div>
         </div>
+        <playlist-buttons></playlist-buttons>
     </div>
 </script>
diff --git a/share/lua/http/src/components/playlist/playlist.component.js b/share/lua/http/src/components/playlist/playlist.component.js
index 7733d40eca..3a928d7a15 100644
--- a/share/lua/http/src/components/playlist/playlist.component.js
+++ b/share/lua/http/src/components/playlist/playlist.component.js
@@ -1,102 +1,36 @@
-import { bus } from '../../services/bus.service.js';
-import { sendCommand } from '../../services/command.service.js';
-import { playItem } from '../player/plyr.methods.js';
-
 Vue.component('playlist', {
     template: '#playlist-template',
+    computed: {
+        ...Vuex.mapState({
+            items: state => state.playlist.items,
+        }),
+    },
     methods: {
-        addItem(mode, id, title, src) {
-            if (mode === 0) {
-                this.$parent.playlistItems.push({
-                    id,
-                    title,
-                    src
-                });
-            } else if (mode === 1) {
-                sendCommand(0, `command=in_enqueue&input=${src}`);
-                this.clearPlaylist();
-                bus.$emit('refreshPlaylist');
-            }
-        },
-        removeItem(id) {
-            sendCommand(0, `command=pl_delete&id=${id}`);
-            this.$parent.playlistItems.splice({
-                id
-            });
-            sendCommand(1);
-        },
-        openPlaylist() {
-            $('#playlistNav').width('60%');
-            $('#mobilePlaylistNavButton').width('0%');
-        },
-        closePlaylist() {
-            $('#playlistNav').width('0%');
-            $('#mobilePlaylistNavButton').width('10%');
-        },
-        populatePlaylist(playlistData) {
-            if (!playlistData) {
-                return;
-            }
-            this.$parent.playlistItems = [];
-            for (let i = 0; i < playlistData.children[0].children.length; i++) {
-                this.addItem(
-                    0,
-                    playlistData.children[0].children[i].id,
-                    playlistData.children[0].children[i].name,
-                    playlistData.children[0].children[i].uri
-                );
-            }
-        },
         fetchPlaylist() {
-            sendCommand(1)
-                .then(data => this.populatePlaylist(data));
+            this.$store.dispatch('playlist/fetchPlaylist');
         },
         refreshPlaylist() {
+            if (this.interval) {
+                clearTimeout(this.interval);
+                this.interval = null;
+            }
             this.fetchPlaylist();
-            setInterval(() => {
+            this.interval = setInterval(() => {
                 this.fetchPlaylist();
-            }, 5000);
-        },
-        clearPlaylist() {
-            this.$parent.playlistItems = [];
-        },
-        play(src, id) {
-            playItem(src,id);
+            }, 30000);
         }
     },
     created() {
-        bus.$on('openPlaylist', () => {
-            this.openPlaylist();
-        });
-
-        bus.$on('closePlaylist', () => {
-            this.closePlaylist();
-        });
-
-        bus.$on('addItem', (params) => {
-            this.addItem(params[0], params[1], params[2], params[3]);
-        });
-
-        bus.$on('removeItem', (id) => {
-            this.removeItem(id);
-        });
-
-        bus.$on('refreshPlaylist', () => {
-            this.refreshPlaylist();
-        });
-
-        bus.$on('play', (arg) => {
-            this.play(arg[0], arg[1]);
+        this.$store.subscribeAction((mutation) => {
+            switch (mutation.type) {
+                case 'layout/openPlaylist':
+                    this.openPlaylist();
+                    break;
+                case 'layout/closePlaylist':
+                    this.closePlaylist();
+                    break;
+            }
         });
-
         this.refreshPlaylist();
     }
 });
-
-$(document).click((e) => {
-    const container = $('#playlistNav');
-
-    if ($(window).width() <= 480 && !container.is(e.target) && container.has(e.target).length === 0 && container.css('width') !== '0px') {
-        bus.$emit('closePlaylist');
-    }
-});
diff --git a/share/lua/http/src/components/playlist/playlist.component.scss b/share/lua/http/src/components/playlist/playlist.component.scss
index 6ae49d3cfe..3b4772f548 100644
--- a/share/lua/http/src/components/playlist/playlist.component.scss
+++ b/share/lua/http/src/components/playlist/playlist.component.scss
@@ -1,43 +1,19 @@
 .playlistNav {
-    right: 0;
-    width: 20%;
-    height: 100%;
-    position: fixed;
-    z-index: 5;
-    top: 0;
-    background-color: orange;
-    overflow-x: hidden;
-    padding-top: 2%;
-    transition: 0.5s;
-}
-
-.playlistIcons {
-    font-size: 2em;
-    padding-left: 4%;
-}
-
-.playlistNavMobile {
-    visibility: hidden;
-    right: 0;
-    width: 10%;
-    height: 100%;
-    position: fixed;
-    z-index: 5;
-    top: 0;
-    background-color: orange;
-    overflow-x: hidden;
-    padding-top: 2%;
+    overflow: hidden;
     transition: 0.5s;
 }
 
 .btn-circle {
-    width: 30px;
-    height: 30px;
     text-align: center;
-    padding: 6px 0;
+    padding: 0;
     border-radius: 15px;
     border: 0;
-    background-color: orange;
+    background-color: transparent;
+    line-height: 0;
+    .svg-icon {
+        width: 1.5rem;
+        height: 1.5rem;
+    }
 }
 
 .btn-circle.btn-lg {
@@ -56,23 +32,20 @@
     border-radius: 35px;
 }
 
-.playlistItem {
-    text-align: center;
-    border: 0;
-    margin-bottom: 0.2em;
-    background-color: white;
+#playlistNav {
+    background-color: var(--sidenav-bg-color);
 }
 
-.playlistHeader{
-    color: black;
-    padding-left: 5%;
-    padding-right: 5%;
+#playlist {
+    // max-height: calc(100% - 120px);
+    height: calc(100% - 160px);
+    overflow-y: auto;
+    overflow-x: hidden;
 }
 
-#playlist {
-    width:21.5%;
-    height: 100%;
-    background-color: white;
+.playlist-header {
+    margin: 8px 8px;
+    font-size: 13px;
 }
 
 @media screen and (min-device-width : 320px) and (max-device-width : 480px) {
diff --git a/share/lua/http/src/components/playlists/playlists-item/playlists-item.component.html b/share/lua/http/src/components/playlists/playlists-item/playlists-item.component.html
new file mode 100644
index 0000000000..a733a239ef
--- /dev/null
+++ b/share/lua/http/src/components/playlists/playlists-item/playlists-item.component.html
@@ -0,0 +1,28 @@
+<script type="text/x-template" id="playlists-item-template">
+    <div class="playlists-item grid-item col-12 col-sm-6 col-md-4 col-lg-3 col-xl-2">
+        <div class="item-img">
+            <div>
+                <div class="d-flex">
+                    <img :src="item.images[0]">
+                    <img :src="item.images[1]">
+                </div>
+                <div class="d-flex">
+                    <img :src="item.images[2]">
+                    <img :src="item.images[3]">
+                </div>
+            </div>
+            <div class="overlay">
+                <play-button class="item-primary-action"></play-button>
+                <div class="item-secondary-action">
+                    <div class="icon-wrapper">
+                        <svg-icon name="more-horizontal"></svg-icon>
+                    </div>
+                </div>
+            </div>
+        </div>
+        <div class="item-body">
+            <router-link to="/album" class="item-title text-truncate">{{ item.name }}</router-link>
+            <router-link to="/artist" class="item-subtitle text-truncate">{{ item.nbItems }} files • {{ item.duration }}</router-link>
+        </div>
+    </div>
+</script>
diff --git a/share/lua/http/src/components/playlists/playlists-item/playlists-item.component.js b/share/lua/http/src/components/playlists/playlists-item/playlists-item.component.js
new file mode 100644
index 0000000000..8022d6ca9c
--- /dev/null
+++ b/share/lua/http/src/components/playlists/playlists-item/playlists-item.component.js
@@ -0,0 +1,7 @@
+Vue.component('playlists-item', {
+    template: '#playlists-item-template',
+    props: ['item'],
+    computed: { },
+    methods: { },
+    created() { }
+});
diff --git a/share/lua/http/src/components/playlists/playlists-item/playlists-item.component.scss b/share/lua/http/src/components/playlists/playlists-item/playlists-item.component.scss
new file mode 100644
index 0000000000..d7cc3e1b80
--- /dev/null
+++ b/share/lua/http/src/components/playlists/playlists-item/playlists-item.component.scss
@@ -0,0 +1,10 @@
+.playlists-item.grid-item {
+    margin-bottom: 16px;
+    img {
+        width: 50%;
+        height: 50%;
+    }
+    .item-img {
+        display: block;
+    }
+}
diff --git a/share/lua/http/src/components/playlists/playlists.component.html b/share/lua/http/src/components/playlists/playlists.component.html
new file mode 100644
index 0000000000..5e848f6bf5
--- /dev/null
+++ b/share/lua/http/src/components/playlists/playlists.component.html
@@ -0,0 +1,13 @@
+<script type="text/x-template" id="playlists-template">
+    <div class="playlists-view container-fluid">
+        <div class="row">
+            <div class="playlists-item grid-item col-12 col-sm-6 col-md-4 col-lg-3 col-xl-2">
+                <div class="create-item">
+                    Create a playlist
+                    <svg-icon name="add"></svg-icon>
+                </div>
+            </div>
+            <playlists-item v-for="item in items" :item="item"></playlists-item>
+        </div>
+    </div>
+</script>
diff --git a/share/lua/http/src/components/playlists/playlists.component.js b/share/lua/http/src/components/playlists/playlists.component.js
new file mode 100644
index 0000000000..4ac085299b
--- /dev/null
+++ b/share/lua/http/src/components/playlists/playlists.component.js
@@ -0,0 +1,19 @@
+Vue.component('playlists', {
+    template: '#playlists-template',
+    data: function() {
+        return {
+            items: [
+
+            ]
+        }
+    },
+    computed: {
+
+    },
+    methods: {
+
+    },
+    created() {
+
+    }
+});
diff --git a/share/lua/http/src/components/sot/stream.manager.component.html b/share/lua/http/src/components/sot/stream.manager.component.html
index 9813fe2f65..3619de0633 100644
--- a/share/lua/http/src/components/sot/stream.manager.component.html
+++ b/share/lua/http/src/components/sot/stream.manager.component.html
@@ -5,8 +5,8 @@
             <!-- Modal content-->
             <div class="modal-content">
                 <div class="modal-header">
-                    <button type="button" class="close" data-dismiss="modal">×</button>
                     <h4 class="modal-title">Stream Manager</h4>
+                    <button type="button" class="close" data-dismiss="modal">×</button>
                 </div>
                 <div class="modal-body">
                 </div>
diff --git a/share/lua/http/src/components/svg-icon/svg-icon.component.scss b/share/lua/http/src/components/svg-icon/svg-icon.component.scss
index 0c6710d4b2..2761b95125 100644
--- a/share/lua/http/src/components/svg-icon/svg-icon.component.scss
+++ b/share/lua/http/src/components/svg-icon/svg-icon.component.scss
@@ -1,7 +1,7 @@
 .svg-icon {
     display: inline-block;
-    width: 2.5rem;
-    height: 2.5rem;
+    width: 1.5rem;
+    height: 1.5rem;
     color: inherit;
     vertical-align: middle;
     fill: none;
diff --git a/share/lua/http/src/components/track-synchronisation/tracksync.component.html b/share/lua/http/src/components/track-synchronisation/tracksync.component.html
index 4ae724b99f..f66d4387b2 100644
--- a/share/lua/http/src/components/track-synchronisation/tracksync.component.html
+++ b/share/lua/http/src/components/track-synchronisation/tracksync.component.html
@@ -5,8 +5,8 @@
             <!-- Modal content-->
             <div class="modal-content">
                 <div class="modal-header">
-                    <button type="button" class="close" data-dismiss="modal">×</button>
                     <h4 class="modal-title">Set Track Synchronization</h4>
+                    <button type="button" class="close" data-dismiss="modal">×</button>
                 </div>
                 <div class="modal-body" id="">
                     Playback Rate
diff --git a/share/lua/http/src/components/tracks/track-item/track-item-table.component.html b/share/lua/http/src/components/tracks/track-item/track-item-table.component.html
new file mode 100644
index 0000000000..82502ded40
--- /dev/null
+++ b/share/lua/http/src/components/tracks/track-item/track-item-table.component.html
@@ -0,0 +1,28 @@
+<script type="text/x-template" id="track-item-table-template">
+  <tr>
+    <th scope="row" class="d-flex justify-content-between">
+      <div class="item-img">
+        <img v-if="item.src" :src="item.src">
+        <svg-icon v-if="!item.src" name="placeholder-album" class="placeholder-album-icon"></svg-icon>
+        <play-button class="item-primary-action item-action" :item="item"></play-button>
+      </div>
+    </th>
+    <td>
+      <router-link to="">
+        <span>{{ item.title }}</span>
+        <span></span>
+      </router-link>
+    </td>
+    <td><router-link to="">{{ item.artist }}</router-link></td>
+    <td><router-link to="">{{ item.album }}</router-link></td>
+    <td class="text-muted">{{ item.duration | formatDuration }}</td>
+    <td class="text-muted">{{ index + 1 }}</td>
+    <td class="text-muted">
+      <div class="d-flex justify-content-between">
+        <div class="item-action">
+          <svg-icon name="more-horizontal"></svg-icon>
+        </div>
+      </div>
+    </td>
+  </tr>
+</script>
\ No newline at end of file
diff --git a/share/lua/http/src/components/tracks/track-item/track-item-table.component.js b/share/lua/http/src/components/tracks/track-item/track-item-table.component.js
new file mode 100644
index 0000000000..ebc8d4bf50
--- /dev/null
+++ b/share/lua/http/src/components/tracks/track-item/track-item-table.component.js
@@ -0,0 +1,16 @@
+Vue.component('track-item-table', {
+    template: '#track-item-table-template',
+    props: ['item', 'index'],
+    data: function() {
+        return {
+            liked: this.item.liked
+        }
+    },
+    computed: { },
+    methods: {
+        toggleLiked() {
+            this.liked = !this.liked;
+        }
+    },
+    created() { }
+});
diff --git a/share/lua/http/src/components/tracks/tracks.component.html b/share/lua/http/src/components/tracks/tracks.component.html
new file mode 100644
index 0000000000..fe8a5e5bd0
--- /dev/null
+++ b/share/lua/http/src/components/tracks/tracks.component.html
@@ -0,0 +1,25 @@
+<script type="text/x-template" id="tracks-template">
+    <div class="tracks-view container-fluid">
+        <!-- <div v-if="displayMode === 'list'" class="row">
+            <artist-item-list v-for="item in items" :item="item"></artist-item-list>
+        </div> -->
+        <div class="row" v-if="displayMode === 'table'">
+            <table class="table table-hover table-item">
+                <thead>
+                  <tr>
+                    <th scope="col"></th>
+                    <th scope="col">Title</th>
+                    <th scope="col">Artist</th>
+                    <th scope="col">Album</th>
+                    <th scope="col">Duration</th>
+                    <th scope="col">Track</th>
+                    <th scope="col"></th>
+                  </tr>
+                </thead>
+                <tbody>
+                    <track-item-table v-for="(item, index) in music.tracks" :item="item" :key="index" :index="index"></track-item-table>
+                </tbody>
+            </table>
+        </div>
+    </div>
+</script>
diff --git a/share/lua/http/src/components/tracks/tracks.component.js b/share/lua/http/src/components/tracks/tracks.component.js
new file mode 100644
index 0000000000..60ea02514a
--- /dev/null
+++ b/share/lua/http/src/components/tracks/tracks.component.js
@@ -0,0 +1,22 @@
+Vue.component('tracks', {
+    template: '#tracks-template',
+    props: ['item'],
+    data: function() {
+        return {
+            displayMode: 'table'
+        }
+    },
+    computed: {
+        ...Vuex.mapState({
+            music: state => state.music,
+        }),
+    },
+    methods: {
+        fetchTracks() {
+            this.$store.dispatch('music/fetchTracks');
+        }
+    },
+    created() {
+        this.fetchTracks();
+    }
+});
diff --git a/share/lua/http/src/components/videos/video-item/video-item-grid.component.html b/share/lua/http/src/components/videos/video-item/video-item-grid.component.html
new file mode 100644
index 0000000000..becf714748
--- /dev/null
+++ b/share/lua/http/src/components/videos/video-item/video-item-grid.component.html
@@ -0,0 +1,33 @@
+<script type="text/x-template" id="video-item-grid-template">
+    <div class="video-item grid-item video-item-grid-template">
+        <div class="item-img embed-responsive embed-responsive-16by10">
+            <img class="embed-responsive-item" :src="item.src">
+            <div class="overlay">
+                <play-button :item="item" class="item-primary-action"></play-button>
+                <div class="item-secondary-action">
+                    <div class="icon-wrapper">
+                        <svg-icon name="more-horizontal"></svg-icon>
+                    </div>
+                </div>
+            </div>
+            <div v-if="item.quality" class="quality">
+                <span class="quality-tag">{{ item.quality }}</span>
+            </div>
+            <div v-if="item.user" class="user">
+                <img src="">
+            </div>
+            <div v-if="item.progress" class="item-progress-bar-container">
+                <div class="item-progress-bar" :style="{width: item.progress}"></div>
+            </div>
+        </div>
+        <div class="item-body px-2">
+            <span class="d-flex align-items-center justify-content-between">
+                <router-link to="/album" class="item-title text-truncate">
+                    <span>{{ item.title }}</span>
+                </router-link>
+                <span class="item-subtitle">{{ item.duration | formatDuration }}</span>
+            </span>
+            <router-link to="/" class="item-subtitle text-truncate text-left">{{ item.subtitle }}</router-link>
+        </div>
+    </div>
+</script>
diff --git a/share/lua/http/src/components/videos/video-item/video-item-grid.component.js b/share/lua/http/src/components/videos/video-item/video-item-grid.component.js
new file mode 100644
index 0000000000..ef710235dd
--- /dev/null
+++ b/share/lua/http/src/components/videos/video-item/video-item-grid.component.js
@@ -0,0 +1,9 @@
+Vue.component('video-item-grid', {
+    template: '#video-item-grid-template',
+    props: ['item'],
+    methods: {
+        toggleLiked() {
+            this.liked = !this.liked;
+        }
+    }
+});
diff --git a/share/lua/http/src/components/videos/video-item/video-item-list.component.html b/share/lua/http/src/components/videos/video-item/video-item-list.component.html
new file mode 100644
index 0000000000..e1a029c0b8
--- /dev/null
+++ b/share/lua/http/src/components/videos/video-item/video-item-list.component.html
@@ -0,0 +1,20 @@
+<script type="text/x-template" id="video-item-list-template">
+    <div class="row align-items-center list-table-item">
+      <div class="d-flex justify-content-between col-2">
+        <div class="item-img img-16by9 d-flex" :style="{'background-image': `url(${item.src})`}">
+          <play-button class="item-primary-action item-action m-auto" :item="item"></play-button>
+        </div>
+      </div>
+      <div class="col-6">
+        <span>{{ item.title }}</span>
+      </div>
+      <div class="col-2 text-center">{{ item.duration | formatDuration }}</div>
+      <div class="text-muted col-2">
+        <div class="d-flex justify-content-between">
+          <div class="item-action">
+            <svg-icon name="more-horizontal"></svg-icon>
+          </div>
+        </div>
+      </div>
+    </div>
+</script>
diff --git a/share/lua/http/src/components/videos/video-item/video-item-list.component.js b/share/lua/http/src/components/videos/video-item/video-item-list.component.js
new file mode 100644
index 0000000000..629bee7f52
--- /dev/null
+++ b/share/lua/http/src/components/videos/video-item/video-item-list.component.js
@@ -0,0 +1,9 @@
+Vue.component('video-item-list', {
+    template: '#video-item-list-template',
+    props: ['item'],
+    methods: {
+        toggleLiked() {
+            this.liked = !this.liked;
+        }
+    }
+});
diff --git a/share/lua/http/src/components/videos/video-item/video-item-poster.component.html b/share/lua/http/src/components/videos/video-item/video-item-poster.component.html
new file mode 100644
index 0000000000..5b4c51227e
--- /dev/null
+++ b/share/lua/http/src/components/videos/video-item/video-item-poster.component.html
@@ -0,0 +1,27 @@
+<script type="text/x-template" id="video-item-poster-template">
+    <div class="video-item grid-item poster-item video-item-poster-template">
+        <div class="item-img embed-responsive embed-responsive-4by6">
+            <img class="embed-responsive-item" :src="item.src">
+            <div class="overlay">
+                <play-button class="item-primary-action" :item="item"></play-button>
+                <div class="item-secondary-action">
+                    <div class="icon-wrapper">
+                        <svg-icon name="more-horizontal"></svg-icon>
+                    </div>
+                </div>
+            </div>
+            <div v-if="item.progress" class="item-progress-bar-container">
+                <div class="item-progress-bar" :style="{width: item.progress}"></div>
+            </div>
+        </div>
+        <div class="item-body px-2">
+            <span class="d-flex align-items-center justify-content-between">
+                <router-link to="/album" class="item-title text-truncate">
+                    <span>{{ item.title }}</span>
+                </router-link>
+                <span class="item-subtitle">{{ item.duration | formatDuration }}</span>
+            </span>
+            <router-link to="/" class="item-subtitle text-truncate text-left">{{ item.subtitle }}</router-link>
+        </div>
+    </div>
+</script>
diff --git a/share/lua/http/src/components/videos/video-item/video-item-poster.component.js b/share/lua/http/src/components/videos/video-item/video-item-poster.component.js
new file mode 100644
index 0000000000..febb7d4c1c
--- /dev/null
+++ b/share/lua/http/src/components/videos/video-item/video-item-poster.component.js
@@ -0,0 +1,4 @@
+Vue.component('video-item-poster', {
+    template: '#video-item-poster-template',
+    props: ['item']
+});
diff --git a/share/lua/http/src/components/videos/videos.component.html b/share/lua/http/src/components/videos/videos.component.html
new file mode 100644
index 0000000000..4109de8f78
--- /dev/null
+++ b/share/lua/http/src/components/videos/videos.component.html
@@ -0,0 +1,90 @@
+<script type="text/x-template" id="videos-template">
+    <div class="videos-view container-fluid">
+        <!-- <div v-if="displayMode === 'list'" class="row">
+            <album-item-list v-for="(item, index) in items" :item="item" :key="index"></album-item-list>
+        </div> -->
+        <!-- <h5 class="separator-title d-flex align-items-center">
+            <span>Videos</span>
+        </h5> -->
+        <div id="collapse-group-grid" class="position-relative collapse-container" v-if="layout.itemLayout === 'grid'">
+            <!-- <div class="carousel-navigation d-flex align-items-center">
+                <svg-icon class="muted-opacity" name="keyboard_arrow_left"></svg-icon>
+                <svg-icon name="keyboard_arrow_right"></svg-icon>
+            </div> -->
+            <template v-for="(item, index) in videos">
+                <video-item-grid :key="index" data-toggle="collapse" v-bind:data-target="'#collapse-' + item.id" aria-expanded="false" aria-controls="collapse" class="col-12 col-sm-6 col-md-4 col-lg-3 collapse-item" :item="item">
+                </video-item-grid><div class="collapse collapse-detail" v-bind:id="'collapse-' + item.id" data-parent="#collapse-group-grid">
+                    <div class="d-flex ml-2">
+                        <button data-toggle="collapse" v-bind:data-target="'#collapse-' + item.id" aria-expanded="false" aria-controls="collapse" class="close">
+                            <svg-icon name="add"></svg-icon>
+                        </button>
+                        <div>
+                            <img width="260" class="embed-responsive-item" :src="item.src">
+                            <div class="mt-2 d-flex">
+                                <play-button-secondary :item="item"></play-button-secondary>
+                                <enqueue-button class="ml-2" :item="item"></enqueue-button>
+                            </div>
+                        </div>
+                        <div class="ml-2">
+                            <h4 class="title mb-0">{{ item.title }}</h4>
+                            <span class="caption">{{ item.duration | formatDuration }}</span>
+                            <div class="subtitle"><span class="font-weight-bold">File name:</span>{{ item.filename }}</div>
+                            <div class="subtitle"><span class="font-weight-bold">Path:</span>{{ item.mrl }}</div>
+                        </div>
+                    </div>
+                </div>
+            </template>
+        </div>
+        <div id="collapse-group-list" class="position-relative collapse-container" v-if="layout.itemLayout === 'list'">
+            <!-- <div class="carousel-navigation d-flex align-items-center">
+                <svg-icon class="muted-opacity" name="keyboard_arrow_left"></svg-icon>
+                <svg-icon name="keyboard_arrow_right"></svg-icon>
+            </div> -->
+            <div class="tracks-view container-fluid list-table-view">
+                <!-- <div v-if="displayMode === 'list'" class="row">
+                    <artist-item-list v-for="item in items" :item="item"></artist-item-list>
+                </div> -->
+                <div class="d-flex list-header row">
+                    <span class="col-2 text-muted track-img">
+                        <svg-icon name="Icon-Vinyl"></svg-icon>
+                    </span>
+                    <span class="col-6 text-muted">Title</span>
+                    <span class="col-2 text-muted text-center">
+                        <svg-icon name="Icon-Time"></svg-icon>
+                    </span>
+                </div>
+                <template v-for="(item, index) in videos">
+                    <video-item-list :key="index" data-toggle="collapse" v-bind:data-target="'#collapse-' + item.id" aria-expanded="false" aria-controls="collapse" :item="item">
+                    </video-item-list><div class="collapse collapse-detail" v-bind:id="'collapse-' + item.id" data-parent="#collapse-group-list">
+                        <div class="d-flex ml-2">
+                            <button data-toggle="collapse" v-bind:data-target="'#collapse-' + item.id" aria-expanded="false" aria-controls="collapse" class="close">
+                                <svg-icon name="add"></svg-icon>
+                            </button>
+                            <div>
+                                <img width="260" class="embed-responsive-item" :src="item.src">
+                                <div class="mt-2 d-flex">
+                                    <play-button-secondary :item="item"></play-button-secondary>
+                                    <enqueue-button class="ml-2" :item="item"></enqueue-button>
+                                </div>
+                            </div>
+                            <div class="ml-2">
+                                <h4 class="title mb-0">{{ item.title }}</h4>
+                                <span class="caption">{{ item.duration | formatDuration }}</span>
+                                <div class="subtitle"><span class="font-weight-bold">File name:</span>{{ item.filename }}</div>
+                                <div class="subtitle"><span class="font-weight-bold">Path:</span>{{ item.mrl }}</div>
+                            </div>
+                        </div>
+                    </div>
+                </template>
+
+            </div>
+        </div>
+        <div class="row position-relative" v-if="layout.itemLayout === 'poster'">
+            <!-- <div class="carousel-navigation d-flex align-items-center">
+                <svg-icon class="muted-opacity" name="keyboard_arrow_left"></svg-icon>
+                <svg-icon name="keyboard_arrow_right"></svg-icon>
+            </div> -->
+            <video-item-poster class="col-12 col-sm-6 col-md-4 col-lg-2" v-for="(item, index) in videos" :item="item" :key="index"></video-item-poster>
+        </div>
+    </div>
+</script>
diff --git a/share/lua/http/src/components/videos/videos.component.js b/share/lua/http/src/components/videos/videos.component.js
new file mode 100644
index 0000000000..1e756194f3
--- /dev/null
+++ b/share/lua/http/src/components/videos/videos.component.js
@@ -0,0 +1,22 @@
+Vue.component('videos', {
+    template: '#videos-template',
+    data: function() {
+        return {
+            displayMode: 'grid'
+        }
+    },
+    computed: {
+        ...Vuex.mapState({
+            videos: state => state.video.videos,
+            layout: state => state.layout
+        }),
+    },
+    methods: {
+        fetchVideos() {
+            this.$store.dispatch('video/fetchVideos');
+        }
+    },
+    created() {
+        this.fetchVideos();
+    }
+});
diff --git a/share/lua/http/src/components/vlm/vlm.component.html b/share/lua/http/src/components/vlm/vlm.component.html
index 222c7ae2c9..905f0b04b4 100644
--- a/share/lua/http/src/components/vlm/vlm.component.html
+++ b/share/lua/http/src/components/vlm/vlm.component.html
@@ -5,8 +5,8 @@
             <!-- Modal content-->
             <div class="modal-content">
                 <div class="modal-header">
-                    <button type="button" class="close" data-dismiss="modal">×</button>
                     <h4 class="modal-title">Write your VLM commands here (seperated by new line)</h4>
+                    <button type="button" class="close" data-dismiss="modal">×</button>
                 </div>
                 <div class="modal-body">
                     <textarea id="vlmCommand"></textarea>
@@ -15,7 +15,7 @@
                     </div>
                 </div>
                 <div class="modal-footer">
-                    <button type="button" id="vlmButton" class="btn btn-default">Submit</button>
+                    <button type="button" v-on:click="executeVLM()" id="vlmButton" class="btn btn-default">Submit</button>
                     <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
                 </div>
             </div>
diff --git a/share/lua/http/src/components/vlm/vlm.component.js b/share/lua/http/src/components/vlm/vlm.component.js
index c7da5b49d1..e6569b6bab 100644
--- a/share/lua/http/src/components/vlm/vlm.component.js
+++ b/share/lua/http/src/components/vlm/vlm.component.js
@@ -1,17 +1,9 @@
-import { bus } from '../../services/bus.service.js';
-import { sendCommand } from '../../services/command.service.js';
-
 Vue.component('vlm-modal', {
     template: '#vlm-modal-template',
     methods: {
         executeVLM() {
             const cmd = $('#vlmCommand').val();
-            sendCommand(2, `?command=${cmd}`);
+            this.$store.dispatch('vlmCmd/postCmd', cmd);
         }
-    },
-    created() {
-        bus.$on('executeVLM', () => {
-            this.executeVLM();
-        });
     }
 });
diff --git a/share/lua/http/src/routes/browse/browse-view.component.html b/share/lua/http/src/routes/browse/browse-view.component.html
new file mode 100644
index 0000000000..2f05444c6a
--- /dev/null
+++ b/share/lua/http/src/routes/browse/browse-view.component.html
@@ -0,0 +1,51 @@
+<script type="text/x-template" id="browse-view-template">
+    <div class="browse-view h-100" :class="{ 'media-player-opened': audioPlayerOpened }">
+        <div class="container-fluid fixed-top primary-navbar d-flex align-content-center align-items-center text-center justify-content-center">
+            <div class="d-flex align-items-center flex-1 justify-content-start">
+                <img width="23" height="26" src="http://images.videolan.org/images/VLC-IconSmall.png">
+                <span class="ml-2">VLC</span>
+            </div>
+            <ul class="nav d-flex align-items-center justify-content-center">
+                <li class="nav-item">
+                    <router-link title="Video" active-class="active" to="/video" class="btn btn-icon btn-link d-flex flex-column">
+                        <svg-icon name="local_movies_outline"></svg-icon>
+                        <span>Video</span>
+                    </router-link>
+                </li>
+                <li class="nav-item">
+                    <router-link title="Music" active-class="active" to="/music" class="btn btn-icon btn-link d-flex flex-column">
+                        <svg-icon name="audiotrack_outline"></svg-icon>
+                        <span>Music</span>
+                    </router-link>
+                </li>
+                <li class="nav-item">
+                    <router-link title="Network" active-class="active" to="/network" class="btn btn-icon btn-link d-flex flex-column">
+                        <svg-icon name="wifi"></svg-icon>
+                        <span>Browse</span>
+                    </router-link>
+                </li>
+                <li class="nav-item">
+                    <router-link title="Discover" active-class="active" to="/discover" class="btn btn-icon btn-link d-flex flex-column">
+                        <svg-icon name="explore"></svg-icon>
+                        <span>Discover</span>
+                    </router-link>
+                </li>
+            </ul>
+            <div class="d-flex flex-1 justify-content-end h-100">
+                <button v-on:click="" class="btn-circle mr-2"><svg-icon name="search"></svg-icon></button>
+                <button v-on:click="" class="btn-circle"><svg-icon name="more-horizontal"></svg-icon></button>
+            </div>
+        </div>
+        <transition name="fade" mode="out-in">
+            <router-view></router-view>
+        </transition>
+        <transition name="slide-fade">
+            <media-player
+                v-if="audioPlayerOpened"
+                canPlay="canPlay"
+                ended="ended"
+                src="">
+            </media-player>
+        </transition>
+    </div>
+</script>
diff --git a/share/lua/http/src/routes/browse/browse-view.component.js b/share/lua/http/src/routes/browse/browse-view.component.js
new file mode 100644
index 0000000000..102c5f5497
--- /dev/null
+++ b/share/lua/http/src/routes/browse/browse-view.component.js
@@ -0,0 +1,19 @@
+export default BrowseViewComponent = {
+    template: '#browse-view-template',
+    methods: {
+        canPlay: function() {
+            console.log('canPlay');
+        },
+        ended: function() {
+            console.log('ended');
+        }
+    },
+    computed: {
+        ...Vuex.mapState({
+            audioPlayerOpened: state => state.layout.audioPlayerOpened
+        }),
+    },
+    created() {
+        document.body.style['overflow-y'] = 'auto';
+    }
+};
diff --git a/share/lua/http/src/routes/browse/browse-view.component.scss b/share/lua/http/src/routes/browse/browse-view.component.scss
new file mode 100644
index 0000000000..b2c50dd613
--- /dev/null
+++ b/share/lua/http/src/routes/browse/browse-view.component.scss
@@ -0,0 +1,3 @@
+.browse-view {
+    margin: 86px 0 20px 0px;
+}
diff --git a/share/lua/http/src/routes/discover/discover-view.component.html b/share/lua/http/src/routes/discover/discover-view.component.html
new file mode 100644
index 0000000000..7d0b7f1be1
--- /dev/null
+++ b/share/lua/http/src/routes/discover/discover-view.component.html
@@ -0,0 +1,37 @@
+<script type="text/x-template" id="discover-view-template">
+    <div class="discover-view h-100 pt-4">
+        <div class="fixed-top secondary-navbar">
+            <div class="container-fluid d-flex h-100 position-relative show collapse justify-content-between align-items-center">
+                <div class="d-flex align-items-center flex-1 justify-content-start">
+                    <button v-on:click="" class="btn btn-circle"><svg-icon name="format_list_bulleted"></svg-icon></button>
+                    <button v-on:click="" class="btn btn-circle ml-2"><svg-icon name="sort"></svg-icon></button>
+                </div>
+                <div class="d-flex align-items-center justify-content-center">
+                    <router-link to="/discover/home" active-class="active" class="btn btn-link">Home</router-link>
+                    <router-link to="/discover/services" active-class="active" class="btn btn-link">Services</router-link>
+                    <router-link to="/discover/url" active-class="active" class="btn btn-link">URL</router-link>
+                </div>
+                <div class="d-flex flex-1 justify-content-end h-100">
+                    <a data-toggle="collapse" href="#" data-target=".collapse" role="button" class="text-white toggler px-2 d-flex align-items-center">
+                        <svg-icon name="playlist_play"></svg-icon>
+                    </a>
+                </div>
+            </div>
+        </div>
+        <div class="container-fluid px-0 h-100" :class="'push'">
+            <div class="row collapse show no-gutters d-flex h-100 position-relative">
+                <div class="col container-view">
+                    <transition name="fade" mode="out-in">
+                        <router-view></router-view>
+                    </transition>
+                </div>
+                <div class="col-3 p-0 h-100 w-sidebar navbar-collapse collapse d-flex sidebar show">
+                    <!-- fixed sidebar -->
+                    <div class="navbar-dark align-self-start w-sidebar w-sidebar-content">
+                        <playlist></playlist>
+                    </div>
+                </div>
+            </div>
+        </div>
+    </div>
+</script>
diff --git a/share/lua/http/src/routes/discover/discover-view.component.js b/share/lua/http/src/routes/discover/discover-view.component.js
new file mode 100644
index 0000000000..4540e4ff26
--- /dev/null
+++ b/share/lua/http/src/routes/discover/discover-view.component.js
@@ -0,0 +1,5 @@
+export default DiscoverViewComponent = {
+    template: '#discover-view-template',
+    methods: { },
+    created() { }
+};
diff --git a/share/lua/http/src/routes/discover/home/discover-home-view.component.html b/share/lua/http/src/routes/discover/home/discover-home-view.component.html
new file mode 100644
index 0000000000..f0e06d0390
--- /dev/null
+++ b/share/lua/http/src/routes/discover/home/discover-home-view.component.html
@@ -0,0 +1,4 @@
+<script type="text/x-template" id="discover-home-view-template">
+    <div class="discover-home-view">
+    </div>
+</script>
diff --git a/share/lua/http/src/routes/discover/home/discover-home-view.component.js b/share/lua/http/src/routes/discover/home/discover-home-view.component.js
new file mode 100644
index 0000000000..36e0295e52
--- /dev/null
+++ b/share/lua/http/src/routes/discover/home/discover-home-view.component.js
@@ -0,0 +1,5 @@
+export default DiscoverHomeViewComponent = {
+    template: '#discover-home-view-template',
+    methods: { },
+    created() { }
+};
diff --git a/share/lua/http/src/routes/discover/services/discover-services-view.component.html b/share/lua/http/src/routes/discover/services/discover-services-view.component.html
new file mode 100644
index 0000000000..6b4e9d1391
--- /dev/null
+++ b/share/lua/http/src/routes/discover/services/discover-services-view.component.html
@@ -0,0 +1,15 @@
+<script type="text/x-template" id="discover-services-view-template">
+    <div class="discover-services-view">
+        <div class="row">
+            <grid-item :item="addService"></grid-item>
+            <template v-for="service in mainServices">
+                <grid-item :item="service"></grid-item>
+            </template>
+        </div>
+        <div class="row">
+            <template v-for="service in secondaryServices">
+                <grid-item :item="service"></grid-item>
+            </template>
+        </div>
+    </div>
+</script>
diff --git a/share/lua/http/src/routes/discover/services/discover-services-view.component.js b/share/lua/http/src/routes/discover/services/discover-services-view.component.js
new file mode 100644
index 0000000000..a762738050
--- /dev/null
+++ b/share/lua/http/src/routes/discover/services/discover-services-view.component.js
@@ -0,0 +1,49 @@
+export default DiscoverServicesViewComponent = {
+    template: '#discover-services-view-template',
+    methods: { },
+    created() { },
+    data: function() {
+        return {
+            addService: {
+                icon: 'add',
+                title: 'Add a service',
+                additionalItemClass: 'create-item'
+            },
+            mainServices: [
+                {
+                    icon: 'display',
+                    title: 'TV',
+                    additionalItemClass: 'tv-service',
+                    titleRoute: 'services/tv'
+                },
+                {
+                    icon: 'feed',
+                    title: 'Radio',
+                    additionalItemClass: 'radio-service'
+                },
+                {
+                    icon: 'mic',
+                    title: 'Podcast',
+                    additionalItemClass: 'podcast-service'
+                },
+                {
+                    icon: 'headphones',
+                    title: 'Audio Book',
+                    additionalItemClass: 'audiobook-service'
+                },
+            ],
+            secondaryServices: [
+                {
+                    icon: 'youtube',
+                    title: 'Youtube',
+                    additionalItemClass: 'youtube-service'
+                },
+                {
+                    icon: 'twitch',
+                    title: 'Twitch',
+                    additionalItemClass: 'twitch-service'
+                }
+            ]
+        }
+    }
+};
diff --git a/share/lua/http/src/routes/discover/services/discover-services-view.component.scss b/share/lua/http/src/routes/discover/services/discover-services-view.component.scss
new file mode 100644
index 0000000000..3f7ea6fc8b
--- /dev/null
+++ b/share/lua/http/src/routes/discover/services/discover-services-view.component.scss
@@ -0,0 +1,33 @@
+.create-item {
+    .svg-icon {
+        color: var(--highlight-color) !important;
+    }
+}
+.tv-service {
+    background-color: #7b5398;
+}
+.radio-service {
+    background-color: #fb610b;
+}
+.podcast-service {
+    background-color: #c1428d;
+}
+.audiobook-service {
+    background-color: #c1428d;
+}
+.youtube-service {
+    background-color: #fb0001;
+}
+.twitch-service {
+    background-color: #9146ff;
+}
+.discover-service-item {
+    .svg-icon {
+        width: 2.8rem;
+        height: 2.8rem;
+        color: #fff;
+        &:hover, &:focus {
+            color: #fff;
+        }
+    }
+}
\ No newline at end of file
diff --git a/share/lua/http/src/routes/discover/services/tv/discover-services-tv-view.component.html b/share/lua/http/src/routes/discover/services/tv/discover-services-tv-view.component.html
new file mode 100644
index 0000000000..f59478788d
--- /dev/null
+++ b/share/lua/http/src/routes/discover/services/tv/discover-services-tv-view.component.html
@@ -0,0 +1,10 @@
+<script type="text/x-template" id="discover-services-tv-view-template">
+    <div class="discover-services-tv-view container-fluid">
+        <div v-if="displayMode === 'list'" class="row">
+            <grid-list v-for="(item, index) in items" :item="item" :key="index"></grid-list>
+        </div>
+        <div class="row" v-if="displayMode === 'grid'">
+            <grid-item v-for="(item, index) in items" :item="item" :key="index"></grid-item>
+        </div>
+    </div>
+</script>
diff --git a/share/lua/http/src/routes/discover/services/tv/discover-services-tv-view.component.js b/share/lua/http/src/routes/discover/services/tv/discover-services-tv-view.component.js
new file mode 100644
index 0000000000..4fd88b9641
--- /dev/null
+++ b/share/lua/http/src/routes/discover/services/tv/discover-services-tv-view.component.js
@@ -0,0 +1,11 @@
+export default DiscoverServicesTvViewComponent = {
+    template: '#discover-services-tv-view-template',
+    data: function() {
+        return {
+            displayMode: 'grid',
+            items: [
+
+            ]
+        }
+    },
+};
diff --git a/share/lua/http/src/routes/discover/url/discover-url-view.component.html b/share/lua/http/src/routes/discover/url/discover-url-view.component.html
new file mode 100644
index 0000000000..e0802f38f5
--- /dev/null
+++ b/share/lua/http/src/routes/discover/url/discover-url-view.component.html
@@ -0,0 +1,4 @@
+<script type="text/x-template" id="discover-url-view-template">
+    <div class="discover-url-view">
+    </div>
+</script>
diff --git a/share/lua/http/src/routes/discover/url/discover-url-view.component.js b/share/lua/http/src/routes/discover/url/discover-url-view.component.js
new file mode 100644
index 0000000000..cf004c11cf
--- /dev/null
+++ b/share/lua/http/src/routes/discover/url/discover-url-view.component.js
@@ -0,0 +1,5 @@
+export default DiscoverUrlViewComponent = {
+    template: '#discover-url-view-template',
+    methods: { },
+    created() { }
+};
diff --git a/share/lua/http/src/routes/main/main-view.component.html b/share/lua/http/src/routes/main/main-view.component.html
new file mode 100644
index 0000000000..626e395980
--- /dev/null
+++ b/share/lua/http/src/routes/main/main-view.component.html
@@ -0,0 +1,3 @@
+<script type="text/x-template" id="main-view-template">
+    <router-view></router-view>
+</script>
diff --git a/share/lua/http/src/routes/main/main-view.component.js b/share/lua/http/src/routes/main/main-view.component.js
new file mode 100644
index 0000000000..eb9c51c386
--- /dev/null
+++ b/share/lua/http/src/routes/main/main-view.component.js
@@ -0,0 +1,5 @@
+Vue.component('main-view', {
+    template: '#main-view-template',
+    methods: { },
+    created() { }
+});
diff --git a/share/lua/http/src/routes/music/albums/music-albums-view.component.html b/share/lua/http/src/routes/music/albums/music-albums-view.component.html
new file mode 100644
index 0000000000..464f931cac
--- /dev/null
+++ b/share/lua/http/src/routes/music/albums/music-albums-view.component.html
@@ -0,0 +1,5 @@
+<script type="text/x-template" id="music-albums-view-template">
+    <div class="music-albums-view pt-4">
+        <albums></albums>
+    </div>
+</script>
diff --git a/share/lua/http/src/routes/music/albums/music-albums-view.component.js b/share/lua/http/src/routes/music/albums/music-albums-view.component.js
new file mode 100644
index 0000000000..59a8b70ada
--- /dev/null
+++ b/share/lua/http/src/routes/music/albums/music-albums-view.component.js
@@ -0,0 +1,6 @@
+export default MusicAlbumsViewComponent = {
+    template: '#music-albums-view-template',
+    methods: { },
+    created() {
+    }
+};
diff --git a/share/lua/http/src/routes/music/artists/detail/music-artists-detail-view.component.html b/share/lua/http/src/routes/music/artists/detail/music-artists-detail-view.component.html
new file mode 100644
index 0000000000..c91773be0a
--- /dev/null
+++ b/share/lua/http/src/routes/music/artists/detail/music-artists-detail-view.component.html
@@ -0,0 +1,5 @@
+<script type="text/x-template" id="music-artists-detail-view-template">
+    <div class="music-artists-detail-view">
+        <artists-detail></artists-detail>
+    </div>
+</script>
diff --git a/share/lua/http/src/routes/music/artists/detail/music-artists-detail-view.component.js b/share/lua/http/src/routes/music/artists/detail/music-artists-detail-view.component.js
new file mode 100644
index 0000000000..f7705cd5d1
--- /dev/null
+++ b/share/lua/http/src/routes/music/artists/detail/music-artists-detail-view.component.js
@@ -0,0 +1,5 @@
+export default MusicArtistsDetailViewComponent = {
+    template: '#music-artists-detail-view-template',
+    methods: { },
+    created() { }
+};
diff --git a/share/lua/http/src/routes/music/artists/music-artists-view.component.html b/share/lua/http/src/routes/music/artists/music-artists-view.component.html
new file mode 100644
index 0000000000..d3c75e838a
--- /dev/null
+++ b/share/lua/http/src/routes/music/artists/music-artists-view.component.html
@@ -0,0 +1,5 @@
+<script type="text/x-template" id="music-artists-view-template">
+    <div class="music-artists-view pt-4">
+        <artists></artists>
+    </div>
+</script>
diff --git a/share/lua/http/src/routes/music/artists/music-artists-view.component.js b/share/lua/http/src/routes/music/artists/music-artists-view.component.js
new file mode 100644
index 0000000000..536ad5a359
--- /dev/null
+++ b/share/lua/http/src/routes/music/artists/music-artists-view.component.js
@@ -0,0 +1,5 @@
+export default MusicArtistsViewComponent = {
+    template: '#music-artists-view-template',
+    methods: { },
+    created() { }
+};
diff --git a/share/lua/http/src/routes/music/genres/music-genres-view.component.html b/share/lua/http/src/routes/music/genres/music-genres-view.component.html
new file mode 100644
index 0000000000..1d3b5c2b3b
--- /dev/null
+++ b/share/lua/http/src/routes/music/genres/music-genres-view.component.html
@@ -0,0 +1,5 @@
+<script type="text/x-template" id="music-genres-view-template">
+    <div class="music-genres-view pt-4">
+        <genres></genres>
+    </div>
+</script>
diff --git a/share/lua/http/src/routes/music/genres/music-genres-view.component.js b/share/lua/http/src/routes/music/genres/music-genres-view.component.js
new file mode 100644
index 0000000000..1b53befaf9
--- /dev/null
+++ b/share/lua/http/src/routes/music/genres/music-genres-view.component.js
@@ -0,0 +1,5 @@
+export default MusicGenresViewComponent = {
+    template: '#music-genres-view-template',
+    methods: { },
+    created() { }
+};
diff --git a/share/lua/http/src/routes/music/music-view.component.html b/share/lua/http/src/routes/music/music-view.component.html
new file mode 100644
index 0000000000..b0c6d0d7f2
--- /dev/null
+++ b/share/lua/http/src/routes/music/music-view.component.html
@@ -0,0 +1,39 @@
+<script type="text/x-template" id="music-view-template">
+    <div class="music-view h-100">
+        <div class="fixed-top secondary-navbar">
+            <div class="container-fluid d-flex h-100 position-relative show collapse justify-content-between align-items-center">
+                <div class="d-flex align-items-center flex-1 justify-content-start">
+                    <item-layout-button></item-layout-button>
+                    <button v-on:click="" class="btn btn-circle ml-2"><svg-icon name="sort"></svg-icon></button>
+                </div>
+                <div class="d-flex align-items-center justify-content-center">
+                    <router-link to="/music/albums" exact-active-class="active" class="btn btn-link">Albums</router-link>
+                    <router-link to="/music/artists" active-class="active" class="btn btn-link">Artists</router-link>
+                    <router-link to="/music/genres" exact-active-class="active" class="btn btn-link">Genres</router-link>
+                    <router-link to="/music/tracks" exact-active-class="active" class="btn btn-link">Tracks</router-link>
+                    <router-link to="/music/playlists" exact-active-class="active" class="btn btn-link">Playlists</router-link>
+                </div>
+                <div class="d-flex flex-1 justify-content-end h-100">
+                    <a data-toggle="collapse" href="#" data-target=".collapse" role="button" class="text-white toggler px-2 d-flex align-items-center">
+                        <svg-icon name="playlist_play"></svg-icon>
+                    </a>
+                </div>
+            </div>
+        </div>
+        <div class="container-fluid px-0 h-100" :class="'push'">
+            <div class="row collapse show no-gutters d-flex h-100 position-relative">
+                <div class="col container-view">
+                    <transition name="fade" mode="out-in">
+                        <router-view></router-view>
+                    </transition>
+                </div>
+                <div class="col-3 p-0 h-100 w-sidebar navbar-collapse collapse d-flex sidebar show">
+                    <!-- fixed sidebar -->
+                    <div class="navbar-dark align-self-start w-sidebar w-sidebar-content">
+                        <playlist></playlist>
+                    </div>
+                </div>
+            </div>
+        </div>
+    </div>
+</script>
diff --git a/share/lua/http/src/routes/music/music-view.component.js b/share/lua/http/src/routes/music/music-view.component.js
new file mode 100644
index 0000000000..0739f843d9
--- /dev/null
+++ b/share/lua/http/src/routes/music/music-view.component.js
@@ -0,0 +1,7 @@
+export default MusicViewComponent = {
+    template: '#music-view-template',
+    methods: { },
+    created() {
+        document.body.style['overflow-y'] = 'auto';
+    }
+};
diff --git a/share/lua/http/src/routes/music/playlists/music-playlists-view.component.html b/share/lua/http/src/routes/music/playlists/music-playlists-view.component.html
new file mode 100644
index 0000000000..5871a89a5d
--- /dev/null
+++ b/share/lua/http/src/routes/music/playlists/music-playlists-view.component.html
@@ -0,0 +1,5 @@
+<script type="text/x-template" id="music-playlists-view-template">
+    <div class="music-playlists-view pt-4">
+        <playlists></playlists>
+    </div>
+</script>
diff --git a/share/lua/http/src/routes/music/playlists/music-playlists-view.component.js b/share/lua/http/src/routes/music/playlists/music-playlists-view.component.js
new file mode 100644
index 0000000000..0c12f51716
--- /dev/null
+++ b/share/lua/http/src/routes/music/playlists/music-playlists-view.component.js
@@ -0,0 +1,5 @@
+export default MusicPlaylistsViewComponent = {
+    template: '#music-playlists-view-template',
+    methods: { },
+    created() { }
+};
diff --git a/share/lua/http/src/routes/music/tracks/music-tracks-view.component.html b/share/lua/http/src/routes/music/tracks/music-tracks-view.component.html
new file mode 100644
index 0000000000..181d747384
--- /dev/null
+++ b/share/lua/http/src/routes/music/tracks/music-tracks-view.component.html
@@ -0,0 +1,5 @@
+<script type="text/x-template" id="music-tracks-view-template">
+    <div class="music-tracks-view pt-4">
+        <tracks></tracks>
+    </div>
+</script>
diff --git a/share/lua/http/src/routes/music/tracks/music-tracks-view.component.js b/share/lua/http/src/routes/music/tracks/music-tracks-view.component.js
new file mode 100644
index 0000000000..f8d8b6a351
--- /dev/null
+++ b/share/lua/http/src/routes/music/tracks/music-tracks-view.component.js
@@ -0,0 +1,5 @@
+export default MusicTracksViewComponent = {
+    template: '#music-tracks-view-template',
+    methods: { },
+    created() { }
+};
diff --git a/share/lua/http/src/routes/network/network-view.component.html b/share/lua/http/src/routes/network/network-view.component.html
new file mode 100644
index 0000000000..0e0d4e2217
--- /dev/null
+++ b/share/lua/http/src/routes/network/network-view.component.html
@@ -0,0 +1,31 @@
+<script type="text/x-template" id="network-view-template">
+    <div class="network-view h-100 pt-4">
+        <div class="fixed-top secondary-navbar">
+            <div class="container-fluid d-flex h-100 position-relative show collapse justify-content-between align-items-center">
+                <div class="d-flex align-items-center flex-1 justify-content-start">
+                    <button v-on:click="" class="btn btn-circle"><svg-icon name="format_list_bulleted"></svg-icon></button>
+                    <button v-on:click="" class="btn btn-circle ml-2"><svg-icon name="sort"></svg-icon></button>
+                </div>
+                <div class="d-flex align-items-center justify-content-center"></div>
+                <div class="d-flex flex-1 justify-content-end h-100">
+                    <a data-toggle="collapse" href="#" data-target=".collapse" role="button" class="text-white toggler px-2 d-flex align-items-center">
+                        <svg-icon name="playlist_play"></svg-icon>
+                    </a>
+                </div>
+            </div>
+        </div>
+        <div class="container-fluid px-0 h-100" :class="'push'">
+            <div class="row collapse show no-gutters d-flex h-100 position-relative">
+                <div class="col container-view">
+
+                </div>
+                <div class="col-3 p-0 h-100 w-sidebar navbar-collapse collapse d-flex sidebar show">
+                    <!-- fixed sidebar -->
+                    <div class="navbar-dark align-self-start w-sidebar w-sidebar-content">
+                        <playlist></playlist>
+                    </div>
+                </div>
+            </div>
+        </div>
+    </div>
+</script>
diff --git a/share/lua/http/src/routes/network/network-view.component.js b/share/lua/http/src/routes/network/network-view.component.js
new file mode 100644
index 0000000000..8fdf022613
--- /dev/null
+++ b/share/lua/http/src/routes/network/network-view.component.js
@@ -0,0 +1,4 @@
+export default NetworkViewComponent = {
+    template: '#network-view-template',
+    methods: { }
+};
diff --git a/share/lua/http/src/routes/video/all/video-all-view.component.html b/share/lua/http/src/routes/video/all/video-all-view.component.html
new file mode 100644
index 0000000000..4adbb62c86
--- /dev/null
+++ b/share/lua/http/src/routes/video/all/video-all-view.component.html
@@ -0,0 +1,6 @@
+<script type="text/x-template" id="video-all-view-template">
+    <div class="video-all-view">
+        <!-- <continue-watching></continue-watching> -->
+        <videos></videos>
+    </div>
+</script>
diff --git a/share/lua/http/src/routes/video/all/video-all-view.component.js b/share/lua/http/src/routes/video/all/video-all-view.component.js
new file mode 100644
index 0000000000..9e550b0cd7
--- /dev/null
+++ b/share/lua/http/src/routes/video/all/video-all-view.component.js
@@ -0,0 +1,5 @@
+export default VideoAllViewComponent = {
+    template: '#video-all-view-template',
+    methods: { },
+    created() { }
+};
diff --git a/share/lua/http/src/routes/video/movies/video-movies-view.component.html b/share/lua/http/src/routes/video/movies/video-movies-view.component.html
new file mode 100644
index 0000000000..03a5ba5543
--- /dev/null
+++ b/share/lua/http/src/routes/video/movies/video-movies-view.component.html
@@ -0,0 +1,3 @@
+<script type="text/x-template" id="video-movies-view-template">
+    <div class="video-movies-view"></div>
+</script>
diff --git a/share/lua/http/src/routes/video/movies/video-movies-view.component.js b/share/lua/http/src/routes/video/movies/video-movies-view.component.js
new file mode 100644
index 0000000000..79ecdfd1c4
--- /dev/null
+++ b/share/lua/http/src/routes/video/movies/video-movies-view.component.js
@@ -0,0 +1,5 @@
+export default VideoMoviesViewComponent = {
+    template: '#video-movies-view-template',
+    methods: { },
+    created() { }
+};
diff --git a/share/lua/http/src/routes/video/playlists/video-playlists-view.component.html b/share/lua/http/src/routes/video/playlists/video-playlists-view.component.html
new file mode 100644
index 0000000000..62c40e5b87
--- /dev/null
+++ b/share/lua/http/src/routes/video/playlists/video-playlists-view.component.html
@@ -0,0 +1,3 @@
+<script type="text/x-template" id="video-playlists-view-template">
+    <div class="video-playlists-view"></div>
+</script>
diff --git a/share/lua/http/src/routes/video/playlists/video-playlists-view.component.js b/share/lua/http/src/routes/video/playlists/video-playlists-view.component.js
new file mode 100644
index 0000000000..172ab20a3f
--- /dev/null
+++ b/share/lua/http/src/routes/video/playlists/video-playlists-view.component.js
@@ -0,0 +1,5 @@
+export default VideoPlaylistsViewComponent = {
+    template: '#video-playlists-view-template',
+    methods: { },
+    created() { }
+};
diff --git a/share/lua/http/src/routes/video/tvshows/video-tvshows-view.component.html b/share/lua/http/src/routes/video/tvshows/video-tvshows-view.component.html
new file mode 100644
index 0000000000..6ea9529936
--- /dev/null
+++ b/share/lua/http/src/routes/video/tvshows/video-tvshows-view.component.html
@@ -0,0 +1,3 @@
+<script type="text/x-template" id="video-tvshows-view-template">
+    <div class="video-tvshows-view"></div>
+</script>
diff --git a/share/lua/http/src/routes/video/tvshows/video-tvshows-view.component.js b/share/lua/http/src/routes/video/tvshows/video-tvshows-view.component.js
new file mode 100644
index 0000000000..604c98d5c5
--- /dev/null
+++ b/share/lua/http/src/routes/video/tvshows/video-tvshows-view.component.js
@@ -0,0 +1,5 @@
+export default VideoTvShowsViewComponent = {
+    template: '#video-tvshows-view-template',
+    methods: { },
+    created() { }
+};
diff --git a/share/lua/http/src/routes/video/video-view.component.html b/share/lua/http/src/routes/video/video-view.component.html
new file mode 100644
index 0000000000..db2127b27c
--- /dev/null
+++ b/share/lua/http/src/routes/video/video-view.component.html
@@ -0,0 +1,38 @@
+<script type="text/x-template" id="video-view-template">
+    <div class="video-view h-100 pt-4">
+        <div class="fixed-top secondary-navbar">
+            <div class="container-fluid d-flex h-100 position-relative show collapse justify-content-between align-items-center">
+                <div class="d-flex align-items-center flex-1 justify-content-start">
+                    <item-layout-button></item-layout-button>
+                    <button v-on:click="" class="btn btn-circle ml-2"><svg-icon name="sort"></svg-icon></button>
+                </div>
+                <div class="d-flex align-items-center justify-content-center">
+                    <router-link to="/video/all" exact-active-class="active" class="btn btn-link">All</router-link>
+                    <router-link to="/video/movies" exact-active-class="active" class="btn btn-link">Movies</router-link>
+                    <router-link to="/video/tvshows" exact-active-class="active" class="btn btn-link">Tv Shows</router-link>
+                    <router-link to="/video/playlists" exact-active-class="active" class="btn btn-link">Playlists</router-link>
+                </div>
+                <div class="d-flex flex-1 justify-content-end h-100">
+                    <a data-toggle="collapse" href="#" data-target=".collapse" role="button" class="text-white toggler px-2 d-flex align-items-center">
+                        <svg-icon name="playlist_play"></svg-icon>
+                    </a>
+                </div>
+            </div>
+        </div>
+        <div class="container-fluid px-0 h-100" :class="'push'">
+            <div class="row collapse show no-gutters d-flex h-100 position-relative">
+                <div class="col container-view">
+                    <transition name="fade" mode="out-in">
+                        <router-view></router-view>
+                    </transition>
+                </div>
+                <div class="col-3 p-0 h-100 w-sidebar navbar-collapse collapse d-flex sidebar show">
+                    <!-- fixed sidebar -->
+                    <div class="navbar-dark align-self-start w-sidebar w-sidebar-content">
+                        <playlist></playlist>
+                    </div>
+                </div>
+            </div>
+        </div>
+    </div>
+</script>
diff --git a/share/lua/http/src/routes/video/video-view.component.js b/share/lua/http/src/routes/video/video-view.component.js
new file mode 100644
index 0000000000..27c60d0eb5
--- /dev/null
+++ b/share/lua/http/src/routes/video/video-view.component.js
@@ -0,0 +1,7 @@
+export default VideoViewComponent = {
+    template: '#video-view-template',
+    methods: { },
+    created() {
+        document.body.style['overflow-y'] = 'auto';
+    }
+};
diff --git a/share/lua/http/src/routes/watch/watch-view.component.html b/share/lua/http/src/routes/watch/watch-view.component.html
new file mode 100644
index 0000000000..0546e71483
--- /dev/null
+++ b/share/lua/http/src/routes/watch/watch-view.component.html
@@ -0,0 +1,36 @@
+<script type="text/x-template" id="watch-view-template">
+    <div class="watch-view h-100">
+        <div>
+            <div class="container-fluid fixed-top primary-navbar">
+                <div class="d-flex h-100 position-relative show collapse justify-content-between align-items-center">
+                    <div class="d-flex align-items-center flex-1 justify-content-start">
+                        <router-link to="/">
+                            <button class="btn btn-circle">
+                                <svg-icon name="keyboard_arrow_left"></svg-icon>
+                            </button>
+                        </router-link>
+                    </div>
+                    <div class="d-flex align-items-center justify-content-center"></div>
+                    <div class="d-flex flex-1 justify-content-end h-100">
+                        <a data-toggle="collapse" href="#" data-target=".collapse" role="button" class="text-white toggler px-2 d-flex align-items-center">
+                            <svg-icon name="playlist_play"></svg-icon>
+                        </a>
+                    </div>
+                </div>
+            </div>
+        </div>
+        <div class="container-fluid px-0" :class="'overlay'">
+            <div class="row collapse show no-gutters d-flex h-100 position-relative">
+                <div class="col">
+                    <media-player></media-player>
+                </div>
+                <div class="col-3 p-0 h-100 w-sidebar navbar-collapse collapse d-flex sidebar show">
+                    <!-- fixed sidebar -->
+                    <div class="navbar-dark align-self-start w-sidebar w-sidebar-content">
+                        <playlist></playlist>
+                    </div>
+                </div>
+            </div>
+        </div>
+    </div>
+</script>
diff --git a/share/lua/http/src/routes/watch/watch-view.component.js b/share/lua/http/src/routes/watch/watch-view.component.js
new file mode 100644
index 0000000000..ba86388f28
--- /dev/null
+++ b/share/lua/http/src/routes/watch/watch-view.component.js
@@ -0,0 +1,7 @@
+export default WatchViewComponent = {
+    template: '#watch-view-template',
+    methods: { },
+    created() {
+        document.body.style['overflow-y'] = 'hidden';
+    }
+};
diff --git a/share/lua/http/src/scss/components.scss b/share/lua/http/src/scss/components.scss
index ea5d6eb9e0..336c0f0df1 100644
--- a/share/lua/http/src/scss/components.scss
+++ b/share/lua/http/src/scss/components.scss
@@ -1,9 +1,25 @@
 // components.scss
 
+ at import '../components/albums/albums.component.scss';
+ at import '../components/albums/album-item/album-item.component.scss';
+ at import '../components/artists/artist-item/artist-item-grid.component.scss';
+ at import '../components/artists/detail/artists-detail.component.scss';
+ at import '../components/library/library.component.scss';
+ at import '../components/library/library-item/library-item.component.scss';
+ at import '../components/media-player/media-player.component.scss';
+ at import '../components/enqueue-button/enqueue-button.component.scss';
+ at import '../components/play-button/play-button-secondary.component.scss';
+ at import '../components/play-button/play-button.component.scss';
 @import '../components/playlist/playlist.component.scss';
+ at import '../components/playlist/playlist-video-item.component.scss';
+ at import '../components/playlist/playlist-audio-item.component.scss';
+ at import '../components/playlist/buttons.playlist.component.scss';
+ at import '../components/playlists/playlists-item/playlists-item.component.scss';
 @import '../components/vlm/vlm.component.scss';
 @import '../components/svg-icon/svg-icon.component.scss';
 
+ at import '../routes/browse/browse-view.component.scss';
+ at import '../routes/discover/services/discover-services-view.component.scss';
 @import './collapse.scss';
 @import './grid-item.scss';
 @import './list-item.scss';
diff --git a/share/lua/http/src/services/initialize.service.js b/share/lua/http/src/services/initialize.service.js
index 4185306342..6eb4c8ad3f 100644
--- a/share/lua/http/src/services/initialize.service.js
+++ b/share/lua/http/src/services/initialize.service.js
@@ -1,79 +1,176 @@
-import { plyrInit } from '../components/player/plyr.methods.js';
-import { notifyBus } from './bus.service.js';
 import { svgIcon } from '../components/svg-icon/svg-icon.component.js';
+import BrowseViewComponent from '../routes/browse/browse-view.component.js';
+import VideoViewComponent from '../routes/video/video-view.component.js';
+import WatchViewComponent from '../routes/watch/watch-view.component.js';
 
-export const VIDEO_TYPES = [
-    'asf', 'avi', 'bik', 'bin', 'divx', 'drc', 'dv', 'f4v', 'flv', 'gxf', 'iso',
-    'm1v', 'm2v', 'm2t', 'm2ts', 'm4v', 'mkv', 'mov',
-    'mp2', 'mp4', 'mpeg', 'mpeg1',
-    'mpeg2', 'mpeg4', 'mpg', 'mts', 'mtv', 'mxf', 'mxg', 'nuv',
-    'ogg', 'ogm', 'ogv', 'ogx', 'ps',
-    'rec', 'rm', 'rmvb', 'rpl', 'thp', 'ts', 'txd', 'vob', 'wmv', 'xesc'
-];
+import store from '../store/index.js';
+import MusicViewComponent from '../routes/music/music-view.component.js';
+import DiscoverViewComponent from '../routes/discover/discover-view.component.js';
+import DiscoverHomeViewComponent from '../routes/discover/home/discover-home-view.component.js';
+import DiscoverServicesViewComponent from '../routes/discover/services/discover-services-view.component.js';
+import DiscoverServicesTvViewComponent from '../routes/discover/services/tv/discover-services-tv-view.component.js';
+import DiscoverUrlViewComponent from '../routes/discover/url/discover-url-view.component.js';
 
-export const AUDIO_TYPES = [
-    '3ga', 'a52', 'aac', 'ac3', 'ape', 'awb', 'dts', 'flac', 'it',
-    'm4a', 'm4p', 'mka', 'mlp', 'mod', 'mp1', 'mp2', 'mp3',
-    'oga', 'ogg', 'oma', 's3m', 'spx', 'thd', 'tta',
-    'wav', 'wma', 'wv', 'xm'
-];
+import MusicAlbumsViewComponent from '../routes/music/albums/music-albums-view.component.js';
+import MusicArtistsViewComponent from '../routes/music/artists/music-artists-view.component.js';
+import MusicArtistsDetailViewComponent from '../routes/music/artists/detail/music-artists-detail-view.component.js';
+import MusicGenresViewComponent from '../routes/music/genres/music-genres-view.component.js';
+import MusicTracksViewComponent from '../routes/music/tracks/music-tracks-view.component.js';
+
+import NetworkViewComponent from '../routes/network/network-view.component.js';
+import VideoMoviesComponent from '../routes/video/movies/video-movies-view.component.js';
+import VideoTvshowsComponent from '../routes/video/tvshows/video-tvshows-view.component.js';
+import VideoAllComponent from '../routes/video/all/video-all-view.component.js';
+import MusicPlaylistsViewComponent from '../routes/music/playlists/music-playlists-view.component.js';
+import VideoPlaylistsComponent from '../routes/video/playlists/video-playlists-view.component.js';
+
+const routes = [
 
-export const PLAYLIST_TYPES = [
-    'asx', 'b4s', 'cue', 'ifo', 'm3u', 'm3u8', 'pls', 'ram', 'rar',
-    'sdp', 'vlc', 'xspf', 'zip', 'conf'
+    {
+        path: '/',
+        component: BrowseViewComponent,
+        redirect: '/video',
+        children: [
+            {
+                path: 'music',
+                component: MusicViewComponent,
+                redirect: 'music/albums',
+                children: [
+                    {
+                        path: 'albums',
+                        component: MusicAlbumsViewComponent
+                    },
+                    {
+                        path: 'artists',
+                        component: MusicArtistsViewComponent,
+                    },
+                    {
+                        path: 'artists/:id',
+                        component: MusicArtistsDetailViewComponent
+                    },
+                    {
+                        path: 'genres',
+                        component: MusicGenresViewComponent
+                    },
+                    {
+                        path: 'tracks',
+                        component: MusicTracksViewComponent
+                    },
+                    {
+                        path: 'playlists',
+                        component: MusicPlaylistsViewComponent
+                    }
+                ]
+            },
+            {
+                path: 'video',
+                component: VideoViewComponent,
+                redirect: 'video/all',
+                children: [
+                    {
+                        path: 'all',
+                        component: VideoAllComponent
+                    },
+                    {
+                        path: 'movies',
+                        component: VideoMoviesComponent
+                    },
+                    {
+                        path: 'tvshows',
+                        component: VideoTvshowsComponent
+                    },
+                    {
+                        path: 'playlists',
+                        component: VideoPlaylistsComponent
+                    }
+                ]
+            },
+            {
+                path: 'network',
+                component: NetworkViewComponent
+            },
+            {
+                path: 'discover',
+                component: DiscoverViewComponent,
+                redirect: 'discover/home',
+                children: [
+                    {
+                        path: 'home',
+                        component: DiscoverHomeViewComponent
+                    },
+                    {
+                        path: 'services',
+                        component: DiscoverServicesViewComponent
+                    },
+                    {
+                        path: 'services/tv',
+                        component: DiscoverServicesTvViewComponent
+                    },
+                    {
+                        path: 'url',
+                        component: DiscoverUrlViewComponent
+                    }
+                ]
+            }
+        ]
+    },
+    {
+        path: '/watch',
+        component: WatchViewComponent
+    }
 ];
 
+const router = new VueRouter({
+    routes
+})
+
 function vueInit() {
+    Vue.use(svgIcon, {
+        tagName: 'svg-icon'
+    });
     return new Vue({
+        router,
         el: '#app',
         data: {
             playlistItems: []
-        }
+        },
+        store,
+        mounted() { }
     });
 }
 
 $(() => {
-    plyrInit();
-    Vue.use(svgIcon, {
-        tagName: 'svg-icon'
-    });
     vueInit();
+    let didScroll;
+    let lastScrollTop = 0;
+    const delta = 5;
+    const navbarHeight = $('.fixed-top.secondary-navbar').outerHeight();
 
-    $('#openNavButton').on('click', () => {
-        if ($(window).width() <= 480 && $('#playlistNav').css('width') === '60%') {
-            notifyBus('closePlaylist');
-            notifyBus('openNav');
-        } else {
-            notifyBus('openNav');
-        }
-    });
-
-    $('#closeNavButton').on('click', () => {
-        notifyBus('closeNav');
-    });
-
-    $('#vlmButton').on('click', () => {
-        notifyBus('executeVLM');
-    });
-
-    $('#repeatButton').on('click', () => {
-        notifyBus('toggleRepeat');
+    $(window).scroll(() => {
+        didScroll = true;
     });
 
-    $('#playButton').on('click', () => {
-        notifyBus('startPlaylist');
-    });
+    setInterval(() => {
+        if (didScroll) {
+            hasScrolled();
+            didScroll = false;
+        }
+    }, 250);
 
-    $('#randomButton').on('click', () => {
-        notifyBus('toggleRandom');
-    });
+    function hasScrolled() {
+        const sValue = $(this).scrollTop();
+        const secondaryNavbar = $('.fixed-top.secondary-navbar');
+        if (Math.abs(lastScrollTop - sValue) <= delta) {
+            return;
+        }
 
-    $('#mobilePlaylistNavButton').on('click', () => {
-        if ($(window).width() <= 480 && $('#sideNav').width() === '60%') {
-            notifyBus('closeNav');
-            notifyBus('openPlaylist');
+        if (sValue > lastScrollTop && sValue > navbarHeight) {
+            secondaryNavbar.removeClass('nav-down').addClass('nav-up');
         } else {
-            notifyBus('openPlaylist');
+            if (sValue + $(window).height() < $(document).height()) {
+                secondaryNavbar.removeClass('nav-up').addClass('nav-down');
+            }
         }
-    });
+        lastScrollTop = sValue;
+    }
 });
-- 
2.18.0



More information about the vlc-devel mailing list