diff --git a/maloja/apis/native_v1.py b/maloja/apis/native_v1.py index 761c23a..df8714a 100644 --- a/maloja/apis/native_v1.py +++ b/maloja/apis/native_v1.py @@ -37,6 +37,28 @@ api.__apipath__ = "mlj_1" + +errors = { + database.MissingScrobbleParameters: lambda e: (400,{ + "status":"failure", + "error":{ + 'type':'missing_scrobble_data', + 'value':e.params, + 'desc':"A scrobble requires these parameters." + } + }), + Exception: lambda e: (500,{ + "status":"failure", + "error":{ + 'type':'unknown_error', + 'value':e.__repr__(), + 'desc':"The server has encountered an exception." + } + }) +} + + + def add_common_args_to_docstring(filterkeys=False,limitkeys=False,delimitkeys=False,amountkeys=False): def decorator(func): timeformats = "Possible formats include '2022', '2022/08', '2022/08/01', '2022/W42', 'today', 'thismonth', 'monday', 'august'" @@ -84,11 +106,16 @@ def test_server(key=None): response.set_header("Access-Control-Allow-Origin","*") if key is not None and not apikeystore.check_key(key): response.status = 403 - return {"status":"error","error":"Wrong API key"} + return { + "status":"error", + "error":"Wrong API key" + } else: response.status = 200 - return {"status":"ok"} + return { + "status":"ok" + } @api.get("serverinfo") @@ -131,7 +158,9 @@ def get_scrobbles_external(**keys): result = result[offset:] if k_amount.get('perpage') is not math.inf: result = result[:k_amount.get('perpage')] - return {"list":result} + return { + "list":result + } @api.get("numscrobbles") @@ -146,7 +175,10 @@ def get_scrobbles_num_external(**keys): ckeys = {**k_filter, **k_time, **k_amount} result = database.get_scrobbles_num(**ckeys) - return {"amount":result} + + return { + "amount":result + } @@ -162,7 +194,10 @@ def get_tracks_external(**keys): ckeys = {**k_filter} result = database.get_tracks(**ckeys) - return {"list":result} + + return { + "list":result + } @@ -174,7 +209,10 @@ def get_artists_external(): :return: list (List) :rtype: Dictionary""" result = database.get_artists() - return {"list":result} + + return { + "list":result + } @@ -191,7 +229,10 @@ def get_charts_artists_external(**keys): ckeys = {**k_time} result = database.get_charts_artists(**ckeys) - return {"list":result} + + return { + "list":result + } @@ -206,7 +247,10 @@ def get_charts_tracks_external(**keys): ckeys = {**k_filter, **k_time} result = database.get_charts_tracks(**ckeys) - return {"list":result} + + return { + "list":result + } @@ -222,7 +266,10 @@ def get_pulse_external(**keys): ckeys = {**k_filter, **k_time, **k_internal, **k_amount} results = database.get_pulse(**ckeys) - return {"list":results} + + return { + "list":results + } @@ -238,7 +285,10 @@ def get_performance_external(**keys): ckeys = {**k_filter, **k_time, **k_internal, **k_amount} results = database.get_performance(**ckeys) - return {"list":results} + + return { + "list":results + } @@ -254,7 +304,10 @@ def get_top_artists_external(**keys): ckeys = {**k_time, **k_internal} results = database.get_top_artists(**ckeys) - return {"list":results} + + return { + "list":results + } @@ -272,7 +325,10 @@ def get_top_tracks_external(**keys): # IMPLEMENT THIS FOR TOP TRACKS OF ARTIST AS WELL? results = database.get_top_tracks(**ckeys) - return {"list":results} + + return { + "list":results + } @@ -325,7 +381,7 @@ def post_scrobble( """Submit a new scrobble. :param string artist: Artist. Can be submitted multiple times as query argument for multiple artists. - :param list artists: List of artists. Overwritten by artist parameter. + :param list artists: List of artists. :param string title: Title of the track. :param string album: Name of the album. Optional. :param list albumartists: Album artists. Optional. @@ -339,7 +395,7 @@ def post_scrobble( """ rawscrobble = { - 'track_artists':artist if artist is not None else artists, + 'track_artists':(artist or []) + artists, 'track_title':title, 'album_name':album, 'album_artists':albumartists, @@ -351,15 +407,15 @@ def post_scrobble( # for logging purposes, don't pass values that we didn't actually supply rawscrobble = {k:rawscrobble[k] for k in rawscrobble if rawscrobble[k]} - result = database.incoming_scrobble( - rawscrobble, - client='browser' if auth_result.get('doreah_native_auth_check') else auth_result.get('client'), - api='native/v1', - fix=(nofix is None) - ) + try: + result = database.incoming_scrobble( + rawscrobble, + client='browser' if auth_result.get('doreah_native_auth_check') else auth_result.get('client'), + api='native/v1', + fix=(nofix is None) + ) - if result: - response = { + responsedict = { 'status': 'success', 'track': { 'artists':result['track']['artists'], @@ -367,14 +423,24 @@ def post_scrobble( } } if extra_kwargs: - response['warnings'] = [ - {'type':'invalid_keyword_ignored','value':k} + responsedict['warnings'] = [ + {'type':'invalid_keyword_ignored','value':k, + 'desc':"This key was not recognized by the server and has been discarded."} for k in extra_kwargs ] - else: - response = {"status":"failure"} + if artist and artists: + responsedict['warnings'] = [ + {'type':'mixed_schema','value':['artist','artists'], + 'desc':"These two fields are meant as alternative methods to submit information. Use of both is discouraged, but works at the moment."} + ] + return responsedict + except Exception as e: + for etype in errors: + if isinstance(e,etype): + errorhandling = errors[etype](e) + response.status = errorhandling[0] + return errorhandling[1] - return response diff --git a/maloja/database/__init__.py b/maloja/database/__init__.py index f37caa4..d83c8f3 100644 --- a/maloja/database/__init__.py +++ b/maloja/database/__init__.py @@ -51,6 +51,11 @@ class DatabaseNotBuilt(HTTPError): ) +class MissingScrobbleParameters(Exception): + def __init__(self,params=[]): + self.params = params + + def waitfordb(func): def newfunc(*args,**kwargs): if not dbstatus['healthy']: raise DatabaseNotBuilt() @@ -86,10 +91,14 @@ cla = CleanerAgent() def incoming_scrobble(rawscrobble,fix=True,client=None,api=None,dbconn=None): - if (not "track_artists" in rawscrobble) or (len(rawscrobble['track_artists']) == 0) or (not "track_title" in rawscrobble): + missing = [] + for necessary_arg in ["track_artists","track_title"]: + if not necessary_arg in rawscrobble or len(rawscrobble[necessary_arg]) == 0: + missing.append(necessary_arg) + if len(missing) > 0: log(f"Invalid Scrobble [Client: {client} | API: {api}]: {rawscrobble} ",color='red') - #return {"status":"failure"} - return False + raise MissingScrobbleParameters(missing) + log(f"Incoming scrobble [Client: {client} | API: {api}]: {rawscrobble}")