Example #1
0
    def get_items(self, uri):
        """
        Get the items in a playlist specified by ``uri``.

        Returns a list of :class:`~mopidy.models.Ref` objects referring to the
        playlist's items.

        If a playlist with the given ``uri`` doesn't exist, it returns
        :class:`None`.

        :rtype: list of :class:`mopidy.models.Ref`, or :class:`None`

        .. versionadded:: 1.0
        """
        validation.check_uri(uri)

        uri_scheme = urllib.parse.urlparse(uri).scheme
        backend = self.backends.with_playlists.get(uri_scheme, None)

        if not backend:
            return None

        with _backend_error_handling(backend):
            items = backend.playlists.get_items(uri).get()
            items is None or validation.check_instances(items, Ref)
            return items

        return None
Example #2
0
    def get_items(self, uri):
        """
        Get the items in a playlist specified by ``uri``.

        Returns a list of :class:`~mopidy.models.Ref` objects referring to the
        playlist's items.

        If a playlist with the given ``uri`` doesn't exist, it returns
        :class:`None`.

        :rtype: list of :class:`mopidy.models.Ref`, or :class:`None`

        .. versionadded:: 1.0
        """
        validation.check_uri(uri)

        uri_scheme = urllib.parse.urlparse(uri).scheme
        backend = self.backends.with_playlists.get(uri_scheme, None)

        if not backend:
            return None

        with _backend_error_handling(backend):
            items = backend.playlists.get_items(uri).get()
            items is None or validation.check_instances(items, Ref)
            return items

        return None
Example #3
0
def parse_urilist(data):
    result = []
    for line in data.splitlines():
        if not line.strip() or line.startswith('#'):
            continue
        try:
            validation.check_uri(line)
        except ValueError:
            return []
        result.append(line)
    return result
Example #4
0
def parse_urilist(data):
    result = []
    for line in data.splitlines():
        if not line.strip() or line.startswith(b'#'):
            continue
        try:
            validation.check_uri(line)
        except ValueError:
            return []
        result.append(line)
    return result
Example #5
0
def parse_urilist(data):
    for line in data.splitlines():
        if not line.strip() or line.startswith(b"#"):
            continue

        try:
            line = line.decode()
        except UnicodeDecodeError:
            continue

        try:
            validation.check_uri(line)
        except ValueError:
            continue

        yield line.strip()
Example #6
0
    def delete(self, uri):
        """
        Delete playlist identified by the URI.

        If the URI doesn't match the URI schemes handled by the current
        backends, nothing happens.

        :param uri: URI of the playlist to delete
        :type uri: string
        """
        validation.check_uri(uri)

        uri_scheme = urlparse.urlparse(uri).scheme
        backend = self.backends.with_playlists.get(uri_scheme, None)
        if not backend:
            return None  # TODO: error reporting to user

        with _backend_error_handling(backend):
            backend.playlists.delete(uri).get()
            # TODO: error detection and reporting to user
            listener.CoreListener.send('playlist_deleted', uri=uri)
Example #7
0
    def lookup(self, uri=None, uris=None):
        """
        Lookup the given URIs.

        If the URI expands to multiple tracks, the returned list will contain
        them all.

        :param uri: track URI
        :type uri: string or :class:`None`
        :param uris: track URIs
        :type uris: list of string or :class:`None`
        :rtype: list of :class:`mopidy.models.Track` if uri was set or
            {uri: list of :class:`mopidy.models.Track`} if uris was set.

        .. versionadded:: 1.0
            The ``uris`` argument.

        .. deprecated:: 1.0
            The ``uri`` argument. Use ``uris`` instead.
        """
        if sum(o is not None for o in [uri, uris]) != 1:
            raise ValueError('Exactly one of "uri" or "uris" must be set')

        uris is None or validation.check_uris(uris)
        uri is None or validation.check_uri(uri)

        if uri:
            deprecation.warn('core.library.lookup:uri_arg')

        if uri is not None:
            uris = [uri]

        futures = {}
        results = {u: [] for u in uris}

        # TODO: lookup(uris) to backend APIs
        for backend, backend_uris in self._get_backends_to_uris(uris).items():
            if backend_uris:
                for u in backend_uris:
                    futures[(backend, u)] = backend.library.lookup(u)

        for (backend, u), future in futures.items():
            with _backend_error_handling(backend):
                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:
            return results[uri]
        return results
Example #8
0
    def delete(self, uri):
        """
        Delete playlist identified by the URI.

        If the URI doesn't match the URI schemes handled by the current
        backends, nothing happens.

        :param uri: URI of the playlist to delete
        :type uri: string
        """
        validation.check_uri(uri)

        uri_scheme = urllib.parse.urlparse(uri).scheme
        backend = self.backends.with_playlists.get(uri_scheme, None)
        if not backend:
            return None  # TODO: error reporting to user

        with _backend_error_handling(backend):
            backend.playlists.delete(uri).get()
            # TODO: error detection and reporting to user
            listener.CoreListener.send('playlist_deleted', uri=uri)
