work in progress o frontend
authormarcelveldt <marcelvanderveldt@MacBook-Silvia.local>
Sat, 26 Oct 2019 23:46:01 +0000 (01:46 +0200)
committermarcelveldt <marcelvanderveldt@MacBook-Silvia.local>
Sat, 26 Oct 2019 23:46:01 +0000 (01:46 +0200)
21 files changed:
music_assistant/database.py
music_assistant/music_manager.py
music_assistant/web.py
music_assistant/web/app.js
music_assistant/web/components/addtoplaylistdialog.vue.js [new file with mode: 0644]
music_assistant/web/components/contextmenu.vue.js [new file with mode: 0644]
music_assistant/web/components/infoheader.vue.js
music_assistant/web/components/listdialog.vue.js [new file with mode: 0644]
music_assistant/web/components/listviewItem.vue.js
music_assistant/web/components/player.vue.js
music_assistant/web/components/playmenu.vue.js [deleted file]
music_assistant/web/components/searchbox.vue.js [deleted file]
music_assistant/web/index.html
music_assistant/web/lib/utils.js
music_assistant/web/pages/albumdetails.vue.js
music_assistant/web/pages/artistdetails.vue.js
music_assistant/web/pages/browse.vue.js
music_assistant/web/pages/playlistdetails.vue.js
music_assistant/web/pages/queue.vue.js
music_assistant/web/pages/search.vue.js
music_assistant/web/pages/trackdetails.vue.js

index 1a2f554437110b2ba2d803fb0c8d610c7b575492..7f087f1f901e5889c090d7a12ccd6a15d30f05fd 100755 (executable)
@@ -246,7 +246,7 @@ class Database():
                 await db.execute(sql_query, (item_id,media_type, provider))
             await db.commit()
     
-    async def artists(self, filter_query=None, limit=100000, offset=0, orderby='name', fulldata=False, db=None) -> List[Artist]:
+    async def artists(self, filter_query=None, limit=100000, offset=0, orderby='name', fulldata=True, db=None) -> List[Artist]:
         ''' fetch artist records from table'''
         artists = []
         sql_query = 'SELECT * FROM artists'
@@ -269,11 +269,10 @@ class Database():
             artist.sort_name = db_row[2]
             artist.provider_ids = await self.__get_prov_ids(artist.item_id, MediaType.Artist, db)
             artist.in_library = await self.__get_library_providers(artist.item_id, MediaType.Artist, db)
-            artist.external_ids = await self.__get_external_ids(artist.item_id, MediaType.Artist, db)
             if fulldata:
+                artist.external_ids = await self.__get_external_ids(artist.item_id, MediaType.Artist, db)
                 artist.metadata = await self.__get_metadata(artist.item_id, MediaType.Artist, db)
                 artist.tags = await self.__get_tags(artist.item_id, MediaType.Artist, db)
-            else:
                 artist.metadata = await self.__get_metadata(artist.item_id, MediaType.Artist, db, filter_key='image')
             artists.append(artist)
         if should_close_db:
@@ -323,7 +322,7 @@ class Database():
         LOGGER.debug('added artist %s (%s) to database: %s' %(artist.name, artist.provider_ids, artist_id))
         return artist_id
     
-    async def albums(self, filter_query=None, limit=100000, offset=0, orderby='name', fulldata=False, db=None) -> List[Album]:
+    async def albums(self, filter_query=None, limit=100000, offset=0, orderby='name', fulldata=True, db=None) -> List[Album]:
         ''' fetch all album records from table'''
         albums = []
         sql_query = 'SELECT * FROM albums'
@@ -342,15 +341,15 @@ class Database():
             for db_row in db_rows:
                 album = Album()
                 album.item_id = db_row[0]
-                album.artist = await self.artist(db_row[1], fulldata=fulldata)
                 album.name = db_row[2]
                 album.albumtype = db_row[3]
                 album.year = db_row[4]
                 album.version = db_row[5]
                 album.provider_ids = await self.__get_prov_ids(album.item_id, MediaType.Album, db)
                 album.in_library = await self.__get_library_providers(album.item_id, MediaType.Album, db)
-                album.external_ids = await self.__get_external_ids(album.item_id, MediaType.Album, db)
                 if fulldata:
+                    album.artist = await self.artist(db_row[1], fulldata=False)
+                    album.external_ids = await self.__get_external_ids(album.item_id, MediaType.Album, db)
                     album.metadata = await self.__get_metadata(album.item_id, MediaType.Album, db)
                     album.tags = await self.__get_tags(album.item_id, MediaType.Album, db)
                     album.labels = await self.__get_album_labels(album.item_id, db)
@@ -407,7 +406,7 @@ class Database():
         LOGGER.debug('added album %s (%s) to database: %s' %(album.name, album.provider_ids, album_id))
         return album_id
 
-    async def tracks(self, filter_query=None, limit=100000, offset=0, orderby='name', fulldata=False, db=None) -> List[Track]:
+    async def tracks(self, filter_query=None, limit=100000, offset=0, orderby='name', fulldata=True, db=None) -> List[Track]:
         ''' fetch all track records from table'''
         tracks = []
         sql_query = 'SELECT * FROM tracks'
@@ -427,17 +426,18 @@ class Database():
             track = Track()
             track.item_id = db_row[0]
             track.name = db_row[1]
-            track.album = await self.album(db_row[2], fulldata=fulldata, db=db)
+            track.album = await self.album(db_row[2], fulldata=False, db=db)
+            track.artists = await self.__get_track_artists(track.item_id, db, fulldata=False)
             track.duration = db_row[3]
             track.version = db_row[4]
             track.disc_number = db_row[5]
             track.track_number = db_row[6]
-            track.metadata = await self.__get_metadata(track.item_id, MediaType.Track, db)
-            track.tags = await self.__get_tags(track.item_id, MediaType.Track, db)
-            track.provider_ids = await self.__get_prov_ids(track.item_id, MediaType.Track, db)
             track.in_library = await self.__get_library_providers(track.item_id, MediaType.Track, db)
-            track.artists = await self.__get_track_artists(track.item_id, db, fulldata=fulldata)
             track.external_ids = await self.__get_external_ids(track.item_id, MediaType.Track, db)
+            track.provider_ids = await self.__get_prov_ids(track.item_id, MediaType.Track, db)
+            if fulldata:
+                track.metadata = await self.__get_metadata(track.item_id, MediaType.Track, db)
+                track.tags = await self.__get_tags(track.item_id, MediaType.Track, db)
             tracks.append(track)
         if should_close_db:
             await db.close()
@@ -505,14 +505,14 @@ class Database():
         ''' get all library tracks for the given artist '''
         artist_id = try_parse_int(artist_id)
         sql_query = ' WHERE track_id in (SELECT track_id FROM track_artists WHERE artist_id = %d)' % artist_id
