emmental: Add the ListenBrainz GObject to the Application

And wire it up.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
This commit is contained in:
Anna Schumaker 2024-02-07 11:32:13 -05:00
parent 6779535cf1
commit dbc60e1c5f
4 changed files with 84 additions and 2 deletions

View File

@ -7,6 +7,7 @@ from . import action
from . import audio
from . import db
from . import header
from . import listenbrainz
from . import mpris2
from . import nowplaying
from . import options
@ -34,6 +35,7 @@ class Application(Adw.Application):
factory = GObject.Property(type=playlist.Factory)
mpris = GObject.Property(type=mpris2.Connection)
player = GObject.Property(type=audio.Player)
lbrainz = GObject.Property(type=listenbrainz.ListenBrainz)
win = GObject.Property(type=window.Window)
autopause = GObject.Property(type=int, default=-1, minimum=-1, maximum=99)
@ -135,11 +137,14 @@ class Application(Adw.Application):
hdr = header.Header(sql=self.db, title=VERSION_STRING)
for prop in ["bg-enabled", "bg-volume", "volume"]:
hdr.bind_property(prop, self.player, prop)
hdr.bind_property("listenbrainz-token", self.lbrainz, "user-token")
for (setting, property) in [("audio.volume", "volume"),
("audio.background.enabled", "bg-enabled"),
("audio.background.volume", "bg-volume"),
("audio.replaygain.enabled", "rg-enabled"),
("audio.replaygain.mode", "rg-mode")]:
("audio.replaygain.mode", "rg-mode"),
("listenbrainz.token",
"listenbrainz_token")]:
self.db.settings.bind_setting(setting, hdr, property)
self.__add_accelerators(hdr.accelerators)
@ -253,6 +258,15 @@ class Application(Adw.Application):
self.mpris.player.connect("SetPosition", self.__set_position)
self.mpris.player.connect("Stop", self.player.stop)
def connect_listenbrainz(self) -> None:
"""Connect the listenbrainz client."""
self.db.tracks.bind_property("current-track",
self.lbrainz, "now-playing")
self.lbrainz.bind_property("valid-token", self.win.header,
"listenbrainz-token-valid")
self.db.tracks.connect("track-played", self.lbrainz.submit_listens)
def connect_playlist_factory(self) -> None:
"""Connect the playlist factory properties."""
self.db.playlists.bind_property("previous",
@ -279,6 +293,7 @@ class Application(Adw.Application):
Adw.Application.do_startup(self)
self.db = db.Connection()
self.mpris = mpris2.Connection()
self.lbrainz = listenbrainz.ListenBrainz(self.db)
self.factory = playlist.Factory(self.db)
self.player = audio.Player()
@ -291,6 +306,7 @@ class Application(Adw.Application):
self.win = self.build_window()
self.add_window(self.win)
self.connect_mpris2()
self.connect_listenbrainz()
self.connect_playlist_factory()
self.connect_player()
@ -316,6 +332,9 @@ class Application(Adw.Application):
if self.win is not None:
self.win.close()
self.win = None
if self.lbrainz is not None:
self.lbrainz.stop()
self.lbrainz = None
if self.mpris is not None:
self.mpris.disconnect()
self.mpris = None

View File

@ -81,7 +81,7 @@ class ListenBrainz(GObject.GObject):
elif not self.offline and self._timeout_id is not None:
self.__source_stop("_timeout_id")
def __notify_user_token(self, scrobbler: GObject.GObject,
def __notify_user_token(self, listenbrainz: GObject.GObject,
param: GObject.ParamSpec) -> None:
match self.user_token:
case "": self._queue.push("clear-token")

View File

@ -49,12 +49,15 @@ class TestEmmental(unittest.TestCase):
self.assertIsNone(self.application.mpris)
self.assertIsNone(self.application.factory)
self.assertIsNone(self.application.player)
self.assertIsNone(self.application.lbrainz)
self.assertIsNone(self.application.win)
self.application.emit("startup")
self.assertIsInstance(self.application.db, emmental.db.Connection)
self.assertIsInstance(self.application.mpris,
emmental.mpris2.Connection)
self.assertIsInstance(self.application.lbrainz,
emmental.listenbrainz.ListenBrainz)
self.assertIsInstance(self.application.player, emmental.audio.Player)
self.assertIsInstance(self.application.factory,
emmental.playlist.Factory)
@ -84,12 +87,16 @@ class TestEmmental(unittest.TestCase):
"""Test that the shutdown signal works as expected."""
db = self.application.db = emmental.db.Connection()
mpris = self.application.mpris = emmental.mpris2.Connection()
lbrainz = self.application.lbrainz = \
emmental.listenbrainz.ListenBrainz(
self.application.db)
self.application.win = emmental.window.Window("Test 1.2.3")
player = self.application.player = emmental.audio.Player()
self.application.emit("shutdown")
self.assertIsNone(self.application.db)
self.assertIsNone(self.application.mpris)
self.assertIsNone(self.application.lbrainz)
self.assertIsNone(self.application.player)
self.assertIsNone(self.application.win)
@ -97,6 +104,7 @@ class TestEmmental(unittest.TestCase):
self.assertFalse(db.connected)
self.assertEqual(player.get_state(), gi.repository.Gst.State.NULL)
mock_close.assert_called()
self.assertFalse(lbrainz._thread.is_alive())
@unittest.mock.patch("sys.stdout", new_callable=io.StringIO)
def test_window_widgets(self, mock_stdout: io.StringIO):
@ -105,6 +113,8 @@ class TestEmmental(unittest.TestCase):
self.application.factory = emmental.playlist.Factory(
self.application.db)
self.application.player = emmental.audio.Player()
self.application.lbrainz = emmental.listenbrainz.ListenBrainz(
self.application.db)
win = self.application.build_window()
self.assertIsInstance(win, emmental.window.Window)
@ -125,6 +135,8 @@ class TestEmmental(unittest.TestCase):
self.application.factory = emmental.playlist.Factory(
self.application.db)
self.application.player = emmental.audio.Player()
self.application.lbrainz = emmental.listenbrainz.ListenBrainz(
self.application.db)
self.application.build_window()
for action, accel in [("app.open-file", "<Control>o"),
@ -147,6 +159,8 @@ class TestEmmental(unittest.TestCase):
self.application.factory = emmental.playlist.Factory(
self.application.db)
self.application.player = emmental.audio.Player()
self.application.lbrainz = emmental.listenbrainz.ListenBrainz(
self.application.db)
win = self.application.build_window()
for action, accel in [("app.toggle-favorite", ["<Control>f"]),
@ -204,6 +218,8 @@ class TestEmmental(unittest.TestCase):
self.application.factory = emmental.playlist.Factory(
self.application.db)
self.application.player = emmental.audio.Player()
self.application.lbrainz = emmental.listenbrainz.ListenBrainz(
self.application.db)
win = self.application.build_window()
for action, accel in [("app.focus-search-playlist",
@ -226,6 +242,8 @@ class TestEmmental(unittest.TestCase):
self.application.factory = emmental.playlist.Factory(
self.application.db)
self.application.player = emmental.audio.Player()
self.application.lbrainz = emmental.listenbrainz.ListenBrainz(
self.application.db)
win = self.application.build_window()
for action, accel in [("app.focus-search-track", "<Control>slash"),
@ -250,6 +268,8 @@ class TestEmmental(unittest.TestCase):
"""Test that the Playlist Factory is wired up properly."""
self.application.db = emmental.db.Connection()
self.application.player = emmental.audio.Player()
self.application.lbrainz = emmental.listenbrainz.ListenBrainz(
self.application.db)
self.application.factory = emmental.playlist.Factory(
self.application.db)
self.application.win = self.application.build_window()
@ -264,6 +284,31 @@ class TestEmmental(unittest.TestCase):
self.assertEqual(self.application.factory.db_previous,
self.application.db.playlists.previous)
def test_listenbrainz(self):
"""Test that listenbrainz is wired up properly."""
self.application.db = emmental.db.Connection()
self.application.player = emmental.audio.Player()
self.application.lbrainz = emmental.listenbrainz.ListenBrainz(
self.application.db)
self.application.factory = emmental.playlist.Factory(
self.application.db)
self.application.win = self.application.build_window()
with unittest.mock.patch.object(self.application.lbrainz,
"submit_listens") as mock_submit:
self.application.connect_listenbrainz()
self.application.db.tracks.emit("track-played", None)
mock_submit.assert_called()
self.application.lbrainz.stop()
self.application.win.header.listenbrainz_token = "abcde"
self.assertEqual(self.application.lbrainz.user_token, "abcde")
self.application.lbrainz.valid_token = False
self.assertFalse(self.application.win.header.listenbrainz_token_valid)
@unittest.mock.patch("sys.stdout", new_callable=io.StringIO)
def test_replaygain(self, mock_stdout: io.StringIO):
"""Test setting replaygain modes."""
@ -271,6 +316,8 @@ class TestEmmental(unittest.TestCase):
self.application.factory = emmental.playlist.Factory(
self.application.db)
self.application.player = emmental.audio.Player()
self.application.lbrainz = emmental.listenbrainz.ListenBrainz(
self.application.db)
win = self.application.build_window()
player = self.application.player
@ -286,6 +333,8 @@ class TestEmmental(unittest.TestCase):
self.application.factory = emmental.playlist.Factory(
self.application.db)
self.application.player = emmental.audio.Player()
self.application.lbrainz = emmental.listenbrainz.ListenBrainz(
self.application.db)
win = self.application.build_window()
player = self.application.player
@ -301,6 +350,8 @@ class TestEmmental(unittest.TestCase):
self.application.factory = emmental.playlist.Factory(
self.application.db)
self.application.player = emmental.audio.Player()
self.application.lbrainz = emmental.listenbrainz.ListenBrainz(
self.application.db)
win = self.application.build_window()
win.show_sidebar = True

View File

@ -21,11 +21,23 @@ class TestSettings(unittest.TestCase):
self.settings = self.app.db.settings
self.win = self.app.win
self.player = self.app.player
self.lbrainz = self.app.lbrainz
def tearDown(self):
"""Clean up."""
self.app.do_shutdown()
def test_save_listenbrainz_token(self, new_callable=io.StringIO):
"""Check saving and loading the listenbrainz token."""
self.assertEqual(self.settings["listenbrainz.token"], "")
self.assertEqual(self.win.header.listenbrainz_token, "")
self.win.header.listenbrainz_token = "abcde"
self.assertEqual(self.settings["listenbrainz.token"], "abcde")
win = self.app.build_window()
self.assertEqual(win.header.listenbrainz_token, "abcde")
def test_save_window_size(self, new_callable=io.StringIO):
"""Check saving and loading window size from the database."""
self.assertEqual(self.settings["window.width"], 1600)