Spaces:
Paused
Paused
| import argparse | |
| import json | |
| from PIL import Image, PngImagePlugin | |
| from rich.console import Console | |
| from rich import print | |
| from rich_argparse import RichHelpFormatter | |
| import os | |
| from pathlib import Path | |
| console = Console() | |
| # BNK_CutoffSetRegions | |
| # BNK_CutoffRegionsToConditioning | |
| # BNK_CutoffBasePrompt | |
| # Extracts metadata from a PNG image and returns it as a dictionary | |
| def extract_metadata(image_path): | |
| image = Image.open(image_path) | |
| prompt = image.info.get("prompt", "") | |
| workflow = image.info.get("workflow", "") | |
| if workflow: | |
| workflow = json.loads(workflow) | |
| if prompt: | |
| prompt = json.loads(prompt) | |
| console.print(f"Metadata extracted from [cyan]{image_path}[/cyan].") | |
| return { | |
| "prompt": prompt, | |
| "workflow": workflow, | |
| } | |
| # Embeds metadata into a PNG image | |
| def embed_metadata(image_path, metadata): | |
| image = Image.open(image_path) | |
| o_metadata = image.info | |
| pnginfo = PngImagePlugin.PngInfo() | |
| if prompt := metadata.get("prompt"): | |
| pnginfo.add_text("prompt", json.dumps(prompt)) | |
| elif "prompt" in o_metadata: | |
| pnginfo.add_text("prompt", o_metadata["prompt"]) | |
| if workflow := metadata.get("workflow"): | |
| pnginfo.add_text("workflow", json.dumps(workflow)) | |
| elif "workflow" in o_metadata: | |
| pnginfo.add_text("workflow", o_metadata["workflow"]) | |
| imgp = Path(image_path) | |
| output = imgp.with_stem(f"{imgp.stem}_comfy_embed") | |
| index = 1 | |
| while output.exists(): | |
| output = imgp.with_stem(f"{imgp.stem}_{index}_comfy_embed").with_suffix(".png") | |
| index += 1 | |
| image.save(output, pnginfo=pnginfo) | |
| console.print(f"Metadata embedded into [cyan]{output}[/cyan].") | |
| # CLI subcommand: extract | |
| def extract(args): | |
| input_files = [] | |
| for input_path in args.input: | |
| if os.path.isdir(input_path): | |
| folder_path = input_path | |
| input_files.extend( | |
| [ | |
| os.path.join(folder_path, file_name) | |
| for file_name in os.listdir(folder_path) | |
| if file_name.lower().endswith((".png", ".jpg", ".jpeg")) | |
| ] | |
| ) | |
| else: | |
| input_files.append(input_path) | |
| if len(input_files) == 1: | |
| metadata = extract_metadata(input_files[0]) | |
| if args.print_output: | |
| print(json.dumps(metadata, indent=4)) | |
| else: | |
| if not args.output: | |
| output = Path(input_files[0]).with_suffix(".json") | |
| index = 1 | |
| while output.exists(): | |
| output = ( | |
| Path(input_files[0]) | |
| .with_stem(f"{Path(input_files[0]).stem}_{index}") | |
| .with_suffix(".json") | |
| ) | |
| index += 1 | |
| else: | |
| output = args.output | |
| with open(output, "w") as file: | |
| json.dump(metadata, file, indent=4) | |
| console.print(f"Metadata extracted and saved to [cyan]{output}[/cyan].") | |
| else: | |
| metadata_dict = {} | |
| for input_file in input_files: | |
| metadata = extract_metadata(input_file) | |
| filename = os.path.basename(input_file) | |
| output = ( | |
| Path(args.output) / f"{filename}.json" | |
| if args.output | |
| else Path(input_file).with_suffix(".json") | |
| ) | |
| index = 1 | |
| while output.exists(): | |
| output = Path(args.output).parent / f"{filename}_{index}.json" | |
| index += 1 | |
| with open(output, "w") as file: | |
| json.dump(metadata, file, indent=4) | |
| metadata_dict[filename] = metadata | |
| if args.output: | |
| with open(args.output, "w") as file: | |
| json.dump(metadata_dict, file, indent=4) | |
| console.print( | |
| f"Metadata extracted and saved to [cyan]{args.output}[/cyan]." | |
| ) | |
| else: | |
| console.print("Multiple metadata files created.") | |
| # CLI subcommand: embed | |
| def embed(args): | |
| input_files = [] | |
| for input_path in args.input: | |
| if os.path.isdir(input_path): | |
| folder_path = input_path | |
| input_files.extend( | |
| [ | |
| os.path.join(folder_path, file_name) | |
| for file_name in os.listdir(folder_path) | |
| if file_name.lower().endswith(".json") | |
| ] | |
| ) | |
| else: | |
| input_files.append(input_path) | |
| for input_file in input_files: | |
| with open(input_file) as file: | |
| metadata = json.load(file) | |
| image_path = input_file.replace(".json", ".png") | |
| if args.output: | |
| output_dir = args.output | |
| if os.path.isdir(output_dir): | |
| output_path = os.path.join(output_dir, os.path.basename(image_path)) | |
| index = 1 | |
| while os.path.exists(output_path): | |
| output_path = os.path.join( | |
| output_dir, | |
| f"{os.path.basename(image_path)}_{index}.png", | |
| ) | |
| index += 1 | |
| else: | |
| output_path = output_dir | |
| else: | |
| output_path = image_path.replace(".png", "_comfy_embed.png") | |
| embed_metadata(image_path, metadata) | |
| # os.rename(image_path, output_path) | |
| console.print(f"Metadata embedded into [cyan]{output_path}[/cyan].") | |
| if __name__ == "__main__": | |
| # Create the main CLI parser | |
| parser = argparse.ArgumentParser( | |
| prog="image-metadata-cli", formatter_class=RichHelpFormatter | |
| ) | |
| subparsers = parser.add_subparsers(title="subcommands") | |
| # Parser for the "extract" subcommand | |
| extract_parser = subparsers.add_parser( | |
| "extract", | |
| help="Extract metadata from PNG image(s) or folder", | |
| formatter_class=RichHelpFormatter, | |
| ) | |
| extract_parser.add_argument( | |
| "input", nargs="+", help="Input PNG image file(s) or folder path" | |
| ) | |
| extract_parser.add_argument( | |
| "--print", | |
| dest="print_output", | |
| action="store_true", | |
| help="Print the output to stdout", | |
| ) | |
| extract_parser.add_argument("--output", help="Output JSON file(s) or directory") | |
| extract_parser.set_defaults(func=extract) | |
| # Parser for the "embed" subcommand | |
| embed_parser = subparsers.add_parser( | |
| "embed", | |
| help="Embed metadata into PNG image(s) or folder", | |
| formatter_class=RichHelpFormatter, | |
| ) | |
| embed_parser.add_argument( | |
| "input", nargs="+", help="Input JSON file(s) or folder path" | |
| ) | |
| embed_parser.add_argument("--output", help="Output PNG image file(s) or directory") | |
| embed_parser.set_defaults(func=embed) | |
| # Parse the command-line arguments and execute the appropriate subcommand | |
| args = parser.parse_args() | |
| if hasattr(args, "func"): | |
| try: | |
| args.func(args) | |
| except ValueError as e: | |
| console.print(f"[bold red]Error:[/bold red] {str(e)}") | |
| else: | |
| parser.print_help() | |