"""Get material design icons.""" import asyncio import io from PIL import Image from PIL.Image import Image as ImageType import aiohttp import aiopath from hyperlink import URL import xdg from greendeck.lib.images.svg import render_svg_image __all__ = ["get_material_design_icon"] mdi_base_path = URL.from_text( "https://raw.githubusercontent.com/Templarian/MaterialDesign/master/svg/" ) svg_cache: dict[str, bytes] = {} image_cache: dict[tuple[str, int, int], ImageType] = {} async def _get_svg(icon: str) -> bytes: global svg_cache if icon in svg_cache: return svg_cache[icon] cache_file = ( aiopath.AsyncPath(xdg.xdg_cache_home()) / "streamdeck" / "svg" / "original" / f"{icon}.svg" ) if await cache_file.exists(): content = await cache_file.read_bytes() svg_cache[icon] = content return content content = None async with aiohttp.ClientSession() as session: mdi_url = mdi_base_path.child(f"{icon}.svg") async with session.get(str(mdi_url)) as response: if response.status == 200: content = await response.read() svg_cache[icon] = content await cache_file.parent.mkdir(parents=True, exist_ok=True) await cache_file.write_bytes(content) return content mdi_url = mdi_base_path.child("alert-circle.svg") async with session.get(str(mdi_url)) as response: if response.status_code == 200: content = await response.read() svg_cache[icon] = content return content raise ValueError async def get_material_design_icon( icon: str, width: int, height: int ) -> ImageType: """Get a material design icon.""" global image_cache loop = asyncio.get_running_loop() if (icon, width, height) in image_cache: return image_cache[(icon, width, height)] cache_file = ( aiopath.AsyncPath(xdg.xdg_cache_home()) / "streamdeck" / "svg" / "rendered" / f"{icon}" / f"{width}x{height}.webp" ) if await cache_file.exists(): def _decompress(compressed_image: bytes) -> ImageType: image = Image.open(io.BytesIO(compressed_image), formats=["webp"]) image.load() return image compressed_image = await cache_file.read_bytes() image = await loop.run_in_executor(None, _decompress, compressed_image) image_cache[(icon, width, height)] = image return image svg = await _get_svg(icon) image = await render_svg_image(svg, width, height) image_cache[(icon, width, height)] = image def _compress(image: ImageType) -> bytes: compressed_image = io.BytesIO() image.save(compressed_image, format="webp", lossless=True) return compressed_image.getvalue() compressed_image = await loop.run_in_executor(None, _compress, image) await cache_file.parent.mkdir(parents=True, exist_ok=True) await cache_file.write_bytes(compressed_image) return image