From dbc60e1c5ffc2fae3421ae25deedef00043b4672 Mon Sep 17 00:00:00 2001 From: Anna Schumaker Date: Wed, 7 Feb 2024 11:32:13 -0500 Subject: [PATCH] emmental: Add the ListenBrainz GObject to the Application And wire it up. Signed-off-by: Anna Schumaker --- emmental/__init__.py | 21 ++++++++++++- emmental/listenbrainz/__init__.py | 2 +- tests/test_emmental.py | 51 +++++++++++++++++++++++++++++++ tests/test_settings.py | 12 ++++++++ 4 files changed, 84 insertions(+), 2 deletions(-) diff --git a/emmental/__init__.py b/emmental/__init__.py index 51adbba..e8756a8 100644 --- a/emmental/__init__.py +++ b/emmental/__init__.py @@ -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 diff --git a/emmental/listenbrainz/__init__.py b/emmental/listenbrainz/__init__.py index f8113f9..166a21e 100644 --- a/emmental/listenbrainz/__init__.py +++ b/emmental/listenbrainz/__init__.py @@ -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") diff --git a/tests/test_emmental.py b/tests/test_emmental.py index 8d178c7..f22cd96 100644 --- a/tests/test_emmental.py +++ b/tests/test_emmental.py @@ -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", "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", ["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", "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 diff --git a/tests/test_settings.py b/tests/test_settings.py index 6c710e5..88c1585 100644 --- a/tests/test_settings.py +++ b/tests/test_settings.py @@ -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)