Change Directory

This commit is contained in:
kokarare1212
2021-02-25 08:07:17 +09:00
parent 01dae8ada4
commit 8daf68831e
133 changed files with 221 additions and 38 deletions

View File

@@ -0,0 +1,33 @@
import queue
import random
import requests
class ApResolver:
base_url = "http://apresolve.spotify.com/"
@staticmethod
def request(service_type: str):
response = requests.get("{}?type={}".format(ApResolver.base_url,
service_type))
return response.json()
@staticmethod
def get_random_of(service_type: str):
pool = ApResolver.request(service_type)
urls = pool.get(service_type)
if urls is None or len(urls) == 0:
raise RuntimeError()
return random.choice(urls)
@staticmethod
def get_random_dealer() -> str:
return ApResolver.get_random_of("dealer")
@staticmethod
def get_random_spclient() -> str:
return ApResolver.get_random_of("spclient")
@staticmethod
def get_random_accesspoint() -> str:
return ApResolver.get_random_of("accesspoint")

View File

@@ -0,0 +1,103 @@
from __future__ import annotations
import concurrent.futures
import enum
import time
import typing
import logging
from librespot.core import Session
from librespot.mercury import RawMercuryRequest
from librespot.standard import ByteArrayOutputStream
class EventService:
_session: Session
_LOGGER: logging = logging.getLogger(__name__)
_worker: concurrent.futures.ThreadPoolExecutor = concurrent.futures.ThreadPoolExecutor(
)
def __init__(self, session: Session):
self._session = session
def _worker_callback(self, event_builder: EventService.EventBuilder):
try:
body = event_builder.to_array()
resp = self._session.mercury().send_sync(RawMercuryRequest.Builder(
).set_uri("hm://event-service/v1/events").set_method(
"POST").add_user_field("Accept-Language", "en").add_user_field(
"X-ClientTimeStamp",
int(time.time() * 1000)).add_payload_part(body).build())
self._LOGGER.debug("Event sent. body: {}, result: {}".format(
body, resp.status_code))
except IOError as ex:
self._LOGGER.error("Failed sending event: {} {}".format(
event_builder, ex))
def send_event(self,
event_or_builder: typing.Union[EventService.GenericEvent,
EventService.EventBuilder]):
if type(event_or_builder) is EventService.GenericEvent:
builder = event_or_builder.build()
elif type(event_or_builder) is EventService.EventBuilder:
builder = event_or_builder
else:
TypeError()
self._worker.submit(lambda: self._worker_callback(builder))
def language(self, lang: str):
event = EventService.EventBuilder(EventService.Type.LANGUAGE)
event.append(s=lang)
def close(self):
pass
class Type(enum.Enum):
LANGUAGE = ("812", 1)
FETCHED_FILE_ID = ("274", 3)
NEW_SESSION_ID = ("557", 3)
NEW_PLAYBACK_ID = ("558", 1)
TRACK_PLAYED = ("372", 1)
TRACK_TRANSITION = ("12", 37)
CDN_REQUEST = ("10", 20)
_eventId: str
_unknown: str
def __init__(self, event_id: str, unknown: str):
self._eventId = event_id
self._unknown = unknown
class GenericEvent:
def build(self) -> EventService.EventBuilder:
pass
class EventBuilder:
body: ByteArrayOutputStream = ByteArrayOutputStream(256)
def __init__(self, type: EventService.Type):
self.append_no_delimiter(type.value[0])
self.append(type.value[1])
def append_no_delimiter(self, s: str = None) -> None:
if s is None:
s = ""
self.body.write(buffer=bytearray(s.encode()))
def append(self,
c: int = None,
s: str = None) -> EventService.EventBuilder:
if c is None and s is None or c is not None and s is not None:
raise TypeError()
if c is not None:
self.body.write(byte=0x09)
self.body.write(byte=c)
return self
elif s is not None:
self.body.write(byte=0x09)
self.append_no_delimiter(s)
return self
def to_array(self) -> bytearray:
return self.body.to_byte_array()

