示例#1
0
def add_playlist_item(playlist_mbid, offset):
    """
    Append recordings to an existing playlist by posting a playlist with one of more recordings in it.
    The playlist must be in JSPF format with MusicBrainz extensions, which is defined here:
    https://musicbrainz.org/doc/jspf .

    If the offset is provided in the URL, then the recordings will be added at that offset,
    otherwise they will be added at the end of the playlist.

    You may only add :data:`~webserver.views.playlist_api.MAX_RECORDINGS_PER_ADD` recordings in one
    call to this endpoint.

    :reqheader Authorization: Token <user token>
    :statuscode 200: playlist accepted.
    :statuscode 400: invalid JSON sent, see error message for details.
    :statuscode 401: invalid authorization. See error message for details.
    :statuscode 403: forbidden. the requesting user was not allowed to carry out this operation.
    :resheader Content-Type: *application/json*
    """

    user = validate_auth_header()
    if offset is not None and offset < 0:
        log_raise_400("Offset must be a positive integer.")

    if not is_valid_uuid(playlist_mbid):
        log_raise_400("Provided playlist ID is invalid.")

    playlist = db_playlist.get_by_mbid(playlist_mbid)
    if playlist is None or not playlist.is_visible_by(user["id"]):
        raise APINotFound("Cannot find playlist: %s" % playlist_mbid)

    if not playlist.is_modifiable_by(user["id"]):
        raise APIForbidden("You are not allowed to add recordings to this playlist.")

    data = request.json
    validate_playlist(data)

    if len(data["playlist"]["track"]) > MAX_RECORDINGS_PER_ADD:
        log_raise_400("You may only add max %d recordings per call." % MAX_RECORDINGS_PER_ADD)

    precordings = []
    if "track" in data["playlist"]:
        for track in data["playlist"]["track"]:
            try:
                mbid = UUID(track['identifier'][len(PLAYLIST_TRACK_URI_PREFIX):])
            except (KeyError, ValueError):
                log_raise_400("Track %d has an invalid identifier field, it must be a complete URI.")
            precordings.append(WritablePlaylistRecording(mbid=mbid, added_by_id=user["id"]))

    try:
        db_playlist.add_recordings_to_playlist(playlist, precordings, offset)
    except Exception as e:
        current_app.logger.error("Error while adding recordings to playlist: {}".format(e))
        raise APIInternalServerError("Failed to add recordings to the playlist. Please try again.")

    return jsonify({'status': 'ok'})
示例#2
0
def load_instant():
    """
    This endpoint takes in a list of recording_mbids and optional desc/name arguments  and then loads
    the recording_mbid's metadata and creates a JSPF file from this data and sends it to the front end
    so a playlist can be instantly played.

    .. note::
        We recommend that you do not send more than 50 recording_mbids in one request -- our
        server infrastructure will likely give you a gateway error (502) if you do.

    :param recording_mbids: A comma separated list of recording_mbids
    :type recording_mbids: ``str``
    :param desc: A description for this instant playlist (optional).
    :type desc: ``str``
    :param name: A name for this instant playlist (optional).
    :type name: ``str``
    :statuscode 200: playlist generated
    :statuscode 400: invalid recording_mbid arguments
    """

    recordings = request.args.get("recording_mbids", default=None)
    if recordings is None:
        raise BadRequest("recording_mbids argument must be present and contain a comma separated list of recording_mbids")

    recording_mbids = []
    for mbid in recordings.split(","):
        mbid_clean = mbid.strip()
        if not is_valid_uuid(mbid_clean):
            raise BadRequest(f"Recording mbid {mbid} is not valid.")

        recording_mbids.append(mbid_clean)

    desc = request.args.get("desc", default="")
    if not desc:
        desc = "Instant playlist"

    name = request.args.get("name", default="")
    if not name:
        name = "Instant playlist"

    now = datetime.now()
    playlist = WritablePlaylist(description=desc, name=name, creator="listenbrainz", creator_id=1, created=now)
    for i, mbid in enumerate(recording_mbids):
        rec = WritablePlaylistRecording(position=i, mbid=mbid, added_by_id=1, created=now)
        playlist.recordings.append(rec)

    fetch_playlist_recording_metadata(playlist)

    return render_template(
        "player/player-page.html",
        props=ujson.dumps({"playlist": serialize_jspf(playlist)})
    )
