added listener, changed env var names

This commit is contained in:
aj 2019-12-23 12:16:03 +00:00
parent de5da3e057
commit e6f120b930
12 changed files with 285 additions and 20 deletions

1
.gitignore vendored
View File

@ -3,5 +3,6 @@ __pycache__
*.csv *.csv
.idea .idea
.spot .spot
data
scratch.py scratch.py

View File

@ -42,9 +42,9 @@ if __name__ == '__main__':
try: try:
network = Network(NetworkUser(os.environ['SPOTCLIENT'], network = Network(NetworkUser(os.environ['SPOT_CLIENT'],
os.environ['SPOTSECRET'], os.environ['SPOT_SECRET'],
os.environ['SPOTREFRESH'])) os.environ['SPOT_REFRESH']))
network.user.refresh_access_token() network.user.refresh_access_token()
found = False found = False

View File

@ -29,9 +29,9 @@ if __name__ == '__main__':
# try: # try:
network = Network(NetworkUser(os.environ['SPOTCLIENT'], network = Network(NetworkUser(os.environ['SPOT_CLIENT'],
os.environ['SPOTSECRET'], os.environ['SPOT_SECRET'],
os.environ['SPOTREFRESH'])) os.environ['SPOT_REFRESH']))
network.user.refresh_access_token() network.user.refresh_access_token()
playlists = network.get_user_playlists() playlists = network.get_user_playlists()

View File

@ -122,9 +122,9 @@ def go():
logger.critical('none to execute, terminating') logger.critical('none to execute, terminating')
return return
net = Network(NetworkUser(os.environ['SPOTCLIENT'], net = Network(NetworkUser(os.environ['SPOT_CLIENT'],
os.environ['SPOTSECRET'], os.environ['SPOT_SECRET'],
os.environ['SPOTREFRESH'])) os.environ['SPOT_REFRESH']))
net.user.refresh_access_token() net.user.refresh_access_token()
engine = PlaylistEngine(net) engine = PlaylistEngine(net)

View File

@ -15,9 +15,9 @@ logger.addHandler(stream_handler)
if __name__ == '__main__': if __name__ == '__main__':
network = Network(NetworkUser(os.environ['SPOTCLIENT'], network = Network(NetworkUser(os.environ['SPOT_CLIENT'],
os.environ['SPOTSECRET'], os.environ['SPOT_SECRET'],
os.environ['SPOTREFRESH'])) os.environ['SPOT_REFRESH']))
network.user.refresh_access_token() network.user.refresh_access_token()
print(network.user.access_token) print(network.user.access_token)

45
listener.py Normal file
View File

@ -0,0 +1,45 @@
import os
import logging
import click
from spotframework.listener.cmd import ListenCmd
from spotframework.net.network import Network, NetworkUser
logger = logging.getLogger('spotframework')
log_format = '%(asctime)s %(levelname)s %(name)s:%(funcName)s - %(message)s'
file_handler = logging.FileHandler("data/listener.log")
formatter = logging.Formatter(log_format)
file_handler.setFormatter(formatter)
stream_log_format = '%(levelname)s %(name)s:%(funcName)s - %(message)s'
stream_formatter = logging.Formatter(stream_log_format)
stream_handler = logging.StreamHandler()
stream_handler.setFormatter(stream_formatter)
logger.addHandler(stream_handler)
logger.addHandler(file_handler)
@click.command()
@click.option('-v', '--verbose', is_flag=True)
@click.option('--client-id', prompt=True, default=lambda: os.environ.get('SPOT_CLIENT', ''))
@click.option('--client-secret', prompt=True, default=lambda: os.environ.get('SPOT_SECRET', ''))
@click.option('--refresh-token', prompt=True, default=lambda: os.environ.get('SPOT_REFRESH', ''))
def listen(verbose, client_id, client_secret, refresh_token):
if verbose:
stream_handler.setLevel('DEBUG')
else:
stream_handler.setLevel('WARNING')
net = Network(NetworkUser(client_id=client_id,
client_secret=client_secret,
refresh_token=refresh_token).refresh_access_token())
cmd = ListenCmd(net, stream_handler)
cmd.cmdloop()
if __name__ == '__main__':
listen()

View File

@ -1,6 +1,7 @@
certifi==2019.6.16 certifi==2019.11.28
chardet==3.0.4 chardet==3.0.4
Click==7.0
idna==2.8 idna==2.8
requests==2.22.0 requests==2.22.0
tabulate==0.8.3 tabulate==0.8.6
urllib3==1.25.3 urllib3==1.25.7

View File

@ -28,9 +28,9 @@ logger.addHandler(stream_handler)
def go(playlist_name): def go(playlist_name):
net = Network(NetworkUser(os.environ['SPOTCLIENT'], net = Network(NetworkUser(os.environ['SPOT_CLIENT'],
os.environ['SPOTSECRET'], os.environ['SPOT_SECRET'],
os.environ['SPOTREFRESH'])) os.environ['SPOT_REFRESH']))
net.user.refresh_access_token() net.user.refresh_access_token()
engine = PlaylistEngine(net) engine = PlaylistEngine(net)

View File

View File

