示例#1
0
def account_get(username_or_id):
    """
    Returns Account
    ---
    tags:
        - Accounts
    responses:
      200:
        description: Returns Account
        schema:
            $ref: '#/definitions/Account'
    """
    if username_or_id.isdigit():
        # an int is DB ID
        user = User.query.filter(User.id == int(username_or_id)).first()
    else:
        # a string is Local User
        user = User.query.filter(User.name == username_or_id,
                                 User.local.is_(True)).first()

    if not user:
        abort(404)
    if len(user.actor) != 1:
        abort(404)

    relationship = False
    if current_token and current_token.user:
        relationship = to_json_relationship(current_token.user, user)
    account = to_json_account(user, relationship)
    return jsonify(account)
示例#2
0
def account_get(username_or_id):
    """
    Returns Account
    ---
    tags:
        - Accounts
    responses:
      200:
        description: Returns Account
        schema:
            $ref: '#/definitions/Account'
    """
    user = User.query.filter(User.name == username_or_id, User.local.is_(True)).first()
    if not user:
        try:
            user = User.query.filter(User.flake_id == username_or_id).first()
        except sqlalchemy.exc.DataError:
            abort(404)

    if not user:
        abort(404)
    if len(user.actor) != 1:
        abort(404)

    relationship = False
    if current_token and current_token.user:
        relationship = to_json_relationship(current_token.user, user)
    account = to_json_account(user, relationship)
    return jsonify(account)
示例#3
0
def get(username_or_id, albumslug):
    """
    Get album details.
    ---
    tags:
        - Albums
    parameters:
        - name: user_id
          in: path
          type: integer
          required: true
          description: User ID
        - name: albumslug
          in: path
          type: string
          required: true
          description: Album slug
    responses:
        200:
            description: Returns album object.
    """
    # Get logged in user from bearer token, or None if not logged in
    if current_token:
        current_user = current_token.user
    else:
        current_user = None

    # Get the associated User from url fetch
    if username_or_id.isdigit():
        album_user = User.query.filter(User.id == username_or_id).first()
    else:
        album_user = User.query.filter(User.name == username_or_id, User.local.is_(True)).first()
    if not album_user:
        return jsonify({"error": "User not found"}), 404

    if current_user and album_user.id == current_user.id:
        # we have a valid token, and album user is token user, can fetch private
        album = Album.query.filter(Album.slug == albumslug, Album.user_id == album_user.id).first()
    else:
        # only fetch public ones
        album = Album.query.filter(
            Album.slug == albumslug, Album.user_id == album_user.id, Album.private.is_(False)
        ).first()

    if not album:
        return jsonify({"error": "not found"}), 404

    if album.private:
        if current_user:
            if album.user_id != current_user.id:
                return jsonify({"error": "forbidden"}), 403
        else:
            return jsonify({"error": "forbidden"}), 403

    relationship = to_json_relationship(current_user, album.user)
    account = to_json_account(album.user, relationship)
    resp = to_json_album(album, account)

    return jsonify(resp)
示例#4
0
def following(user_id):
    """
    Accounts followed by the given account.
    ---
    tags:
        - Accounts
    parameters:
        - name: id
          in: path
          type: integer
          required: true
          description: User ID to follow
        - name: count
          in: query
          type: integer
          required: true
          description: count per page
        - name: page
          in: query
          type: integer
          description: page number
    responses:
      200:
        description: Returns paginated array of Account
        schema:
            $ref: '#/definitions/Account'
    """
    user = User.query.filter(User.id == user_id).first()
    if not user:
        abort(404)

    count = int(request.args.get("count", 20))
    page = int(request.args.get("page", 1))

    q = user.actor[0].followings
    q = q.paginate(page=page, per_page=count)

    followings = []
    for t in q.items:
        # Note: the items are Follower(actor, target)
        # Where actor is `user` since we are asking his followers
        # And target = the user following `user`
        relationship = False
        if current_token and current_token.user:
            relationship = to_json_relationship(current_token.user,
                                                t.target.user)
        account = to_json_account(t.target.user, relationship)
        followings.append(account)

    resp = {
        "page": page,
        "page_size": count,
        "totalItems": q.total,
        "items": followings,
        "totalPages": q.pages
    }
    return jsonify(resp)
