mirror of
				https://github.com/krateng/maloja.git
				synced 2025-10-30 18:22:29 -04:00 
			
		
		
		
	Merge remote-tracking branch 'origin/master'
This commit is contained in:
		
						commit
						c6cf28896c
					
				| @ -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}") | ||||||
|  | |||||||
| @ -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("<", "<") | ||||||
|  | 		str_xml = str_xml.replace("<", "<") | ||||||
|  | 		str_xml = str_xml.replace("\"", """) | ||||||
|  | 		str_xml = str_xml.replace("'", "'") | ||||||
|  | 		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() | ||||||
|  | |||||||
| @ -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"] | ||||||
|  | |||||||
| @ -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: | ||||||
|  | |||||||
| @ -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}) | ||||||
|  | |||||||
| @ -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 | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user