From e6bb844ff9871cc8f8aa3926b92d5e096b126591 Mon Sep 17 00:00:00 2001 From: krateng Date: Thu, 14 Apr 2022 16:14:31 +0200 Subject: [PATCH 01/10] Added some docstrings to native API endpoints, GH-114 --- maloja/apis/native_v1.py | 86 ++++++++++++++++++++++++++++++++-------- 1 file changed, 70 insertions(+), 16 deletions(-) diff --git a/maloja/apis/native_v1.py b/maloja/apis/native_v1.py index 5bab8c0..2cdb0f9 100644 --- a/maloja/apis/native_v1.py +++ b/maloja/apis/native_v1.py @@ -37,6 +37,36 @@ api.__apipath__ = "mlj_1" +def add_common_args_to_docstring(filterkeys=False,limitkeys=False,delimitkeys=False,amountkeys=False): + def decorator(func): + if filterkeys: + func.__doc__ += """ + :param string title: Track title + :param string artist: Track artist + :param bool associated: Whether to include associated artists. + """ + if limitkeys: + func.__doc__ += """ + :param string from: Start of the desired time range. Can also be called since or start. + :param string until: End of the desired range. Can also be called to or end. + :param string in: Desired range. Can also be called within or during. + """ + if delimitkeys: + func.__doc__ += """ + :param string step: Step, e.g. month or week. + :param int stepn: Number of base type units per step + :param int trail: How many preceding steps should be factored in. + :param bool cumulative: Instead of a fixed trail length, use all history up to this point. + """ + if amountkeys: + func.__doc__ += """ + :param int page: Page to show + :param int perpage: Entries per page. + :param int max: Legacy. Show first page with this many entries. + """ + return func + return decorator + @api.get("test") @@ -59,6 +89,8 @@ def test_server(key=None): @api.get("serverinfo") def server_info(): + """Returns basic information about the server. + """ response.set_header("Access-Control-Allow-Origin","*") @@ -76,7 +108,10 @@ def server_info(): @api.get("scrobbles") +@add_common_args_to_docstring(filterkeys=True,limitkeys=True,amountkeys=True) def get_scrobbles_external(**keys): + """Returns a list of scrobbles. + """ k_filter, k_time, _, k_amount, _ = uri_to_internal(keys,api=True) ckeys = {**k_filter, **k_time, **k_amount} @@ -89,19 +124,11 @@ def get_scrobbles_external(**keys): return {"list":result} -# info for comparison -@api.get("info") -def info_external(**keys): - - response.set_header("Access-Control-Allow-Origin","*") - response.set_header("Content-Type","application/json") - - return info() - - - @api.get("numscrobbles") +@add_common_args_to_docstring(filterkeys=True,limitkeys=True,amountkeys=True) def get_scrobbles_num_external(**keys): + """Returns amount of scrobbles. + """ k_filter, k_time, _, k_amount, _ = uri_to_internal(keys) ckeys = {**k_filter, **k_time, **k_amount} @@ -111,7 +138,10 @@ def get_scrobbles_num_external(**keys): @api.get("tracks") +@add_common_args_to_docstring(filterkeys=True) def get_tracks_external(**keys): + """Returns all tracks of an artist. + """ k_filter, _, _, _, _ = uri_to_internal(keys,forceArtist=True) ckeys = {**k_filter} @@ -121,7 +151,9 @@ def get_tracks_external(**keys): @api.get("artists") +@add_common_args_to_docstring() def get_artists_external(): + """""" result = database.get_artists() return {"list":result} @@ -130,7 +162,9 @@ def get_artists_external(): @api.get("charts/artists") +@add_common_args_to_docstring(limitkeys=True) def get_charts_artists_external(**keys): + """""" _, k_time, _, _, _ = uri_to_internal(keys) ckeys = {**k_time} @@ -140,7 +174,9 @@ def get_charts_artists_external(**keys): @api.get("charts/tracks") +@add_common_args_to_docstring(filterkeys=True,limitkeys=True) def get_charts_tracks_external(**keys): + """""" k_filter, k_time, _, _, _ = uri_to_internal(keys,forceArtist=True) ckeys = {**k_filter, **k_time} @@ -151,7 +187,9 @@ def get_charts_tracks_external(**keys): @api.get("pulse") +@add_common_args_to_docstring(filterkeys=True,limitkeys=True,delimitkeys=True,amountkeys=True) def get_pulse_external(**keys): + """""" k_filter, k_time, k_internal, k_amount, _ = uri_to_internal(keys) ckeys = {**k_filter, **k_time, **k_internal, **k_amount} @@ -162,7 +200,9 @@ def get_pulse_external(**keys): @api.get("performance") +@add_common_args_to_docstring(filterkeys=True,limitkeys=True,delimitkeys=True,amountkeys=True) def get_performance_external(**keys): + """""" k_filter, k_time, k_internal, k_amount, _ = uri_to_internal(keys) ckeys = {**k_filter, **k_time, **k_internal, **k_amount} @@ -173,7 +213,9 @@ def get_performance_external(**keys): @api.get("top/artists") +@add_common_args_to_docstring(limitkeys=True,delimitkeys=True) def get_top_artists_external(**keys): + """""" _, k_time, k_internal, _, _ = uri_to_internal(keys) ckeys = {**k_time, **k_internal} @@ -184,7 +226,9 @@ def get_top_artists_external(**keys): @api.get("top/tracks") +@add_common_args_to_docstring(limitkeys=True,delimitkeys=True) def get_top_tracks_external(**keys): + """""" _, k_time, k_internal, _, _ = uri_to_internal(keys) ckeys = {**k_time, **k_internal} @@ -197,7 +241,9 @@ def get_top_tracks_external(**keys): @api.get("artistinfo") +@add_common_args_to_docstring(filterkeys=True) def artist_info_external(**keys): + """""" k_filter, _, _, _, _ = uri_to_internal(keys,forceArtist=True) ckeys = {**k_filter} @@ -206,7 +252,9 @@ def artist_info_external(**keys): @api.get("trackinfo") +@add_common_args_to_docstring(filterkeys=True) def track_info_external(artist:Multi[str],**keys): + """""" # transform into a multidict so we can use our nomral uri_to_internal function keys = FormsDict(keys) for a in artist: @@ -217,11 +265,6 @@ def track_info_external(artist:Multi[str],**keys): return database.track_info(**ckeys) -@api.get("compare") -def compare_external(**keys): - return database.compare(keys["remote"]) - - @api.post("newscrobble") @authenticated_function(alternate=api_key_correct,api=True,pass_auth_result_as='auth_result') def post_scrobble(artist:Multi=None,auth_result=None,**keys): @@ -275,6 +318,7 @@ def post_scrobble(artist:Multi=None,auth_result=None,**keys): @api.post("importrules") @authenticated_api def import_rulemodule(**keys): + """""" filename = keys.get("filename") remove = keys.get("remove") is not None validchars = "-_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" @@ -292,6 +336,7 @@ def import_rulemodule(**keys): @api.post("rebuild") @authenticated_api def rebuild(**keys): + """""" log("Database rebuild initiated!") database.sync() dbstatus['rebuildinprogress'] = True @@ -307,6 +352,7 @@ def rebuild(**keys): @api.get("search") def search(**keys): + """""" query = keys.get("query") max_ = keys.get("max") if max_ is not None: max_ = int(max_) @@ -345,6 +391,7 @@ def search(**keys): @api.post("addpicture") @authenticated_api def add_picture(b64,artist:Multi=[],title=None): + """""" keys = FormsDict() for a in artist: keys.append("artist",a) @@ -357,6 +404,7 @@ def add_picture(b64,artist:Multi=[],title=None): @api.post("newrule") @authenticated_api def newrule(**keys): + """""" pass # TODO after implementing new rule system #tsv.add_entry(data_dir['rules']("webmade.tsv"),[k for k in keys]) @@ -366,22 +414,26 @@ def newrule(**keys): @api.post("settings") @authenticated_api def set_settings(**keys): + """""" malojaconfig.update(keys) @api.post("apikeys") @authenticated_api def set_apikeys(**keys): + """""" apikeystore.update(keys) @api.post("import") @authenticated_api def import_scrobbles(identifier): + """""" from ..thirdparty import import_scrobbles import_scrobbles(identifier) @api.get("backup") @authenticated_api def get_backup(**keys): + """""" from ..proccontrol.tasks.backup import backup import tempfile @@ -393,6 +445,7 @@ def get_backup(**keys): @api.get("export") @authenticated_api def get_export(**keys): + """""" from ..proccontrol.tasks.export import export import tempfile @@ -405,4 +458,5 @@ def get_export(**keys): @api.post("delete_scrobble") @authenticated_api def delete_scrobble(timestamp): + """""" database.remove_scrobble(timestamp) From 29f722e3d3f607e2a533a9441e52db7125fcc6e9 Mon Sep 17 00:00:00 2001 From: krateng Date: Thu, 14 Apr 2022 17:00:45 +0200 Subject: [PATCH 02/10] Added time format info to docstrings --- maloja/apis/native_v1.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/maloja/apis/native_v1.py b/maloja/apis/native_v1.py index 2cdb0f9..f54a5d8 100644 --- a/maloja/apis/native_v1.py +++ b/maloja/apis/native_v1.py @@ -39,17 +39,19 @@ api.__apipath__ = "mlj_1" 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'" + if filterkeys: - func.__doc__ += """ + func.__doc__ += f""" :param string title: Track title :param string artist: Track artist :param bool associated: Whether to include associated artists. """ if limitkeys: - func.__doc__ += """ - :param string from: Start of the desired time range. Can also be called since or start. - :param string until: End of the desired range. Can also be called to or end. - :param string in: Desired range. Can also be called within or during. + func.__doc__ += f""" + :param string from: Start of the desired time range. Can also be called since or start. {timeformats} + :param string until: End of the desired range. Can also be called to or end. {timeformats} + :param string in: Desired range. Can also be called within or during. {timeformats} """ if delimitkeys: func.__doc__ += """ From 65fd57dcebb5d14a5315175c54f9004ff1cb1f72 Mon Sep 17 00:00:00 2001 From: krateng Date: Thu, 14 Apr 2022 17:29:10 +0200 Subject: [PATCH 03/10] Explicit arguments for native scrobble endpoint --- maloja/apis/native_v1.py | 32 +++++++++++++++++++++----------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/maloja/apis/native_v1.py b/maloja/apis/native_v1.py index f54a5d8..f709a83 100644 --- a/maloja/apis/native_v1.py +++ b/maloja/apis/native_v1.py @@ -269,14 +269,24 @@ def track_info_external(artist:Multi[str],**keys): @api.post("newscrobble") @authenticated_function(alternate=api_key_correct,api=True,pass_auth_result_as='auth_result') -def post_scrobble(artist:Multi=None,auth_result=None,**keys): +def post_scrobble( + artist:Multi=None, + artists:list=[], + title:str="", + album:str=None, + albumartists:list=[], + duration:int=None, + length:int=None, + time:int=None, + nofix:bool=False, + auth_result=None): """Submit a new scrobble. :param string artist: Artist. Can be submitted multiple times as query argument for multiple artists. - :param string artists: List of artists. Overwritten by artist parameter. + :param list artists: List of artists. Overwritten by artist parameter. :param string title: Title of the track. :param string album: Name of the album. Optional. - :param string albumartists: Album artists. Optional. + :param list albumartists: Album artists. Optional. :param int duration: Actual listened duration of the scrobble in seconds. Optional. :param int length: Total length of the track in seconds. Optional. :param int time: UNIX timestamp of the scrobble. Optional, not needed if scrobble is at time of request. @@ -284,13 +294,13 @@ def post_scrobble(artist:Multi=None,auth_result=None,**keys): """ rawscrobble = { - 'track_artists':artist if artist is not None else keys.get("artists"), - 'track_title':keys.get('title'), - 'album_name':keys.get('album'), - 'album_artists':keys.get('albumartists'), - 'scrobble_duration':keys.get('duration'), - 'track_length':keys.get('length'), - 'scrobble_time':int(keys.get('time')) if (keys.get('time') is not None) else None + 'track_artists':artist if artist is not None else artists, + 'track_title':title, + 'album_name':album, + 'album_artists':albumartists, + 'scrobble_duration':duration, + 'track_length':length, + 'scrobble_time':time } # for logging purposes, don't pass values that we didn't actually supply @@ -300,7 +310,7 @@ def post_scrobble(artist:Multi=None,auth_result=None,**keys): rawscrobble, client='browser' if auth_result.get('doreah_native_auth_check') else auth_result.get('client'), api='native/v1', - fix=(keys.get("nofix") is None) + fix=not nofix ) if result: From 7c1d45f4af0f9a3a22d159e753187cdd208ef625 Mon Sep 17 00:00:00 2001 From: krateng Date: Thu, 14 Apr 2022 17:32:27 +0200 Subject: [PATCH 04/10] Fixed mistake in API testing --- dev/testing/Maloja.postman_collection.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dev/testing/Maloja.postman_collection.json b/dev/testing/Maloja.postman_collection.json index 37edaa1..7ca23a4 100644 --- a/dev/testing/Maloja.postman_collection.json +++ b/dev/testing/Maloja.postman_collection.json @@ -249,7 +249,7 @@ ], "body": { "mode": "raw", - "raw": "{\n \"key\": \"{{api_key}}\",\n \"artist\": \"{{data.artist1}}\",\n \"title\": \"{{data.artist2}}\"\n}" + "raw": "{\n \"key\": \"{{api_key}}\",\n \"artist\": \"{{data.artist1}}\",\n \"title\": \"{{data.title1}}\"\n}" }, "url": { "raw": "{{url}}/api/newscrobble", @@ -904,4 +904,4 @@ "value": "" } ] -} \ No newline at end of file +} From 16d8ed05751f8179ce3f3bdc96a786ee85a53dfd Mon Sep 17 00:00:00 2001 From: krateng Date: Thu, 14 Apr 2022 17:44:52 +0200 Subject: [PATCH 05/10] Fixed nofix argument for scrobbling --- maloja/apis/native_v1.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/maloja/apis/native_v1.py b/maloja/apis/native_v1.py index f709a83..2a39b39 100644 --- a/maloja/apis/native_v1.py +++ b/maloja/apis/native_v1.py @@ -278,7 +278,7 @@ def post_scrobble( duration:int=None, length:int=None, time:int=None, - nofix:bool=False, + nofix=None, auth_result=None): """Submit a new scrobble. @@ -290,7 +290,7 @@ def post_scrobble( :param int duration: Actual listened duration of the scrobble in seconds. Optional. :param int length: Total length of the track in seconds. Optional. :param int time: UNIX timestamp of the scrobble. Optional, not needed if scrobble is at time of request. - :param boolean nofix: Skip server-side metadata parsing. Optional. + :param flag nofix: Skip server-side metadata parsing. Optional. """ rawscrobble = { @@ -310,7 +310,7 @@ def post_scrobble( rawscrobble, client='browser' if auth_result.get('doreah_native_auth_check') else auth_result.get('client'), api='native/v1', - fix=not nofix + fix=(nofix is None) ) if result: From 62a654bfbfa84029516ddc4558542e5f8527cb63 Mon Sep 17 00:00:00 2001 From: krateng Date: Thu, 14 Apr 2022 19:34:42 +0200 Subject: [PATCH 06/10] Added more docstrings --- maloja/apis/native_v1.py | 44 ++++++++++++++++++++-------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/maloja/apis/native_v1.py b/maloja/apis/native_v1.py index 2a39b39..8a612ea 100644 --- a/maloja/apis/native_v1.py +++ b/maloja/apis/native_v1.py @@ -44,7 +44,7 @@ def add_common_args_to_docstring(filterkeys=False,limitkeys=False,delimitkeys=Fa if filterkeys: func.__doc__ += f""" :param string title: Track title - :param string artist: Track artist + :param string artist: Track artist. Can be specified multiple times. :param bool associated: Whether to include associated artists. """ if limitkeys: @@ -142,7 +142,7 @@ def get_scrobbles_num_external(**keys): @api.get("tracks") @add_common_args_to_docstring(filterkeys=True) def get_tracks_external(**keys): - """Returns all tracks of an artist. + """Returns all tracks (optionally of an artist). """ k_filter, _, _, _, _ = uri_to_internal(keys,forceArtist=True) ckeys = {**k_filter} @@ -155,7 +155,7 @@ def get_tracks_external(**keys): @api.get("artists") @add_common_args_to_docstring() def get_artists_external(): - """""" + """Returns all artists.""" result = database.get_artists() return {"list":result} @@ -166,7 +166,7 @@ def get_artists_external(): @api.get("charts/artists") @add_common_args_to_docstring(limitkeys=True) def get_charts_artists_external(**keys): - """""" + """Returns artist charts""" _, k_time, _, _, _ = uri_to_internal(keys) ckeys = {**k_time} @@ -178,7 +178,7 @@ def get_charts_artists_external(**keys): @api.get("charts/tracks") @add_common_args_to_docstring(filterkeys=True,limitkeys=True) def get_charts_tracks_external(**keys): - """""" + """Returns track charts""" k_filter, k_time, _, _, _ = uri_to_internal(keys,forceArtist=True) ckeys = {**k_filter, **k_time} @@ -191,7 +191,7 @@ def get_charts_tracks_external(**keys): @api.get("pulse") @add_common_args_to_docstring(filterkeys=True,limitkeys=True,delimitkeys=True,amountkeys=True) def get_pulse_external(**keys): - """""" + """Returns amounts of scrobbles in specified time frames""" k_filter, k_time, k_internal, k_amount, _ = uri_to_internal(keys) ckeys = {**k_filter, **k_time, **k_internal, **k_amount} @@ -204,7 +204,7 @@ def get_pulse_external(**keys): @api.get("performance") @add_common_args_to_docstring(filterkeys=True,limitkeys=True,delimitkeys=True,amountkeys=True) def get_performance_external(**keys): - """""" + """Returns artist's or track's rank in specified time frames""" k_filter, k_time, k_internal, k_amount, _ = uri_to_internal(keys) ckeys = {**k_filter, **k_time, **k_internal, **k_amount} @@ -217,7 +217,7 @@ def get_performance_external(**keys): @api.get("top/artists") @add_common_args_to_docstring(limitkeys=True,delimitkeys=True) def get_top_artists_external(**keys): - """""" + """Returns respective number 1 artists in specified time frames""" _, k_time, k_internal, _, _ = uri_to_internal(keys) ckeys = {**k_time, **k_internal} @@ -230,7 +230,7 @@ def get_top_artists_external(**keys): @api.get("top/tracks") @add_common_args_to_docstring(limitkeys=True,delimitkeys=True) def get_top_tracks_external(**keys): - """""" + """Returns respective number 1 tracks in specified time frames""" _, k_time, k_internal, _, _ = uri_to_internal(keys) ckeys = {**k_time, **k_internal} @@ -245,7 +245,7 @@ def get_top_tracks_external(**keys): @api.get("artistinfo") @add_common_args_to_docstring(filterkeys=True) def artist_info_external(**keys): - """""" + """Returns information about an artist""" k_filter, _, _, _, _ = uri_to_internal(keys,forceArtist=True) ckeys = {**k_filter} @@ -256,7 +256,7 @@ def artist_info_external(**keys): @api.get("trackinfo") @add_common_args_to_docstring(filterkeys=True) def track_info_external(artist:Multi[str],**keys): - """""" + """Returns information about a track""" # transform into a multidict so we can use our nomral uri_to_internal function keys = FormsDict(keys) for a in artist: @@ -330,7 +330,7 @@ def post_scrobble( @api.post("importrules") @authenticated_api def import_rulemodule(**keys): - """""" + """Internal Use Only""" filename = keys.get("filename") remove = keys.get("remove") is not None validchars = "-_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" @@ -348,7 +348,7 @@ def import_rulemodule(**keys): @api.post("rebuild") @authenticated_api def rebuild(**keys): - """""" + """Internal Use Only""" log("Database rebuild initiated!") database.sync() dbstatus['rebuildinprogress'] = True @@ -364,7 +364,7 @@ def rebuild(**keys): @api.get("search") def search(**keys): - """""" + """Internal Use Only""" query = keys.get("query") max_ = keys.get("max") if max_ is not None: max_ = int(max_) @@ -403,7 +403,7 @@ def search(**keys): @api.post("addpicture") @authenticated_api def add_picture(b64,artist:Multi=[],title=None): - """""" + """Internal Use Only""" keys = FormsDict() for a in artist: keys.append("artist",a) @@ -416,7 +416,7 @@ def add_picture(b64,artist:Multi=[],title=None): @api.post("newrule") @authenticated_api def newrule(**keys): - """""" + """Internal Use Only""" pass # TODO after implementing new rule system #tsv.add_entry(data_dir['rules']("webmade.tsv"),[k for k in keys]) @@ -426,26 +426,26 @@ def newrule(**keys): @api.post("settings") @authenticated_api def set_settings(**keys): - """""" + """Internal Use Only""" malojaconfig.update(keys) @api.post("apikeys") @authenticated_api def set_apikeys(**keys): - """""" + """Internal Use Only""" apikeystore.update(keys) @api.post("import") @authenticated_api def import_scrobbles(identifier): - """""" + """Internal Use Only""" from ..thirdparty import import_scrobbles import_scrobbles(identifier) @api.get("backup") @authenticated_api def get_backup(**keys): - """""" + """Internal Use Only""" from ..proccontrol.tasks.backup import backup import tempfile @@ -457,7 +457,7 @@ def get_backup(**keys): @api.get("export") @authenticated_api def get_export(**keys): - """""" + """Internal Use Only""" from ..proccontrol.tasks.export import export import tempfile @@ -470,5 +470,5 @@ def get_export(**keys): @api.post("delete_scrobble") @authenticated_api def delete_scrobble(timestamp): - """""" + """Internal Use Only""" database.remove_scrobble(timestamp) From 1a977d9c0c5702a29b35f4442f7116490d2557a3 Mon Sep 17 00:00:00 2001 From: krateng Date: Thu, 14 Apr 2022 19:36:50 +0200 Subject: [PATCH 07/10] Moved all native API endpoints to new auth handling --- maloja/apis/native_v1.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/maloja/apis/native_v1.py b/maloja/apis/native_v1.py index 8a612ea..810c651 100644 --- a/maloja/apis/native_v1.py +++ b/maloja/apis/native_v1.py @@ -328,7 +328,7 @@ def post_scrobble( @api.post("importrules") -@authenticated_api +@authenticated_function(api=True) def import_rulemodule(**keys): """Internal Use Only""" filename = keys.get("filename") @@ -346,7 +346,7 @@ def import_rulemodule(**keys): @api.post("rebuild") -@authenticated_api +@authenticated_function(api=True) def rebuild(**keys): """Internal Use Only""" log("Database rebuild initiated!") @@ -401,7 +401,7 @@ def search(**keys): @api.post("addpicture") -@authenticated_api +@authenticated_function(api=True) def add_picture(b64,artist:Multi=[],title=None): """Internal Use Only""" keys = FormsDict() @@ -414,7 +414,7 @@ def add_picture(b64,artist:Multi=[],title=None): @api.post("newrule") -@authenticated_api +@authenticated_function(api=True) def newrule(**keys): """Internal Use Only""" pass @@ -424,26 +424,26 @@ def newrule(**keys): @api.post("settings") -@authenticated_api +@authenticated_function(api=True) def set_settings(**keys): """Internal Use Only""" malojaconfig.update(keys) @api.post("apikeys") -@authenticated_api +@authenticated_function(api=True) def set_apikeys(**keys): """Internal Use Only""" apikeystore.update(keys) @api.post("import") -@authenticated_api +@authenticated_function(api=True) def import_scrobbles(identifier): """Internal Use Only""" from ..thirdparty import import_scrobbles import_scrobbles(identifier) @api.get("backup") -@authenticated_api +@authenticated_function(api=True) def get_backup(**keys): """Internal Use Only""" from ..proccontrol.tasks.backup import backup @@ -455,7 +455,7 @@ def get_backup(**keys): return static_file(os.path.basename(archivefile),root=tmpfolder) @api.get("export") -@authenticated_api +@authenticated_function(api=True) def get_export(**keys): """Internal Use Only""" from ..proccontrol.tasks.export import export @@ -468,7 +468,7 @@ def get_export(**keys): @api.post("delete_scrobble") -@authenticated_api +@authenticated_function(api=True) def delete_scrobble(timestamp): """Internal Use Only""" database.remove_scrobble(timestamp) From c91cae9de1f2eda09f8b7fda84ffcbab898c4b31 Mon Sep 17 00:00:00 2001 From: krateng Date: Thu, 14 Apr 2022 20:02:02 +0200 Subject: [PATCH 08/10] Added info about API endpoint return values, fix GH-114 --- maloja/apis/native_v1.py | 64 +++++++++++++++++++++++++++++++++------- 1 file changed, 54 insertions(+), 10 deletions(-) diff --git a/maloja/apis/native_v1.py b/maloja/apis/native_v1.py index 810c651..684ef06 100644 --- a/maloja/apis/native_v1.py +++ b/maloja/apis/native_v1.py @@ -78,11 +78,13 @@ def test_server(key=None): always respond with 200. :param string key: An API key to be tested. Optional. + :return: status (String), error (String) + :rtype: Dictionary """ response.set_header("Access-Control-Allow-Origin","*") if key is not None and not apikeystore.check_key(key): response.status = 403 - return {"error":"Wrong API key"} + return {"status":"error","error":"Wrong API key"} else: response.status = 200 @@ -92,6 +94,9 @@ def test_server(key=None): @api.get("serverinfo") def server_info(): """Returns basic information about the server. + + :return: name (String), version (Tuple), versionstring (String), db_status (String). Additional keys can be added at any point, but will not be removed within API version. + :rtype: Dictionary """ @@ -113,6 +118,9 @@ def server_info(): @add_common_args_to_docstring(filterkeys=True,limitkeys=True,amountkeys=True) def get_scrobbles_external(**keys): """Returns a list of scrobbles. + + :return: list (List) + :rtype: Dictionary """ k_filter, k_time, _, k_amount, _ = uri_to_internal(keys,api=True) ckeys = {**k_filter, **k_time, **k_amount} @@ -130,6 +138,9 @@ def get_scrobbles_external(**keys): @add_common_args_to_docstring(filterkeys=True,limitkeys=True,amountkeys=True) def get_scrobbles_num_external(**keys): """Returns amount of scrobbles. + + :return: amount (Integer) + :rtype: Dictionary """ k_filter, k_time, _, k_amount, _ = uri_to_internal(keys) ckeys = {**k_filter, **k_time, **k_amount} @@ -143,6 +154,9 @@ def get_scrobbles_num_external(**keys): @add_common_args_to_docstring(filterkeys=True) def get_tracks_external(**keys): """Returns all tracks (optionally of an artist). + + :return: list (List) + :rtype: Dictionary """ k_filter, _, _, _, _ = uri_to_internal(keys,forceArtist=True) ckeys = {**k_filter} @@ -155,7 +169,10 @@ def get_tracks_external(**keys): @api.get("artists") @add_common_args_to_docstring() def get_artists_external(): - """Returns all artists.""" + """Returns all artists. + + :return: list (List) + :rtype: Dictionary""" result = database.get_artists() return {"list":result} @@ -166,7 +183,10 @@ def get_artists_external(): @api.get("charts/artists") @add_common_args_to_docstring(limitkeys=True) def get_charts_artists_external(**keys): - """Returns artist charts""" + """Returns artist charts + + :return: list (List) + :rtype: Dictionary""" _, k_time, _, _, _ = uri_to_internal(keys) ckeys = {**k_time} @@ -178,7 +198,10 @@ def get_charts_artists_external(**keys): @api.get("charts/tracks") @add_common_args_to_docstring(filterkeys=True,limitkeys=True) def get_charts_tracks_external(**keys): - """Returns track charts""" + """Returns track charts + + :return: list (List) + :rtype: Dictionary""" k_filter, k_time, _, _, _ = uri_to_internal(keys,forceArtist=True) ckeys = {**k_filter, **k_time} @@ -191,7 +214,10 @@ def get_charts_tracks_external(**keys): @api.get("pulse") @add_common_args_to_docstring(filterkeys=True,limitkeys=True,delimitkeys=True,amountkeys=True) def get_pulse_external(**keys): - """Returns amounts of scrobbles in specified time frames""" + """Returns amounts of scrobbles in specified time frames + + :return: list (List) + :rtype: Dictionary""" k_filter, k_time, k_internal, k_amount, _ = uri_to_internal(keys) ckeys = {**k_filter, **k_time, **k_internal, **k_amount} @@ -204,7 +230,10 @@ def get_pulse_external(**keys): @api.get("performance") @add_common_args_to_docstring(filterkeys=True,limitkeys=True,delimitkeys=True,amountkeys=True) def get_performance_external(**keys): - """Returns artist's or track's rank in specified time frames""" + """Returns artist's or track's rank in specified time frames + + :return: list (List) + :rtype: Dictionary""" k_filter, k_time, k_internal, k_amount, _ = uri_to_internal(keys) ckeys = {**k_filter, **k_time, **k_internal, **k_amount} @@ -217,7 +246,10 @@ def get_performance_external(**keys): @api.get("top/artists") @add_common_args_to_docstring(limitkeys=True,delimitkeys=True) def get_top_artists_external(**keys): - """Returns respective number 1 artists in specified time frames""" + """Returns respective number 1 artists in specified time frames + + :return: list (List) + :rtype: Dictionary""" _, k_time, k_internal, _, _ = uri_to_internal(keys) ckeys = {**k_time, **k_internal} @@ -230,7 +262,10 @@ def get_top_artists_external(**keys): @api.get("top/tracks") @add_common_args_to_docstring(limitkeys=True,delimitkeys=True) def get_top_tracks_external(**keys): - """Returns respective number 1 tracks in specified time frames""" + """Returns respective number 1 tracks in specified time frames + + :return: list (List) + :rtype: Dictionary""" _, k_time, k_internal, _, _ = uri_to_internal(keys) ckeys = {**k_time, **k_internal} @@ -245,7 +280,10 @@ def get_top_tracks_external(**keys): @api.get("artistinfo") @add_common_args_to_docstring(filterkeys=True) def artist_info_external(**keys): - """Returns information about an artist""" + """Returns information about an artist + + :return: artist (String), scrobbles (Integer), position (Integer), associated (List), medals (Mapping), topweeks (Integer) + :rtype: Dictionary""" k_filter, _, _, _, _ = uri_to_internal(keys,forceArtist=True) ckeys = {**k_filter} @@ -256,7 +294,10 @@ def artist_info_external(**keys): @api.get("trackinfo") @add_common_args_to_docstring(filterkeys=True) def track_info_external(artist:Multi[str],**keys): - """Returns information about a track""" + """Returns information about a track + + :return: track (Mapping), scrobbles (Integer), position (Integer), medals (Mapping), certification (String), topweeks (Integer) + :rtype: Dictionary""" # transform into a multidict so we can use our nomral uri_to_internal function keys = FormsDict(keys) for a in artist: @@ -291,6 +332,9 @@ def post_scrobble( :param int length: Total length of the track in seconds. Optional. :param int time: UNIX timestamp of the scrobble. Optional, not needed if scrobble is at time of request. :param flag nofix: Skip server-side metadata parsing. Optional. + + :return: status (String), track (Mapping) + :rtype: Dictionary """ rawscrobble = { From 1d9247fc724d7410b6e50d2cbfaa8f375d5e70af Mon Sep 17 00:00:00 2001 From: krateng Date: Thu, 14 Apr 2022 20:09:01 +0200 Subject: [PATCH 09/10] Version bump --- maloja/__pkginfo__.py | 2 +- pyproject.toml | 2 +- requirements.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/maloja/__pkginfo__.py b/maloja/__pkginfo__.py index ab4d615..7647a42 100644 --- a/maloja/__pkginfo__.py +++ b/maloja/__pkginfo__.py @@ -4,7 +4,7 @@ # you know what f*ck it # this is hardcoded for now because of that damn project / package name discrepancy # i'll fix it one day -VERSION = "3.0.2" +VERSION = "3.0.3" HOMEPAGE = "https://github.com/krateng/maloja" diff --git a/pyproject.toml b/pyproject.toml index d9600f0..76cacdf 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "malojaserver" -version = "3.0.2" +version = "3.0.3" description = "Self-hosted music scrobble database" readme = "./README.md" requires-python = ">=3.6" diff --git a/requirements.txt b/requirements.txt index 60aceb1..d4f1bf4 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,6 @@ bottle>=0.12.16 waitress>=1.3 -doreah>=1.9.0, <2 +doreah>=1.9.1, <2 nimrodel>=0.8.0 setproctitle>=1.1.10 jinja2>=2.11 From 7f9aa125af30b4864362fce64f7ab146bfe92bc5 Mon Sep 17 00:00:00 2001 From: krateng Date: Thu, 14 Apr 2022 20:49:40 +0200 Subject: [PATCH 10/10] Enabled dual stack web server --- maloja/globalconf.py | 2 +- maloja/server.py | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/maloja/globalconf.py b/maloja/globalconf.py index 938e542..16206a8 100644 --- a/maloja/globalconf.py +++ b/maloja/globalconf.py @@ -142,7 +142,7 @@ malojaconfig = Configuration( "dev_mode":(tp.Boolean(), "Enable developer mode", False), }, "Network":{ - "host":(tp.String(), "Host", "::", "Host for your server - most likely :: for IPv6 or 0.0.0.0 for IPv4"), + "host":(tp.String(), "Host", "*", "Host for your server, e.g. '*' for dual stack, '::' for IPv6 or '0.0.0.0' for IPv4"), "port":(tp.Integer(), "Port", 42010), }, "Technical":{ diff --git a/maloja/server.py b/maloja/server.py index 4796838..63359eb 100644 --- a/maloja/server.py +++ b/maloja/server.py @@ -332,8 +332,9 @@ def run_server(): try: #run(webserver, host=HOST, port=MAIN_PORT, server='waitress') - log(f"Listening on {HOST}:{PORT}") - waitress.serve(webserver, host=HOST, port=PORT, threads=THREADS) + listen = f"{HOST}:{PORT}" + log(f"Listening on {listen}") + waitress.serve(webserver, listen=listen, threads=THREADS) except OSError: log("Error. Is another Maloja process already running?") raise