示例#5
0
def albums():
    """
    User albums timeline.
    ---
    tags:
        - Timelines
    parameters:
        - name: count
          in: query
          type: integer
          required: true
          description: count
        - name: page
          in: query
          type: integer
          description: page number
        - name: user
          in: query
          type: string
          description: the user flake id to get albums list
    responses:
        200:
            description: Returns array of Status
    """
    count = int(request.args.get("count", 20))
    page = int(request.args.get("page", 1))
    user = request.args.get("user", None)
    if not user:
        abort(400)

    user = User.query.filter(User.flake_id == user).first()
    if not user:
        return jsonify({"error": "User does not exist"}), 404

    q = Album.query.order_by(Album.created.desc())

    only_public = True
    if current_token and current_token.user:
        if user.id == current_token.user.id:
            only_public = False

    if only_public:
        q = q.filter(Album.user_id == user.id, Album.private.is_(False))
    else:
        q = q.filter(Album.user_id == current_token.user.id)

    q = q.paginate(page=page, per_page=count)

    albums = []
    for t in q.items:
        relationship = False
        if current_token and current_token.user:
            relationship = to_json_relationship(current_token.user, t.user)
        account = to_json_account(t.user, relationship)
        albums.append(to_json_album(t, account))
    resp = {"page": page, "page_size": count, "totalItems": q.total, "items": albums, "totalPages": q.pages}
    return jsonify(resp)
示例#6
0
def unprocessed():
    """
    User unprocessed tracks timeline.
    ---
    tags:
        - Timelines
    parameters:
        - name: count
          in: query
          type: integer
          required: true
          description: count
        - name: page
          in: query
          type: integer
          description: page number
    responses:
        200:
            description: Returns array of Status
    """
    user = current_token.user
    if not user:
        return jsonify({"error": "Unauthorized"}), 403

    count = int(request.args.get("count", 20))
    page = int(request.args.get("page", 1))

    q = Sound.query.filter(
        Sound.user_id == user.id,
        Sound.transcode_state.in_(
            (Sound.TRANSCODE_WAITING, Sound.TRANSCODE_PROCESSING,
             Sound.TRANSCODE_ERROR)),
    )

    q = q.order_by(Sound.uploaded.desc())

    q = q.paginate(page=page, per_page=count)

    tracks = []
    for t in q.items:
        relationship = to_json_relationship(current_token.user, t.user)
        account = to_json_account(t.user, relationship)
        tracks.append(to_json_track(t, account))
    resp = {
        "page": page,
        "page_size": count,
        "totalItems": q.total,
        "items": tracks,
        "totalPages": q.pages
    }
    return jsonify(resp)
示例#7
0
def reorder(username, albumslug):
    """
    Edit album tracks order.
    ---
    tags:
        - Albums
    security:
        - OAuth2:
            - write
    responses:
        200:
            description: Returns a Status with extra reel2bits params.
    """
    current_user = current_token.user
    if not current_user:
        return jsonify({"error": "Unauthorized"}), 403

    # Get the album
    album = Album.query.filter(Album.user_id == current_user.id,
                               Album.slug == albumslug).first()
    if not album:
        return jsonify({"error": "Not found"}), 404

    pos = 0
    for track in request.json:
        dbt = Sound.query.filter(Sound.flake_id == track["id"],
                                 Sound.album_id == album.id).first()
        if not dbt:
            return jsonify({"error": "Not found"}), 404
        dbt.album_order = pos
        pos += 1
    db.session.commit()

    relationship = to_json_relationship(current_user, album.user)
    account = to_json_account(album.user, relationship)
    resp = to_json_album(album, account)

    return jsonify(resp)
