import socket import pickle import base64 import ipaddress from urllib.parse import urlparse def load_artwork_settings(settings_data): try: if not settings_data: return None try: padding = len(settings_data) % 4 if padding: settings_data_padded = settings_data + '=' * (4 - padding) else: settings_data_padded = settings_data raw = base64.b64decode(settings_data_padded) if raw[:2] in (b'\x80\x03', b'\x80\x04', b'\x80\x05', b'\x80\x02'): settings = pickle.loads(raw) if hasattr(settings, '__dict__'): return settings.__dict__ elif isinstance(settings, dict): return settings else: return {'data': str(settings)} except: pass return {'description': settings_data} except Exception as e: return {'error': str(e)} def save_artwork_description(description): if not description: return None try: padding = len(description) % 4 if padding: padded = description + '=' * (4 - padding) else: padded = description raw = base64.b64decode(padded) if raw[:2] in (b'\x80\x03', b'\x80\x04', b'\x80\x05', b'\x80\x02'): return description except: pass return description class ArtworkConfig: def __init__(self, colors=None, animation=False, public=True): self.colors = colors or ["#FF0000", "#00FF00", "#0000FF"] self.animation = animation self.public = public def __repr__(self): return f"ArtworkConfig(colors={self.colors}, animation={self.animation}, public={self.public})" def __str__(self): return self.__repr__() def __reduce__(self): return (self.__class__, (self.colors, self.animation, self.public)) def is_safe_url(url: str): try: parsed = urlparse(url) if parsed.scheme not in ("http", "https"): return False, "403" hostname = parsed.hostname if not hostname: return False, "403" try: ip_str = socket.gethostbyname(hostname) except socket.gaierror: return False, "403" try: ip = ipaddress.ip_address(ip_str) except ValueError: return False, "403" if ( ip.is_loopback or ip.is_private or ip.is_link_local or ip.is_unspecified ): return False, "403" return True, ip_str except Exception: return False, "403"