-        return await self.tracks(sql_query, limit=limit, offset=offset, orderby=orderby)
+        return await self.tracks(sql_query, limit=limit, offset=offset, orderby=orderby, fulldata=False)
 
     async def artist_albums(self, artist_id, limit=1000000, offset=0, orderby='name') -> List[Album]:
         ''' get all library albums for the given artist '''
         sql_query = ' WHERE artist_id = %d' % artist_id
-        return await self.albums(sql_query, limit=limit, offset=offset, orderby=orderby)
+        return await self.albums(sql_query, limit=limit, offset=offset, orderby=orderby, fulldata=False)
 
-    async def playlist_tracks(self, playlist_id:int, limit=100000, offset=0, orderby='position', fulldata=False) -> List[Track]:
+    async def playlist_tracks(self, playlist_id:int, limit=100000, offset=0, orderby='position') -> List[Track]:
         ''' get playlist tracks for the given playlist_id '''
         playlist_id = try_parse_int(playlist_id)
         playlist_tracks = []
@@ -524,7 +524,7 @@ class Database():
                 db_rows = await cursor.fetchall()
             playlist_track_ids = [str(item[0]) for item in db_rows]
             sql_query = 'WHERE track_id in (%s)' % ','.join(playlist_track_ids)
-            tracks = await self.tracks(sql_query, orderby='track_id', db=db)
+            tracks = await self.tracks(sql_query, orderby='track_id', db=db, fulldata=False)
             for index, track in enumerate(tracks):
                 track.position = db_rows[index][1]
                 playlist_tracks.append(track)
@@ -534,7 +534,7 @@ class Database():
     async def add_playlist_track(self, playlist_id:int, track_id, position):
         ''' add playlist track to playlist '''
         async with aiosqlite.connect(self.dbfile, timeout=20) as db:
-            sql_query = 'INSERT or IGNORE INTO playlist_tracks (playlist_id, track_id, position) VALUES(?,?,?);'
+            sql_query = 'INSERT or REPLACE INTO playlist_tracks (playlist_id, track_id, position) VALUES(?,?,?);'
             await db.execute(sql_query, (playlist_id, track_id, position))
             await db.commit()
 
@@ -625,7 +625,7 @@ class Database():
     async def __get_track_artists(self, track_id, db, fulldata=False) -> List[Artist]:
         ''' get artists for track '''
         sql_query = 'WHERE artist_id in (SELECT artist_id FROM track_artists WHERE track_id = %s)' % track_id
-        return await self.artists(sql_query, db=db)
+        return await self.artists(sql_query, db=db, fulldata=fulldata)
     
     async def __add_external_ids(self, item_id, media_type, external_ids, db):
         ''' add or update external_ids'''
index d52bf90dd673434cea6f587de8f66f31bc1215c2..c7533542e957c88a81216b20e82da0425df48043 100755 (executable)
@@ -201,7 +201,9 @@ class MusicManager():
         ''' perform action on item (such as library add/remove) '''
         result = None
         item = await self.item(item_id, media_type, provider, lazy=False)
-        if item and action in ['library_add', 'library_remove']:
+        if not item:
+            return False
+        if action in ['library_add', 'library_remove']:
             # remove or add item to the library
             for prov_mapping in item.provider_ids:
                 prov_id = prov_mapping['provider']
@@ -214,8 +216,10 @@ class MusicManager():
                         elif action == 'library_remove':
                             result = await prov.remove_library(prov_item_id, media_type)
                             await self.mass.db.remove_from_library(item.item_id, item.media_type, prov_id)
-                        elif action == 'add_to_playlist':
-                            result = await self.add_playlist_tracks(action_details, [item])
+        elif action == 'playlist_add':
+            result = await self.add_playlist_tracks(action_details, [item])
+        elif action == 'playlist_remove':
+            result = await self.remove_playlist_tracks(action_details, [item])
         return result
     
     async def add_playlist_tracks(self, playlist_id, tracks:List[Track]):
@@ -230,7 +234,7 @@ class MusicManager():
         # grab all (database) track ids in the playlist so we can check for duplicates
         cur_playlist_track_ids = [item.item_id for item in cur_playlist_tracks]
         track_ids_to_add = []
-        for track in tracks:
+        for index, track in enumerate(tracks):
             if not track.provider == 'database':
                 # make sure we have a database track
                 track = await self.track(track.item_id, track.provider, lazy=False)
@@ -255,10 +259,35 @@ class MusicManager():
             else:
                 LOGGER.warning("Track %s not available on provider %s - skip addition to playlist %s" %(track.name, playlist_prov['provider'], playlist.name))
                 continue
+            # add track to db playlist
+            new_pos = len(cur_playlist_tracks) + index
+            await self.mass.db.add_playlist_track(playlist.item_id, track.item_id, new_pos)
         # actually add the tracks to the playlist on the provider
-        await self.providers[playlist_prov['provider']].add_playlist_tracks(playlist_prov['item_id'], track_ids_to_add)
-        # schedule sync
-        self.mass.event_loop.create_task(self.sync_playlist_tracks(playlist.item_id, playlist_prov['provider'], playlist_prov['item_id']))
+        return await self.providers[playlist_prov['provider']].add_playlist_tracks(playlist_prov['item_id'], track_ids_to_add)
+
+    async def remove_playlist_tracks(self, playlist_id, tracks:List[Track]):
+        ''' remove tracks from playlist '''
+        # we can only edit playlists that are in the database (marked as editable)
+        playlist = await self.playlist(playlist_id, 'database')
+        if not playlist or not playlist.is_editable:
+            LOGGER.warning("Playlist %s is not editable - skip removal of tracks" %(playlist.name))
+            return False
+        prov_playlist = playlist.provider_ids[0] # playlist can only have one provider (for now)
+        prov_playlist_playlist_id = prov_playlist['item_id']
+        prov_playlist_provider_id = prov_playlist['provider']
+        track_ids_to_remove = []
+        for track in tracks:
+            if not track.provider == 'database':
+                # make sure we have a database track
+                track = await self.track(track.item_id, track.provider, lazy=False)
+            # a track can contain multiple versions on the same provider, remove all
+            for track_provider in track.provider_ids:
+                if track_provider['provider'] == prov_playlist_provider_id:
+                    track_ids_to_remove.append(track_provider['item_id'])
+            # remove track from db playlist
+            await self.mass.db.remove_playlist_track(playlist.item_id, track.item_id)
+        # actually remove the tracks from the playlist on the provider
+        return await self.providers[prov_playlist_provider_id].add_playlist_tracks(prov_playlist_playlist_id, track_ids_to_remove)
 
     @run_periodic(3600)
     async def sync_music_providers(self):
index 62798f97243ed3c2ad876e66e1afb0d2a203f750..b4153fd10cbb0bd94c2d222e0f91b387d6b5e5e1 100755 (executable)
@@ -82,7 +82,6 @@ class Web():
         app.add_routes([web.get('/api/artists/{artist_id}/toptracks', self.artist_toptracks)])
         app.add_routes([web.get('/api/artists/{artist_id}/albums', self.artist_albums)])
         app.add_routes([web.get('/api/albums/{album_id}/tracks', self.album_tracks)])