Example #9
0
    def lookup(self, uri=None, uris=None):
        """
        Lookup the given URIs.

        If the URI expands to multiple tracks, the returned list will contain
        them all.

        :param uri: track URI
        :type uri: string or :class:`None`
        :param uris: track URIs
        :type uris: list of string or :class:`None`
        :rtype: list of :class:`mopidy.models.Track` if uri was set or
            {uri: list of :class:`mopidy.models.Track`} if uris was set.

        .. versionadded:: 1.0
            The ``uris`` argument.

        .. deprecated:: 1.0
            The ``uri`` argument. Use ``uris`` instead.
        """
        if sum(o is not None for o in [uri, uris]) != 1:
            raise ValueError('Exactly one of "uri" or "uris" must be set')

        uris is None or validation.check_uris(uris)
        uri is None or validation.check_uri(uri)

        if uri:
            deprecation.warn('core.library.lookup:uri_arg')

        if uri is not None:
            uris = [uri]

        futures = {}
        results = {u: [] for u in uris}

        # TODO: lookup(uris) to backend APIs
        for backend, backend_uris in self._get_backends_to_uris(uris).items():
            if backend_uris:
                for u in backend_uris:
                    futures[(backend, u)] = backend.library.lookup(u)

        for (backend, u), future in futures.items():
            with _backend_error_handling(backend):
                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:
            return results[uri]
        return results
Example #10
0
    def browse(self, uri):
        """
        Browse directories and tracks at the given ``uri``.

        ``uri`` is a string which represents some directory belonging to a
        backend. To get the intial root directories for backends pass
        :class:`None` as the URI.

        Returns a list of :class:`mopidy.models.Ref` objects for the
        directories and tracks at the given ``uri``.

        The :class:`~mopidy.models.Ref` objects representing tracks keep the
        track's original URI. A matching pair of objects can look like this::

            Track(uri='dummy:/foo.mp3', name='foo', artists=..., album=...)
            Ref.track(uri='dummy:/foo.mp3', name='foo')

        The :class:`~mopidy.models.Ref` objects representing directories have
        backend specific URIs. These are opaque values, so no one but the
        backend that created them should try and derive any meaning from them.
        The only valid exception to this is checking the scheme, as it is used
        to route browse requests to the correct backend.

        For example, the dummy library's ``/bar`` directory could be returned
        like this::

            Ref.directory(uri='dummy:directory:/bar', name='bar')

        :param string uri: URI to browse
        :rtype: list of :class:`mopidy.models.Ref`

        .. versionadded:: 0.18
        """
        if uri is None:
            return self._roots()
        elif not uri.strip():
            return []
        validation.check_uri(uri)
        return self._browse(uri)
Example #11
0
    def browse(self, uri):
        """
        Browse directories and tracks at the given ``uri``.

        ``uri`` is a string which represents some directory belonging to a
        backend. To get the intial root directories for backends pass
        :class:`None` as the URI.

        Returns a list of :class:`mopidy.models.Ref` objects for the
        directories and tracks at the given ``uri``.

        The :class:`~mopidy.models.Ref` objects representing tracks keep the
        track's original URI. A matching pair of objects can look like this::

            Track(uri='dummy:/foo.mp3', name='foo', artists=..., album=...)
            Ref.track(uri='dummy:/foo.mp3', name='foo')

        The :class:`~mopidy.models.Ref` objects representing directories have
        backend specific URIs. These are opaque values, so no one but the
        backend that created them should try and derive any meaning from them.
        The only valid exception to this is checking the scheme, as it is used
        to route browse requests to the correct backend.

        For example, the dummy library's ``/bar`` directory could be returned
        like this::

            Ref.directory(uri='dummy:directory:/bar', name='bar')

        :param string uri: URI to browse
        :rtype: list of :class:`mopidy.models.Ref`

        .. versionadded:: 0.18
        """
        if uri is None:
            return self._roots()
        elif not uri.strip():
            return []
        validation.check_uri(uri)
        return self._browse(uri)
Example #12
0
    def delete(self, uri):
        """
        Delete playlist identified by the URI.

        If the URI doesn't match the URI schemes handled by the current
        backends, nothing happens.

        Returns :class:`True` if deleted, :class:`False` otherwise.

        :param uri: URI of the playlist to delete
        :type uri: string
        :rtype: :class:`bool`

        .. versionchanged:: 2.2
            Return type defined.
        """
        validation.check_uri(uri)

        uri_scheme = urllib.parse.urlparse(uri).scheme
        backend = self.backends.with_playlists.get(uri_scheme, None)
        if not backend:
            return False

        success = False
        with _backend_error_handling(backend):
            success = backend.playlists.delete(uri).get()

        if success is None:
            # Return type was defined in Mopidy 2.2. Assume everything went
            # well if the backend doesn't report otherwise.
            success = True

        if success:
            listener.CoreListener.send("playlist_deleted", uri=uri)

        return success