示例#8
0
def edit(username, soundslug):
    """
    Edit track.
    ---
    tags:
        - Tracks
    security:
        - OAuth2:
            - write
    parameters:
        - name: username
          in: path
          type: string
          required: true
          description: User username
        - name: soundslug
          in: path
          type: string
          required: true
          description: Track slug
    responses:
        200:
            description: Returns a Status with extra reel2bits params.
    """
    current_user = current_token.user
    if not current_user:
        return jsonify({"error": "Unauthorized"}), 403

    # Get the track
    sound = Sound.query.filter(Sound.user_id == current_user.id, Sound.slug == soundslug).first()
    if not sound:
        return jsonify({"error": "Not found"}), 404

    album = request.json.get("album")
    description = request.json.get("description")
    licence = request.json.get("licence")
    private = request.json.get("private")
    title = request.json.get("title")
    genre = request.json.get("genre")
    tags = request.json.get("tags")

    if sound.private and not private:
        return jsonify({"error": "Cannot change to private: track already federated"})

    if not title:
        title, _ = splitext(sound.filename_orig)
    else:
        sound.title = title

    sound.description = description
    sound.licence = licence
    sound.genre = genre

    # First remove tags which have been removed
    for tag in sound.tags:
        if tag.name not in tags:
            sound.tags.remove(tag)

    # Then add the new ones if new
    for tag in tags:
        if tag not in [a.name for a in sound.tags]:
            dbt = SoundTag.query.filter(SoundTag.name == tag).first()
            if not dbt:
                dbt = SoundTag(name=tag)
                db.session.add(dbt)
            sound.tags.append(dbt)

    # Purge orphaned tags
    for otag in SoundTag.query.filter(and_(~SoundTag.sounds.any(), ~SoundTag.albums.any())).all():
        db.session.delete(otag)

    # Fetch album, and associate if owner
    if album and (album != "__None"):
        db_album = Album.query.filter(Album.id == album).first()
        if db_album and (db_album.user_id == current_user.id):
            sound.album_id = db_album.id
            if not db_album.sounds:
                sound.album_order = 0
            else:
                sound.album_order = db_album.sounds.count() + 1
    elif album == "__None":
        sound.album_id = None
        sound.album_order = 0

    db.session.commit()

    # trigger a sound update
    send_update_sound(sound)

    relationship = False
    if current_token and current_token.user:
        relationship = to_json_relationship(current_token.user, sound.user)
    account = to_json_account(sound.user, relationship)
    return jsonify(to_json_track(sound, account))
示例#9
0
def show(username_or_id, soundslug):
    """
    Get track details.
    ---
    tags:
        - Tracks
    parameters:
        - name: user_id
          in: path
          type: integer
          required: true
          description: User ID
        - name: soundslug
          in: path
          type: string
          required: true
          description: Track slug
    responses:
        200:
            description: Returns track details.
    """
    # Get logged in user from bearer token, or None if not logged in
    if current_token:
        current_user = current_token.user
    else:
        current_user = None

    # Get the associated User from url fetch
    track_user = User.query.filter(User.name == username_or_id, User.local.is_(True)).first()
    if not track_user:
        try:
            track_user = User.query.filter(User.flake_id == username_or_id).first()
        except sqlalchemy.exc.DataError:
            return jsonify({"error": "User not found"}), 404
    if not track_user:
        return jsonify({"error": "User not found"}), 404

    if current_user and (track_user.id == current_user.id):
        print("user")
        sound = Sound.query.filter(Sound.slug == soundslug, Sound.user_id == track_user.id).first()
    else:
        print("no user")
        sound = Sound.query.filter(
            Sound.slug == soundslug, Sound.user_id == track_user.id, Sound.transcode_state == Sound.TRANSCODE_DONE
        ).first()

    if not sound:
        print("mmmh")
        return jsonify({"error": "not found"}), 404

    if sound.private:
        if current_user:
            if sound.user_id != current_user.id:
                return jsonify({"error": "forbidden"}), 403
        else:
            return jsonify({"error": "forbidden"}), 403

    relationship = False
    if current_token and current_token.user:
        relationship = to_json_relationship(current_token.user, sound.user)
    account = to_json_account(sound.user, relationship)
    return jsonify(to_json_track(sound, account))
