Skip to content

Commit

Permalink
Merge pull request #3118 from metabrainz/opengraph-meta-tags
Browse files Browse the repository at this point in the history
Add OpenGraph meta tags in artist, album and user pages
  • Loading branch information
MonkeyDo authored Jan 16, 2025
2 parents 0953f3f + 48ac135 commit 24b7a8d
Show file tree
Hide file tree
Showing 4 changed files with 112 additions and 14 deletions.
12 changes: 7 additions & 5 deletions listenbrainz/webserver/templates/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,15 @@
<meta property="og:title" content="{{ opengraph_title }}" />
<meta property="og:type" content="{{ opengraph_type }}" />
<meta property="og:description" content="{{ opengraph_description }}" />
<!-- OpenGraph image meta tags -->
<meta
{%- if 'image' not in (og_meta_tags or {}) -%}
<!-- OpenGraph image meta tags -->
<meta
property="og:image"
content="{{ url_for('static', filename='img/share-header.png', _external=True) }}"
/>
<meta property="og:image:width" content="1280" />
<meta property="og:image:height" content="640" />
/>
<meta property="og:image:width" content="1280" />
<meta property="og:image:height" content="640" />
{%- endif -%}
<!-- Other OpenGraph meta tags (title, type and description are already handled above) -->
{%- for tagname, tagvalue in (og_meta_tags or {}).items() if tagname not in ['description', 'title', 'type']-%}
<meta property="og:{{ tagname }}" content="{{ tagvalue }}" />
Expand Down
12 changes: 12 additions & 0 deletions listenbrainz/webserver/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,18 @@ def sizeof_readable(num, suffix='B'):
return "%.1f%s%s" % (num, 'Yb', suffix)


def number_readable(num: int):
# Solution by rtaft from https://stackoverflow.com/a/45846841/4904467
""" Converts a number to a short human-readable format (1.2K, 6.6M, etc.)"""
suffixes = ['', 'K', 'M', 'B', 'T']
num = float('{:.2g}'.format(num))
magnitude = 0
while abs(num) >= 1000:
magnitude += 1
num /= 1000.0
return '{}{}'.format('{:f}'.format(num).rstrip('0').rstrip('.'), suffixes[magnitude])


def reformat_date(value, fmt="%b %d, %Y"):
return value.strftime(fmt)

Expand Down
83 changes: 75 additions & 8 deletions listenbrainz/webserver/views/entity_pages.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from listenbrainz.db.stats import get_entity_listener
from listenbrainz.webserver import db_conn, ts_conn
from listenbrainz.webserver.decorators import web_listenstore_needed
from listenbrainz.webserver.utils import number_readable
from listenbrainz.db.metadata import get_metadata_for_artist
from listenbrainz.webserver.views.api_tools import is_valid_uuid
from listenbrainz.webserver.views.metadata_api import fetch_release_group_metadata
Expand Down Expand Up @@ -111,14 +112,40 @@ def release_redirect(release_mbid):


@artist_bp.get("/", defaults={'path': ''})
@artist_bp.get('/<path:path>/')
def artist_page(path):
return render_template("index.html")
@artist_bp.get("/<artist_mbid>/")
def artist_page(artist_mbid: str):
og_meta_tags = None
if is_valid_uuid(artist_mbid) and artist_mbid not in {"89ad4ac3-39f7-470e-963a-56509c546377"}:
artist_data = get_metadata_for_artist(ts_conn, [artist_mbid])
if len(artist_data) == 0:
pass
else:
artist = artist_data[0]
artist_name = artist.artist_data.get("name")
release_group_data = artist.release_group_data
release_group_mbids = [rg["mbid"] for rg in release_group_data]
album_count = len(release_group_mbids)
listening_stats = get_entity_listener(
db_conn, "artists", artist_mbid, "all_time")
total_listen_count = 0
if listening_stats and "total_listen_count" in listening_stats:
total_listen_count = number_readable(
listening_stats["total_listen_count"] or 0)