示例#3
0
def load_release(release_mbid):
    """
    This endpoint takes a release mbid, loads the tracks for this release and makes a playlist from it and
    sends it to the front end via JSPF.

    :statuscode 200: playlist generated
    :statuscode 400: invalid recording_mbid arguments
    """

    release_mbid = release_mbid.strip()
    if not is_valid_uuid(release_mbid):
        raise BadRequest(f"Recording mbid {release_mbid} is not valid.")

    playlist = None
    if mb_engine:
        release = get_release_by_mbid(release_mbid, includes=["media", "artists"])
        if not release:
            raise NotFound("This release was not found in our database. It may not have replicated to this server yet.")

        name = "Release %s by %s" % (release["name"], release["artist-credit-phrase"])
        desc = 'Release <a href="https://musicbrainz.org/release/%s">%s</a> by %s' % (release["mbid"],
                                                                                      release["name"],
                                                                                      release["artist-credit-phrase"])
        now = datetime.now()
        playlist = WritablePlaylist(description=desc, name=name, creator="listenbrainz", creator_id=1, created=now)
        for medium in release["medium-list"]:
            for recording in medium["track-list"]:
                rec = WritablePlaylistRecording(title=recording["name"],
                                                artist_credit=release["artist-credit-phrase"],
                                                artist_mbids=[a["artist"]["mbid"] for a in recording["artist-credit"]],
                                                release_name=release["name"],
                                                release_mbid=release["mbid"],
                                                position=recording["position"],
                                                mbid=recording["recording_id"],
                                                added_by_id=1, created=now)
                playlist.recordings.append(rec)

    return render_template(
        "player/player-page.html",
        props=ujson.dumps({"playlist": serialize_jspf(playlist) if playlist is not None else {}})
    )
示例#4
0
def create_playlist():
    """
    Create a playlist. The playlist must be in JSPF format with MusicBrainz extensions, which is defined
    here: https://musicbrainz.org/doc/jspf . To create an empty playlist, you can send an empty playlist
    with only the title field filled out. If you would like to create a playlist populated with recordings,
    each of the track items in the playlist must have an identifier element that contains the MusicBrainz
    recording that includes the recording MBID.

    When creating a playlist, only the playlist title and the track identifier elements will be used -- all
    other elements in the posted JSPF wil be ignored.

    If a created_for field is found and the user is not an approved playlist bot, then a 403 forbidden will be raised.

    :reqheader Authorization: Token <user token>
    :statuscode 200: playlist accepted.
    :statuscode 400: invalid JSON sent, see error message for details.
    :statuscode 401: invalid authorization. See error message for details.
    :statuscode 403: forbidden. The submitting user is not allowed to create playlists for other users.
    :resheader Content-Type: *application/json*
    """

    user = validate_auth_header()

    data = request.json
    validate_create_playlist_required_items(data)
    validate_playlist(data)

    public = data["playlist"]["extension"][PLAYLIST_EXTENSION_URI]["public"]
    collaborators = data.get("playlist", {}).\
        get("extension", {}).get(PLAYLIST_EXTENSION_URI, {}).\
        get("collaborators", [])

    # Uniquify collaborators list
    collaborators = list(set(collaborators))

    # Don't allow creator to also be a collaborator
    if user["musicbrainz_id"] in collaborators:
        collaborators.remove(user["musicbrainz_id"])

    username_lookup = collaborators
    created_for = data["playlist"].get("created_for", None)
    if created_for:
        username_lookup.append(created_for)

    users = {}
    if username_lookup:
        users = db_user.get_many_users_by_mb_id(username_lookup)

    collaborator_ids = []
    for collaborator in collaborators:
        if collaborator.lower() not in users:
            log_raise_400("Collaborator {} doesn't exist".format(collaborator))
        collaborator_ids.append(users[collaborator.lower()]["id"])

    # filter description
    description = data["playlist"].get("annotation", None)
    if description is not None:
        description = _filter_description_html(description)

    playlist = WritablePlaylist(name=data['playlist']['title'],
                                creator_id=user["id"],
                                description=description,
                                collaborator_ids=collaborator_ids,
                                collaborators=collaborators,
                                public=public)

    if data["playlist"].get("created_for", None):
        if user["musicbrainz_id"] not in current_app.config["APPROVED_PLAYLIST_BOTS"]:
            raise APIForbidden("Playlist contains a created_for field, but submitting user is not an approved playlist bot.")
        created_for_user = users.get(data["playlist"]["created_for"].lower())
        if not created_for_user:
            log_raise_400("created_for user does not exist.")
        playlist.created_for_id = created_for_user["id"]

    if "track" in data["playlist"]:
        for track in data["playlist"]["track"]:
            try:
                playlist.recordings.append(WritablePlaylistRecording(mbid=UUID(track['identifier'][len(PLAYLIST_TRACK_URI_PREFIX):]),
                                           added_by_id=user["id"]))
            except ValueError:
                log_raise_400("Invalid recording MBID found in submitted recordings")

    try:
        playlist = db_playlist.create(playlist)
    except Exception as e:
        current_app.logger.error("Error while creating new playlist: {}".format(e))
        raise APIInternalServerError("Failed to create the playlist. Please try again.")

    return jsonify({'status': 'ok', 'playlist_mbid': playlist.mbid})