示例#10
0
def search():
    """
    Search.
    ---
    tags:
        - Global
    parameters:
        - name: q
          in: query
          type: string
          required: true
          description: search string
    responses:
        200:
            description: fixme.
    """
    # Get logged in user from bearer token, or None if not logged in
    if current_token:
        current_user = current_token.user
    else:
        current_user = None

    s = request.args.get("q", None)
    if not s:
        return jsonify({"error": "No search string provided"}), 400

    # This is the old search endpoint and needs to be improved
    # Especially tracks and accounts needs to be returned in the right format, with the data helpers
    # Users should be searched from known Actors or fetched
    # URI should be searched from known activities or fetched
    # FTS, well, FTS needs to be implemented

    results = {"accounts": [], "sounds": [], "mode": None, "from": None}

    if current_user:
        results["from"] = current_user.name

    # Search for sounds
    # TODO: Implement FTS to get sounds search
    sounds = []

    # Search for accounts
    accounts = []
    is_user_at_account = RE_ACCOUNT.match(s)

    if s.startswith("https://"):
        # Try to match the URI from Activities in database
        results["mode"] = "uri"
        users = Actor.query.filter(Actor.meta_deleted.is_(False),
                                   Actor.url == s).all()
    elif is_user_at_account:
        # It matches [email protected], try to match it locally
        results["mode"] = "acct"
        user = is_user_at_account.group("user")
        instance = is_user_at_account.group("instance")
        users = Actor.query.filter(Actor.meta_deleted.is_(False),
                                   Actor.preferred_username == user,
                                   Actor.domain == instance).all()
    else:
        # It's a FTS search
        results["mode"] = "username"
        # Match actor username in database
        if current_user:
            users = (db.session.query(Actor, Follower).outerjoin(
                Follower,
                and_(Actor.id == Follower.target_id,
                     Follower.actor_id == current_user.actor[0].id)).filter(
                         or_(Actor.preferred_username.contains(s),
                             Actor.name.contains(s))).filter(
                                 not_(Actor.id ==
                                      current_user.actor[0].id)).all())
        else:
            users = (db.session.query(Actor).filter(
                or_(Actor.preferred_username.contains(s),
                    Actor.name.contains(s))).all())

    # Handle the found users
    if len(users) > 0:
        for actor in users:
            relationship = False
            if current_user:
                relationship = to_json_relationship(current_user, actor.user)
            accounts.append(to_json_account(actor.user, relationship))

    if len(accounts) <= 0:
        # Do a webfinger
        # TODO FIXME: We should do this only if https:// or user@account submitted
        # And rework it slightly differently since we needs to backend.fetch_iri() for https:// who
        # can match a Sound and not only an Actor
        current_app.logger.debug(f"webfinger for {s}")
        try:
            remote_actor_url = get_actor_url(s, debug=current_app.debug)
            # We need to get the remote Actor
            backend = ap.get_backend()
            iri = backend.fetch_iri(remote_actor_url)
            if iri:
                # We have fetched an unknown Actor
                # Save it in database and return it properly
                current_app.logger.debug(
                    f"got remote actor URL {remote_actor_url}")

                act = ap.parse_activity(iri)

                fetched_actor, fetched_user = create_remote_actor(act)
                db.session.add(fetched_user)
                db.session.add(fetched_actor)
                db.session.commit()

                relationship = False
                if current_user:
                    relationship = to_json_relationship(
                        current_user, fetched_user)
                accounts.append(to_json_account(fetched_user, relationship))
                results["mode"] = "webfinger"

        except (InvalidURLError, ValueError):
            current_app.logger.exception(f"Invalid AP URL: {s}")
            # Then test fetching as a "normal" Activity ?
    # Finally fill the results dict
    results["accounts"] = accounts

    # FIXME: handle exceptions
    if results["mode"] == "uri" and len(sounds) <= 0:
        backend = ap.get_backend()
        iri = backend.fetch_iri(s)
        if iri:
            # FIXME: Is INBOX the right choice here ?
            backend.save(Box.INBOX, iri)
        # Fetch again, but get it from database
        activity = Activity.query.filter(Activity.url == iri).first()
        if not activity:
            current_app.logger.exception("WTF Activity is not saved")
        else:
            from tasks import create_sound_for_remote_track, upload_workflow

            sound_id = create_sound_for_remote_track(activity)
            sound = Sound.query.filter(Sound.id == sound_id).one()
            upload_workflow.delay(sound.id)

            relationship = False
            if current_user:
                relationship = to_json_relationship(current_user, sound.user)
            acct = to_json_account(sound.user, relationship)
            sounds.append(to_json_track(sound, acct))

    return jsonify({"who": s, "results": results})
