Merge remote-tracking branch 'origin/master'

This commit is contained in:
krateng 2024-05-05 18:39:26 +02:00
commit c6cf28896c
6 changed files with 75 additions and 24 deletions

View File

@ -135,7 +135,7 @@ def debug():
def print_info(): def print_info():
print_header_info() print_header_info()
print(col['lightblue']("Configuration Directory:"),conf.dir_settings['config']) print(col['lightblue']("Configuration Directory:"),conf.dir_settings['config'])
print(col['lightblue']("Data Directory: "),conf.dir_settings['state']) print(col['lightblue']("State Directory: "),conf.dir_settings['state'])
print(col['lightblue']("Log Directory: "),conf.dir_settings['logs']) print(col['lightblue']("Log Directory: "),conf.dir_settings['logs'])
print(col['lightblue']("Network: "),f"Dual Stack, Port {conf.malojaconfig['port']}" if conf.malojaconfig['host'] == "*" else f"IPv{ip_address(conf.malojaconfig['host']).version}, Port {conf.malojaconfig['port']}") print(col['lightblue']("Network: "),f"Dual Stack, Port {conf.malojaconfig['port']}" if conf.malojaconfig['host'] == "*" else f"IPv{ip_address(conf.malojaconfig['host']).version}, Port {conf.malojaconfig['port']}")
print(col['lightblue']("Timezone: "),f"UTC{conf.malojaconfig['timezone']:+d}") print(col['lightblue']("Timezone: "),f"UTC{conf.malojaconfig['timezone']:+d}")

View File

@ -28,6 +28,15 @@ class Audioscrobbler(APIHandler):
Exception: (500, {"error": 8, "message": "Operation failed"}) Exception: (500, {"error": 8, "message": "Operation failed"})
} }
# xml string escaping: https://stackoverflow.com/a/28703510
def xml_escape(self, str_xml: str):
str_xml = str_xml.replace("&", "&")
str_xml = str_xml.replace("<", "&lt;")
str_xml = str_xml.replace("<", "&lt;")
str_xml = str_xml.replace("\"", "&quot;")
str_xml = str_xml.replace("'", "&apos;")
return str_xml
def get_method(self,pathnodes,keys): def get_method(self,pathnodes,keys):
return keys.get("method") return keys.get("method")
@ -45,12 +54,22 @@ class Audioscrobbler(APIHandler):
token = keys.get("authToken") token = keys.get("authToken")
user = keys.get("username") user = keys.get("username")
password = keys.get("password") password = keys.get("password")
format = keys.get("format") or "xml" # Audioscrobbler 2.0 uses XML by default
# either username and password # either username and password
if user is not None and password is not None: if user is not None and password is not None:
client = apikeystore.check_and_identify_key(password) client = apikeystore.check_and_identify_key(password)
if client: if client:
sessionkey = self.generate_key(client) sessionkey = self.generate_key(client)
if format == "json":
return 200,{"session":{"key":sessionkey}} return 200,{"session":{"key":sessionkey}}
else:
return 200,"""<lfm status="ok">
<session>
<name>%s</name>
<key>%s</key>
<subscriber>0</subscriber>
</session>
</lfm>""" % (self.xml_escape(user), self.xml_escape(sessionkey))
else: else:
raise InvalidAuthException() raise InvalidAuthException()
# or username and token (deprecated by lastfm) # or username and token (deprecated by lastfm)
@ -59,7 +78,16 @@ class Audioscrobbler(APIHandler):
key = apikeystore[client] key = apikeystore[client]
if md5(user + md5(key)) == token: if md5(user + md5(key)) == token:
sessionkey = self.generate_key(client) sessionkey = self.generate_key(client)
if format == "json":
return 200,{"session":{"key":sessionkey}} return 200,{"session":{"key":sessionkey}}
else:
return 200,"""<lfm status="ok">
<session>
<name>%s</name>
<key>%s</key>
<subscriber>0</subscriber>
</session>
</lfm>""" % (self.xml_escape(user), self.xml_escape(sessionkey))
raise InvalidAuthException() raise InvalidAuthException()
else: else:
raise BadAuthException() raise BadAuthException()

View File

@ -15,13 +15,15 @@ class CleanerAgent:
def updateRules(self): def updateRules(self):
rawrules = [] rawrules = []
try:
for f in os.listdir(data_dir["rules"]()): for f in os.listdir(data_dir["rules"]()):
if f.split('.')[-1].lower() != 'tsv': continue if f.split('.')[-1].lower() != 'tsv': continue
filepath = data_dir["rules"](f) filepath = data_dir["rules"](f)
with open(filepath,'r') as filed: with open(filepath,'r') as filed:
reader = csv.reader(filed,delimiter="\t") reader = csv.reader(filed,delimiter="\t")
rawrules += [[col for col in entry if col] for entry in reader if len(entry)>0 and not entry[0].startswith('#')] rawrules += [[col for col in entry if col] for entry in reader if len(entry)>0 and not entry[0].startswith('#')]
except FileNotFoundError:
pass
self.rules_belongtogether = [r[1] for r in rawrules if r[0]=="belongtogether"] self.rules_belongtogether = [r[1] for r in rawrules if r[0]=="belongtogether"]
self.rules_notanartist = [r[1] for r in rawrules if r[0]=="notanartist"] self.rules_notanartist = [r[1] for r in rawrules if r[0]=="notanartist"]

View File

