Added UI selector for including associated artists

This commit is contained in:
krateng 2023-10-18 10:58:46 +02:00
parent acf7402095
commit b9e3cd7624
7 changed files with 82 additions and 29 deletions

View File

@ -6,6 +6,8 @@ minor_release_name: "Nicole"
- "[Feature] Added basic support for albums"
- "[Feature] New start page"
- "[Feature] Added UI for track-artist, track-album and album-artist association"
- "[Feature] Added inline UI for association and merging in chart lists"
- "[Feature] Added UI selector for including associated artists"
- "[Performance] Improved image rendering"
- "[Bugfix] Fixed configuration of time format"
- "[Bugfix] Fixed search on manual scrobble page"

View File

@ -309,8 +309,9 @@ def associate_tracks_to_album(target_id,source_ids):
@waitfordb
def get_scrobbles(dbconn=None,**keys):
(since,to) = keys.get('timerange').timestamps()
associated = keys.get('associated',False)
if 'artist' in keys:
result = sqldb.get_scrobbles_of_artist(artist=keys['artist'],since=since,to=to,dbconn=dbconn)
result = sqldb.get_scrobbles_of_artist(artist=keys['artist'],since=since,to=to,associated=associated,dbconn=dbconn)
elif 'track' in keys:
result = sqldb.get_scrobbles_of_track(track=keys['track'],since=since,to=to,dbconn=dbconn)
elif 'album' in keys:
@ -324,8 +325,9 @@ def get_scrobbles(dbconn=None,**keys):
@waitfordb
def get_scrobbles_num(dbconn=None,**keys):
(since,to) = keys.get('timerange').timestamps()
associated = keys.get('associated',False)
if 'artist' in keys:
result = len(sqldb.get_scrobbles_of_artist(artist=keys['artist'],since=since,to=to,resolve_references=False,dbconn=dbconn))
result = len(sqldb.get_scrobbles_of_artist(artist=keys['artist'],since=since,to=to,associated=associated,resolve_references=False,dbconn=dbconn))
elif 'track' in keys:
result = len(sqldb.get_scrobbles_of_track(track=keys['track'],since=since,to=to,resolve_references=False,dbconn=dbconn))
elif 'album' in keys:
@ -370,14 +372,15 @@ def get_tracks_without_album(dbconn=None,resolve_ids=True):
@waitfordb
def get_charts_artists(dbconn=None,resolve_ids=True,**keys):
(since,to) = keys.get('timerange').timestamps()
result = sqldb.count_scrobbles_by_artist(since=since,to=to,resolve_ids=resolve_ids,dbconn=dbconn)
associated = keys.get('associated',True)
result = sqldb.count_scrobbles_by_artist(since=since,to=to,resolve_ids=resolve_ids,associated=associated,dbconn=dbconn)
return result
@waitfordb
def get_charts_tracks(dbconn=None,resolve_ids=True,**keys):
(since,to) = keys.get('timerange').timestamps()
if 'artist' in keys:
result = sqldb.count_scrobbles_by_track_of_artist(since=since,to=to,artist=keys['artist'],resolve_ids=resolve_ids,dbconn=dbconn)
result = sqldb.count_scrobbles_by_track_of_artist(since=since,to=to,artist=keys['artist'],associated=keys.get('associated',False),resolve_ids=resolve_ids,dbconn=dbconn)
elif 'album' in keys:
result = sqldb.count_scrobbles_by_track_of_album(since=since,to=to,album=keys['album'],resolve_ids=resolve_ids,dbconn=dbconn)
else:
@ -388,7 +391,7 @@ def get_charts_tracks(dbconn=None,resolve_ids=True,**keys):
def get_charts_albums(dbconn=None,resolve_ids=True,**keys):
(since,to) = keys.get('timerange').timestamps()
if 'artist' in keys:
result = sqldb.count_scrobbles_by_album_of_artist(since=since,to=to,artist=keys['artist'],resolve_ids=resolve_ids,dbconn=dbconn)
result = sqldb.count_scrobbles_by_album_of_artist(since=since,to=to,artist=keys['artist'],associated=keys.get('associated',False),resolve_ids=resolve_ids,dbconn=dbconn)
else:
result = sqldb.count_scrobbles_by_album(since=since,to=to,resolve_ids=resolve_ids,dbconn=dbconn)
return result
@ -629,15 +632,16 @@ def get_featured(dbconn=None):
alltime()
]
funcs = {
"artist": get_charts_artists,
"album": get_charts_albums,
"track": get_charts_tracks
"artist": (get_charts_artists,{'associated':False}),
"album": (get_charts_albums,{}),
"track": (get_charts_tracks,{})
}
result = {t:None for t in funcs}
for entity_type in funcs:
for r in ranges:
chart = funcs[entity_type](timerange=r)
func,kwargs = funcs[entity_type]
chart = func(timerange=r,**kwargs)
if chart:
result[entity_type] = chart[0][entity_type]
break