示例#11
0
def user_statuses(user_id):
    """
    User statuses.
    ---
    tags:
        - Timelines
    parameters:
        - name: count
          in: query
          type: integer
          required: true
          description: count per page
        - name: with_muted
          in: query
          type: boolean
          required: true
          description: with muted users
        - name: page
          in: query
          type: integer
          description: page number
    responses:
        200:
            description: Returns array of Status
    """
    # Caveats: only handle public Sounds since we either federate (public) or no
    count = int(request.args.get("count", 20))
    page = int(request.args.get("page", 1))

    # Get associated user
    user = User.query.filter(User.id == user_id).first()
    if not user:
        abort(404)

    q = db.session.query(Activity, Sound).filter(
        Activity.type == "Create",
        Activity.payload[("object", "type")].astext == "Audio")
    q = q.filter(Activity.meta_deleted.is_(False))

    q = q.filter(Activity.payload["to"].astext.contains(
        "https://www.w3.org/ns/activitystreams#Public"))

    q = q.filter(Activity.actor == user.actor[0].id)

    q = q.join(Sound, Sound.activity_id == Activity.id)
    q = q.order_by(Activity.creation_date.desc())

    q = q.paginate(page=page, per_page=count)

    tracks = []
    for t in q.items:
        if t.Sound:
            relationship = False
            if current_token and current_token.user:
                relationship = to_json_relationship(current_token.user,
                                                    t.Sound.user)
            account = to_json_account(t.Sound.user, relationship)
            tracks.append(to_json_track(t.Sound, account))
        else:
            print(t.Activity)
    resp = {
        "page": page,
        "page_size": count,
        "totalItems": q.total,
        "items": tracks,
        "totalPages": q.pages
    }
    return jsonify(resp)
示例#12
0
def accounts_update_credentials():
    """
    Update user’s own account.
    ---
    tags:
        - Accounts
    security:
        - OAuth2:
            - write
    responses:
        200:
            description: Returns Account with extra Source and Pleroma attributes.
            schema:
                allOf:
                    - $ref: '#/definitions/Account'
                    - $ref: '#/definitions/Source'
                    - $ref: '#/definitions/AccountPleroma'
    """
    current_user = current_token.user

    if not current_user:
        # WTF ?
        return jsonify({"error": "User not found"}), 404

    # Update fields like bio, language, etc.
    if request.json:
        r_lang = request.json.get("lang", None)
        r_fullname = request.json.get("fullname", None)
        r_bio = request.json.get("bio", None)

        if r_lang:
            current_user.locale = r_lang
        if r_fullname:
            current_user.display_name = r_fullname
        if r_bio:
            current_user.actor[0].summary = r_bio
    elif request.files:
        # Update things like user background, profile picture, etc.
        if "avatar" in request.files:
            avatar_uploaded = request.files["avatar"]
            avatar_uploaded.seek(0, os.SEEK_END)
            avatar_size = avatar_uploaded.tell()
            avatar_uploaded.seek(0)
            if avatar_size > Reel2bitsDefaults.avatar_size_limit:
                return jsonify({"error": "artwork too big, 2MB maximum"
                                }), 413  # Request Entity Too Large

            # Delete old avatar if any
            if current_user.avatar_filename:
                old_avatar = os.path.join(
                    current_app.config["UPLOADED_AVATARS_DEST"],
                    current_user.path_avatar())
                if os.path.isfile(old_avatar):
                    os.unlink(old_avatar)
                else:
                    print(
                        f"Error: cannot delete old avatar: {current_user.id} / {current_user.avatar_filename}"
                    )

            # Save new avatar
            avatar_filename = get_hashed_filename(avatar_uploaded.filename)
            avatars.save(avatar_uploaded,
                         folder=current_user.slug,
                         name=avatar_filename)
            current_user.avatar_filename = avatar_filename

    # commit changes
    db.session.commit()

    # log action
    add_user_log(current_user.id, current_user.id, "user", "info",
                 "Edited user profile")

    # trigger a profile update
    send_update_profile(current_user)

    return jsonify(to_json_account(current_user))
