mirror of
				https://github.com/walkxcode/dashboard-icons.git
				synced 2025-10-26 21:19:04 +08:00 
			
		
		
		
	
		
			
				
	
	
		
			179 lines
		
	
	
		
			5.8 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			179 lines
		
	
	
		
			5.8 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| import os
 | |
| import re
 | |
| import hashlib
 | |
| from pathlib import Path
 | |
| from PIL import Image
 | |
| import cairosvg
 | |
| 
 | |
| # Define paths
 | |
| ROOT_DIR = Path(__file__).resolve().parent.parent
 | |
| SVG_DIR = ROOT_DIR / "svg"
 | |
| PNG_DIR = ROOT_DIR / "png"
 | |
| WEBP_DIR = ROOT_DIR / "webp"
 | |
| 
 | |
| # Ensure the output folders exist
 | |
| PNG_DIR.mkdir(parents=True, exist_ok=True)
 | |
| WEBP_DIR.mkdir(parents=True, exist_ok=True)
 | |
| 
 | |
| # Track results
 | |
| failed_files = []
 | |
| converted_pngs = 0
 | |
| converted_webps = 0
 | |
| total_icons = 0
 | |
| 
 | |
| def file_size_readable(size_bytes):
 | |
|     """Convert bytes to a human-readable format."""
 | |
|     for unit in ['B', 'KB', 'MB', 'GB']:
 | |
|         if size_bytes < 1024:
 | |
|             return f"{size_bytes:.2f} {unit}"
 | |
|             #size_bytes /= 1024
 | |
|         size_bytes /= 1024
 | |
|     return f"{size_bytes:.2f} TB"
 | |
| 
 | |
| def hash_file(file_path):
 | |
|     """Generate an MD5 hash for a file."""
 | |
|     hash_md5 = hashlib.md5()
 | |
|     with open(file_path, "rb") as f:
 | |
|         for chunk in iter(lambda: f.read(4096), b""):
 | |
|             hash_md5.update(chunk)
 | |
|     return hash_md5.hexdigest()
 | |
| 
 | |
| def convert_to_kebab_case(name):
 | |
|     """Convert a filename to kebab-case."""
 | |
|     cleaned = re.sub(r'[^a-zA-Z0-9\s-]', '', name)
 | |
|     kebab_case_name = re.sub(r'[\s_]+', '-', cleaned).lower()
 | |
|     return kebab_case_name
 | |
| 
 | |
| def rename_if_needed(file_path):
 | |
|     """Ensure the filename is in kebab-case; rename if necessary."""
 | |
|     new_name = convert_to_kebab_case(file_path.stem) + file_path.suffix
 | |
|     new_path = file_path.parent / new_name
 | |
| 
 | |
|     if new_path != file_path:
 | |
|         if new_path.exists():
 | |
|             raise FileExistsError(f"File conflict: {new_path} already exists.")
 | |
|         file_path.rename(new_path)
 | |
|         print(f"Renamed: {file_path} -> {new_path}")
 | |
| 
 | |
|     return new_path
 | |
| 
 | |
| def needs_conversion(output_file, data=None):
 | |
|     """Check if a file needs to be converted or overwritten."""
 | |
|     if output_file.exists():
 | |
|         if data:
 | |
|             existing_hash = hash_file(output_file)
 | |
|             new_hash = hashlib.md5(data).hexdigest()
 | |
|             return existing_hash != new_hash
 | |
|         return False
 | |
|     return True
 | |
| 
 | |
| def convert_svg_to_png(svg_path, png_path):
 | |
|     """Convert SVG to PNG."""
 | |
|     global converted_pngs
 | |
|     try:
 | |
|         png_data = cairosvg.svg2png(url=str(svg_path), output_height=512)
 | |
| 
 | |
|         if needs_conversion(png_path, png_data):
 | |
|             with open(png_path, 'wb') as f:
 | |
|                 f.write(png_data)
 | |
|             print(f"Converted PNG: {png_path} ({file_size_readable(png_path.stat().st_size)})")
 | |
|             converted_pngs += 1
 | |
|         else:
 | |
|             print(f"PNG already up-to-date: {png_path}")
 | |
| 
 | |
|     except Exception as e:
 | |
|         print(f"Failed to convert {svg_path} to PNG: {e}")
 | |
|         failed_files.append(svg_path)
 | |
| 
 | |
| def convert_image_to_webp(image_path, webp_path):
 | |
