db: Give Media knowledge about their Tracks & Properties

I expand on the media_view to include additional playlist properties,
and configure the default sort order to sort by track number.

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

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
This commit is contained in:
Anna Schumaker 2022-10-05 13:24:24 -04:00
parent 8e55de26d1
commit 36ca0b9818
3 changed files with 76 additions and 5 deletions

View File

@ -223,13 +223,15 @@ CREATE TABLE media (
);
CREATE VIEW media_view AS
SELECT mediumid, propertyid, albumid, number, name, type, active
SELECT mediumid, propertyid, albumid, number, name, type,
active, loop, shuffle, sort_order, current_trackid
FROM media
JOIN playlist_properties USING (propertyid);
CREATE TRIGGER media_insert_trigger AFTER INSERT ON media
BEGIN
INSERT INTO playlist_properties (active) VALUES (False);
INSERT INTO playlist_properties (active, sort_order)
VALUES (False, "mediumno, number");
UPDATE media SET propertyid = last_insert_rowid()
WHERE mediumid = NEW.mediumid;
END;
@ -536,6 +538,13 @@ CREATE VIEW album_tracks_view AS
JOIN libraries USING (libraryid)
WHERE libraries.deleting = False;
CREATE VIEW medium_tracks_view AS
SELECT tracks.trackid, media.mediumid
FROM tracks
JOIN media USING (mediumid)
JOIN libraries USING (libraryid)
WHERE libraries.deleting = False;
/****************************************************
* *

View File

@ -4,6 +4,7 @@ import sqlite3
from gi.repository import GObject
from .. import format
from . import playlist
from . import tracks
class Medium(playlist.Playlist):
@ -36,15 +37,28 @@ class Medium(playlist.Playlist):
class Table(playlist.Table):
"""Our Media Table."""
def __init__(self, sql: GObject.TYPE_PYOBJECT, **kwargs):
"""Initialize the Media Table."""
super().__init__(sql=sql, autodelete=True,
system_tracks=False, **kwargs)
def do_construct(self, **kwargs) -> Medium:
"""Construct a new medium."""
return Medium(**kwargs)
def do_add_track(self, medium: Medium, track: tracks.Track) -> bool:
"""Verify adding a Track to the Medium playlist."""
return track.get_medium() == medium
def do_get_sort_key(self, medium: Medium) -> tuple[int, int, tuple, str]:
"""Get the sort key for a medium."""
return (medium.albumid, medium.number,
format.sort_key(medium.name), medium.type)
def do_remove_track(self, medium: Medium, track: tracks.Track) -> bool:
"""Verify removing a Track from the Medium playlist."""
return True
def do_sql_delete(self, medium: Medium) -> sqlite3.Cursor:
"""Delete a medium."""
return self.sql("DELETE FROM media WHERE mediumid=?",
@ -75,6 +89,11 @@ class Table(playlist.Table):
WHERE albumid=? AND number=? AND type=?""",
album.albumid, number, type)
def do_sql_select_trackids(self, medium: Medium) -> sqlite3.Cursor:
"""Load a Medium's Tracks from the database."""
return self.sql("""SELECT trackid FROM medium_tracks_view
WHERE mediumid=?""", medium.mediumid)
def do_sql_update(self, medium: Medium,
column: str, newval) -> sqlite3.Cursor:
"""Update a medium."""

View File

@ -1,5 +1,6 @@
# Copyright 2022 (c) Anna Schumaker.
"""Tests our medium Gio.ListModel."""
import pathlib
import unittest.mock
import emmental.db
import tests.util
@ -54,10 +55,25 @@ class TestMediumsTable(tests.util.TestCase):
self.album = self.sql.albums.create("Test Album", "Test Artist", "123")
self.table = self.sql.media
self.library = self.sql.libraries.create(pathlib.Path("/a/b"))
self.year = self.sql.years.create(2023)
def test_init(self):
"""Test that the medium model is configured corretly."""
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 a Medium playlist."""
medium = self.table.create(self.album, "", number=1)
track = self.sql.tracks.create(self.library, pathlib.Path("1.ogg"),
medium, self.year)
self.assertTrue(self.table.add_track(medium, track))
medium2 = self.table.create(self.album, "", number=2)
self.assertFalse(self.table.add_track(medium2, track))
def test_construct(self):
"""Test constructing a medium playlist."""
@ -72,6 +88,7 @@ class TestMediumsTable(tests.util.TestCase):
self.assertEqual(medium.name, "Medium 2")
self.assertEqual(medium.number, 2)
self.assertEqual(medium.type, "CD")
self.assertFalse(medium.tracks_movable)
def test_create(self):
"""Test creating a medium playlist."""
@ -81,6 +98,7 @@ class TestMediumsTable(tests.util.TestCase):
self.assertEqual(medium1.name, "")
self.assertEqual(medium1.number, 1)
self.assertEqual(medium1.type, "")
self.assertEqual(medium1.sort_order, "mediumno, number")
cur = self.sql("SELECT COUNT(name) FROM media")
self.assertEqual(cur.fetchone()["COUNT(name)"], 1)
@ -117,6 +135,16 @@ class TestMediumsTable(tests.util.TestCase):
self.assertFalse(medium.delete())
def test_get_trackids(self):
"""Test loading medium tracks from the database."""
medium = self.table.create(self.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(medium),
{track1.trackid, track2.trackid})
def test_filter(self):
"""Test filtering medium playlists."""
self.table.create(self.album, "Medium 1", number=1)
@ -170,6 +198,12 @@ class TestMediumsTable(tests.util.TestCase):
self.assertIsNone(self.table.lookup(self.album, number=3,
type="Enhanced CD"))
def test_remove_track(self):
"""Test removing a Track from the Medium."""
medium = self.table.create(self.album, "Test Medium",
number=1, type="CD")
self.assertTrue(self.table.remove_track(medium, unittest.mock.Mock()))
def test_rename(self):
"""Test renaming a medium playlist."""
medium1 = self.table.create(self.album, "Medium 1",
@ -191,7 +225,16 @@ class TestMediumsTable(tests.util.TestCase):
medium = self.table.create(self.album, "Test Medium",
number=1, type="CD")
medium.active = True
medium.loop = "Track"
medium.shuffle = True
medium.sort_order = "trackid"
row = self.sql("""SELECT active FROM playlist_properties
WHERE propertyid=?""", medium.propertyid).fetchone()
self.assertEqual(row["active"], True)
row = self.sql("""SELECT active, loop, shuffle,
sort_order, current_trackid
FROM media_view WHERE mediumid=?""",
medium.mediumid).fetchone()
self.assertTrue(row["active"])
self.assertEqual(row["loop"], "Track")
self.assertTrue(row["shuffle"])
self.assertEqual(row["sort_order"], "trackid")
self.assertIsNone(row["current_trackid"])