示例#13
0
def accounts_verify_credentials():
    """
    User’s own account.
    ---
    tags:
        - Accounts
    security:
        - OAuth2:
            - read
    definitions:
      Field:
        type: object
        properties:
            name:
                type: string
                nullable: false
            value:
                type: string
                nullable: false
            verified_at:
                type: integer
                nullable: true
      Emoji:
        type: object
        properties:
            shortcode:
                type: string
                nullable: false
            static_url:
                type: string
                format: uri
                nullable: false
            url:
                type: string
                format: uri
                nullable: false
            visible_in_picker:
                type: boolean
                nullable: false
      Source:
        type: object
        properties:
            privacy:
                type: string
                nullable: true
            sensitive:
                type: boolean
                nullable: true
            language:
                type: string
                nullable: true
            note:
                type: string
                nullable: false
            fields:
                type: array
                nullable: false
                items:
                    type: object
                    $ref: '#/definitions/Field'
      AccountPleroma:
        type: object
        properties:
            pleroma:
                type: object
                properties:
                    is_admin:
                        type: boolean
      Account:
        type: object
        properties:
            id:
                type: string
                nullable: false
            username:
                type: string
                nullable: false
            acct:
                type: string
                nullable: false
            display_name:
                type: integer
                nullable: false
            locked:
                type: boolean
                nullable: false
            created_at:
                type: integer
                nullable: false
            followers_count:
                type: integer
                nullable: false
            following_count:
                type: integer
                nullable: false
            statuses_count:
                type: integer
                nullable: false
            note:
                type: string
                nullable: false
            url:
                type: string
                format: uri
                nullable: false
            avatar:
                type: string
                format: uri
                nullable: false
            avatar_static:
                type: string
                format: uri
                nullable: false
            header:
                type: string
                format: uri
                nullable: false
            header_static:
                type: string
                format: uri
                nullable: false
            emojis:
                type: hash
                nullable: false
                items:
                    type: object
                    $ref: '#/definitions/Emoji'
            moved:
                type: object
                $ref: '#/definitions/Account'
                nullable: true
            fields:
                type: array
                nullable: true
                items:
                    type: object
                    $ref: '#/definitions/Field'
            bot:
                type: boolean
                nullable: true
    responses:
        200:
            description: Returns Account with extra Source and Pleroma attributes.
            schema:
                allOf:
                    - $ref: '#/definitions/Account'
                    - $ref: '#/definitions/Source'
                    - $ref: '#/definitions/AccountPleroma'
    """
    user = current_token.user
    return jsonify(to_json_account(user))
