emmental: Wire up the Next and Previous buttons
And connect to the Player EOS and about-to-finish signals so we can select the next track when the current one finishes (or slightly before for gapless playback). Implements: #7 (Add MPRIS2 Support) Implements: #48 (Implement Intelligent ReplayGain) Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
This commit is contained in:
parent
a687b564a9
commit
0d27a09233
|
@ -40,8 +40,12 @@ class Application(Adw.Application):
|
|||
flags=Gio.ApplicationFlags.HANDLES_OPEN)
|
||||
self.add_main_option_entries([options.Version])
|
||||
|
||||
def __load_file(self, file: pathlib.Path) -> None:
|
||||
def __load_file(self, file: pathlib.Path,
|
||||
*, gapless: bool = False) -> None:
|
||||
self.__stop_current_track()
|
||||
if gapless:
|
||||
self.player.file = file
|
||||
else:
|
||||
self.player.stop()
|
||||
self.player.file = file
|
||||
self.player.play()
|
||||
|
@ -51,9 +55,19 @@ class Application(Adw.Application):
|
|||
self.__load_track(track)
|
||||
self.__load_file(path)
|
||||
|
||||
def __load_track(self, track: GObject.GObject) -> None:
|
||||
self.__load_file(track.path)
|
||||
def __load_track(self, track: GObject.GObject, *, gapless: bool = False,
|
||||
rg_auto: str = "track", restart: bool = False) -> None:
|
||||
self.__load_file(track.path, gapless=gapless)
|
||||
if restart:
|
||||
track.restart()
|
||||
else:
|
||||
track.start()
|
||||
self.__set_replaygain(rg_auto=rg_auto)
|
||||
|
||||
def __pick_next_track(self, *, user: bool, gapless: bool = False) -> None:
|
||||
(track, rg_auto, restart) = self.factory.next_track(user=user)
|
||||
self.__load_track(track, gapless=gapless,
|
||||
rg_auto=rg_auto, restart=restart)
|
||||
|
||||
def __on_seek(self, nowplay: nowplaying.Card, newpos: float) -> None:
|
||||
"""Handle a seek event."""
|
||||
|
@ -67,16 +81,29 @@ class Application(Adw.Application):
|
|||
trackid: str, position: float) -> None:
|
||||
self.player.seek(position)
|
||||
|
||||
def __set_replaygain(self, *args) -> None:
|
||||
def __set_replaygain(self, *args, rg_auto="track") -> None:
|
||||
enabled = self.db.settings["audio.replaygain.enabled"]
|
||||
mode = self.db.settings["audio.replaygain.mode"]
|
||||
mode = "track" if mode == "auto" else mode
|
||||
mode = rg_auto if mode == "auto" else mode
|
||||
self.player.set_replaygain(enabled, mode)
|
||||
|
||||
def __stop_current_track(self) -> None:
|
||||
if self.db.tracks.current_track is not None:
|
||||
self.db.tracks.current_track.stop(self.player.playtime)
|
||||
|
||||
def __system_next(self, player: audio.Player, gapless: bool) -> None:
|
||||
self.player.pause_on_load = self.autopause == 0
|
||||
if self.autopause >= 0:
|
||||
self.autopause -= 1
|
||||
self.__pick_next_track(user=False, gapless=gapless)
|
||||
|
||||
def __user_next(self, *args) -> None:
|
||||
self.__pick_next_track(user=True)
|
||||
|
||||
def __user_previous(self, *args) -> None:
|
||||
self.__load_track(self.factory.previous_track(),
|
||||
rg_auto="track", restart=True)
|
||||
|
||||
def __tracks_table_loaded(self, track_table, param) -> None:
|
||||
if track_table.current_track is not None:
|
||||
self.player.file = track_table.current_track.path
|
||||
|
@ -110,12 +137,16 @@ class Application(Adw.Application):
|
|||
playing, "have-db-track")
|
||||
self.db.tracks.bind_property("current-favorite", playing, "favorite",
|
||||
GObject.BindingFlags.BIDIRECTIONAL)
|
||||
self.factory.bind_property("can-go-next", playing, "have-next")
|
||||
self.factory.bind_property("can-go-previous", playing, "have-previous")
|
||||
self.db.settings.bind_setting("now-playing.prefer-artist",
|
||||
playing, "prefer-artist")
|
||||
|
||||
playing.connect("play", self.player.play)
|
||||
playing.connect("pause", self.player.pause)
|
||||
playing.connect("seek", self.__on_seek)
|
||||
playing.connect("next", self.__user_next)
|
||||
playing.connect("previous", self.__user_previous)
|
||||
return playing
|
||||
|
||||
def build_sidebar(self) -> sidebar.Card:
|
||||
|
@ -159,8 +190,14 @@ class Application(Adw.Application):
|
|||
("active-loop", "LoopStatus")]:
|
||||
self.factory.bind_property(prop, self.mpris.player, mpris_prop,
|
||||
GObject.BindingFlags.BIDIRECTIONAL)
|
||||
for (prop, mpris_prop) in [("can-go-next", "CanGoNext"),
|
||||
("can-go-previous", "CanGoPrevious")]:
|
||||
self.factory.bind_property(prop, self.mpris.player, mpris_prop)
|
||||
self.mpris.player.link_property("Volume", self.win.header, "volume")
|
||||
|
||||
self.mpris.player.connect("OpenPath", self.__load_path)
|
||||
self.mpris.player.connect("Next", self.__user_next)
|
||||
self.mpris.player.connect("Previous", self.__user_previous)
|
||||
self.mpris.player.connect("Pause", self.player.pause)
|
||||
self.mpris.player.connect("Play", self.player.play)
|
||||
self.mpris.player.connect("PlayPause", self.player.play_pause)
|
||||
|
@ -175,6 +212,11 @@ class Application(Adw.Application):
|
|||
self.win.sidebar.bind_property("selected-playlist",
|
||||
self.factory, "db-visible")
|
||||
|
||||
def connect_player(self) -> None:
|
||||
"""Connect the audio.Player."""
|
||||
self.player.connect("about-to-finish", self.__system_next, True)
|
||||
self.player.connect("eos", self.__system_next, False)
|
||||
|
||||
def do_handle_local_options(self, opts: GLib.VariantDict) -> int:
|
||||
"""Handle any command line options."""
|
||||
if opts.contains("version"):
|
||||
|
@ -201,6 +243,7 @@ class Application(Adw.Application):
|
|||
self.add_window(self.win)
|
||||
self.connect_mpris2()
|
||||
self.connect_playlist_factory()
|
||||
self.connect_player()
|
||||
|
||||
def do_activate(self) -> None:
|
||||
"""Handle the Adw.Application::activate signal."""
|
||||
|
|
|
@ -100,6 +100,8 @@ class TestEmmental(unittest.TestCase):
|
|||
def test_window_widgets(self, mock_stdout: io.StringIO):
|
||||
"""Check that the window widgets are added properly."""
|
||||
self.application.db = emmental.db.Connection()
|
||||
self.application.factory = emmental.playlist.Factory(
|
||||
self.application.db)
|
||||
self.application.player = emmental.audio.Player()
|
||||
win = self.application.build_window()
|
||||
|
||||
|
@ -118,6 +120,8 @@ class TestEmmental(unittest.TestCase):
|
|||
pause_func: unittest.mock.Mock):
|
||||
"""Check that the nowplaying widget is wired up properly."""
|
||||
self.application.db = emmental.db.Connection()
|
||||
self.application.factory = emmental.playlist.Factory(
|
||||
self.application.db)
|
||||
self.application.player = emmental.audio.Player()
|
||||
win = self.application.build_window()
|
||||
|
||||
|
@ -131,6 +135,11 @@ class TestEmmental(unittest.TestCase):
|
|||
self.application.db.tracks.have_current_track = True
|
||||
self.assertTrue(win.now_playing.have_db_track)
|
||||
|
||||
self.application.factory.can_go_next = True
|
||||
self.assertTrue(win.now_playing.have_next)
|
||||
self.application.factory.can_go_previous = True
|
||||
self.assertTrue(win.now_playing.have_previous)
|
||||
|
||||
win.now_playing.emit("play")
|
||||
play_func.assert_called()
|
||||
win.now_playing.emit("pause")
|
||||
|
@ -152,6 +161,8 @@ class TestEmmental(unittest.TestCase):
|
|||
def test_sidebar(self, mock_stdout: io.StringIO):
|
||||
"""Check that the sidebar widget is wired up properly."""
|
||||
self.application.db = emmental.db.Connection()
|
||||
self.application.factory = emmental.playlist.Factory(
|
||||
self.application.db)
|
||||
self.application.player = emmental.audio.Player()
|
||||
win = self.application.build_window()
|
||||
|
||||
|
@ -180,6 +191,8 @@ class TestEmmental(unittest.TestCase):
|
|||
def test_replaygain(self, mock_stdout: io.StringIO):
|
||||
"""Test setting replaygain modes."""
|
||||
self.application.db = emmental.db.Connection()
|
||||
self.application.factory = emmental.playlist.Factory(
|
||||
self.application.db)
|
||||
self.application.player = emmental.audio.Player()
|
||||
win = self.application.build_window()
|
||||
player = self.application.player
|
||||
|
|
Loading…
Reference in New Issue