|     """Convert an image (PNG or other) to WEBP."""
 | |
|     global converted_webps
 | |
|     try:
 | |
|         image = Image.open(image_path).convert("RGBA")
 | |
| 
 | |
|         if needs_conversion(webp_path):
 | |
|             image.save(webp_path, format='WEBP')
 | |
|             print(f"Converted WEBP: {webp_path} ({file_size_readable(webp_path.stat().st_size)})")
 | |
|             converted_webps += 1
 | |
|         else:
 | |
|             print(f"WEBP already up-to-date: {webp_path}")
 | |
| 
 | |
|     except Exception as e:
 | |
|         print(f"Failed to convert {image_path} to WEBP: {e}")
 | |
|         failed_files.append(image_path)
 | |
| 
 | |
| def clean_up_files(folder, valid_basenames):
 | |
|     """Remove files that no longer have corresponding SVG or PNG files."""
 | |
|     removed_files = 0
 | |
|     for file_path in folder.glob('*'):
 | |
|         if file_path.stem not in valid_basenames:
 | |
|             file_path.unlink()
 | |
|             print(f"Removed: {file_path}")
 | |
|             removed_files += 1
 | |
|     return removed_files
 | |
| 
 | |
| if __name__ == "__main__":
 | |
|     # Track valid basenames (from SVG and PNG files)
 | |
|     valid_basenames = set()
 | |
| 
 | |
|     # Process all SVG files
 | |
|     for svg_file in SVG_DIR.glob("*.svg"):
 | |
|         total_icons += 1
 | |
| 
 | |
|         # Ensure the filename is in kebab-case
 | |
|         try:
 | |
|             svg_path = rename_if_needed(svg_file)
 | |
|         except Exception as e:
 | |
|             print(f"Error renaming {svg_file}: {e}")
 | |
|             failed_files.append(svg_file)
 | |
|             continue
 | |
| 
 | |
|         valid_basenames.add(svg_path.stem)
 | |
| 
 | |
|         # Set paths for PNG and WEBP
 | |
|         png_path = PNG_DIR / f"{svg_path.stem}.png"
 | |
|         webp_path = WEBP_DIR / f"{svg_path.stem}.webp"
 | |
| 
 | |
|         # Convert SVG to PNG
 | |
|         convert_svg_to_png(svg_path, png_path)
 | |
| 
 | |
|         # Convert PNG to WEBP
 | |
|         if png_path.exists():
 | |
|             convert_image_to_webp(png_path, webp_path)
 | |
| 
 | |
|     # Process PNG-only files
 | |
|     for png_file in PNG_DIR.glob("*.png"):
 | |
|         if png_file.stem not in valid_basenames:
 | |
|             # Ensure the filename is in kebab-case
 | |
|             try:
 | |
|                 png_path = rename_if_needed(png_file)
 | |
|             except Exception as e:
 | |
|                 print(f"Error renaming {png_file}: {e}")
 | |
|                 failed_files.append(png_file)
 | |
|                 continue
 | |
| 
 | |
|             valid_basenames.add(png_path.stem)
 | |
| 
 | |
|             # Set path for WEBP
 | |
|             webp_path = WEBP_DIR / f"{png_path.stem}.webp"
 | |
| 
 | |
|             # Convert PNG to WEBP
 | |
|             convert_image_to_webp(png_path, webp_path)
 | |
| 
 | |
|     # Clean up unused files in PNG and WEBP directories
 | |
|     # Only remove files that don't have corresponding SVG or PNG files
 | |
|     removed_pngs = clean_up_files(PNG_DIR, valid_basenames.union({p.stem for p in SVG_DIR.glob("*.svg")}))
 | |
|     removed_webps = clean_up_files(WEBP_DIR, valid_basenames)
 | |
| 
 | |
|     # Display summary
 | |
|     if converted_pngs == 0 and converted_webps == 0 and removed_pngs == 0 and removed_webps == 0:
 | |
|         print("\nAll icons are already up-to-date.")
 | |
|     else:
 | |
|         print(f"\nConverted {converted_pngs} PNGs and {converted_webps} WEBPs out of {total_icons} icons.")
 | |
|         print(f"Removed {removed_pngs} PNGs and {removed_webps} WEBPs.")
 | |
| 
 | |
|     # Display any failed conversions
 | |
|     if failed_files:
 | |
|         print("\nThe following files failed to convert:")
 | |
|         for file in failed_files:
 | |
|             print(file) | 
