110 lines
3.1 KiB
Python
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
|