View File

@ -763,19 +763,23 @@ def merge_albums(target_id,source_ids,dbconn=None):
@cached_wrapper
@connection_provider
def get_scrobbles_of_artist(artist,since=None,to=None,resolve_references=True,dbconn=None):
def get_scrobbles_of_artist(artist,since=None,to=None,resolve_references=True,associated=False,dbconn=None):
if since is None: since=0
if to is None: to=now()
artist_id = get_artist_id(artist,dbconn=dbconn)
if associated:
artist_ids = get_associated_artists(artist,resolve_ids=False,dbconn=dbconn) + [get_artist_id(artist,dbconn=dbconn)]
else:
artist_ids = [get_artist_id(artist,dbconn=dbconn)]
jointable = sql.join(DB['scrobbles'],DB['trackartists'],DB['scrobbles'].c.track_id == DB['trackartists'].c.track_id)
op = jointable.select().where(
DB['scrobbles'].c.timestamp<=to,
DB['scrobbles'].c.timestamp>=since,
DB['trackartists'].c.artist_id==artist_id
DB['trackartists'].c.artist_id.in_(artist_ids)
).order_by(sql.asc('timestamp'))
result = dbconn.execute(op).all()
@ -911,7 +915,7 @@ def get_tracks(dbconn=None):
@cached_wrapper
@connection_provider
def count_scrobbles_by_artist(since,to,resolve_ids=True,dbconn=None):
def count_scrobbles_by_artist(since,to,associated=True,resolve_ids=True,dbconn=None):
jointable = sql.join(
DB['scrobbles'],
DB['trackartists'],
@ -924,18 +928,24 @@ def count_scrobbles_by_artist(since,to,resolve_ids=True,dbconn=None):
DB['trackartists'].c.artist_id == DB['associated_artists'].c.source_artist,
isouter=True
)
if associated:
artistselect = sql.func.coalesce(DB['associated_artists'].c.target_artist,DB['trackartists'].c.artist_id)
else:
artistselect = DB['trackartists'].c.artist_id
op = sql.select(
sql.func.count(sql.func.distinct(DB['scrobbles'].c.timestamp)).label('count'),
# only count distinct scrobbles - because of artist replacement, we could end up
# with two artists of the same scrobble counting it twice for the same artist
# e.g. Irene and Seulgi adding two scrobbles to Red Velvet for one real scrobble
sql.func.coalesce(DB['associated_artists'].c.target_artist,DB['trackartists'].c.artist_id).label('artist_id')
artistselect.label('artist_id')
# use the replaced artist as artist to count if it exists, otherwise original one
).select_from(jointable2).where(
DB['scrobbles'].c.timestamp<=to,
DB['scrobbles'].c.timestamp>=since
).group_by(
sql.func.coalesce(DB['associated_artists'].c.target_artist,DB['trackartists'].c.artist_id)
artistselect
).order_by(sql.desc('count'))
result = dbconn.execute(op).all()
@ -1001,9 +1011,12 @@ def count_scrobbles_by_album(since,to,resolve_ids=True,dbconn=None):
@connection_provider
# this ranks the albums of that artist, not albums the artist appears on - even scrobbles
# of tracks the artist is not part of!
def count_scrobbles_by_album_of_artist(since,to,artist,resolve_ids=True,dbconn=None):
def count_scrobbles_by_album_of_artist(since,to,artist,associated=False,resolve_ids=True,dbconn=None):
artist_id = get_artist_id(artist,dbconn=dbconn)
if associated:
artist_ids = get_associated_artists(artist,resolve_ids=False,dbconn=dbconn) + [get_artist_id(artist,dbconn=dbconn)]
else:
artist_ids = [get_artist_id(artist,dbconn=dbconn)]
jointable = sql.join(
DB['scrobbles'],
@ -1022,7 +1035,7 @@ def count_scrobbles_by_album_of_artist(since,to,artist,resolve_ids=True,dbconn=N
).select_from(jointable2).where(
DB['scrobbles'].c.timestamp<=to,
DB['scrobbles'].c.timestamp>=since,
DB['albumartists'].c.artist_id == artist_id
DB['albumartists'].c.artist_id.in_(artist_ids)
).group_by(DB['tracks'].c.album_id).order_by(sql.desc('count'))
result = dbconn.execute(op).all()
@ -1038,9 +1051,12 @@ def count_scrobbles_by_album_of_artist(since,to,artist,resolve_ids=True,dbconn=N
@connection_provider
# this ranks the tracks of that artist by the album they appear on - even when the album
# is not the artist's
def count_scrobbles_of_artist_by_album(since,to,artist,resolve_ids=True,dbconn=None):
def count_scrobbles_of_artist_by_album(since,to,artist,associated=False,resolve_ids=True,dbconn=None):
artist_id = get_artist_id(artist,dbconn=dbconn)
if associated:
artist_ids = get_associated_artists(artist,resolve_ids=False,dbconn=dbconn) + [get_artist_id(artist,dbconn=dbconn)]
else:
artist_ids = [get_artist_id(artist,dbconn=dbconn)]
jointable = sql.join(
DB['scrobbles'],
@ -1059,7 +1075,7 @@ def count_scrobbles_of_artist_by_album(since,to,artist,resolve_ids=True,dbconn=N
).select_from(jointable2).where(
DB['scrobbles'].c.timestamp<=to,
DB['scrobbles'].c.timestamp>=since,
DB['trackartists'].c.artist_id == artist_id
DB['trackartists'].c.artist_id.in_(artist_ids)
).group_by(DB['tracks'].c.album_id).order_by(sql.desc('count'))
result = dbconn.execute(op).all()
@ -1074,9 +1090,12 @@ def count_scrobbles_of_artist_by_album(since,to,artist,resolve_ids=True,dbconn=N
@cached_wrapper
@connection_provider
def count_scrobbles_by_track_of_artist(since,to,artist,resolve_ids=True,dbconn=None):
def count_scrobbles_by_track_of_artist(since,to,artist,associated=False,resolve_ids=True,dbconn=None):
artist_id = get_artist_id(artist,dbconn=dbconn)
if associated:
artist_ids = get_associated_artists(artist,resolve_ids=False,dbconn=dbconn) + [get_artist_id(artist,dbconn=dbconn)]
else:
artist_ids = [get_artist_id(artist,dbconn=dbconn)]
jointable = sql.join(
DB['scrobbles'],
@ -1090,7 +1109,7 @@ def count_scrobbles_by_track_of_artist(since,to,artist,resolve_ids=True,dbconn=N
).select_from(jointable).filter(
DB['scrobbles'].c.timestamp<=to,
DB['scrobbles'].c.timestamp>=since,
DB['trackartists'].c.artist_id==artist_id
DB['trackartists'].c.artist_id.in_(artist_ids)
).group_by(DB['scrobbles'].c.track_id).order_by(sql.desc('count'))
result = dbconn.execute(op).all()
@ -1301,7 +1320,7 @@ def get_albums_map(album_ids,dbconn=None):
@cached_wrapper
@connection_provider
def get_associated_artists(*artists,dbconn=None):
def get_associated_artists(*artists,resolve_ids=True,dbconn=None):
artist_ids = [get_artist_id(a,dbconn=dbconn) for a in artists]
jointable = sql.join(
@ -1319,8 +1338,11 @@ def get_associated_artists(*artists,dbconn=None):
)
result = dbconn.execute(op).all()
artists = artists_db_to_dict(result,dbconn=dbconn)
return artists
if resolve_ids:
artists = artists_db_to_dict(result,dbconn=dbconn)
return artists
else:
return [a.id for a in result]
@cached_wrapper
@connection_provider

View File

@ -72,6 +72,10 @@ def update_jinja_environment():
{"identifier":"longtrailing","replacekeys":{"trail":3},"localisation":"Long Trailing"},
{"identifier":"inert","replacekeys":{"trail":10},"localisation":"Inert","heavy":True},
{"identifier":"cumulative","replacekeys":{"trail":math.inf},"localisation":"Cumulative","heavy":True}
],
"xassociated": [
{"identifier":"include_associated","replacekeys":{"associated":True},"localisation":"Associated"},
{"identifier":"exclude_associated","replacekeys":{"associated":False},"localisation":"Exclusive"}
]
}

View File

@ -31,12 +31,18 @@ def uri_to_internal(keys,forceTrack=False,forceArtist=False,forceAlbum=False,api
filterkeys = {"track":{"artists":keys.getall("artist"),"title":keys.get("title")}}
elif type == "artist":
filterkeys = {"artist":keys.get("artist")}
if "associated" in keys: filterkeys["associated"] = True
filterkeys["associated"] = (keys.get('associated','no').lower() == 'yes')
# associated is only used for filtering by artist, to indicate that we include associated artists
# for actual artist charts, to show that we want to count them, use 'unified'
elif type == "album":
filterkeys = {"album":{"artists":keys.getall("artist"),"albumtitle":keys.get("albumtitle") or keys.get("title")}}
else:
filterkeys = {}
# this can be the case regardless of actual entity filter
# e.g if i get scrobbles of an artist associated tells me i want all scrobbles by associated artists
# but seeing the artist charts (wich have no filterkeys) also is affected by this
# 2
limitkeys = {}
since,to,within = None,None,None
@ -105,6 +111,7 @@ def internal_to_uri(keys):
urikeys.append("artist",a)
urikeys.append("albumtitle",keys["album"]["albumtitle"])
#time
if "timerange" in keys:
keydict = keys["timerange"].urikeys()

View File

@ -3,7 +3,7 @@
{% macro desc(filterkeys,limitkeys,prefix="by") %}
{% if filterkeys.get('artist') is not none %}
{{ prefix }} {{ links.link(filterkeys.get('artist')) }}
{{ prefix }} {{ links.link(filterkeys.get('artist')) }}{% if filterkeys.get('associated') %} (and associated artists){% endif %}
{% elif filterkeys.get('track') is not none %}
of {{ links.link(filterkeys.get('track')) }}
by {{ links.links(filterkeys["track"]["artists"]) }}

View File

@ -61,3 +61,17 @@
</div>
{% endif %}
{% if 'artist' in filterkeys %}
<div>
{% for o in xassociated %}
{% if o.replacekeys | map('compare_key_in_dicts',o.replacekeys,allkeys) | alltrue %}
<span style='opacity:0.5;'>{{ o.localisation }}</span>
{% else %}
<a href='{{ mlj_uri.create_uri("",allkeys,o.replacekeys) }}'><span>{{ o.localisation }}</span></a>
{% endif %}
{{ "|" if not loop.last }}
{% endfor %}
</div>
{% endif %}