-        app.add_routes([web.get('/api/{media_type}/{media_id}/{action}', self.get_item)])
         app.add_routes([web.get('/api/{media_type}/{media_id}', self.get_item)])
         app.add_routes([web.get('/api/{media_type}', self.get_items)])
         app.add_routes([web.get('/', self.index)])
@@ -118,7 +117,8 @@ class Web():
         media_type_str = request.match_info.get('media_type')
         media_type = media_type_from_string(media_type_str)
         media_id = request.match_info.get('media_id')
-        action = request.match_info.get('action','')
+        # optional params
+        action = request.rel_url.query.get('action','')
         action_details = request.rel_url.query.get('action_details')
         lazy = request.rel_url.query.get('lazy', '') != 'false'
         provider = request.rel_url.query.get('provider')
index 7ac1bec962ea9ac4ca4465448eadb52d5e73dade..c81d905e6acd4a12cb9170d791e2f130a583ca30 100644 (file)
@@ -52,27 +52,19 @@ const routes = [
     },
 ]
 
-let router = new VueRouter({
-    //mode: 'history',
-    routes // short for `routes: routes`
-})
-
-router.beforeEach((to, from, next) => {
-    next()
-})
-
 const globalStore = new Vue({
     data: {
         windowtitle: 'Home',
         loading: false,
-        showplaymenu: false,
-        showsearchbox: false,
-        playmenuitem: null,
+        showcontextmenu: false,
+        contextmenuitem: null,
+        contextmenucontext: null,
         server: null,
         apiAddress: null,
         wsAddress: null
     }
 })
+
 Vue.prototype.$globals = globalStore;
 Vue.prototype.isMobile = isMobile;
 Vue.prototype.isInStandaloneMode = isInStandaloneMode;
@@ -80,6 +72,15 @@ Vue.prototype.toggleLibrary = toggleLibrary;
 Vue.prototype.showPlayMenu = showPlayMenu;
 Vue.prototype.clickItem= clickItem;
 
