From: marcelveldt Date: Sat, 26 Oct 2019 23:46:01 +0000 (+0200) Subject: work in progress o frontend X-Git-Url: https://git.kitaultman.com/?a=commitdiff_plain;h=d3526cdef290ffcb90209c044e1854eb3eedf398;p=music-assistant-server.git work in progress o frontend --- diff --git a/music_assistant/database.py b/music_assistant/database.py index 1a2f5544..7f087f1f 100755 --- a/music_assistant/database.py +++ b/music_assistant/database.py @@ -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''' diff --git a/music_assistant/music_manager.py b/music_assistant/music_manager.py index d52bf90d..c7533542 100755 --- a/music_assistant/music_manager.py +++ b/music_assistant/music_manager.py @@ -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): diff --git a/music_assistant/web.py b/music_assistant/web.py index 62798f97..b4153fd1 100755 --- a/music_assistant/web.py +++ b/music_assistant/web.py @@ -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') diff --git a/music_assistant/web/app.js b/music_assistant/web/app.js index 7ac1bec9..c81d905e 100644 --- a/music_assistant/web/app.js +++ b/music_assistant/web/app.js @@ -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: ` + + + + + + + + + ` }) \ 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 index 00000000..97eba4a7 --- /dev/null +++ b/music_assistant/web/components/addtoplaylistdialog.vue.js @@ -0,0 +1,173 @@ +Vue.component("addtoplaylistdialog", { + template: ` + + + + {{ header }} + {{ subheader }} +
+ + + {{item.icon}} + + + {{ $t(item.label) }} + + + +
+
+
+
+`, + props: ['value', 'active_player'], + data () { + return { + mediaPlayItems: [ + { + label: "play_now", + action: "play", + icon: "play_circle_outline" + }, + { + label: "play_next", + action: "next", + icon: "queue_play_next" + }, + { + label: "add_queue", + action: "add", + icon: "playlist_add" + } + ], + showTrackInfoItem: { + label: "show_info", + action: "info", + icon: "info" + }, + addToPlaylistItem: { + label: "add_playlist", + action: "add_playlist", + icon: "add_circle_outline" + }, + removeFromPlaylistItem: { + label: "remove_playlist", + action: "remove_playlist", + icon: "remove_circle_outline" + }, + playerQueueItems: [ + ], + playlists: [], + show_playlists: false + } + }, + mounted() { }, + created() { }, + computed: { + menuItems() { + if (!this.$globals.contextmenuitem) + return []; + else if (this.show_playlists) + return this.playlists; + else if (this.$globals.contextmenucontext == 'playerqueue') + return this.playerQueueItems; // TODO: return queue contextmenu + else if (this.$globals.contextmenucontext == 'trackdetails') { + // track details + var items = []; + items.push(...this.mediaPlayItems); + items.push(this.addToPlaylistItem); + return items; + } + else if (this.$globals.contextmenuitem.media_type == 3) { + // track item in list + var items = []; + items.push(...this.mediaPlayItems); + items.push(this.showTrackInfoItem); + items.push(this.addToPlaylistItem); + if (this.$globals.contextmenucontext.is_editable) + items.push(this.removeFromPlaylistItem); + return items; + } + else { + // all other playable media + return this.mediaPlayItems; + } + }, + header() { + return !!this.$globals.contextmenuitem ? this.$globals.contextmenuitem.name : ''; + }, + subheader() { + if (!!this.active_player) + return this.$t('play_on') + this.active_player.name; + else + return ""; + } + }, + methods: { + itemCommand(cmd) { + console.log('itemCommand: ' + cmd); + if (cmd == 'info') { + // show track info + this.$router.push({ path: '/tracks/' + this.$globals.contextmenuitem.item_id, query: {provider: this.$globals.contextmenuitem.provider}}) + this.$globals.showcontextmenu = false; + } + else if (cmd == 'add_playlist') { + // add to playlist + console.log(`add ${this.$globals.contextmenuitem.name} to playlist?`); + this.getPlaylists(); + this.show_playlists = true; + } + else if (cmd == 'remove_playlist') { + // remove track from playlist + this.playlistAddRemove(this.$globals.contextmenuitem, this.$globals.contextmenucontext.item_id, 'playlist_remove'); + this.$globals.showcontextmenu = false; + } + else { + // assume play command + this.$emit('playItem', this.$globals.contextmenuitem, cmd) + this.$globals.showcontextmenu = false; + } + + }, + playlistAddRemove(track, playlist_id, action='playlist_add') { + /// add or remove track on playlist + var url = `${this.$globals.server}api/track/${track.item_id}`; + console.log('loading ' + url); + axios + .get(url, { params: { + provider: track.provider, + action: action, + action_details: playlist_id + }}) + .then(result => { + console.log(result); + // reload playlist + if (action == 'playlist_remove') + this.$router.go() + }) + .catch(error => { + console.log("error", error); + }); + }, + getPlaylists() { + // get all editable playlists + const api_url = this.$globals.apiAddress + 'playlists'; + axios + .get(api_url, { }) + .then(result => { + let items = [] + for (var item of result.data) { + if (item.item_id != this.$globals.contextmenucontext.item_id) + if (item.is_editable) + items.push(item); + } + console.log(items); + this.playlists = items; + }) + .catch(error => { + console.log("error", error); + this.playlists = []; + }); + } + } + }) diff --git a/music_assistant/web/components/contextmenu.vue.js b/music_assistant/web/components/contextmenu.vue.js new file mode 100644 index 00000000..554987b2 --- /dev/null +++ b/music_assistant/web/components/contextmenu.vue.js @@ -0,0 +1,156 @@ +Vue.component("contextmenu", { + template: ` + +`, + props: ['active_player'], + data () { + return { + mediaPlayItems: [ + { + label: "play_now", + action: "play", + icon: "play_circle_outline" + }, + { + label: "play_next", + action: "next", + icon: "queue_play_next" + }, + { + label: "add_queue", + action: "add", + icon: "playlist_add" + } + ], + showTrackInfoItem: { + label: "show_info", + action: "info", + icon: "info" + }, + addToPlaylistItem: { + label: "add_playlist", + action: "add_playlist", + icon: "add_circle_outline" + }, + removeFromPlaylistItem: { + label: "remove_playlist", + action: "remove_playlist", + icon: "remove_circle_outline" + }, + playerQueueItems: [ + ] + } + }, + mounted() { }, + created() { }, + computed: { + menuItems() { + if (!this.$globals.contextmenuitem) + return []; + else if (this.$globals.contextmenucontext == 'playerqueue') + return this.playerQueueItems; // TODO: return queue contextmenu + else if (this.$globals.contextmenucontext == 'trackdetails') { + // track details + var items = []; + items.push(...this.mediaPlayItems); + items.push(this.addToPlaylistItem); + return items; + } + else if (this.$globals.contextmenuitem.media_type == 3) { + // track item in list + var items = []; + items.push(...this.mediaPlayItems); + items.push(this.showTrackInfoItem); + items.push(this.addToPlaylistItem); + if (this.$globals.contextmenucontext.is_editable) + items.push(this.removeFromPlaylistItem); + return items; + } + else { + // all other playable media + return this.mediaPlayItems; + } + }, + header() { + return !!this.$globals.contextmenuitem ? this.$globals.contextmenuitem.name : ''; + }, + subheader() { + if (!!this.active_player) + return this.$t('play_on') + this.active_player.name; + else + return ""; + } + }, + methods: { + itemCommand(cmd) { + console.log('itemCommand: ' + cmd); + if (cmd == 'info') { + // show track info + this.$router.push({ path: '/tracks/' + this.$globals.contextmenuitem.item_id, query: {provider: this.$globals.contextmenuitem.provider}}) + this.$globals.showcontextmenu = false; + } + else if (cmd == 'add_playlist') { + // add to playlist + console.log(`add ${this.$globals.contextmenuitem.name} to playlist?`); + this.getPlaylists(); + this.show_playlists = true; + } + else if (cmd == 'remove_playlist') { + // remove track from playlist + this.playlistAddRemove(this.$globals.contextmenuitem, this.$globals.contextmenucontext.item_id, 'playlist_remove'); + this.$globals.showcontextmenu = false; + } + else { + // assume play command + this.$emit('playItem', this.$globals.contextmenuitem, cmd) + this.$globals.showcontextmenu = false; + } + + }, + playlistAddRemove(track, playlist_id, action='playlist_add') { + /// add or remove track on playlist + var url = `${this.$globals.server}api/track/${track.item_id}`; + console.log('loading ' + url); + axios + .get(url, { params: { + provider: track.provider, + action: action, + action_details: playlist_id + }}) + .then(result => { + console.log(result); + // reload playlist + if (action == 'playlist_remove') + this.$router.go() + }) + .catch(error => { + console.log("error", error); + }); + }, + getPlaylists() { + // get all editable playlists + const api_url = this.$globals.apiAddress + 'playlists'; + axios + .get(api_url, { }) + .then(result => { + let items = [] + for (var item of result.data) { + if (item.item_id != this.$globals.contextmenucontext.item_id) + if (item.is_editable) + items.push(item); + } + console.log(items); + this.playlists = items; + }) + .catch(error => { + console.log("error", error); + this.playlists = []; + }); + } + } + }) diff --git a/music_assistant/web/components/infoheader.vue.js b/music_assistant/web/components/infoheader.vue.js index 15c97a07..1fd42e3b 100644 --- a/music_assistant/web/components/infoheader.vue.js +++ b/music_assistant/web/components/infoheader.vue.js @@ -16,12 +16,12 @@ Vue.component("infoheader", { - - - -
- -
+ + + +
+ +
@@ -51,7 +51,7 @@ Vue.component("infoheader", {
- play_circle_outline{{ $t('play') }} + play_circle_outline{{ $t('play') }} favorite_border{{ $t('add_library') }} favorite{{ $t('remove_library') }}
@@ -74,7 +74,7 @@ Vue.component("infoheader", {
`, - props: ['info'], + props: ['info', 'context'], data (){ return{} }, diff --git a/music_assistant/web/components/listdialog.vue.js b/music_assistant/web/components/listdialog.vue.js new file mode 100644 index 00000000..6df30bbd --- /dev/null +++ b/music_assistant/web/components/listdialog.vue.js @@ -0,0 +1,31 @@ +Vue.component("listdialog", { + template: ` + + + + {{ header }} + {{ subheader }} +
+ + + {{item.icon}} + + + {{ $t(item.label) }} + + + +
+
+
+
+`, + props: ['value', 'items', 'header', 'subheader'], + data () { + return {} + }, + mounted() { }, + created() { }, + computed: { }, + methods: { } + }) diff --git a/music_assistant/web/components/listviewItem.vue.js b/music_assistant/web/components/listviewItem.vue.js index 687c69c9..b29a3260 100755 --- a/music_assistant/web/components/listviewItem.vue.js +++ b/music_assistant/web/components/listviewItem.vue.js @@ -4,7 +4,7 @@ Vue.component("listviewItem", { + @click="clickItem(item, context)"> @@ -59,14 +59,14 @@ Vue.component("listviewItem", { - more_vert + more_vert `, -props: ['item', 'index', 'totalitems', 'hideavatar', 'hidetracknum', 'hideproviders', 'hidemenu', 'hidelibrary', 'hideduration'], +props: ['item', 'context', 'index', 'totalitems', 'hideavatar', 'hidetracknum', 'hideproviders', 'hidemenu', 'hidelibrary', 'hideduration'], data() { return {} }, diff --git a/music_assistant/web/components/player.vue.js b/music_assistant/web/components/player.vue.js index b9fa0ec1..92f03099 100755 --- a/music_assistant/web/components/player.vue.js +++ b/music_assistant/web/components/player.vue.js @@ -135,7 +135,7 @@ Vue.component("player", { - + `, diff --git a/music_assistant/web/components/playmenu.vue.js b/music_assistant/web/components/playmenu.vue.js deleted file mode 100644 index 611ecc36..00000000 --- a/music_assistant/web/components/playmenu.vue.js +++ /dev/null @@ -1,93 +0,0 @@ -Vue.component("playmenu", { - template: ` - - - - {{ !!$globals.playmenuitem ? $globals.playmenuitem.name : '' }} - {{ $t('play_on') }} {{ active_player.name }} - - - - play_circle_outline - - - {{ $t('play_now') }} - - - - - - - queue_play_next - - - {{ $t('play_next') }} - - - - - - - playlist_add - - - {{ $t('add_queue') }} - - - - - - - info - - - {{ $t('show_info') }} - - - - - - - add_circle_outline - - - {{ $t('add_playlist') }} - - - - - - - remove_circle_outline - - - {{ $t('remove_playlist') }} - - - - - - - -`, - props: ['value', 'active_player'], - data (){ - return{ - fav: true, - message: false, - hints: true, - } - }, - mounted() { }, - created() { }, - methods: { - itemClick(cmd) { - if (cmd == 'info') - this.$router.push({ path: '/tracks/' + this.$globals.playmenuitem.item_id, query: {provider: this.$globals.playmenuitem.provider}}) - else - this.$emit('playItem', this.$globals.playmenuitem, cmd) - // close dialog - this.$globals.showplaymenu = false; - }, - } - }) diff --git a/music_assistant/web/components/searchbox.vue.js b/music_assistant/web/components/searchbox.vue.js deleted file mode 100644 index 1570ab6c..00000000 --- a/music_assistant/web/components/searchbox.vue.js +++ /dev/null @@ -1,50 +0,0 @@ -Vue.component("searchbox", { - template: ` - - - - - `, - 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}}); - }, - } -}) -/* */ \ No newline at end of file diff --git a/music_assistant/web/index.html b/music_assistant/web/index.html index 14b39713..ac1fae03 100755 --- a/music_assistant/web/index.html +++ b/music_assistant/web/index.html @@ -18,15 +18,7 @@
- - - - - - - - - +
@@ -61,11 +53,12 @@ - + + + - diff --git a/music_assistant/web/lib/utils.js b/music_assistant/web/lib/utils.js index 2f4b4790..9f08215d 100644 --- a/music_assistant/web/lib/utils.js +++ b/music_assistant/web/lib/utils.js @@ -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); }); - }; + + diff --git a/music_assistant/web/pages/albumdetails.vue.js b/music_assistant/web/pages/albumdetails.vue.js index 056bc76a..ebdb0cdb 100755 --- a/music_assistant/web/pages/albumdetails.vue.js +++ b/music_assistant/web/pages/albumdetails.vue.js @@ -1,7 +1,7 @@ var AlbumDetails = Vue.component('AlbumDetails', { template: `
- + @@ -36,6 +37,7 @@ var AlbumDetails = Vue.component('AlbumDetails', { :key="item.db_id" v-bind:totalitems="albumversions.length" v-bind:index="index" + :context="'albumtracks'" > @@ -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); diff --git a/music_assistant/web/pages/artistdetails.vue.js b/music_assistant/web/pages/artistdetails.vue.js index 379f8354..cc6a7fba 100755 --- a/music_assistant/web/pages/artistdetails.vue.js +++ b/music_assistant/web/pages/artistdetails.vue.js @@ -1,7 +1,7 @@ var ArtistDetails = Vue.component('ArtistDetails', { template: `
- + + :hidelibrary="isMobile()" + :context="'artisttracks'" + > @@ -38,6 +40,7 @@ var ArtistDetails = Vue.component('ArtistDetails', { v-bind:totalitems="artistalbums.length" v-bind:index="index" :hideproviders="isMobile()" + :context="'artistalbums'" > @@ -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 diff --git a/music_assistant/web/pages/browse.vue.js b/music_assistant/web/pages/browse.vue.js index fbc11c68..c9ba1141 100755 --- a/music_assistant/web/pages/browse.vue.js +++ b/music_assistant/web/pages/browse.vue.js @@ -4,14 +4,16 @@ var Browse = Vue.component('Browse', { + :hidelibrary="isMobile() ? true : item.media_type != 3" + :context="mediatype" + >
diff --git a/music_assistant/web/pages/playlistdetails.vue.js b/music_assistant/web/pages/playlistdetails.vue.js index f96d3817..0414065b 100755 --- a/music_assistant/web/pages/playlistdetails.vue.js +++ b/music_assistant/web/pages/playlistdetails.vue.js @@ -1,7 +1,7 @@ var PlaylistDetails = Vue.component('PlaylistDetails', { template: `
- + + :hidelibrary="isMobile()" + v-bind:context="info" + > @@ -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); diff --git a/music_assistant/web/pages/queue.vue.js b/music_assistant/web/pages/queue.vue.js index 8440ec2c..c74c8bce 100755 --- a/music_assistant/web/pages/queue.vue.js +++ b/music_assistant/web/pages/queue.vue.js @@ -9,7 +9,9 @@ var Queue = Vue.component('Queue', { :hideavatar="isMobile()" :hidetracknum="true" :hideproviders="isMobile()" - :hidelibrary="isMobile()"> + :hidelibrary="isMobile()" + :context="'playerqueue'" + >
`, diff --git a/music_assistant/web/pages/search.vue.js b/music_assistant/web/pages/search.vue.js index fd86688e..2600c5a3 100755 --- a/music_assistant/web/pages/search.vue.js +++ b/music_assistant/web/pages/search.vue.js @@ -31,7 +31,9 @@ var Search = Vue.component('Search', { :hidetracknum="true" :hideproviders="isMobile()" :hideduration="isMobile()" - :showlibrary="true"> + :showlibrary="true" + :context="'searchtracks'" + > @@ -48,6 +50,7 @@ var Search = Vue.component('Search', { v-bind:totalitems="artists.length" v-bind:index="index" :hideproviders="isMobile()" + :context="'searchartists'" > @@ -65,6 +68,7 @@ var Search = Vue.component('Search', { v-bind:totalitems="albums.length" v-bind:index="index" :hideproviders="isMobile()" + :context="'searchalbums'" > @@ -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'" + > diff --git a/music_assistant/web/pages/trackdetails.vue.js b/music_assistant/web/pages/trackdetails.vue.js index 3b428732..b2c018bd 100755 --- a/music_assistant/web/pages/trackdetails.vue.js +++ b/music_assistant/web/pages/trackdetails.vue.js @@ -1,7 +1,7 @@ var TrackDetails = Vue.component('TrackDetails', { template: `
- + + :hidelibrary="isMobile()" + :context="'trackversions'" + > @@ -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; })