db: Give System Playlists special property handling
I need to do something slightly different for each system Playlist: * Collection: I disallow setting loop to "None". * Favorite Tracks: I set the user-tracks property to "True" * Most Played Tracks: I add playcount as the first sort field. * Previous Tracks: I disallow changing loop, shuffle, and sort-order. * Queued Tracks: I set the user-tracks and tracks-movable properties to "True" User created playlists also set the user-tracks and tracks-movable properties to "True". I also disable autodelete on the Table so playlists aren't deleted unexpectedly. New Tracks and Unplayed Tracks have no special properties set. Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
This commit is contained in:
parent
85c42216ab
commit
99496ca8bf
|
@ -60,14 +60,23 @@ CREATE TABLE playlists (
|
|||
);
|
||||
|
||||
CREATE VIEW playlists_view AS
|
||||
SELECT playlistid, propertyid, name, image, active
|
||||
SELECT playlistid, propertyid, name, image,
|
||||
active, loop, shuffle, sort_order, current_trackid
|
||||
FROM playlists
|
||||
JOIN playlist_properties USING (propertyid);
|
||||
|
||||
CREATE TRIGGER playlists_insert_trigger AFTER INSERT ON playlists
|
||||
BEGIN
|
||||
INSERT INTO playlist_properties (active)
|
||||
VALUES (NEW.name == "Collection");
|
||||
INSERT INTO playlist_properties (active, loop, sort_order)
|
||||
VALUES (NEW.name == "Collection",
|
||||
IIF(NEW.name == "Collection", "Playlist", "None"),
|
||||
CASE
|
||||
WHEN NEW.name == "Most Played Tracks"
|
||||
THEN "playcount DESC, albumartist, album, mediumno, number"
|
||||
WHEN NEW.name == "Previous Tracks"
|
||||
THEN "laststarted DESC"
|
||||
ELSE "albumartist, album, mediumno, number"
|
||||
END);
|
||||
UPDATE playlists SET propertyid = last_insert_rowid()
|
||||
WHERE playlistid = NEW.playlistid;
|
||||
END;
|
||||
|
@ -77,6 +86,42 @@ CREATE TRIGGER playlists_delete_trigger AFTER DELETE ON playlists
|
|||
DELETE FROM playlist_properties WHERE propertyid = OLD.propertyid;
|
||||
END;
|
||||
|
||||
CREATE TRIGGER collection_loop_trigger
|
||||
BEFORE UPDATE OF loop ON playlist_properties
|
||||
WHEN NEW.loop == "None" AND NEW.propertyid == (SELECT propertyid
|
||||
FROM playlists
|
||||
WHERE name='Collection')
|
||||
BEGIN
|
||||
SELECT RAISE(ABORT, "Collection playlist cannot disable loop");
|
||||
END;
|
||||
|
||||
CREATE TRIGGER previous_loop_trigger
|
||||
BEFORE UPDATE OF loop ON playlist_properties
|
||||
WHEN NEW.loop != "None" AND NEW.propertyid == (SELECT propertyid
|
||||
FROM playlists
|
||||
WHERE name='Previous Tracks')
|
||||
BEGIN
|
||||
SELECT RAISE(ABORT, "Previous Tracks cannot be looped");
|
||||
END;
|
||||
|
||||
CREATE TRIGGER previous_shuffle_trigger
|
||||
BEFORE UPDATE OF shuffle ON playlist_properties
|
||||
WHEN NEW.shuffle = TRUE AND NEW.propertyid == (SELECT propertyid
|
||||
FROM playlists
|
||||
WHERE name='Previous Tracks')
|
||||
BEGIN
|
||||
SELECT RAISE(ABORT, "Previous Tracks cannot be shuffled");
|
||||
END;
|
||||
|
||||
CREATE TRIGGER previous_sort_order_trigger
|
||||
BEFORE UPDATE OF sort_order ON playlist_properties
|
||||
WHEN NEW.sort_order != "laststarted DESC" AND NEW.propertyid == (SELECT propertyid
|
||||
FROM playlists
|
||||
WHERE name='Previous Tracks')
|
||||
BEGIN
|
||||
SELECT RAISE(ABORT, "Previous Tracks cannot be sorted");
|
||||
END;
|
||||
|
||||
|
||||
/*************************
|
||||
* *
|
||||
|
|
|
@ -11,6 +11,21 @@ class Playlist(playlist.Playlist):
|
|||
playlistid = GObject.Property(type=int)
|
||||
image = GObject.Property(type=GObject.TYPE_PYOBJECT)
|
||||
|
||||
def do_update(self, column: str) -> None:
|
||||
"""Update a playlist object."""
|
||||
match (self.name, column, self.get_property(column)):
|
||||
case ("Collection", "loop", "None"):
|
||||
self.loop = "Playlist"
|
||||
case ("Previous Tracks", "loop", "Playlist") | \
|
||||
("Previous Tracks", "loop", "Track"):
|
||||
self.loop = "None"
|
||||
case ("Previous Tracks", "shuffle", True):
|
||||
self.shuffle = False
|
||||
case ("Previous Tracks", "sort-order", _):
|
||||
if self.sort_order != "laststarted DESC":
|
||||
self.sort_order = "laststarted DESC"
|
||||
case (_, _, _): super().do_update(column)
|
||||
|
||||
def rename(self, new_name: str) -> bool:
|
||||
"""Rename this playlist."""
|
||||
return self.table.rename(self, new_name)
|
||||
|
@ -32,16 +47,28 @@ class Table(playlist.Table):
|
|||
queued = GObject.Property(type=Playlist)
|
||||
unplayed = GObject.Property(type=Playlist)
|
||||
|
||||
def __init__(self, sql: GObject.TYPE_PYOBJECT, **kwargs):
|
||||
"""Initialize the Playlists Table."""
|
||||
super().__init__(sql=sql, **kwargs)
|
||||
|
||||
def do_construct(self, **kwargs) -> Playlist:
|
||||
"""Construct a new playlist."""
|
||||
match (plist := Playlist(**kwargs)).name:
|
||||
case "Collection": self.collection = plist
|
||||
case "Favorite Tracks": self.favorites = plist
|
||||
case "Favorite Tracks":
|
||||
self.favorites = plist
|
||||
self.favorites.user_tracks = True
|
||||
case "Most Played Tracks": self.most_played = plist
|
||||
case "New Tracks": self.new_tracks = plist
|
||||
case "Previous Tracks": self.previous = plist
|
||||
case "Queued Tracks": self.queued = plist
|
||||
case "Queued Tracks":
|
||||
self.queued = plist
|
||||
self.queued.user_tracks = True
|
||||
self.queued.tracks_movable = True
|
||||
case "Unplayed Tracks": self.unplayed = plist
|
||||
case _:
|
||||
plist.user_tracks = True
|
||||
plist.tracks_movable = True
|
||||
return plist
|
||||
|
||||
def do_sql_delete(self, playlist: Playlist) -> sqlite3.Cursor:
|
||||
|
|
|
@ -54,9 +54,19 @@ class TestPlaylistTable(tests.util.TestCase):
|
|||
self.sql("DELETE FROM playlists")
|
||||
self.table = self.sql.playlists
|
||||
|
||||
self.library = self.sql.libraries.create(pathlib.Path("/a/b"))
|
||||
self.album = self.sql.albums.create("Test Album", "Artist", "2023-04")
|
||||
self.medium = self.sql.media.create(self.album, "", number=1)
|
||||
self.year = self.sql.years.create(2023)
|
||||
|
||||
self.track = self.sql.tracks.create(self.library,
|
||||
pathlib.Path("/a/b/c.ogg"),
|
||||
self.medium, self.year)
|
||||
|
||||
def test_init(self):
|
||||
"""Test that the playlist model is configured correctly."""
|
||||
self.assertIsInstance(self.table, emmental.db.playlist.Table)
|
||||
self.assertFalse(self.table.autodelete)
|
||||
self.assertEqual(len(self.table), 0)
|
||||
|
||||
self.assertIsNone(self.table.collection)
|
||||
|
@ -77,12 +87,17 @@ class TestPlaylistTable(tests.util.TestCase):
|
|||
self.assertEqual(playlist.playlistid, 1)
|
||||
self.assertEqual(playlist.name, "Test Playlist")
|
||||
self.assertIsNone(playlist.image)
|
||||
self.assertTrue(playlist.user_tracks)
|
||||
self.assertTrue(playlist.tracks_movable)
|
||||
|
||||
def test_create(self):
|
||||
"""Test creating a playlist."""
|
||||
playlist = self.table.create(" Test Playlist ")
|
||||
self.assertIsInstance(playlist, emmental.db.playlists.Playlist)
|
||||
self.assertEqual(playlist.name, "Test Playlist")
|
||||
self.assertEqual(playlist.loop, "None")
|
||||
self.assertEqual(playlist.sort_order,
|
||||
"albumartist, album, mediumno, number")
|
||||
self.assertIsNone(playlist.image)
|
||||
|
||||
cur = self.sql("SELECT COUNT(name) FROM playlists")
|
||||
|
@ -91,7 +106,7 @@ class TestPlaylistTable(tests.util.TestCase):
|
|||
self.assertEqual(self.table.get_item(0), playlist)
|
||||
|
||||
cur = self.sql("SELECT COUNT(*) FROM playlist_properties")
|
||||
self.assertEqual(cur.fetchone()["COUNT(*)"], 1)
|
||||
self.assertEqual(cur.fetchone()["COUNT(*)"], playlist.propertyid)
|
||||
|
||||
for name in ["", " ", "Test Playlist", "test playlist"]:
|
||||
self.assertIsNone(self.table.create(name))
|
||||
|
@ -111,7 +126,7 @@ class TestPlaylistTable(tests.util.TestCase):
|
|||
self.assertIsNone(self.table.get_item(0))
|
||||
|
||||
cur = self.sql("SELECT COUNT(*) FROM playlist_properties")
|
||||
self.assertEqual(cur.fetchone()["COUNT(*)"], 0)
|
||||
self.assertEqual(cur.fetchone()["COUNT(*)"], playlist.propertyid - 1)
|
||||
|
||||
self.assertFalse(playlist.delete())
|
||||
|
||||
|
@ -171,11 +186,21 @@ class TestPlaylistTable(tests.util.TestCase):
|
|||
playlist = self.table.create("Test Playlist")
|
||||
playlist.image = tests.util.COVER_JPG
|
||||
playlist.active = True
|
||||
playlist.loop = "Track"
|
||||
playlist.sort_order = "trackid"
|
||||
playlist.shuffle = True
|
||||
playlist.current_trackid = self.track.trackid
|
||||
|
||||
cur = self.sql("""SELECT image, active FROM playlists_view
|
||||
cur = self.sql("""SELECT image, active, loop, shuffle,
|
||||
sort_order, current_trackid
|
||||
FROM playlists_view
|
||||
WHERE playlistid=?""", playlist.playlistid)
|
||||
row = cur.fetchone()
|
||||
self.assertEqual(row["image"], tests.util.COVER_JPG)
|
||||
self.assertEqual(row["loop"], "Track")
|
||||
self.assertEqual(row["shuffle"], True)
|
||||
self.assertEqual(row["sort_order"], "trackid")
|
||||
self.assertEqual(row["current_trackid"], self.track.trackid)
|
||||
self.assertTrue(row["active"])
|
||||
|
||||
|
||||
|
@ -192,18 +217,48 @@ class TestSystemPlaylists(tests.util.TestCase):
|
|||
"""Test the Collection playlist."""
|
||||
self.assertIsInstance(self.table.collection,
|
||||
emmental.db.playlists.Playlist)
|
||||
|
||||
self.assertEqual(self.table.collection.name, "Collection")
|
||||
self.assertEqual(self.table.collection.loop, "Playlist")
|
||||
self.assertEqual(self.table.collection.sort_order,
|
||||
"albumartist, album, mediumno, number")
|
||||
|
||||
self.assertTrue(self.table.collection.active)
|
||||
|
||||
self.assertFalse(self.table.collection.shuffle)
|
||||
self.assertFalse(self.table.collection.user_tracks)
|
||||
self.assertFalse(self.table.collection.tracks_movable)
|
||||
|
||||
self.assertEqual(self.table.lookup("Collection"),
|
||||
self.table.collection)
|
||||
|
||||
def test_collection_loop(self):
|
||||
"""Test that the Collection::loop property cannot be disabled."""
|
||||
self.assertIsNone(self.sql("""UPDATE playlist_properties
|
||||
SET loop='None' WHERE propertyid=?""",
|
||||
self.table.collection.propertyid))
|
||||
|
||||
self.assertEqual(self.table.collection.loop, "Playlist")
|
||||
self.table.collection.loop = "Track"
|
||||
self.assertEqual(self.table.collection.loop, "Track")
|
||||
self.table.collection.loop = "None"
|
||||
self.assertEqual(self.table.collection.loop, "Playlist")
|
||||
|
||||
def test_favorites(self):
|
||||
"""Test the favorite tracks playlist."""
|
||||
self.assertIsInstance(self.table.favorites,
|
||||
emmental.db.playlists.Playlist)
|
||||
|
||||
self.assertEqual(self.table.favorites.name, "Favorite Tracks")
|
||||
self.assertEqual(self.table.favorites.loop, "None")
|
||||
self.assertEqual(self.table.favorites.sort_order,
|
||||
"albumartist, album, mediumno, number")
|
||||
|
||||
self.assertTrue(self.table.favorites.user_tracks)
|
||||
|
||||
self.assertFalse(self.table.favorites.shuffle)
|
||||
self.assertFalse(self.table.favorites.active)
|
||||
self.assertFalse(self.table.favorites.tracks_movable)
|
||||
|
||||
self.assertEqual(self.table.lookup("Favorite Tracks"),
|
||||
self.table.favorites)
|
||||
|
@ -212,8 +267,16 @@ class TestSystemPlaylists(tests.util.TestCase):
|
|||
"""Test the most-played tracks playlist."""
|
||||
self.assertIsInstance(self.table.most_played,
|
||||
emmental.db.playlists.Playlist)
|
||||
|
||||
sort_order = "playcount DESC, albumartist, album, mediumno, number"
|
||||
self.assertEqual(self.table.most_played.name, "Most Played Tracks")
|
||||
self.assertEqual(self.table.most_played.loop, "None")
|
||||
self.assertEqual(self.table.most_played.sort_order, sort_order)
|
||||
|
||||
self.assertFalse(self.table.most_played.active)
|
||||
self.assertFalse(self.table.most_played.shuffle)
|
||||
self.assertFalse(self.table.most_played.user_tracks)
|
||||
self.assertFalse(self.table.most_played.tracks_movable)
|
||||
|
||||
self.assertEqual(self.table.lookup("Most Played Tracks"),
|
||||
self.table.most_played)
|
||||
|
@ -222,8 +285,16 @@ class TestSystemPlaylists(tests.util.TestCase):
|
|||
"""Test the new tracks playlist."""
|
||||
self.assertIsInstance(self.table.new_tracks,
|
||||
emmental.db.playlists.Playlist)
|
||||
|
||||
self.assertEqual(self.table.new_tracks.name, "New Tracks")
|
||||
self.assertEqual(self.table.new_tracks.loop, "None")
|
||||
self.assertEqual(self.table.new_tracks.sort_order,
|
||||
"albumartist, album, mediumno, number")
|
||||
|
||||
self.assertFalse(self.table.new_tracks.active)
|
||||
self.assertFalse(self.table.new_tracks.shuffle)
|
||||
self.assertFalse(self.table.new_tracks.user_tracks)
|
||||
self.assertFalse(self.table.new_tracks.tracks_movable)
|
||||
|
||||
self.assertEqual(self.table.lookup("New Tracks"),
|
||||
self.table.new_tracks)
|
||||
|
@ -232,18 +303,67 @@ class TestSystemPlaylists(tests.util.TestCase):
|
|||
"""Test the previous tracks playlist."""
|
||||
self.assertIsInstance(self.table.previous,
|
||||
emmental.db.playlists.Playlist)
|
||||
|
||||
self.assertEqual(self.table.previous.name, "Previous Tracks")
|
||||
self.assertEqual(self.table.previous.loop, "None")
|
||||
self.assertEqual(self.table.previous.sort_order, "laststarted DESC")
|
||||
|
||||
self.assertFalse(self.table.previous.active)
|
||||
self.assertFalse(self.table.previous.shuffle)
|
||||
self.assertFalse(self.table.previous.user_tracks)
|
||||
self.assertFalse(self.table.previous.tracks_movable)
|
||||
|
||||
self.assertEqual(self.table.lookup("Previous Tracks"),
|
||||
self.table.previous)
|
||||
|
||||
def test_previous_loop(self):
|
||||
"""Test that the Previous::loop property cannot be disabled."""
|
||||
for loop in ["Track", "Playlist"]:
|
||||
with self.subTest(loop=loop):
|
||||
cur = self.sql("""UPDATE playlist_properties
|
||||
SET loop=? WHERE propertyid=?""",
|
||||
loop, self.table.previous.propertyid)
|
||||
self.assertIsNone(cur)
|
||||
|
||||
self.table.previous.loop = loop
|
||||
self.assertEqual(self.table.previous.loop, "None")
|
||||
|
||||
def test_previous_shuffle(self):
|
||||
"""Test that the Previous::shuffle property cannot be enabled."""
|
||||
self.assertIsNone(self.sql("""UPDATE playlist_properties
|
||||
SET shuffle=TRUE WHERE propertyid=?""",
|
||||
self.table.previous.propertyid))
|
||||
|
||||
self.assertFalse(self.table.previous.shuffle)
|
||||
self.table.previous.shuffle = True
|
||||
self.assertFalse(self.table.previous.shuffle)
|
||||
|
||||
def test_previous_sort_order(self):
|
||||
"""Test that the Previous::sort-order property cannot be changed."""
|
||||
self.assertIsNone(self.sql("""UPDATE playlist_properties
|
||||
SET sort_order='trackid'
|
||||
WHERE propertyid=?""",
|
||||
self.table.previous.propertyid))
|
||||
|
||||
self.assertEqual(self.table.previous.sort_order, "laststarted DESC")
|
||||
self.table.previous.sort_order = "trackid"
|
||||
self.assertEqual(self.table.previous.sort_order, "laststarted DESC")
|
||||
|
||||
def test_queued(self):
|
||||
"""Test the queued tracks playlist."""
|
||||
self.assertIsInstance(self.table.queued,
|
||||
emmental.db.playlists.Playlist)
|
||||
|
||||
self.assertEqual(self.table.queued.name, "Queued Tracks")
|
||||
self.assertEqual(self.table.queued.loop, "None")
|
||||
self.assertEqual(self.table.queued.sort_order,
|
||||
"albumartist, album, mediumno, number")
|
||||
|
||||
self.assertTrue(self.table.queued.user_tracks)
|
||||
self.assertTrue(self.table.queued.tracks_movable)
|
||||
|
||||
self.assertFalse(self.table.queued.active)
|
||||
self.assertFalse(self.table.queued.shuffle)
|
||||
|
||||
self.assertEqual(self.table.lookup("Queued Tracks"),
|
||||
self.table.queued)
|
||||
|
@ -252,8 +372,16 @@ class TestSystemPlaylists(tests.util.TestCase):
|
|||
"""Test the unplayed tracks playlist."""
|
||||
self.assertIsInstance(self.table.unplayed,
|
||||
emmental.db.playlists.Playlist)
|
||||
|
||||
self.assertEqual(self.table.unplayed.name, "Unplayed Tracks")
|
||||
self.assertEqual(self.table.unplayed.loop, "None")
|
||||
self.assertEqual(self.table.unplayed.sort_order,
|
||||
"albumartist, album, mediumno, number")
|
||||
|
||||
self.assertFalse(self.table.unplayed.active)
|
||||
self.assertFalse(self.table.unplayed.shuffle)
|
||||
self.assertFalse(self.table.unplayed.user_tracks)
|
||||
self.assertFalse(self.table.unplayed.tracks_movable)
|
||||
|
||||
self.assertEqual(self.table.lookup("Unplayed Tracks"),
|
||||
self.table.unplayed)
|
||||
|
|
Loading…
Reference in New Issue