Пример #1
0
def publish_data_to_queue(data, exchange, queue, error_msg):
    """ Publish specified data to the specified queue.

    Args:
        data: the data to be published
        exchange (str): the name of the exchange
        queue (str): the name of the queue
        error_msg (str): the error message to be returned in case of an error
    """
    try:
        with rabbitmq_connection._rabbitmq.get() as connection:
            channel = connection.channel
            channel.exchange_declare(exchange=exchange, exchange_type='fanout')
            channel.queue_declare(queue, durable=True)
            channel.basic_publish(
                exchange=exchange,
                routing_key='',
                body=ujson.dumps(data),
                properties=pika.BasicProperties(delivery_mode=2, ),
            )
    except pika.exceptions.ConnectionClosed as e:
        current_app.logger.error("Connection to rabbitmq closed while trying to publish: %s" % str(e), exc_info=True)
        raise APIServiceUnavailable(error_msg)
    except Exception as e:
        current_app.logger.error("Cannot publish to rabbitmq channel: %s / %s" % (type(e).__name__, str(e)), exc_info=True)
        raise APIServiceUnavailable(error_msg)
Пример #2
0
 def decorator(*args, **kwargs):
     from listenbrainz.webserver.errors import APIServiceUnavailable
     if timescale_connection._ts is None:
         raise APIServiceUnavailable(
             "The listen database is momentarily offline. " +
             "Please wait a few minutes and try again.")
     return func(*args, **kwargs)
Пример #3
0
def get_listen_count(user_name):
    """
        Get the number of listens for a user ``user_name``.

        The returned listen count has an element 'payload' with only key: 'count'
        which unsurprisingly contains the listen count for the user.

    :statuscode 200: Yay, you have listen counts!
    :statuscode 404: The requested user was not found.
    :resheader Content-Type: *application/json*
    """
    user = db_user.get_by_mb_id(user_name)
    if user is None:
        raise APINotFound("Cannot find user: %s" % user_name)

    try:
        listen_count = timescale_connection._ts.get_listen_count_for_user(user["id"])
    except psycopg2.OperationalError as err:
        current_app.logger.error("cannot fetch user listen count: ", str(err))
        raise APIServiceUnavailable(
            "Cannot fetch user listen count right now.")

    return jsonify({'payload': {
        'count': listen_count
    }})
Пример #4
0
def delete_listen():
    """
    Delete a particular listen from a user's listen history.
    This checks for the correct authorization token and deletes the listen.

    .. note::

        The listen is not deleted immediately, but is scheduled for deletion, which
        usually happens shortly after the hour.

    The format of the JSON to be POSTed to this endpoint is:

    .. code-block:: json

        {
            "listened_at": 1,
            "recording_msid": "d23f4719-9212-49f0-ad08-ddbfbfc50d6f"
        }

    :reqheader Authorization: Token <user token>
    :reqheader Content-Type: *application/json*
    :statuscode 200: listen deleted.
    :statuscode 400: invalid JSON sent, see error message for details.
    :statuscode 401: invalid authorization. See error message for details.
    :resheader Content-Type: *application/json*
    """
    user = validate_auth_header()

    data = request.json

    if "listened_at" not in data:
        log_raise_400("Listen timestamp missing.")
    try:
        listened_at = data["listened_at"]
        listened_at = int(listened_at)
    except ValueError:
        log_raise_400("%s: Listen timestamp invalid." % listened_at)

    if "recording_msid" not in data:
        log_raise_400("Recording MSID missing.")

    recording_msid = data["recording_msid"]
    if not is_valid_uuid(recording_msid):
        log_raise_400("%s: Recording MSID format invalid." % recording_msid)

    try:
        timescale_connection._ts.delete_listen(listened_at=listened_at,
                                               recording_msid=recording_msid, user_id=user["id"])
    except TimescaleListenStoreException as e:
        current_app.logger.error("Cannot delete listen for user: %s" % str(e))
        raise APIServiceUnavailable(
            "We couldn't delete the listen. Please try again later.")
    except Exception as e:
        current_app.logger.error("Cannot delete listen for user: %s" % str(e))
        raise APIInternalServerError(
            "We couldn't delete the listen. Please try again later.")

    return jsonify({'status': 'ok'})
Пример #5
0
def _send_listens_to_queue(listen_type, listens):
    submit = []
    for listen in listens:
        if listen_type == LISTEN_TYPE_PLAYING_NOW:
            try:
                listen = handle_playing_now(listen)
                if listen:
                    submit.append(listen)
            except Exception:
                current_app.logger.error(
                    "Redis rpush playing_now write error: ", exc_info=True)
                raise APIServiceUnavailable(
                    "Cannot record playing_now at this time.")
        else:
            submit.append(listen)

    if submit:
        # check if rabbitmq connection exists or not
        # and if not then try to connect
        try:
            rabbitmq_connection.init_rabbitmq_connection(current_app)
        except ConnectionError as e:
            current_app.logger.error('Cannot connect to RabbitMQ: %s' % str(e))
            raise APIServiceUnavailable(
                'Cannot submit listens to queue, please try again later.')

        if listen_type == LISTEN_TYPE_PLAYING_NOW:
            exchange = current_app.config['PLAYING_NOW_EXCHANGE']
            queue = current_app.config['PLAYING_NOW_QUEUE']
        else:
            exchange = current_app.config['INCOMING_EXCHANGE']
            queue = current_app.config['INCOMING_QUEUE']

        publish_data_to_queue(
            data=submit,
            exchange=exchange,
            queue=queue,
            error_msg='Cannot submit listens to queue, please try again later.',
        )
