Skip to content

Instantly share code, notes, and snippets.

@jag-k
Last active April 10, 2025 23:36
Show Gist options
  • Save jag-k/e8c0c801fc88aa683e85611d414ae147 to your computer and use it in GitHub Desktop.
Save jag-k/e8c0c801fc88aa683e85611d414ae147 to your computer and use it in GitHub Desktop.
Convert your MacOS apps icons to FlexBar Icon Pack!

MacOS Icon Extractor and Converter

This script extracts icons from MacOS applications and system resources, then converts them to a specific JSON format with Base64-encoded images.

Usage:

# Just run a script with default settings
uv run flexbar_macos_icon_extractor.py

# Help
uv run flexbar_macos_icon_extractor.py --help
#!/usr/bin/env python3
# /// script
# dependencies = [
# "Pillow",
# ]
# ///
"""
MacOS Icon Extractor and Converter
This script extracts icons from MacOS applications and system resources,
then converts them to a specific JSON format with Base64-encoded images.
Usage:
uv run flexbar_macos_icon_extractor.py [-h] [--name NAME] [--description DESCRIPTION] [--size SIZE] [--version VERSION]
"""
import os
import sys
import json
import base64
import hashlib
import uuid
import subprocess
import datetime
import tempfile
import argparse
from pathlib import Path
from PIL import Image
def find_applications():
"""Find all application bundles on the system."""
app_paths = []
# Standard application directories
app_dirs = [
"/Applications",
"/System/Applications",
"/System/Library/CoreServices",
str(Path.home() / "Applications"),
]
for app_dir in app_dirs:
if os.path.exists(app_dir):
# Find .app bundles
cmd = ["find", app_dir, "-name", "*.app", "-maxdepth", "2"]
try:
result = subprocess.run(cmd, capture_output=True, text=True, check=True)
app_paths.extend(result.stdout.strip().split("\n"))
except subprocess.CalledProcessError:
print(f"Error searching in {app_dir}")
# Filter out empty strings
return [path for path in app_paths if path]
def extract_icon(app_path, size=60):
"""Extract icon from an application bundle and convert to PNG."""
try:
# Create a temporary directory
with tempfile.TemporaryDirectory() as temp_dir:
temp_file = os.path.join(temp_dir, "icon.png")
# Use sips to extract and convert the icon
icon_path = os.path.join(app_path, "Contents", "Resources", "AppIcon.icns")
if not os.path.exists(icon_path):
# Try alternative locations
info_plist = os.path.join(app_path, "Contents", "Info.plist")
if os.path.exists(info_plist):
# Extract icon name from Info.plist
cmd = ["defaults", "read", info_plist, "CFBundleIconFile"]
try:
result = subprocess.run(cmd, capture_output=True, text=True)
icon_name = result.stdout.strip()
if not icon_name.endswith(".icns"):
icon_name += ".icns"
icon_path = os.path.join(
app_path, "Contents", "Resources", icon_name
)
except subprocess.CalledProcessError:
pass
if os.path.exists(icon_path):
# Convert ICNS to PNG
cmd = [
"sips",
"-s",
"format",
"png",
icon_path,
"--out",
temp_file,
"--resampleHeightWidth",
str(size),
str(size),
]
subprocess.run(cmd, capture_output=True, check=True)
# Read the image and resize if needed
with Image.open(temp_file) as img:
if img.size != (size, size):
img = img.resize((size, size), Image.LANCZOS)
img.save(temp_file)
# Return the image data
with open(temp_file, "rb") as f:
return f.read()
except Exception as e:
print(f"Error extracting icon from {app_path}: {e}")
return None
def image_to_base64(image_data):
"""Convert image data to base64 string."""
if image_data:
return base64.b64encode(image_data).decode("utf-8")
return None
def create_json_structure(
icons_data,
name: str = "Sticker Pack",
version: str = "1.0.5",
size: int = 60,
description: str = "",
):
"""Create the JSON structure for the sticker pack."""
data_obj = {}
for app_name, image_data in icons_data.items():
if image_data:
# Generate MD5 hash for the image data
img_hash = hashlib.md5(image_data).hexdigest()
# Convert image to base64
base64_data = image_to_base64(image_data)
if base64_data:
data_obj[img_hash] = {
"src": f"data:image/png;base64,{base64_data}",
"width": size,
"height": size,
}
# Add metadata
final_json = {
"__FLEXBAR_DESIGNER_MARK__": 0,
"uuid": str(uuid.uuid4()),
"date": datetime.datetime.now().isoformat(),
"version": version,
"detailed": description,
"name": name,
"data": data_obj,
}
return final_json
def main():
parser = argparse.ArgumentParser(
description="Extract MacOS icons and convert to JSON format"
)
parser.add_argument(
"--name", default="FlexBar Apps Sticker Pack", help="Name of the sticker pack"
)
parser.add_argument(
"--description", default="", help="Description of the sticker pack"
)
parser.add_argument("--size", type=int, default=60, help="Icon size (default: 60)")
parser.add_argument(
"--version", default="1.0.5", help="Version of the sticker pack"
)
args = parser.parse_args()
print("Finding applications...")
app_paths = find_applications()
print(f"Found {len(app_paths)} applications")
icons_data = {}
longest_path_name: int = max(
map(lambda x: len(os.path.basename(x).replace(".app", "")), app_paths)
)
for i, app_path in enumerate(app_paths):
app_name = os.path.basename(app_path).replace(".app", "")
print(
f"Processing {i + 1}/{len(app_paths)}: {app_name:<{longest_path_name}}",
end="\r",
)
icon_data = extract_icon(app_path, args.size)
if icon_data:
icons_data[app_name] = icon_data
print(f"\nSuccessfully extracted {len(icons_data)} icons")
# Create JSON structure
json_data = create_json_structure(
dict(sorted(icons_data.items())),
name=args.name,
size=args.size,
description=args.description,
version=args.version,
)
output_filename = f"{args.name}.iconpack"
# Save to file
with open(output_filename, "w") as f:
json.dump(json_data, f)
print(f"JSON file saved to {output_filename}")
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment