133 lines
		
	
	
	
		
			3.5 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable file
		
	
	
	
	
			
		
		
	
	
			133 lines
		
	
	
	
		
			3.5 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable file
		
	
	
	
	
| #!/usr/bin/python3
 | |
| 
 | |
| import argparse
 | |
| from typing import List
 | |
| from pathlib import Path
 | |
| 
 | |
| from PIL import Image
 | |
| 
 | |
| 
 | |
| SOURCES = ["konachan", "wallhaven", "yandere", "moe_imouto"]
 | |
| RATIO_PATHS = ("4_3", "16_9", "16_10", "vertical")
 | |
| 
 | |
| 
 | |
| def get_wallpaper_source(filename: Path) -> str:
 | |
| 
 | |
|     if filename.name.startswith("Konachan.com"):
 | |
|         return "konachan"
 | |
|     elif filename.name.startswith("wallhaven"):
 | |
|         return "wallhaven"
 | |
|     elif filename.name.startswith("yande.re"):
 | |
|         return "yandere"
 | |
|     elif filename.name.startswith("moe"):
 | |
|         return "moe_imouto"
 | |
|     else:
 | |
|         return ""
 | |
| 
 | |
| 
 | |
| def ratio_and_resolution(filename: Path, guess_source=True) -> Path:
 | |
| 
 | |
|     img = Image.open(filename)
 | |
|     resolution = f"{img.width}x{img.height}"
 | |
|     ratio = img.width / img.height
 | |
| 
 | |
|     check_ratios = [("4:3", 4/3), ("16:9", 16/9), ("16:10", 16/10)]
 | |
|     # Calculate the distance from each ratio of the image in question
 | |
|     distances = {label: (ratio-ref)**2 for label, ref in check_ratios}
 | |
|     # Calculate the ratio with the minimum distance
 | |
|     min_dist = min(distances, key=distances.get).replace(":", "_")
 | |
| 
 | |
|     if guess_source:
 | |
|         source = get_wallpaper_source(filename)
 | |
|     else:
 | |
|         source = ""
 | |
| 
 | |
|     new_filename = "_".join((filename.stem, resolution)) + filename.suffix
 | |
| 
 | |
|     # Special case phone wallpapers for now
 | |
|     if img.height > img.width:
 | |
|         min_dist = "vertical"
 | |
| 
 | |
|     if source:
 | |
|         final_name = Path(min_dist) / source / new_filename
 | |
|     else:
 | |
|         final_name = Path(min_dist) / new_filename
 | |
| 
 | |
|     return final_name
 | |
| 
 | |
| 
 | |
| def organize_images(files: List[Path],
 | |
|                     destination_path: Path, dry_run=True) -> None:
 | |
| 
 | |
|     for ratio_set in RATIO_PATHS:
 | |
|         if not dry_run:
 | |
|             (destination_path / ratio_set).mkdir(exist_ok=True)
 | |
|             for source in SOURCES:
 | |
|                 (destination_path / ratio_set / source).mkdir(exist_ok=True)
 | |
| 
 | |
|     for element in files:
 | |
|         new_name = destination_path / ratio_and_resolution(element)
 | |
| 
 | |
|         if dry_run:
 | |
|             print(f"{element} -> {new_name}")
 | |
|             continue
 | |
| 
 | |
|         if not new_name.exists():
 | |
|             element.rename(new_name)
 | |
|             # new_name.symlink_to(element)
 | |
| 
 | |
| 
 | |
| def get_image_list(source: Path) -> List[Path]:
 | |
| 
 | |
|     candidates = list()
 | |
|     for element in source.glob("**/*"):
 | |
| 
 | |
|         if element.is_dir():
 | |
|             continue
 | |
| 
 | |
|         # Exclude already processed directories
 | |
|         if any(part in RATIO_PATHS for part in element.parts):
 | |
|             continue
 | |
| 
 | |
|         if element.suffix in (".png", ".jpg", ".webp", ".jpeg", ".bmp"):
 | |
|             candidates.append(element)
 | |
| 
 | |
|     return candidates
 | |
| 
 | |
| 
 | |
| def cleanup(destination: Path) -> None:
 | |
| 
 | |
|     for element in RATIO_PATHS:
 | |
|         full_path = destination / element
 | |
| 
 | |
|         for source in SOURCES:
 | |
|             source_path = full_path / source
 | |
| 
 | |
|             if not any(source_path.iterdir()):
 | |
|                 source_path.rmdir()
 | |
| 
 | |
|         if not any(full_path.iterdir()):
 | |
|             full_path.rmdir()
 | |
| 
 | |
| 
 | |
| def main():
 | |
| 
 | |
|     parser = argparse.ArgumentParser()
 | |
|     parser.add_argument("--dry-run", action="store_true",
 | |
|                         help="Only simulate")
 | |
|     parser.add_argument("source", help="Source wallpaper directory")
 | |
|     parser.add_argument("destination", help="Destination path to move data to")
 | |
| 
 | |
|     options = parser.parse_args()
 | |
|     source = Path(options.source).absolute()
 | |
| 
 | |
|     destination = Path(options.destination).absolute()
 | |
| 
 | |
|     images = get_image_list(source)
 | |
|     organize_images(images, destination, options.dry_run)
 | |
| 
 | |
|     cleanup(destination)
 | |
| 
 | |
| 
 | |
| if __name__ == "__main__":
 | |
|     main()
 |