Skip to content

Commit

Permalink
Refactor icon loading: optimise caching, add icons.json pre-fetching,…
Browse files Browse the repository at this point in the history
… streamline icon structure, improve error handling
  • Loading branch information
beecho01 committed Oct 27, 2024
1 parent 7b36ac9 commit bd7f5a3
Show file tree
Hide file tree
Showing 32,706 changed files with 56,452 additions and 28,760 deletions.
The diff you're trying to view is too large. We only load the first 3000 changed files.
58 changes: 8 additions & 50 deletions custom_components/material_symbols/__init__.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,8 @@
import logging

from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.components.frontend import add_extra_js_url
from homeassistant.components.http import StaticPathConfig
from homeassistant.components.http.view import HomeAssistantView
from homeassistant.core import async_get_hass
from homeassistant.helpers import config_validation as cv

import json
from os import path, walk

LOGGER = logging.getLogger(__name__)

Expand All @@ -18,62 +11,29 @@
DATA_EXTRA_MODULE_URL = "frontend_extra_module_url"
LOADER_URL = f"/{DOMAIN}/material_symbols.js"
LOADER_PATH = f"custom_components/{DOMAIN}/material_symbols.js"
ICONS_URL = f"/{DOMAIN}/icons"
ICONLIST_URL = f"/{DOMAIN}/list"
ICONS_URL = f"/{DOMAIN}"
ICONS_PATH = f"custom_components/{DOMAIN}/data"

class ListingView(HomeAssistantView):

requires_auth = False

def __init__(self, url, iconpath, hass):
self.url = url
self.iconpath = iconpath
self.hass: HomeAssistant = hass
self.name = "Icon Listing"

async def get(self, request):
icons_list = await self.hass.async_add_executor_job(
self.get_icons_list, self.iconpath
)
return icons_list

def get_icons_list(self, iconpath):
icons = []
for dirpath, dirnames, filenames in walk(iconpath):
icons.extend(
[
{"name": path.join(dirpath[len(iconpath) :].lstrip("/"), fn[:-4])}
for fn in filenames
if fn.endswith(".svg")
]
)
return json.dumps(icons)


async def async_setup(hass: HomeAssistant, config):
# Register the JS loader file
await hass.http.async_register_static_paths(
[StaticPathConfig(LOADER_URL, hass.config.path(LOADER_PATH), True)]
)
add_extra_js_url(hass, LOADER_URL)

for iset in ["outlined", "outlined_filled", "rounded", "rounded_filled", "sharp", "sharp_filled"]:
# Define icon set folders and register their static paths
iconset_prefixes = ["m3o", "m3of", "m3r", "m3rf", "m3s", "m3sf"]
for iconset_prefix in iconset_prefixes:
await hass.http.async_register_static_paths(
[
StaticPathConfig(
ICONS_URL + "/" + iset,
hass.config.path(ICONS_PATH + "/" + iset),
f"{ICONS_URL}/{iconset_prefix}",
hass.config.path(f"{ICONS_PATH}/{iconset_prefix}"),
True,
)
]
)
hass.http.register_view(
ListingView(
ICONLIST_URL + "/" + iset,
hass.config.path(ICONS_PATH + "/" + iset),
hass,
)
)

return True

async def async_setup_entry(hass, entry):
Expand All @@ -84,10 +44,8 @@ async def async_remove_entry(hass, entry):

