Ejemplo n.º 1
0
    def filter(self, criteria=None, **kwargs):
        """
        Filter playlists by the given criterias.

        Examples::

            # Returns track with name 'a'
            filter({'name': 'a'})

            # Returns track with URI 'xyz'
            filter({'uri': 'xyz'})

            # Returns track with name 'a' and URI 'xyz'
            filter({'name': 'a', 'uri': 'xyz'})

        :param criteria: one or more criteria to match by
        :type criteria: dict
        :rtype: list of :class:`mopidy.models.Playlist`

        .. deprecated:: 1.0
            Use :meth:`as_list` and filter yourself.
        """
        deprecation.warn('core.playlists.filter')

        criteria = criteria or kwargs
        validation.check_query(criteria,
                               validation.PLAYLIST_FIELDS,
                               list_values=False)

        matches = self.playlists  # TODO: stop using self playlists
        for (key, value) in criteria.iteritems():
            matches = filter(lambda p: getattr(p, key) == value, matches)
        return matches
Ejemplo n.º 2
0
    def filter(self, criteria=None, **kwargs):
        """
        Filter playlists by the given criterias.

        Examples::

            # Returns track with name 'a'
            filter({'name': 'a'})

            # Returns track with URI 'xyz'
            filter({'uri': 'xyz'})

            # Returns track with name 'a' and URI 'xyz'
            filter({'name': 'a', 'uri': 'xyz'})

        :param criteria: one or more criteria to match by
        :type criteria: dict
        :rtype: list of :class:`mopidy.models.Playlist`

        .. deprecated:: 1.0
            Use :meth:`as_list` and filter yourself.
        """
        deprecation.warn('core.playlists.filter')

        criteria = criteria or kwargs
        validation.check_query(
            criteria, validation.PLAYLIST_FIELDS, list_values=False)

        matches = self.playlists  # TODO: stop using self playlists
        for (key, value) in criteria.iteritems():
            matches = filter(lambda p: getattr(p, key) == value, matches)
        return matches
Ejemplo n.º 3
0
    def get_distinct(self, field, query=None):
        """
        List distinct values for a given field from the library.

        This has mainly been added to support the list commands the MPD
        protocol supports in a more sane fashion. Other frontends are not
        recommended to use this method.

        :param string field: One of ``track``, ``artist``, ``albumartist``,
            ``album``, ``composer``, ``performer``, ``date`` or ``genre``.
        :param dict query: Query to use for limiting results, see
            :meth:`search` for details about the query format.
        :rtype: set of values corresponding to the requested field type.

        .. versionadded:: 1.0
        """
        validation.check_choice(field, validation.DISTINCT_FIELDS)
        query is None or validation.check_query(query)  # TODO: normalize?

        result = set()
        futures = {b: b.library.get_distinct(field, query) for b in self.backends.with_library.values()}
        for backend, future in futures.items():
            with _backend_error_handling(backend):
                values = future.get()
                if values is not None:
                    validation.check_instances(values, compat.text_type)
                    result.update(values)
        return result
Ejemplo n.º 4
0
    def get_distinct(self, field, query=None):
        """
        List distinct values for a given field from the library.

        This has mainly been added to support the list commands the MPD
        protocol supports in a more sane fashion. Other frontends are not
        recommended to use this method.

        :param string field: One of ``track``, ``artist``, ``albumartist``,
            ``album``, ``composer``, ``performer``, ``date`` or ``genre``.
        :param dict query: Query to use for limiting results, see
            :meth:`search` for details about the query format.
        :rtype: set of values corresponding to the requested field type.

        .. versionadded:: 1.0
        """
        validation.check_choice(field, validation.DISTINCT_FIELDS)
        query is None or validation.check_query(query)  # TODO: normalize?

        result = set()
        futures = {
            b: b.library.get_distinct(field, query)
            for b in self.backends.with_library.values()
        }
        for backend, future in futures.items():
            with _backend_error_handling(backend):
                values = future.get()
                if values is not None:
                    validation.check_instances(values, str)
                    result.update(values)
        return result
