librespot-python/librespot/dealer/ApiClient.py

148 lines
4.9 KiB
Python
Raw Normal View History

2021-05-22 03:26:35 +02:00
import logging
import typing
import requests
2021-02-24 00:46:59 +01:00
from librespot.core.ApResolver import ApResolver
2021-05-22 03:26:41 +02:00
from librespot.metadata import AlbumId
from librespot.metadata import ArtistId
from librespot.metadata import EpisodeId
from librespot.metadata import ShowId
from librespot.metadata import TrackId
from librespot.proto import Connect_pb2 as Connect
from librespot.proto import Metadata_pb2 as Metadata
2021-02-24 00:46:59 +01:00
from librespot.standard import Closeable
class ApiClient(Closeable):
_LOGGER: logging = logging.getLogger(__name__)
_session = None
2021-07-31 13:42:13 +02:00
_baseUrl: str
2021-02-24 00:46:59 +01:00
def __init__(self, session):
self._session = session
self._baseUrl = "https://{}".format(ApResolver.get_random_spclient())
def build_request(
2021-05-22 03:26:33 +02:00
self,
method: str,
suffix: str,
headers: typing.Union[None, typing.Dict[str, str]],
body: typing.Union[None, bytes],
) -> requests.PreparedRequest:
2021-02-24 00:46:59 +01:00
request = requests.PreparedRequest()
request.method = method
request.data = body
request.headers = {}
if headers is not None:
request.headers = headers
request.headers["Authorization"] = "Bearer {}".format(
2021-05-22 03:27:30 +02:00
self._session.tokens().get("playlist-read"))
2021-02-24 00:46:59 +01:00
request.url = self._baseUrl + suffix
return request
2021-05-22 03:26:33 +02:00
def send(
self,
method: str,
suffix: str,
headers: typing.Union[None, typing.Dict[str, str]],
body: typing.Union[None, bytes],
) -> requests.Response:
2021-02-24 00:46:59 +01:00
resp = self._session.client().send(
2021-05-22 03:27:30 +02:00
self.build_request(method, suffix, headers, body))
2021-02-24 00:46:59 +01:00
return resp
2021-05-22 03:27:30 +02:00
def put_connect_state(self, connection_id: str,
proto: Connect.PutStateRequest) -> None:
2021-02-24 00:46:59 +01:00
resp = self.send(
"PUT",
2021-05-22 03:26:33 +02:00
"/connect-state/v1/devices/{}".format(self._session.device_id()),
{
2021-02-24 00:46:59 +01:00
"Content-Type": "application/protobuf",
2021-05-22 03:26:33 +02:00
"X-Spotify-Connection-Id": connection_id,
},
proto.SerializeToString(),
)
2021-02-24 00:46:59 +01:00
if resp.status_code == 413:
self._LOGGER.warning(
2021-05-22 03:27:30 +02:00
"PUT state payload is too large: {} bytes uncompressed.".
format(len(proto.SerializeToString())))
2021-02-24 00:46:59 +01:00
elif resp.status_code != 200:
2021-05-22 03:27:30 +02:00
self._LOGGER.warning("PUT state returned {}. headers: {}".format(
resp.status_code, resp.headers))
2021-02-24 00:46:59 +01:00
def get_metadata_4_track(self, track: TrackId) -> Metadata.Track:
2021-05-22 03:27:30 +02:00
resp = self.send("GET", "/metadata/4/track/{}".format(track.hex_id()),
None, None)
2021-02-24 00:46:59 +01:00
ApiClient.StatusCodeException.check_status(resp)
body = resp.content
if body is None:
raise RuntimeError()
proto = Metadata.Track()
2021-02-24 00:46:59 +01:00
proto.ParseFromString(body)
return proto
def get_metadata_4_episode(self, episode: EpisodeId) -> Metadata.Episode:
2021-05-22 03:27:30 +02:00
resp = self.send("GET",
"/metadata/4/episode/{}".format(episode.hex_id()),
None, None)
2021-02-24 00:46:59 +01:00
ApiClient.StatusCodeException.check_status(resp)
body = resp.content
if body is None:
raise IOError()
proto = Metadata.Episode()
2021-02-24 00:46:59 +01:00
proto.ParseFromString(body)
return proto
def get_metadata_4_album(self, album: AlbumId) -> Metadata.Album:
2021-05-22 03:27:30 +02:00
resp = self.send("GET", "/metadata/4/album/{}".format(album.hex_id()),
None, None)
2021-02-24 00:46:59 +01:00
ApiClient.StatusCodeException.check_status(resp)
body = resp.content
if body is None:
raise IOError()
proto = Metadata.Album()
2021-02-24 00:46:59 +01:00
proto.ParseFromString(body)
return proto
def get_metadata_4_artist(self, artist: ArtistId) -> Metadata.Artist:
2021-05-22 03:27:30 +02:00
resp = self.send("GET",
"/metadata/4/artist/{}".format(artist.hex_id()), None,
None)
2021-02-24 00:46:59 +01:00
ApiClient.StatusCodeException.check_status(resp)
body = resp.content
if body is None:
raise IOError()
proto = Metadata.Artist()
2021-02-24 00:46:59 +01:00
proto.ParseFromString(body)
return proto
def get_metadata_4_show(self, show: ShowId) -> Metadata.Show:
2021-05-22 03:27:30 +02:00
resp = self.send("GET", "/metadata/4/show/{}".format(show.hex_id()),
None, None)
2021-02-24 00:46:59 +01:00
ApiClient.StatusCodeException.check_status(resp)
body = resp.content
if body is None:
raise IOError()
proto = Metadata.Show()
2021-02-24 00:46:59 +01:00
proto.ParseFromString(body)
return proto
class StatusCodeException(IOError):
code: int
def __init__(self, resp: requests.Response):
super().__init__(resp.status_code)
self.code = resp.status_code
@staticmethod
def check_status(resp: requests.Response) -> None:
if resp.status_code != 200:
raise ApiClient.StatusCodeException(resp)