added listener, changed env var names
This commit is contained in:
parent
de5da3e057
commit
e6f120b930
1
.gitignore
vendored
1
.gitignore
vendored
@ -3,5 +3,6 @@ __pycache__
|
||||
*.csv
|
||||
.idea
|
||||
.spot
|
||||
data
|
||||
|
||||
scratch.py
|
6
alarm.py
6
alarm.py
@ -42,9 +42,9 @@ if __name__ == '__main__':
|
||||
|
||||
try:
|
||||
|
||||
network = Network(NetworkUser(os.environ['SPOTCLIENT'],
|
||||
os.environ['SPOTSECRET'],
|
||||
os.environ['SPOTREFRESH']))
|
||||
network = Network(NetworkUser(os.environ['SPOT_CLIENT'],
|
||||
os.environ['SPOT_SECRET'],
|
||||
os.environ['SPOT_REFRESH']))
|
||||
network.user.refresh_access_token()
|
||||
|
||||
found = False
|
||||
|
@ -29,9 +29,9 @@ if __name__ == '__main__':
|
||||
|
||||
# try:
|
||||
|
||||
network = Network(NetworkUser(os.environ['SPOTCLIENT'],
|
||||
os.environ['SPOTSECRET'],
|
||||
os.environ['SPOTREFRESH']))
|
||||
network = Network(NetworkUser(os.environ['SPOT_CLIENT'],
|
||||
os.environ['SPOT_SECRET'],
|
||||
os.environ['SPOT_REFRESH']))
|
||||
network.user.refresh_access_token()
|
||||
playlists = network.get_user_playlists()
|
||||
|
||||
|
@ -122,9 +122,9 @@ def go():
|
||||
logger.critical('none to execute, terminating')
|
||||
return
|
||||
|
||||
net = Network(NetworkUser(os.environ['SPOTCLIENT'],
|
||||
os.environ['SPOTSECRET'],
|
||||
os.environ['SPOTREFRESH']))
|
||||
net = Network(NetworkUser(os.environ['SPOT_CLIENT'],
|
||||
os.environ['SPOT_SECRET'],
|
||||
os.environ['SPOT_REFRESH']))
|
||||
net.user.refresh_access_token()
|
||||
|
||||
engine = PlaylistEngine(net)
|
||||
|
@ -15,9 +15,9 @@ logger.addHandler(stream_handler)
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
network = Network(NetworkUser(os.environ['SPOTCLIENT'],
|
||||
os.environ['SPOTSECRET'],
|
||||
os.environ['SPOTREFRESH']))
|
||||
network = Network(NetworkUser(os.environ['SPOT_CLIENT'],
|
||||
os.environ['SPOT_SECRET'],
|
||||
os.environ['SPOT_REFRESH']))
|
||||
network.user.refresh_access_token()
|
||||
|
||||
print(network.user.access_token)
|
||||
|
45
listener.py
Normal file
45
listener.py
Normal 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()
|
@ -1,6 +1,7 @@
|
||||
certifi==2019.6.16
|
||||
certifi==2019.11.28
|
||||
chardet==3.0.4
|
||||
Click==7.0
|
||||
idna==2.8
|
||||
requests==2.22.0
|
||||
tabulate==0.8.3
|
||||
urllib3==1.25.3
|
||||
tabulate==0.8.6
|
||||
urllib3==1.25.7
|
||||
|
@ -28,9 +28,9 @@ logger.addHandler(stream_handler)
|
||||
|
||||
def go(playlist_name):
|
||||
|
||||
net = Network(NetworkUser(os.environ['SPOTCLIENT'],
|
||||
os.environ['SPOTSECRET'],
|
||||
os.environ['SPOTREFRESH']))
|
||||
net = Network(NetworkUser(os.environ['SPOT_CLIENT'],
|
||||
os.environ['SPOT_SECRET'],
|
||||
os.environ['SPOT_REFRESH']))
|
||||
net.user.refresh_access_token()
|
||||
|
||||
engine = PlaylistEngine(net)
|
||||
|
0
spotframework/listener/__init__.py
Normal file
0
spotframework/listener/__init__.py
Normal file
153
spotframework/listener/cmd.py
Normal file
153
spotframework/listener/cmd.py
Normal 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')
|
65
spotframework/listener/thread.py
Normal file
65
spotframework/listener/thread.py
Normal 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)
|
@ -25,7 +25,7 @@ class Context:
|
||||
return f'Context: {self.object_type} uri({self.uri})'
|
||||
|
||||
def __str__(self):
|
||||
return f'{self.object_type} / {self.uri}'
|
||||
return str(self.uri)
|
||||
|
||||
|
||||
class Device:
|
||||
@ -110,4 +110,4 @@ class CurrentlyPlaying:
|
||||
else:
|
||||
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)})'
|
||||
|
Loading…
Reference in New Issue
Block a user