Ejemplo n.º 5
0
    def filter(self, criteria=None, **kwargs):
        """
        Filter the tracklist by the given criterias.

        A criteria consists of a model field to check and a list of values to
        compare it against. If the model field matches one of the values, it
        may be returned.

        Only tracks that matches all the given criterias are returned.

        Examples::

            # Returns tracks with TLIDs 1, 2, 3, or 4 (tracklist ID)
            filter({'tlid': [1, 2, 3, 4]})

            # Returns track with URIs 'xyz' or 'abc'
            filter({'uri': ['xyz', 'abc']})

            # Returns track with a matching TLIDs (1, 3 or 6) and a
            # matching URI ('xyz' or 'abc')
            filter({'tlid': [1, 3, 6], 'uri': ['xyz', 'abc']})

        :param criteria: on or more criteria to match by
        :type criteria: dict, of (string, list) pairs
        :rtype: list of :class:`mopidy.models.TlTrack`

        .. deprecated:: 1.1
            Providing the criteria via ``kwargs``.
        """
        if kwargs:
            deprecation.warn('core.tracklist.filter:kwargs_criteria')

        criteria = criteria or kwargs
        tlids = criteria.pop('tlid', [])
        validation.check_query(criteria, validation.TRACKLIST_FIELDS)
        validation.check_instances(tlids, int)

        matches = self._tl_tracks
        for (key, values) in criteria.items():
            matches = [
                ct for ct in matches if getattr(ct.track, key) in values
            ]
        if tlids:
            matches = [ct for ct in matches if ct.tlid in tlids]
        return matches
Ejemplo n.º 6
0
    def filter(self, criteria=None, **kwargs):
        """
        Filter the tracklist by the given criterias.

        A criteria consists of a model field to check and a list of values to
        compare it against. If the model field matches one of the values, it
        may be returned.

        Only tracks that matches all the given criterias are returned.

        Examples::

            # Returns tracks with TLIDs 1, 2, 3, or 4 (tracklist ID)
            filter({'tlid': [1, 2, 3, 4]})

            # Returns track with URIs 'xyz' or 'abc'
            filter({'uri': ['xyz', 'abc']})

            # Returns track with a matching TLIDs (1, 3 or 6) and a
            # matching URI ('xyz' or 'abc')
            filter({'tlid': [1, 3, 6], 'uri': ['xyz', 'abc']})

        :param criteria: on or more criteria to match by
        :type criteria: dict, of (string, list) pairs
        :rtype: list of :class:`mopidy.models.TlTrack`

        .. deprecated:: 1.1
            Providing the criteria via ``kwargs``.
        """
        if kwargs:
            deprecation.warn('core.tracklist.filter:kwargs_criteria')

        criteria = criteria or kwargs
        tlids = criteria.pop('tlid', [])
        validation.check_query(criteria, validation.TRACKLIST_FIELDS)
        validation.check_instances(tlids, int)

        matches = self._tl_tracks
        for (key, values) in criteria.items():
            matches = [
                ct for ct in matches if getattr(ct.track, key) in values]
        if tlids:
            matches = [ct for ct in matches if ct.tlid in tlids]
        return matches
Ejemplo n.º 7
0
    def filter(self, criteria):
        """
        Filter the tracklist by the given criteria.

        Each rule in the criteria consists of a model field and a list of
        values to compare it against. If the model field matches any of the
        values, it may be returned.

        Only tracks that match all the given criteria are returned.

        Examples::

            # Returns tracks with TLIDs 1, 2, 3, or 4 (tracklist ID)
            filter({'tlid': [1, 2, 3, 4]})

            # Returns track with URIs 'xyz' or 'abc'
            filter({'uri': ['xyz', 'abc']})

            # Returns track with a matching TLIDs (1, 3 or 6) and a
            # matching URI ('xyz' or 'abc')
            filter({'tlid': [1, 3, 6], 'uri': ['xyz', 'abc']})

        :param criteria: one or more rules to match by
        :type criteria: dict, of (string, list) pairs
        :rtype: list of :class:`mopidy.models.TlTrack`
        """
        tlids = criteria.pop("tlid", [])
        validation.check_query(criteria, validation.TRACKLIST_FIELDS)
        validation.check_instances(tlids, int)

        matches = self._tl_tracks
        for (key, values) in criteria.items():
            matches = [
                ct for ct in matches if getattr(ct.track, key) in values
            ]
        if tlids:
            matches = [ct for ct in matches if ct.tlid in tlids]
        return matches