示例#14
0
def public():
    """
    Public or TWKN statuses.
    ---
    tags:
        - Timelines
    parameters:
        - name: count
          in: query
          type: integer
          required: true
          description: count
        - name: with_muted
          in: query
          type: boolean
          required: true
          description: with muted users
        - name: local
          in: query
          type: boolean
          description: local only or TWKN
    responses:
        200:
            description: Returns array of Status
    """
    # Caveats: only handle public Sounds since we either federate (public) or no

    paginated = request.args.get("paginated", False)
    count = int(request.args.get("count", 20))
    local_only = request.args.get("local", False)

    q = db.session.query(Activity, Sound).filter(
        Activity.type == "Create", Activity.payload[("object", "type")].astext == "Audio"
    )
    q = q.filter(Activity.meta_deleted.is_(False))

    if local_only:
        q = q.filter(Activity.local.is_(True))

    q = q.filter(Activity.payload["to"].astext.contains("https://www.w3.org/ns/activitystreams#Public"))

    q = q.join(Sound, Sound.activity_id == Activity.id)
    q = q.order_by(Activity.creation_date.desc())

    if paginated:
        # Render timeline as paginated
        page = int(request.args.get("page", 1))

        q = q.paginate(page=page, per_page=count)

        tracks = []
        for t in q.items:
            if t.Sound:
                # TODO(dashie) FIXME can probably be moved out to the q.filter()
                if not t.Sound.transcode_state == Sound.TRANSCODE_DONE:
                    continue
                relationship = False
                if current_token and current_token.user:
                    relationship = to_json_relationship(current_token.user, t.Sound.user)
                account = to_json_account(t.Sound.user, relationship)
                tracks.append(to_json_track(t.Sound, account))
            else:
                print(t.Activity)
        resp = {"page": page, "page_size": count, "totalItems": q.total, "items": tracks, "totalPages": q.pages}
        return jsonify(resp)
    else:
        # mastoapi compatible
        since_id = request.args.get("since_id")

        # since then we want the timeline
        if since_id:
            q = q.filter(Sound.flake_id > since_id)

        # then limit count
        q = q.limit(count)

        tracks = []
        for t in q.all():
            if t.Sound:
                relationship = False
                if current_token and current_token.user:
                    relationship = to_json_relationship(current_token.user, t.Sound.user)
                account = to_json_account(t.Sound.user, relationship)
                tracks.append(to_json_track(t.Sound, account))
            else:
                print(t.Activity)
        return jsonify(tracks)
示例#15
0
def edit(username, albumslug):
    """
    Edit album.
    ---
    tags:
        - Albums
    security:
        - OAuth2:
            - write
    parameters:
        - name: username
          in: path
          type: string
          required: true
          description: User username
        - name: albumslug
          in: path
          type: string
          required: true
          description: Album slug
    responses:
        200:
            description: Returns a Status with extra reel2bits params.
    """
    current_user = current_token.user
    if not current_user:
        return jsonify({"error": "Unauthorized"}), 403

    # Get the album
    album = Album.query.filter(Album.user_id == current_user.id,
                               Album.slug == albumslug).first()
    if not album:
        return jsonify({"error": "Not found"}), 404

    description = request.json.get("description")
    private = request.json.get("private")
    title = request.json.get("title")
    genre = request.json.get("genre")
    tags = request.json.get("tags")

    if album.private and not private:
        return jsonify(
            {"error": "Cannot change to private: album already federated"})

    if not title:
        return jsonify({"error": "Album title is required"}), 400

    album.title = title
    album.description = description
    album.genre = genre

    # First remove tags which have been removed
    for tag in album.tags:
        if tag.name not in tags:
            album.tags.remove(tag)

    # Then add the new ones if new
    for tag in tags:
        if tag not in [a.name for a in album.tags]:
            dbt = SoundTag.query.filter(SoundTag.name == tag).first()
            if not dbt:
                dbt = SoundTag(name=tag)
                db.session.add(dbt)
            album.tags.append(dbt)

    # Purge orphaned tags
    for otag in SoundTag.query.filter(
            and_(~SoundTag.sounds.any(), ~SoundTag.albums.any())).all():
        db.session.delete(otag)

    db.session.commit()

    relationship = False
    if current_token and current_token.user:
        relationship = to_json_relationship(current_token.user, album.user)
    account = to_json_account(album.user, relationship)
    return jsonify(to_json_album(album, account))