playlist: Create a Playlist Factory object

The playlist factory has properties for the currently selected,
currently active, and previous playlists. It will eventually create
Gio.ListModel instances representing the tracks in each of these playlists,
in sorted order.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
This commit is contained in:
Anna Schumaker 2023-04-15 13:31:15 -04:00
parent 7d26d89405
commit fdfc12fbd2
5 changed files with 125 additions and 0 deletions

View File

@ -9,6 +9,7 @@ from . import header
from . import mpris2
from . import nowplaying
from . import options
from . import playlist
from . import sidebar
from . import window
from gi.repository import GObject
@ -25,6 +26,7 @@ class Application(Adw.Application):
"""Our custom Adw.Application."""
db = GObject.Property(type=db.Connection)
factory = GObject.Property(type=playlist.Factory)
mpris = GObject.Property(type=mpris2.Connection)
player = GObject.Property(type=audio.Player)
win = GObject.Property(type=window.Window)
@ -162,6 +164,13 @@ class Application(Adw.Application):
self.mpris.player.connect("SetPosition", self.__set_position)
self.mpris.player.connect("Stop", self.player.stop)
def connect_playlist_factory(self) -> None:
"""Connect the playlist factory properties."""
self.db.playlists.bind_property("previous",
self.factory, "db-previous")
self.win.sidebar.bind_property("selected-playlist",
self.factory, "db-visible")
def do_handle_local_options(self, opts: GLib.VariantDict) -> int:
"""Handle any command line options."""
if opts.contains("version"):
@ -175,6 +184,7 @@ class Application(Adw.Application):
Adw.Application.do_startup(self)
self.db = db.Connection()
self.mpris = mpris2.Connection()
self.factory = playlist.Factory(self.db)
self.player = audio.Player()
gsetup.add_style()
@ -186,6 +196,7 @@ class Application(Adw.Application):
self.win = self.build_window()
self.add_window(self.win)
self.connect_mpris2()
self.connect_playlist_factory()
def do_activate(self) -> None:
"""Handle the Adw.Application::activate signal."""

View File

@ -0,0 +1,27 @@
# Copyright 2023 (c) Anna Schumaker.
"""An object for managing the visible and active playlists."""
from gi.repository import GObject
from .. import db
class Factory(GObject.GObject):
"""Our playlist model factory."""
sql = GObject.Property(type=db.Connection)
db_active = GObject.Property(type=db.playlist.Playlist)
db_previous = GObject.Property(type=db.playlist.Playlist)
db_visible = GObject.Property(type=db.playlist.Playlist)
def __init__(self, sql: db.Connection):
"""Initialize the Playlist Factory."""
super().__init__(sql=sql)
self.sql.bind_property("active-playlist", self, "db-active")
self.connect("notify", self.__notify_db_playlists)
def __notify_db_playlists(self, factory: GObject.GObject, param) -> None:
match param.name:
case "db-active" | "db-previous" | "db-visible":
plist = self.get_property(param.name)
name = "<None>" if plist is None else plist.name
print(f"factory: {param.name[3:]} playlist is:", name)

View File

@ -0,0 +1,2 @@
# Copyright 2023 (c) Anna Schumaker.
"""Needed to fix the unique basename problem with pytest."""

View File

@ -0,0 +1,63 @@
# Copyright 2023 (c) Anna Schumaker.
"""Test our Playlist Manager object."""
import io
import unittest.mock
import emmental.playlist
import tests.util
from gi.repository import GObject
@unittest.mock.patch("sys.stdout", new_callable=io.StringIO)
class TestFactory(tests.util.TestCase):
"""Test the Playlist Factory class."""
def setUp(self):
"""Set up common variables."""
super().setUp()
self.sql.playlists.load(now=True)
self.user_plist = self.sql.playlists.create("User Playlist")
self.factory = emmental.playlist.Factory(self.sql)
def test_init(self, mock_stdout: io.StringIO):
"""Check that the Playlist Factory is set up properly."""
self.assertIsInstance(self.factory, GObject.GObject)
self.assertEqual(self.factory.sql, self.sql)
def test_active(self, mock_stdout: io.StringIO):
"""Test the active playlist property."""
self.assertIsNone(self.factory.db_active)
self.sql.set_active_playlist(self.user_plist)
self.assertEqual(self.factory.db_active, self.user_plist)
self.assertRegex(mock_stdout.getvalue(),
"factory: active playlist is: User Playlist")
self.sql.set_active_playlist(None)
self.assertIsNone(self.factory.db_active)
self.assertRegex(mock_stdout.getvalue(), "active playlist is: <None>")
def test_previous(self, mock_stdout: io.StringIO):
"""Test the previous playlist property."""
self.assertIsNone(self.factory.db_previous)
self.factory.db_previous = self.sql.playlists.previous
self.assertEqual(self.factory.db_previous, self.sql.playlists.previous)
self.assertRegex(mock_stdout.getvalue(),
r"factory: previous playlist is: Previous Tracks")
self.factory.db_previous = None
self.assertRegex(mock_stdout.getvalue(),
"factory: previous playlist is: <None>")
def test_visible(self, mock_stdout: io.StringIO):
"""Test the visible playlist property."""
self.assertIsNone(self.factory.db_visible)
self.factory.db_visible = self.user_plist
self.assertEqual(self.factory.db_visible, self.user_plist)
self.assertRegex(mock_stdout.getvalue(),
"factory: visible playlist is: User Playlist")
self.factory.db_visible = None
self.assertRegex(mock_stdout.getvalue(),
"factory: visible playlist is: <None>")

View File

@ -45,6 +45,7 @@ class TestEmmental(unittest.TestCase):
"""Test that the startup signal works as expected."""
self.assertIsNone(self.application.db)
self.assertIsNone(self.application.mpris)
self.assertIsNone(self.application.factory)
self.assertIsNone(self.application.player)
self.assertIsNone(self.application.win)
@ -53,6 +54,8 @@ class TestEmmental(unittest.TestCase):
self.assertIsInstance(self.application.mpris,
emmental.mpris2.Connection)
self.assertIsInstance(self.application.player, emmental.audio.Player)
self.assertIsInstance(self.application.factory,
emmental.playlist.Factory)
self.assertIsInstance(self.application.win, emmental.window.Window)
mock_startup.assert_called()
@ -154,6 +157,25 @@ class TestEmmental(unittest.TestCase):
self.assertEqual(win.sidebar.sql, self.application.db)
@unittest.mock.patch("sys.stdout", new_callable=io.StringIO)
def test_playlist_factory(self, mock_stdout: io.StringIO):
"""Test that the Playlist Factory is wired up properly."""
self.application.db = emmental.db.Connection()
self.application.player = emmental.audio.Player()
self.application.factory = emmental.playlist.Factory(
self.application.db)
self.application.win = self.application.build_window()
self.application.connect_playlist_factory()
self.application.db.playlists.load(now=True)
playlist = self.application.db.playlists.create("Test Playlist")
self.application.win.sidebar.selected_playlist = playlist
self.assertEqual(self.application.factory.db_visible, playlist)
self.assertEqual(self.application.factory.db_previous,
self.application.db.playlists.previous)
@unittest.mock.patch("sys.stdout", new_callable=io.StringIO)
def test_replaygain(self, mock_stdout: io.StringIO):
"""Test setting replaygain modes."""