diff --git a/emmental/db/albums.py b/emmental/db/albums.py index 24ac5a7..2f1da70 100644 --- a/emmental/db/albums.py +++ b/emmental/db/albums.py @@ -7,6 +7,7 @@ from gi.repository import Gtk from .media import Medium from .. import format from . import playlist +from . import tracks class Album(playlist.Playlist): @@ -50,6 +51,15 @@ class Album(playlist.Playlist): class Table(playlist.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: """Construct a new album.""" return Album(**kwargs) @@ -62,6 +72,10 @@ class Table(playlist.Table): format.sort_key(album.artist), 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: """Delete an album.""" for artist in album.get_artists(): @@ -106,6 +120,11 @@ class Table(playlist.Table): return self.sql(f"""SELECT albumid FROM albums 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: """Rename an album.""" return self.sql(f"UPDATE albums SET {column}=? WHERE albumid=?", diff --git a/emmental/db/emmental.sql b/emmental/db/emmental.sql index ed139bc..7f3fe0c 100644 --- a/emmental/db/emmental.sql +++ b/emmental/db/emmental.sql @@ -181,14 +181,16 @@ CREATE TABLE albums ( ); 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 JOIN playlist_properties USING (propertyid); CREATE TRIGGER albums_insert_trigger AFTER INSERT ON albums 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(), mbid = LOWER(NEW.mbid) WHERE albumid = NEW.albumid; @@ -526,6 +528,14 @@ CREATE VIEW artist_tracks_view AS JOIN libraries USING (libraryid) 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; + /**************************************************** * * diff --git a/tests/db/test_albums.py b/tests/db/test_albums.py index dd35847..d89388e 100644 --- a/tests/db/test_albums.py +++ b/tests/db/test_albums.py @@ -82,10 +82,26 @@ class TestAlbumTable(tests.util.TestCase): tests.util.TestCase.setUp(self) 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): """Test that the album model is configured correctly.""" self.assertIsInstance(self.table, emmental.db.playlist.Table) 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): """Test constructing an album playlist.""" @@ -103,6 +119,7 @@ class TestAlbumTable(tests.util.TestCase): self.assertEqual(album.mbid, "ab-cd-ef") self.assertEqual(album.cover, tests.util.COVER_JPG) self.assertFalse(album.active) + self.assertFalse(album.tracks_movable) def test_create(self): """Test creating an album playlist.""" @@ -113,6 +130,7 @@ class TestAlbumTable(tests.util.TestCase): self.assertEqual(album1.release, "2023-03") self.assertEqual(album1.mbid, "") self.assertIsNone(album1.cover) + self.assertEqual(album1.sort_order, "mediumno, number") self.assertEqual(self.table[0], album1) cur = self.sql("SELECT COUNT(name) FROM albums") @@ -197,6 +215,17 @@ class TestAlbumTable(tests.util.TestCase): (("album", "2"), False, "ab-cd-ef", ("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): """Test loading the album table.""" 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.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): """Test updating album attributes.""" album = self.table.create("Test Album", "Album Artist", "2023-03") album.cover = tests.util.COVER_JPG 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() self.assertEqual(row["cover"], tests.util.COVER_JPG) 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 row = self.sql("SELECT cover FROM albums WHERE albumid=?",