Ejemplo n.º 8
0
def test_check_query_random_iterables():
    for value in None, tuple(), list(), 'abc':
        with raises(exceptions.ValidationError):
            validation.check_query(value)
Ejemplo n.º 9
0
def test_check_query_valid_values():
    for value in {}, {'any': []}, {'any': ['abc']}:
        validation.check_query(value)
Ejemplo n.º 10
0
def test_check_values_error_message():
    with raises(exceptions.ValidationError) as excinfo:
        validation.check_query({'any': 'abc'})
    assert 'Expected "any" to be list of strings, not' in str(excinfo.value)
Ejemplo n.º 11
0
def test_check_query_invalid_values():
    for value in '', None, 'foo', 123, [''], [None], iter(['abc']):
        with raises(exceptions.ValidationError):
            validation.check_query({'any': value})
Ejemplo n.º 12
0
def test_check_field_error_message():
    with raises(exceptions.ValidationError) as excinfo:
        validation.check_query({'wrong': ['abc']})
    assert 'Expected query field to be one of ' in str(excinfo.value)
Ejemplo n.º 13
0
def test_check_values_error_message():
    with raises(exceptions.ValidationError) as excinfo:
        validation.check_query({'any': 'abc'})
    assert 'Expected "any" to be list of strings, not' in str(excinfo.value)
Ejemplo n.º 14
0
def test_check_mapping_error_message():
    with raises(exceptions.ValidationError) as excinfo:
        validation.check_query([])
    assert 'Expected a query dictionary, not []' == str(excinfo.value)
Ejemplo n.º 15
0
def test_check_query_valid_values():
    for value in {}, {'any': []}, {'any': ['abc']}:
        validation.check_query(value)
Ejemplo n.º 16
0
def test_check_query_invalid_fields():
    for value in "wrong", "bar", "foo", "tlid":
        with raises(exceptions.ValidationError):
            validation.check_query({value: []})
Ejemplo n.º 17
0
def test_check_query_valid_values():
    for value in {}, {"any": []}, {"any": ["abc"]}:
        validation.check_query(value)
Ejemplo n.º 18
0
def test_check_query_invalid_values():
    for value in "", None, "foo", 123, [""], [None], iter(["abc"]):
        with raises(exceptions.ValidationError):
            validation.check_query({"any": value})
Ejemplo n.º 19
0
def test_check_field_error_message():
    with raises(exceptions.ValidationError) as excinfo:
        validation.check_query({"wrong": ["abc"]})
    assert "Expected query field to be one of " in str(excinfo.value)
Ejemplo n.º 20
0
def test_check_mapping_error_message():
    with raises(exceptions.ValidationError) as excinfo:
        validation.check_query([])
    assert 'Expected a query dictionary, not []' == str(excinfo.value)
