Source code for spotify_local.core

import sys

from threading import Thread
from collections import defaultdict, OrderedDict

from requests import Session

from .config import DEFAULT_ORIGIN
from .utils import get_url, get_csrf_token, get_oauth_token


[docs]class SpotifyLocal: """Controller for the local spotify web helper, throws events when the state of spotify changes. """ def __init__(self): self._registered_events = defaultdict(OrderedDict) self._csrf_token = get_csrf_token() self._oauth_token = get_oauth_token() self._session = Session() def __enter__(self): return self def __exit__(self, *args): self.close() def _request(self, url, params={}): """Makes a request using the currently open session. :param url: A url fragment to use in the creation of the master url """ r = self._session.get(url=url, params=params, headers=DEFAULT_ORIGIN) return r def close(self): self._session.close()
[docs] def on(self, event): """Decorator function that adds function to callback list for event system There are three events you can subscribe too; **status_change**, **play_state_change**, **track_change**. :param event: name of the event you wish to register the function under, you can use multiple decorators """ def _on(func): self.add_event_handler(event, func) return func return _on
[docs] def add_event_handler(self, event, func): """Add function and event to Ordered Dict :param event: Name of the event you wish to register the function :param func: Function to register witht the associated event """ self._registered_events[event][func] = func
[docs] def emit(self, event, *args, **kwargs): """Send out an event and call it's associated functions :param event: Name of the event to trigger """ for func in self._registered_events[event].values(): func(*args, **kwargs)
[docs] def remove_listener(self, event, func): """Remove an event listner :param event: Event you wish to remove the function from :param func: Function you wish to remove """ self._registered_events[event].pop(func)
[docs] def remove_all_listeners(self, event=None): """Remove all functions for all events, or one event if one is specifed. :param event: Optional event you wish to remove all functions from """ if event is not None: self._registered_events[event] = OrderedDict() else: self._registered_events = defaultdict(OrderedDict)
[docs] def listeners(self, event): """Return list of listners associated to a particular event :param event: Name of the event you wish to query """ return list(self._registered_events[event].keys())
@property def version(self): """Spotify version information""" url: str = get_url("/service/version.json") params = {"service": "remote"} r = self._request(url=url, params=params) return r.json()
[docs] def get_current_status(self): """Returns the current state of the local spotify client""" url = get_url("/remote/status.json") params = {"oauth": self._oauth_token, "csrf": self._csrf_token} r = self._request(url=url, params=params) return r.json()
[docs] def pause(self, pause=True): """Pauses the spotify player :param pause: boolean value to choose the pause/play state """ url: str = get_url("/remote/pause.json") params = { "oauth": self._oauth_token, "csrf": self._csrf_token, "pause": "true" if pause else "false", } self._request(url=url, params=params)
[docs] def unpause(self): """Unpauses the player by calling pause()""" self.pause(pause=False)
[docs] def playURI(self, uri): """Play a Spotify uri, for example spotify:track:5Yn8WCB4Dqm8snemB5Mu4K :param uri: Playlist, Artist, Album, or Song Uri """ url: str = get_url("/remote/play.json") params = { "oauth": self._oauth_token, "csrf": self._csrf_token, "uri": uri, "context": uri, } r = self._request(url=url, params=params) return r.json()
[docs] @staticmethod def skip(): """Skips the current song""" if sys.platform == "darwin": keyboard.send("KEYTYPE_SKIP") else: keyboard.send("next track")
[docs] @staticmethod def previous(): """Goes to the beginning of the track, or if called twice goes to the previous track.""" if sys.platform == "darwin": keyboard.send("KEYTYPE_PREVIOUS") else: keyboard.send("previous track")
[docs] def listen(self, wait=60, blocking=True) -> None: """Listen for events and call any associated callbacks when there is an event. There are three events you can subscribe too; **status_change**, **play_state_change**, **track_change** :param wait: how long to wait for a response before starting a new connection :param blocking: if listen should block the current process or not """ url = get_url("/remote/status.json") def listen_for_status_change(): params = {"oauth": self._oauth_token, "csrf": self._csrf_token} r = self._request(url=url, params=params) old = r.json() self.emit("status_change", old) params = { "oauth": self._oauth_token, "csrf": self._csrf_token, "returnon": "play,pause,error,ap", "returnafter": wait, } while True: r = self._request(url=url, params=params) new = r.json() if new != old: self.emit("status_change", new) if new["playing"] != old["playing"]: self.emit("play_state_change", new) if ( new["track"]["track_resource"]["uri"] != old["track"]["track_resource"]["uri"] ): self.emit("track_change", new) old = new if blocking: listen_for_status_change() else: thread = Thread(target=listen_for_status_change) thread.daemon = True thread.start()