@ -0,0 +1,153 @@
from cmd import Cmd
import functools
import logging
from typing import Optional
from spotframework.net.network import Network
from spotframework.listener.thread import ListenerThread
logger = logging.getLogger(__name__)
def check_thread(func):
@functools.wraps(func)
def check_thread_wrapper(self, *args, **kwargs):
if self.listen_thread is not None:
return func(self, *args, **kwargs)
else:
print('>> not running')
return check_thread_wrapper
def check_last_dataset(func):
@functools.wraps(func)
def check_last_dataset_wrapper(self, *args, **kwargs):
if self.last_dataset is not None:
return func(self, *args, **kwargs)
else:
print('>> not running')
return check_last_dataset_wrapper
class ListenCmd(Cmd):
intro = 'listener... ? for help'
prompt = '(listen)> '
dt_format = "%-H:%M:%S %-d %b %-y"
def __init__(self, net: Network, log_stream_handler=None):
Cmd.__init__(self)
self.net = net
self.listen_thread: Optional[ListenerThread] = None
self.last_dataset = None
self.verbose = False
self.log_stream_handler = log_stream_handler
def start_listener(self):
if self.listen_thread is not None:
logger.info('restarting')
print('>> restarting listener')
self.stop_listener()
logger.info('starting')
print('>> starting listener')
self.listen_thread = ListenerThread(self.net)
self.listen_thread.on_playback_change.append(lambda x: print(f'playback changed -> {x}'))
self.listen_thread.start()
@check_thread
def stop_listener(self):
logger.info('stopping')
print('>> stopping listener')
self.last_dataset = self.listen_thread.recent_tracks
self.listen_thread.stop()
def print_tracks(self, tracks):
[print(f'({i.played_at.strftime(self.dt_format)}) {i.name} / {i.artists_names}')
for i in tracks]
def print(self):
if self.listen_thread is not None:
print('now playing:', self.listen_thread.now_playing)
self.print_tracks(self.listen_thread.recent_tracks)
elif self.last_dataset is not None:
self.print_tracks(self.last_dataset)
else:
print('>> no tracks to print')
@check_thread
def set_poll_interval(self, value):
if value.isdigit():
logger.info(f'setting polling interval to {value}')
print(f'>> interval set to {value}')
self.listen_thread.sleep_interval = int(value)
else:
logger.error(f'{value} is not a valid interval')
print('>> not a number')
def do_interval(self, args):
"""set polling interval"""
self.set_poll_interval(args)
def do_print(self, args):
"""print recently played tracks"""
self.print()
def do_p(self, args):
"""print recently played tracks"""
self.print()
def do_listen(self, args):
"""start listener"""
self.start_listener()
def do_l(self, args):
"""start listener"""
self.start_listener()
def do_stop(self, args):
"""stop listener"""
self.stop_listener()
def do_e(self, args):
"""stop listener"""
self.stop_listener()
def do_verbose(self, args):
"""toggle verbosity"""
if self.log_stream_handler is not None:
self.verbose = not self.verbose
if self.verbose:
self.log_stream_handler.setLevel('DEBUG')
else:
self.log_stream_handler.setLevel('WARNING')
@check_thread
def do_quit(self, args):
"""stop and quit"""
logger.info('quitting')
self.stop_listener()
self.listen_thread.join()
exit(0)
@check_thread
def do_q(self, args):
"""stop and quit"""
logger.info('quitting')
self.stop_listener()
self.listen_thread.join()
exit(0)
@check_thread
def do_status(self, args):
"""stop and quit"""
if self.listen_thread is not None:
if self.listen_thread.is_alive():
print('>> running')
else:
print('>> thread dead')
else:
print('>> not running')

View File

@ -0,0 +1,65 @@
import threading
import logging
from typing import Optional
from spotframework.net.network import Network
from spotframework.model.service import CurrentlyPlaying
logger = logging.getLogger(__name__)
class ListenerThread(threading.Thread):
def __init__(self,
net: Network,
sleep_interval: int = 5,
request_size: int = 5):
super(ListenerThread, self).__init__()
self._stop_event = threading.Event()
self.net = net
self.sleep_interval = sleep_interval
self.request_size = request_size
self.recent_tracks = []
self.prev_now_playing: Optional[CurrentlyPlaying] = None
self.now_playing: Optional[CurrentlyPlaying] = net.get_player()
self.on_playback_change = []
def stop(self):
logger.info('stopping thread')
self._stop_event.set()
def stopped(self):
return self._stop_event.is_set()
def update_now_playing(self):
live_now_playing = self.net.get_player()
if self.now_playing is None and live_now_playing is None:
return
if live_now_playing != self.now_playing:
self.prev_now_playing = self.now_playing
self.now_playing = live_now_playing
for func in self.on_playback_change:
func(live_now_playing)
else:
self.now_playing = live_now_playing
def update_recent_tracks(self):
logger.debug('pulling tracks')
tracks = self.net.get_recently_played_tracks(response_limit=self.request_size)
for track in tracks:
if track.played_at not in [i.played_at for i in self.recent_tracks]:
self.recent_tracks.append(track)
self.recent_tracks.sort(key=lambda x: x.played_at)
def run(self):
while not self.stopped():
self.update_recent_tracks()
self.update_now_playing()
self._stop_event.wait(self.sleep_interval)

View File

@ -25,7 +25,7 @@ class Context:
return f'Context: {self.object_type} uri({self.uri})' return f'Context: {self.object_type} uri({self.uri})'
def __str__(self): def __str__(self):
return f'{self.object_type} / {self.uri}' return str(self.uri)
class Device: class Device:
@ -110,4 +110,4 @@ class CurrentlyPlaying:
else: else:
playing = '(paused)' playing = '(paused)'
return f'{playing} {self.track} on {self.device} ({self._format_duration(self.progress_ms)})' return f'{playing} {self.track} on {self.device} from {self.context} ({self._format_duration(self.progress_ms)})'