Revert "adding cleanup of stale track entries"

This reverts commit 1a2506742f.
This commit is contained in:
evilchili 2022-12-10 10:20:30 -08:00
parent 1a2506742f
commit 4b6246096d
9 changed files with 28 additions and 64 deletions

View File

@ -106,7 +106,6 @@ def scan(
initialize() initialize()
with database_manager() as manager: with database_manager() as manager:
scanner = media_scanner(root=root, db=manager.session) scanner = media_scanner(root=root, db=manager.session)
scanner.cleanup()
count = scanner.scan() count = scanner.scan()
logging.info(f"Imported {count} new tracks.") logging.info(f"Imported {count} new tracks.")

View File

@ -5,7 +5,7 @@ import music_tag
from pathlib import Path from pathlib import Path
from typing import Callable, Union, Iterable from typing import Callable, Union, Iterable
from sqlalchemy import func, delete from sqlalchemy import func
import groove.db import groove.db
import groove.path import groove.path
@ -40,31 +40,10 @@ class MediaScanner:
async def _do_import(): async def _do_import():
logging.debug("Scanning filesystem (this may take a minute)...") logging.debug("Scanning filesystem (this may take a minute)...")
for path in sources: 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()) asyncio.run(_do_import())
self.db.commit() 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 def _get_tags(self, path): # pragma: no cover
tags = music_tag.load_file(path) tags = music_tag.load_file(path)
return { return {

View File

@ -66,7 +66,7 @@ class Playlist:
@property @property
def info(self): def info(self):
count = len(self.entries) 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 @property
def url(self) -> str: def url(self) -> str:
@ -133,7 +133,7 @@ class Playlist:
@property @property
def as_string(self) -> str: def as_string(self) -> str:
text = self.info + self.description text = self.info
for (tracknum, entry) in enumerate(self.entries): for (tracknum, entry) in enumerate(self.entries):
text += f" - {tracknum+1} {entry.artist} - {entry.title}\n" text += f" - {tracknum+1} {entry.artist} - {entry.title}\n"
return text return text

View File

@ -14,11 +14,6 @@ class BasePrompt(Completer):
self._values = [] self._values = []
self._parent = parent self._parent = parent
self._manager = manager self._manager = manager
self._commands = {'help': self.help}
@property
def commands(self):
return self._commands
@property @property
def usage(self): def usage(self):
@ -45,7 +40,7 @@ class BasePrompt(Completer):
@property @property
def values(self): 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): def get_completions(self, document, complete_event):
word = document.get_word_before_cursor() word = document.get_word_before_cursor()
@ -64,7 +59,7 @@ class BasePrompt(Completer):
def start(self, cmd=''): def start(self, cmd=''):
while True: while True:
if not cmd: if not cmd:
cmd = prompt(f'{self.prompt} ', completer=self, complete_while_typing=True) cmd = prompt(f'{self.prompt} ', completer=self)
if not cmd: if not cmd:
return return
cmd, *parts = cmd.split() cmd, *parts = cmd.split()
@ -72,13 +67,6 @@ class BasePrompt(Completer):
return return
cmd = '' 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): def default_completer(self, document, complete_event):
raise NotImplementedError() raise NotImplementedError()

View File

@ -54,8 +54,8 @@ class CommandPrompt(BasePrompt):
session=self.manager.session, session=self.manager.session,
create_ok=True create_ok=True
) )
self.commands['_playlist'].start() res = self.commands['_playlist'].start()
return True return True and res
def start(): # pragma: no cover def start(): # pragma: no cover

View File

@ -8,14 +8,21 @@ from groove import db
class _playlist(BasePrompt): 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 @property
def prompt(self): def prompt(self):
return f"{self.parent.playlist}\n{self.parent.playlist.slug}> " return f"{self.parent.playlist}\n{self.parent.playlist.slug}> "
@property
def values(self):
return self.commands.keys()
@property @property
def commands(self): def commands(self):
if not self._commands: if not self._commands:
@ -24,20 +31,9 @@ class _playlist(BasePrompt):
'delete': self.delete, 'delete': self.delete,
'add': self.add, 'add': self.add,
'edit': self.edit, 'edit': self.edit,
'help': self.help
} }
return self._commands 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): def process(self, cmd, *parts):
res = True res = True
if cmd in self.commands: if cmd in self.commands:
@ -70,6 +66,16 @@ class _playlist(BasePrompt):
return True return True
self._add_track(text) 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): def delete(self, parts):
res = prompt( res = prompt(
'Type DELETE to permanently delete the playlist ' 'Type DELETE to permanently delete the playlist '

View File

@ -38,10 +38,6 @@ def test_scanner(monkeypatch, in_memory_db, media):
# replace the filesystem glob with the test fixture generator # replace the filesystem glob with the test fixture generator
monkeypatch.setattr(scanner.MediaScanner, 'find_sources', MagicMock(return_value=media())) 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): def mock_loader(path):
return { return {
'artist': 'foo', 'artist': 'foo',

View File

@ -16,10 +16,6 @@ def response_factory(responses):
return MagicMock(side_effect=responses + ([''] * 10)) 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', [ @pytest.mark.parametrize('inputs, expected', [
(['stats'], 'Database contains 4 playlists'), # match the db fixture (['stats'], 'Database contains 4 playlists'), # match the db fixture
]) ])