@ -19,12 +19,16 @@ def load_associated_rules():
# load from file # load from file
rawrules = [] rawrules = []
try:
for f in os.listdir(data_dir["rules"]()): for f in os.listdir(data_dir["rules"]()):
if f.split('.')[-1].lower() != 'tsv': continue if f.split('.')[-1].lower() != 'tsv': continue
filepath = data_dir["rules"](f) filepath = data_dir["rules"](f)
with open(filepath,'r') as filed: with open(filepath,'r') as filed:
reader = csv.reader(filed,delimiter="\t") reader = csv.reader(filed,delimiter="\t")
rawrules += [[col for col in entry if col] for entry in reader if len(entry)>0 and not entry[0].startswith('#')] rawrules += [[col for col in entry if col] for entry in reader if len(entry)>0 and not entry[0].startswith('#')]
except FileNotFoundError:
return
rules = [{'source_artist':r[1],'target_artist':r[2]} for r in rawrules if r[0]=="countas"] rules = [{'source_artist':r[1],'target_artist':r[2]} for r in rawrules if r[0]=="countas"]
#for rule in rules: #for rule in rules:

View File

@ -311,6 +311,12 @@ data_directories = {
} }
for identifier,path in data_directories.items(): for identifier,path in data_directories.items():
if path is None:
continue
if malojaconfig.readonly and (path == dir_settings['config'] or path.startswith(dir_settings['config']+'/')):
continue
try: try:
os.makedirs(path,exist_ok=True) os.makedirs(path,exist_ok=True)
if not is_dir_usable(path): raise PermissionError(f"Directory {path} is not usable!") if not is_dir_usable(path): raise PermissionError(f"Directory {path} is not usable!")
@ -321,17 +327,19 @@ for identifier,path in data_directories.items():
print("Cannot use",path,"for cache, finding new folder...") print("Cannot use",path,"for cache, finding new folder...")
data_directories['cache'] = dir_settings['cache'] = malojaconfig['DIRECTORY_CACHE'] = find_good_folder('cache') data_directories['cache'] = dir_settings['cache'] = malojaconfig['DIRECTORY_CACHE'] = find_good_folder('cache')
else: else:
print("Directory",path,"is not usable.") print(f"Directory for {identifier} ({path}) is not writeable.")
print("Please change permissions or settings!") print("Please change permissions or settings!")
print("Make sure Maloja has write and execute access to this directory.") print("Make sure Maloja has write and execute access to this directory.")
raise raise
class DataDirs:
def __init__(self, dirs):
self.dirs = dirs
data_dir = { def __getitem__(self, key):
k:lambda *x,k=k: pthj(data_directories[k],*x) for k in data_directories return lambda *x, k=key: pthj(self.dirs[k], *x)
}
data_dir = DataDirs(data_directories)
### DOREAH OBJECTS ### DOREAH OBJECTS
@ -344,8 +352,10 @@ auth = doreah.auth.AuthManager(singleuser=True,cookieprefix='maloja',stylesheets
doreah.logging.defaultlogger.logfolder = data_dir['logs']() if malojaconfig["LOGGING"] else None doreah.logging.defaultlogger.logfolder = data_dir['logs']() if malojaconfig["LOGGING"] else None
try:
custom_css_files = [f for f in os.listdir(data_dir['css']()) if f.lower().endswith('.css')] custom_css_files = [f for f in os.listdir(data_dir['css']()) if f.lower().endswith('.css')]
except FileNotFoundError:
custom_css_files = []
from ..database.sqldb import set_maloja_info from ..database.sqldb import set_maloja_info
set_maloja_info({'last_run_version':VERSION}) set_maloja_info({'last_run_version':VERSION})

View File

@ -24,6 +24,12 @@ ext_apikeys = [
def copy_initial_local_files(): def copy_initial_local_files():
with resources.files("maloja") / 'data_files' as folder: with resources.files("maloja") / 'data_files' as folder:
for cat in dir_settings: for cat in dir_settings:
if dir_settings[cat] is None:
continue
if cat == 'config' and malojaconfig.readonly:
continue
distutils.dir_util.copy_tree(os.path.join(folder,cat),dir_settings[cat],update=False) distutils.dir_util.copy_tree(os.path.join(folder,cat),dir_settings[cat],update=False)
charset = list(range(10)) + list("abcdefghijklmnopqrstuvwxyz") + list("ABCDEFGHIJKLMNOPQRSTUVWXYZ") charset = list(range(10)) + list("abcdefghijklmnopqrstuvwxyz") + list("ABCDEFGHIJKLMNOPQRSTUVWXYZ")
@ -37,7 +43,6 @@ def setup():
SKIP = malojaconfig["SKIP_SETUP"] SKIP = malojaconfig["SKIP_SETUP"]
try: try:
print("Various external services can be used to display images. If not enough of them are set up, only local images will be used.") print("Various external services can be used to display images. If not enough of them are set up, only local images will be used.")
for k in ext_apikeys: for k in ext_apikeys:
keyname = malojaconfig.get_setting_info(k)['name'] keyname = malojaconfig.get_setting_info(k)['name']
@ -45,6 +50,8 @@ def setup():
if key is False: if key is False:
print(f"\tCurrently not using a {col['red'](keyname)} for image display.") print(f"\tCurrently not using a {col['red'](keyname)} for image display.")
elif key is None or key == "ASK": elif key is None or key == "ASK":
if malojaconfig.readonly:
continue
promptmsg = f"\tPlease enter your {col['gold'](keyname)}. If you do not want to use one at this moment, simply leave this empty and press Enter." promptmsg = f"\tPlease enter your {col['gold'](keyname)}. If you do not want to use one at this moment, simply leave this empty and press Enter."
key = prompt(promptmsg,types=(str,),default=False,skip=SKIP) key = prompt(promptmsg,types=(str,),default=False,skip=SKIP)
malojaconfig[k] = key malojaconfig[k] = key