Merge branch 'master' into feature-webedit

This commit is contained in:
krateng 2022-04-22 17:16:26 +02:00
commit 01d52d7e36
4 changed files with 59 additions and 47 deletions

View File

@ -39,9 +39,10 @@ COPY . .
RUN pip install /usr/src/app RUN pip install /usr/src/app
# Docker-specific configuration and default to IPv4 # Docker-specific configuration
# defaulting to IPv4 is no longer necessary (default host is dual stack)
ENV MALOJA_SKIP_SETUP=yes ENV MALOJA_SKIP_SETUP=yes
ENV MALOJA_HOST=0.0.0.0 ENV PYTHONUNBUFFERED=1
EXPOSE 42010 EXPOSE 42010
# use exec form for better signal handling https://docs.docker.com/engine/reference/builder/#entrypoint # use exec form for better signal handling https://docs.docker.com/engine/reference/builder/#entrypoint

View File

@ -36,3 +36,6 @@ minor_release_name: "Yeonhee"
3.0.6: 3.0.6:
notes: notes:
- "[Bugfix] Better parsing of featuring artists" - "[Bugfix] Better parsing of featuring artists"
- "[Bugfix] Fixed buffered output in Docker"
- "[Bugfix] Fixed importing a Spotify file without path"
- "[Bugfix] No longer releasing database lock during scrobble creation"

View File

