db: Give Libraries knowledge about their Tracks & Properties

I expand on the libraries_view to include additional playlist
properties, and configure the default sort order to sort by filepath.

I then set up the library_tracks_view to make it easy to select tracks
that belong to a specific Library.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
This commit is contained in:
Anna Schumaker 2022-10-03 16:43:36 -04:00
parent e39d128488
commit 7920b3d5a8
3 changed files with 71 additions and 4 deletions

View File

@ -386,13 +386,15 @@ CREATE TABLE libraries (
);
CREATE VIEW libraries_view AS
SELECT libraryid, propertyid, path, path as name, enabled, active
SELECT libraryid, propertyid, path, path as name, enabled,
active, loop, shuffle, sort_order, current_trackid
FROM libraries
JOIN playlist_properties USING (propertyid);
CREATE TRIGGER libraries_insert_trigger AFTER INSERT ON libraries
BEGIN
INSERT INTO playlist_properties (active) VALUES (False);
INSERT INTO playlist_properties (active, sort_order)
VALUES (False, "filepath");
UPDATE libraries SET propertyid = last_insert_rowid()
WHERE libraryid = NEW.libraryid;
END;
@ -573,6 +575,12 @@ CREATE VIEW year_tracks_view AS
JOIN libraries USING (libraryid)
WHERE libraries.deleting = False;
CREATE VIEW library_tracks_view AS
SELECT tracks.trackid, libraries.libraryid
FROM tracks
JOIN libraries USING (libraryid)
WHERE libraries.deleting = False;
/****************************************************
* *

View File

@ -7,6 +7,7 @@ from .. import path
from . import idle
from . import playlist
from . import tagger
from . import tracks
class Library(playlist.Playlist):
@ -100,10 +101,22 @@ class Library(playlist.Playlist):
class Table(playlist.Table):
"""Our Library ListModel."""
def __init__(self, sql: GObject.TYPE_PYOBJECT, **kwargs):
"""Initialize the Libraries Table."""
super().__init__(sql=sql, system_tracks=False, **kwargs)
def do_add_track(self, library: Library, track: tracks.Track) -> bool:
"""Verify adding a Track to a Library playlist."""
return track.get_library() == library
def do_construct(self, **kwargs) -> Library:
"""Construct a new library."""
return Library(**kwargs)
def do_remove_track(self, library: Library, track: tracks.Track) -> bool:
"""Verify removing a Track from a Library playlist."""
return True
def do_sql_delete(self, library: Library) -> sqlite3.Cursor:
"""Delete a library."""
return self.sql("DELETE FROM libraries WHERE libraryid=?",
@ -128,6 +141,11 @@ class Table(playlist.Table):
"""Look up a library by path."""
return self.sql("SELECT libraryid FROM libraries WHERE path=?", path)
def do_sql_select_trackids(self, library: Library) -> sqlite3.Cursor:
"""Load a Library's Tracks from the database."""
return self.sql("""SELECT trackid FROM library_tracks_view
WHERE libraryid=?""", library.libraryid)
def do_sql_update(self, library: Library, column: str, newval) -> bool:
"""Update a Library playlist."""
if column == "enabled" and self.sql.playlists.collection:

View File

@ -185,10 +185,25 @@ class TestLibraryTable(tests.util.TestCase):
tests.util.TestCase.setUp(self)
self.table = self.sql.libraries
self.album = self.sql.albums.create("Test Album", "Test Artist", "123")
self.medium = self.sql.media.create(self.album, "", number=1)
self.year = self.sql.years.create(2023)
def test_init(self):
"""Test that the library model is configured correctly."""
self.assertIsInstance(self.table, emmental.db.playlist.Table)
self.assertEqual(len(self.table), 0)
self.assertFalse(self.table.autodelete)
self.assertFalse(self.table.system_tracks)
def test_add_track(self):
"""Test adding a Track to a Library playlist."""
library = self.table.create(pathlib.Path("/a/b"))
track = self.sql.tracks.create(library, pathlib.Path("/a/b/c.ogg"),
self.medium, self.year)
self.assertTrue(self.table.add_track(library, track))
library2 = self.table.create(pathlib.Path("/a/d"))
self.assertFalse(self.table.add_track(library2, track))
def test_construct(self):
"""Test constructing a new library."""
@ -202,12 +217,14 @@ class TestLibraryTable(tests.util.TestCase):
self.assertEqual(library.path, pathlib.Path("/a/b/c"))
self.assertEqual(library.name, "/a/b/c")
self.assertFalse(library.active)
self.assertFalse(library.tracks_movable)
def test_create(self):
"""Test creating a library."""
library = self.table.create(pathlib.Path("/a/b/c"))
self.assertIsInstance(library, emmental.db.libraries.Library)
self.assertEqual(library.path, pathlib.Path("/a/b/c"))
self.assertEqual(library.sort_order, "filepath")
cur = self.sql("SELECT COUNT(path) FROM libraries")
self.assertEqual(cur.fetchone()["COUNT(path)"], 1)
@ -247,6 +264,16 @@ class TestLibraryTable(tests.util.TestCase):
self.table.filter("*a/b*", now=True)
self.assertSetEqual(self.table.get_filter().keys, {1, 2})
def test_get_trackids(self):
"""Test loading library tracks from the database."""
library = self.table.create("/a/b/")
track1 = self.sql.tracks.create(library, pathlib.Path("/a/b/1.ogg"),
self.medium, self.year)
track2 = self.sql.tracks.create(library, pathlib.Path("/a/b/2.ogg"),
self.medium, self.year)
self.assertSetEqual(self.table.get_trackids(library),
{track1.trackid, track2.trackid})
def test_load(self):
"""Test loading libraries from the database."""
self.table.create("/a/b/c")
@ -272,6 +299,11 @@ class TestLibraryTable(tests.util.TestCase):
self.assertEqual(self.table.lookup(pathlib.Path("/a/b/c/")), library)
self.assertIsNone(self.table.lookup(pathlib.Path("/no/library/path")))
def test_remove_track(self):
"""Test removing a Track from the Library."""
library = self.table.create("/a/b/")
self.assertTrue(self.table.remove_track(library, unittest.mock.Mock()))
def test_stop(self):
"""Test stopping the library table."""
library = self.table.create(pathlib.Path("/a/b/c"))
@ -286,11 +318,20 @@ class TestLibraryTable(tests.util.TestCase):
library = self.table.create("/a/b/c")
library.active = True
library.enabled = False
library.loop = "Track"
library.shuffle = True
library.sort_order = "trackid"
row = self.sql("""SELECT active, enabled FROM libraries_view
WHERE libraryid=?""", library.libraryid).fetchone()
row = self.sql("""SELECT active, enabled, loop, shuffle,
sort_order, current_trackid
FROM libraries_view WHERE libraryid=?""",
library.libraryid).fetchone()
self.assertTrue(row["active"])
self.assertFalse(row["enabled"])
self.assertEqual(row["loop"], "Track")
self.assertTrue(row["shuffle"])
self.assertEqual(row["sort_order"], "trackid")
self.assertIsNone(row["current_trackid"])
def test_library_online(self):
"""Test the library-online signal."""