audio: Start storing the tags we find in audio
Adds a new get_currents_tags method for fetching the full set of current tags. There are still some untested cases for this, and I also suspect we still want some API refinements one core starts using this.
This commit is contained in:
parent
bc347f1650
commit
983148a9a4
@ -372,6 +372,7 @@ class _Handler(object):
|
||||
def on_end_of_stream(self):
|
||||
gst_logger.debug('Got end-of-stream message.')
|
||||
logger.debug('Audio event: reached_end_of_stream()')
|
||||
self._audio._tags = {}
|
||||
AudioListener.send('reached_end_of_stream')
|
||||
|
||||
def on_error(self, error, debug):
|
||||
@ -390,10 +391,10 @@ class _Handler(object):
|
||||
gst_logger.debug('Got async-done.')
|
||||
|
||||
def on_tag(self, taglist):
|
||||
# TODO: store current tags and reset on stream changes.
|
||||
tags = taglist.keys()
|
||||
logger.debug('Audio event: tags_changed(tags=%r)', tags)
|
||||
AudioListener.send('tags_changed', tags=tags)
|
||||
tags = utils.convert_taglist(taglist)
|
||||
self._audio._tags.update(tags)
|
||||
logger.debug('Audio event: tags_changed(tags=%r)', tags.keys())
|
||||
AudioListener.send('tags_changed', tags=tags.keys())
|
||||
|
||||
def on_missing_plugin(self, msg):
|
||||
desc = gst.pbutils.missing_plugin_message_get_description(msg)
|
||||
@ -440,6 +441,7 @@ class Audio(pykka.ThreadingActor):
|
||||
self._config = config
|
||||
self._target_state = gst.STATE_NULL
|
||||
self._buffering = False
|
||||
self._tags = {}
|
||||
|
||||
self._playbin = None
|
||||
self._outputs = None
|
||||
@ -546,6 +548,7 @@ class Audio(pykka.ThreadingActor):
|
||||
:param uri: the URI to play
|
||||
:type uri: string
|
||||
"""
|
||||
self._tags = {} # TODO: add test for this somehow
|
||||
self._playbin.set_property('uri', uri)
|
||||
|
||||
def set_appsrc(
|
||||
@ -733,6 +736,7 @@ class Audio(pykka.ThreadingActor):
|
||||
# of faking it in the message handling when result=OK
|
||||
return True
|
||||
|
||||
# TODO: bake this into setup appsrc perhaps?
|
||||
def set_metadata(self, track):
|
||||
"""
|
||||
Set track metadata for currently playing song.
|
||||
@ -763,5 +767,22 @@ class Audio(pykka.ThreadingActor):
|
||||
taglist[gst.TAG_ALBUM] = track.album.name
|
||||
|
||||
event = gst.event_new_tag(taglist)
|
||||
# TODO: check if we get this back on our own bus?
|
||||
self._playbin.send_event(event)
|
||||
gst_logger.debug('Sent tag event: track=%s', track.uri)
|
||||
|
||||
def get_current_tags(self):
|
||||
"""
|
||||
Get the currently playing media's tags.
|
||||
|
||||
If no tags have been found, or nothing is playing this returns an empty
|
||||
dictionary. For each set of tags we collect a tags_changed event is
|
||||
emitted with the keys of the changes tags. After such calls users may
|
||||
call this function to get the updated values.
|
||||
|
||||
:rtype: {key: [values]} dict for the current media.
|
||||
"""
|
||||
# TODO: should this be a (deep) copy? most likely yes
|
||||
# TODO: should we return None when stopped?
|
||||
# TODO: support only fetching keys we care about?
|
||||
return self._tags
|
||||
|
||||
@ -21,9 +21,11 @@ class DummyAudio(pykka.ThreadingActor):
|
||||
self._callback = None
|
||||
self._uri = None
|
||||
self._state_change_result = True
|
||||
self._tags = {}
|
||||
|
||||
def set_uri(self, uri):
|
||||
assert self._uri is None, 'prepare change not called before set'
|
||||
self._tags = {}
|
||||
self._uri = uri
|
||||
|
||||
def set_appsrc(self, *args, **kwargs):
|
||||
@ -66,6 +68,9 @@ class DummyAudio(pykka.ThreadingActor):
|
||||
def set_metadata(self, track):
|
||||
pass
|
||||
|
||||
def get_current_tags(self):
|
||||
return self._tags
|
||||
|
||||
def set_about_to_finish_callback(self, callback):
|
||||
self._callback = callback
|
||||
|
||||
@ -92,7 +97,8 @@ class DummyAudio(pykka.ThreadingActor):
|
||||
new_state=new_state, target_state=None)
|
||||
|
||||
if new_state == PlaybackState.PLAYING:
|
||||
AudioListener.send('tags_changed', tags=[])
|
||||
self._tags['audio-codec'] = [u'fake info...']
|
||||
AudioListener.send('tags_changed', tags=['audio-codec'])
|
||||
|
||||
return self._state_change_result
|
||||
|
||||
@ -107,6 +113,7 @@ class DummyAudio(pykka.ThreadingActor):
|
||||
self._callback()
|
||||
|
||||
if not self._uri or not self._callback:
|
||||
self._tags = {}
|
||||
AudioListener.send('reached_end_of_stream')
|
||||
else:
|
||||
AudioListener.send('position_changed', position=0)
|
||||
|
||||
@ -338,7 +338,7 @@ class AudioEventTest(BaseTest):
|
||||
if not event.wait(timeout=1.0):
|
||||
self.fail('End of stream not reached within deadline')
|
||||
|
||||
# Make sure that gapless really works:
|
||||
self.assertFalse(self.audio.get_current_tags().get())
|
||||
|
||||
def test_gapless(self, send_mock):
|
||||
uris = self.uris[1:]
|
||||
@ -380,6 +380,60 @@ class AudioEventTest(BaseTest):
|
||||
self.assertEqual(1, keys.count('state_changed'))
|
||||
self.assertEqual(1, keys.count('reached_end_of_stream'))
|
||||
|
||||
# TODO: test tag states within gaples
|
||||
|
||||
def test_current_tags_are_blank_to_begin_with(self, send_mock):
|
||||
self.assertFalse(self.audio.get_current_tags().get())
|
||||
|
||||
def test_current_tags_blank_after_end_of_stream(self, send_mock):
|
||||
done = threading.Event()
|
||||
|
||||
def send(name, **kwargs):
|
||||
if name == 'reached_end_of_stream':
|
||||
done.set()
|
||||
|
||||
send_mock.side_effect = send
|
||||
|
||||
self.audio.prepare_change()
|
||||
self.audio.set_uri(self.uris[0])
|
||||
self.audio.start_playback()
|
||||
|
||||
self.possibly_trigger_fake_about_to_finish()
|
||||
self.audio.wait_for_state_change().get()
|
||||
|
||||
if not done.wait(timeout=1.0):
|
||||
self.fail('EOS not received')
|
||||
|
||||
self.assertFalse(self.audio.get_current_tags().get())
|
||||
|
||||
def test_current_tags_stored(self, send_mock):
|
||||
done = threading.Event()
|
||||
tags = []
|
||||
|
||||
def callback():
|
||||
tags.append(self.audio.get_current_tags().get())
|
||||
|
||||
def send(name, **kwargs):
|
||||
if name == 'reached_end_of_stream':
|
||||
done.set()
|
||||
|
||||
send_mock.side_effect = send
|
||||
self.audio.set_about_to_finish_callback(callback).get()
|
||||
|
||||
self.audio.prepare_change()
|
||||
self.audio.set_uri(self.uris[0])
|
||||
self.audio.start_playback()
|
||||
|
||||
self.possibly_trigger_fake_about_to_finish()
|
||||
self.audio.wait_for_state_change().get()
|
||||
|
||||
if not done.wait(timeout=1.0):
|
||||
self.fail('EOS not received')
|
||||
|
||||
self.assertTrue(tags[0])
|
||||
|
||||
# TODO: test that we reset when we expect between songs
|
||||
|
||||
|
||||
class AudioDummyEventTest(DummyMixin, AudioEventTest):
|
||||
"""Exercise the AudioEventTest against our mock audio classes."""
|
||||
|
||||
Loading…
Reference in New Issue
Block a user