import asyncio import copy from argparse import ArgumentParser from pathlib import Path from typing import Any, Dict, List import filetype from meme_generator.app import run_server from meme_generator.config import meme_config from meme_generator.download import check_resources from meme_generator.exception import MemeGeneratorException, NoSuchMeme from meme_generator.log import setup_logger from meme_generator.manager import get_meme, get_memes parser = ArgumentParser("meme") subparsers = parser.add_subparsers(dest="handle") list_parser = subparsers.add_parser("list", aliases=["ls"], help="查看表情列表") show_parser = subparsers.add_parser("info", aliases=["show"], help="查看表情详情") show_parser.add_argument("key", type=str, help="表情名") preview_parser = subparsers.add_parser("preview", help="生成表情预览") preview_parser.add_argument("key", type=str, help="表情名") generate_parser = subparsers.add_parser("generate", aliases=["make"], help="制作表情") memes_subparsers = generate_parser.add_subparsers(dest="key", help="表情名") run_parser = subparsers.add_parser("run", aliases=["start"], help="启动 web server") download_parser = subparsers.add_parser("download", help="下载内置表情图片") download_parser.add_argument( "--url", type=str, help="指定资源链接", default=meme_config.resource.resource_url ) def add_parsers(): for meme in get_memes(): meme_parser = ( copy.deepcopy(meme.params_type.args_type.parser) if meme.params_type.args_type else ArgumentParser() ) meme_parser.add_argument("--images", nargs="+", default=[], help="输入图片路径") meme_parser.add_argument("--texts", nargs="+", default=[], help="输入文字") memes_subparsers.add_parser( meme.key, parents=[meme_parser], add_help=False, prefix_chars=meme_parser.prefix_chars, ) def list_memes() -> str: memes = sorted(get_memes(), key=lambda meme: meme.key) return "\n".join( f"{i}. {meme.key} ({'/'.join(meme.keywords)})" for i, meme in enumerate(memes, start=1) ) def meme_info(key: str) -> str: try: meme = get_meme(key) except NoSuchMeme: return f'表情 "{key}" 不存在!' keywords = "、".join([f'"{keyword}"' for keyword in meme.keywords]) patterns = "、".join([f'"{pattern}"' for pattern in meme.patterns]) image_num = f"{meme.params_type.min_images}" if meme.params_type.max_images > meme.params_type.min_images: image_num += f" ~ {meme.params_type.max_images}" text_num = f"{meme.params_type.min_texts}" if meme.params_type.max_texts > meme.params_type.min_texts: text_num += f" ~ {meme.params_type.max_texts}" default_texts = ", ".join([f'"{text}"' for text in meme.params_type.default_texts]) def arg_info(name: str, info: Dict[str, Any]) -> str: text = ( f' "{name}"\n' f" 描述:{info.get('description', '')}\n" f" 类型:`{info.get('type', '')}`\n" f" 默认值:`{info.get('default', '')}`" ) if enum := info.get("enum", []): assert isinstance(enum, list) text += "\n 可选值:" + "、".join([f'"{e}"' for e in enum]) return text if args := meme.params_type.args_type: model = args.model properties: Dict[str, Dict[str, Any]] = model.schema().get("properties", {}) properties.pop("user_infos") args_info = "\n" + "\n".join( [arg_info(name, info) for name, info in properties.items()] ) else: args_info = "" return ( f"表情名:{meme.key}\n" + f"关键词:{keywords}\n" + (f"正则表达式:{patterns}\n" if patterns else "") + "参数:\n" + f" 需要图片数目:{image_num}\n" + f" 需要文字数目:{text_num}\n" + (f" 默认文字:[{default_texts}]\n" if default_texts else "") + (f" 其他参数:{args_info}\n" if args_info else "") ) def generate_meme_preview(key: str) -> str: try: meme = get_meme(key) except NoSuchMeme: return f'表情 "{key}" 不存在!' try: loop = asyncio.new_event_loop() result = loop.run_until_complete(meme.generate_preview()) content = result.getvalue() ext = filetype.guess_extension(content) filename = f"result.{ext}" with open(filename, "wb") as f: f.write(content) return f'表情制作成功!生成的表情文件为 "{filename}"' except MemeGeneratorException as e: return str(e) def generate_meme( key: str, images: List[str], texts: List[str], args: Dict[str, Any] ) -> str: try: meme = get_meme(key) except NoSuchMeme: return f'表情 "{key}" 不存在!' for image in images: if not Path(image).exists(): return f'图片路径 "{image}" 不存在!' try: loop = asyncio.new_event_loop() result = loop.run_until_complete(meme(images=images, texts=texts, args=args)) content = result.getvalue() ext = filetype.guess_extension(content) filename = f"result.{ext}" with open(filename, "wb") as f: f.write(content) return f'表情制作成功!生成的表情文件为 "{filename}"' except MemeGeneratorException as e: return str(e) def main(): setup_logger() add_parsers() args = parser.parse_args() handle = str(args.handle) if handle in ["list", "ls"]: print(list_memes()) elif handle in ["info", "show"]: key = str(args.key) print(meme_info(key)) elif handle in ["preview"]: key = str(args.key) print(generate_meme_preview(key)) elif handle in ["generate", "make"]: kwargs = vars(args) kwargs.pop("handle") key: str = kwargs.pop("key") images: List[str] = kwargs.pop("images") texts: List[str] = kwargs.pop("texts") print(generate_meme(key, images, texts, kwargs)) elif handle in ["run", "start"]: run_server() elif handle in ["download"]: meme_config.resource.resource_url = args.url loop = asyncio.new_event_loop() loop.run_until_complete(check_resources()) else: print(parser.format_help()) if __name__ == "__main__": main()