add support for editing playlist entries
This commit is contained in:
parent
3fdd3ee9a5
commit
27e8ee48eb
|
@ -17,7 +17,15 @@ EDITOR_TEMPLATE = """
|
||||||
# Groove On Demand Playlist Editor
|
# Groove On Demand Playlist Editor
|
||||||
#
|
#
|
||||||
# This file is in YAML format. Blank lines and lines beginning with # are
|
# This file is in YAML format. Blank lines and lines beginning with # are
|
||||||
# ignored. Here's a complete example:
|
# ignored, and the following structure is expected:
|
||||||
|
#
|
||||||
|
# PLAYLIST_TITLE:
|
||||||
|
# description: STRING_DESCRIPTION
|
||||||
|
# entries:
|
||||||
|
# - TRACK_ARTIST - TRACK_TITLE
|
||||||
|
# ...
|
||||||
|
#
|
||||||
|
# Here's a complete example, with a multi-line description:
|
||||||
#
|
#
|
||||||
# My Awesome Jams, Vol. 2:
|
# My Awesome Jams, Vol. 2:
|
||||||
# description: |
|
# description: |
|
||||||
|
@ -26,9 +34,15 @@ EDITOR_TEMPLATE = """
|
||||||
#
|
#
|
||||||
# yo.
|
# yo.
|
||||||
# entries:
|
# entries:
|
||||||
# - Beastie Boys - Help Me, Ronda
|
# - Beastie Boys: Help Me, Rhonda
|
||||||
# - Bob and Doug McKenzie - Messiah (Hallelujah Eh)
|
# - Bob and Doug McKenzie: Messiah (Hallelujah Eh)
|
||||||
#
|
#
|
||||||
|
# Tracks can be reordered or removed. You can also add a track, if the artist/title
|
||||||
|
# combination exactly matches precisely one Track entry your database. Searches are
|
||||||
|
# case-sensitive.
|
||||||
|
#
|
||||||
|
# Playlists can be renamed and descriptions can be updated or removed. If you rename a
|
||||||
|
# playlist, its slug will be regnenerated, breaking previous web links to said playlist.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -28,7 +28,7 @@ class Playlist:
|
||||||
self._slug = slug
|
self._slug = slug
|
||||||
self._name = name
|
self._name = name
|
||||||
self._description = description
|
self._description = description
|
||||||
self._entries = None
|
self._entries = []
|
||||||
self._record = None
|
self._record = None
|
||||||
self._create_ok = create_ok
|
self._create_ok = create_ok
|
||||||
self._deleted = False
|
self._deleted = False
|
||||||
|
@ -62,12 +62,13 @@ class Playlist:
|
||||||
return self._description
|
return self._description
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def summary(self):
|
def info(self):
|
||||||
return ' :: '.join([
|
count = len(self.entries)
|
||||||
f"[ {self.record.id} ]",
|
return f"{self.name}: {self.url} [{count} tracks]\n{self.description}\n"
|
||||||
self.record.name,
|
|
||||||
f"http://{os.environ['HOST']}/{self.slug}",
|
@property
|
||||||
])
|
def url(self) -> str:
|
||||||
|
return f"http://{os.environ['HOST']}:{os.environ['PORT']}/{self.slug}"
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def slug(self) -> str:
|
def slug(self) -> str:
|
||||||
|
@ -83,28 +84,29 @@ class Playlist:
|
||||||
Cache the playlist row from the database and return it. Optionally create it if it doesn't exist.
|
Cache the playlist row from the database and return it. Optionally create it if it doesn't exist.
|
||||||
"""
|
"""
|
||||||
if not self._record:
|
if not self._record:
|
||||||
self._record = self.get_or_create()
|
self.get_or_create()
|
||||||
return self._record
|
return self._record
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def entries(self) -> Union[List, None]:
|
def entries(self) -> List:
|
||||||
"""
|
"""
|
||||||
Cache the list of entries on this playlist and return it.
|
Cache the list of entries on this playlist and return it.
|
||||||
"""
|
"""
|
||||||
if self.record and not self._entries:
|
if not self._entries:
|
||||||
query = self.session.query(
|
if self.record:
|
||||||
db.entry,
|
query = self.session.query(
|
||||||
db.track
|
db.entry,
|
||||||
).filter(
|
db.track
|
||||||
(db.playlist.c.id == self.record.id)
|
).filter(
|
||||||
).filter(
|
(db.playlist.c.id == self.record.id)
|
||||||
db.entry.c.playlist_id == db.playlist.c.id
|
).filter(
|
||||||
).filter(
|
db.entry.c.playlist_id == db.playlist.c.id
|
||||||
db.entry.c.track_id == db.track.c.id
|
).filter(
|
||||||
).order_by(
|
db.entry.c.track_id == db.track.c.id
|
||||||
db.entry.c.track
|
).order_by(
|
||||||
)
|
db.entry.c.track
|
||||||
self._entries = query.all()
|
)
|
||||||
|
self._entries = query.all()
|
||||||
return self._entries
|
return self._entries
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
@ -120,11 +122,9 @@ class Playlist:
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def as_string(self) -> str:
|
def as_string(self) -> str:
|
||||||
if not self.exists:
|
text = self.info
|
||||||
return ''
|
for (tracknum, entry) in enumerate(self.entries):
|
||||||
text = f"{self.summary}\n"
|
text += f" - {tracknum+1} {entry.artist} - {entry.title}\n"
|
||||||
for entry in self.entries:
|
|
||||||
text += f" - {entry.track} {entry.artist} - {entry.title}\n"
|
|
||||||
return text
|
return text
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
@ -132,7 +132,7 @@ class Playlist:
|
||||||
template_vars = self.as_dict
|
template_vars = self.as_dict
|
||||||
template_vars['entries'] = ''
|
template_vars['entries'] = ''
|
||||||
for entry in self.entries:
|
for entry in self.entries:
|
||||||
template_vars['entries'] += f" - {entry.artist} - {entry.title}\n"
|
template_vars['entries'] += f' - "{entry.artist}": "{entry.title}"\n'
|
||||||
return EDITOR_TEMPLATE.format(**template_vars)
|
return EDITOR_TEMPLATE.format(**template_vars)
|
||||||
|
|
||||||
def _get_tracks_by_path(self, paths: List[str]) -> List:
|
def _get_tracks_by_path(self, paths: List[str]) -> List:
|
||||||
|
@ -142,6 +142,14 @@ class Playlist:
|
||||||
"""
|
"""
|
||||||
return [self.session.query(db.track).filter(db.track.c.relpath.ilike(f"%{path}%")).one() for path in paths]
|
return [self.session.query(db.track).filter(db.track.c.relpath.ilike(f"%{path}%")).one() for path in paths]
|
||||||
|
|
||||||
|
def _get_tracks_by_artist_and_title(self, entries: List[tuple]) -> List:
|
||||||
|
return [
|
||||||
|
self.session.query(db.track).filter(
|
||||||
|
db.track.c.artist == artist, db.track.c.title == title
|
||||||
|
).one()
|
||||||
|
for (artist, title) in entries
|
||||||
|
]
|
||||||
|
|
||||||
def edit(self):
|
def edit(self):
|
||||||
edits = self.editor.edit(self)
|
edits = self.editor.edit(self)
|
||||||
if not edits:
|
if not edits:
|
||||||
|
@ -154,7 +162,8 @@ class Playlist:
|
||||||
self._slug = new.slug
|
self._slug = new.slug
|
||||||
self._name = new.name.strip()
|
self._name = new.name.strip()
|
||||||
self._description = new.description.strip()
|
self._description = new.description.strip()
|
||||||
self._record = self.save()
|
self._entries = new._entries
|
||||||
|
self.save()
|
||||||
|
|
||||||
def add(self, paths: List[str]) -> int:
|
def add(self, paths: List[str]) -> int:
|
||||||
"""
|
"""
|
||||||
|
@ -198,13 +207,14 @@ class Playlist:
|
||||||
|
|
||||||
def get_or_create(self, create_ok: bool = False) -> Row:
|
def get_or_create(self, create_ok: bool = False) -> Row:
|
||||||
try:
|
try:
|
||||||
return self._get()
|
self._record = self._get()
|
||||||
|
return
|
||||||
except NoResultFound:
|
except NoResultFound:
|
||||||
logging.debug(f"Could not find a playlist with slug {self.slug}.")
|
logging.debug(f"Could not find a playlist with slug {self.slug}.")
|
||||||
if self.deleted:
|
if self.deleted:
|
||||||
raise RuntimeError("Object has been deleted.")
|
raise RuntimeError("Object has been deleted.")
|
||||||
if self._create_ok or create_ok:
|
if self._create_ok or create_ok:
|
||||||
return self.save()
|
self.save()
|
||||||
|
|
||||||
def _get(self):
|
def _get(self):
|
||||||
return self.session.query(db.playlist).filter(
|
return self.session.query(db.playlist).filter(
|
||||||
|
@ -237,9 +247,16 @@ class Playlist:
|
||||||
'description': self.description
|
'description': self.description
|
||||||
}
|
}
|
||||||
logging.debug(f"Saving values: {values}")
|
logging.debug(f"Saving values: {values}")
|
||||||
obj = self._update(values) if self._record else self._insert(values)
|
self._record = self._update(values) if self._record else self._insert(values)
|
||||||
logging.debug(f"Saved playlist {obj.id} with slug {obj.slug}")
|
logging.debug(f"Saved playlist {self._record.id} with slug {self._record.slug}")
|
||||||
return obj
|
self.save_entries()
|
||||||
|
|
||||||
|
def save_entries(self):
|
||||||
|
plid = self.record.id
|
||||||
|
stmt = delete(db.entry).where(db.entry.c.playlist_id == plid)
|
||||||
|
logging.debug(f"Deleting entries associated with playlist {plid}: {stmt}")
|
||||||
|
self.session.execute(stmt)
|
||||||
|
return self.create_entries(self.entries)
|
||||||
|
|
||||||
def load(self):
|
def load(self):
|
||||||
self.get_or_create(create_ok=False)
|
self.get_or_create(create_ok=False)
|
||||||
|
@ -282,7 +299,7 @@ class Playlist:
|
||||||
try:
|
try:
|
||||||
name = list(source.keys())[0].strip()
|
name = list(source.keys())[0].strip()
|
||||||
description = (source[name]['description'] or '').strip()
|
description = (source[name]['description'] or '').strip()
|
||||||
return Playlist(
|
pl = Playlist(
|
||||||
slug=slugify(name),
|
slug=slugify(name),
|
||||||
name=name,
|
name=name,
|
||||||
description=description,
|
description=description,
|
||||||
|
@ -291,7 +308,14 @@ class Playlist:
|
||||||
except (IndexError, KeyError):
|
except (IndexError, KeyError):
|
||||||
PlaylistImportError("The specified source was not a valid playlist.")
|
PlaylistImportError("The specified source was not a valid playlist.")
|
||||||
|
|
||||||
|
pl._entries = pl._get_tracks_by_artist_and_title(entries=[
|
||||||
|
list(entry.items())[0] for entry in source[name]['entries']
|
||||||
|
])
|
||||||
|
return pl
|
||||||
|
|
||||||
def __eq__(self, obj):
|
def __eq__(self, obj):
|
||||||
|
logging.debug(f"Comparing obj to self:\n{obj.as_string}\n--\n{self.as_string}")
|
||||||
|
return obj.as_string == self.as_string
|
||||||
for key in ('slug', 'name', 'description'):
|
for key in ('slug', 'name', 'description'):
|
||||||
if getattr(obj, key) != getattr(self, key):
|
if getattr(obj, key) != getattr(self, key):
|
||||||
logging.debug(f"{key}: {getattr(obj, key)} != {getattr(self, key)}")
|
logging.debug(f"{key}: {getattr(obj, key)} != {getattr(self, key)}")
|
||||||
|
|
Loading…
Reference in New Issue
Block a user