greendeck/greendeck/lib/images/materialdesignicons.py
2022-12-13 14:25:50 -06:00

110 lines
3.1 KiB
Python

"""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