From 9fb92139ec6e21490a1889fa765e5a97fbbf5a5e Mon Sep 17 00:00:00 2001 From: Stein Magnus Jodal Date: Mon, 14 Sep 2015 10:40:46 +0200 Subject: [PATCH 01/45] tests: Remove version ordering test I believe we're able to choose proper version numbers without the busy-work of updating this test. --- tests/test_version.py | 57 ------------------------------------------- 1 file changed, 57 deletions(-) diff --git a/tests/test_version.py b/tests/test_version.py index 011c8de7..41607c98 100644 --- a/tests/test_version.py +++ b/tests/test_version.py @@ -8,62 +8,5 @@ from mopidy import __version__ class VersionTest(unittest.TestCase): - def assertVersionLess(self, first, second): # noqa: N802 - self.assertLess(StrictVersion(first), StrictVersion(second)) - def test_current_version_is_parsable_as_a_strict_version_number(self): StrictVersion(__version__) - - def test_versions_can_be_strictly_ordered(self): - self.assertVersionLess('0.1.0a0', '0.1.0a1') - self.assertVersionLess('0.1.0a1', '0.1.0a2') - self.assertVersionLess('0.1.0a2', '0.1.0a3') - self.assertVersionLess('0.1.0a3', '0.1.0') - self.assertVersionLess('0.1.0', '0.2.0') - self.assertVersionLess('0.1.0', '1.0.0') - self.assertVersionLess('0.2.0', '0.3.0') - self.assertVersionLess('0.3.0', '0.3.1') - self.assertVersionLess('0.3.1', '0.4.0') - self.assertVersionLess('0.4.0', '0.4.1') - self.assertVersionLess('0.4.1', '0.5.0') - self.assertVersionLess('0.5.0', '0.6.0') - self.assertVersionLess('0.6.0', '0.6.1') - self.assertVersionLess('0.6.1', '0.7.0') - self.assertVersionLess('0.7.0', '0.7.1') - self.assertVersionLess('0.7.1', '0.7.2') - self.assertVersionLess('0.7.2', '0.7.3') - self.assertVersionLess('0.7.3', '0.8.0') - self.assertVersionLess('0.8.0', '0.8.1') - self.assertVersionLess('0.8.1', '0.9.0') - self.assertVersionLess('0.9.0', '0.10.0') - self.assertVersionLess('0.10.0', '0.11.0') - self.assertVersionLess('0.11.0', '0.11.1') - self.assertVersionLess('0.11.1', '0.12.0') - self.assertVersionLess('0.12.0', '0.13.0') - self.assertVersionLess('0.13.0', '0.14.0') - self.assertVersionLess('0.14.0', '0.14.1') - self.assertVersionLess('0.14.1', '0.14.2') - self.assertVersionLess('0.14.2', '0.15.0') - self.assertVersionLess('0.15.0', '0.16.0') - self.assertVersionLess('0.16.0', '0.17.0') - self.assertVersionLess('0.17.0', '0.18.0') - self.assertVersionLess('0.18.0', '0.18.1') - self.assertVersionLess('0.18.1', '0.18.2') - self.assertVersionLess('0.18.2', '0.18.3') - self.assertVersionLess('0.18.3', '0.19.0') - self.assertVersionLess('0.19.0', '0.19.1') - self.assertVersionLess('0.19.1', '0.19.2') - self.assertVersionLess('0.19.2', '0.19.3') - self.assertVersionLess('0.19.3', '0.19.4') - self.assertVersionLess('0.19.4', '0.19.5') - self.assertVersionLess('0.19.5', '1.0.0') - self.assertVersionLess('1.0.0', '1.0.1') - self.assertVersionLess('1.0.1', '1.0.2') - self.assertVersionLess('1.0.2', '1.0.3') - self.assertVersionLess('1.0.3', '1.0.4') - self.assertVersionLess('1.0.4', '1.0.5') - self.assertVersionLess('1.0.5', '1.0.6') - self.assertVersionLess('1.0.6', '1.0.7') - self.assertVersionLess('1.0.7', '1.0.8') - self.assertVersionLess('1.0.8', __version__) - self.assertVersionLess(__version__, '1.1.1') From e361ac6b542cf3a0cf842a12cc47a6ea625244b1 Mon Sep 17 00:00:00 2001 From: Stein Magnus Jodal Date: Thu, 24 Sep 2015 22:44:56 +0200 Subject: [PATCH 02/45] Ignore .cache/ used by pytest 2.8 (cherry picked from commit dc237985266b68ed41482df42f3d76a9d2eaa44f) --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 990d75ca..d5d8194c 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ *.pyc *.swp *~ +.cache/ .coverage .idea .noseids @@ -15,5 +16,5 @@ dist/ docs/_build/ mopidy.log* nosetests.xml -xunit-*.xml tmp/ +xunit-*.xml From 6f32bdc0834f52a1c749205385d204bbf7d3d71d Mon Sep 17 00:00:00 2001 From: Stein Magnus Jodal Date: Sun, 4 Oct 2015 23:08:07 +0200 Subject: [PATCH 03/45] stream: Don't parse as playlist if playable --- docs/changelog.rst | 10 ++++++++ mopidy/stream/actor.py | 14 ++++++----- tests/stream/test_playback.py | 45 ++++++++++++++++++++++++++++------- 3 files changed, 54 insertions(+), 15 deletions(-) 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), From 525019d03e33cb733758ece79a1e4f07415f5070 Mon Sep 17 00:00:00 2001 From: Cadel Watson Date: Mon, 5 Oct 2015 17:14:34 +1100 Subject: [PATCH 04/45] Exit with error if clear or scan attempts to load nonexistent library Fixes #1298 (cherry picked from commit 1e037adfc1fbecaebcd6173a2bcda7ce6d436c37) (cherry picked from commit 44eb668ff957fae42d3ddcab9a3acda56d9ecc8c) (cherry picked from commit bf318fb1ea09108d9b0c32de2d70383f9f740c4b) --- docs/changelog.rst | 3 +++ mopidy/local/commands.py | 9 +++++++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index f346b0c9..bc596d87 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -14,6 +14,9 @@ Bug fix release. example Ogg Vorbis has the MIME type ``application/ogg``. (Fixes: :issue:`1299`) +- Local: If the scan or clear commands are used on a library that does not + exist, exit with an error. (Fixes: :issue:`1298`) + v1.1.1 (2015-09-14) =================== diff --git a/mopidy/local/commands.py b/mopidy/local/commands.py index 7033f3aa..d61cf441 100644 --- a/mopidy/local/commands.py +++ b/mopidy/local/commands.py @@ -21,8 +21,8 @@ def _get_library(args, config): library_name = config['local']['library'] if library_name not in libraries: - logger.warning('Local library %s not found', library_name) - return 1 + logger.error('Local library %s not found', library_name) + return None logger.debug('Using %s as the local library', library_name) return libraries[library_name](config) @@ -41,6 +41,9 @@ class ClearCommand(commands.Command): def run(self, args, config): library = _get_library(args, config) + if library is None: + return 1 + prompt = '\nAre you sure you want to clear the library? [y/N] ' if compat.input(prompt).lower() != 'y': @@ -76,6 +79,8 @@ class ScanCommand(commands.Command): bytes(file_ext.lower()) for file_ext in excluded_file_extensions) library = _get_library(args, config) + if library is None: + return 1 file_mtimes, file_errors = path.find_mtimes( media_dir, follow=config['local']['scan_follow_symlinks']) From 32ae87360c9b5c15bd5ef33ec9090e56ad6e1756 Mon Sep 17 00:00:00 2001 From: Stein Magnus Jodal Date: Mon, 26 Oct 2015 23:21:55 +0100 Subject: [PATCH 05/45] http: Catch and log all requests exceptions --- mopidy/internal/http.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/mopidy/internal/http.py b/mopidy/internal/http.py index e35b8561..751d04ce 100644 --- a/mopidy/internal/http.py +++ b/mopidy/internal/http.py @@ -29,7 +29,11 @@ def download(session, uri, timeout=1.0, chunk_size=4096): '%.3fs', uri, timeout) return None except requests.exceptions.InvalidSchema: - logger.warning('%s has an unsupported schema.', uri) + logger.warning('Download of %r failed due to unsupported schema', uri) + return None + except requests.exceptions.RequestException as exc: + logger.warning('Download of %r failed: %s', uri, exc) + logger.debug('Download exception details', exc_info=True) return None content = [] From 149fa15cab206979f245348aa7b82191ee68c5f9 Mon Sep 17 00:00:00 2001 From: Stein Magnus Jodal Date: Sun, 22 Nov 2015 21:05:31 +0100 Subject: [PATCH 06/45] docs: Fix return value reference Fixes #1332 --- mopidy/ext.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mopidy/ext.py b/mopidy/ext.py index 7fd68f96..fe8d0daf 100644 --- a/mopidy/ext.py +++ b/mopidy/ext.py @@ -54,7 +54,7 @@ class Extension(object): def get_config_schema(self): """The extension's config validation schema - :returns: :class:`~mopidy.config.schema.ExtensionConfigSchema` + :returns: :class:`~mopidy.config.schemas.ConfigSchema` """ schema = config_lib.ConfigSchema(self.ext_name) schema['enabled'] = config_lib.Boolean() From 22ec9d906032c6341dd290f8d400c4234c9ee0dd Mon Sep 17 00:00:00 2001 From: Thomas Adamcik Date: Fri, 4 Dec 2015 22:11:55 +0100 Subject: [PATCH 07/45] mpd: Add missing seeked event handling for idle Fixes #1331 (cherry picked from commit 19daa89e15efa64cc128e742f6f8d3426f1adb0b) --- docs/changelog.rst | 2 ++ mopidy/mpd/actor.py | 3 +++ 2 files changed, 5 insertions(+) diff --git a/docs/changelog.rst b/docs/changelog.rst index bc596d87..7637de58 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -17,6 +17,8 @@ Bug fix release. - Local: If the scan or clear commands are used on a library that does not exist, exit with an error. (Fixes: :issue:`1298`) +- MPD: Notify idling clients when a seek is performed. (Fixes: :issue:`1331`) + v1.1.1 (2015-09-14) =================== diff --git a/mopidy/mpd/actor.py b/mopidy/mpd/actor.py index 8eb59c1f..58c758e4 100644 --- a/mopidy/mpd/actor.py +++ b/mopidy/mpd/actor.py @@ -77,3 +77,6 @@ class MpdFrontend(pykka.ThreadingActor, CoreListener): def stream_title_changed(self, title): self.send_idle('playlist') + + def seeked(self, time_position): + self.send_idle('player') From 2b00e831791cbab26c9aa19e28a42be6f7abdfa4 Mon Sep 17 00:00:00 2001 From: jcass Date: Sun, 6 Dec 2015 16:01:26 +0200 Subject: [PATCH 08/45] Mark track as playing and add to history if changing track while paused. --- docs/changelog.rst | 3 +++ mopidy/core/playback.py | 12 +++++++---- tests/core/test_playback.py | 43 ++++++++++++++++++++++++++++++++++++- 3 files changed, 53 insertions(+), 5 deletions(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index 7637de58..29f7ef82 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -19,6 +19,9 @@ Bug fix release. - MPD: Notify idling clients when a seek is performed. (Fixes: :issue:`1331`) +- Core: Fix error in :meth:`~mopidy.core.PlaybackController._change_track` + docstring. (Fixes: :issue:`1352`) + v1.1.1 (2015-09-14) =================== diff --git a/mopidy/core/playback.py b/mopidy/core/playback.py index 9a11066b..e3a4f68d 100644 --- a/mopidy/core/playback.py +++ b/mopidy/core/playback.py @@ -207,12 +207,16 @@ class PlaybackController(object): if old_state == PlaybackState.PLAYING: self._play(on_error_step=on_error_step) elif old_state == PlaybackState.PAUSED: - # NOTE: this is just a quick hack to fix #1177 as this code has - # already been killed in the gapless branch. + # NOTE: this is just a quick hack to fix #1177 and #1352 as this + # code has already been killed in the gapless branch. backend = self._get_backend() if backend: - backend.playback.prepare_change() - backend.playback.change_track(tl_track.track).get() + success = ( + backend.playback.prepare_change().get and + backend.playback.change_track(tl_track.track).get()) + if success: + self.core.tracklist._mark_playing(tl_track) + self.core.history._add_track(tl_track.track) self.pause() # TODO: this is not really end of track, this is on_need_next_track diff --git a/tests/core/test_playback.py b/tests/core/test_playback.py index 5a8c9649..6bacb3ed 100644 --- a/tests/core/test_playback.py +++ b/tests/core/test_playback.py @@ -8,7 +8,7 @@ import pykka from mopidy import backend, core from mopidy.internal import deprecation -from mopidy.models import Track +from mopidy.models import TlTrack, Track from tests import dummy_audio as audio @@ -789,3 +789,44 @@ class Bug1177RegressionTest(unittest.TestCase): c.playback.pause() c.playback.next() b.playback.change_track.assert_called_once_with(track2) + + +class Bug1352RegressionTest(unittest.TestCase): + def test(self): + config = { + 'core': { + 'max_tracklist_length': 10000, + } + } + + b = mock.Mock() + b.uri_schemes.get.return_value = ['dummy'] + b.playback = mock.Mock(spec=backend.PlaybackProvider) + b.playback.change_track.return_value.get.return_value = True + b.playback.play.return_value.get.return_value = True + + track1 = Track(uri='dummy:a', length=40000) + track2 = Track(uri='dummy:b', length=40000) + + tl_track2 = TlTrack(1, track2) + + c = core.Core(config, mixer=None, backends=[b]) + c.tracklist.add([track1, track2]) + + d = mock.Mock() + c.history._add_track = d + + e = mock.Mock() + c.tracklist._mark_playing = e + + c.playback.play() + b.playback.change_track.reset_mock() + d.reset_mock() + e.reset_mock() + + c.playback.pause() + c.playback.next() + b.playback.change_track.assert_called_once_with(track2) + + d.assert_called_once_with(track2) + e.assert_called_once_with(tl_track2) From 139634b93bd0886a4245b4286923c406ccefc595 Mon Sep 17 00:00:00 2001 From: jcass Date: Sun, 6 Dec 2015 19:27:02 +0200 Subject: [PATCH 09/45] Update changelog. --- docs/changelog.rst | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index 29f7ef82..7d901aef 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -19,8 +19,9 @@ Bug fix release. - MPD: Notify idling clients when a seek is performed. (Fixes: :issue:`1331`) -- Core: Fix error in :meth:`~mopidy.core.PlaybackController._change_track` - docstring. (Fixes: :issue:`1352`) +- Core: Fix :meth:`~mopidy.core.PlaybackController._change_track` to mark + track as playing and add it to the history if changing track while paused. + (Fixes: :issue:`1352`) v1.1.1 (2015-09-14) From fb7b466bee23674f6d6bd97391ee5222f1daf580 Mon Sep 17 00:00:00 2001 From: jcass Date: Mon, 7 Dec 2015 07:53:34 +0200 Subject: [PATCH 10/45] Result of prepare_change no longer affects whether a track is added to the history. Update changelog and test cases. --- docs/changelog.rst | 6 +++--- mopidy/core/playback.py | 5 ++--- tests/core/test_playback.py | 15 ++++++--------- 3 files changed, 11 insertions(+), 15 deletions(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index 7d901aef..41d5cccc 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -19,9 +19,9 @@ Bug fix release. - MPD: Notify idling clients when a seek is performed. (Fixes: :issue:`1331`) -- Core: Fix :meth:`~mopidy.core.PlaybackController._change_track` to mark - track as playing and add it to the history if changing track while paused. - (Fixes: :issue:`1352`) +- Core: Fix bug in playback controller. If changing to another track while + the player is paused, the new track would not be added to the history or + marked as currently playing. (Fixes: :issue:`1352`) v1.1.1 (2015-09-14) diff --git a/mopidy/core/playback.py b/mopidy/core/playback.py index e3a4f68d..eeba5106 100644 --- a/mopidy/core/playback.py +++ b/mopidy/core/playback.py @@ -211,9 +211,8 @@ class PlaybackController(object): # code has already been killed in the gapless branch. backend = self._get_backend() if backend: - success = ( - backend.playback.prepare_change().get and - backend.playback.change_track(tl_track.track).get()) + backend.playback.prepare_change() + success = backend.playback.change_track(tl_track.track).get() if success: self.core.tracklist._mark_playing(tl_track) self.core.history._add_track(tl_track.track) diff --git a/tests/core/test_playback.py b/tests/core/test_playback.py index 6bacb3ed..f7b6dfda 100644 --- a/tests/core/test_playback.py +++ b/tests/core/test_playback.py @@ -813,20 +813,17 @@ class Bug1352RegressionTest(unittest.TestCase): c = core.Core(config, mixer=None, backends=[b]) c.tracklist.add([track1, track2]) - d = mock.Mock() - c.history._add_track = d + c.history._add_track = mock.PropertyMock() + c.tracklist._mark_playing = mock.PropertyMock() - e = mock.Mock() - c.tracklist._mark_playing = e c.playback.play() b.playback.change_track.reset_mock() - d.reset_mock() - e.reset_mock() + c.history._add_track.reset_mock() + c.tracklist._mark_playing.reset_mock() c.playback.pause() c.playback.next() b.playback.change_track.assert_called_once_with(track2) - - d.assert_called_once_with(track2) - e.assert_called_once_with(tl_track2) + c.history._add_track.assert_called_once_with(track2) + c.tracklist._mark_playing.assert_called_once_with(tl_track2) \ No newline at end of file From 3cd3b45512d8ff06dafaecd3b7ef2934e60495d5 Mon Sep 17 00:00:00 2001 From: jcass Date: Mon, 7 Dec 2015 07:56:35 +0200 Subject: [PATCH 11/45] Fix flake8 errors. --- tests/core/test_playback.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/core/test_playback.py b/tests/core/test_playback.py index f7b6dfda..61dfbb15 100644 --- a/tests/core/test_playback.py +++ b/tests/core/test_playback.py @@ -816,7 +816,6 @@ class Bug1352RegressionTest(unittest.TestCase): c.history._add_track = mock.PropertyMock() c.tracklist._mark_playing = mock.PropertyMock() - c.playback.play() b.playback.change_track.reset_mock() c.history._add_track.reset_mock() @@ -826,4 +825,4 @@ class Bug1352RegressionTest(unittest.TestCase): c.playback.next() b.playback.change_track.assert_called_once_with(track2) c.history._add_track.assert_called_once_with(track2) - c.tracklist._mark_playing.assert_called_once_with(tl_track2) \ No newline at end of file + c.tracklist._mark_playing.assert_called_once_with(tl_track2) From e9eb9d61b4fbbb388d12ed8b4028f79a7fea5c1e Mon Sep 17 00:00:00 2001 From: jcass Date: Fri, 11 Dec 2015 07:47:07 +0200 Subject: [PATCH 12/45] Test cases for issue #1358 --- tests/core/test_playback.py | 81 +++++++++++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) diff --git a/tests/core/test_playback.py b/tests/core/test_playback.py index 5a8c9649..48e30ad4 100644 --- a/tests/core/test_playback.py +++ b/tests/core/test_playback.py @@ -388,6 +388,25 @@ class CorePlaybackTest(unittest.TestCase): self.assertNotIn(tl_track, self.core.tracklist.tl_tracks) + def test_next_in_consume_mode_removes_unplayable_track(self): + self.backend1.playback.change_track = mock.PropertyMock() + self.backend1.playback.change_track.return_value.get.return_value = \ + False + + self.backend2.playback.change_track = mock.PropertyMock() + self.backend2.playback.change_track.return_value.get.return_value = \ + False + self.core.tracklist.consume = True + + self.core.playback.play(self.tl_tracks[0]) + self.core.playback.next() + + self.assertNotIn(self.tl_tracks[1], self.core.tracklist.tl_tracks) + self.assertNotIn(self.tl_tracks[2], self.core.tracklist.tl_tracks) + + self.backend1.playback.change_track.reset_mock() + self.backend2.playback.change_track.reset_mock() + @mock.patch( 'mopidy.core.playback.listener.CoreListener', spec=core.CoreListener) def test_next_emits_events(self, listener_mock): @@ -789,3 +808,65 @@ class Bug1177RegressionTest(unittest.TestCase): c.playback.pause() c.playback.next() b.playback.change_track.assert_called_once_with(track2) + + +class Bug1358RegressionTest(unittest.TestCase): + + def setUp(self): # noqa: N802 + config = { + 'core': { + 'max_tracklist_length': 10000, + } + } + + self.backend1 = mock.Mock() + self.backend1.uri_schemes.get.return_value = ['dummy1'] + self.playback1 = mock.Mock(spec=backend.PlaybackProvider) + self.backend1.playback.change_track.return_value.get.return_value = \ + False + self.backend1.playback = self.playback1 + + self.backend2 = mock.Mock() + self.backend2.uri_schemes.get.return_value = ['dummy2'] + self.playback2 = mock.Mock(spec=backend.PlaybackProvider) + self.backend1.playback.change_track.return_value.get.return_value = \ + False + self.backend2.playback = self.playback2 + + self.tracks = [ + Track(uri='dummy1:a', length=40000), + Track(uri='dummy2:a', length=40000), + ] + + self.uris = [ + 'dummy1:a', 'dummy2:a'] + + self.core = core.Core(config, mixer=None, + backends=[self.backend1, self.backend2]) + + def lookup(uris): + result = {uri: [] for uri in uris} + for track in self.tracks: + if track.uri in result: + result[track.uri].append(track) + return result + + self.lookup_patcher = mock.patch.object(self.core.library, 'lookup') + self.lookup_mock = self.lookup_patcher.start() + self.lookup_mock.side_effect = lookup + + self.core.tracklist.add(uris=self.uris) + + self.tl_tracks = self.core.tracklist.tl_tracks + + def tearDown(self): # noqa: N802 + self.lookup_patcher.stop() + + def test_next_in_consume_mode_removes_unplayable_track(self): + self.core.tracklist.consume = True + + self.core.playback.play(self.tl_tracks[0]) + self.core.playback.next() + + self.assertNotIn(self.tl_tracks[0], self.core.tracklist.tl_tracks) + self.assertNotIn(self.tl_tracks[1], self.core.tracklist.tl_tracks) From be5bdb8a85ebb546a0932d0de4f4797cad1d4b92 Mon Sep 17 00:00:00 2001 From: Stein Magnus Jodal Date: Sat, 12 Dec 2015 11:01:52 +0100 Subject: [PATCH 13/45] Fix review comments on PR#1359 --- tests/core/test_playback.py | 42 ++++++++++++++++++------------------- 1 file changed, 20 insertions(+), 22 deletions(-) diff --git a/tests/core/test_playback.py b/tests/core/test_playback.py index 48e30ad4..5fab9f6b 100644 --- a/tests/core/test_playback.py +++ b/tests/core/test_playback.py @@ -390,22 +390,20 @@ class CorePlaybackTest(unittest.TestCase): def test_next_in_consume_mode_removes_unplayable_track(self): self.backend1.playback.change_track = mock.PropertyMock() - self.backend1.playback.change_track.return_value.get.return_value = \ - False + self.backend1.playback.change_track.return_value.get.return_value = ( + False) self.backend2.playback.change_track = mock.PropertyMock() - self.backend2.playback.change_track.return_value.get.return_value = \ - False - self.core.tracklist.consume = True + self.backend2.playback.change_track.return_value.get.return_value = ( + False) + self.core.tracklist.set_consume(True) self.core.playback.play(self.tl_tracks[0]) self.core.playback.next() - self.assertNotIn(self.tl_tracks[1], self.core.tracklist.tl_tracks) - self.assertNotIn(self.tl_tracks[2], self.core.tracklist.tl_tracks) - - self.backend1.playback.change_track.reset_mock() - self.backend2.playback.change_track.reset_mock() + tl_tracks = self.core.tracklist.get_tl_tracks() + self.assertNotIn(self.tl_tracks[1], tl_tracks) + self.assertNotIn(self.tl_tracks[2], tl_tracks) @mock.patch( 'mopidy.core.playback.listener.CoreListener', spec=core.CoreListener) @@ -822,15 +820,15 @@ class Bug1358RegressionTest(unittest.TestCase): self.backend1 = mock.Mock() self.backend1.uri_schemes.get.return_value = ['dummy1'] self.playback1 = mock.Mock(spec=backend.PlaybackProvider) - self.backend1.playback.change_track.return_value.get.return_value = \ - False + self.backend1.playback.change_track.return_value.get.return_value = ( + False) self.backend1.playback = self.playback1 self.backend2 = mock.Mock() self.backend2.uri_schemes.get.return_value = ['dummy2'] self.playback2 = mock.Mock(spec=backend.PlaybackProvider) - self.backend1.playback.change_track.return_value.get.return_value = \ - False + self.backend1.playback.change_track.return_value.get.return_value = ( + False) self.backend2.playback = self.playback2 self.tracks = [ @@ -838,11 +836,10 @@ class Bug1358RegressionTest(unittest.TestCase): Track(uri='dummy2:a', length=40000), ] - self.uris = [ - 'dummy1:a', 'dummy2:a'] + self.uris = [t.uri for t in self.tracks] - self.core = core.Core(config, mixer=None, - backends=[self.backend1, self.backend2]) + self.core = core.Core( + config, mixer=None, backends=[self.backend1, self.backend2]) def lookup(uris): result = {uri: [] for uri in uris} @@ -857,16 +854,17 @@ class Bug1358RegressionTest(unittest.TestCase): self.core.tracklist.add(uris=self.uris) - self.tl_tracks = self.core.tracklist.tl_tracks + self.tl_tracks = self.core.tracklist.get_tl_tracks() def tearDown(self): # noqa: N802 self.lookup_patcher.stop() def test_next_in_consume_mode_removes_unplayable_track(self): - self.core.tracklist.consume = True + self.core.tracklist.set_consume(True) self.core.playback.play(self.tl_tracks[0]) self.core.playback.next() - self.assertNotIn(self.tl_tracks[0], self.core.tracklist.tl_tracks) - self.assertNotIn(self.tl_tracks[1], self.core.tracklist.tl_tracks) + tl_tracks = self.core.tracklist.get_tl_tracks() + self.assertNotIn(self.tl_tracks[0], tl_tracks) + self.assertNotIn(self.tl_tracks[1], tl_tracks) From 92bb9b9b77e206d9dac1df3bd005afc289cdf8a5 Mon Sep 17 00:00:00 2001 From: Thomas Adamcik Date: Sat, 5 Dec 2015 22:44:39 +0100 Subject: [PATCH 14/45] logging: Catch errors when loading logging/config_file (cherry picked from commit ede5b8abff6ff269f7180288f98ff1f68c3de1fc) --- docs/changelog.rst | 3 +++ mopidy/internal/log.py | 10 ++++++++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index 41d5cccc..26a115e5 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -23,6 +23,9 @@ Bug fix release. the player is paused, the new track would not be added to the history or marked as currently playing. (Fixes: :issue:`1352`) +- Main: Catch errors when loading :confval:`logging/config_file`. (Fixes: + :issue:`1320`) + v1.1.1 (2015-09-14) =================== diff --git a/mopidy/internal/log.py b/mopidy/internal/log.py index 9c40da4f..011a70d2 100644 --- a/mopidy/internal/log.py +++ b/mopidy/internal/log.py @@ -19,6 +19,8 @@ LOG_LEVELS = { TRACE_LOG_LEVEL = 5 logging.addLevelName(TRACE_LOG_LEVEL, 'TRACE') +logger = logging.getLogger(__name__) + class DelayedHandler(logging.Handler): @@ -54,8 +56,12 @@ def setup_logging(config, verbosity_level, save_debug_log): if config['logging']['config_file']: # Logging config from file must be read before other handlers are # added. If not, the other handlers will have no effect. - logging.config.fileConfig(config['logging']['config_file'], - disable_existing_loggers=False) + try: + path = config['logging']['config_file'] + logging.config.fileConfig(path, disable_existing_loggers=False) + except Exception as e: + # Catch everything as logging does not specify what can go wrong. + logger.error('Loading logging config %r failed. %s', path, e) setup_console_logging(config, verbosity_level) if save_debug_log: From 0cfa6927bbc8b9d2c80db1b50364b0cca66c75e1 Mon Sep 17 00:00:00 2001 From: Daniel Hahler Date: Sun, 29 Nov 2015 05:03:31 +0100 Subject: [PATCH 15/45] Skip tracks with empty uri in `track_to_mpd_format` Ref: https://github.com/mopidy/mopidy/issues/1340 --- mopidy/mpd/translator.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/mopidy/mpd/translator.py b/mopidy/mpd/translator.py index 4aa4bdb9..b601fd4b 100644 --- a/mopidy/mpd/translator.py +++ b/mopidy/mpd/translator.py @@ -1,11 +1,15 @@ from __future__ import absolute_import, unicode_literals import datetime +import logging import re from mopidy.models import TlTrack from mopidy.mpd.protocol import tagtype_list + +logger = logging.getLogger(__name__) + # TODO: special handling of local:// uri scheme normalize_path_re = re.compile(r'[^/]+') @@ -34,8 +38,12 @@ def track_to_mpd_format(track, position=None, stream_title=None): else: (tlid, track) = (None, track) + if not track.uri: + logger.warning('Ignoring track without uri') + return [] + result = [ - ('file', track.uri or ''), + ('file', track.uri), ('Time', track.length and (track.length // 1000) or 0), ('Artist', concat_multi_values(track.artists, 'name')), ('Album', track.album and track.album.name or ''), From 6afd5b0c701a1af314bf222fe8568513059a804e Mon Sep 17 00:00:00 2001 From: Stein Magnus Jodal Date: Fri, 1 Jan 2016 23:11:56 +0100 Subject: [PATCH 16/45] Fix tests and update changelog --- docs/changelog.rst | 2 ++ mopidy/mpd/translator.py | 4 +++- tests/mpd/test_translator.py | 10 +++++++--- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index 26a115e5..dafbb794 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -26,6 +26,8 @@ Bug fix release. - Main: Catch errors when loading :confval:`logging/config_file`. (Fixes: :issue:`1320`) +- MPD: Don't return tracks with empty URIs. (Partly fixes: :issue:`1340`) + v1.1.1 (2015-09-14) =================== diff --git a/mopidy/mpd/translator.py b/mopidy/mpd/translator.py index b601fd4b..a76d6d59 100644 --- a/mopidy/mpd/translator.py +++ b/mopidy/mpd/translator.py @@ -172,7 +172,9 @@ def tracks_to_mpd_format(tracks, start=0, end=None): assert len(tracks) == len(positions) result = [] for track, position in zip(tracks, positions): - result.append(track_to_mpd_format(track, position)) + formatted_track = track_to_mpd_format(track, position) + if formatted_track: + result.append(formatted_track) return result diff --git a/tests/mpd/test_translator.py b/tests/mpd/test_translator.py index 65c80bbb..e1ef703d 100644 --- a/tests/mpd/test_translator.py +++ b/tests/mpd/test_translator.py @@ -56,7 +56,7 @@ class TrackMpdFormatTest(unittest.TestCase): def test_track_to_mpd_format_with_position_and_tlid(self): result = translator.track_to_mpd_format( - TlTrack(2, Track()), position=1) + TlTrack(2, Track(uri='a uri')), position=1) self.assertIn(('Pos', 1), result) self.assertIn(('Id', 2), result) @@ -153,13 +153,17 @@ class PlaylistMpdFormatTest(unittest.TestCase): def test_mpd_format(self): playlist = Playlist(tracks=[ - Track(track_no=1), Track(track_no=2), Track(track_no=3)]) + Track(uri='foo', track_no=1), + Track(uri='bar', track_no=2), + Track(uri='baz', track_no=3)]) result = translator.playlist_to_mpd_format(playlist) self.assertEqual(len(result), 3) def test_mpd_format_with_range(self): playlist = Playlist(tracks=[ - Track(track_no=1), Track(track_no=2), Track(track_no=3)]) + Track(uri='foo', track_no=1), + Track(uri='bar', track_no=2), + Track(uri='baz', track_no=3)]) result = translator.playlist_to_mpd_format(playlist, 1, 2) self.assertEqual(len(result), 1) self.assertEqual(dict(result[0])['Track'], 2) From 19726832e7db5ca7b46efdd81975ef3c04307321 Mon Sep 17 00:00:00 2001 From: Stein Magnus Jodal Date: Fri, 1 Jan 2016 23:41:53 +0100 Subject: [PATCH 17/45] docs: Add PR ref to changelog --- docs/changelog.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index dafbb794..bea003f5 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -26,7 +26,8 @@ Bug fix release. - Main: Catch errors when loading :confval:`logging/config_file`. (Fixes: :issue:`1320`) -- MPD: Don't return tracks with empty URIs. (Partly fixes: :issue:`1340`) +- MPD: Don't return tracks with empty URIs. (Partly fixes: :issue:`1340`, PR: + :issue:`1343`) v1.1.1 (2015-09-14) From 094a874055aba9ccb6da2ea9f9d96c54e8cf371b Mon Sep 17 00:00:00 2001 From: Stein Magnus Jodal Date: Fri, 1 Jan 2016 23:42:32 +0100 Subject: [PATCH 18/45] core: Make lookup() ignore tracks without URI Fixes #1340 --- docs/changelog.rst | 3 +++ mopidy/core/library.py | 2 +- tests/core/test_library.py | 13 +++++++++++-- 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index bea003f5..a7404cc4 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -29,6 +29,9 @@ Bug fix release. - MPD: Don't return tracks with empty URIs. (Partly fixes: :issue:`1340`, PR: :issue:`1343`) +- Core: Make :meth:`~mopidy.core.LibraryController.lookup` ignore tracks with + empty URIs. (Partly fixes: :issue:`1340`, PR: :issue:`1381`) + v1.1.1 (2015-09-14) =================== diff --git a/mopidy/core/library.py b/mopidy/core/library.py index ce420812..2a4ee87d 100644 --- a/mopidy/core/library.py +++ b/mopidy/core/library.py @@ -236,7 +236,7 @@ class LibraryController(object): result = future.get() if result is not None: validation.check_instances(result, models.Track) - results[u] = result + results[u] = [r for r in result if r.uri] if uri: return results[uri] diff --git a/tests/core/test_library.py b/tests/core/test_library.py index 92b22bfb..750f371f 100644 --- a/tests/core/test_library.py +++ b/tests/core/test_library.py @@ -153,8 +153,8 @@ class CoreLibraryTest(BaseCoreLibraryTest): self.core.library.lookup('dummy1:a', ['dummy2:a']) def test_lookup_can_handle_uris(self): - track1 = Track(name='abc') - track2 = Track(name='def') + track1 = Track(uri='dummy1:a', name='abc') + track2 = Track(uri='dummy2:a', name='def') self.library1.lookup().get.return_value = [track1] self.library2.lookup().get.return_value = [track2] @@ -169,6 +169,15 @@ class CoreLibraryTest(BaseCoreLibraryTest): self.assertFalse(self.library1.lookup.called) self.assertFalse(self.library2.lookup.called) + def test_lookup_ignores_tracks_without_uri_set(self): + track1 = Track(uri='dummy1:a', name='abc') + track2 = Track() + + self.library1.lookup().get.return_value = [track1, track2] + + result = self.core.library.lookup(uris=['dummy1:a']) + self.assertEqual(result, {'dummy1:a': [track1]}) + def test_refresh_with_uri_selects_dummy1_backend(self): self.core.library.refresh('dummy1:a') From f2194e9d5eac09d524454c643f54d8af855f932f Mon Sep 17 00:00:00 2001 From: Stein Magnus Jodal Date: Sat, 12 Dec 2015 11:01:52 +0100 Subject: [PATCH 19/45] fix: skip uplayable tracks when the next track is selected while in a paused state. --- docs/changelog.rst | 10 ++++++- mopidy/core/playback.py | 15 ++++++++-- tests/core/test_playback.py | 55 +++++++++++++++++++++++++++++++------ 3 files changed, 67 insertions(+), 13 deletions(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index bea003f5..eb389166 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -21,7 +21,15 @@ Bug fix release. - Core: Fix bug in playback controller. If changing to another track while the player is paused, the new track would not be added to the history or - marked as currently playing. (Fixes: :issue:`1352`) + marked as currently playing. (Fixes: :issue:`1352`) Also skips over + unplayable tracks if the user attempts to change tracks while paused. + (Fixes :issue:`1378`). + +- Main: Catch errors when loading :confval:`logging/config_file`. (Fixes: + :issue:`1320`) + +- MPD: Don't return tracks with empty URIs. (Partly fixes: :issue:`1340`, PR: + :issue:`1343`) - Main: Catch errors when loading :confval:`logging/config_file`. (Fixes: :issue:`1320`) diff --git a/mopidy/core/playback.py b/mopidy/core/playback.py index eeba5106..a11b646d 100644 --- a/mopidy/core/playback.py +++ b/mopidy/core/playback.py @@ -207,8 +207,8 @@ class PlaybackController(object): if old_state == PlaybackState.PLAYING: self._play(on_error_step=on_error_step) elif old_state == PlaybackState.PAUSED: - # NOTE: this is just a quick hack to fix #1177 and #1352 as this - # code has already been killed in the gapless branch. + # NOTE: this is just a quick hack to fix #1177, #1352, and #1378 + # as this code has already been killed in the gapless branch. backend = self._get_backend() if backend: backend.playback.prepare_change() @@ -216,7 +216,16 @@ class PlaybackController(object): if success: self.core.tracklist._mark_playing(tl_track) self.core.history._add_track(tl_track.track) - self.pause() + else: + self.core.tracklist._mark_unplayable( + self.get_current_tl_track()) + if on_error_step == 1: + # TODO: can cause an endless loop for single track + # repeat. + self.next() + elif on_error_step == -1: + self.previous() + self.pause() # TODO: this is not really end of track, this is on_need_next_track def _on_end_of_track(self): diff --git a/tests/core/test_playback.py b/tests/core/test_playback.py index d176db0b..d46bdd52 100644 --- a/tests/core/test_playback.py +++ b/tests/core/test_playback.py @@ -41,19 +41,32 @@ class CorePlaybackTest(unittest.TestCase): self.backend3.uri_schemes.get.return_value = ['dummy3'] self.backend3.has_playback().get.return_value = False + # A backend for which 'change_track' fails + self.backend4 = mock.Mock() + self.backend4.uri_schemes.get.return_value = ['dummy4'] + self.playback4 = mock.Mock(spec=backend.PlaybackProvider) + self.playback4.get_time_position.return_value.get.return_value = 1000 + future_mock = mock.Mock(spec=pykka.future.Future) + future_mock.get.return_value = False + self.playback4.change_track.return_value = future_mock + self.backend4.playback = self.playback4 + self.tracks = [ Track(uri='dummy1:a', length=40000), Track(uri='dummy2:a', length=40000), - Track(uri='dummy3:a', length=40000), # Unplayable + Track(uri='dummy3:a', length=40000), # No playback provider Track(uri='dummy1:b', length=40000), Track(uri='dummy1:c', length=None), # No duration + Track(uri='dummy4:a', length=40000), # Unplayable + Track(uri='dummy1:d', length=40000), ] self.uris = [ - 'dummy1:a', 'dummy2:a', 'dummy3:a', 'dummy1:b', 'dummy1:c'] + 'dummy1:a', 'dummy2:a', 'dummy3:a', 'dummy1:b', 'dummy1:c', + 'dummy4:a', 'dummy1:d'] self.core = core.Core(config, mixer=None, backends=[ - self.backend1, self.backend2, self.backend3]) + self.backend1, self.backend2, self.backend3, self.backend4]) def lookup(uris): result = {uri: [] for uri in uris} @@ -172,16 +185,40 @@ class CorePlaybackTest(unittest.TestCase): self.playback2.change_track.return_value.get.return_value = False self.core.tracklist.clear() - self.core.tracklist.add(uris=self.uris[:2]) + self.core.tracklist.add(uris=self.uris[-2:]) tl_tracks = self.core.tracklist.tl_tracks self.core.playback.play(tl_tracks[0]) - self.core.playback.play(tl_tracks[1]) - # TODO: we really want to check that the track was marked unplayable - # and that next was called. This is just an indirect way of checking - # this :( - self.assertEqual(self.core.playback.state, core.PlaybackState.STOPPED) + assert self.core.playback.get_current_tl_track() == tl_tracks[1] + + def test_pause_play_skips_to_next_on_unplayable_track(self): + """Checks that we handle backend.change_track failing.""" + self.playback2.change_track.return_value.get.return_value = False + + self.core.tracklist.clear() + self.core.tracklist.add(uris=self.uris[-3:]) + tl_tracks = self.core.tracklist.tl_tracks + + self.core.playback.pause() + self.core.playback._set_current_tl_track(tl_tracks[0]) + self.core.playback.next() + self.core.playback.play(self.core.playback.get_current_tl_track()) + assert self.core.playback.get_current_tl_track() == tl_tracks[2] + + def test_pause_resume_skips_to_next_on_unplayable_track(self): + """Checks that we handle backend.change_track failing.""" + self.playback2.change_track.return_value.get.return_value = False + + self.core.tracklist.clear() + self.core.tracklist.add(uris=self.uris[-3:]) + tl_tracks = self.core.tracklist.tl_tracks + + self.core.playback.pause() + self.core.playback._set_current_tl_track(tl_tracks[0]) + self.core.playback.next() + self.core.playback.resume() + assert self.core.playback.get_current_tl_track() == tl_tracks[2] @mock.patch( 'mopidy.core.playback.listener.CoreListener', spec=core.CoreListener) From aa140beda44a638b0fc1c72ed06af847609e4ef9 Mon Sep 17 00:00:00 2001 From: jcass Date: Sat, 2 Jan 2016 15:56:04 +0200 Subject: [PATCH 20/45] Fix changelog merge conflict. --- docs/changelog.rst | 6 ------ 1 file changed, 6 deletions(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index eb389166..3957ce83 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -31,12 +31,6 @@ Bug fix release. - MPD: Don't return tracks with empty URIs. (Partly fixes: :issue:`1340`, PR: :issue:`1343`) -- Main: Catch errors when loading :confval:`logging/config_file`. (Fixes: - :issue:`1320`) - -- MPD: Don't return tracks with empty URIs. (Partly fixes: :issue:`1340`, PR: - :issue:`1343`) - v1.1.1 (2015-09-14) =================== From 75656b9b3dbc4307f2e09687bf60a16636a3e17f Mon Sep 17 00:00:00 2001 From: Stein Magnus Jodal Date: Sun, 3 Jan 2016 21:07:10 +0100 Subject: [PATCH 21/45] core: Address review comments --- mopidy/core/library.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mopidy/core/library.py b/mopidy/core/library.py index 2a4ee87d..240de619 100644 --- a/mopidy/core/library.py +++ b/mopidy/core/library.py @@ -236,6 +236,8 @@ class LibraryController(object): result = future.get() if result is not None: validation.check_instances(result, models.Track) + # TODO Consider making Track.uri field mandatory, and + # then remove this filtering of tracks without URIs. results[u] = [r for r in result if r.uri] if uri: From 31ec804d7e8a8ba22420b13e99f27a766fffad18 Mon Sep 17 00:00:00 2001 From: jcass Date: Mon, 4 Jan 2016 00:05:10 +0200 Subject: [PATCH 22/45] core: Address review comments: remove unnecessary call to get_current_tl_track() --- mopidy/core/playback.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/mopidy/core/playback.py b/mopidy/core/playback.py index a11b646d..dd484ff8 100644 --- a/mopidy/core/playback.py +++ b/mopidy/core/playback.py @@ -217,8 +217,7 @@ class PlaybackController(object): self.core.tracklist._mark_playing(tl_track) self.core.history._add_track(tl_track.track) else: - self.core.tracklist._mark_unplayable( - self.get_current_tl_track()) + self.core.tracklist._mark_unplayable(tl_track) if on_error_step == 1: # TODO: can cause an endless loop for single track # repeat. From 456dbe6c3a42344195e9526182c7a6fde86cee16 Mon Sep 17 00:00:00 2001 From: Stein Magnus Jodal Date: Sun, 3 Jan 2016 23:41:34 +0100 Subject: [PATCH 23/45] tests: Fix mock usage --- tests/core/test_playback.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/core/test_playback.py b/tests/core/test_playback.py index d46bdd52..46564860 100644 --- a/tests/core/test_playback.py +++ b/tests/core/test_playback.py @@ -39,7 +39,7 @@ class CorePlaybackTest(unittest.TestCase): # A backend without the optional playback provider self.backend3 = mock.Mock() self.backend3.uri_schemes.get.return_value = ['dummy3'] - self.backend3.has_playback().get.return_value = False + self.backend3.has_playback.return_value.get.return_value = False # A backend for which 'change_track' fails self.backend4 = mock.Mock() From 7589d37f15822340fb526eb8388f954bc97c6c61 Mon Sep 17 00:00:00 2001 From: Stein Magnus Jodal Date: Sun, 3 Jan 2016 23:41:46 +0100 Subject: [PATCH 24/45] docs: Cleanup changelog --- docs/changelog.rst | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index 6c878c4b..fbbb6f69 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -9,6 +9,20 @@ v1.1.2 (UNRELEASED) Bug fix release. +- Main: Catch errors when loading :confval:`logging/config_file`. (Fixes: + :issue:`1320`) + +- Core: If changing to another track while + the player is paused, the new track would not be added to the history or + marked as currently playing. (Fixes: :issue:`1352`, PR: :issue:`1356`) + +- Core: Skips over unplayable tracks if the user attempts to change tracks + while paused, like we already did if in playing state. (Fixes :issue:`1378`, + PR: :issue:`1379`) + +- Core: Make :meth:`~mopidy.core.LibraryController.lookup` ignore tracks with + empty URIs. (Partly fixes: :issue:`1340`, PR: :issue:`1381`) + - 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: @@ -19,21 +33,9 @@ Bug fix release. - MPD: Notify idling clients when a seek is performed. (Fixes: :issue:`1331`) -- Core: Fix bug in playback controller. If changing to another track while - the player is paused, the new track would not be added to the history or - marked as currently playing. (Fixes: :issue:`1352`) Also skips over - unplayable tracks if the user attempts to change tracks while paused. - (Fixes :issue:`1378`). - -- Main: Catch errors when loading :confval:`logging/config_file`. (Fixes: - :issue:`1320`) - - MPD: Don't return tracks with empty URIs. (Partly fixes: :issue:`1340`, PR: :issue:`1343`) -- Core: Make :meth:`~mopidy.core.LibraryController.lookup` ignore tracks with - empty URIs. (Partly fixes: :issue:`1340`, PR: :issue:`1381`) - v1.1.1 (2015-09-14) =================== From c2c2aa032254481e8ebf1693bb08e5f7a4e94421 Mon Sep 17 00:00:00 2001 From: jcass Date: Sat, 26 Dec 2015 15:28:07 +0200 Subject: [PATCH 25/45] Handle missing or empty 'port' configuration parameter. (cherry picked from commit 3488e6442de65254e47961edb64d3b28f7212b51) --- mopidy/httpclient.py | 4 ++-- tests/test_httpclient.py | 3 +++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/mopidy/httpclient.py b/mopidy/httpclient.py index 682a78bd..6be127ca 100644 --- a/mopidy/httpclient.py +++ b/mopidy/httpclient.py @@ -21,8 +21,8 @@ def format_proxy(proxy_config, auth=True): if not proxy_config.get('hostname'): return None - port = proxy_config.get('port', 80) - if port < 0: + port = proxy_config.get('port') + if not port or port < 0: port = 80 if proxy_config.get('username') and proxy_config.get('password') and auth: diff --git a/tests/test_httpclient.py b/tests/test_httpclient.py index 63591f80..30f03d8d 100644 --- a/tests/test_httpclient.py +++ b/tests/test_httpclient.py @@ -9,6 +9,7 @@ from mopidy import httpclient @pytest.mark.parametrize("config,expected", [ ({}, None), + ({'hostname': ''}, None), ({'hostname': 'proxy.lan'}, 'http://proxy.lan:80'), ({'scheme': None, 'hostname': 'proxy.lan'}, 'http://proxy.lan:80'), ({'scheme': 'https', 'hostname': 'proxy.lan'}, 'https://proxy.lan:80'), @@ -16,6 +17,8 @@ from mopidy import httpclient ({'password': 'pass', 'hostname': 'proxy.lan'}, 'http://proxy.lan:80'), ({'hostname': 'proxy.lan', 'port': 8080}, 'http://proxy.lan:8080'), ({'hostname': 'proxy.lan', 'port': -1}, 'http://proxy.lan:80'), + ({'hostname': 'proxy.lan', 'port': None}, 'http://proxy.lan:80'), + ({'hostname': 'proxy.lan', 'port': ''}, 'http://proxy.lan:80'), ({'username': 'user', 'password': 'pass', 'hostname': 'proxy.lan'}, 'http://user:pass@proxy.lan:80'), ]) From 38307a571756146debadd9f130893e8769400c13 Mon Sep 17 00:00:00 2001 From: Stein Magnus Jodal Date: Tue, 5 Jan 2016 21:18:57 +0100 Subject: [PATCH 26/45] docs: Add PR#1371 to changelog --- docs/changelog.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/changelog.rst b/docs/changelog.rst index fbbb6f69..a67e6a15 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -36,6 +36,9 @@ Bug fix release. - MPD: Don't return tracks with empty URIs. (Partly fixes: :issue:`1340`, PR: :issue:`1343`) +- Proxy: Handle case where :confval:`proxy/port` is either missing from config + or set to an empty string. (PR: :issue:`1371`) + v1.1.1 (2015-09-14) =================== From 3d6270ad3772d464aac334faa27209ef690da38c Mon Sep 17 00:00:00 2001 From: Stein Magnus Jodal Date: Sun, 10 Jan 2016 07:41:50 +0100 Subject: [PATCH 27/45] docs: Document model's attributes Fixes #1385 --- docs/api/models.rst | 8 ++++++++ mopidy/models/__init__.py | 8 ++++---- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/docs/api/models.rst b/docs/api/models.rst index 27c7647f..cc8518ba 100644 --- a/docs/api/models.rst +++ b/docs/api/models.rst @@ -55,20 +55,28 @@ Data model API :synopsis: Data model API .. autoclass:: mopidy.models.Ref + :members: .. autoclass:: mopidy.models.Track + :members: .. autoclass:: mopidy.models.Album + :members: .. autoclass:: mopidy.models.Artist + :members: .. autoclass:: mopidy.models.Playlist + :members: .. autoclass:: mopidy.models.Image + :members: .. autoclass:: mopidy.models.TlTrack + :members: .. autoclass:: mopidy.models.SearchResult + :members: Data model helpers diff --git a/mopidy/models/__init__.py b/mopidy/models/__init__.py index 7afa2db8..1ea23be0 100644 --- a/mopidy/models/__init__.py +++ b/mopidy/models/__init__.py @@ -348,14 +348,14 @@ class SearchResult(ValidatedImmutableObject): :type albums: list of :class:`Album` elements """ - # The search result URI. Read-only. + #: The search result URI. Read-only. uri = fields.URI() - # The tracks matching the search query. Read-only. + #: The tracks matching the search query. Read-only. tracks = fields.Collection(type=Track, container=tuple) - # The artists matching the search query. Read-only. + #: The artists matching the search query. Read-only. artists = fields.Collection(type=Artist, container=tuple) - # The albums matching the search query. Read-only. + #: The albums matching the search query. Read-only. albums = fields.Collection(type=Album, container=tuple) From 3ff120a0e60f941e16a51c48aea1a1a59e72f121 Mon Sep 17 00:00:00 2001 From: Thomas Adamcik Date: Fri, 4 Dec 2015 23:38:55 +0100 Subject: [PATCH 28/45] listener: Try and protect actors against "bad" events (cherry picked from commit aa010e03e995d68913a071793f1847a1ac8cd356) --- mopidy/core/listener.py | 3 ++- mopidy/listener.py | 6 +++++- mopidy/mpd/dispatcher.py | 1 + 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/mopidy/core/listener.py b/mopidy/core/listener.py index d95bd491..5b7ea221 100644 --- a/mopidy/core/listener.py +++ b/mopidy/core/listener.py @@ -31,7 +31,8 @@ class CoreListener(listener.Listener): :type event: string :param kwargs: any other arguments to the specific event handlers """ - getattr(self, event)(**kwargs) + # Just delegate to parent, entry mostly for docs. + super(CoreListener, self).on_event(event, **kwargs) def track_playback_paused(self, tl_track, time_position): """ diff --git a/mopidy/listener.py b/mopidy/listener.py index 35bd8b73..7b129955 100644 --- a/mopidy/listener.py +++ b/mopidy/listener.py @@ -51,4 +51,8 @@ class Listener(object): :type event: string :param kwargs: any other arguments to the specific event handlers """ - getattr(self, event)(**kwargs) + try: + getattr(self, event)(**kwargs) + except Exception: + # Ensure we don't crash the actor due to "bad" events. + logger.exception('Triggering event failed: %s', event) diff --git a/mopidy/mpd/dispatcher.py b/mopidy/mpd/dispatcher.py index 099a2f18..175d8b32 100644 --- a/mopidy/mpd/dispatcher.py +++ b/mopidy/mpd/dispatcher.py @@ -47,6 +47,7 @@ class MpdDispatcher(object): return self._call_next_filter(request, response, filter_chain) def handle_idle(self, subsystem): + # TODO: validate against mopidy/mpd/protocol/status.SUBSYSTEMS self.context.events.add(subsystem) subsystems = self.context.subscriptions.intersection( From 70b4851a2991c79481108f362cac22df404fe6d7 Mon Sep 17 00:00:00 2001 From: Stein Magnus Jodal Date: Sun, 10 Jan 2016 07:51:56 +0100 Subject: [PATCH 29/45] docs: Add fix for bad event crash to changelog Fixes #1383 --- docs/changelog.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/changelog.rst b/docs/changelog.rst index a67e6a15..e8135398 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -23,6 +23,9 @@ Bug fix release. - Core: Make :meth:`~mopidy.core.LibraryController.lookup` ignore tracks with empty URIs. (Partly fixes: :issue:`1340`, PR: :issue:`1381`) +- Core: Fix crash if backends emits events with wrong names or arguments. + (Fixes: :issue:`1383`) + - 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: From 2a01760af37fac43cf14a0c29ffd559a19ca5789 Mon Sep 17 00:00:00 2001 From: Stein Magnus Jodal Date: Sat, 16 Jan 2016 20:26:56 +0100 Subject: [PATCH 30/45] docs: Update Raspberry Pi howto Fixes #1326 --- .../raspberry-pi-by-jwrodgers.jpg | Bin 52681 -> 0 bytes docs/installation/raspberrypi.rst | 189 +++++++----------- docs/installation/raspberrypi2.jpg | Bin 0 -> 97346 bytes 3 files changed, 73 insertions(+), 116 deletions(-) delete mode 100644 docs/installation/raspberry-pi-by-jwrodgers.jpg create mode 100644 docs/installation/raspberrypi2.jpg diff --git a/docs/installation/raspberry-pi-by-jwrodgers.jpg b/docs/installation/raspberry-pi-by-jwrodgers.jpg deleted file mode 100644 index d093bb8836a30f2d166c202bdf37ec3c06a910d7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 52681 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. As of -January 2013, Mopidy will run with Spotify support on both the armel -(soft-float) and armhf (hard-float) architectures, which includes the Raspbian -distribution. +Mopidy runs on all versions of `Raspberry Pi `_. +However, note that Raspberry Pi 2 B's CPU is approximately six times as +powerful as Raspberry Pi 1 and Raspberry Pi Zero, so Mopidy will be more joyful +to use on a Raspberry Pi 2. -.. image:: raspberry-pi-by-jwrodgers.jpg +.. image:: raspberrypi2.jpg :width: 640 - :height: 427 + :height: 363 .. _raspi-wheezy: -How to for Raspbian "wheezy" and Debian "wheezy" -================================================ +How to for Raspbian Jessie +========================== -This guide applies for both: +#. Download the latest Jessie or Jessie Lite disk image from + http://www.raspberrypi.org/downloads/raspbian/. -- Raspbian "wheezy" for armhf (hard-float), and -- Debian "wheezy" for armel (soft-float) + If you're only using your Pi for Mopidy, go with Jessie Lite as you won't + need the full graphical desktop included in the Jessie image. -If you don't know which one to select, go for the armhf variant, as it'll give -you a lot better performance. +#. Flash the Raspbian image you downloaded to your SD card. -#. Download the latest "wheezy" disk image from - http://www.raspberrypi.org/downloads/. This was last tested with the images - from 2013-05-25 for armhf and 2013-05-29 for armel. + See the `Raspberry Pi installation docs + `_ + for instructions. -#. Flash the OS image to your SD card. See - http://elinux.org/RPi_Easy_SD_Card_Setup for help. +#. If you connect a monitor and a keyboard, you'll see that the Pi boots right + into the ``raspi-config`` tool. -#. If you have an SD card that's >2 GB, you don't have to resize the file - systems on another computer. Just boot up your Raspberry Pi with the - unaltered partions, and it will boot right into the ``raspi-config`` tool, - which will let you grow the root file system to fill the SD card. This tool - will also allow you do other useful stuff, like turning on the SSH server. + If you boot with only a network cable connected, you'll have to find the IP + address of the Pi yourself, e.g. by looking in the client list on your + router/DHCP server. When you have found the Pi's IP address, you can SSH to + the IP address and login with the user ``pi`` and password ``raspberry``. + Once logged in, run ``sudo raspi-config`` to start the config tool as the + ``root`` user. -#. You can login to the default user using username ``pi`` and password - ``raspberry``. To become root, just enter ``sudo -i``. +#. Use the ``raspi-config`` tool to setup the basics of your Pi. You might want + to do one or more of the following: -#. To avoid a couple of potential problems with Mopidy, turn on IPv6 support: + - Expand the file system to fill the SD card. + - Change the password of the ``pi`` user. + - Change the time zone. - - Load the IPv6 kernel module now:: + Under "Advanced Options": - sudo modprobe ipv6 + - Set a hostname. + - Enable SSH if not already enabled. + - If your will use HDMI for display and 3.5mm jack for audio, force the + audio output to the 3.5mm jack. By default it will use HDMI for audio + output if an HDMI cable is connected and the 3.5mm jack if not. - - Add ``ipv6`` to ``/etc/modules`` to ensure the IPv6 kernel module is - loaded on boot:: + Once done, select "Finish" and restart your Pi. - echo ipv6 | sudo tee -a /etc/modules + If you want to change any settings later, you can simply rerun ``sudo + raspi-config``. -#. Since I have a HDMI cable connected, but want the sound on the analog sound - connector, I have to run:: - - sudo amixer cset numid=3 1 - - to force it to use analog output. ``1`` means analog, ``0`` means auto, and - is the default, while ``2`` means HDMI. You can test sound output - independent of Mopidy by running:: - - aplay /usr/share/sounds/alsa/Front_Center.wav - - If you hear a voice saying "Front Center", then your sound is working. - - To make the change to analog output stick, you can add the ``amixer`` - command to e.g. ``/etc/rc.local``, which will be executed when the system is - booting. +#. Once you've rebooted and has logged in as the ``pi`` user, you can enter + ``sudo -i`` to become ``root``. #. Install Mopidy and its dependencies as described in :ref:`debian-install`. @@ -79,76 +72,35 @@ you a lot better performance. starting at boot. -Appendix A: Fixing audio quality issues -======================================= +Testing sound output +==================== -As of about April 2013 the following steps should resolve any audio -issues for HDMI and analog without the use of an external USB sound -card. +You can test sound output independent of Mopidy by running:: -#. Ensure your system is up to date. On Debian based systems run:: + aplay /usr/share/sounds/alsa/Front_Center.wav - sudo apt-get update - sudo apt-get dist-upgrade +If you hear a voice saying "Front Center", then your sound is working. -#. Ensure you have a new enough firmware. On Debian based systems - `rpi-update `_ - can be used. +If you want to change your audio output setting, simply rerun ``sudo +raspi-config``. Alternatively, you can change the audio output setting +directly by running: -#. Update either ``~/.asoundrc`` or ``/etc/asound.conf`` to the - following:: - - pcm.!default { - type hw - card 0 - } - ctl.!default { - type hw - card 0 - } - - Note that if you have an ``~/.asoundrc`` it will overide any global - settings from ``/etc/asound.conf``. - -#. For Mopidy to output audio directly to ALSA, instead of Jack which - GStreamer usually defaults to on Raspberry Pi, install the - ``gstreamer0.10-alsa`` package:: - - sudo apt-get install gstreamer0.10-alsa - - Then update your ``~/.config/mopidy/mopidy.conf`` to contain:: - - [audio] - output = alsasink - -Following these steps you should be able to get crackle free sound on either -HDMI or analog. Note that you might need to ensure that PulseAudio is no longer -running to get this working nicely. - -This recipe has been confirmed as working by a number of users on our issue -tracker and IRC. As a reference, the following versions where used for testing -this, however all newer and some older version are likely to work as we have -not determined the exact revision that fixed this:: - - $ uname -a - Linux raspberrypi 3.6.11+ #408 PREEMPT Wed Apr 10 20:33:39 BST 2013 armv6l GNU/Linux - - $ /opt/vc/bin/vcgencmd version - Apr 25 2013 01:07:36 - Copyright (c) 2012 Broadcom - version 386589 (release) - -The only remaining known issue is a slight gap in playback at track changes -this is likely due to gapless playback not being implemented and is being -worked on irrespective of Raspberry Pi related work. +- Auto (HDMI if connected, else 3.5mm jack): ``sudo amixer cset numid=3 0`` +- Use 3.5mm jack: ``sudo amixer cset numid=3 1`` +- Use HDMI: ``sudo amixer cset numid=3 2`` -Appendix B: Raspbmc not booting -=============================== +Workarounds for Raspbmc +======================= -Due to a dependency version problem where XBMC uses another version of libtag -than what Debian originally ships with, you might have to make some minor -changes for Raspbmc to start properly after installing Mopidy. +.. note:: + + This section is probably outdated. Get in contact if you can confirm that + this is no longer an issue or if this section needs other updates. + +Due to a dependency version problem where XBMC uses another version of +``libtag`` than what Debian originally ships with, you might have to make some +minor changes for Raspbmc to start properly after installing Mopidy. If you notice that XBMC is not starting but gets stuck in a loop, you need to make the following changes:: @@ -175,11 +127,16 @@ Please note that if you're running Xbian or another XBMC distribution these instructions might vary for your system. -Appendix C: Installation on XBian -================================= +Workarounds for XBian +===================== -Similar to the Raspbmc issue outlined in Appendix B, it's not possible to -install Mopidy on XBian without first resolving a dependency problem between +.. note:: + + This section is probably outdated. Get in contact if you can confirm that + this is no longer an issue or if this section needs other updates. + +Similar to the Raspbmc issue outlined above, it's not possible to install +Mopidy on XBian without first resolving a dependency problem between ``gstreamer0.10-plugins-good`` and ``libtag1c2a``. More information can be found in `this post `_. diff --git a/docs/installation/raspberrypi2.jpg b/docs/installation/raspberrypi2.jpg new file mode 100644 index 0000000000000000000000000000000000000000..8af91864176bcbeb0662e4624cf56d14342345ac GIT binary patch literal 97346 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 S{<^`{{{Z-Z>OYV7U;o)Qe0X#K literal 0 HcmV?d00001 From 18b609fa6ed3c06c0dc3156cbb7409c9494c0bc2 Mon Sep 17 00:00:00 2001 From: Stein Magnus Jodal Date: Sun, 17 Jan 2016 07:55:34 +0100 Subject: [PATCH 31/45] flake8: Fix compat with pep8 1.7.0 --- mopidy/audio/scan.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/mopidy/audio/scan.py b/mopidy/audio/scan.py index ca2c308c..fd5d2d49 100644 --- a/mopidy/audio/scan.py +++ b/mopidy/audio/scan.py @@ -141,8 +141,9 @@ def _process(pipeline, timeout_ms): have_audio = False missing_message = None - types = (gst.MESSAGE_ELEMENT | gst.MESSAGE_APPLICATION | gst.MESSAGE_ERROR - | gst.MESSAGE_EOS | gst.MESSAGE_ASYNC_DONE | gst.MESSAGE_TAG) + types = ( + gst.MESSAGE_ELEMENT | gst.MESSAGE_APPLICATION | gst.MESSAGE_ERROR | + gst.MESSAGE_EOS | gst.MESSAGE_ASYNC_DONE | gst.MESSAGE_TAG) previous = clock.get_time() while timeout > 0: From 0ac77ebb475129e3530fc8cf6936200389460bc0 Mon Sep 17 00:00:00 2001 From: Stein Magnus Jodal Date: Sun, 17 Jan 2016 21:06:53 +0100 Subject: [PATCH 32/45] docs: Remove Raspbmc and XBian workarounds Ref https://github.com/mopidy/mopidy/issues/1326#issuecomment-172323148 Raspbmc is replaced by OSMC that works with Mopidy without conflicts. XBian I'm not sure about, but the workaround is still searchable in older version of our docs, and we'll soon be moving to GStreamer 1.x, probably affecting the dep on libtag. --- docs/installation/raspberrypi.rst | 59 ------------------------------- 1 file changed, 59 deletions(-) diff --git a/docs/installation/raspberrypi.rst b/docs/installation/raspberrypi.rst index 386ffb30..c2fd10ad 100644 --- a/docs/installation/raspberrypi.rst +++ b/docs/installation/raspberrypi.rst @@ -88,62 +88,3 @@ directly by running: - Auto (HDMI if connected, else 3.5mm jack): ``sudo amixer cset numid=3 0`` - Use 3.5mm jack: ``sudo amixer cset numid=3 1`` - Use HDMI: ``sudo amixer cset numid=3 2`` - - -Workarounds for Raspbmc -======================= - -.. note:: - - This section is probably outdated. Get in contact if you can confirm that - this is no longer an issue or if this section needs other updates. - -Due to a dependency version problem where XBMC uses another version of -``libtag`` than what Debian originally ships with, you might have to make some -minor changes for Raspbmc to start properly after installing Mopidy. - -If you notice that XBMC is not starting but gets stuck in a loop, -you need to make the following changes:: - - sudo ln -sf /home/pi/.xbmc-current/xbmc-bin/lib/xbmc/system/libtag.so.1 \ - /usr/lib/arm-linux-gnueabihf/libtag.so.1 - -However, this will not persist the changes. To persist the changes edit -:file:`/etc/ld.so.conf.d/arm-linux-gnueabihf.conf` and add the following at the -top:: - - /home/pi/.xbmc-current/xbmc-bin/lib/xbmc/system - -It's very important to add it at the top of the file as this indicates the -priority of the folder in which to look for shared libraries. - -XBMC doesn't play nicely with the system wide installed version of libtag that -got installed together with Mopidy, but rather vendors in its own version. - -More info about this issue can be found in `this post -`_. - -Please note that if you're running Xbian or another XBMC distribution these -instructions might vary for your system. - - -Workarounds for XBian -===================== - -.. note:: - - This section is probably outdated. Get in contact if you can confirm that - this is no longer an issue or if this section needs other updates. - -Similar to the Raspbmc issue outlined above, it's not possible to install -Mopidy on XBian without first resolving a dependency problem between -``gstreamer0.10-plugins-good`` and ``libtag1c2a``. More information can be -found in `this post -`_. - -Run the following commands to remedy this and then install Mopidy as normal:: - - cd /tmp - wget http://apt.xbian.org/pool/stable/rpi-wheezy/l/libtag1c2a/libtag1c2a_1.7.2-1_armhf.deb - sudo dpkg -i libtag1c2a_1.7.2-1_armhf.deb - rm libtag1c2a_1.7.2-1_armhf.deb From f15b4e732716087a6e40787ab2c718d7a0c8f99b Mon Sep 17 00:00:00 2001 From: Stein Magnus Jodal Date: Sun, 17 Jan 2016 22:14:43 +0100 Subject: [PATCH 33/45] mpd: Move setvol tests to its own test case --- tests/mpd/protocol/test_playback.py | 77 +++++++++++++++-------------- 1 file changed, 40 insertions(+), 37 deletions(-) diff --git a/tests/mpd/protocol/test_playback.py b/tests/mpd/protocol/test_playback.py index b9adb646..470491e9 100644 --- a/tests/mpd/protocol/test_playback.py +++ b/tests/mpd/protocol/test_playback.py @@ -80,41 +80,6 @@ class PlaybackOptionsHandlerTest(protocol.BaseTestCase): self.assertTrue(self.core.tracklist.repeat.get()) self.assertInResponse('OK') - def test_setvol_below_min(self): - self.send_request('setvol "-10"') - self.assertEqual(0, self.core.mixer.get_volume().get()) - self.assertInResponse('OK') - - def test_setvol_min(self): - self.send_request('setvol "0"') - self.assertEqual(0, self.core.mixer.get_volume().get()) - self.assertInResponse('OK') - - def test_setvol_middle(self): - self.send_request('setvol "50"') - self.assertEqual(50, self.core.mixer.get_volume().get()) - self.assertInResponse('OK') - - def test_setvol_max(self): - self.send_request('setvol "100"') - self.assertEqual(100, self.core.mixer.get_volume().get()) - self.assertInResponse('OK') - - def test_setvol_above_max(self): - self.send_request('setvol "110"') - self.assertEqual(100, self.core.mixer.get_volume().get()) - self.assertInResponse('OK') - - def test_setvol_plus_is_ignored(self): - self.send_request('setvol "+10"') - self.assertEqual(10, self.core.mixer.get_volume().get()) - self.assertInResponse('OK') - - def test_setvol_without_quotes(self): - self.send_request('setvol 50') - self.assertEqual(50, self.core.mixer.get_volume().get()) - self.assertInResponse('OK') - def test_single_off(self): self.send_request('single "0"') self.assertFalse(self.core.tracklist.single.get()) @@ -451,9 +416,47 @@ class PlaybackControlHandlerTest(protocol.BaseTestCase): self.assertInResponse('OK') -class PlaybackOptionsHandlerNoneMixerTest(protocol.BaseTestCase): +class VolumeTest(protocol.BaseTestCase): + + def test_setvol_below_min(self): + self.send_request('setvol "-10"') + self.assertEqual(0, self.core.mixer.get_volume().get()) + self.assertInResponse('OK') + + def test_setvol_min(self): + self.send_request('setvol "0"') + self.assertEqual(0, self.core.mixer.get_volume().get()) + self.assertInResponse('OK') + + def test_setvol_middle(self): + self.send_request('setvol "50"') + self.assertEqual(50, self.core.mixer.get_volume().get()) + self.assertInResponse('OK') + + def test_setvol_max(self): + self.send_request('setvol "100"') + self.assertEqual(100, self.core.mixer.get_volume().get()) + self.assertInResponse('OK') + + def test_setvol_above_max(self): + self.send_request('setvol "110"') + self.assertEqual(100, self.core.mixer.get_volume().get()) + self.assertInResponse('OK') + + def test_setvol_plus_is_ignored(self): + self.send_request('setvol "+10"') + self.assertEqual(10, self.core.mixer.get_volume().get()) + self.assertInResponse('OK') + + def test_setvol_without_quotes(self): + self.send_request('setvol 50') + self.assertEqual(50, self.core.mixer.get_volume().get()) + self.assertInResponse('OK') + + +class VolumeWithNoMixerTest(protocol.BaseTestCase): enable_mixer = False - def test_setvol_max_error(self): + def test_setvol_without_mixer_fails(self): self.send_request('setvol "100"') self.assertInResponse('ACK [52@0] {setvol} problems setting volume') From 14444fe24f1e2990bad45e0d239fbdcf87a69ede Mon Sep 17 00:00:00 2001 From: Stein Magnus Jodal Date: Sun, 17 Jan 2016 22:28:23 +0100 Subject: [PATCH 34/45] mpd: Add volume command --- docs/changelog.rst | 3 +++ mopidy/mpd/protocol/playback.py | 24 +++++++++++++++++++ tests/mpd/protocol/test_playback.py | 36 +++++++++++++++++++++++++++++ 3 files changed, 63 insertions(+) diff --git a/docs/changelog.rst b/docs/changelog.rst index e8135398..135da15a 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -39,6 +39,9 @@ Bug fix release. - MPD: Don't return tracks with empty URIs. (Partly fixes: :issue:`1340`, PR: :issue:`1343`) +- MPD: Add ``volume`` command that was reintroduced, though still as a + deprecated command, in MPD 0.18 and is in use by some clients like mpc. + - Proxy: Handle case where :confval:`proxy/port` is either missing from config or set to an empty string. (PR: :issue:`1371`) diff --git a/mopidy/mpd/protocol/playback.py b/mopidy/mpd/protocol/playback.py index 333e1ccb..c9dcc341 100644 --- a/mopidy/mpd/protocol/playback.py +++ b/mopidy/mpd/protocol/playback.py @@ -426,3 +426,27 @@ def stop(context): Stops playing. """ context.core.playback.stop() + + +@protocol.commands.add('volume', change=protocol.INT) +def volume(context, change): + """ + *musicpd.org, playback section:* + + ``volume {CHANGE}`` + + Changes volume by amount ``CHANGE``. + + Note: ``volume`` is deprecated, use ``setvol`` instead. + """ + if change < -100 or change > 100: + raise exceptions.MpdArgError('Invalid volume value', command='volume') + + old_volume = context.core.mixer.get_volume().get() + if old_volume is None: + raise exceptions.MpdSystemError('problems setting volume') + + new_volume = min(max(0, old_volume + change), 100) + success = context.core.mixer.set_volume(new_volume).get() + if not success: + raise exceptions.MpdSystemError('problems setting volume') diff --git a/tests/mpd/protocol/test_playback.py b/tests/mpd/protocol/test_playback.py index 470491e9..de02ae36 100644 --- a/tests/mpd/protocol/test_playback.py +++ b/tests/mpd/protocol/test_playback.py @@ -453,6 +453,38 @@ class VolumeTest(protocol.BaseTestCase): self.assertEqual(50, self.core.mixer.get_volume().get()) self.assertInResponse('OK') + def test_volume_plus(self): + self.core.mixer.set_volume(50) + + self.send_request('volume +20') + + self.assertEqual(70, self.core.mixer.get_volume().get()) + self.assertInResponse('OK') + + def test_volume_minus(self): + self.core.mixer.set_volume(50) + + self.send_request('volume -20') + + self.assertEqual(30, self.core.mixer.get_volume().get()) + self.assertInResponse('OK') + + def test_volume_less_than_minus_100(self): + self.core.mixer.set_volume(50) + + self.send_request('volume -110') + + self.assertEqual(50, self.core.mixer.get_volume().get()) + self.assertInResponse('ACK [2@0] {volume} Invalid volume value') + + def test_volume_more_than_plus_100(self): + self.core.mixer.set_volume(50) + + self.send_request('volume +110') + + self.assertEqual(50, self.core.mixer.get_volume().get()) + self.assertInResponse('ACK [2@0] {volume} Invalid volume value') + class VolumeWithNoMixerTest(protocol.BaseTestCase): enable_mixer = False @@ -460,3 +492,7 @@ class VolumeWithNoMixerTest(protocol.BaseTestCase): def test_setvol_without_mixer_fails(self): self.send_request('setvol "100"') self.assertInResponse('ACK [52@0] {setvol} problems setting volume') + + def test_volume_without_mixer_failes(self): + self.send_request('volume +100') + self.assertInResponse('ACK [52@0] {volume} problems setting volume') From 450b1d0f7c8039283abe2f37a6f0f2b1e65e4ef4 Mon Sep 17 00:00:00 2001 From: Stein Magnus Jodal Date: Mon, 18 Jan 2016 21:21:35 +0100 Subject: [PATCH 35/45] docs: Remove section on missing Debian-packaged extensions For apt.mopidy.com, none of this is an issue. For Ubuntu: - none of this is an issue in 15.10 or 16.04 LTS. - 15.04 is soon out of support. - 14.04 LTS is old enough that you'll have to read older docs to expect facts matching your installation. For Debian: - none of this is an issue in sid or stretch. - Jessie this might be an issue, but the same sentiment as for Ubuntu 14.04 LTS applies. Fixes #1399 --- docs/debian.rst | 15 ------------- docs/installation/debian.rst | 41 ------------------------------------ 2 files changed, 56 deletions(-) diff --git a/docs/debian.rst b/docs/debian.rst index f761c4b0..d7279567 100644 --- a/docs/debian.rst +++ b/docs/debian.rst @@ -112,18 +112,3 @@ from a regular Mopidy setup you'll want to know about. - You can check if Mopidy is currently running as a system service by running:: sudo service mopidy status - -- Mopidy installed from a Debian package can use Mopidy extensions installed - both from Debian packages and with pip. This has always been the case. - - Mopidy installed with pip can use extensions installed with pip, but - not extensions installed from a Debian package released before August 2015. - This is because the Debian packages used to install extensions into - :file:`/usr/share/mopidy` which is normally not on your ``PYTHONPATH``. - Thus, your pip-installed Mopidy would not find the Debian package-installed - extensions. - - In August 2015, all Mopidy extension Debian packages was modified to install - into :file:`/usr/lib/python2.7/dist-packages`, like any other Python Debian - package. Thus, Mopidy installed with pip can now use extensions installed - from Debian. diff --git a/docs/installation/debian.rst b/docs/installation/debian.rst index a04dfa25..5b4bf841 100644 --- a/docs/installation/debian.rst +++ b/docs/installation/debian.rst @@ -87,44 +87,3 @@ about any other requirements needed for the extension to work properly. For a full list of available Mopidy extensions, including those not installable from apt.mopidy.com, see :ref:`ext`. - - -Missing extensions -================== - -If you've installed a Mopidy extension with pip, restarted Mopidy, and Mopidy -doesn't find the extension, there's probably a simple explanation and solution. - -Mopidy installed with APT can detect and use Mopidy extensions installed with -both APT and pip. APT installs Mopidy as :file:`/usr/bin/mopidy`. - -Mopidy installed with pip can only detect Mopidy extensions installed with pip. -pip usually installs Mopidy as :file:`/usr/local/bin/mopidy`. - -If you have Mopidy installed from both APT and pip, then the pip-installed -Mopidy will probably shadow the APT-installed Mopidy because -:file:`/usr/local/bin` usually has precedence over :file:`/usr/bin` in the -``PATH`` environment variable. To check if this is the case on your system, you -can use ``which`` to see what installation of Mopidy you use when you run -``mopidy`` in your shell:: - - $ which mopidy - /usr/local/bin/mopidy - -If this is the case on your system, the recommended solution is to check that -you have Mopidy installed from APT too:: - - $ /usr/bin/mopidy --version - Mopidy 0.19.5 - -And then uninstall the pip-installed Mopidy:: - - sudo pip uninstall mopidy - -Depending on what shell you use, the shell may still try to use -:file:`/usr/local/bin/mopidy` even if it no longer exists. Check again with -``which mopidy`` what your shell believes is the right ``mopidy`` executable to -run. If the shell is still confused, you may need to restart it, or in the case -of zsh, run ``rehash`` to update the shell. - -For more details on why this works this way, see :ref:`debian`. From e69ea22cdd2da485da3f215c774a38dffc1fcb27 Mon Sep 17 00:00:00 2001 From: Stein Magnus Jodal Date: Mon, 18 Jan 2016 21:40:55 +0100 Subject: [PATCH 36/45] docs: Remove old cruft from Debian package page Part of #1398 --- docs/debian.rst | 47 ++++++++++------------------------------------- 1 file changed, 10 insertions(+), 37 deletions(-) diff --git a/docs/debian.rst b/docs/debian.rst index d7279567..25df7a4b 100644 --- a/docs/debian.rst +++ b/docs/debian.rst @@ -26,30 +26,19 @@ Running as a system service =========================== The Debian package comes with an init script. It starts Mopidy as a system -service running as the ``mopidy`` user, which is created by the package. +service running as the ``mopidy`` user. The user is created by the package. -The Debian package version 0.18.3-1 and older starts Mopidy as a system -service by default. Version 0.18.3-2 and newer asks if you want to run Mopidy -as a system service, defaulting to not doing so. +The Debian package might ask if you want to run Mopidy as a system service. If +you don't get the question, your system is probably configured to ignore +questions at that priority level during installs, and defaults to not enabling +the Mopidy service. -If you're running 0.18.3-2 or newer, and you've changed your mind about whether -or not to run Mopidy as a system service, just run the following command to +If you didn't get the question or if you've changed your mind about whether or +not to run Mopidy as a system service, just run the following command to reconfigure the package:: sudo dpkg-reconfigure mopidy -If you're running 0.18.3-1 or older, and don't want to use the init script to -run Mopidy as a system service, but instead just run Mopidy manually using your -own user, you need to disable the init script and stop Mopidy by running:: - - sudo update-rc.d mopidy disable - sudo service mopidy stop - -This way of disabling the system service is compatible with the improved -0.18.3-2 or newer version of the Debian package, so if you later upgrade to a -newer version, you can change your mind using the ``dpkg-reconfigure`` command -above. - Differences when running as a system service ============================================ @@ -59,20 +48,9 @@ from a regular Mopidy setup you'll want to know about. - All configuration is in :file:`/etc/mopidy`, not in your user's home directory. The main configuration file is :file:`/etc/mopidy/mopidy.conf`. - You can do all your changes in this file. - -- Mopidy extensions installed from Debian packages will sometimes install - additional configuration files in :file:`/usr/share/mopidy/conf.d/`. These - files just provide different defaults for the extension when run as a system - service. You can override anything from :file:`/usr/share/mopidy/conf.d/` in - the :file:`/etc/mopidy/mopidy.conf` configuration file. - - Previously, the extension's default config was installed in - :file:`/etc/mopidy/extensions.d/`. This was removed with the Debian - package mopidy 0.19.4-3. If you have modified any files in - :file:`/etc/mopidy/extensions.d/`, you should redo your modifications in - :file:`/etc/mopidy/mopidy.conf` and delete the - :file:`/etc/mopidy/extensions.d/` directory. + This is the configuration file with the highest priority, so it can override + configs from all other config files. Thus, you can do all your changes in + this file. - The init script runs Mopidy as the ``mopidy`` user. The ``mopidy`` user will need read access to any local music you want Mopidy to play. @@ -97,11 +75,6 @@ from a regular Mopidy setup you'll want to know about. sudo mopidyctl local scan - Previously, you used ``sudo service mopidy run `` instead of - ``mopidyctl``. This was deprecated in Debian package version 0.19.4-3 in - favor of ``mopidyctl``, which also work for systems using systemd instead of - sysvinit and traditional init scripts. - - Mopidy is started, stopped, and restarted just like any other system service:: From 123614ad69bc7c46947e71056e4ac4fcdcae26ad Mon Sep 17 00:00:00 2001 From: Stein Magnus Jodal Date: Mon, 18 Jan 2016 22:05:03 +0100 Subject: [PATCH 37/45] docs: Repurpose Debian pkg page as a service page Fixes #1398 --- docs/debian.rst | 87 ---------------------------------- docs/index.rst | 2 +- docs/installation/debian.rst | 2 +- docs/running.rst | 21 ++------ docs/service.rst | 92 ++++++++++++++++++++++++++++++++++++ 5 files changed, 98 insertions(+), 106 deletions(-) delete mode 100644 docs/debian.rst create mode 100644 docs/service.rst diff --git a/docs/debian.rst b/docs/debian.rst deleted file mode 100644 index 25df7a4b..00000000 --- a/docs/debian.rst +++ /dev/null @@ -1,87 +0,0 @@ -.. _debian: - -*************** -Debian packages -*************** - -The Mopidy Debian package, ``mopidy``, is available from `apt.mopidy.com -`__ as well as from Debian, Ubuntu and other -Debian-based Linux distributions. - -Some extensions are also available from all of these sources, while others, -like Mopidy-Spotify and its dependencies, are only available from -apt.mopidy.com. This may either be temporary until the package is uploaded to -Debian and with time propagates to the other distributions. It may also be more -long term, like in the Mopidy-Spotify case where there is uncertainities around -licensing and distribution of non-free packages. - - -Installation -============ - -See :ref:`debian-install`. - - -Running as a system service -=========================== - -The Debian package comes with an init script. It starts Mopidy as a system -service running as the ``mopidy`` user. The user is created by the package. - -The Debian package might ask if you want to run Mopidy as a system service. If -you don't get the question, your system is probably configured to ignore -questions at that priority level during installs, and defaults to not enabling -the Mopidy service. - -If you didn't get the question or if you've changed your mind about whether or -not to run Mopidy as a system service, just run the following command to -reconfigure the package:: - - sudo dpkg-reconfigure mopidy - - -Differences when running as a system service -============================================ - -If you want to run Mopidy using the init script, there's a few differences -from a regular Mopidy setup you'll want to know about. - -- All configuration is in :file:`/etc/mopidy`, not in your user's home - directory. The main configuration file is :file:`/etc/mopidy/mopidy.conf`. - This is the configuration file with the highest priority, so it can override - configs from all other config files. Thus, you can do all your changes in - this file. - -- The init script runs Mopidy as the ``mopidy`` user. The ``mopidy`` user will - need read access to any local music you want Mopidy to play. - -- To run Mopidy subcommands with the same user and config files as the init - script uses, you can use ``sudo mopidyctl ``. In other words, - where you'll usually run:: - - mopidy config - - You should instead run the following to inspect the system service's - configuration:: - - sudo mopidyctl config - - The same applies to scanning your local music collection. Where you'll - normally run:: - - mopidy local scan - - You should instead run:: - - sudo mopidyctl local scan - -- Mopidy is started, stopped, and restarted just like any other system - service:: - - sudo service mopidy start - sudo service mopidy stop - sudo service mopidy restart - -- You can check if Mopidy is currently running as a system service by running:: - - sudo service mopidy status diff --git a/docs/index.rst b/docs/index.rst index 70d14a73..e6b2da98 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -81,8 +81,8 @@ announcements related to Mopidy and Mopidy extensions. installation/index config running + service troubleshooting - debian .. _ext: diff --git a/docs/installation/debian.rst b/docs/installation/debian.rst index 5b4bf841..728467c4 100644 --- a/docs/installation/debian.rst +++ b/docs/installation/debian.rst @@ -48,7 +48,7 @@ and armhf (compatible with Raspberry Pi 1 and 2). sudo apt-get update sudo apt-get install mopidy -#. Before continuing, make sure you've read the :ref:`debian` section to learn +#. Before continuing, make sure you've read the :ref:`service` section to learn about the differences between running Mopidy as a system service and manually as your own system user. diff --git a/docs/running.rst b/docs/running.rst index e329ccaa..1aa0a657 100644 --- a/docs/running.rst +++ b/docs/running.rst @@ -39,21 +39,8 @@ using ``pkill``:: pkill mopidy -Init scripts -============ +Running as a service +==================== -- The ``mopidy`` package at `apt.mopidy.com `__ comes - with an `sysvinit init script - `_. For - more details, see the :ref:`debian` section of the docs. - -- The ``mopidy`` package in `Arch Linux - `__ comes with a systemd init - script. - -- A blog post by Benjamin Guillet explains how to `Daemonize Mopidy and Launch - It at Login on OS X - `_. - -- Issue :issue:`266` contains a bunch of init scripts for Mopidy, including - Upstart init scripts. +Once you're done exploring Mopidy and want to run it as a proper service, check +out :ref:`service`. diff --git a/docs/service.rst b/docs/service.rst new file mode 100644 index 00000000..a2664d77 --- /dev/null +++ b/docs/service.rst @@ -0,0 +1,92 @@ +.. _service: + +******************** +Running as a service +******************** + +If you want to run Mopidy as a service using either an init script or a systemd +service, there's a few differences from running Mopidy as your own user you'll +want to know about. The following applies to Debian, Ubuntu, Raspbian, and +Arch. Hopefully, other distributions packaging Mopidy will make sure this works +the same way on their distribution. + + +Configuration +============= + +All configuration is in :file:`/etc/mopidy`, not in your user's home directory. + +The main configuration file is :file:`/etc/mopidy/mopidy.conf`. If there are +more than one configuration file, this is the configuration file with the +highest priority, so it can override configs from all other config files. +Thus, you can do all your changes in this file. + + +mopidy User +=========== + +The init script runs Mopidy as the ``mopidy`` user, which is automatically +created when you install the Mopidy package. The ``mopidy`` user will need read +access to any local music you want Mopidy to play. + + +Subcommands +=========== + +To run Mopidy subcommands with the same user and config files as the service +uses, you can use ``sudo mopidyctl ``. In other words, where you'll +usually run:: + + mopidy config + +You should instead run the following to inspect the service's configuration:: + + sudo mopidyctl config + +The same applies to scanning your local music collection. Where you'll normally +run:: + + mopidy local scan + +You should instead run:: + + sudo mopidyctl local scan + + +Service management with systemd +=============================== + +On modern systems using systemd you can enable the Mopidy service by running:: + + sudo systemctl enable mopidy + +This will make Mopidy start when the system boots. + +Mopidy is started, stopped, and restarted just like any other systemd service:: + + sudo systemctl start mopidy + sudo systemctl stop mopidy + sudo systemctl restart mopidy + +You can check if Mopidy is currently running as a service by running:: + + sudo systemctl status mopidy + + +Service management on Debian +============================ + +On Debian systems (both those using systemd and not) you can enable the Mopidy +service by running:: + + sudo dpkg-reconfigure mopidy + +Mopidy can be started, stopped, and restarted using the ``service`` command:: + + sudo service mopidy start + sudo service mopidy stop + sudo service mopidy restart + +You can check if Mopidy is currently running as a service by running:: + + sudo service mopidy status From bd0c88b22b10342dd552a31859be22eb0ddc656d Mon Sep 17 00:00:00 2001 From: Stein Magnus Jodal Date: Mon, 18 Jan 2016 22:06:28 +0100 Subject: [PATCH 38/45] docs: Fix title capitalization --- docs/service.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/service.rst b/docs/service.rst index a2664d77..7e7f7232 100644 --- a/docs/service.rst +++ b/docs/service.rst @@ -22,7 +22,7 @@ highest priority, so it can override configs from all other config files. Thus, you can do all your changes in this file. -mopidy User +mopidy user =========== The init script runs Mopidy as the ``mopidy`` user, which is automatically From 2b0c121e6c7aa960ee3531f409d50bd7c1664f15 Mon Sep 17 00:00:00 2001 From: Stein Magnus Jodal Date: Mon, 18 Jan 2016 22:21:20 +0100 Subject: [PATCH 39/45] docs: Refer to service docs from Arch and Debian install docs --- docs/installation/arch.rst | 3 ++- docs/installation/debian.rst | 7 ++----- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/docs/installation/arch.rst b/docs/installation/arch.rst index c5675403..59928a3a 100644 --- a/docs/installation/arch.rst +++ b/docs/installation/arch.rst @@ -16,7 +16,8 @@ If you are running Arch Linux, you can install Mopidy using the pacman -Syu #. Finally, you need to set a couple of :doc:`config values `, and - then you're ready to :doc:`run Mopidy `. + then you're ready to :doc:`run Mopidy ` or run Mopidy as a + :ref:`service `. Installing extensions diff --git a/docs/installation/debian.rst b/docs/installation/debian.rst index 728467c4..a1ad339e 100644 --- a/docs/installation/debian.rst +++ b/docs/installation/debian.rst @@ -48,12 +48,9 @@ and armhf (compatible with Raspberry Pi 1 and 2). sudo apt-get update sudo apt-get install mopidy -#. Before continuing, make sure you've read the :ref:`service` section to learn - about the differences between running Mopidy as a system service and - manually as your own system user. - #. Finally, you need to set a couple of :doc:`config values `, and then - you're ready to :doc:`run Mopidy `. + you're ready to :doc:`run Mopidy ` or run Mopidy as a + :ref:`service `. When a new release of Mopidy is out, and you can't wait for you system to figure it out for itself, run the following to upgrade right away:: From 0ea3de2c6831e6c47c562da860264ad6e04b0a93 Mon Sep 17 00:00:00 2001 From: Stein Magnus Jodal Date: Mon, 18 Jan 2016 22:21:37 +0100 Subject: [PATCH 40/45] docs: Refer to OS X service from service docs --- docs/installation/osx.rst | 2 ++ docs/service.rst | 6 ++++++ 2 files changed, 8 insertions(+) diff --git a/docs/installation/osx.rst b/docs/installation/osx.rst index e9ce16e3..45c554ea 100644 --- a/docs/installation/osx.rst +++ b/docs/installation/osx.rst @@ -86,6 +86,8 @@ For a full list of available Mopidy extensions, including those not installable from Homebrew, see :ref:`ext`. +.. _osx-service: + Running Mopidy automatically on login ===================================== diff --git a/docs/service.rst b/docs/service.rst index 7e7f7232..e99e1645 100644 --- a/docs/service.rst +++ b/docs/service.rst @@ -90,3 +90,9 @@ Mopidy can be started, stopped, and restarted using the ``service`` command:: You can check if Mopidy is currently running as a service by running:: sudo service mopidy status + + +Service on OS X +=============== + +If you're installing Mopidy on OS X, see :ref:`osx-service`. From 77e2e088fd9db9c3197b60535d3ed8aa9a936bc2 Mon Sep 17 00:00:00 2001 From: Stein Magnus Jodal Date: Mon, 18 Jan 2016 22:34:10 +0100 Subject: [PATCH 41/45] docs: Cleanup changelog --- docs/changelog.rst | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index e8135398..2f54ec67 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -9,12 +9,12 @@ v1.1.2 (UNRELEASED) Bug fix release. -- Main: Catch errors when loading :confval:`logging/config_file`. (Fixes: - :issue:`1320`) +- Main: Catch errors when loading the :confval:`logging/config_file` file. + (Fixes: :issue:`1320`) -- Core: If changing to another track while - the player is paused, the new track would not be added to the history or - marked as currently playing. (Fixes: :issue:`1352`, PR: :issue:`1356`) +- Core: If changing to another track while the player is paused, the new track + would not be added to the history or marked as currently playing. (Fixes: + :issue:`1352`, PR: :issue:`1356`) - Core: Skips over unplayable tracks if the user attempts to change tracks while paused, like we already did if in playing state. (Fixes :issue:`1378`, From b09e667127609c04ac7786fe29e3cdcea589041a Mon Sep 17 00:00:00 2001 From: Stein Magnus Jodal Date: Mon, 18 Jan 2016 22:44:11 +0100 Subject: [PATCH 42/45] mpd: Fix review comments --- docs/changelog.rst | 1 + mopidy/mpd/protocol/playback.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index 135da15a..6e6d0bde 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -41,6 +41,7 @@ Bug fix release. - MPD: Add ``volume`` command that was reintroduced, though still as a deprecated command, in MPD 0.18 and is in use by some clients like mpc. + (Fixes: :issue:`1393`, PR: :issue:`1397`) - Proxy: Handle case where :confval:`proxy/port` is either missing from config or set to an empty string. (PR: :issue:`1371`) diff --git a/mopidy/mpd/protocol/playback.py b/mopidy/mpd/protocol/playback.py index c9dcc341..48aaae2c 100644 --- a/mopidy/mpd/protocol/playback.py +++ b/mopidy/mpd/protocol/playback.py @@ -440,7 +440,7 @@ def volume(context, change): Note: ``volume`` is deprecated, use ``setvol`` instead. """ if change < -100 or change > 100: - raise exceptions.MpdArgError('Invalid volume value', command='volume') + raise exceptions.MpdArgError('Invalid volume value') old_volume = context.core.mixer.get_volume().get() if old_volume is None: From b0b3bd85dcd84505538d9e85fe9f28b762563a1d Mon Sep 17 00:00:00 2001 From: Stein Magnus Jodal Date: Mon, 18 Jan 2016 22:53:27 +0100 Subject: [PATCH 43/45] docs: Update copyright years --- docs/authors.rst | 2 +- docs/conf.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/authors.rst b/docs/authors.rst index 90ec6f23..f4f93d56 100644 --- a/docs/authors.rst +++ b/docs/authors.rst @@ -4,7 +4,7 @@ Authors ******* -Mopidy is copyright 2009-2015 Stein Magnus Jodal and contributors. Mopidy is +Mopidy is copyright 2009-2016 Stein Magnus Jodal and contributors. Mopidy is licensed under the `Apache License, Version 2.0 `_. diff --git a/docs/conf.py b/docs/conf.py index cbb2f228..3a93cc90 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -93,14 +93,14 @@ source_suffix = '.rst' master_doc = 'index' project = 'Mopidy' -copyright = '2009-2015, Stein Magnus Jodal and contributors' +copyright = '2009-2016, Stein Magnus Jodal and contributors' from mopidy.internal.versioning import get_version release = get_version() version = '.'.join(release.split('.')[:2]) # To make the build reproducible, avoid using today's date in the manpages -today = '2015' +today = '2016' exclude_trees = ['_build'] From 89888cde48c2357c1e2ed4798ea66cd8196273cb Mon Sep 17 00:00:00 2001 From: Stein Magnus Jodal Date: Mon, 18 Jan 2016 22:52:38 +0100 Subject: [PATCH 44/45] Bump version to 1.1.2 --- mopidy/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mopidy/__init__.py b/mopidy/__init__.py index df9aacc3..59d0444e 100644 --- a/mopidy/__init__.py +++ b/mopidy/__init__.py @@ -14,4 +14,4 @@ if not (2, 7) <= sys.version_info < (3,): warnings.filterwarnings('ignore', 'could not open display') -__version__ = '1.1.1' +__version__ = '1.1.2' From 6d0e3ec698f93eb1823c90b87518f7779b1d169a Mon Sep 17 00:00:00 2001 From: Stein Magnus Jodal Date: Mon, 18 Jan 2016 22:53:02 +0100 Subject: [PATCH 45/45] docs: Update changelog for 1.1.2 --- docs/changelog.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index a6ebfc61..8054ac82 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -4,7 +4,7 @@ Changelog This changelog is used to track all major changes to Mopidy. -v1.1.2 (UNRELEASED) +v1.1.2 (2016-01-18) =================== Bug fix release.