+let router = new VueRouter({
+    //mode: 'history',
+    routes // short for `routes: routes`
+})
+
+router.beforeEach((to, from, next) => {
+    next()
+})
+
 const i18n = new VueI18n({
     locale: navigator.language.split('-')[0],
     fallbackLocale: 'en',
@@ -117,5 +118,15 @@ var app = new Vue({
     },
     data: { },
     methods: {},
-    router
+    router,
+    template: `
+        <v-app light>
+            <v-content>
+                <headermenu></headermenu>
+                <player></player>
+                <router-view app :key="$route.path"></router-view>               
+            </v-content>
+            <loading :active.sync="$globals.loading" :can-cancel="true" color="#2196f3" loader="dots"></loading>
+        </v-app>
+    `
 })
\ No newline at end of file
diff --git a/music_assistant/web/components/addtoplaylistdialog.vue.js b/music_assistant/web/components/addtoplaylistdialog.vue.js
new file mode 100644 (file)
index 0000000..97eba4a
--- /dev/null
@@ -0,0 +1,173 @@
+Vue.component("addtoplaylistdialog", {\r
+       template: `\r
+       <v-dialog :value="value" @input="$emit('input', $event)" max-width="500px">\r
+        <v-card>\r
+                       <v-list>\r
+                               <v-subheader class="title">{{ header }}</v-subheader>\r
+                               <v-subheader>{{ subheader }}</v-subheader>\r
+                               <div v-for="(item, index) in menuItems">\r
+                                       <v-list-tile avatar @click="itemCommand(item.action)">\r
+                                               <v-list-tile-avatar>\r
+                                                       <v-icon>{{item.icon}}</v-icon>\r
+                                               </v-list-tile-avatar>\r
+                                               <v-list-tile-content>\r
+                                                       <v-list-tile-title>{{ $t(item.label) }}</v-list-tile-title>\r
+                                               </v-list-tile-content>\r
+                                       </v-list-tile>\r
+                                       <v-divider></v-divider>\r
+                               </div>\r
+                       </v-list>\r
+               </v-card>\r
+      </v-dialog>\r
+`,\r
+       props: ['value', 'active_player'],\r
+       data () { \r
+               return {\r
+                       mediaPlayItems: [\r
+                               {\r
+                                       label: "play_now",\r
+                                       action: "play",\r
+                                       icon: "play_circle_outline"\r
+                               },\r
+                               {\r
+                                       label: "play_next",\r
+                                       action: "next",\r
+                                       icon: "queue_play_next"\r
+                               },\r
+                               {\r
+                                       label: "add_queue",\r
+                                       action: "add",\r
+                                       icon: "playlist_add"\r
+                               }\r
+                       ],\r
+                       showTrackInfoItem: {\r
+                                       label: "show_info",\r
+                                       action: "info",\r
+                                       icon: "info"\r
+                       },\r
+                       addToPlaylistItem: {\r
+                                       label: "add_playlist",\r
+                                       action: "add_playlist",\r
+                                       icon: "add_circle_outline"\r
+                       },\r
+                       removeFromPlaylistItem: {\r
+                                       label: "remove_playlist",\r
+                                       action: "remove_playlist",\r
+                                       icon: "remove_circle_outline"\r
+                       },\r
+                       playerQueueItems: [\r
+                       ],\r
+                       playlists: [],\r
+                       show_playlists: false\r
+               }\r
+       },\r
+       mounted() { },\r
+       created() { },\r
+       computed: {\r
+               menuItems() {\r
+                       if (!this.$globals.contextmenuitem)\r
+                               return [];\r
+                       else if (this.show_playlists)\r
+                               return this.playlists;\r
+                       else if (this.$globals.contextmenucontext == 'playerqueue')\r
+                               return this.playerQueueItems; // TODO: return queue contextmenu\r
+                       else if (this.$globals.contextmenucontext == 'trackdetails') {\r
+                               // track details\r
+                               var items = [];\r
+                               items.push(...this.mediaPlayItems);\r
+                               items.push(this.addToPlaylistItem);\r
+                               return items;\r
+                       }\r
+                       else if (this.$globals.contextmenuitem.media_type == 3) {\r
+                               // track item in list\r
+                               var items = [];\r
+                               items.push(...this.mediaPlayItems);\r
+                               items.push(this.showTrackInfoItem);\r
+                               items.push(this.addToPlaylistItem);\r
+                               if (this.$globals.contextmenucontext.is_editable)\r
+                                       items.push(this.removeFromPlaylistItem);\r
+                               return items;\r
+                       }\r
+                       else {\r
+                               // all other playable media\r
+                               return this.mediaPlayItems;\r
+                       }\r
+               },\r
+               header() {\r
+                       return !!this.$globals.contextmenuitem ? this.$globals.contextmenuitem.name : '';\r
+               },\r
+               subheader() {\r
+                       if (!!this.active_player)\r
+                               return this.$t('play_on') + this.active_player.name;\r
+                       else\r
+                               return "";\r
+               }\r
+       },\r
+       methods: { \r
+               itemCommand(cmd) {\r
+                       console.log('itemCommand: ' + cmd);\r
+               if (cmd == 'info') {\r
+                               // show track info\r
+                               this.$router.push({ path: '/tracks/' + this.$globals.contextmenuitem.item_id, query: {provider: this.$globals.contextmenuitem.provider}})\r
+                               this.$globals.showcontextmenu = false;\r
+                       }       \r
+                       else if (cmd == 'add_playlist') {\r
+                               // add to playlist\r
+                               console.log(`add ${this.$globals.contextmenuitem.name} to playlist?`);\r
+                               this.getPlaylists();\r
+                               this.show_playlists = true;\r
+                       }\r
+                       else if (cmd == 'remove_playlist') {\r
+                               // remove track from playlist\r
+                               this.playlistAddRemove(this.$globals.contextmenuitem, this.$globals.contextmenucontext.item_id, 'playlist_remove');\r
+                               this.$globals.showcontextmenu = false;\r
+                       }\r
+                       else {\r
+                               // assume play command\r
+                               this.$emit('playItem', this.$globals.contextmenuitem, cmd)\r
+                               this.$globals.showcontextmenu = false;\r
+                       }\r
+                       \r
+               },\r
+               playlistAddRemove(track, playlist_id, action='playlist_add') {\r
+                       /// add or remove track on playlist\r
+                       var url = `${this.$globals.server}api/track/${track.item_id}`;\r
+                       console.log('loading ' + url);\r
+                       axios\r
+                               .get(url, { params: { \r
+                                       provider: track.provider, \r
+                                       action: action, \r
+                                       action_details: playlist_id\r
+                               }})\r
+                               .then(result => {\r
+                                       console.log(result);\r
+                                       // reload playlist\r
+                                       if (action == 'playlist_remove')\r
+                                               this.$router.go()\r
+                                       })\r
+                               .catch(error => {\r
+                                       console.log("error", error);\r
+                               });\r
+               },\r
+               getPlaylists() {\r
+                       // get all editable playlists\r
+                       const api_url = this.$globals.apiAddress + 'playlists';\r
+                       axios\r
+                               .get(api_url, { })\r
+                               .then(result => {\r
+                                       let items = []\r
+                                       for (var item of result.data) {\r
+                                               if (item.item_id != this.$globals.contextmenucontext.item_id)\r
+                                                       if (item.is_editable)\r
+                                                               items.push(item);\r
+                                       }\r
+                                       console.log(items);\r
+                                       this.playlists = items;\r
+                               })\r
+                               .catch(error => {\r
+                                       console.log("error", error);\r
+                                       this.playlists = [];\r
+                       });\r
+               }\r
+       }\r
+  })\r
diff --git a/music_assistant/web/components/contextmenu.vue.js b/music_assistant/web/components/contextmenu.vue.js
new file mode 100644 (file)
index 0000000..554987b
--- /dev/null
@@ -0,0 +1,156 @@
+Vue.component("contextmenu", {\r
+       template: `\r
+       <listdialog v-model="$globals.showcontextmenu" \r
+               v-on:onClick="itemCommand" \r
+               :items=menuItems \r
+               :header="header" \r
+               :subheader="subheader" \r
+       </listdialog>\r
+`,\r
+       props: ['active_player'],\r
+       data () { \r
+               return {\r
+                       mediaPlayItems: [\r
+                               {\r
+                                       label: "play_now",\r
+                                       action: "play",\r
+                                       icon: "play_circle_outline"\r
+                               },\r
+                               {\r
+                                       label: "play_next",\r
+                                       action: "next",\r
+                                       icon: "queue_play_next"\r
+                               },\r
+                               {\r
+                                       label: "add_queue",\r
+                                       action: "add",\r
+                                       icon: "playlist_add"\r
+                               }\r
+                       ],\r
+                       showTrackInfoItem: {\r
+                                       label: "show_info",\r
+                                       action: "info",\r
+                                       icon: "info"\r
+                       },\r
+                       addToPlaylistItem: {\r
+                                       label: "add_playlist",\r
+                                       action: "add_playlist",\r
+                                       icon: "add_circle_outline"\r
+                       },\r
+                       removeFromPlaylistItem: {\r
+                                       label: "remove_playlist",\r
+                                       action: "remove_playlist",\r
+                                       icon: "remove_circle_outline"\r
+                       },\r
+                       playerQueueItems: [\r
+                       ]\r
+               }\r
+       },\r
+       mounted() { },\r
+       created() { },\r
+       computed: {\r
+               menuItems() {\r
+                       if (!this.$globals.contextmenuitem)\r
+                               return [];\r
+                       else if (this.$globals.contextmenucontext == 'playerqueue')\r
+                               return this.playerQueueItems; // TODO: return queue contextmenu\r
+                       else if (this.$globals.contextmenucontext == 'trackdetails') {\r
+                               // track details\r
+                               var items = [];\r
+                               items.push(...this.mediaPlayItems);\r
+                               items.push(this.addToPlaylistItem);\r
+                               return items;\r
+                       }\r
+                       else if (this.$globals.contextmenuitem.media_type == 3) {\r
+                               // track item in list\r
+                               var items = [];\r
+                               items.push(...this.mediaPlayItems);\r
+                               items.push(this.showTrackInfoItem);\r
+                               items.push(this.addToPlaylistItem);\r
+                               if (this.$globals.contextmenucontext.is_editable)\r
+                                       items.push(this.removeFromPlaylistItem);\r
+                               return items;\r
+                       }\r
+                       else {\r
+                               // all other playable media\r
+                               return this.mediaPlayItems;\r
+                       }\r
+               },\r
+               header() {\r
+                       return !!this.$globals.contextmenuitem ? this.$globals.contextmenuitem.name : '';\r
+               },\r
+               subheader() {\r
+                       if (!!this.active_player)\r
+                               return this.$t('play_on') + this.active_player.name;\r
+                       else\r
+                               return "";\r
+               }\r
+       },\r
+       methods: { \r
+               itemCommand(cmd) {\r
+                       console.log('itemCommand: ' + cmd);\r
+               if (cmd == 'info') {\r
+                               // show track info\r
+                               this.$router.push({ path: '/tracks/' + this.$globals.contextmenuitem.item_id, query: {provider: this.$globals.contextmenuitem.provider}})\r
+                               this.$globals.showcontextmenu = false;\r
+                       }       \r
+                       else if (cmd == 'add_playlist') {\r
+                               // add to playlist\r
+                               console.log(`add ${this.$globals.contextmenuitem.name} to playlist?`);\r
+                               this.getPlaylists();\r
+                               this.show_playlists = true;\r
+                       }\r
+                       else if (cmd == 'remove_playlist') {\r
+                               // remove track from playlist\r
+                               this.playlistAddRemove(this.$globals.contextmenuitem, this.$globals.contextmenucontext.item_id, 'playlist_remove');\r
+                               this.$globals.showcontextmenu = false;\r
+                       }\r
+                       else {\r
+                               // assume play command\r
+                               this.$emit('playItem', this.$globals.contextmenuitem, cmd)\r
+                               this.$globals.showcontextmenu = false;\r
+                       }\r
+                       \r
+               },\r
+               playlistAddRemove(track, playlist_id, action='playlist_add') {\r
+                       /// add or remove track on playlist\r
+                       var url = `${this.$globals.server}api/track/${track.item_id}`;\r
+                       console.log('loading ' + url);\r
+                       axios\r
+                               .get(url, { params: { \r
+                                       provider: track.provider, \r
+                                       action: action, \r
+                                       action_details: playlist_id\r
+                               }})\r
+                               .then(result => {\r
+                                       console.log(result);\r
+                                       // reload playlist\r
+                                       if (action == 'playlist_remove')\r
+                                               this.$router.go()\r
+                                       })\r
+                               .catch(error => {\r
+                                       console.log("error", error);\r
+                               });\r
+               },\r
+               getPlaylists() {\r
+                       // get all editable playlists\r
+                       const api_url = this.$globals.apiAddress + 'playlists';\r
+                       axios\r
+                               .get(api_url, { })\r
+                               .then(result => {\r
+                                       let items = []\r
+                                       for (var item of result.data) {\r
+                                               if (item.item_id != this.$globals.contextmenucontext.item_id)\r
+                                                       if (item.is_editable)\r
+                                                               items.push(item);\r
+                                       }\r
+                                       console.log(items);\r
+                                       this.playlists = items;\r
+                               })\r
+                               .catch(error => {\r
+                                       console.log("error", error);\r
+                                       this.playlists = [];\r
+                       });\r
+               }\r
+       }\r
+  })\r
index 15c97a0720398a85b47a67b31b6e5f561139f692..1fd42e3b7e600810a9d09669f053168f4e76e9b1 100644 (file)
@@ -16,12 +16,12 @@ Vue.component("infoheader", {
               \r
               <!-- left side: cover image -->\r
               <v-flex xs5 pa-4 v-if="!isMobile()">\r
-                                                               <v-img :src="getThumb()" lazy-src="./images/default_artist.png" width="250px" height="250px" style="border: 4px solid grey;border-radius: 15px;"></v-img>\r
-                                                               \r
-                                                               <!-- tech specs and provider icons -->\r
-                                                               <div style="margin-top:10px;">\r
-                                                                       <providericons v-bind:item="info" :height="30" :compact="false"/>\r
-                                                               </div>\r
+                               <v-img :src="getThumb()" lazy-src="./images/default_artist.png" width="250px" height="250px" style="border: 4px solid grey;border-radius: 15px;"></v-img>\r
+                               \r
+                               <!-- tech specs and provider icons -->\r
+                               <div style="margin-top:10px;">\r
+                                       <providericons v-bind:item="info" :height="30" :compact="false"/>\r
+                               </div>\r
               </v-flex>\r
               \r
               <v-flex>\r
@@ -51,7 +51,7 @@ Vue.component("infoheader", {
 \r
                   <!-- play/info buttons -->\r
                   <div style="margin-left:8px;">\r
-                      <v-btn color="blue-grey" @click="showPlayMenu(info)"  class="white--text"><v-icon v-if="!isMobile()" left dark>play_circle_outline</v-icon>{{ $t('play') }}</v-btn>\r
+                      <v-btn color="blue-grey" @click="showPlayMenu(info, context)"  class="white--text"><v-icon v-if="!isMobile()" left dark>play_circle_outline</v-icon>{{ $t('play') }}</v-btn>\r
                       <v-btn v-if="!!info.in_library && info.in_library.length == 0" color="blue-grey" @click="toggleLibrary(info)"  class="white--text"><v-icon v-if="!isMobile()" left dark>favorite_border</v-icon>{{ $t('add_library') }}</v-btn>\r
                       <v-btn v-if="!!info.in_library && info.in_library.length > 0" color="blue-grey" @click="toggleLibrary(info)"  class="white--text"><v-icon v-if="!isMobile()" left dark>favorite</v-icon>{{ $t('remove_library') }}</v-btn>\r
                   </div>\r
@@ -74,7 +74,7 @@ Vue.component("infoheader", {
           </v-card>\r
         </v-flex>\r
 `,\r
-       props: ['info'],\r
+       props: ['info', 'context'],\r
        data (){\r
                return{}\r
        },\r
diff --git a/music_assistant/web/components/listdialog.vue.js b/music_assistant/web/components/listdialog.vue.js
new file mode 100644 (file)
index 0000000..6df30bb
--- /dev/null
@@ -0,0 +1,31 @@
+Vue.component("listdialog", {\r
+       template: `\r
+       <v-dialog :value="value" @input="$emit('input', $event)" max-width="500px">\r
+        <v-card>\r
+                       <v-list>\r
+                               <v-subheader class="title">{{ header }}</v-subheader>\r
+                               <v-subheader>{{ subheader }}</v-subheader>\r
+                               <div v-for="(item, index) in items">\r
+                                       <v-list-tile avatar @click="$emit('onClick', item)">\r
+                                               <v-list-tile-avatar>\r
+                                                       <v-icon>{{item.icon}}</v-icon>\r
+                                               </v-list-tile-avatar>\r
+                                               <v-list-tile-content>\r
+                                                       <v-list-tile-title>{{ $t(item.label) }}</v-list-tile-title>\r
+                                               </v-list-tile-content>\r
+                                       </v-list-tile>\r
+                                       <v-divider></v-divider>\r
+                               </div>\r
+                       </v-list>\r
+               </v-card>\r
+      </v-dialog>\r
+`,\r
+       props: ['value', 'items', 'header', 'subheader'],\r
+       data () { \r
+               return {}\r
+       },\r
+       mounted() { },\r
+       created() { },\r
+       computed: { },\r
+       methods: { }\r
+  })\r
index 687c69c947f67b0104fc4ab0b877ec8f672b921d..b29a326012cbec4a7c1f52a4e0c70b8d3527f1c8 100755 (executable)
@@ -4,7 +4,7 @@ Vue.component("listviewItem", {
     <v-list-tile
     avatar
     ripple
-    @click="clickItem(item)">
+    @click="clickItem(item, context)">
 
           <v-list-tile-avatar color="grey" v-if="!hideavatar">
               <img v-if="(item.media_type != 3) && item.metadata && item.metadata.image" :src="item.metadata.image"/>
@@ -59,14 +59,14 @@ Vue.component("listviewItem", {
           </v-list-tile-action> 
         
           <!-- menu button/icon -->
-          <v-icon v-if="!hidemenu" @click="showPlayMenu(item)" @click.stop="" color="grey lighten-1" style="margin-right:-10px;padding-left:10px">more_vert</v-icon>
+          <v-icon v-if="!hidemenu" @click="showPlayMenu(item, context)" @click.stop="" color="grey lighten-1" style="margin-right:-10px;padding-left:10px">more_vert</v-icon>
           
 
         </v-list-tile>
         <v-divider v-if="index + 1 < totalitems" :key="index"></v-divider>
         </div>
      `,
-props: ['item', 'index', 'totalitems', 'hideavatar', 'hidetracknum', 'hideproviders', 'hidemenu', 'hidelibrary', 'hideduration'],
+props: ['item', 'context', 'index', 'totalitems', 'hideavatar', 'hidetracknum', 'hideproviders', 'hidemenu', 'hidelibrary', 'hideduration'],
 data() {
   return {}
   },
index b9fa0ec128223e1ef7c2f28a4411f9f8f02b5f4b..92f030999ba17905f39c286b92eeb0aa2a031833 100755 (executable)
@@ -135,7 +135,7 @@ Vue.component("player", {
             </div>
         </v-list>
     </v-navigation-drawer>
-    <playmenu v-model="$globals.showplaymenu" v-on:playItem="playItem" :active_player="active_player" />
+    <contextmenu v-on:playItem="playItem" :active_player="active_player" />
   </div>
   
   `,
diff --git a/music_assistant/web/components/playmenu.vue.js b/music_assistant/web/components/playmenu.vue.js
deleted file mode 100644 (file)
index 611ecc3..0000000
+++ /dev/null
@@ -1,93 +0,0 @@
-Vue.component("playmenu", {\r
-       template: `\r
-       <v-dialog :value="value" @input="$emit('input', $event)" max-width="500px" v-if="$globals.playmenuitem">\r
-        <v-card>\r
-               <v-list>\r
-               <v-subheader class="title">{{ !!$globals.playmenuitem ? $globals.playmenuitem.name : '' }}</v-subheader>\r
-                       <v-subheader>{{ $t('play_on') }} {{ active_player.name }}</v-subheader>\r
-                       \r
-                       <v-list-tile avatar @click="itemClick('play')">\r
-                               <v-list-tile-avatar>\r
-                                       <v-icon>play_circle_outline</v-icon>\r
-                               </v-list-tile-avatar>\r
-                               <v-list-tile-content>\r
-                                       <v-list-tile-title>{{ $t('play_now') }}</v-list-tile-title>\r
-                               </v-list-tile-content>\r
-                       </v-list-tile>\r
-                       <v-divider></v-divider>\r
-\r
-                       <v-list-tile avatar @click="itemClick('next')">\r
-                               <v-list-tile-avatar>\r
-                                       <v-icon>queue_play_next</v-icon>\r
-                               </v-list-tile-avatar>\r
-                               <v-list-tile-content>\r
-                                       <v-list-tile-title>{{ $t('play_next') }}</v-list-tile-title>\r
-                               </v-list-tile-content>\r
-                       </v-list-tile>\r
-                       <v-divider></v-divider>\r
-\r
-                       <v-list-tile avatar @click="itemClick('add')">\r
-                               <v-list-tile-avatar>\r
-                                       <v-icon>playlist_add</v-icon>\r
-                               </v-list-tile-avatar>\r
-                               <v-list-tile-content>\r
-                                       <v-list-tile-title>{{ $t('add_queue') }}</v-list-tile-title>\r
-                               </v-list-tile-content>\r
-                       </v-list-tile>\r
-                       <v-divider></v-divider>\r
-\r
-                       <v-list-tile avatar @click="itemClick('info')" v-if="$globals.playmenuitem.media_type == 3">\r
-                               <v-list-tile-avatar>\r
-                                       <v-icon>info</v-icon>\r
-                               </v-list-tile-avatar>\r
-                               <v-list-tile-content>\r
-                                       <v-list-tile-title>{{ $t('show_info') }}</v-list-tile-title>\r
-                               </v-list-tile-content>\r
-                       </v-list-tile>\r
-                       <v-divider v-if="$globals.playmenuitem.media_type == 3"/>\r
-\r
-                       <v-list-tile avatar @click="itemClick('add_playlist')" v-if="$globals.playmenuitem.media_type == 3">\r
-                               <v-list-tile-avatar>\r
-                                       <v-icon>add_circle_outline</v-icon>\r
-                               </v-list-tile-avatar>\r
-                               <v-list-tile-content>\r
-                                       <v-list-tile-title>{{ $t('add_playlist') }}</v-list-tile-title>\r
-                               </v-list-tile-content>\r
-                       </v-list-tile>\r
-                       <v-divider v-if="$globals.playmenuitem.media_type == 3"/>\r
-\r
-                       <v-list-tile avatar @click="itemClick('remove_playlist')" v-if="$globals.playmenuitem.media_type == 3 && this.$route.path.startsWith('/playlists/')">\r
-                               <v-list-tile-avatar>\r
-                                       <v-icon>remove_circle_outline</v-icon>\r
-                               </v-list-tile-avatar>\r
-                               <v-list-tile-content>\r
-                                       <v-list-tile-title>{{ $t('remove_playlist') }}</v-list-tile-title>\r
-                               </v-list-tile-content>\r
-                       </v-list-tile>\r
-                       <v-divider v-if="$globals.playmenuitem.media_type == 3  && this.$route.path.startsWith('/playlists/')"/>\r
-                       \r
-               </v-list>\r
-        </v-card>\r
-      </v-dialog>\r
-`,\r
-       props: ['value', 'active_player'],\r
-       data (){\r
-               return{\r
-                       fav: true,\r
-                       message: false,\r
-                       hints: true,\r
-                       }\r
-       },\r
-       mounted() { },\r
-       created() { },\r
-       methods: { \r
-               itemClick(cmd) {\r
-               if (cmd == 'info')\r
-                               this.$router.push({ path: '/tracks/' + this.$globals.playmenuitem.item_id, query: {provider: this.$globals.playmenuitem.provider}})\r
-                       else\r
-                               this.$emit('playItem', this.$globals.playmenuitem, cmd)\r
-                       // close dialog\r
-                       this.$globals.showplaymenu = false;\r
-       },\r
-       }\r
-  })\r
diff --git a/music_assistant/web/components/searchbox.vue.js b/music_assistant/web/components/searchbox.vue.js
deleted file mode 100644 (file)
index 1570ab6..0000000
+++ /dev/null
@@ -1,50 +0,0 @@
-Vue.component("searchbox", {
-  template: `
-  <v-dialog :value="$globals.showsearchbox" @input="$emit('input', $event)" max-width="500px">
-      <v-text-field
-            solo
-            clearable
-            :label="$t('type_to_search')"
-            prepend-inner-icon="search"
-            v-model="searchQuery">
-          </v-text-field>
-      </v-dialog>
-  `,
-  data () {
-    return {
-      searchQuery: "",
-    }
-  },
-  props: ['value'],
-  mounted () {
-    this.searchQuery = "" // TODO: set to last searchquery ?
-  },
-  watch: {
-    searchQuery: {
-      handler: _.debounce(function (val) {
-        this.onSearch();
-        // if (this.searchQuery)
-        //     this.$globals.showsearchbox = false;
-      }, 1000)
-    },
-    newSearchQuery (val) {
-      this.searchQuery = val
-    }
-  },
-  computed: {},
-  methods: {
-    onSearch () {
-      //this.$emit('clickSearch', this.searchQuery)
-      console.log(this.searchQuery);
-      router.push({ path: '/search', query: {searchQuery: this.searchQuery}});
-    },
-  }
-})
-/* <style>
-.searchbar {
-  padding: 1rem 1.5rem!important;
-  width: 100%;
-  box-shadow: 0 0 70px 0 rgba(0, 0, 0, 0.3);
-  background: #fff;
-}
-</style> */
\ No newline at end of file
index 14b39713ca71a500db470304018b392a4fdd0b2b..ac1fae03ec651812223ad2f13d1325b7275cf771 100755 (executable)
     <body>
 
         <div id="app">
-            <v-app light>
-                <v-content>
-                    <headermenu></headermenu>
-                    <player></player>
-                    <router-view app :key="$route.path"></router-view>      
-                    <searchbox/>             
-                </v-content>
-                <loading :active.sync="$globals.loading" :can-cancel="true" color="#2196f3" loader="dots"></loading>
-            </v-app>
+            
         </div>
 
 
         <script src='./components/player.vue.js'></script>
         <script src='./components/listviewItem.vue.js'></script>
         <script src='./components/readmore.vue.js'></script>
-        <script src='./components/playmenu.vue.js'></script>
+        <script src='./components/listdialog.vue.js'></script>
+        <script src='./components/contextmenu.vue.js'></script>
+        <script src='./components/addtoplaylistdialog.vue.js'></script>
         <script src='./components/volumecontrol.vue.js'></script>
         <script src='./components/infoheader.vue.js'></script>
         <script src='./components/providericons.vue.js'></script>
-        <script src='./components/searchbox.vue.js'></script>
         
         <script src='./strings.js'></script>
         <script src='./app.js'></script>
index 2f4b47900e35da0665b610b3371af014e7b71230..9f08215d39fe7db119261ecd44177f6708dbdbe4 100644 (file)
@@ -1,22 +1,26 @@
 const isMobile = () => (document.body.clientWidth < 800);
 const isInStandaloneMode = () => ('standalone' in window.navigator) && (window.navigator.standalone);
 
-function showPlayMenu (item) {
-    this.$globals.playmenuitem = item;
-    this.$globals.showplaymenu = !this.$globals.showplaymenu;
+function showPlayMenu (item, context=null) {
+    /// make the contextmenu visible
+    console.log(context);
+    this.$globals.contextmenuitem = item;
+    this.$globals.contextmenucontext = context;
+    this.$globals.showcontextmenu = !this.$globals.showcontextmenu;
     }
 
-function clickItem (item) {
+function clickItem (item, context=null) {
+    /// triggered when user clicks on mediaitem
     var endpoint = "";
     if (item.media_type == 1)
         endpoint = "/artists/"
     else if (item.media_type == 2)
         endpoint = "/albums/"
     else if (item.media_type == 3 || item.media_type == 5)
-        {
-        this.showPlayMenu(item);
+    {
+        this.showPlayMenu(item, context);
         return;
-        }
+    }
     else if (item.media_type == 4)
         endpoint = "/playlists/"
     item_id = item.item_id.toString();
@@ -39,15 +43,16 @@ String.prototype.formatDuration = function () {
         return hours+':'+minutes+':'+seconds;
 }
 function toggleLibrary (item) {
+    /// triggered when user clicks the library (heart) button
     var endpoint = this.$globals.server + "api/" + item.media_type + "/";
     item_id = item.item_id.toString();
-    var action = "/library_remove"
+    var action = "library_remove"
     if (item.in_library.length == 0)
-        action = "/library_add"
-    var url = endpoint + item_id + action;
+        action = "library_add"
+    var url = endpoint + item_id;
     console.log('loading ' + url);
     axios
-        .get(url, { params: { provider: item.provider }})
+        .get(url, { params: { provider: item.provider, action: action }})
         .then(result => {
             data = result.data;
             console.log(data);
@@ -59,5 +64,6 @@ function toggleLibrary (item) {
         .catch(error => {
             console.log("error", error);
         });
-
 };
+
+
index 056bc76a8b1b39b71558c0068a98cd9acc3f9c83..ebdb0cdbe889e277eb78b055e0e5b02d73abd200 100755 (executable)
@@ -1,7 +1,7 @@
 var AlbumDetails = Vue.component('AlbumDetails', {
   template: `
   <section>
-      <infoheader v-bind:info="info"/>
+      <infoheader v-bind:info="info" :context="'albumdetails'"/>
       <v-tabs
           v-model="active"
           color="transparent"
@@ -20,6 +20,7 @@ var AlbumDetails = Vue.component('AlbumDetails', {
                     v-bind:index="index"
                     :hideavatar="true"
                     :hideproviders="isMobile()"
+                    :context="'albumtracks'"
                     >
                 </listviewItem>
               </v-list>
@@ -36,6 +37,7 @@ var AlbumDetails = Vue.component('AlbumDetails', {
                       :key="item.db_id"
                       v-bind:totalitems="albumversions.length"
                       v-bind:index="index"
+                      :context="'albumtracks'"
                       >
                   </listviewItem>
               </v-list>
@@ -62,7 +64,6 @@ var AlbumDetails = Vue.component('AlbumDetails', {
   },
   methods: {
     getInfo () {
-      this.$globals.loading = true;
       const api_url = this.$globals.server + 'api/albums/' + this.media_id
       axios
         .get(api_url, { params: { provider: this.provider }})
@@ -70,13 +71,14 @@ var AlbumDetails = Vue.component('AlbumDetails', {
           data = result.data;
           this.info = data;
           this.getAlbumVersions()
-          this.$globals.loading = false;
+          this.$globals.curContext = data;
         })
         .catch(error => {
           console.log("error", error);
         });
     },
     getAlbumTracks () {
+      this.$globals.loading = true;
       const api_url = this.$globals.server + 'api/albums/' + this.media_id + '/tracks'
       axios
         .get(api_url, { params: { offset: this.offset, limit: 50, provider: this.provider}})
@@ -84,6 +86,7 @@ var AlbumDetails = Vue.component('AlbumDetails', {
           data = result.data;
           this.albumtracks.push(...data);
           this.offset += 50;
+          this.$globals.loading = false;
         })
         .catch(error => {
           console.log("error", error);
index 379f83541b4910ff313b92a5b1754ea027413b2d..cc6a7fba4358ee8966206204146f21a28d178774 100755 (executable)
@@ -1,7 +1,7 @@
 var ArtistDetails = Vue.component('ArtistDetails', {
   template: `
   <section>
-      <infoheader v-bind:info="info"/>
+      <infoheader v-bind:info="info" :context="'artistdetails'"/>
       <v-tabs
           v-model="active"
           color="transparent"
@@ -21,7 +21,9 @@ var ArtistDetails = Vue.component('ArtistDetails', {
                       :hideavatar="isMobile()"
                       :hidetracknum="true"
                       :hideproviders="isMobile()"
-                      :hidelibrary="isMobile()">
+                      :hidelibrary="isMobile()"
+                      :context="'artisttracks'"
+                      >
                   </listviewItem>
                 </v-list>
             </v-card>
@@ -38,6 +40,7 @@ var ArtistDetails = Vue.component('ArtistDetails', {
                         v-bind:totalitems="artistalbums.length"
                         v-bind:index="index"
                         :hideproviders="isMobile()"
+                        :context="'artistalbums'"
                         >
                     </listviewItem>
               </v-list>
@@ -54,8 +57,8 @@ var ArtistDetails = Vue.component('ArtistDetails', {
       artistalbums: [],
       bg_image: "../images/info_gradient.jpg",
       active: null,
-      playmenu: false,
-      playmenuitem: null
+      contextmenu: false,
+      contextmenuitem: null
     }
   },
   created() {
@@ -80,6 +83,7 @@ var ArtistDetails = Vue.component('ArtistDetails', {
         .then(result => {
           data = result.data;
           this.info = data;
+          this.$globals.curContext = data;
           this.$globals.loading = false;
           if (data.is_lazy == true)
               // refresh the info if we got a lazy object
index fbc11c68af24bd36ca655d508ef8602264deccf7..c9ba1141422670fde0e75048ac583ac6f89ae9f8 100755 (executable)
@@ -4,14 +4,16 @@ var Browse = Vue.component('Browse', {
       <v-list two-line>
         <listviewItem 
             v-for="(item, index) in items"
-            :key="item.db_id"
+            :key="item.item_id"
             v-bind:item="item"
             v-bind:totalitems="items.length"
             v-bind:index="index"
             :hideavatar="item.media_type == 3 ? isMobile() : false"
             :hidetracknum="true"
             :hideproviders="isMobile()"
-            :hidelibrary="isMobile() ? true : item.media_type != 3">
+            :hidelibrary="isMobile() ? true : item.media_type != 3"
+            :context="mediatype"
+            >
         </listviewItem>
       </v-list>
     </section>
index f96d3817d16117b44189d14cf26d48dd6dc1ba2d..0414065b458ca197a9d75cedc6b5122d07ee2c3d 100755 (executable)
@@ -1,7 +1,7 @@
 var PlaylistDetails = Vue.component('PlaylistDetails', {
   template: `
   <section>
-      <infoheader v-bind:info="info"/>
+      <infoheader v-bind:info="info" :context="'playlistdetails'"/>
       <v-tabs
           v-model="active"
           color="transparent"
@@ -19,7 +19,9 @@ var PlaylistDetails = Vue.component('PlaylistDetails', {
                       :hideavatar="isMobile()"
                       :hidetracknum="true"
                       :hideproviders="isMobile()"
-                      :hidelibrary="isMobile()">
+                      :hidelibrary="isMobile()"
+                      v-bind:context="info"
+                      >
                   </listviewItem>
                 </v-list>
             </v-card>
@@ -50,6 +52,7 @@ var PlaylistDetails = Vue.component('PlaylistDetails', {
         .then(result => {
           data = result.data;
           this.info = data;
+          this.$globals.curContext = data;
         })
         .catch(error => {
           console.log("error", error);
index 8440ec2c81e9fd3d8dd15c845eb779b2e6812915..c74c8bcea38ac6dd133c3e68074eac6d2eba4389 100755 (executable)
@@ -9,7 +9,9 @@ var Queue = Vue.component('Queue', {
             :hideavatar="isMobile()"
             :hidetracknum="true"
             :hideproviders="isMobile()"
-            :hidelibrary="isMobile()">
+            :hidelibrary="isMobile()"
+            :context="'playerqueue'"
+            >
         </listviewItem>
       </v-list>
       </section>`,
index fd86688e9c3dfbdf4534fc977ef0ae53937e5d5e..2600c5a30f81a4bc0f71705410cd574b9724e6d8 100755 (executable)
@@ -31,7 +31,9 @@ var Search = Vue.component('Search', {
                         :hidetracknum="true"
                         :hideproviders="isMobile()"
                         :hideduration="isMobile()"
-                        :showlibrary="true">
+                        :showlibrary="true"
+                        :context="'searchtracks'"
+                        >
                     </listviewItem>
               </v-list>
             </v-card>
@@ -48,6 +50,7 @@ var Search = Vue.component('Search', {
                       v-bind:totalitems="artists.length"
                       v-bind:index="index"
                       :hideproviders="isMobile()"
+                      :context="'searchartists'"
                       >
                   </listviewItem>
                 </v-list>
@@ -65,6 +68,7 @@ var Search = Vue.component('Search', {
                         v-bind:totalitems="albums.length"
                         v-bind:index="index"
                         :hideproviders="isMobile()"
+                        :context="'searchalbums'"
                         >
                     </listviewItem>
               </v-list>
@@ -81,7 +85,9 @@ var Search = Vue.component('Search', {
                         :key="item.db_id"
                         v-bind:totalitems="playlists.length"
                         v-bind:index="index"
-                        :hidelibrary="true">
+                        :hidelibrary="true"
+                        :context="'searchplaylists'"
+                        >
                     </listviewItem>
               </v-list>
             </v-card>
index 3b428732acb9f052608e694542578f1444b9279e..b2c018bd17760c7a62a918a683bd5d8e6b079925 100755 (executable)
@@ -1,7 +1,7 @@
 var TrackDetails = Vue.component('TrackDetails', {
   template: `
   <section>
-      <infoheader v-bind:info="info"/>
+      <infoheader v-bind:info="info" :context="'trackdetails'"/>
       <v-tabs
           v-model="active"
           color="transparent"
@@ -21,7 +21,9 @@ var TrackDetails = Vue.component('TrackDetails', {
                       :hideavatar="isMobile()"
                       :hidetracknum="true"
                       :hideproviders="isMobile()"
-                      :hidelibrary="isMobile()">
+                      :hidelibrary="isMobile()"
+                      :context="'trackversions'"
+                      >
                   </listviewItem>
               </v-list>
             </v-card>
@@ -52,6 +54,7 @@ var TrackDetails = Vue.component('TrackDetails', {
         .then(result => {
           data = result.data;
           this.info = data;
+          this.$globals.curContext = data;
           this.getTrackVersions()
           this.$globals.loading = false;
         })