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)
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)
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 }})
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'})
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.', )
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, })
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"]})
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}})
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