Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add OpenGraph tags to playlist page head #3078

Merged
merged 9 commits into from
Jan 13, 2025
24 changes: 23 additions & 1 deletion frontend/js/src/playlists/Playlist.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import {
getRecordingMBIDFromJSPFTrack,
isPlaylistOwner,
JSPFTrackToListen,
LISTENBRAINZ_URI_PREFIX,
PLAYLIST_TRACK_URI_PREFIX,
PLAYLIST_URI_PREFIX,
} from "./utils";
Expand Down Expand Up @@ -320,7 +321,28 @@ export default function PlaylistPage() {
return (
<div role="main">
<Helmet>
<title>{playlist.title} - Playlist</title>
<title>
{playlist.title} by {playlist.creator}
</title>
<meta property="og:type" content="music.playlist" />
<meta
property="og:title"
content={`${playlist.title} by ${playlist.creator} (${
playlist.track?.length ?? 0
} tracks) — ListenBrainz`}
/>
<meta property="og:description" content={playlist.annotation} />
<meta
property="music:creator"
content={`${LISTENBRAINZ_URI_PREFIX}user/${playlist.creator}`}
/>
{totalDurationMs && (
<meta
property="music:duration"
content={String(totalDurationMs / 1000)}
/>
)}
<meta property="og:url" content={playlist.identifier} />
</Helmet>
<div className="row">
<div
Expand Down
3 changes: 2 additions & 1 deletion frontend/js/src/playlists/utils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ export const MUSICBRAINZ_JSPF_PLAYLIST_EXTENSION =
export const MUSICBRAINZ_JSPF_TRACK_EXTENSION =
"https://musicbrainz.org/doc/jspf#track";

export const PLAYLIST_URI_PREFIX = "https://listenbrainz.org/playlist/";
export const LISTENBRAINZ_URI_PREFIX = "https://listenbrainz.org/";
export const PLAYLIST_URI_PREFIX = `${LISTENBRAINZ_URI_PREFIX}playlist/`;
export const PLAYLIST_TRACK_URI_PREFIX = "https://musicbrainz.org/recording/";
export const PLAYLIST_ARTIST_URI_PREFIX = "https://musicbrainz.org/artist/";

Expand Down
18 changes: 18 additions & 0 deletions listenbrainz/db/playlist.py
Original file line number Diff line number Diff line change
Expand Up @@ -591,6 +591,24 @@ def get_recordings_for_playlists(db_conn, ts_conn, playlist_ids: List[int]):
playlist_recordings_map[playlist_id] = []
return dict(playlist_recordings_map)

def get_recordings_count_for_playlist(ts_conn, playlist_id: int):
""" Get a count of recordings for a given playlist.

Arguments:
ts_conn: timsecale database connection
playlist_id: Numerical sequential id of a playlist (NOT its UUID)

Returns:
an integer of the number of recordings in the playlist """

query = text("""
SELECT COUNT(*)
FROM playlist.playlist_recording
WHERE playlist_id = :playlist_id
""")
result = ts_conn.execute(query, {"playlist_id": playlist_id})
return result.scalar()


def _remove_old_collaborative_playlists(ts_conn, creator_id: int, created_for_id: int, source_patch: str):
"""
Expand Down
40 changes: 24 additions & 16 deletions listenbrainz/webserver/templates/index.html
Original file line number Diff line number Diff line change
@@ -1,5 +1,16 @@
{% from 'macros.html' import print_message %}

{%
set opengraph_title = og_meta_tags['title'] if og_meta_tags and og_meta_tags['title']
else "ListenBrainz"
%}
{%
set opengraph_description = og_meta_tags['description'] if og_meta_tags and og_meta_tags['description']
else "Track, explore, visualise and share the music you listen to. Follow your favourites and discover great new music."
%}
{%
set opengraph_type = og_meta_tags['type'] if og_meta_tags and og_meta_tags['type']
else "website"
%}
<!DOCTYPE html>
<html>
<head>
Expand All @@ -11,31 +22,28 @@

<meta
name="description"
content="Track, explore, visualise and share the music you listen to.
Follow your favourites and discover great new music."
content="{{ opengraph_description }}"
/>
<!-- OpenGraph meta tags -->
<meta property="og:type" content="website" />
<meta property="og:title" content="ListenBrainz" />
<meta
property="og:description"
content="Track, explore, visualise and share the music you listen to.
Follow your favourites and discover great new music."
/>
<meta property="og:site_name" content="ListenBrainz" />
<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
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" />
<!-- 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 }}" />
{%- endfor -%}

<!-- Twitter meta tags -->
<meta name="twitter:title" content="ListenBrainz" />
<meta
property="twitter:description"
content="Track, explore, visualise and share the music you listen to.
Follow your favourites and discover great new music."
/>
<meta name="twitter:title" content="{{ opengraph_title }}" />
<meta property="twitter:description" content="{{ opengraph_description }}"/>
<!-- Twitter image meta tags -->
<meta name="twitter:card" content="summary_large_image" />
<meta
Expand Down
29 changes: 24 additions & 5 deletions listenbrainz/webserver/views/playlist.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from flask import Blueprint, render_template, jsonify
from flask import Blueprint, current_app, render_template, jsonify
from flask_login import current_user

from listenbrainz.webserver import ts_conn, db_conn
Expand All @@ -10,10 +10,29 @@
playlist_bp = Blueprint("playlist", __name__)


@playlist_bp.get("/", defaults={'path': ''})
@playlist_bp.get('/<path:path>/')
def playlist_page(path):
return render_template("index.html")

@playlist_bp.get("/", defaults={'playlist_mbid': ''})
@playlist_bp.get('/<playlist_mbid>/')
def playlist_page(playlist_mbid: str):
current_user_id = None
og_meta_tags = None

if current_user.is_authenticated:
current_user_id = current_user.id

if is_valid_uuid(playlist_mbid):
playlist = db_playlist.get_by_mbid(db_conn, ts_conn, playlist_mbid, False)
if playlist is not None and playlist.is_visible_by(current_user_id):
recordings_count = db_playlist.get_recordings_count_for_playlist(ts_conn, playlist.id)
og_meta_tags = {
"title": f'{playlist.name} — Playlist on ListenBrainz',
"description": f'Playlist by {playlist.creator} — {recordings_count} track{"s" if recordings_count > 1 else ""} — ListenBrainz',
"type": "music:playlist",
"url": f'{current_app.config["SERVER_ROOT_URL"]}/playlist/{playlist_mbid}',
"music:creator": f'{current_app.config["SERVER_ROOT_URL"]}/user/{playlist.creator}',
# "image": Once we have playlist images we can try adding it here
}
return render_template("index.html", og_meta_tags=og_meta_tags)


@playlist_bp.post("/<playlist_mbid>/")
Expand Down
Loading