From ebf2fcd3af65302611173752ee494a6347e08cd0 Mon Sep 17 00:00:00 2001 From: Yoshi Askharoun Date: Mon, 14 Feb 2022 23:49:24 -0600 Subject: [PATCH] Added artist images --- .gitignore | 1 + api/discogs.py | 52 +++++++++++++++++++++++++++++++++++ app.py | 71 +++++++++++++++++++++++------------------------- requirements.txt | 1 + 4 files changed, 88 insertions(+), 37 deletions(-) create mode 100644 api/discogs.py diff --git a/.gitignore b/.gitignore index e1e9234..9e2f5e3 100644 --- a/.gitignore +++ b/.gitignore @@ -96,3 +96,4 @@ ENV/ # App secrets *.key +/secrets.py diff --git a/api/discogs.py b/api/discogs.py new file mode 100644 index 0000000..67a4841 --- /dev/null +++ b/api/discogs.py @@ -0,0 +1,52 @@ +import requests +from requests import RequestException, Response +import urllib.error +import musicbrainzngs +from musicbrainzngs.musicbrainz import ResponseError +from secrets import * +import json + +from typing import Union, Tuple, Dict + + +DISCOGS_API: str = "https://api.discogs.com" +DC_HEADERS: Dict[str, str] = { + "User-Agent": USER_AGENT, + "Authorization": f"Discogs key={DC_API_KEY}, secret={DC_API_SECRET}" +} + + +def get_artist_from_dcid(dcid: Union[int, str]) -> Union[int, dict]: + try: + response: Response = requests.get("https://api.discogs.com/artists/" + str(dcid), headers=DC_HEADERS) + except RequestException as error: + return error.response.status_code + + discogs: dict = response.json() + return discogs + + +def get_artist_from_mbid(mbid: str) -> Tuple[Union[int, dict], dict]: + try: + mb_artist: dict = musicbrainzngs.get_artist_by_id(mbid, includes=["url-rels", "tags"])["artist"] + except ResponseError as error: + return error.cause.code + + dc_artist: dict = get_artist_from_mbobj(mb_artist) + return dc_artist, mb_artist + + +def get_artist_from_mbobj(mbobj: dict) -> Union[int, dict]: + if "url-relation-list" not in mbobj: + return 404 + discogs_rel = [x for x in mbobj["url-relation-list"] if x["type"] == "discogs"] + if len(discogs_rel) < 1: + return 404 + discogs_link: str = discogs_rel[0]["target"].replace("www", "api").replace("artist", "artists") + try: + response = urllib.request.urlopen(discogs_link) + except urllib.error.HTTPError as error: + return error.code + + discogs: dict = json.load(response) + return discogs diff --git a/app.py b/app.py index c43a5bb..e9668f3 100644 --- a/app.py +++ b/app.py @@ -1,19 +1,29 @@ import json +from typing import Dict, List from flask import Flask, request, Response, abort, send_file -import urllib.request -from urllib.error import HTTPError +from io import BytesIO +import requests +from requests import RequestException import musicbrainzngs +import api.discogs as discogs +from secrets import USER_AGENT from locale import * + + locale = getdefaultlocale()[0] app = Flask(__name__) caa_supported_sizes = [250, 500, 1200] +dc_artist_cache: Dict[str, dict] = {} musicbrainzngs.set_useragent("Zune", "4.8", "https://github.com/yoshiask/PyZuneImageCatalogServer") +DEFAULT_HEADERS: Dict[str, str] = { + "User-Agent": USER_AGENT +} import re @@ -40,46 +50,33 @@ def default(): @app.route(f"/v3.2//image/") def get_image(mbid: str, locale: str): - # The Cover Art Archive API supports sizes of 250, 500, and 1200 - requested_width = request.args.get("width", default=500, type=int) - width = min(caa_supported_sizes, key=lambda x: abs(x - requested_width)) + image_url: str = "" + if mbid.endswith('0' * 12): + dcid: int = int(mbid[:8], 16) + img_idx: int = int(mbid[9:13], 16) + + # Get or update cached artist + dc_artist: dict = dc_artist_cache.get(dcid) + if dc_artist is None: + # Artist not in cache + dc_artist = discogs.get_artist_from_dcid(dcid) + dc_artist_cache[dcid] = dc_artist + + # Get URL for requested image + image_url = dc_artist["images"][img_idx]["uri"] + else: + # The Cover Art Archive API supports sizes of 250, 500, and 1200 + requested_width = request.args.get("width", default=500, type=int) + width = min(caa_supported_sizes, key=lambda x: abs(x - requested_width)) + image_url = f"http://coverartarchive.org/release/{mbid}/front-{width}" # Request the image from the API and forward it to the Zune software try: - image = urllib.request.urlopen(f"http://coverartarchive.org/release/{mbid}/front-{width}") - return Response(image.read(), mimetype="image/jpeg") - except urllib.error.HTTPError as error: + image = requests.get(image_url, headers=DEFAULT_HEADERS, stream=True) + return Response(BytesIO(image.content), content_type="image/jpeg") + except RequestException as error: return send_file('noart.png', attachment_filename='noart.png') -@app.route("/v3.2//music/artist//primaryImage") -def get_artist_primary_image(mbid: str, locale: str): - try: - artist = musicbrainzngs.get_artist_by_id(mbid, ["url-rels"])["artist"] - except musicbrainzngs.ResponseError as error: - abort(error.cause.code) - return - - # Get the Deezer artist ID - deezerUrls = [ - rel["target"] - for rel in artist["url-relation-list"] - if rel["type"] == "free streaming" and "deezer" in rel["target"] - ] - if len(deezerUrls) == 0: - abort(404) - deezerUrl: str = deezerUrls[0] - - # Get Deezer's artist info - dzResponse = urllib.request.urlopen(deezerUrl.replace("www", "api", 1)) - raw_data = dzResponse.read() - encoding = dzResponse.info().get_content_charset('utf8') # JSON default - dzArtist = json.loads(raw_data.decode(encoding)) - - # Request the image from the API and forward it to the Zune software - image = urllib.request.urlopen(dzArtist["picture_xl"]) - return Response(image.read(), mimetype="image/jpeg") - - if __name__ == "__main__": app.run(host="127.0.0.1", port=80) diff --git a/requirements.txt b/requirements.txt index a1643bd..24cdd85 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,4 @@ Flask>=1.0,<=1.1.2 musicbrainzngs==0.7.1 +requests>=2.27.1 gunicorn; platform_system == "Linux"