Пример #6
0
def refresh_spotify_token():
    spotify_user = spotify.get_user(current_user.id)
    if not spotify_user:
        raise APINotFound("User has not authenticated to Spotify")
    if spotify_user.token_expired:
        try:
            spotify_user = spotify.refresh_user_token(spotify_user)
        except spotify.SpotifyAPIError:
            raise APIServiceUnavailable("Cannot refresh Spotify token right now")

    return jsonify({
        'id': current_user.id,
        'musicbrainz_id': current_user.musicbrainz_id,
        'user_token': spotify_user.user_token,
        'permission': spotify_user.permission,
    })
Пример #7
0
def refresh_service_token(service_name: str):
    service = _get_service_or_raise_404(service_name)
    user = service.get_user(current_user.id)
    if not user:
        raise APINotFound("User has not authenticated to %s" %
                          service_name.capitalize())

    if service.user_oauth_token_has_expired(user):
        try:
            user = service.refresh_access_token(current_user.id,
                                                user["refresh_token"])
        except ExternalServiceInvalidGrantError:
            raise APINotFound("User has revoked authorization to %s" %
                              service_name.capitalize())
        except Exception:
            raise APIServiceUnavailable("Cannot refresh %s token right now" %
                                        service_name.capitalize())

    return jsonify({"access_token": user["access_token"]})
Пример #8
0
def get_listen_count(user_name):
    """
        Get the number of listens for a user ``user_name``.

        The returned listen count has an element 'payload' with only key: 'count'
        which unsurprisingly contains the listen count for the user.

    :statuscode 200: Yay, you have listen counts!
    :resheader Content-Type: *application/json*
    """

    try:
        db_conn = webserver.create_timescale(current_app)
        listen_count = db_conn.get_listen_count_for_user(user_name)
        if listen_count < 0:
            raise APINotFound("Cannot find user: %s" % user_name)
    except psycopg2.OperationalError as err:
        current_app.logger.error("cannot fetch user listen count: ", str(err))
        raise APIServiceUnavailable(
            "Cannot fetch user listen count right now.")

    return jsonify({'payload': {'count': listen_count}})
Пример #9
0
def _messybrainz_lookup(listens):

    msb_listens = []
    for listen in listens:
        messy_dict = {
            'artist': listen['track_metadata']['artist_name'],
            'title': listen['track_metadata']['track_name'],
        }
        if 'release_name' in listen['track_metadata']:
            messy_dict['release'] = listen['track_metadata']['release_name']

        if 'additional_info' in listen['track_metadata']:
            ai = listen['track_metadata']['additional_info']
            if 'artist_mbids' in ai and isinstance(ai['artist_mbids'], list):
                messy_dict['artist_mbids'] = ai['artist_mbids']
            if 'release_mbid' in ai:
                messy_dict['release_mbid'] = ai['release_mbid']
            if 'recording_mbid' in ai:
                messy_dict['recording_mbid'] = ai['recording_mbid']
            if 'track_number' in ai:
                messy_dict['track_number'] = ai['track_number']
            if 'spotify_id' in ai:
                messy_dict['spotify_id'] = ai['spotify_id']
        msb_listens.append(messy_dict)

    try:
        msb_responses = messybrainz.submit_listens(msb_listens)
    except messybrainz.exceptions.BadDataException as e:
        log_raise_400(str(e))
    except messybrainz.exceptions.NoDataFoundException:
        return []
    except messybrainz.exceptions.ErrorAddingException as e:
        raise APIServiceUnavailable(str(e))

    augmented_listens = []
    for listen, messybrainz_resp in zip(listens, msb_responses['payload']):
        messybrainz_resp = messybrainz_resp['ids']

        if 'additional_info' not in listen['track_metadata']:
            listen['track_metadata']['additional_info'] = {}

        try:
            listen['recording_msid'] = messybrainz_resp['recording_msid']
            listen['track_metadata']['additional_info']['artist_msid'] = messybrainz_resp['artist_msid']
        except KeyError:
            current_app.logger.error("MessyBrainz did not return a proper set of ids")
            raise APIInternalServerError

        try:
            listen['track_metadata']['additional_info']['release_msid'] = messybrainz_resp['release_msid']
        except KeyError:
            pass

        artist_mbids = messybrainz_resp.get('artist_mbids', [])
        release_mbid = messybrainz_resp.get('release_mbid', None)
        recording_mbid = messybrainz_resp.get('recording_mbid', None)

        if 'artist_mbids'   not in listen['track_metadata']['additional_info'] and \
           'release_mbid'   not in listen['track_metadata']['additional_info'] and \
           'recording_mbid' not in listen['track_metadata']['additional_info']:

            if len(artist_mbids) > 0 and release_mbid and recording_mbid:
                listen['track_metadata']['additional_info']['artist_mbids'] = artist_mbids
                listen['track_metadata']['additional_info']['release_mbid'] = release_mbid
                listen['track_metadata']['additional_info']['recording_mbid'] = recording_mbid

        augmented_listens.append(listen)
    return augmented_listens