From: marcelveldt Date: Fri, 25 Oct 2019 21:40:23 +0000 (+0200) Subject: prep for playlist editing X-Git-Url: https://git.kitaultman.com/?a=commitdiff_plain;h=3d36647dc16908c8de9733e24cf717bb9f6db395;p=music-assistant-server.git prep for playlist editing --- diff --git a/music_assistant/models/musicprovider.py b/music_assistant/models/musicprovider.py index f40855c7..2cc4c9a2 100755 --- a/music_assistant/models/musicprovider.py +++ b/music_assistant/models/musicprovider.py @@ -245,7 +245,7 @@ class MusicProvider(): searchstr = "%s - %s" %(searchartist.name, searchtrack.name) search_results = await self.search(searchstr, [MediaType.Track], limit=5) for item in search_results["tracks"]: - if item.name == searchtrack.name and item.version == searchtrack.version and item.album.name == searchtrack.album.name: + if item and item.name == searchtrack.name and item.version == searchtrack.version and item.album.name == searchtrack.album.name: # double safety check - artist must match exactly ! for artist in item.artists: if artist.name == searchartist.name: diff --git a/music_assistant/models/player.py b/music_assistant/models/player.py index be284b97..ffe81558 100755 --- a/music_assistant/models/player.py +++ b/music_assistant/models/player.py @@ -500,6 +500,11 @@ class Player(): async def volume_set(self, volume_level): ''' [PROTECTED] send new volume level command to player ''' + if isinstance(volume_level, str): + if '+' in volume_level or 'up' in volume_level.lower(): + return await self.volume_up() + elif '-' in volume_level or 'down' in volume_level.lower(): + return await self.volume_down() volume_level = try_parse_int(volume_level) # handle group volume if self.is_group: diff --git a/music_assistant/music_manager.py b/music_assistant/music_manager.py index 632ae3f4..e1a9d42f 100755 --- a/music_assistant/music_manager.py +++ b/music_assistant/music_manager.py @@ -200,18 +200,20 @@ class MusicManager(): async def item_action(self, item_id, media_type, provider, action, action_details=None): ''' perform action on item (such as library add/remove) ''' result = None - item = await self.item(item_id, media_type, provider) + item = await self.item(item_id, media_type, provider, lazy=False) if item and action in ['library_add', 'library_remove']: # remove or add item to the library - for prov_mapping in result.provider_ids: + for prov_mapping in item.provider_ids: prov_id = prov_mapping['provider'] prov_item_id = prov_mapping['item_id'] for prov in self.providers.values(): if prov.prov_id == prov_id: - if action == 'add': + if action == 'library_add': result = await prov.add_library(prov_item_id, media_type) - elif action == 'remove': + await self.mass.db.add_to_library(item.item_id, item.media_type, prov_id) + 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) return result async def add_playlist_tracks(self, playlist_id, tracks:List[Track]): @@ -239,7 +241,7 @@ class MusicManager(): track_playlist_provs = [item['provider'] for item in track.provider_ids] if playlist_prov['provider'] in track_playlist_provs: # a track can contain multiple versions on the same provider - # # simply sort by quality and just add the first one (assuming the track is still available) + # simply sort by quality and just add the first one (assuming the track is still available) track_versions = sorted(track.provider_ids, key=operator.itemgetter('quality'), reverse=True) for track_version in track_versions: if track_version['provider'] == playlist_prov['provider']: diff --git a/music_assistant/musicproviders/file.py b/music_assistant/musicproviders/file.py index 10e74071..506c596f 100644 --- a/music_assistant/musicproviders/file.py +++ b/music_assistant/musicproviders/file.py @@ -252,6 +252,8 @@ class FileProvider(MusicProvider): ''' return the content details for the given track when it will be streamed''' if not os.sep in track_id: track_id = base64.b64decode(track_id).decode('utf-8') + if not os.path.isfile(track_id): + return None # TODO: retrieve sanple rate and bitdepth return { "type": "file", diff --git a/music_assistant/musicproviders/spotify.py b/music_assistant/musicproviders/spotify.py index 0e2a2662..d51ab00a 100644 --- a/music_assistant/musicproviders/spotify.py +++ b/music_assistant/musicproviders/spotify.py @@ -223,6 +223,22 @@ class SpotifyProvider(MusicProvider): await self.mass.db.remove_from_library(item.item_id, media_type, self.prov_id) LOGGER.debug("deleted item %s from %s - %s" %(prov_item_id, self.prov_id, result)) + async def add_playlist_tracks(self, prov_playlist_id, prov_track_ids): + ''' add track(s) to playlist ''' + track_uris = [] + for track_id in prov_track_ids: + track_uris.append("spotify:track:%s" % track_id) + data = {"uris": track_uris} + return await self.__post_data(f'playlists/{prov_playlist_id}/tracks', data=data) + + async def remove_playlist_tracks(self, prov_playlist_id, prov_track_ids): + ''' remove track(s) from playlist ''' + track_uris = [] + for track_id in prov_track_ids: + track_uris.append("spotify:track:%s" % track_id) + data = {"tracks": track_uris} + return await self.__delete_data(f'playlists/{prov_playlist_id}/tracks', data=data) + async def devices(self): ''' list all available devices ''' items = await self.__get_data('me/player/devices') @@ -245,16 +261,22 @@ class SpotifyProvider(MusicProvider): async def get_stream_details(self, track_id): ''' return the content details for the given track when it will be streamed''' - # make sure there is a valid token in cache + # make sure a valid track is requested + track = await self.get_track(track_id) + if not track: + return None + # make sure that the token is still valid by just requesting it await self.get_token() spotty = self.get_spotty_binary() - spotty_exec = '%s -n temp -c "%s" --pass-through --single-track %s' %(spotty, self.mass.datapath, track_id) + spotty_exec = '%s -n temp -c "%s" --pass-through --single-track %s' %(spotty, self.mass.datapath, track.item_id) return { "type": "executable", "path": spotty_exec, "content_type": "ogg", "sample_rate": 44100, - "bit_depth": 16 + "bit_depth": 16, + "provider": PROV_ID, + "item_id": track.item_id } async def __parse_artist(self, artist_obj): @@ -483,12 +505,12 @@ class SpotifyProvider(MusicProvider): result = None return result - async def __delete_data(self, endpoint, params={}): - ''' get data from api''' + async def __delete_data(self, endpoint, params={}, data=None): + ''' delete data from api''' url = 'https://api.spotify.com/v1/%s' % endpoint token = await self.get_token() headers = {'Authorization': 'Bearer %s' % token["accessToken"]} - async with self.http_session.delete(url, headers=headers, params=params) as response: + async with self.http_session.delete(url, headers=headers, params=params, json=data, verify_ssl=False) as response: return await response.text() async def __put_data(self, endpoint, params={}, data=None): @@ -496,7 +518,15 @@ class SpotifyProvider(MusicProvider): url = 'https://api.spotify.com/v1/%s' % endpoint token = await self.get_token() headers = {'Authorization': 'Bearer %s' % token["accessToken"]} - async with self.http_session.put(url, headers=headers, params=params, json=data) as response: + async with self.http_session.put(url, headers=headers, params=params, json=data, verify_ssl=False) as response: + return await response.text() + + async def __post_data(self, endpoint, params={}, data=None): + ''' post data on api''' + url = 'https://api.spotify.com/v1/%s' % endpoint + token = await self.get_token() + headers = {'Authorization': 'Bearer %s' % token["accessToken"]} + async with self.http_session.post(url, headers=headers, params=params, json=data, verify_ssl=False) as response: return await response.text() @staticmethod diff --git a/music_assistant/playerproviders/squeezebox.py b/music_assistant/playerproviders/squeezebox.py index 61a4970b..c8fb27f9 100644 --- a/music_assistant/playerproviders/squeezebox.py +++ b/music_assistant/playerproviders/squeezebox.py @@ -200,7 +200,7 @@ class PySqueezePlayer(Player): async def cmd_queue_insert(self, queue_items, insert_at_index): # queue handled by built-in queue controller # we only check the start index - if insert_at_index == 0: + if insert_at_index == self.queue.cur_index: return await self.cmd_queue_play_index(insert_at_index) async def cmd_queue_append(self, queue_items): diff --git a/music_assistant/web.py b/music_assistant/web.py index ae8840cd..87f0d9a1 100755 --- a/music_assistant/web.py +++ b/music_assistant/web.py @@ -82,9 +82,9 @@ 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}', self.get_items)]) 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)]) webdir = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'web/') app.router.add_static("/", webdir) @@ -349,6 +349,12 @@ class Web(): await player.next() elif cmd_str == 'playlist index -1': await player.previous() + elif 'mixer volume' in cmd_str and '+' in cmds[2]: + volume_level = cmds[2].split('+')[1] + await player.volume_set(volume_level) + elif 'mixer volume' in cmd_str and '-' in cmds[2]: + volume_level = cmds[2].split('-')[1] + await player.volume_set(volume_level) elif 'mixer volume' in cmd_str: await player.volume_set(cmds[2]) elif cmd_str == 'mixer muting 1':