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

Allow post get metadata #2831

Merged
merged 5 commits into from
Apr 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
15 changes: 7 additions & 8 deletions listenbrainz/db/metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,9 @@
import psycopg2.extras

from listenbrainz.db.model.metadata import RecordingMetadata, ArtistMetadata, ReleaseGroupMetadata
from listenbrainz.webserver.views.api_tools import MAX_ITEMS_PER_GET
from typing import List

MAX_NUMBER_OF_ENTITIES_PER_CALL = 50


def fixup_mbids_to_artists(row):
""" Add artist mbid to the artists data retrieved from recording or release group cache """
Expand All @@ -16,7 +15,7 @@ def fixup_mbids_to_artists(row):

def get_metadata_for_recording(ts_conn, recording_mbid_list: List[str]) -> List[RecordingMetadata]:
""" Get a list of recording Metadata objects for a given recording in descending order of their creation.
The list of recordings cannot exceed `~db.metadata.MAX_NUMBER_OF_ENTITIES_PER_CALL` per call.
The list of recordings cannot exceed `~db.metadata.MAX_ITEMS_PER_GET` per call.
If the number of items exceeds this limit, ValueError will be raised. Data is sorted according
to recording_mbid

Expand All @@ -29,7 +28,7 @@ def get_metadata_for_recording(ts_conn, recording_mbid_list: List[str]) -> List[
"""

recording_mbid_list = tuple(recording_mbid_list)
if len(recording_mbid_list) > MAX_NUMBER_OF_ENTITIES_PER_CALL:
if len(recording_mbid_list) > MAX_ITEMS_PER_GET:
raise ValueError("Too many recording mbids passed in.")

query = """SELECT *
Expand All @@ -48,7 +47,7 @@ def get_metadata_for_recording(ts_conn, recording_mbid_list: List[str]) -> List[

def get_metadata_for_release_group(ts_conn, release_group_mbid_list: List[str]) -> List[ReleaseGroupMetadata]:
""" Get a list of release_group Metadata objects for a given release_group in descending order of their creation.
The list of release groups cannot exceed `~db.metadata.MAX_NUMBER_OF_ENTITIES_PER_CALL` per call.
The list of release groups cannot exceed `~db.metadata.MAX_ITEMS_PER_GET` per call.
If the number of items exceeds this limit, ValueError will be raised. Data is sorted according
to release_group_mbid

Expand All @@ -60,7 +59,7 @@ def get_metadata_for_release_group(ts_conn, release_group_mbid_list: List[str])
"""

release_group_mbid_list = tuple(release_group_mbid_list)
if len(release_group_mbid_list) > MAX_NUMBER_OF_ENTITIES_PER_CALL:
if len(release_group_mbid_list) > MAX_ITEMS_PER_GET:
raise ValueError("Too many recording mbids passed in.")

query = """SELECT *
Expand All @@ -79,7 +78,7 @@ def get_metadata_for_release_group(ts_conn, release_group_mbid_list: List[str])

def get_metadata_for_artist(ts_conn, artist_mbid_list: List[str]) -> List[ArtistMetadata]:
""" Get a list of artist Metadata objects for a given recording in descending order of their creation.
The list of artists cannot exceed `~db.metadata.MAX_NUMBER_OF_ENTITIES_PER_CALL` per call.
The list of artists cannot exceed `~db.metadata.MAX_ITEMS_PER_GET` per call.
If the number of items exceeds this limit, ValueError will be raised. Data is sorted according
to recording_mbid

Expand All @@ -91,7 +90,7 @@ def get_metadata_for_artist(ts_conn, artist_mbid_list: List[str]) -> List[Artist
"""

artist_mbid_list = tuple(artist_mbid_list)
if len(artist_mbid_list) > MAX_NUMBER_OF_ENTITIES_PER_CALL:
if len(artist_mbid_list) > MAX_ITEMS_PER_GET:
raise ValueError("Too many artist mbids passed in.")

