diff --git a/groove/cli.py b/groove/cli.py index 8424921..4b13ef4 100644 --- a/groove/cli.py +++ b/groove/cli.py @@ -106,7 +106,6 @@ def scan( initialize() with database_manager() as manager: scanner = media_scanner(root=root, db=manager.session) - scanner.cleanup() count = scanner.scan() logging.info(f"Imported {count} new tracks.") diff --git a/groove/db/scanner.py b/groove/db/scanner.py index 9c61e73..b97d74f 100644 --- a/groove/db/scanner.py +++ b/groove/db/scanner.py @@ -5,7 +5,7 @@ import music_tag from pathlib import Path from typing import Callable, Union, Iterable -from sqlalchemy import func, delete +from sqlalchemy import func import groove.db import groove.path @@ -40,31 +40,10 @@ class MediaScanner: async def _do_import(): logging.debug("Scanning filesystem (this may take a minute)...") for path in sources: - if path.exists() and not path.is_dir(): - asyncio.create_task(self._import_one_track(path)) + asyncio.create_task(self._import_one_track(path)) asyncio.run(_do_import()) self.db.commit() - def cleanup(self) -> int: - """ - Check for the existence of every track in the databse. - """ - async def _del(track): - path = self.root / Path(track.relpath) - if path.exists(): - return - logging.info(f"Deleting missing track {track.relpath}") - self.db.execute( - delete(groove.db.track).where(groove.db.track.c.id == track.id) - ) - - async def _do_cleanup(): - logging.debug("Locating stale track definitions in the database...") - for track in self.db.query(groove.db.track).all(): - asyncio.create_task(_del(track)) - asyncio.run(_do_cleanup()) - self.db.commit() - def _get_tags(self, path): # pragma: no cover tags = music_tag.load_file(path) return { diff --git a/groove/playlist.py b/groove/playlist.py index 212ebe0..13b0ac4 100644 --- a/groove/playlist.py +++ b/groove/playlist.py @@ -66,7 +66,7 @@ class Playlist: @property def info(self): count = len(self.entries) - return f"{self.name}: {self.url} [{count} tracks]\n{self.description}" + return f"{self.name}: {self.url} [{count} tracks]\n{self.description}\n" @property def url(self) -> str: @@ -133,7 +133,7 @@ class Playlist: @property def as_string(self) -> str: - text = self.info + self.description + text = self.info for (tracknum, entry) in enumerate(self.entries): text += f" - {tracknum+1} {entry.artist} - {entry.title}\n" return text diff --git a/groove/shell/base.py b/groove/shell/base.py index f3c9ce6..f6eb614 100644 --- a/groove/shell/base.py +++ b/groove/shell/base.py @@ -14,11 +14,6 @@ class BasePrompt(Completer): self._values = [] self._parent = parent self._manager = manager - self._commands = {'help': self.help} - - @property - def commands(self): - return self._commands @property def usage(self): @@ -45,7 +40,7 @@ class BasePrompt(Completer): @property def values(self): - return [k for k in self.commands.keys() if not k.startswith('_')] + return self._values def get_completions(self, document, complete_event): word = document.get_word_before_cursor() @@ -64,7 +59,7 @@ class BasePrompt(Completer): def start(self, cmd=''): while True: if not cmd: - cmd = prompt(f'{self.prompt} ', completer=self, complete_while_typing=True) + cmd = prompt(f'{self.prompt} ', completer=self) if not cmd: return cmd, *parts = cmd.split() @@ -72,13 +67,6 @@ class BasePrompt(Completer): return cmd = '' - def help(self, parts): - if not parts: - print(self.__doc__) - else: - print(getattr(self, parts[0]).__doc__) - return True - def default_completer(self, document, complete_event): raise NotImplementedError() diff --git a/groove/shell/interactive_shell.py b/groove/shell/interactive_shell.py index 82cc896..68bfaba 100644 --- a/groove/shell/interactive_shell.py +++ b/groove/shell/interactive_shell.py @@ -54,8 +54,8 @@ class CommandPrompt(BasePrompt): session=self.manager.session, create_ok=True ) - self.commands['_playlist'].start() - return True + res = self.commands['_playlist'].start() + return True and res def start(): # pragma: no cover diff --git a/groove/shell/playlist.py b/groove/shell/playlist.py index 9616d1a..11e495d 100644 --- a/groove/shell/playlist.py +++ b/groove/shell/playlist.py @@ -8,14 +8,21 @@ from groove import db class _playlist(BasePrompt): - """ - PLAYLIST - """ + + def __init__(self, parent, manager=None): + super().__init__(manager=manager, parent=parent) + self._parent = parent + self._prompt = '' + self._commands = None @property def prompt(self): return f"{self.parent.playlist}\n{self.parent.playlist.slug}> " + @property + def values(self): + return self.commands.keys() + @property def commands(self): if not self._commands: @@ -24,20 +31,9 @@ class _playlist(BasePrompt): 'delete': self.delete, 'add': self.add, 'edit': self.edit, - 'help': self.help } return self._commands - def _add_track(self, text): - sess = self.parent.manager.session - try: - track = sess.query(db.track).filter(db.track.c.relpath == text).one() - self.parent.playlist.create_entries([track]) - except NoResultFound: - print("No match for '{text}'") - return - return text - def process(self, cmd, *parts): res = True if cmd in self.commands: @@ -70,6 +66,16 @@ class _playlist(BasePrompt): return True self._add_track(text) + def _add_track(self, text): + sess = self.parent.manager.session + try: + track = sess.query(db.track).filter(db.track.c.relpath == text).one() + self.parent.playlist.create_entries([track]) + except NoResultFound: + print("No match for '{text}'") + return + return text + def delete(self, parts): res = prompt( 'Type DELETE to permanently delete the playlist ' diff --git a/test/fixtures/themes/alt_theme/static b/test/fixtures/themes/alt_theme/static deleted file mode 100644 index e69de29..0000000 diff --git a/test/test_scanner.py b/test/test_scanner.py index 69b0ae1..3630572 100644 --- a/test/test_scanner.py +++ b/test/test_scanner.py @@ -38,10 +38,6 @@ def test_scanner(monkeypatch, in_memory_db, media): # replace the filesystem glob with the test fixture generator monkeypatch.setattr(scanner.MediaScanner, 'find_sources', MagicMock(return_value=media())) - # pretend things exist - monkeypatch.setattr(scanner.Path, 'exists', MagicMock(return_value=True)) - monkeypatch.setattr(scanner.Path, 'is_dir', MagicMock(return_value=False)) - def mock_loader(path): return { 'artist': 'foo', diff --git a/test/test_shell.py b/test/test_shell.py index 2200bde..acf65b5 100644 --- a/test/test_shell.py +++ b/test/test_shell.py @@ -16,10 +16,6 @@ def response_factory(responses): return MagicMock(side_effect=responses + ([''] * 10)) -def test_commands(cmd_prompt): - assert cmd_prompt.commands.keys() == cmd_prompt.commands.keys() - - @pytest.mark.parametrize('inputs, expected', [ (['stats'], 'Database contains 4 playlists'), # match the db fixture ])