grooveondemand/groove/handlers.py

106 lines
3.2 KiB
Python
Raw Normal View History

2022-11-27 18:42:46 -08:00
from prompt_toolkit import prompt
from prompt_toolkit.completion import Completion, FuzzyCompleter
from slugify import slugify
from sqlalchemy import func
from rich import print
from groove import db
from groove.playlist import Playlist
class FuzzyTableCompleter(FuzzyCompleter):
def __init__(self, table, column, formatter, session):
super(FuzzyTableCompleter).__init__()
self._table = table
self._column = column
self._formatter = formatter
self._session = session
def get_completions(self, document, complete_event):
word = document.get_word_before_cursor()
query = self._session.query(self._table).filter(self._column.ilike(f"%{word}%"))
for row in query.all():
yield Completion(
self._formatter(row),
start_position=-len(word)
)
class Command:
def __init__(self, processor):
self._processor = processor
def handle(self, *parts):
raise NotImplementedError()
class help(Command):
"""Display the help documentation."""
def handle(self, *parts):
print("Available commands:")
for handler in Command.__subclasses__():
print(f"{handler.__name__}: {handler.__doc__}")
class add(Command):
"""Add a track to the current playlist."""
def handle(self, *parts):
if not self._processor.playlist:
print("Please select a playlist first, using the 'playlist' command.")
return
text = prompt(
'Add which track? > ',
completer=FuzzyTableCompleter(db.track, db.track.c.relpath, self._track_to_string, self._processor.session),
complete_in_thread=True, complete_while_typing=True
)
return text
def _track_to_string(row):
return f"{row.artist} - {row.title}"
class list(Command):
"""Display the current playlist."""
def handle(self, *parts):
if not self._processor.playlist:
print("Please select a playlist first, using the 'playlist' command.")
return
print(self._processor.playlist.as_dict)
class stats(Command):
"""Display database statistics."""
def handle(self, *parts):
sess = self._processor.session
playlists = sess.query(func.count(db.playlist.c.id)).scalar()
entries = sess.query(func.count(db.entry.c.track)).scalar()
tracks = sess.query(func.count(db.track.c.relpath)).scalar()
print(f"Database contains {playlists} playlists with a total of {entries} entries, from {tracks} known tracks.")
class quit(Command):
"""Exit the interactive shell."""
def handle(self, *parts):
raise SystemExit()
class playlist(Command):
"""Create or load a playlist."""
def handle(self, *parts):
name = ' '.join(parts)
slug = slugify(name)
self._processor.playlist = Playlist(
slug=slug,
name=name,
session=self._processor.session,
create_if_not_exists=True
)
self._processor.prompt = slug
print(f"Loaded playlist with slug {self._processor.playlist.record.slug}.")
def load(processor):
for handler in Command.__subclasses__():
yield handler.__name__, handler(processor)