1
0
mirror of https://github.com/krateng/maloja.git synced 2023-08-10 21:12:55 +03:00

Implemented images for albums

This commit is contained in:
krateng 2023-03-28 19:58:12 +02:00
parent 69b456dc73
commit fd9987ec35
9 changed files with 133 additions and 40 deletions

View File

@ -519,7 +519,7 @@ def post_scrobble(
@api.post("addpicture")
@authenticated_function(alternate=api_key_correct,api=True)
@catch_exceptions
def add_picture(b64,artist:Multi=[],title=None):
def add_picture(b64,artist:Multi=[],title=None,albumtitle=None):
"""Uploads a new image for an artist or track.
param string b64: Base 64 representation of the image
@ -531,8 +531,10 @@ def add_picture(b64,artist:Multi=[],title=None):
for a in artist:
keys.append("artist",a)
if title is not None: keys.append("title",title)
elif albumtitle is not None: keys.append("albumtitle",albumtitle)
k_filter, _, _, _, _ = uri_to_internal(keys)
if "track" in k_filter: k_filter = k_filter["track"]
elif "album" in k_filter: k_filter = k_filter["album"]
url = images.set_image(b64,**k_filter)
return {

View File

@ -39,6 +39,13 @@ DB['tracks'] = sql.Table(
sql.Column('expire',sql.Integer),
sql.Column('raw',sql.String)
)
DB['albums'] = sql.Table(
'albums', meta,
sql.Column('id',sql.Integer,primary_key=True),
sql.Column('url',sql.String),
sql.Column('expire',sql.Integer),
sql.Column('raw',sql.String)
)
meta.create_all(engine)
@ -137,7 +144,7 @@ def resolve_track_image(track_id):
# local image
if malojaconfig["USE_LOCAL_IMAGES"]:
images = local_files(artists=track['artists'],title=track['title'])
images = local_files(track=track)
if len(images) != 0:
result = random.choice(images)
result = urllib.parse.quote(result)
@ -181,31 +188,56 @@ def resolve_artist_image(artist_id):
return result
def resolve_album_image(album_id):
with resolve_semaphore:
# check cache
result = get_image_from_cache(album_id,'albums')
if result is not None:
return result
album = database.sqldb.get_album(album_id)
# local image
if malojaconfig["USE_LOCAL_IMAGES"]:
images = local_files(album=album)
if len(images) != 0:
result = random.choice(images)
result = urllib.parse.quote(result)
result = {'type':'url','value':result}
set_image_in_cache(album_id,'tracks',result['value'])
return result
# third party
result = thirdparty.get_image_album_all((album['artists'],album['albumtitle']))
result = {'type':'url','value':result}
set_image_in_cache(album_id,'albums',result['value'])
return result
# removes emojis and weird shit from names
def clean(name):
return "".join(c for c in name if c.isalnum() or c in []).strip()
def get_all_possible_filenames(artist=None,artists=None,title=None):
# check if we're dealing with a track or artist, then clean up names
# (only remove non-alphanumeric, allow korean and stuff)
if title is not None and artists is not None:
track = True
title, artists = clean(title), [clean(a) for a in artists]
elif artist is not None:
track = False
# new and improved
def get_all_possible_filenames(artist=None,track=None,album=None):
if track:
title, artists = clean(track['title']), [clean(a) for a in track['artists']]
superfolder = "tracks/"
elif album:
title, artists = clean(album['albumtitle']), [clean(a) for a in album['artists']]
superfolder = "albums/"
elif artist:
artist = clean(artist)
else: return []
superfolder = "tracks/" if track else "artists/"
superfolder = "artists/"
else:
return []
filenames = []
if track:
#unsafeartists = [artist.translate(None,"-_./\\") for artist in artists]
if track or album:
safeartists = [re.sub("[^a-zA-Z0-9]","",artist) for artist in artists]
#unsafetitle = title.translate(None,"-_./\\")
safetitle = re.sub("[^a-zA-Z0-9]","",title)
if len(artists) < 4:
@ -215,7 +247,6 @@ def get_all_possible_filenames(artist=None,artists=None,title=None):
unsafeperms = [sorted(artists)]
safeperms = [sorted(safeartists)]
for unsafeartistlist in unsafeperms:
filename = "-".join(unsafeartistlist) + "_" + title
if filename != "":
@ -246,10 +277,11 @@ def get_all_possible_filenames(artist=None,artists=None,title=None):
return [superfolder + name for name in filenames]
def local_files(artist=None,artists=None,title=None):
def local_files(artist=None,album=None,track=None):
filenames = get_all_possible_filenames(artist,artists,title)
filenames = get_all_possible_filenames(artist=artist,album=album,track=track)
images = []
@ -276,13 +308,18 @@ class MalformedB64(Exception):
pass
def set_image(b64,**keys):
track = "title" in keys
if track:
entity = {'artists':keys['artists'],'title':keys['title']}
id = database.sqldb.get_track_id(entity)
else:
entity = keys['artist']
id = database.sqldb.get_artist_id(entity)
if "title" in keys:
entity = {"track":keys}
id = database.sqldb.get_track_id(entity['track'])
dbtable = "tracks"
elif "albumtitle" in keys:
entity = {"album":keys}
id = database.sqldb.get_album_id(entity['album'])
dbtable = "albums"
elif "artist" in keys:
entity = keys
id = database.sqldb.get_artist_id(entity['artist'])
dbtable = "artists"
log("Trying to set image, b64 string: " + str(b64[:30] + "..."),module="debug")
@ -293,13 +330,13 @@ def set_image(b64,**keys):
type,b64 = match.groups()
b64 = base64.b64decode(b64)
filename = "webupload" + str(int(datetime.datetime.now().timestamp())) + "." + type
for folder in get_all_possible_filenames(**keys):
for folder in get_all_possible_filenames(**entity):
if os.path.exists(data_dir['images'](folder)):
with open(data_dir['images'](folder,filename),"wb") as f:
f.write(b64)
break
else:
folder = get_all_possible_filenames(**keys)[0]
folder = get_all_possible_filenames(**entity)[0]
os.makedirs(data_dir['images'](folder))
with open(data_dir['images'](folder,filename),"wb") as f:
f.write(b64)
@ -308,7 +345,6 @@ def set_image(b64,**keys):
log("Saved image as " + data_dir['images'](folder,filename),module="debug")
# set as current picture in rotation
if track: set_image_in_cache(id,'tracks',os.path.join("/images",folder,filename))
else: set_image_in_cache(id,'artists',os.path.join("/images",folder,filename))
set_image_in_cache(id,dbtable,os.path.join("/images",folder,filename))
return os.path.join("/images",folder,filename)

View File

@ -19,7 +19,7 @@ from doreah import auth
# rest of the project
from . import database
from .database.jinjaview import JinjaDBConnection
from .images import resolve_track_image, resolve_artist_image
from .images import resolve_track_image, resolve_artist_image, resolve_album_image
from .malojauri import uri_to_internal, remove_identical
from .pkg_global.conf import malojaconfig, data_dir
from .jinjaenv.context import jinja_environment
@ -124,6 +124,8 @@ def dynamic_image():
result = resolve_track_image(keys['id'])
elif keys['type'] == 'artist':
result = resolve_artist_image(keys['id'])
elif keys['type'] == 'album':
result = resolve_album_image(keys['id'])
if result is None or result['value'] in [None,'']:
return ""

View File

@ -63,7 +63,18 @@ def get_image_artist_all(artist):
log("Could not get artist image for " + str(artist) + " from " + service.name)
except Exception as e:
log("Error getting artist image from " + service.name + ": " + repr(e))
def get_image_album_all(album):
with thirdpartylock:
for service in services["metadata"]:
try:
res = service.get_image_album(album)
if res is not None:
log("Got album image for " + str(album) + " from " + service.name)
return res
else:
log("Could not get album image for " + str(album) + " from " + service.name)
except Exception as e:
log("Error getting album image from " + service.name + ": " + repr(e))
class GenericInterface:
@ -217,6 +228,23 @@ class MetadataInterface(GenericInterface,abstract=True):
if imgurl is not None: imgurl = self.postprocess_url(imgurl)
return imgurl
def get_image_album(self,album):
artists, title = album
artiststring = urllib.parse.quote(", ".join(artists))
titlestring = urllib.parse.quote(title)
response = urllib.request.urlopen(
self.metadata["albumurl"].format(artist=artiststring,title=titlestring,**self.settings)
)
responsedata = response.read()
if self.metadata["response_type"] == "json":
data = json.loads(responsedata)
imgurl = self.metadata_parse_response_album(data)
else:
imgurl = None
if imgurl is not None: imgurl = self.postprocess_url(imgurl)
return imgurl
# default function to parse response by descending down nodes
# override if more complicated
def metadata_parse_response_artist(self,data):
@ -225,6 +253,9 @@ class MetadataInterface(GenericInterface,abstract=True):
def metadata_parse_response_track(self,data):
return self._parse_response("response_parse_tree_track", data)
def metadata_parse_response_album(self,data):
return self._parse_response("response_parse_tree_album", data)
def _parse_response(self, resp, data):
res = data
for node in self.metadata[resp]:

View File

@ -9,13 +9,17 @@ class AudioDB(MetadataInterface):
}
metadata = {
#"trackurl": "https://theaudiodb.com/api/v1/json/{api_key}/searchtrack.php?s={artist}&t={title}",
#"trackurl": "https://theaudiodb.com/api/v1/json/{api_key}/searchtrack.php?s={artist}&t={title}", #patreon
"artisturl": "https://www.theaudiodb.com/api/v1/json/{api_key}/search.php?s={artist}",
#"albumurl": "https://www.theaudiodb.com/api/v1/json/{api_key}/searchalbum.php?s={artist}&a={title}", #patreon
"response_type":"json",
#"response_parse_tree_track": ["tracks",0,"astrArtistThumb"],
"response_parse_tree_artist": ["artists",0,"strArtistThumb"],
"required_settings": ["api_key"],
}
def get_image_track(self,artist):
def get_image_track(self,track):
return None
def get_image_album(self,album):
return None

View File

@ -8,10 +8,17 @@ class Deezer(MetadataInterface):
}
metadata = {
"trackurl": "https://api.deezer.com/search?q={artist}%20{title}",
#"trackurl": "https://api.deezer.com/search?q={artist}%20{title}",
"artisturl": "https://api.deezer.com/search?q={artist}",
"albumurl": "https://api.deezer.com/search?q={artist}%20{title}",
"response_type":"json",
"response_parse_tree_track": ["data",0,"album","cover_medium"],
#"response_parse_tree_track": ["data",0,"album","cover_medium"],
"response_parse_tree_artist": ["data",0,"artist","picture_medium"],
"response_parse_tree_album": ["data",0,"album","cover_medium"],
"required_settings": [],
}
def get_image_track(self,track):
return None
# we can use the album pic from the track search,
# but should do so via maloja logic

View File

@ -22,15 +22,22 @@ class LastFM(MetadataInterface, ProxyScrobbleInterface):
"activated_setting": "SCROBBLE_LASTFM"
}
metadata = {
#"artisturl": "https://ws.audioscrobbler.com/2.0/?method=artist.getinfo&artist={artist}&api_key={apikey}&format=json"
"trackurl": "https://ws.audioscrobbler.com/2.0/?method=track.getinfo&track={title}&artist={artist}&api_key={apikey}&format=json",
"albumurl": "https://ws.audioscrobbler.com/2.0/?method=album.getinfo&api_key={apikey}&artist={artist}&album={title}&format=json",
"response_type":"json",
"response_parse_tree_track": ["track","album","image",-1,"#text"],
# technically just the album artwork, but we use it for now
#"response_parse_tree_artist": ["artist","image",-1,"#text"],
"response_parse_tree_album": ["album","image",-1,"#text"],
"required_settings": ["apikey"],
}
def get_image_artist(self,artist):
return None
# lastfm doesn't provide artist images
# lastfm still provides that endpoint with data,
# but doesn't provide actual images
def proxyscrobble_parse_response(self,data):
return data.attrib.get("status") == "ok" and data.find("scrobbles").attrib.get("ignored") == "0"

View File

@ -26,6 +26,8 @@ class MusicBrainz(MetadataInterface):
return None
# not supported
def get_image_album(self,album):
return None
def get_image_track(self,track):
self.lock.acquire()

View File

@ -15,9 +15,11 @@ class Spotify(MetadataInterface):
metadata = {
"trackurl": "https://api.spotify.com/v1/search?q=artist:{artist}%20track:{title}&type=track&access_token={token}",
"albumurl": "https://api.spotify.com/v1/search?q=artist:{artist}%album:{title}&type=album&access_token={token}",
"artisturl": "https://api.spotify.com/v1/search?q=artist:{artist}&type=artist&access_token={token}",
"response_type":"json",
"response_parse_tree_track": ["tracks","items",0,"album","images",0,"url"],
"response_parse_tree_track": ["tracks","items",0,"album","images",0,"url"], # use album art
"response_parse_tree_album": ["albums","items",0,"images",0,"url"],
"response_parse_tree_artist": ["artists","items",0,"images",0,"url"],
"required_settings": ["apiid","secret"],
}