diff --git a/docs/changes.rst b/docs/changes.rst index 42478178..c05cda1c 100644 --- a/docs/changes.rst +++ b/docs/changes.rst @@ -122,6 +122,10 @@ backends: - MPD no longer lowercases search queries. This broke e.g. search by URI, where casing may be essential. +- :issue:`236`: The ``mopidy-scan`` command failed to include tags from ALAC + files (Apple lossless) because it didn't support multiple tag messages from + GStreamer per track it scanned. + v0.8.1 (2012-10-30) =================== diff --git a/mopidy/scanner.py b/mopidy/scanner.py index c20ef4fb..d84c262c 100644 --- a/mopidy/scanner.py +++ b/mopidy/scanner.py @@ -89,51 +89,70 @@ def translator(data): class Scanner(object): def __init__(self, folder, data_callback, error_callback=None): + self.data = {} self.files = path.find_files(folder) self.data_callback = data_callback self.error_callback = error_callback self.loop = gobject.MainLoop() - fakesink = gst.element_factory_make('fakesink') + self.fakesink = gst.element_factory_make('fakesink') + self.fakesink.set_property('signal-handoffs', True) + self.fakesink.connect('handoff', self.process_handoff) self.uribin = gst.element_factory_make('uridecodebin') self.uribin.set_property('caps', gst.Caps(b'audio/x-raw-int')) - self.uribin.connect( - 'pad-added', self.process_new_pad, fakesink.get_pad('sink')) + self.uribin.connect('pad-added', self.process_new_pad) self.pipe = gst.element_factory_make('pipeline') self.pipe.add(self.uribin) - self.pipe.add(fakesink) + self.pipe.add(self.fakesink) bus = self.pipe.get_bus() bus.add_signal_watch() + bus.connect('message::application', self.process_application) bus.connect('message::tag', self.process_tags) bus.connect('message::error', self.process_error) - def process_new_pad(self, source, pad, target_pad): - pad.link(target_pad) + def process_handoff(self, fakesink, buffer_, pad): + # When this function is called the first buffer has reached the end of + # the pipeline, and we can continue with the next track. Since we're + # in another thread, we send a message back to the main thread using + # the bus. + structure = gst.Structure('handoff') + message = gst.message_new_application(fakesink, structure) + bus = self.pipe.get_bus() + bus.post(message) + + def process_new_pad(self, source, pad): + pad.link(self.fakesink.get_pad('sink')) + + def process_application(self, bus, message): + if message.src != self.fakesink: + return + + if message.structure.get_name() != 'handoff': + return + + self.data['uri'] = unicode(self.uribin.get_property('uri')) + self.data[gst.TAG_DURATION] = self.get_duration() + + try: + self.data_callback(self.data) + self.next_uri() + except KeyboardInterrupt: + self.stop() def process_tags(self, bus, message): taglist = message.parse_tag() - data = { - 'uri': unicode(self.uribin.get_property('uri')), - gst.TAG_DURATION: self.get_duration(), - } for key in taglist.keys(): # XXX: For some crazy reason some wma files spit out lists here, # not sure if this is due to better data in headers or wma being # stupid. So ugly hack for now :/ if type(taglist[key]) is list: - data[key] = taglist[key][0] + self.data[key] = taglist[key][0] else: - data[key] = taglist[key] - - try: - self.data_callback(data) - self.next_uri() - except KeyboardInterrupt: - self.stop() + self.data[key] = taglist[key] def process_error(self, bus, message): if self.error_callback: @@ -151,6 +170,7 @@ class Scanner(object): return None def next_uri(self): + self.data = {} try: uri = path.path_to_uri(self.files.next()) except StopIteration: @@ -158,7 +178,7 @@ class Scanner(object): return False self.pipe.set_state(gst.STATE_NULL) self.uribin.set_property('uri', uri) - self.pipe.set_state(gst.STATE_PAUSED) + self.pipe.set_state(gst.STATE_PLAYING) return True def start(self):