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:
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:
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]):
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']:
''' 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",
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')
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):
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):
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
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):
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)
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':