og_meta_tags = {
"title": f'{artist_name}',
"description": f'Artist — {total_listen_count} listens — {album_count} albums — ListenBrainz',
"type": "profile",
"profile:username": artist_name,
"profile.gender": artist.artist_data.get("gender"),
"url": f'{current_app.config["SERVER_ROOT_URL"]}/artist/{artist.artist_mbid}',
}
return render_template("index.html", og_meta_tags=og_meta_tags)


@artist_bp.post("/<artist_mbid>/")
@web_listenstore_needed
def artist_entity(artist_mbid):
def artist_entity(artist_mbid: str):
""" Show a artist page with all their relevant information """
# VA artist mbid
if artist_mbid in {"89ad4ac3-39f7-470e-963a-56509c546377"}:
Expand Down Expand Up @@ -209,14 +236,54 @@ def artist_entity(artist_mbid):


@album_bp.get("/", defaults={'path': ''})
@album_bp.get('/<path:path>/')
def album_page(path):
return render_template("index.html")
@album_bp.get("/<release_group_mbid>/")
def album_page(release_group_mbid: str):
og_meta_tags = None
if is_valid_uuid(release_group_mbid):
metadata = fetch_release_group_metadata(
[release_group_mbid],
["artist", "recording"]
)
if len(metadata) == 0:
pass
else:
release_group = metadata[release_group_mbid]

recording_data = release_group.pop("recording")
mediums = recording_data.get("mediums", [])
recording_mbids = []
for medium in mediums:
for track in medium["tracks"]:
recording_mbids.append(track["recording_mbid"])

album_name = release_group.get("release_group").get("name")
artist_name = release_group.get("artist").get("name")
track_count = len(recording_mbids)
listening_stats = get_entity_listener(
db_conn, "release_groups", release_group_mbid, "all_time")
total_listen_count = 0
if listening_stats and "total_listen_count" in listening_stats:
total_listen_count = number_readable(
listening_stats["total_listen_count"] or 0)

og_meta_tags = {
"title": f'{album_name}{artist_name}',
"description": f'Album — {track_count} tracks — {total_listen_count} listens — ListenBrainz',
"type": "music.album",
"music:musician": artist_name,
"music:release_date": release_group.get("release_group").get("date"),
"image": f'https://coverartarchive.org/release-group/{release_group_mbid}/front-500',
"image:width": "500",
"image:alt": f"Cover art for {album_name}",
"url": f'{current_app.config["SERVER_ROOT_URL"]}/album/{release_group_mbid}',
}

return render_template("index.html", og_meta_tags=og_meta_tags)


@album_bp.post("/<release_group_mbid>/")
@web_listenstore_needed
def album_entity(release_group_mbid):
def album_entity(release_group_mbid: str):
""" Show an album page with all their relevant information """

if not is_valid_uuid(release_group_mbid):
Expand Down
19 changes: 18 additions & 1 deletion listenbrainz/webserver/views/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@
from listenbrainz.webserver.errors import APIBadRequest
from listenbrainz.webserver.login import User, api_login_required
from listenbrainz.webserver.views.api import DEFAULT_NUMBER_OF_PLAYLISTS_PER_CALL
from listenbrainz.webserver.utils import number_readable
from listenbrainz.webserver.views.api_tools import get_non_negative_param
from werkzeug.exceptions import NotFound

from brainzutils import cache

Expand Down Expand Up @@ -463,4 +465,19 @@ def year_in_music(user_name, year: int = 2024):
@web_listenstore_needed
def index(user_name, path):
user = _get_user(user_name)
return render_template("index.html", user=user)
if not user:
og_meta_tags = None
else:
listen_count = None
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))
og_meta_tags = {
"title": f"{user_name} on ListenBrainz",
"description": f'User{f" — {number_readable(listen_count)} listens" if listen_count else ""} — ListenBrainz',
"type": "profile",
"profile:username": user_name,
"url": f'{current_app.config["SERVER_ROOT_URL"]}/user/{user_name}/',
}
return render_template("index.html", og_meta_tags=og_meta_tags, user=user)

0 comments on commit 24b7a8d

Please sign in to comment.