emmental/emmental/db/playlist.py
Anna Schumaker 1b9458c278 db: Add Track support to Playlist Objects
Playlists use a tracks.TrackidSet to manage a set of trackids
representing the Tracks in this Playlist.

I have two functions for loading tracks: load_tracks() and
reload_tracks(). Calling load_tracks() checks if the tracks have been
loaded first before doing any work, but calling reload_tracks() will
force the Playlist to go to the database to load the latest tracks.

Finally, I add a have-next-track property to the main database
connection. This is set to True whenever the active playlist has one or
more tracks.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-05-10 17:31:33 -04:00

156 lines
5.8 KiB
Python

# Copyright 2022 (c) Anna Schumaker
"""A customized Gio.ListStore for tracking Playlist GObjects."""
from gi.repository import GObject
from gi.repository import Gio
from gi.repository import Gtk
from .tracks import Track, TrackidSet
from .. import format
from . import table
class Playlist(table.Row):
"""Our shared Playlist Row object."""
propertyid = GObject.Property(type=int)
name = GObject.Property(type=str)
active = GObject.Property(type=bool, default=False)
tracks = GObject.Property(type=TrackidSet)
n_tracks = GObject.Property(type=int)
tracks_loaded = GObject.Property(type=bool, default=False)
tracks_movable = GObject.Property(type=bool, default=False)
children = GObject.Property(type=Gtk.FilterListModel)
def __init__(self, table: Gio.ListModel, propertyid: int,
name: str, **kwargs):
"""Initialize a Playlist object."""
super().__init__(table=table, propertyid=propertyid, name=name,
tracks=TrackidSet(), **kwargs)
self.tracks.bind_property("n-trackids", self, "n-tracks")
def __add_track(self, track: Track) -> bool:
self.tracks.add_track(track)
return True
def __remove_track(self, track: Track) -> bool:
self.tracks.remove_track(track)
self.table.remove_track(self, track)
return True
def add_children(self, child_table: table.Table,
child_filter: Gtk.Filter) -> None:
"""Create a FilterListModel for this playlist's children."""
self.children = Gtk.FilterListModel.new(child_table, child_filter)
self.children.set_incremental(True)
def do_update(self, column: str) -> bool:
"""Update a Playlist object."""
match column:
case "propertyid" | "name" | "n-tracks" | "children" | \
"tracks-loaded" | "tracks-movable": pass
case _: return super().do_update(column)
return True
def add_track(self, track: Track, *, idle: bool = False) -> None:
"""Add a Track to this Playlist."""
if self.table.add_track(self, track):
self.table.queue.push(self.__add_track, track, now=not idle)
def has_track(self, track: Track) -> bool:
"""Check if a Track is on this Playlist."""
return track in self.tracks
def load_tracks(self) -> bool:
"""Load this Playlist's Tracks (if they haven't been loaded yet)."""
if not self.tracks_loaded:
self.tracks.trackids = self.table.get_trackids(self)
self.tracks_loaded = True
return True
def move_track_down(self, track: Track) -> bool:
"""Move a track down in the sort order."""
return self.table.move_track_down(self, track)
def move_track_up(self, track: Track) -> bool:
"""Move a track up in the sort order."""
return self.table.move_track_up(self, track)
def reload_tracks(self, *, idle: bool = False) -> None:
"""Load this Playlist's Tracks."""
self.tracks_loaded = False
self.table.queue.push(self.load_tracks, now=not idle)
def remove_track(self, track: table.Row, *, idle: bool = False) -> None:
"""Remove a Track from this Playlist."""
self.table.queue.push(self.__remove_track, track, now=not idle)
def rename(self, new_name: str) -> bool:
"""Rename this playlist."""
return self.table.rename(self, new_name)
@GObject.Property(type=table.Row)
def parent(self) -> table.Row | None:
"""Get this playlist's parent playlist."""
return None
class Table(table.Table):
"""A table.Table with extra functionality for Playlists."""
active_playlist = GObject.Property(type=Playlist)
treemodel = GObject.Property(type=Gtk.TreeListModel)
def __init__(self, sql: GObject.TYPE_PYOBJECT, **kwargs):
"""Initialize a Playlist Table."""
super().__init__(sql=sql, **kwargs)
self.treemodel = Gtk.TreeListModel.new(root=self,
passthrough=False,
autoexpand=False,
create_func=self.__create_tree)
def __create_tree(self, plist: Playlist) -> Gtk.FilterListModel | None:
return plist.children
def do_get_sort_key(self, playlist: Playlist) -> tuple[str]:
"""Get a sort key for the requested Playlist."""
return format.sort_key(playlist.name)
def clear(self) -> None:
"""Clear the Table."""
self.active_playlist = None
super().clear()
def construct(self, propertyid: int, name: str, **kwargs) -> Playlist:
"""Construct a new Playlist object."""
res = super().construct(propertyid=propertyid, name=name, **kwargs)
if res.active:
self.sql.set_active_playlist(res)
return res
def delete(self, playlist: Playlist) -> bool:
"""Delete a playlist from the database."""
if playlist.active:
self.sql.set_active_playlist(None)
return super().delete(playlist)
def update(self, playlist: Playlist, column: str, newval) -> bool:
"""Update a Playlist in the Database."""
match column:
case "active":
return self.update_playlist_property(playlist, column, newval)
case _:
return super().update(playlist, column, newval)
def update_playlist_property(self, playlist: Playlist,
column: str, newval) -> bool:
"""Update the playlists_common table."""
match column:
case "active":
self.active_playlist = playlist if playlist.active else None
return self.sql(f"""UPDATE playlist_properties
SET {column}=? WHERE propertyid=?""",
newval, playlist.propertyid) is not None