query = """SELECT *
Expand Down
60 changes: 53 additions & 7 deletions listenbrainz/webserver/views/metadata_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,17 @@
from listenbrainz.webserver.decorators import crossdomain
from listenbrainz.webserver.errors import APIBadRequest, APIInternalServerError
from listenbrainz.webserver.utils import parse_boolean_arg
from listenbrainz.webserver.views.api_tools import is_valid_uuid, validate_auth_header
from listenbrainz.webserver.views.api_tools import is_valid_uuid, validate_auth_header, MAX_ITEMS_PER_GET

metadata_bp = Blueprint('metadata', __name__)

#: The maximum length of the query permitted for a mapping search
MAX_MAPPING_QUERY_LENGTH = 250


def parse_incs():
def parse_incs(incs):
allowed_incs = ("artist", "tag", "release", "recording", "release_group")

incs = request.args.get("inc")
if not incs:
return []
incs = incs.split()
Expand Down Expand Up @@ -101,7 +100,7 @@ def metadata_recording():
:statuscode 200: you have data!
:statuscode 400: invalid recording_mbid arguments
"""
incs = parse_incs()
incs = parse_incs(request.args.get("inc"))

recordings = request.args.get("recording_mbids", default=None)
if recordings is None:
Expand All @@ -120,6 +119,53 @@ def metadata_recording():
return jsonify(result)


@metadata_bp.route("/recording/", methods=["POST"])
@crossdomain
@ratelimit()
def metadata_recording_post():
"""
This endpoint is the POST verson for fetching recording metadata, since it allows up to the
max number of items allowed. (:data:`~webserver.views.api.MAX_ITEMS_PER_GET` items)

A JSON document with a list of recording_mbids and inc string must be POSTed
to this endpoint to returns an array of dicts that contain
recording metadata suitable for showing in a context that requires as much detail about
a recording and the artist. Using the inc parameter, you can control which portions of metadata
to fetch.
{ "recording_mbids": [ "25d47b0c-5177-49db-b740-c166e4acebd1", ... ], inc="artist tag" }

To see what data this endpoint returns, please look at the data above for the GET version.

:statuscode 200: you have data!
:statuscode 400: invalid recording_mbid arguments
"""
data = request.json

try:
incs = parse_incs(data["inc"])
except KeyError:
incs = []

try:
recording_mbids = data["recording_mbids"]
except KeyError:
raise APIBadRequest(
"recording_mbids JSON element must be present and contain a list of recording_mbids")

for mbid in recording_mbids:
if not is_valid_uuid(mbid):
raise APIBadRequest(f"Recording mbid {mbid} is not valid.")

if len(recording_mbids) == 0:
raise APIBadRequest("At least one valid recording_mbid must be present.")

if len(recording_mbids) > MAX_ITEMS_PER_GET:
raise APIBadRequest("Maximum number of recordings_mbids that can be fetchs at once is %s" % MAX_ITEMS_PER_GET)

result = fetch_metadata(recording_mbids, incs)
return jsonify(result)


@metadata_bp.route("/release_group/", methods=["GET", "OPTIONS"])
@crossdomain
@ratelimit()
Expand All @@ -144,7 +190,7 @@ def metadata_release_group():
:statuscode 200: you have data!
:statuscode 400: invalid release_group_mbid arguments
"""
incs = parse_incs()
incs = parse_incs(request.args.get("inc"))

release_groups = request.args.get("release_group_mbids", default=None)
if release_groups is None:
Expand Down Expand Up @@ -214,7 +260,7 @@ def get_mbid_mapping():
f" arguments must be less than {MAX_MAPPING_QUERY_LENGTH}")

metadata = parse_boolean_arg("metadata")
incs = parse_incs() if metadata else []
incs = parse_incs(request.args.get("inc")) if metadata else []

params = [
{
Expand Down Expand Up @@ -345,7 +391,7 @@ def metadata_artist():
:statuscode 200: you have data!
:statuscode 400: invalid recording_mbid arguments
"""
incs = parse_incs()
incs = parse_incs(request.args.get("inc"))

artists = request.args.get("artist_mbids", default=None)
if artists is None:
Expand Down