audio: Add a Player class

I provide a way to load, play, and pause tracks. I also add functions
for picking the next and previous tracks from the tagdb.Stack

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
This commit is contained in:
Anna Schumaker 2021-06-30 16:10:37 -04:00
parent d834ce3a34
commit 0fde1f58e2
4 changed files with 126 additions and 0 deletions

4
audio/__init__.py Normal file
View File

@ -0,0 +1,4 @@
# Copyright 2021 (c) Anna Schumaker.
from gi.repository import Gst
import sys
Gst.init(sys.argv)

51
audio/player.py Normal file
View File

@ -0,0 +1,51 @@
# Copyright 2021 (c) Anna Schumaker.
from gi.repository import Gst
import trackdb
class Player:
def __init__(self):
self.video = Gst.ElementFactory.make("fakesink")
self.playbin = Gst.ElementFactory.make("playbin")
self.playbin.set_property("video-sink", self.video)
self.bus = self.playbin.get_bus()
self.bus.add_signal_watch()
self.track = None
def get_state(self):
(ret, state, pending) = self.playbin.get_state(Gst.CLOCK_TIME_NONE)
if ret == Gst.StateChangeReturn.SUCCESS:
return state
return ret
def load_track(self, track):
self.track = track
if track is not None:
uri = track.filepath().absolute().as_uri()
self.playbin.set_property("uri", uri)
def load_set_state(self, track, state):
self.playbin.set_state(Gst.State.READY)
self.load_track(track)
self.playbin.set_state(state)
def next(self, *args):
(track, cont) = trackdb.Stack.next()
state = Gst.State.PLAYING if cont else Gst.State.PAUSED
self.load_set_state(track, state)
def pause(self, *args):
self.playbin.set_state(Gst.State.PAUSED)
def play(self, *args):
self.playbin.set_state(Gst.State.PLAYING)
def play_track(self, track):
if track == self.track:
return False
self.load_set_state(track, Gst.State.PLAYING)
return True
def previous(self, *args):
self.play_track(trackdb.Stack.previous())

70
audio/test_player.py Normal file
View File

@ -0,0 +1,70 @@
# Copyright 2021 (c) Anna Schumaker.
from . import player
from gi.repository import Gst
import pathlib
import trackdb
import unittest
test_album = pathlib.Path("./trier/Test Album/")
test_track = test_album / "01 - Test Track.ogg"
class TestPlayer(unittest.TestCase):
def setUpClass():
trackdb.reset()
lib = trackdb.Library.add(test_album)
lib.scan().join()
def setUp(self):
self.library = trackdb.Library.store[test_album]
self.track = [ t for t in self.library.tracks if t.tracknumber == 1 ][0]
def tearDownClass():
trackdb.reset()
def test_player_init(self):
play = player.Player()
self.assertIsInstance(play.video, Gst.Element)
self.assertIsInstance(play.playbin, Gst.Element)
self.assertIsInstance(play.bus, Gst.Bus)
self.assertIsNone(play.track)
self.assertEqual(play.playbin.get_property("video-sink"), play.video)
def test_player_load_track(self):
play = player.Player()
uri = test_track.absolute().as_uri()
self.assertEqual(play.playbin.get_property("uri"), None)
play.load_track(self.track)
self.assertEqual(play.playbin.get_property("uri"), uri)
self.assertEqual(play.get_state(), Gst.State.NULL)
self.assertFalse(play.play_track(self.track))
def test_player_play_pause(self):
play = player.Player()
play.load_track(self.track)
self.assertEqual(play.track, self.track)
self.assertEqual(play.get_state(), Gst.State.NULL)
play.pause()
self.assertEqual(play.get_state(), Gst.State.PAUSED)
play.play()
self.assertEqual(play.get_state(), Gst.State.PLAYING)
play.pause()
self.assertEqual(play.get_state(), Gst.State.PAUSED)
def test_player_next_previous(self):
play = player.Player()
play.next()
self.assertEqual(play.track.trackid, 0)
play.next()
self.assertEqual(play.track.trackid, 1)
play.previous()
self.assertEqual(play.track.trackid, 0)

View File

@ -2,6 +2,7 @@
# Copyright 2021 (c) Anna Schumaker.
import gi
gi.require_version('Gtk', '4.0')
gi.require_version('Gst', '1.0')
import lib
import trackdb