View File

@@ -0,0 +1,6 @@
from librespot.crypto.Packet import Packet
class PacketsReceiver:
def dispatch(self, packet: Packet):
pass

View File

@@ -0,0 +1,10 @@
from __future__ import annotations
from librespot.core import Session
class SearchManager:
_BASE_URL: str = "hm://searchview/km/v4/search/"
_session: Session
def __init__(self, session: Session):
self._session = session

1048
librespot/core/Session.py Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,36 @@
import math
import time
class TimeProvider:
offset = 0
method = 0x00
def init(self, conf=None, session=None):
if conf is None and session is None:
return
if conf is not None:
self.method = conf.time_synchronization_method
if conf.time_synchronization_method == TimeProvider.Method.ntp:
self.update_with_ntp()
if conf.time_synchronization_method == TimeProvider.Method.manual:
self.offset = conf.time_manual_correction
if session is not None:
if self.method != TimeProvider.Method.melody:
return
self.update_melody(session)
def current_time_millis(self):
return math.floor(time.time() * 1000) + self.offset
def update_melody(self, session):
pass
def update_with_ntp(self):
pass
class Method:
ntp = 0x00
ping = 0x01
melody = 0x02
manual = 0x03

View File

@@ -0,0 +1,84 @@
from __future__ import annotations
from librespot.core import Session, TimeProvider
from librespot.mercury import MercuryRequests
import logging
class TokenProvider:
_LOGGER: logging = logging.getLogger(__name__)
_TOKEN_EXPIRE_THRESHOLD = 10
_session: Session = None
_tokens: list[TokenProvider.StoredToken] = []
def __init__(self, session: Session):
self._session = session
def find_token_with_all_scopes(
self, scopes: list[str]) -> TokenProvider.StoredToken:
for token in self._tokens:
if token.has_scopes(scopes):
return token
# noinspection PyTypeChecker
return None
def get_token(self, *scopes) -> TokenProvider.StoredToken:
scopes = list(scopes)
if len(scopes) == 0:
raise RuntimeError()
token = self.find_token_with_all_scopes(scopes)
if token is not None:
if token.expired():
self._tokens.remove(token)
else:
return token
self._LOGGER.debug(
"Token expired or not suitable, requesting again. scopes: {}, old_token: {}"
.format(scopes, token))
resp = self._session.mercury().send_sync_json(
MercuryRequests.request_token(self._session.device_id(),
",".join(scopes)))
token = TokenProvider.StoredToken(resp)
self._LOGGER.debug(
"Updated token successfully! scopes: {}, new_token: {}".format(
scopes, token))
self._tokens.append(token)
return token
def get(self, scope: str) -> str:
return self.get_token(scope).access_token
class StoredToken:
expires_in: int
access_token: str
scopes: list[str]
timestamp: int
def __init__(self, obj):
self.timestamp = TimeProvider.TimeProvider().current_time_millis()
self.expires_in = obj["expiresIn"]
self.access_token = obj["accessToken"]
self.scopes = obj["scope"]
def expired(self) -> bool:
return self.timestamp + (
self.expires_in - TokenProvider._TOKEN_EXPIRE_THRESHOLD
) * 1000 < TimeProvider.TimeProvider().current_time_millis()
def has_scope(self, scope: str) -> bool:
for s in self.scopes:
if s == scope:
return True
return False
def has_scopes(self, sc: list[str]) -> bool:
for s in sc:
if not self.has_scope(s):
return False
return True

View File

@@ -0,0 +1,7 @@
from librespot.core.ApResolver import ApResolver
from librespot.core.EventService import EventService
from librespot.core.PacketsReceiver import PacketsReceiver
from librespot.core.SearchManager import SearchManager
from librespot.core.Session import Session
from librespot.core.TimeProvider import TimeProvider
from librespot.core.TokenProvider import TokenProvider