db: Give Albums knowledge about their Tracks & Properties
I expand on the albums_view to include additional playlist properties, and configure the default sort order to sort by track numbers in an intuitive way. I then set up the album_tracks_view to make it easy to select tracks that belong to a specific album. Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
This commit is contained in:
parent
b4d8a7cfaa
commit
8e55de26d1
|
@ -7,6 +7,7 @@ from gi.repository import Gtk
|
||||||
from .media import Medium
|
from .media import Medium
|
||||||
from .. import format
|
from .. import format
|
||||||
from . import playlist
|
from . import playlist
|
||||||
|
from . import tracks
|
||||||
|
|
||||||
|
|
||||||
class Album(playlist.Playlist):
|
class Album(playlist.Playlist):
|
||||||
|
@ -50,6 +51,15 @@ class Album(playlist.Playlist):
|
||||||
class Table(playlist.Table):
|
class Table(playlist.Table):
|
||||||
"""Our Album Table."""
|
"""Our Album Table."""
|
||||||
|
|
||||||
|
def __init__(self, sql: GObject.TYPE_PYOBJECT, **kwargs):
|
||||||
|
"""Initialize the Album Table."""
|
||||||
|
super().__init__(sql=sql, autodelete=True,
|
||||||
|
system_tracks=False, **kwargs)
|
||||||
|
|
||||||
|
def do_add_track(self, album: Album, track: tracks.Track) -> bool:
|
||||||
|
"""Verify adding a Track to the Album playlist."""
|
||||||
|
return track.get_medium().get_album() == album
|
||||||
|
|
||||||
def do_construct(self, **kwargs) -> Album:
|
def do_construct(self, **kwargs) -> Album:
|
||||||
"""Construct a new album."""
|
"""Construct a new album."""
|
||||||
return Album(**kwargs)
|
return Album(**kwargs)
|
||||||
|
@ -62,6 +72,10 @@ class Table(playlist.Table):
|
||||||
format.sort_key(album.artist),
|
format.sort_key(album.artist),
|
||||||
album.release)
|
album.release)
|
||||||
|
|
||||||
|
def do_remove_track(self, album: Album, track: tracks.Track) -> bool:
|
||||||
|
"""Verify removing a Track from the Album playlist."""
|
||||||
|
return True
|
||||||
|
|
||||||
def do_sql_delete(self, album: Album) -> sqlite3.Cursor:
|
def do_sql_delete(self, album: Album) -> sqlite3.Cursor:
|
||||||
"""Delete an album."""
|
"""Delete an album."""
|
||||||
for artist in album.get_artists():
|
for artist in album.get_artists():
|
||||||
|
@ -106,6 +120,11 @@ class Table(playlist.Table):
|
||||||
return self.sql(f"""SELECT albumid FROM albums
|
return self.sql(f"""SELECT albumid FROM albums
|
||||||
WHERE {" AND ".join(where)}""", *args)
|
WHERE {" AND ".join(where)}""", *args)
|
||||||
|
|
||||||
|
def do_sql_select_trackids(self, album: Album) -> sqlite3.Cursor:
|
||||||
|
"""Load an Album's Tracks from the database."""
|
||||||
|
return self.sql("""SELECT trackid FROM album_tracks_view
|
||||||
|
WHERE albumid=?""", album.albumid)
|
||||||
|
|
||||||
def do_sql_update(self, album: Album, column: str, newval) -> bool:
|
def do_sql_update(self, album: Album, column: str, newval) -> bool:
|
||||||
"""Rename an album."""
|
"""Rename an album."""
|
||||||
return self.sql(f"UPDATE albums SET {column}=? WHERE albumid=?",
|
return self.sql(f"UPDATE albums SET {column}=? WHERE albumid=?",
|
||||||
|
|
|
@ -181,14 +181,16 @@ CREATE TABLE albums (
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE VIEW albums_view AS
|
CREATE VIEW albums_view AS
|
||||||
SELECT albumid, propertyid, name, mbid, artist, release, cover, active
|
SELECT albumid, propertyid, name, mbid, artist, release, cover,
|
||||||
|
active, loop, shuffle, sort_order, current_trackid
|
||||||
FROM albums
|
FROM albums
|
||||||
JOIN playlist_properties USING (propertyid);
|
JOIN playlist_properties USING (propertyid);
|
||||||
|
|
||||||
|
|
||||||
CREATE TRIGGER albums_insert_trigger AFTER INSERT ON albums
|
CREATE TRIGGER albums_insert_trigger AFTER INSERT ON albums
|
||||||
BEGIN
|
BEGIN
|
||||||
INSERT INTO playlist_properties (active) VALUES (False);
|
INSERT INTO playlist_properties (active, sort_order)
|
||||||
|
VALUES (False, "mediumno, number");
|
||||||
UPDATE albums SET propertyid = last_insert_rowid(),
|
UPDATE albums SET propertyid = last_insert_rowid(),
|
||||||
mbid = LOWER(NEW.mbid)
|
mbid = LOWER(NEW.mbid)
|
||||||
WHERE albumid = NEW.albumid;
|
WHERE albumid = NEW.albumid;
|
||||||
|
@ -526,6 +528,14 @@ CREATE VIEW artist_tracks_view AS
|
||||||
JOIN libraries USING (libraryid)
|
JOIN libraries USING (libraryid)
|
||||||
WHERE libraries.deleting = False;
|
WHERE libraries.deleting = False;
|
||||||
|
|
||||||
|
CREATE VIEW album_tracks_view AS
|
||||||
|
SELECT tracks.trackid, albums.albumid
|
||||||
|
FROM tracks
|
||||||
|
JOIN media USING (mediumid)
|
||||||
|
JOIN albums USING (albumid)
|
||||||
|
JOIN libraries USING (libraryid)
|
||||||
|
WHERE libraries.deleting = False;
|
||||||
|
|
||||||
|
|
||||||
/****************************************************
|
/****************************************************
|
||||||
* *
|
* *
|
||||||
|
|
|
@ -82,10 +82,26 @@ class TestAlbumTable(tests.util.TestCase):
|
||||||
tests.util.TestCase.setUp(self)
|
tests.util.TestCase.setUp(self)
|
||||||
self.table = self.sql.albums
|
self.table = self.sql.albums
|
||||||
|
|
||||||
|
self.library = self.sql.libraries.create(pathlib.Path("/a/b"))
|
||||||
|
self.year = self.sql.years.create(2023)
|
||||||
|
|
||||||
def test_init(self):
|
def test_init(self):
|
||||||
"""Test that the album model is configured correctly."""
|
"""Test that the album model is configured correctly."""
|
||||||
self.assertIsInstance(self.table, emmental.db.playlist.Table)
|
self.assertIsInstance(self.table, emmental.db.playlist.Table)
|
||||||
self.assertEqual(len(self.table), 0)
|
self.assertEqual(len(self.table), 0)
|
||||||
|
self.assertTrue(self.table.autodelete)
|
||||||
|
self.assertFalse(self.table.system_tracks)
|
||||||
|
|
||||||
|
def test_add_track(self):
|
||||||
|
"""Test adding a Track to an Album playlist."""
|
||||||
|
album = self.table.create("Test Album", "Artist", "2023")
|
||||||
|
medium = self.sql.media.create(album, "", number=1)
|
||||||
|
track = self.sql.tracks.create(self.library, pathlib.Path("1.ogg"),
|
||||||
|
medium, self.year)
|
||||||
|
self.assertTrue(self.table.add_track(album, track))
|
||||||
|
|
||||||
|
album2 = self.table.create("Album 2", "Artist 2", "2022")
|
||||||
|
self.assertFalse(self.table.add_track(album2, track))
|
||||||
|
|
||||||
def test_construct(self):
|
def test_construct(self):
|
||||||
"""Test constructing an album playlist."""
|
"""Test constructing an album playlist."""
|
||||||
|
@ -103,6 +119,7 @@ class TestAlbumTable(tests.util.TestCase):
|
||||||
self.assertEqual(album.mbid, "ab-cd-ef")
|
self.assertEqual(album.mbid, "ab-cd-ef")
|
||||||
self.assertEqual(album.cover, tests.util.COVER_JPG)
|
self.assertEqual(album.cover, tests.util.COVER_JPG)
|
||||||
self.assertFalse(album.active)
|
self.assertFalse(album.active)
|
||||||
|
self.assertFalse(album.tracks_movable)
|
||||||
|
|
||||||
def test_create(self):
|
def test_create(self):
|
||||||
"""Test creating an album playlist."""
|
"""Test creating an album playlist."""
|
||||||
|
@ -113,6 +130,7 @@ class TestAlbumTable(tests.util.TestCase):
|
||||||
self.assertEqual(album1.release, "2023-03")
|
self.assertEqual(album1.release, "2023-03")
|
||||||
self.assertEqual(album1.mbid, "")
|
self.assertEqual(album1.mbid, "")
|
||||||
self.assertIsNone(album1.cover)
|
self.assertIsNone(album1.cover)
|
||||||
|
self.assertEqual(album1.sort_order, "mediumno, number")
|
||||||
self.assertEqual(self.table[0], album1)
|
self.assertEqual(self.table[0], album1)
|
||||||
|
|
||||||
cur = self.sql("SELECT COUNT(name) FROM albums")
|
cur = self.sql("SELECT COUNT(name) FROM albums")
|
||||||
|
@ -197,6 +215,17 @@ class TestAlbumTable(tests.util.TestCase):
|
||||||
(("album", "2"), False, "ab-cd-ef",
|
(("album", "2"), False, "ab-cd-ef",
|
||||||
("album", "artist"), "2023-02"))
|
("album", "artist"), "2023-02"))
|
||||||
|
|
||||||
|
def test_get_trackids(self):
|
||||||
|
"""Test loading album tracks from the database."""
|
||||||
|
album = self.table.create("Test Album", "Test Artist", "2023")
|
||||||
|
medium = self.sql.media.create(album, "", number=1)
|
||||||
|
track1 = self.sql.tracks.create(self.library, pathlib.Path("1.ogg"),
|
||||||
|
medium, self.year)
|
||||||
|
track2 = self.sql.tracks.create(self.library, pathlib.Path("2.ogg"),
|
||||||
|
medium, self.year)
|
||||||
|
self.assertSetEqual(self.table.get_trackids(album),
|
||||||
|
{track1.trackid, track2.trackid})
|
||||||
|
|
||||||
def test_load(self):
|
def test_load(self):
|
||||||
"""Test loading the album table."""
|
"""Test loading the album table."""
|
||||||
self.table.create("Album 1", "Album Artist", "2023-03")
|
self.table.create("Album 1", "Album Artist", "2023-03")
|
||||||
|
@ -242,16 +271,30 @@ class TestAlbumTable(tests.util.TestCase):
|
||||||
self.assertEqual(self.table.lookup(mbid="AB-CD-EF"), album2)
|
self.assertEqual(self.table.lookup(mbid="AB-CD-EF"), album2)
|
||||||
self.assertIsNone(self.table.lookup(mbid="gh-ij-kl"))
|
self.assertIsNone(self.table.lookup(mbid="gh-ij-kl"))
|
||||||
|
|
||||||
|
def test_remove_track(self):
|
||||||
|
"""Test removing a Track from the Album."""
|
||||||
|
album = self.table.create("Test Album", "Artist", "2023")
|
||||||
|
self.assertTrue(self.table.remove_track(album, unittest.mock.Mock()))
|
||||||
|
|
||||||
def test_update(self):
|
def test_update(self):
|
||||||
"""Test updating album attributes."""
|
"""Test updating album attributes."""
|
||||||
album = self.table.create("Test Album", "Album Artist", "2023-03")
|
album = self.table.create("Test Album", "Album Artist", "2023-03")
|
||||||
album.cover = tests.util.COVER_JPG
|
album.cover = tests.util.COVER_JPG
|
||||||
album.active = True
|
album.active = True
|
||||||
|
album.loop = "Track"
|
||||||
|
album.shuffle = True
|
||||||
|
album.sort_order = "trackid"
|
||||||
|
|
||||||
row = self.sql("SELECT cover, active FROM albums_view WHERE albumid=?",
|
row = self.sql("""SELECT cover, active, loop, shuffle,
|
||||||
|
sort_order, current_trackid
|
||||||
|
FROM albums_view WHERE albumid=?""",
|
||||||
album.albumid).fetchone()
|
album.albumid).fetchone()
|
||||||
self.assertEqual(row["cover"], tests.util.COVER_JPG)
|
self.assertEqual(row["cover"], tests.util.COVER_JPG)
|
||||||
self.assertEqual(row["active"], True)
|
self.assertEqual(row["active"], True)
|
||||||
|
self.assertEqual(row["loop"], "Track")
|
||||||
|
self.assertTrue(row["shuffle"])
|
||||||
|
self.assertEqual(row["sort_order"], "trackid")
|
||||||
|
self.assertIsNone(row["current_trackid"])
|
||||||
|
|
||||||
album.cover = None
|
album.cover = None
|
||||||
row = self.sql("SELECT cover FROM albums WHERE albumid=?",
|
row = self.sql("SELECT cover FROM albums WHERE albumid=?",
|
||||||
|
|
Loading…
Reference in New Issue