"""MetaMesh icon family v2 — one procedural mesh sphere + a badge per app. python3 meta_icons.py [APP ...] # render icons (default: all) Neutral icon = the sphere alone (MetaCore). Every other app = sphere + a dark badge bottom-right: coloured ring (family colour), a white glyph, a label that shrinks to fit. Spirit of v1, regenerated from code so nothing is ever lost again. """ import subprocess, sys, os from gen_mesh import mesh_svg MESH = dict(n=46, seed=8, ax=-0.35, ay=0.35) FONT_IM = "DejaVu-Sans-Bold" # ImageMagick name, for measuring FONT_SVG = "DejaVu Sans, Helvetica, Arial, sans-serif" DARK = "#2c3038" ORANGE, TEAL, GOLD, GREEN, PURPLE = "#e8821e", "#00b4b4", "#dca000", "#3ca050", "#8c50c8" # Blue = the MetaPlugin family (headless enrichment workers). Distinct from the # mesh gradient's blues, which never appear inside a badge. BLUE = "#3a7bd5" W = "#ffffff" # Glyphs live in a 160x128 box, drawn white on the dark badge. G = { "nodes": f'' f'', "arrow": f'', "layers": f'', "folder": f'', "play": f'', "cards": f'' f'' f'', "search": f'', "caption": f'' f'', "book": f'' f'', "photo": f'' f'', "gif": f'' f'GIF', "paper": f'' f'', "medical": f'' f'', "flask": f'' f'', "idcard": f'' f'' f'', "pages": f'' f'' f'', "news": f'' f'' f'', "magnet": f'' f'', # --- MetaFeeder additions ------------------------------------------------- "bank": f'' f'' f'' f'', "note": f'' f'' f'' f'' f'', "disc": f'' f'' f'', "video": f'' f'', # --- MetaPlugin family ---------------------------------------------------- "fileinfo": f'' f'', "film": f'' f'' f'' f'' f'', "tag": f'' f'', "hash": f'' f'' f'', "capout": f'' f'' f'' f'' f'', "globe": f'' f'' f'', "speech": f'' f'A', "star": f'', } # app dir → (label, glyph, ring colour); None = neutral (no badge) APPS = { "MetaCore": None, "MetaShare": ("SHARE", "nodes", TEAL), "MetaGateway": ("GATEWAY", "arrow", ORANGE), "MetaSort": ("SORT", "layers", GOLD), "MetaFuse": ("FUSE", "folder", GREEN), "MetaStremio": ("PLAY", "play", PURPLE), "MetaFeederTMDB": ("TMDB", "cards", ORANGE), "MetaFeederProwlarr": ("PROWLARR", "search", ORANGE), "MetaFeederOpenSubtitles": ("OPENSUBS", "caption", ORANGE), "MetaFeederGutenberg": ("GUTENBERG", "book", ORANGE), "MetaFeederWikiCommons": ("COMMONS", "photo", ORANGE), "MetaFeederGiphy": ("GIPHY", "gif", ORANGE), "MetaFeederArxiv": ("ARXIV", "paper", ORANGE), "MetaFeederEuropePMC": ("EUROPE PMC", "medical", ORANGE), "MetaFeederSciHub": ("SCI-HUB", "flask", ORANGE), "MetaFeederAniList": ("ANILIST", "idcard", ORANGE), "MetaFeederSuwayomi": ("SUWAYOMI", "pages", ORANGE), "MetaFeederUsenet": ("USENET", "news", ORANGE), "MetaFeederTribler": ("TRIBLER", "magnet", ORANGE), "MetaFeederInternetArchive": ("ARCHIVE", "bank", ORANGE), "MetaFeederJamendo": ("JAMENDO", "note", ORANGE), "MetaFeederMusicBrainz": ("MUSICBRAINZ", "disc", ORANGE), "MetaFeederYouTube": ("YOUTUBE", "video", ORANGE), "MetaFeederStremio": ("STREMIO", "play", ORANGE), # MetaPlugin family — headless enrichment workers, blue ring. "MetaPluginFileInfo": ("FILE INFO", "fileinfo", BLUE), "MetaPluginFFmpeg": ("FFMPEG", "film", BLUE), "MetaPluginFilenameParser": ("FILENAME", "tag", BLUE), "MetaPluginFullHash": ("FULL HASH", "hash", BLUE), "MetaPluginTMDB": ("TMDB", "idcard", BLUE), "MetaPluginSubtitle": ("SUBTITLE", "caption", BLUE), "MetaPluginSubtitleExtractor": ("SUB EXTRACT", "capout", BLUE), "MetaPluginStillExtractor": ("STILL", "photo", BLUE), "MetaPluginOpenSubtitles": ("OPENSUBS", "globe", BLUE), "MetaPluginLanguage": ("LANGUAGE", "speech", BLUE), "MetaPluginAnimeDetector": ("ANIME", "star", BLUE), "MetaPluginJellyfinNFO": ("NFO", "paper", BLUE), } BADGE_C, BADGE_R, RING = (770, 770), 226, 18 LABEL_MAX_W, LABEL_MAX_PT = 340, 72 def label_size(text): w = int(subprocess.run(["convert", "-font", FONT_IM, "-pointsize", str(LABEL_MAX_PT), f"label:{text}", "-format", "%w", "info:"], capture_output=True, text=True, check=True).stdout) return min(LABEL_MAX_PT, LABEL_MAX_PT * LABEL_MAX_W / w) def icon_svg(spec): if spec is None: svg, _, _ = mesh_svg(512, 512, 385, stroke=11, **MESH) return svg label, glyph, ring = spec svg, _, _ = mesh_svg(500, 500, 340, stroke=10, **MESH) cx, cy = BADGE_C size = label_size(label) badge = (f'' f'' f'{G[glyph]}' f'{label}') return svg.replace("", badge + "") def render(app, out_dir): svg_path = os.path.join(out_dir, f"{app}.svg") png_path = os.path.join(out_dir, f"{app}.png") open(svg_path, "w").write(icon_svg(APPS[app])) subprocess.run(["rsvg-convert", "-w", "1024", "-h", "1024", svg_path, "-o", png_path], check=True) return png_path if __name__ == "__main__": out = sys.argv[1]; os.makedirs(out, exist_ok=True) for app in (sys.argv[2:] or list(APPS)): print(render(app, out))