Change Directory
This commit is contained in:
129
librespot/dealer/ApiClient.py
Normal file
129
librespot/dealer/ApiClient.py
Normal file
@@ -0,0 +1,129 @@
|
||||
from librespot.core.ApResolver import ApResolver
|
||||
from librespot.metadata import AlbumId, ArtistId, EpisodeId, TrackId, ShowId
|
||||
from librespot.proto import Connect, Metadata
|
||||
from librespot.standard import Closeable
|
||||
from typing import Union
|
||||
import logging
|
||||
import requests
|
||||
|
||||
|
||||
class ApiClient(Closeable):
|
||||
_LOGGER: logging = logging.getLogger(__name__)
|
||||
_session = None
|
||||
_baseUrl: str = None
|
||||
|
||||
def __init__(self, session):
|
||||
self._session = session
|
||||
self._baseUrl = "https://{}".format(ApResolver.get_random_spclient())
|
||||
|
||||
def build_request(self, method: str, suffix: str,
|
||||
headers: Union[None, dict[str, str]],
|
||||
body: Union[None, bytes]) -> requests.PreparedRequest:
|
||||
request = requests.PreparedRequest()
|
||||
request.method = method
|
||||
request.data = body
|
||||
request.headers = {}
|
||||
if headers is not None:
|
||||
request.headers = headers
|
||||
request.headers["Authorization"] = "Bearer {}".format(
|
||||
self._session.tokens().get("playlist-read"))
|
||||
request.url = self._baseUrl + suffix
|
||||
return request
|
||||
|
||||
def send(self, method: str, suffix: str, headers: Union[None, dict[str,
|
||||
str]],
|
||||
body: Union[None, bytes]) -> requests.Response:
|
||||
resp = self._session.client().send(
|
||||
self.build_request(method, suffix, headers, body))
|
||||
return resp
|
||||
|
||||
def put_connect_state(self, connection_id: str,
|
||||
proto: Connect.PutStateRequest) -> None:
|
||||
resp = self.send(
|
||||
"PUT",
|
||||
"/connect-state/v1/devices/{}".format(self._session.device_id()), {
|
||||
"Content-Type": "application/protobuf",
|
||||
"X-Spotify-Connection-Id": connection_id
|
||||
}, proto.SerializeToString())
|
||||
|
||||
if resp.status_code == 413:
|
||||
self._LOGGER.warning(
|
||||
"PUT state payload is too large: {} bytes uncompressed.".
|
||||
format(len(proto.SerializeToString())))
|
||||
elif resp.status_code != 200:
|
||||
self._LOGGER.warning("PUT state returned {}. headers: {}".format(
|
||||
resp.status_code, resp.headers))
|
||||
|
||||
def get_metadata_4_track(self, track: TrackId) -> Metadata.Track:
|
||||
resp = self.send("GET", "/metadata/4/track/{}".format(track.hex_id()),
|
||||
None, None)
|
||||
ApiClient.StatusCodeException.check_status(resp)
|
||||
|
||||
body = resp.content
|
||||
if body is None:
|
||||
raise RuntimeError()
|
||||
proto = Metadata.Track()
|
||||
proto.ParseFromString(body)
|
||||
return proto
|
||||
|
||||
def get_metadata_4_episode(self, episode: EpisodeId) -> Metadata.Episode:
|
||||
resp = self.send("GET",
|
||||
"/metadata/4/episode/{}".format(episode.hex_id()),
|
||||
None, None)
|
||||
ApiClient.StatusCodeException.check_status(resp)
|
||||
|
||||
body = resp.content
|
||||
if body is None:
|
||||
raise IOError()
|
||||
proto = Metadata.Episode()
|
||||
proto.ParseFromString(body)
|
||||
return proto
|
||||
|
||||
def get_metadata_4_album(self, album: AlbumId) -> Metadata.Album:
|
||||
resp = self.send("GET", "/metadata/4/album/{}".format(album.hex_id()),
|
||||
None, None)
|
||||
ApiClient.StatusCodeException.check_status(resp)
|
||||
|
||||
body = resp.content
|
||||
if body is None:
|
||||
raise IOError()
|
||||
proto = Metadata.Album()
|
||||
proto.ParseFromString(body)
|
||||
return proto
|
||||
|
||||
def get_metadata_4_artist(self, artist: ArtistId) -> Metadata.Artist:
|
||||
resp = self.send("GET",
|
||||
"/metadata/4/artist/{}".format(artist.hex_id()), None,
|
||||
None)
|
||||
ApiClient.StatusCodeException.check_status(resp)
|
||||
|
||||
body = resp.content
|
||||
if body is None:
|
||||
raise IOError()
|
||||
proto = Metadata.Artist()
|
||||
proto.ParseFromString(body)
|
||||
return proto
|
||||
|
||||
def get_metadata_4_show(self, show: ShowId) -> Metadata.Show:
|
||||
resp = self.send("GET", "/metadata/4/show/{}".format(show.hex_id()),
|
||||
None, None)
|
||||
ApiClient.StatusCodeException.check_status(resp)
|
||||
|
||||
body = resp.content
|
||||
if body is None:
|
||||
raise IOError()
|
||||
proto = Metadata.Show()
|
||||
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)
|
||||
19
librespot/dealer/DealerClient.py
Normal file
19
librespot/dealer/DealerClient.py
Normal file
@@ -0,0 +1,19 @@
|
||||
from __future__ import annotations
|
||||
from librespot.standard.Closeable import Closeable
|
||||
|
||||
|
||||
class DealerClient(Closeable):
|
||||
def __init__(self, session):
|
||||
pass
|
||||
|
||||
def connect(self):
|
||||
pass
|
||||
|
||||
def add_message_listener(self, listener: DealerClient.MessageListener,
|
||||
*uris: str):
|
||||
pass
|
||||
|
||||
class MessageListener:
|
||||
def on_message(self, uri: str, headers: dict[str, str],
|
||||
payload: bytes):
|
||||
pass
|
||||
2
librespot/dealer/__init__.py
Normal file
2
librespot/dealer/__init__.py
Normal file
@@ -0,0 +1,2 @@
|
||||
from librespot.dealer.ApiClient import ApResolver
|
||||
from librespot.dealer.DealerClient import DealerClient
|
||||
Reference in New Issue
Block a user