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:
parent
69b456dc73
commit
fd9987ec35
@ -519,7 +519,7 @@ def post_scrobble(
|
|||||||
@api.post("addpicture")
|
@api.post("addpicture")
|
||||||
@authenticated_function(alternate=api_key_correct,api=True)
|
@authenticated_function(alternate=api_key_correct,api=True)
|
||||||
@catch_exceptions
|
@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.
|
"""Uploads a new image for an artist or track.
|
||||||
|
|
||||||
param string b64: Base 64 representation of the image
|
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:
|
for a in artist:
|
||||||
keys.append("artist",a)
|
keys.append("artist",a)
|
||||||
if title is not None: keys.append("title",title)
|
if title is not None: keys.append("title",title)
|
||||||
|
elif albumtitle is not None: keys.append("albumtitle",albumtitle)
|
||||||
k_filter, _, _, _, _ = uri_to_internal(keys)
|
k_filter, _, _, _, _ = uri_to_internal(keys)
|
||||||
if "track" in k_filter: k_filter = k_filter["track"]
|
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)
|
url = images.set_image(b64,**k_filter)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -39,6 +39,13 @@ DB['tracks'] = sql.Table(
|
|||||||
sql.Column('expire',sql.Integer),
|
sql.Column('expire',sql.Integer),
|
||||||
sql.Column('raw',sql.String)
|
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)
|
meta.create_all(engine)
|
||||||
|
|
||||||
@ -137,7 +144,7 @@ def resolve_track_image(track_id):
|
|||||||
|
|
||||||
# local image
|
# local image
|
||||||
if malojaconfig["USE_LOCAL_IMAGES"]:
|
if malojaconfig["USE_LOCAL_IMAGES"]:
|
||||||
images = local_files(artists=track['artists'],title=track['title'])
|
images = local_files(track=track)
|
||||||
if len(images) != 0:
|
if len(images) != 0:
|
||||||
result = random.choice(images)
|
result = random.choice(images)
|
||||||
result = urllib.parse.quote(result)
|
result = urllib.parse.quote(result)
|
||||||
@ -181,31 +188,56 @@ def resolve_artist_image(artist_id):
|
|||||||
return result
|
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
|
# removes emojis and weird shit from names
|
||||||
def clean(name):
|
def clean(name):
|
||||||
return "".join(c for c in name if c.isalnum() or c in []).strip()
|
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):
|
# new and improved
|
||||||
# check if we're dealing with a track or artist, then clean up names
|
def get_all_possible_filenames(artist=None,track=None,album=None):
|
||||||
# (only remove non-alphanumeric, allow korean and stuff)
|
if track:
|
||||||
|
title, artists = clean(track['title']), [clean(a) for a in track['artists']]
|
||||||
if title is not None and artists is not None:
|
superfolder = "tracks/"
|
||||||
track = True
|
elif album:
|
||||||
title, artists = clean(title), [clean(a) for a in artists]
|
title, artists = clean(album['albumtitle']), [clean(a) for a in album['artists']]
|
||||||
elif artist is not None:
|
superfolder = "albums/"
|
||||||
track = False
|
elif artist:
|
||||||
artist = clean(artist)
|
artist = clean(artist)
|
||||||
else: return []
|
superfolder = "artists/"
|
||||||
|
else:
|
||||||
|
return []
|
||||||
superfolder = "tracks/" if track else "artists/"
|
|
||||||
|
|
||||||
filenames = []
|
filenames = []
|
||||||
|
|
||||||
if track:
|
if track or album:
|
||||||
#unsafeartists = [artist.translate(None,"-_./\\") for artist in artists]
|
|
||||||
safeartists = [re.sub("[^a-zA-Z0-9]","",artist) for artist in artists]
|
safeartists = [re.sub("[^a-zA-Z0-9]","",artist) for artist in artists]
|
||||||
#unsafetitle = title.translate(None,"-_./\\")
|
|
||||||
safetitle = re.sub("[^a-zA-Z0-9]","",title)
|
safetitle = re.sub("[^a-zA-Z0-9]","",title)
|
||||||
|
|
||||||
if len(artists) < 4:
|
if len(artists) < 4:
|
||||||
@ -215,7 +247,6 @@ def get_all_possible_filenames(artist=None,artists=None,title=None):
|
|||||||
unsafeperms = [sorted(artists)]
|
unsafeperms = [sorted(artists)]
|
||||||
safeperms = [sorted(safeartists)]
|
safeperms = [sorted(safeartists)]
|
||||||
|
|
||||||
|
|
||||||
for unsafeartistlist in unsafeperms:
|
for unsafeartistlist in unsafeperms:
|
||||||
filename = "-".join(unsafeartistlist) + "_" + title
|
filename = "-".join(unsafeartistlist) + "_" + title
|
||||||
if filename != "":
|
if filename != "":
|
||||||
@ -246,10 +277,11 @@ def get_all_possible_filenames(artist=None,artists=None,title=None):
|
|||||||
|
|
||||||
return [superfolder + name for name in filenames]
|
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 = []
|
images = []
|
||||||
|
|
||||||
@ -276,13 +308,18 @@ class MalformedB64(Exception):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
def set_image(b64,**keys):
|
def set_image(b64,**keys):
|
||||||
track = "title" in keys
|
if "title" in keys:
|
||||||
if track:
|
entity = {"track":keys}
|
||||||
entity = {'artists':keys['artists'],'title':keys['title']}
|
id = database.sqldb.get_track_id(entity['track'])
|
||||||
id = database.sqldb.get_track_id(entity)
|
dbtable = "tracks"
|
||||||
else:
|
elif "albumtitle" in keys:
|
||||||
entity = keys['artist']
|
entity = {"album":keys}
|
||||||
id = database.sqldb.get_artist_id(entity)
|
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")
|
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()
|
type,b64 = match.groups()
|
||||||
b64 = base64.b64decode(b64)
|
b64 = base64.b64decode(b64)
|
||||||
filename = "webupload" + str(int(datetime.datetime.now().timestamp())) + "." + type
|
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)):
|
if os.path.exists(data_dir['images'](folder)):
|
||||||
with open(data_dir['images'](folder,filename),"wb") as f:
|
with open(data_dir['images'](folder,filename),"wb") as f:
|
||||||
f.write(b64)
|
f.write(b64)
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
folder = get_all_possible_filenames(**keys)[0]
|
folder = get_all_possible_filenames(**entity)[0]
|
||||||
os.makedirs(data_dir['images'](folder))
|
os.makedirs(data_dir['images'](folder))
|
||||||
with open(data_dir['images'](folder,filename),"wb") as f:
|
with open(data_dir['images'](folder,filename),"wb") as f:
|
||||||
f.write(b64)
|
f.write(b64)
|
||||||
@ -308,7 +345,6 @@ def set_image(b64,**keys):
|
|||||||
log("Saved image as " + data_dir['images'](folder,filename),module="debug")
|
log("Saved image as " + data_dir['images'](folder,filename),module="debug")
|
||||||
|
|
||||||
# set as current picture in rotation
|
# set as current picture in rotation
|
||||||
if track: set_image_in_cache(id,'tracks',os.path.join("/images",folder,filename))
|
set_image_in_cache(id,dbtable,os.path.join("/images",folder,filename))
|
||||||
else: set_image_in_cache(id,'artists',os.path.join("/images",folder,filename))
|
|
||||||
|
|
||||||
return os.path.join("/images",folder,filename)
|
return os.path.join("/images",folder,filename)
|
||||||
|
@ -19,7 +19,7 @@ from doreah import auth
|
|||||||
# rest of the project
|
# rest of the project
|
||||||
from . import database
|
from . import database
|
||||||
from .database.jinjaview import JinjaDBConnection
|
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 .malojauri import uri_to_internal, remove_identical
|
||||||
from .pkg_global.conf import malojaconfig, data_dir
|
from .pkg_global.conf import malojaconfig, data_dir
|
||||||
from .jinjaenv.context import jinja_environment
|
from .jinjaenv.context import jinja_environment
|
||||||
@ -124,6 +124,8 @@ def dynamic_image():
|
|||||||
result = resolve_track_image(keys['id'])
|
result = resolve_track_image(keys['id'])
|
||||||
elif keys['type'] == 'artist':
|
elif keys['type'] == 'artist':
|
||||||
result = resolve_artist_image(keys['id'])
|
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,'']:
|
if result is None or result['value'] in [None,'']:
|
||||||
return ""
|
return ""
|
||||||
|
33
maloja/thirdparty/__init__.py
vendored
33
maloja/thirdparty/__init__.py
vendored
@ -63,7 +63,18 @@ def get_image_artist_all(artist):
|
|||||||
log("Could not get artist image for " + str(artist) + " from " + service.name)
|
log("Could not get artist image for " + str(artist) + " from " + service.name)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
log("Error getting artist image from " + service.name + ": " + repr(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:
|
class GenericInterface:
|
||||||
@ -217,6 +228,23 @@ class MetadataInterface(GenericInterface,abstract=True):
|
|||||||
if imgurl is not None: imgurl = self.postprocess_url(imgurl)
|
if imgurl is not None: imgurl = self.postprocess_url(imgurl)
|
||||||
return 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
|
# default function to parse response by descending down nodes
|
||||||
# override if more complicated
|
# override if more complicated
|
||||||
def metadata_parse_response_artist(self,data):
|
def metadata_parse_response_artist(self,data):
|
||||||
@ -225,6 +253,9 @@ class MetadataInterface(GenericInterface,abstract=True):
|
|||||||
def metadata_parse_response_track(self,data):
|
def metadata_parse_response_track(self,data):
|
||||||
return self._parse_response("response_parse_tree_track", 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):
|
def _parse_response(self, resp, data):
|
||||||
res = data
|
res = data
|
||||||
for node in self.metadata[resp]:
|
for node in self.metadata[resp]:
|
||||||
|
8
maloja/thirdparty/audiodb.py
vendored
8
maloja/thirdparty/audiodb.py
vendored
@ -9,13 +9,17 @@ class AudioDB(MetadataInterface):
|
|||||||
}
|
}
|
||||||
|
|
||||||
metadata = {
|
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}",
|
"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_type":"json",
|
||||||
#"response_parse_tree_track": ["tracks",0,"astrArtistThumb"],
|
#"response_parse_tree_track": ["tracks",0,"astrArtistThumb"],
|
||||||
"response_parse_tree_artist": ["artists",0,"strArtistThumb"],
|
"response_parse_tree_artist": ["artists",0,"strArtistThumb"],
|
||||||
"required_settings": ["api_key"],
|
"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
|
return None
|
||||||
|
11
maloja/thirdparty/deezer.py
vendored
11
maloja/thirdparty/deezer.py
vendored
@ -8,10 +8,17 @@ class Deezer(MetadataInterface):
|
|||||||
}
|
}
|
||||||
|
|
||||||
metadata = {
|
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}",
|
"artisturl": "https://api.deezer.com/search?q={artist}",
|
||||||
|
"albumurl": "https://api.deezer.com/search?q={artist}%20{title}",
|
||||||
"response_type":"json",
|
"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_artist": ["data",0,"artist","picture_medium"],
|
||||||
|
"response_parse_tree_album": ["data",0,"album","cover_medium"],
|
||||||
"required_settings": [],
|
"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
|
||||||
|
9
maloja/thirdparty/lastfm.py
vendored
9
maloja/thirdparty/lastfm.py
vendored
@ -22,15 +22,22 @@ class LastFM(MetadataInterface, ProxyScrobbleInterface):
|
|||||||
"activated_setting": "SCROBBLE_LASTFM"
|
"activated_setting": "SCROBBLE_LASTFM"
|
||||||
}
|
}
|
||||||
metadata = {
|
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",
|
"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_type":"json",
|
||||||
"response_parse_tree_track": ["track","album","image",-1,"#text"],
|
"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"],
|
"required_settings": ["apikey"],
|
||||||
}
|
}
|
||||||
|
|
||||||
def get_image_artist(self,artist):
|
def get_image_artist(self,artist):
|
||||||
return None
|
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):
|
def proxyscrobble_parse_response(self,data):
|
||||||
return data.attrib.get("status") == "ok" and data.find("scrobbles").attrib.get("ignored") == "0"
|
return data.attrib.get("status") == "ok" and data.find("scrobbles").attrib.get("ignored") == "0"
|
||||||
|
2
maloja/thirdparty/musicbrainz.py
vendored
2
maloja/thirdparty/musicbrainz.py
vendored
@ -26,6 +26,8 @@ class MusicBrainz(MetadataInterface):
|
|||||||
return None
|
return None
|
||||||
# not supported
|
# not supported
|
||||||
|
|
||||||
|
def get_image_album(self,album):
|
||||||
|
return None
|
||||||
|
|
||||||
def get_image_track(self,track):
|
def get_image_track(self,track):
|
||||||
self.lock.acquire()
|
self.lock.acquire()
|
||||||
|
4
maloja/thirdparty/spotify.py
vendored
4
maloja/thirdparty/spotify.py
vendored
@ -15,9 +15,11 @@ class Spotify(MetadataInterface):
|
|||||||
|
|
||||||
metadata = {
|
metadata = {
|
||||||
"trackurl": "https://api.spotify.com/v1/search?q=artist:{artist}%20track:{title}&type=track&access_token={token}",
|
"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}",
|
"artisturl": "https://api.spotify.com/v1/search?q=artist:{artist}&type=artist&access_token={token}",
|
||||||
"response_type":"json",
|
"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"],
|
"response_parse_tree_artist": ["artists","items",0,"images",0,"url"],
|
||||||
"required_settings": ["apiid","secret"],
|
"required_settings": ["apiid","secret"],
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user