@ -158,8 +158,8 @@ def connection_provider(func):
### DB -> DICT ### DB -> DICT
def scrobbles_db_to_dict(rows,include_internal=False): def scrobbles_db_to_dict(rows,include_internal=False,dbconn=None):
tracks = get_tracks_map(set(row.track_id for row in rows)) tracks = get_tracks_map(set(row.track_id for row in rows),dbconn=dbconn)
return [ return [
{ {
**{ **{
@ -177,11 +177,11 @@ def scrobbles_db_to_dict(rows,include_internal=False):
for row in rows for row in rows
] ]
def scrobble_db_to_dict(row): def scrobble_db_to_dict(row,dbconn=None):
return scrobbles_db_to_dict([row])[0] return scrobbles_db_to_dict([row],dbconn=dbconn)[0]
def tracks_db_to_dict(rows): def tracks_db_to_dict(rows,dbconn=None):
artists = get_artists_of_tracks(set(row.id for row in rows)) artists = get_artists_of_tracks(set(row.id for row in rows),dbconn=dbconn)
return [ return [
{ {
"artists":artists[row.id], "artists":artists[row.id],
@ -192,41 +192,41 @@ def tracks_db_to_dict(rows):
for row in rows for row in rows
] ]
def track_db_to_dict(row): def track_db_to_dict(row,dbconn=None):
return tracks_db_to_dict([row])[0] return tracks_db_to_dict([row],dbconn=dbconn)[0]
def artists_db_to_dict(rows): def artists_db_to_dict(rows,dbconn=None):
return [ return [
row.name row.name
for row in rows for row in rows
] ]
def artist_db_to_dict(row): def artist_db_to_dict(row,dbconn=None):
return artists_db_to_dict([row])[0] return artists_db_to_dict([row],dbconn=dbconn)[0]
### DICT -> DB ### DICT -> DB
def scrobble_dict_to_db(info): def scrobble_dict_to_db(info,dbconn=None):
return { return {
"timestamp":info['time'], "timestamp":info['time'],
"origin":info['origin'], "origin":info['origin'],
"duration":info['duration'], "duration":info['duration'],
"track_id":get_track_id(info['track']), "track_id":get_track_id(info['track'],dbconn=dbconn),
"extra":json.dumps(info.get('extra',{})), "extra":json.dumps(info.get('extra',{})),
"rawscrobble":json.dumps(info.get('rawscrobble',{})) "rawscrobble":json.dumps(info.get('rawscrobble',{}))
} }
def track_dict_to_db(info): def track_dict_to_db(info,dbconn=None):
return { return {
"title":info['title'], "title":info['title'],
"title_normalized":normalize_name(info['title']), "title_normalized":normalize_name(info['title']),
"length":info.get('length') "length":info.get('length')
} }
def artist_dict_to_db(info): def artist_dict_to_db(info,dbconn=None):
return { return {
"name": info, "name": info,
"name_normalized":normalize_name(info) "name_normalized":normalize_name(info)
@ -250,7 +250,7 @@ def add_scrobbles(scrobbleslist,dbconn=None):
ops = [ ops = [
DB['scrobbles'].insert().values( DB['scrobbles'].insert().values(
**scrobble_dict_to_db(s) **scrobble_dict_to_db(s,dbconn=dbconn)
) for s in scrobbleslist ) for s in scrobbleslist
] ]
@ -286,7 +286,7 @@ def delete_scrobble(scrobble_id,dbconn=None):
@connection_provider @connection_provider
def get_track_id(trackdict,create_new=True,dbconn=None): def get_track_id(trackdict,create_new=True,dbconn=None):
ntitle = normalize_name(trackdict['title']) ntitle = normalize_name(trackdict['title'])
artist_ids = [get_artist_id(a) for a in trackdict['artists']] artist_ids = [get_artist_id(a,dbconn=dbconn) for a in trackdict['artists']]
artist_ids = list(set(artist_ids)) artist_ids = list(set(artist_ids))
@ -318,7 +318,7 @@ def get_track_id(trackdict,create_new=True,dbconn=None):
op = DB['tracks'].insert().values( op = DB['tracks'].insert().values(
**track_dict_to_db(trackdict) **track_dict_to_db(trackdict,dbconn=dbconn)
) )
result = dbconn.execute(op) result = dbconn.execute(op)
track_id = result.inserted_primary_key[0] track_id = result.inserted_primary_key[0]
@ -459,7 +459,7 @@ def get_scrobbles_of_artist(artist,since=None,to=None,resolve_references=True,db
result = dbconn.execute(op).all() result = dbconn.execute(op).all()
if resolve_references: if resolve_references:
result = scrobbles_db_to_dict(result) result = scrobbles_db_to_dict(result,dbconn=dbconn)
#result = [scrobble_db_to_dict(row,resolve_references=resolve_references) for row in result] #result = [scrobble_db_to_dict(row,resolve_references=resolve_references) for row in result]
return result return result
@ -498,7 +498,7 @@ def get_scrobbles(since=None,to=None,resolve_references=True,dbconn=None):
result = dbconn.execute(op).all() result = dbconn.execute(op).all()
if resolve_references: if resolve_references:
result = scrobbles_db_to_dict(result) result = scrobbles_db_to_dict(result,dbconn=dbconn)
#result = [scrobble_db_to_dict(row,resolve_references=resolve_references) for i,row in enumerate(result) if i<max] #result = [scrobble_db_to_dict(row,resolve_references=resolve_references) for i,row in enumerate(result) if i<max]
return result return result
@ -528,7 +528,7 @@ def get_artists_of_track(track_id,resolve_references=True,dbconn=None):
) )
result = dbconn.execute(op).all() result = dbconn.execute(op).all()
artists = [get_artist(row.artist_id) if resolve_references else row.artist_id for row in result] artists = [get_artist(row.artist_id,dbconn=dbconn) if resolve_references else row.artist_id for row in result]
return artists return artists
@ -543,7 +543,7 @@ def get_tracks_of_artist(artist,dbconn=None):
) )
result = dbconn.execute(op).all() result = dbconn.execute(op).all()
return tracks_db_to_dict(result) return tracks_db_to_dict(result,dbconn=dbconn)
@cached_wrapper @cached_wrapper
@connection_provider @connection_provider
@ -552,7 +552,7 @@ def get_artists(dbconn=None):
op = DB['artists'].select() op = DB['artists'].select()
result = dbconn.execute(op).all() result = dbconn.execute(op).all()
return artists_db_to_dict(result) return artists_db_to_dict(result,dbconn=dbconn)
@cached_wrapper @cached_wrapper
@connection_provider @connection_provider
@ -561,7 +561,7 @@ def get_tracks(dbconn=None):
op = DB['tracks'].select() op = DB['tracks'].select()
result = dbconn.execute(op).all() result = dbconn.execute(op).all()
return tracks_db_to_dict(result) return tracks_db_to_dict(result,dbconn=dbconn)
### functions that count rows for parameters ### functions that count rows for parameters
@ -597,7 +597,7 @@ def count_scrobbles_by_artist(since,to,resolve_ids=True,dbconn=None):
if resolve_ids: if resolve_ids:
counts = [row.count for row in result] counts = [row.count for row in result]
artists = get_artists_map([row.artist_id for row in result]) artists = get_artists_map([row.artist_id for row in result],dbconn=dbconn)
result = [{'scrobbles':row.count,'artist':artists[row.artist_id]} for row in result] result = [{'scrobbles':row.count,'artist':artists[row.artist_id]} for row in result]
else: else:
result = [{'scrobbles':row.count,'artist_id':row.artist_id} for row in result] result = [{'scrobbles':row.count,'artist_id':row.artist_id} for row in result]
@ -620,7 +620,7 @@ def count_scrobbles_by_track(since,to,resolve_ids=True,dbconn=None):
if resolve_ids: if resolve_ids:
counts = [row.count for row in result] counts = [row.count for row in result]
tracks = get_tracks_map([row.track_id for row in result]) tracks = get_tracks_map([row.track_id for row in result],dbconn=dbconn)
result = [{'scrobbles':row.count,'track':tracks[row.track_id]} for row in result] result = [{'scrobbles':row.count,'track':tracks[row.track_id]} for row in result]
else: else:
result = [{'scrobbles':row.count,'track_id':row.track_id} for row in result] result = [{'scrobbles':row.count,'track_id':row.track_id} for row in result]
@ -671,7 +671,7 @@ def get_artists_of_tracks(track_ids,dbconn=None):
artists = {} artists = {}
for row in result: for row in result:
artists.setdefault(row.track_id,[]).append(artist_db_to_dict(row)) artists.setdefault(row.track_id,[]).append(artist_db_to_dict(row,dbconn=dbconn))
return artists return artists
@ -686,7 +686,7 @@ def get_tracks_map(track_ids,dbconn=None):
tracks = {} tracks = {}
result = list(result) result = list(result)
# this will get a list of artistdicts in the correct order of our rows # this will get a list of artistdicts in the correct order of our rows
trackdicts = tracks_db_to_dict(result) trackdicts = tracks_db_to_dict(result,dbconn=dbconn)
for row,trackdict in zip(result,trackdicts): for row,trackdict in zip(result,trackdicts):
tracks[row.id] = trackdict tracks[row.id] = trackdict
return tracks return tracks
@ -703,7 +703,7 @@ def get_artists_map(artist_ids,dbconn=None):
artists = {} artists = {}
result = list(result) result = list(result)
# this will get a list of artistdicts in the correct order of our rows # this will get a list of artistdicts in the correct order of our rows
artistdicts = artists_db_to_dict(result) artistdicts = artists_db_to_dict(result,dbconn=dbconn)
for row,artistdict in zip(result,artistdicts): for row,artistdict in zip(result,artistdicts):
artists[row.id] = artistdict artists[row.id] = artistdict
return artists return artists
@ -714,7 +714,7 @@ def get_artists_map(artist_ids,dbconn=None):
@cached_wrapper @cached_wrapper
@connection_provider @connection_provider
def get_associated_artists(*artists,dbconn=None): def get_associated_artists(*artists,dbconn=None):
artist_ids = [get_artist_id(a) for a in artists] artist_ids = [get_artist_id(a,dbconn=dbconn) for a in artists]
jointable = sql.join( jointable = sql.join(
DB['associated_artists'], DB['associated_artists'],
@ -727,7 +727,7 @@ def get_associated_artists(*artists,dbconn=None):
) )
result = dbconn.execute(op).all() result = dbconn.execute(op).all()
artists = artists_db_to_dict(result) artists = artists_db_to_dict(result,dbconn=dbconn)
return artists return artists
@cached_wrapper @cached_wrapper
@ -747,7 +747,7 @@ def get_credited_artists(*artists,dbconn=None):
) )
result = dbconn.execute(op).all() result = dbconn.execute(op).all()
artists = artists_db_to_dict(result) artists = artists_db_to_dict(result,dbconn=dbconn)
return artists return artists
@ -762,7 +762,7 @@ def get_track(id,dbconn=None):
result = dbconn.execute(op).all() result = dbconn.execute(op).all()
trackinfo = result[0] trackinfo = result[0]
return track_db_to_dict(trackinfo) return track_db_to_dict(trackinfo,dbconn=dbconn)
@cached_wrapper @cached_wrapper
@connection_provider @connection_provider
@ -773,7 +773,7 @@ def get_artist(id,dbconn=None):
result = dbconn.execute(op).all() result = dbconn.execute(op).all()
artistinfo = result[0] artistinfo = result[0]
return artist_db_to_dict(artistinfo) return artist_db_to_dict(artistinfo,dbconn=dbconn)
@ -823,15 +823,14 @@ def renormalize_names():
with engine.begin() as conn: with engine.begin() as conn:
rows = conn.execute(DB['artists'].select()).all() rows = conn.execute(DB['artists'].select()).all()
for row in rows: for row in rows:
id = row.id id = row.id
name = row.name name = row.name
norm_actual = row.name_normalized norm_actual = row.name_normalized
norm_target = normalize_name(name) norm_target = normalize_name(name)
if norm_actual != norm_target: if norm_actual != norm_target:
log(f"{name} should be normalized to {norm_target}, but is instead {norm_actual}, fixing...") log(f"{name} should be normalized to {norm_target}, but is instead {norm_actual}, fixing...")
with engine.begin() as conn:
rows = conn.execute(DB['artists'].update().where(DB['artists'].c.id == id).values(name_normalized=norm_target)) rows = conn.execute(DB['artists'].update().where(DB['artists'].c.id == id).values(name_normalized=norm_target))

View File

@ -117,10 +117,15 @@ def import_scrobbles(inputf):
return result return result
def parse_spotify_lite(inputf): def parse_spotify_lite(inputf):
inputfolder = os.path.dirname(inputf) pth = os.path
inputfolder = pth.relpath(pth.dirname(pth.abspath(inputf)))
filenames = re.compile(r'StreamingHistory[0-9]+\.json') filenames = re.compile(r'StreamingHistory[0-9]+\.json')
inputfiles = [os.path.join(inputfolder,f) for f in os.listdir(inputfolder) if filenames.match(f)] inputfiles = [os.path.join(inputfolder,f) for f in os.listdir(inputfolder) if filenames.match(f)]
if len(inputfiles) == 0:
print("No files found!")
return
if inputfiles != [inputf]: if inputfiles != [inputf]:
print("Spotify files should all be imported together to identify duplicates across the whole dataset.") print("Spotify files should all be imported together to identify duplicates across the whole dataset.")
if not ask("Import " + ", ".join(col['yellow'](i) for i in inputfiles) + "?",default=True): if not ask("Import " + ", ".join(col['yellow'](i) for i in inputfiles) + "?",default=True):
@ -161,11 +166,15 @@ def parse_spotify_lite(inputf):
def parse_spotify_full(inputf): def parse_spotify_full(inputf):
pth = os.path
inputfolder = os.path.dirname(inputf) inputfolder = pth.relpath(pth.dirname(pth.abspath(inputf)))
filenames = re.compile(r'endsong_[0-9]+\.json') filenames = re.compile(r'endsong_[0-9]+\.json')
inputfiles = [os.path.join(inputfolder,f) for f in os.listdir(inputfolder) if filenames.match(f)] inputfiles = [os.path.join(inputfolder,f) for f in os.listdir(inputfolder) if filenames.match(f)]
if len(inputfiles) == 0:
print("No files found!")
return
if inputfiles != [inputf]: if inputfiles != [inputf]:
print("Spotify files should all be imported together to identify duplicates across the whole dataset.") print("Spotify files should all be imported together to identify duplicates across the whole dataset.")
if not ask("Import " + ", ".join(col['yellow'](i) for i in inputfiles) + "?",default=True): if not ask("Import " + ", ".join(col['yellow'](i) for i in inputfiles) + "?",default=True):