From cee73b5501b7956ebc6cc5a5712fc31c3ff30523 Mon Sep 17 00:00:00 2001 From: Thomas Adamcik Date: Wed, 11 Mar 2015 23:09:14 +0100 Subject: [PATCH] audio: Add support for checking seekable state in scanner Return type of scanner changed to a named tuple with (uri, tags, duration, seekable). This should help with #872 and the related "live" issues. Tests, local scan and stream metadata lookup have been updated to account for the changes. --- mopidy/audio/scan.py | 22 +++++++++++++++++----- mopidy/local/commands.py | 3 ++- mopidy/stream/actor.py | 6 +++--- tests/audio/test_scan.py | 6 +++--- 4 files changed, 25 insertions(+), 12 deletions(-) diff --git a/mopidy/audio/scan.py b/mopidy/audio/scan.py index c3eec941..d443b8bd 100644 --- a/mopidy/audio/scan.py +++ b/mopidy/audio/scan.py @@ -1,5 +1,6 @@ from __future__ import absolute_import, division, unicode_literals +import collections import time import pygst @@ -13,6 +14,9 @@ from mopidy.utils import encoding _missing_plugin_desc = gst.pbutils.missing_plugin_message_get_description +Result = collections.namedtuple( + 'Result', ('uri', 'tags', 'duration', 'seekable')) + class Scanner(object): """ @@ -54,19 +58,22 @@ class Scanner(object): :param uri: URI of the resource to scan. :type event: string - :return: (tags, duration) pair. tags is a dictionary of lists for all - the tags we found and duration is the length of the URI in - milliseconds, or :class:`None` if the URI has no duration. + :return: A named tuple containing ``(uri, tags, duration, seekable)``. + ``tags`` is a dictionary of lists for all the tags we found. + ``duration`` is the length of the URI in milliseconds, or + :class:`None` if the URI has no duration. ``seekable`` is boolean + indicating if a seek would succeed. """ - tags, duration = None, None + tags, duration, seekable = None, None, None try: self._setup(uri) tags = self._collect() duration = self._query_duration() + seekable = self._query_seekable() finally: self._reset() - return tags, duration + return Result(uri, tags, duration, seekable) def _setup(self, uri): """Primes the pipeline for collection.""" @@ -123,3 +130,8 @@ class Scanner(object): return None else: return duration // gst.MSECOND + + def _query_seekable(self): + query = gst.query_new_seeking(gst.FORMAT_TIME) + self._pipe.query(query) + return query.parse_seeking()[1] diff --git a/mopidy/local/commands.py b/mopidy/local/commands.py index 279fda13..af8b0025 100644 --- a/mopidy/local/commands.py +++ b/mopidy/local/commands.py @@ -133,7 +133,8 @@ class ScanCommand(commands.Command): try: relpath = translator.local_track_uri_to_path(uri, media_dir) file_uri = path.path_to_uri(os.path.join(media_dir, relpath)) - tags, duration = scanner.scan(file_uri) + result = scanner.scan(file_uri) + tags, duration = result.tags, result.duration if duration < MIN_DURATION_MS: logger.warning('Failed %s: Track shorter than %dms', uri, MIN_DURATION_MS) diff --git a/mopidy/stream/actor.py b/mopidy/stream/actor.py index 58fd966a..47bfd58f 100644 --- a/mopidy/stream/actor.py +++ b/mopidy/stream/actor.py @@ -45,9 +45,9 @@ class StreamLibraryProvider(backend.LibraryProvider): return [Track(uri=uri)] try: - tags, duration = self._scanner.scan(uri) - track = utils.convert_tags_to_track(tags).copy( - uri=uri, length=duration) + result = self._scanner.scan(uri) + track = utils.convert_tags_to_track(result.tags).copy( + uri=uri, length=result.duration) except exceptions.ScannerError as e: logger.warning('Problem looking up %s: %s', uri, e) track = Track(uri=uri) diff --git a/tests/audio/test_scan.py b/tests/audio/test_scan.py index 50ec8352..b2937a3f 100644 --- a/tests/audio/test_scan.py +++ b/tests/audio/test_scan.py @@ -31,9 +31,9 @@ class ScannerTest(unittest.TestCase): uri = path_lib.path_to_uri(path) key = uri[len('file://'):] try: - tags, duration = scanner.scan(uri) - self.tags[key] = tags - self.durations[key] = duration + result = scanner.scan(uri) + self.tags[key] = result.tags + self.durations[key] = result.duration except exceptions.ScannerError as error: self.errors[key] = error