async def async_migrate_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Migrate from old entry."""

if entry.version == 1:
entry.version = 2

hass.config_entries.async_update_entry(
entry,
title="Material Symbols"
Expand Down
45 changes: 18 additions & 27 deletions custom_components/material_symbols/data/extract_names.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,18 @@
import json

# Base data folder path
data_folder = r"C:\Users\James Beeching\OneDrive\Home Assistant\material-symbols-2\custom_components\material_symbols\data"
data_folder = r"C:\Github\material-symbols\custom_components\material_symbols\data"

# Define the six icon sets with their corresponding folder paths
icon_sets = {
"outlined": os.path.join(data_folder, "outlined"),
"outlined_filled": os.path.join(data_folder, "outlined_filled"),
"rounded": os.path.join(data_folder, "rounded"),
"rounded_filled": os.path.join(data_folder, "rounded_filled"),
"sharp": os.path.join(data_folder, "sharp"),
"sharp_filled": os.path.join(data_folder, "sharp_filled")
"m3o": os.path.join(data_folder, "m3o"),
"m3of": os.path.join(data_folder, "m3of"),
"m3r": os.path.join(data_folder, "m3r"),
"m3rf": os.path.join(data_folder, "m3rf"),
"m3s": os.path.join(data_folder, "m3s"),
"m3sf": os.path.join(data_folder, "m3sf")
}

# Dictionary to hold icon names per icon set
icons_by_set = {}

# Loop through each icon set and its folder
for icon_set_name, folder in icon_sets.items():
# Check if the folder exists
Expand All @@ -39,22 +36,16 @@
print(f"Error accessing folder {folder}: {e}")
continue

# Sort the names alphabetically
sorted_names = sorted(unique_names)

# Create a dictionary where both keys and values are the same
name_dict = {name: name for name in sorted_names}

# Add this dictionary to the main dictionary under the icon set name
icons_by_set[icon_set_name] = name_dict
# Sort the names alphabetically and convert to the required structure
icons_array = [{"name": name} for name in sorted(unique_names)]

# Define the path where you want the minified JSON file to be saved
json_output = os.path.join(data_folder, "names.min.json")
# Define the path for the icons.json file for this icon set
json_output = os.path.join(folder, "icons.json")

# Write the minified JSON file to the specified location
try:
with open(json_output, "w") as json_file:
json.dump(icons_by_set, json_file, separators=(',', ':')) # Minify the JSON
print(f"Minified icon names by set saved to {json_output}")
except Exception as e:
print(f"Error writing JSON file: {e}")
# Write the JSON file in the correct format
try:
with open(json_output, "w") as json_file:
json.dump(icons_array, json_file, indent=2) # Pretty print for readability
print(f"icons.json file saved to {json_output}")
except Exception as e:
print(f"Error writing JSON file for {icon_set_name}: {e}")
54 changes: 25 additions & 29 deletions custom_components/material_symbols/data/generate_svgs.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,26 +5,21 @@

# Constants for the folder paths
FOLDERS = {
"outlined": "outlined",
"outline-rounded": "rounded",
"outline-sharp": "sharp",
"rounded": "rounded_filled",
"sharp": "sharp_filled",
"default": "outlined_filled" # Remaining files go here
"outlined": "m3o",
"outline-rounded": "m3r",
"outline-sharp": "m3s",
"rounded": "m3rf",
"sharp": "m3sf",
"default": "m3of"
}

# Remove existing folders and create new ones
def create_folders(base_path):
# Iterate through the folders defined in FOLDERS
for folder in FOLDERS.values():
folder_path = os.path.join(base_path, folder)

# If the folder exists, remove it
if os.path.exists(folder_path):
shutil.rmtree(folder_path)
print(f"Removed existing folder and contents at: {folder_path}")

# Recreate the folder
os.makedirs(folder_path)
print(f"Created folder: {folder_path}")

Expand All @@ -36,7 +31,7 @@ def fetch_json(url):
else:
raise Exception(f"Failed to fetch JSON data. Status code: {response.status_code}")

# Strip the suffix from the name (e.g., remove '-outline', '-rounded')
# Strip the suffix from the name
def strip_suffix(name):
if name.endswith("outline"):
return name.replace("-outline", "")
Expand All @@ -51,16 +46,13 @@ def strip_suffix(name):
else:
return name

# Parse the JSON and export SVG files
# Export SVG files and collect icon names by folder
def export_svgs(base_path, icons):
icon_names_by_folder = {folder: [] for folder in FOLDERS.values()}
for name, data in icons.items():
# Replace escaped quotes with regular quotes in the SVG path
svg_body = data["body"].replace('\"', '"')

# Create the full SVG content with correct quotes
svg_content = f'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">{svg_body}</svg>'

# Determine the folder based on the filename (use full name here)
if name.endswith("outline"):
folder = FOLDERS["outlined"]
elif name.endswith("outline-rounded"):
Expand All @@ -74,32 +66,36 @@ def export_svgs(base_path, icons):
else:
folder = FOLDERS["default"]

# Strip the suffix to get the base name
base_name = strip_suffix(name)

# Create the full path for the SVG file using the base name
file_path = os.path.join(base_path, folder, f"{base_name}.svg")

# Write the SVG content to the file
with open(file_path, "w") as svg_file:
svg_file.write(svg_content)
print(f"Exported {base_name}.svg to {folder}")

# Track icon name for JSON output
icon_names_by_folder[folder].append(base_name)

# Generate JSON files for each folder
for folder, icons in icon_names_by_folder.items():
create_icon_json(base_path, folder, icons)

# Create a JSON file listing all icons in a folder
def create_icon_json(base_path, folder, icons):
icons_data = [{"name": icon} for icon in icons]
json_path = os.path.join(base_path, folder, "icons.json")
with open(json_path, "w") as json_file:
json.dump(icons_data, json_file, indent=4)
print(f"Created JSON for {folder} with {len(icons)} icons")

# Main function
def main():
url = "https://raw.githubusercontent.com/iconify/icon-sets/refs/heads/master/json/material-symbols.json"
base_path = r"C:\Users\jbeeching\Downloads\OneDrive-2024-10-21\data" # Specify your output folder
base_path = r"C:\Github\material-symbols\custom_components\material_symbols\data" # Specify your output folder

# Create (or recreate) the necessary folders
create_folders(base_path)

# Fetch and parse the JSON data
json_data = fetch_json(url)

# Get the icons section
icons = json_data.get("icons", {})

# Export SVGs
export_svgs(base_path, icons)

if __name__ == "__main__":
Expand Down
1 change: 1 addition & 0 deletions custom_components/material_symbols/data/m3o/10k.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions custom_components/material_symbols/data/m3o/10mp.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions custom_components/material_symbols/data/m3o/11mp.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions custom_components/material_symbols/data/m3o/12mp.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions custom_components/material_symbols/data/m3o/13mp.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions custom_components/material_symbols/data/m3o/14mp.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions custom_components/material_symbols/data/m3o/15mp.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions custom_components/material_symbols/data/m3o/16mp.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions custom_components/material_symbols/data/m3o/17mp.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions custom_components/material_symbols/data/m3o/18mp.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions custom_components/material_symbols/data/m3o/19mp.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions custom_components/material_symbols/data/m3o/1k-plus.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions custom_components/material_symbols/data/m3o/1k.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions custom_components/material_symbols/data/m3o/20mp.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions custom_components/material_symbols/data/m3o/21mp.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions custom_components/material_symbols/data/m3o/22mp.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit bd7f5a3

Please sign in to comment.