Ejemplo n.º 21
0
    def search(self, query=None, uris=None, exact=False, **kwargs):
        """
        Search the library for tracks where ``field`` contains ``values``.

        If ``uris`` is given, the search is limited to results from within the
        URI roots. For example passing ``uris=['file:']`` will limit the search
        to the local backend.

        Examples::

            # Returns results matching 'a' in any backend
            search({'any': ['a']})

            # Returns results matching artist 'xyz' in any backend
            search({'artist': ['xyz']})

            # Returns results matching 'a' and 'b' and artist 'xyz' in any
            # backend
            search({'any': ['a', 'b'], 'artist': ['xyz']})

            # Returns results matching 'a' if within the given URI roots
            # "file:///media/music" and "spotify:"
            search({'any': ['a']}, uris=['file:///media/music', 'spotify:'])

            # Returns results matching artist 'xyz' and 'abc' in any backend
            search({'artist': ['xyz', 'abc']})

        :param query: one or more queries to search for
        :type query: dict
        :param uris: zero or more URI roots to limit the search to
        :type uris: list of string or :class:`None`
        :param exact: if the search should use exact matching
        :type exact: :class:`bool`
        :rtype: list of :class:`mopidy.models.SearchResult`

        .. versionadded:: 1.0
            The ``exact`` keyword argument, which replaces :meth:`find_exact`.

        .. deprecated:: 1.0
            Previously, if the query was empty, and the backend could support
            it, all available tracks were returned. This has not changed, but
            it is strongly discouraged. No new code should rely on this
            behavior.

        .. deprecated:: 1.1
            Providing the search query via ``kwargs`` is no longer supported.
        """
        query = _normalize_query(query or kwargs)

        uris is None or validation.check_uris(uris)
        query is None or validation.check_query(query)
        validation.check_boolean(exact)

        if kwargs:
            deprecation.warn('core.library.search:kwargs_query')

        if not query:
            deprecation.warn('core.library.search:empty_query')

        futures = {}
        for backend, backend_uris in self._get_backends_to_uris(uris).items():
            futures[backend] = backend.library.search(query=query,
                                                      uris=backend_uris,
                                                      exact=exact)

        # Some of our tests check for LookupError to catch bad queries. This is
        # silly and should be replaced with query validation before passing it
        # to the backends.
        reraise = (TypeError, LookupError)

        results = []
        for backend, future in futures.items():
            try:
                with _backend_error_handling(backend, reraise=reraise):
                    result = future.get()
                    if result is not None:
                        validation.check_instance(result, models.SearchResult)
                        results.append(result)
            except TypeError:
                backend_name = backend.actor_ref.actor_class.__name__
                logger.warning(
                    '%s does not implement library.search() with "exact" '
                    'support. Please upgrade it.', backend_name)

        return results
Ejemplo n.º 22
0
def test_check_query_random_iterables():
    for value in None, tuple(), list(), 'abc':
        with raises(exceptions.ValidationError):
            validation.check_query(value)
Ejemplo n.º 23
0
    def search(self, query=None, uris=None, exact=False, **kwargs):
        """
        Search the library for tracks where ``field`` contains ``values``.

        If ``uris`` is given, the search is limited to results from within the
        URI roots. For example passing ``uris=['file:']`` will limit the search
        to the local backend.

        Examples::

            # Returns results matching 'a' in any backend
            search({'any': ['a']})

            # Returns results matching artist 'xyz' in any backend
            search({'artist': ['xyz']})

            # Returns results matching 'a' and 'b' and artist 'xyz' in any
            # backend
            search({'any': ['a', 'b'], 'artist': ['xyz']})

            # Returns results matching 'a' if within the given URI roots
            # "file:///media/music" and "spotify:"
            search({'any': ['a']}, uris=['file:///media/music', 'spotify:'])

            # Returns results matching artist 'xyz' and 'abc' in any backend
            search({'artist': ['xyz', 'abc']})

        :param query: one or more queries to search for
        :type query: dict
        :param uris: zero or more URI roots to limit the search to
        :type uris: list of string or :class:`None`
        :param exact: if the search should use exact matching
        :type exact: :class:`bool`
        :rtype: list of :class:`mopidy.models.SearchResult`

        .. versionadded:: 1.0
            The ``exact`` keyword argument, which replaces :meth:`find_exact`.

        .. deprecated:: 1.0
            Previously, if the query was empty, and the backend could support
            it, all available tracks were returned. This has not changed, but
            it is strongly discouraged. No new code should rely on this
            behavior.

        .. deprecated:: 1.1
            Providing the search query via ``kwargs`` is no longer supported.
        """
        query = _normalize_query(query or kwargs)

        uris is None or validation.check_uris(uris)
        query is None or validation.check_query(query)
        validation.check_boolean(exact)

        if kwargs:
            deprecation.warn("core.library.search:kwargs_query")

        if not query:
            deprecation.warn("core.library.search:empty_query")

        futures = {}
        for backend, backend_uris in self._get_backends_to_uris(uris).items():
            futures[backend] = backend.library.search(query=query, uris=backend_uris, exact=exact)

        # Some of our tests check for LookupError to catch bad queries. This is
        # silly and should be replaced with query validation before passing it
        # to the backends.
        reraise = (TypeError, LookupError)

        results = []
        for backend, future in futures.items():
            try:
                with _backend_error_handling(backend, reraise=reraise):
                    result = future.get()
                    if result is not None:
                        validation.check_instance(result, models.SearchResult)
                        results.append(result)
            except TypeError:
                backend_name = backend.actor_ref.actor_class.__name__
                logger.warning(
                    '%s does not implement library.search() with "exact" ' "support. Please upgrade it.", backend_name
                )

        return results
