def recent_listens(): recent = [] for listen in _redis.get_recent_listens(NUMBER_OF_RECENT_LISTENS): recent.append({ "track_metadata": listen.data, "user_name" : listen.user_name, "listened_at": listen.ts_since_epoch, "listened_at_iso": listen.timestamp.isoformat() + "Z", }) spotify_user = {} if current_user.is_authenticated: spotify_user = spotify.get_user_dict(current_user.id) props = { "listens": recent, "mode": "recent", "spotify": spotify_user, "api_url": current_app.config["API_URL"], } return render_template("index/recent.html", props=ujson.dumps(props), mode='recent', active_section='listens')
def load_playlist(playlist_mbid: str): """Load a single playlist by id """ if not is_valid_uuid(playlist_mbid): raise BadRequest("Provided playlist ID is invalid: %s" % playlist_mbid) playlist = db_playlist.get_by_mbid(playlist_mbid, True) # TODO: Allow playlist collaborators to access private playlist if playlist is None or not playlist.public and (not current_user.is_authenticated or playlist.creator_id != current_user.id): raise NotFound("Cannot find playlist: %s" % playlist_mbid) fetch_playlist_recording_metadata(playlist) spotify_data = {} current_user_data = {} if current_user.is_authenticated: spotify_data = spotify.get_user_dict(current_user.id) current_user_data = { "id": current_user.id, "name": current_user.musicbrainz_id, "auth_token": current_user.auth_token, } props = { "current_user": current_user_data, "spotify": spotify_data, "api_url": current_app.config["API_URL"], "web_sockets_server_url": current_app.config['WEBSOCKETS_SERVER_URL'], "playlist": serialize_jspf(playlist) } return render_template( "playlists/playlist.html", props=ujson.dumps(props) )
def recommendation_playlists(user_name: str): """ Show playlists created for user """ if not current_app.config.get("FEATURE_PLAYLIST", False): raise NotFound() offset = request.args.get('offset', 0) try: offset = int(offset) except ValueError: raise BadRequest("Incorrect int argument offset: %s" % request.args.get("offset")) count = request.args.get("count", DEFAULT_NUMBER_OF_PLAYLISTS_PER_CALL) try: count = int(count) except ValueError: raise BadRequest("Incorrect int argument count: %s" % request.args.get("count")) user = _get_user(user_name) user_data = { "name": user.musicbrainz_id, "id": user.id, } spotify_data = {} current_user_data = {} if current_user.is_authenticated: spotify_data = spotify.get_user_dict(current_user.id) current_user_data = { "id": current_user.id, "name": current_user.musicbrainz_id, "auth_token": current_user.auth_token, } playlists = [] user_playlists, playlist_count = get_playlists_created_for_user(user.id, False, count, offset) for playlist in user_playlists: playlists.append(serialize_jspf(playlist)) props = { "current_user": current_user_data, "api_url": current_app.config["API_URL"], "playlists": playlists, "user": user_data, "active_section": "recommendations", "playlist_count": playlist_count, } return render_template( "playlists/playlists.html", active_section="recommendations", props=ujson.dumps(props), user=user )
def load(): """This is the start of the BrainzPlayer concept where anyone (logged into LB) can post a playlist composed of an array of listens-formatted items and get returned a playable playlist page. """ try: raw_listens = request.form['listens'] except KeyError: return render_template( "index/player.html", error_msg="Missing form data key 'listens'" ) try: listens = ujson.loads(raw_listens) except ValueError as e: return render_template( "index/player.html", error_msg="Could not parse JSON array. Error: %s" % e ) if not isinstance(listens, list): return render_template( "index/player.html", error_msg="'listens' should be a stringified JSON array." ) if len(listens) <= 0: return render_template( "index/player.html", error_msg="'Listens' array must have one or more items." ) user_data = { "id": current_user.id, "name": current_user.musicbrainz_id, "auth_token": current_user.auth_token, } spotify_data = spotify.get_user_dict(current_user.id) props = { "user": user_data, "spotify": spotify_data, "api_url": current_app.config["API_URL"], "listens": listens } return render_template( "index/player.html", props=ujson.dumps(props), user=current_user )
def feed(): spotify_user = {} if current_user.is_authenticated: spotify_user = spotify.get_user_dict(current_user.id) current_user_data = { "id": current_user.id, "name": current_user.musicbrainz_id, "auth_token": current_user.auth_token, } props = { "current_user": current_user_data, "spotify": spotify_user, "api_url": current_app.config["API_URL"], } return render_template('index/feed.html', props=ujson.dumps(props))
def follow(user_list): """ Allow an LB user to follow the stream of one or more other LB users. """ if user_list: default_list = {'name': ''} follow_list_members = parse_param_list(user_list) else: default_list = db_follow_list.get_latest(creator=current_user.id) if not default_list: default_list = {'name': '', 'members': []} follow_list_members = [ member['musicbrainz_id'] for member in default_list['members'] ] user_data = { "id": current_user.id, "name": current_user.musicbrainz_id, "auth_token": current_user.auth_token, } spotify_data = spotify.get_user_dict(current_user.id) props = { "user": user_data, "mode": "follow", "follow_list": follow_list_members, "spotify": spotify_data, "web_sockets_server_url": current_app.config["WEBSOCKETS_SERVER_URL"], "api_url": current_app.config["API_URL"], "save_url": "{}/1/follow/save".format(current_app.config["API_URL"]), "follow_list_name": default_list["name"], "follow_list_id": default_list["id"] if "id" in default_list else None, } return render_template( "index/follow.html", props=ujson.dumps(props), mode='follow', user=current_user, follow_list=follow_list_members, active_section='listens', )
def load_playlist(playlist_mbid: str): """Load a single playlist by id """ if not is_valid_uuid(playlist_mbid): raise BadRequest("Provided playlist ID is invalid: %s" % playlist_mbid) current_user_id = None if current_user.is_authenticated: current_user_id = current_user.id playlist = db_playlist.get_by_mbid(playlist_mbid, True) if playlist is None or not playlist.is_visible_by(current_user_id): raise NotFound("Cannot find playlist: %s" % playlist_mbid) fetch_playlist_recording_metadata(playlist) spotify_data = {} current_user_data = {} if current_user.is_authenticated: spotify_data = spotify.get_user_dict(current_user.id) current_user_data = { "id": current_user.id, "name": current_user.musicbrainz_id, "auth_token": current_user.auth_token, } props = { "current_user": current_user_data, "spotify": spotify_data, "api_url": current_app.config["API_URL"], "labs_api_url": current_app.config["LISTENBRAINZ_LABS_API_URL"], "web_sockets_server_url": current_app.config['WEBSOCKETS_SERVER_URL'], "playlist": serialize_jspf(playlist), "sentry_dsn": current_app.config.get("LOG_SENTRY", {}).get("dsn") } return render_template( "playlists/playlist.html", props=ujson.dumps(props) )
def profile(user_name): # Which database to use to showing user listens. db_conn = webserver.timescale_connection._ts # Which database to use to show playing_now stream. playing_now_conn = webserver.redis_connection._redis user = _get_user(user_name) # User name used to get user may not have the same case as original user name. user_name = user.musicbrainz_id # Getting data for current page max_ts = request.args.get("max_ts") if max_ts is not None: try: max_ts = int(max_ts) except ValueError: raise BadRequest("Incorrect timestamp argument max_ts: %s" % request.args.get("max_ts")) min_ts = request.args.get("min_ts") if min_ts is not None: try: min_ts = int(min_ts) except ValueError: raise BadRequest("Incorrect timestamp argument min_ts: %s" % request.args.get("min_ts")) # Send min and max listen times to allow React component to hide prev/next buttons accordingly (min_ts_per_user, max_ts_per_user) = db_conn.get_timestamps_for_user(user_name) if max_ts is None and min_ts is None: if max_ts_per_user: max_ts = max_ts_per_user + 1 else: max_ts = int(time.time()) listens = [] if min_ts_per_user != max_ts_per_user: args = {} if max_ts: args['to_ts'] = max_ts else: args['from_ts'] = min_ts for listen in db_conn.fetch_listens(user_name, limit=LISTENS_PER_PAGE, **args): listens.append({ "track_metadata": listen.data, "listened_at": listen.ts_since_epoch, "listened_at_iso": listen.timestamp.isoformat() + "Z", }) # If there are no previous listens then display now_playing if not listens or listens[0]['listened_at'] >= max_ts_per_user: playing_now = playing_now_conn.get_playing_now(user.id) if playing_now: listen = { "track_metadata": playing_now.data, "playing_now": "true", } listens.insert(0, listen) user_stats = db_stats.get_user_artists(user.id, 'all_time') try: artist_count = user_stats.all_time.count except (AttributeError, ValidationError): artist_count = None spotify_data = {} current_user_data = {} logged_in_user_follows_user = None if current_user.is_authenticated: spotify_data = spotify.get_user_dict(current_user.id) current_user_data = { "id": current_user.id, "name": current_user.musicbrainz_id, "auth_token": current_user.auth_token, } logged_in_user_follows_user = db_user_relationship.is_following_user(current_user.id, user.id) props = { "user": { "id": user.id, "name": user.musicbrainz_id, }, "current_user": current_user_data, "listens": listens, "latest_listen_ts": max_ts_per_user, "oldest_listen_ts": min_ts_per_user, "latest_spotify_uri": _get_spotify_uri_for_listens(listens), "artist_count": format(artist_count, ",d") if artist_count else None, "profile_url": url_for('user.profile', user_name=user_name), "mode": "listens", "spotify": spotify_data, "web_sockets_server_url": current_app.config['WEBSOCKETS_SERVER_URL'], "api_url": current_app.config['API_URL'], "logged_in_user_follows_user": logged_in_user_follows_user, } return render_template("user/profile.html", props=ujson.dumps(props), mode='listens', user=user, active_section='listens')
def _get_template(active_section, user): """ Get template to render based on active section. Args: active_section (str): Type of recommendation playlist to render i.e top_artist, similar_artist user: Database user object. Returns: Template to render. """ data = db_recommendations_cf_recording.get_user_recommendation(user.id) if data is None: current_app.logger.error('Inactive user: "******"'.format(user.musicbrainz_id)) return render_template( "recommendations_cf_recording/{}.html".format(active_section), active_section=active_section, user=user, error_msg="Looks like the user wasn't active in the last week. Submit your listens and check back after a week!" ) result = getattr(data, 'recording_mbid').dict()[active_section] if not result: current_app.logger.error('Top/Similar artists not found in Mapping/artist relation for "{}"'.format(user.musicbrainz_id)) return render_template( "recommendations_cf_recording/{}.html".format(active_section), active_section=active_section, user=user, error_msg="Looks like the recommendations weren't generated because of anomalies in our data." \ "We are working on it. Check back later." ) listens = _get_listens_from_recording_mbid(result) if not listens: current_app.logger.error('The API returned an empty response for {} recommendations.\nData: {}' .format(active_section, result)) return render_template( "recommendations_cf_recording/{}.html".format(active_section), active_section=active_section, user=user, error_msg="An error occurred while processing your request. Check back later!" ) spotify_data = {} current_user_data = {} if current_user.is_authenticated: spotify_data = spotify.get_user_dict(current_user.id) current_user_data = { "id": current_user.id, "name": current_user.musicbrainz_id, "auth_token": current_user.auth_token, } props = { "user": { "id": user.id, "name": user.musicbrainz_id, }, "current_user": current_user_data, "spotify": spotify_data, "api_url": current_app.config["API_URL"], "web_sockets_server_url": current_app.config['WEBSOCKETS_SERVER_URL'], "listens": listens, "mode": "cf_recs" } return render_template( "recommendations_cf_recording/{}.html".format(active_section), active_section=active_section, props=ujson.dumps(props), user=user, last_updated=getattr(data, 'created').strftime('%d %b %Y') )
def profile(user_name): # Which database to use to showing user listens. db_conn = webserver.influx_connection._influx # Which database to use to show playing_now stream. playing_now_conn = webserver.redis_connection._redis user = _get_user(user_name) # User name used to get user may not have the same case as original user name. user_name = user.musicbrainz_id try: have_listen_count = True listen_count = db_conn.get_listen_count_for_user(user_name) except (InfluxDBServerError, InfluxDBClientError): have_listen_count = False listen_count = 0 # Getting data for current page max_ts = request.args.get("max_ts") if max_ts is not None: try: max_ts = int(max_ts) except ValueError: raise BadRequest("Incorrect timestamp argument max_ts: %s" % request.args.get("max_ts")) min_ts = request.args.get("min_ts") if min_ts is not None: try: min_ts = int(min_ts) except ValueError: raise BadRequest("Incorrect timestamp argument min_ts: %s" % request.args.get("min_ts")) if max_ts is None and min_ts is None: max_ts = int(time.time()) args = {} if max_ts: args['to_ts'] = max_ts else: args['from_ts'] = min_ts listens = [] for listen in db_conn.fetch_listens(user_name, limit=LISTENS_PER_PAGE, **args): # Let's fetch one more listen, so we know to show a next page link or not listens.append({ "track_metadata": listen.data, "listened_at": listen.ts_since_epoch, "listened_at_iso": listen.timestamp.isoformat() + "Z", }) latest_listen = db_conn.fetch_listens(user_name=user_name, limit=1, to_ts=int(time.time())) latest_listen_ts = latest_listen[0].ts_since_epoch if len( latest_listen) > 0 else 0 # Calculate if we need to show next/prev buttons previous_listen_ts = None next_listen_ts = None if listens: (min_ts_per_user, max_ts_per_user) = db_conn.get_timestamps_for_user(user_name) if min_ts_per_user >= 0: if listens[-1]['listened_at'] > min_ts_per_user: next_listen_ts = listens[-1]['listened_at'] else: next_listen_ts = None if listens[0]['listened_at'] < max_ts_per_user: previous_listen_ts = listens[0]['listened_at'] else: previous_listen_ts = None # If there are no previous listens then display now_playing if not previous_listen_ts: playing_now = playing_now_conn.get_playing_now(user.id) if playing_now: listen = { "track_metadata": playing_now.data, "playing_now": "true", } listens.insert(0, listen) user_stats = db_stats.get_user_artists(user.id) try: artist_count = int(user_stats['artist']['count']) except (KeyError, TypeError): artist_count = None spotify_data = {} if current_user.is_authenticated: spotify_data = spotify.get_user_dict(current_user.id) props = { "user": { "id": user.id, "name": user.musicbrainz_id, }, "listens": listens, "previous_listen_ts": previous_listen_ts, "next_listen_ts": next_listen_ts, "latest_listen_ts": latest_listen_ts, "latest_spotify_uri": _get_spotify_uri_for_listens(listens), "have_listen_count": have_listen_count, "listen_count": format(int(listen_count), ",d"), "artist_count": format(artist_count, ",d") if artist_count else None, "profile_url": url_for('user.profile', user_name=user_name), "mode": "listens", "spotify": spotify_data, "web_sockets_server_url": current_app.config['WEBSOCKETS_SERVER_URL'], "api_url": current_app.config['API_URL'], } return render_template("user/profile.html", props=ujson.dumps(props), mode='listens', user=user, active_section='listens')