Example #13
0
    def delete(self, uri):
        """
        Delete playlist identified by the URI.

        If the URI doesn't match the URI schemes handled by the current
        backends, nothing happens.

        Returns :class:`True` if deleted, :class:`False` otherwise.

        :param uri: URI of the playlist to delete
        :type uri: string
        :rtype: :class:`bool`

        .. versionchanged:: 2.2
            Return type defined.
        """
        validation.check_uri(uri)

        uri_scheme = urllib.parse.urlparse(uri).scheme
        backend = self.backends.with_playlists.get(uri_scheme, None)
        if not backend:
            return False

        success = False
        with _backend_error_handling(backend):
            success = backend.playlists.delete(uri).get()

        if success is None:
            # Return type was defined in Mopidy 2.2. Assume everything went
            # well if the backend doesn't report otherwise.
            success = True

        if success:
            listener.CoreListener.send('playlist_deleted', uri=uri)

        return success
Example #14
0
    def refresh(self, uri=None):
        """
        Refresh library. Limit to URI and below if an URI is given.

        :param uri: directory or track URI
        :type uri: string
        """
        uri is None or validation.check_uri(uri)

        futures = {}
        backends = {}
        uri_scheme = urllib.parse.urlparse(uri).scheme if uri else None

        for backend_scheme, backend in self.backends.with_library.items():
            backends.setdefault(backend, set()).add(backend_scheme)

        for backend, backend_schemes in backends.items():
            if uri_scheme is None or uri_scheme in backend_schemes:
                futures[backend] = backend.library.refresh(uri)

        for backend, future in futures.items():
            with _backend_error_handling(backend):
                future.get()
Example #15
0
    def refresh(self, uri=None):
        """
        Refresh library. Limit to URI and below if an URI is given.

        :param uri: directory or track URI
        :type uri: string
        """
        uri is None or validation.check_uri(uri)

        futures = {}
        backends = {}
        uri_scheme = urlparse.urlparse(uri).scheme if uri else None

        for backend_scheme, backend in self.backends.with_library.items():
            backends.setdefault(backend, set()).add(backend_scheme)

        for backend, backend_schemes in backends.items():
            if uri_scheme is None or uri_scheme in backend_schemes:
                futures[backend] = backend.library.refresh(uri)

        for backend, future in futures.items():
            with _backend_error_handling(backend):
                future.get()
Example #16
0
def test_check_uri_with_invalid_values():
    # Note that tuple catches a potential bug with using "'foo' % arg" for
    # formatting.
    for value in ('foobar', 'htt p://example.com', None, 1234, tuple()):
        with raises(exceptions.ValidationError):
            validation.check_uri(value)
Example #17
0
def test_check_uri_error_message():
    with raises(exceptions.ValidationError) as excinfo:
        validation.check_uri('testing')
    assert "Expected a valid URI, not u'testing'" == str(excinfo.value)
Example #18
0
def test_check_uri_with_valid_values():
    for value in "foobar:", "http://example.com", "git+http://example.com":
        validation.check_uri(value)