Ejemplo n.º 24
0
def test_check_field_error_message():
    with raises(exceptions.ValidationError) as excinfo:
        validation.check_query({'wrong': ['abc']})
    assert 'Expected query field to be one of ' in str(excinfo.value)
Ejemplo n.º 25
0
def test_check_query_invalid_fields():
    for value in 'wrong', 'bar', 'foo', 'tlid':
        with raises(exceptions.ValidationError):
            validation.check_query({value: []})
Ejemplo n.º 26
0
def test_check_query_invalid_values():
    for value in '', None, 'foo', 123, [''], [None], iter(['abc']):
        with raises(exceptions.ValidationError):
            validation.check_query({'any': value})
Ejemplo n.º 27
0
    def search(self, query, uris=None, exact=False):
        """
        Search the library for tracks where ``field`` contains ``values``.

        ``field`` can be one of ``uri``, ``track_name``, ``album``, ``artist``,
        ``albumartist``, ``composer``, ``performer``, ``track_no``, ``genre``,
        ``date``, ``comment``, or ``any``.

        If ``uris`` is given, the search is limited to results from within the
        URI roots. For example passing ``uris=['file:']`` will limit the search
        to the local backend.

        Examples::

            # Returns results matching 'a' in any backend
            search({'any': ['a']})

            # Returns results matching artist 'xyz' in any backend
            search({'artist': ['xyz']})

            # Returns results matching 'a' and 'b' and artist 'xyz' in any
            # backend
            search({'any': ['a', 'b'], 'artist': ['xyz']})

            # Returns results matching 'a' if within the given URI roots
            # "file:///media/music" and "spotify:"
            search({'any': ['a']}, uris=['file:///media/music', 'spotify:'])

            # Returns results matching artist 'xyz' and 'abc' in any backend
            search({'artist': ['xyz', 'abc']})

        :param query: one or more queries to search for
        :type query: dict
        :param uris: zero or more URI roots to limit the search to
        :type uris: list of string or :class:`None`
        :param exact: if the search should use exact matching
        :type exact: :class:`bool`
        :rtype: list of :class:`mopidy.models.SearchResult`

        .. versionadded:: 1.0
            The ``exact`` keyword argument.
        """
        query = _normalize_query(query)

        uris is None or validation.check_uris(uris)
        validation.check_query(query)
        validation.check_boolean(exact)

        if not query:
            return []

        futures = {}
        for backend, backend_uris in self._get_backends_to_uris(uris).items():
            futures[backend] = backend.library.search(query=query,
                                                      uris=backend_uris,
                                                      exact=exact)

        # Some of our tests check for LookupError to catch bad queries. This is
        # silly and should be replaced with query validation before passing it
        # to the backends.
        reraise = (TypeError, LookupError)

        results = []
        for backend, future in futures.items():
            try:
                with _backend_error_handling(backend, reraise=reraise):
                    result = future.get()
                    if result is not None:
                        validation.check_instance(result, models.SearchResult)
                        results.append(result)
            except TypeError:
                backend_name = backend.actor_ref.actor_class.__name__
                logger.warning(
                    '%s does not implement library.search() with "exact" '
                    "support. Please upgrade it.",
                    backend_name,
                )

        return results
Ejemplo n.º 28
0
def test_check_query_invalid_fields():
    for value in 'wrong', 'bar', 'foo', 'tlid':
        with raises(exceptions.ValidationError):
            validation.check_query({value: []})