diff --git a/docs/changelog.rst b/docs/changelog.rst index 6932fb54..f346b0c9 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -4,6 +4,16 @@ Changelog This changelog is used to track all major changes to Mopidy. +v1.1.2 (UNRELEASED) +=================== + +Bug fix release. + +- Stream: If an URI is considered playable, don't consider it as a candidate + for playlist parsing. Just looking at MIME type prefixes isn't enough, as for + example Ogg Vorbis has the MIME type ``application/ogg``. (Fixes: + :issue:`1299`) + v1.1.1 (2015-09-14) =================== diff --git a/mopidy/stream/actor.py b/mopidy/stream/actor.py index b3bf0b30..818d570e 100644 --- a/mopidy/stream/actor.py +++ b/mopidy/stream/actor.py @@ -123,12 +123,14 @@ def _unwrap_stream(uri, timeout, scanner, requests_session): logger.debug('GStreamer failed scanning URI (%s): %s', uri, exc) scan_result = None - if scan_result is not None and not ( - scan_result.mime.startswith('text/') or - scan_result.mime.startswith('application/')): - logger.debug( - 'Unwrapped potential %s stream: %s', scan_result.mime, uri) - return uri + if scan_result is not None: + if scan_result.playable or ( + not scan_result.mime.startswith('text/') and + not scan_result.mime.startswith('application/') + ): + logger.debug( + 'Unwrapped potential %s stream: %s', scan_result.mime, uri) + return uri download_timeout = deadline - time.time() if download_timeout < 0: diff --git a/tests/stream/test_playback.py b/tests/stream/test_playback.py index 4c42b1cd..ef7da0bf 100644 --- a/tests/stream/test_playback.py +++ b/tests/stream/test_playback.py @@ -38,7 +38,9 @@ def audio(): @pytest.fixture def scanner(): - return mock.Mock(spec=scan.Scanner) + scan_mock = mock.Mock(spec=scan.Scanner) + scan_mock.scan.return_value = None + return scan_mock @pytest.fixture @@ -58,7 +60,24 @@ class TestTranslateURI(object): @responses.activate def test_audio_stream_returns_same_uri(self, scanner, provider): - scanner.scan.return_value.mime = 'audio/mpeg' + scanner.scan.side_effect = [ + # Set playable to False to test detection by mimetype + mock.Mock(mime='audio/mpeg', playable=False), + ] + + result = provider.translate_uri(STREAM_URI) + + scanner.scan.assert_called_once_with(STREAM_URI, timeout=mock.ANY) + assert result == STREAM_URI + + @responses.activate + def test_playable_ogg_stream_is_not_considered_a_playlist( + self, scanner, provider): + + scanner.scan.side_effect = [ + # Set playable to True to ignore detection as possible playlist + mock.Mock(mime='application/ogg', playable=True), + ] result = provider.translate_uri(STREAM_URI) @@ -70,8 +89,10 @@ class TestTranslateURI(object): self, scanner, provider, caplog): scanner.scan.side_effect = [ - mock.Mock(mime='text/foo'), # scanning playlist - mock.Mock(mime='audio/mpeg'), # scanning stream + # Scanning playlist + mock.Mock(mime='text/foo', playable=False), + # Scanning stream + mock.Mock(mime='audio/mpeg', playable=True), ] responses.add( responses.GET, PLAYLIST_URI, @@ -100,8 +121,10 @@ class TestTranslateURI(object): @responses.activate def test_xml_playlist_with_mpeg_stream(self, scanner, provider): scanner.scan.side_effect = [ - mock.Mock(mime='application/xspf+xml'), # scanning playlist - mock.Mock(mime='audio/mpeg'), # scanning stream + # Scanning playlist + mock.Mock(mime='application/xspf+xml', playable=False), + # Scanning stream + mock.Mock(mime='audio/mpeg', playable=True), ] responses.add( responses.GET, PLAYLIST_URI, @@ -120,8 +143,10 @@ class TestTranslateURI(object): self, scanner, provider, caplog): scanner.scan.side_effect = [ - exceptions.ScannerError('some failure'), # scanning playlist - mock.Mock(mime='audio/mpeg'), # scanning stream + # Scanning playlist + exceptions.ScannerError('some failure'), + # Scanning stream + mock.Mock(mime='audio/mpeg', playable=True), ] responses.add( responses.GET, PLAYLIST_URI, @@ -169,7 +194,9 @@ class TestTranslateURI(object): @responses.activate def test_playlist_references_itself(self, scanner, provider, caplog): - scanner.scan.return_value.mime = 'text/foo' + scanner.scan.side_effect = [ + mock.Mock(mime='text/foo', playable=False) + ] responses.add( responses.GET, PLAYLIST_URI, body=BODY.replace(STREAM_URI, PLAYLIST_URI),