Example #19
0
    def add(self, tracks=None, at_position=None, uri=None, uris=None):
        """
        Add tracks to the tracklist.

        If ``uri`` is given instead of ``tracks``, the URI is looked up in the
        library and the resulting tracks are added to the tracklist.

        If ``uris`` is given instead of ``uri`` or ``tracks``, the URIs are
        looked up in the library and the resulting tracks are added to the
        tracklist.

        If ``at_position`` is given, the tracks are inserted at the given
        position in the tracklist. If ``at_position`` is not given, the tracks
        are appended to the end of the tracklist.

        Triggers the :meth:`mopidy.core.CoreListener.tracklist_changed` event.

        :param tracks: tracks to add
        :type tracks: list of :class:`mopidy.models.Track` or :class:`None`
        :param at_position: position in tracklist to add tracks
        :type at_position: int or :class:`None`
        :param uri: URI for tracks to add
        :type uri: string or :class:`None`
        :param uris: list of URIs for tracks to add
        :type uris: list of string or :class:`None`
        :rtype: list of :class:`mopidy.models.TlTrack`

        .. versionadded:: 1.0
            The ``uris`` argument.

        .. deprecated:: 1.0
            The ``tracks`` and ``uri`` arguments. Use ``uris``.
        """
        if sum(o is not None for o in [tracks, uri, uris]) != 1:
            raise ValueError(
                'Exactly one of "tracks", "uri" or "uris" must be set')

        tracks is None or validation.check_instances(tracks, Track)
        uri is None or validation.check_uri(uri)
        uris is None or validation.check_uris(uris)
        validation.check_integer(at_position or 0)

        if tracks:
            deprecation.warn('core.tracklist.add:tracks_arg')

        if uri:
            deprecation.warn('core.tracklist.add:uri_arg')

        if tracks is None:
            if uri is not None:
                uris = [uri]

            tracks = []
            track_map = self.core.library.lookup(uris=uris)
            for uri in uris:
                tracks.extend(track_map[uri])

        tl_tracks = []
        max_length = self.core._config['core']['max_tracklist_length']

        for track in tracks:
            if self.get_length() >= max_length:
                raise exceptions.TracklistFull(
                    'Tracklist may contain at most %d tracks.' % max_length)

            tl_track = TlTrack(self._next_tlid, track)
            self._next_tlid += 1
            if at_position is not None:
                self._tl_tracks.insert(at_position, tl_track)
                at_position += 1
            else:
                self._tl_tracks.append(tl_track)
            tl_tracks.append(tl_track)

        if tl_tracks:
            self._increase_version()

        return tl_tracks
Example #20
0
    def add(self, tracks=None, at_position=None, uri=None, uris=None):
        """
        Add tracks to the tracklist.

        If ``uri`` is given instead of ``tracks``, the URI is looked up in the
        library and the resulting tracks are added to the tracklist.

        If ``uris`` is given instead of ``uri`` or ``tracks``, the URIs are
        looked up in the library and the resulting tracks are added to the
        tracklist.

        If ``at_position`` is given, the tracks are inserted at the given
        position in the tracklist. If ``at_position`` is not given, the tracks
        are appended to the end of the tracklist.

        Triggers the :meth:`mopidy.core.CoreListener.tracklist_changed` event.

        :param tracks: tracks to add
        :type tracks: list of :class:`mopidy.models.Track` or :class:`None`
        :param at_position: position in tracklist to add tracks
        :type at_position: int or :class:`None`
        :param uri: URI for tracks to add
        :type uri: string or :class:`None`
        :param uris: list of URIs for tracks to add
        :type uris: list of string or :class:`None`
        :rtype: list of :class:`mopidy.models.TlTrack`

        .. versionadded:: 1.0
            The ``uris`` argument.

        .. deprecated:: 1.0
            The ``tracks`` and ``uri`` arguments. Use ``uris``.
        """
        if sum(o is not None for o in [tracks, uri, uris]) != 1:
            raise ValueError(
                'Exactly one of "tracks", "uri" or "uris" must be set')

        tracks is None or validation.check_instances(tracks, Track)
        uri is None or validation.check_uri(uri)
        uris is None or validation.check_uris(uris)
        validation.check_integer(at_position or 0)

        if tracks:
            deprecation.warn('core.tracklist.add:tracks_arg')

        if uri:
            deprecation.warn('core.tracklist.add:uri_arg')

        if tracks is None:
            if uri is not None:
                uris = [uri]

            tracks = []
            track_map = self.core.library.lookup(uris=uris)
            for uri in uris:
                tracks.extend(track_map[uri])

        tl_tracks = []
        max_length = self.core._config['core']['max_tracklist_length']

        for track in tracks:
            if self.get_length() >= max_length:
                raise exceptions.TracklistFull(
                    'Tracklist may contain at most %d tracks.' % max_length)

            tl_track = TlTrack(self._next_tlid, track)
            self._next_tlid += 1
            if at_position is not None:
                self._tl_tracks.insert(at_position, tl_track)
                at_position += 1
            else:
                self._tl_tracks.append(tl_track)
            tl_tracks.append(tl_track)

        if tl_tracks:
            self._increase_version()

        return tl_tracks
Example #21
0
def test_check_uri_with_valid_values():
    for value in 'foobar:', 'http://example.com', 'git+http://example.com':
        validation.check_uri(value)
Example #22
0
def test_check_uri_with_valid_values():
    for value in 'foobar:', 'http://example.com', 'git+http://example.com':
        validation.check_uri(value)
Example #23
0
def test_check_uri_with_invalid_values():
    # Note that tuple catches a potential bug with using "'foo' % arg" for
    # formatting.
    for value in ('foobar', 'htt p://example.com', None, 1234, tuple()):
        with raises(exceptions.ValidationError):
            validation.check_uri(value)
Example #24
0
def test_check_uri_error_message():
    with raises(exceptions.ValidationError) as excinfo:
        validation.check_uri('testing')
    assert "Expected a valid URI, not u'testing'" == str(excinfo.value)