meme-generator-next 0.2.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- meme_generator/__init__.py +59 -0
- meme_generator/app.py +322 -0
- meme_generator/cli.py +238 -0
- meme_generator/compat.py +96 -0
- meme_generator/config.py +98 -0
- meme_generator/dirs.py +251 -0
- meme_generator/download.py +116 -0
- meme_generator/exception.py +120 -0
- meme_generator/log.py +87 -0
- meme_generator/manager.py +111 -0
- meme_generator/meme.py +167 -0
- meme_generator/memes/5000choyen/__init__.py +202 -0
- meme_generator/memes/abstinence/__init__.py +93 -0
- meme_generator/memes/ace_attorney_dialog/__init__.py +122 -0
- meme_generator/memes/acg_entrance/__init__.py +47 -0
- meme_generator/memes/add_chaos/__init__.py +29 -0
- meme_generator/memes/addiction/__init__.py +41 -0
- meme_generator/memes/alike/__init__.py +32 -0
- meme_generator/memes/alipay/__init__.py +89 -0
- meme_generator/memes/always/__init__.py +138 -0
- meme_generator/memes/always_like/__init__.py +96 -0
- meme_generator/memes/anti_kidnap/__init__.py +26 -0
- meme_generator/memes/anya_suki/__init__.py +50 -0
- meme_generator/memes/applaud/__init__.py +39 -0
- meme_generator/memes/arona_throw/__init__.py +64 -0
- meme_generator/memes/ascension/__init__.py +37 -0
- meme_generator/memes/ask/__init__.py +94 -0
- meme_generator/memes/atri_pillow/__init__.py +75 -0
- meme_generator/memes/ayachi_holdsign/__init__.py +49 -0
- meme_generator/memes/ba_say/__init__.py +144 -0
- meme_generator/memes/back_to_work/__init__.py +28 -0
- meme_generator/memes/backflip/__init__.py +81 -0
- meme_generator/memes/bad_news/__init__.py +41 -0
- meme_generator/memes/beat_head/__init__.py +52 -0
- meme_generator/memes/beat_up/__init__.py +38 -0
- meme_generator/memes/bite/__init__.py +41 -0
- meme_generator/memes/blamed_mahiro/__init__.py +78 -0
- meme_generator/memes/blood_pressure/__init__.py +30 -0
- meme_generator/memes/bluearchive/__init__.py +67 -0
- meme_generator/memes/bocchi_draft/__init__.py +52 -0
- meme_generator/memes/bronya_holdsign/__init__.py +41 -0
- meme_generator/memes/bubble_tea/__init__.py +76 -0
- meme_generator/memes/call_110/__init__.py +28 -0
- meme_generator/memes/capoo_draw/__init__.py +47 -0
- meme_generator/memes/capoo_point/__init__.py +40 -0
- meme_generator/memes/capoo_rip/__init__.py +63 -0
- meme_generator/memes/capoo_rub/__init__.py +40 -0
- meme_generator/memes/capoo_say/__init__.py +71 -0
- meme_generator/memes/capoo_stew/__init__.py +40 -0
- meme_generator/memes/capoo_strike/__init__.py +48 -0
- meme_generator/memes/captain/__init__.py +39 -0
- meme_generator/memes/caused_by_this/__init__.py +66 -0
- meme_generator/memes/certificate/__init__.py +125 -0
- meme_generator/memes/charpic/__init__.py +43 -0
- meme_generator/memes/chase_train/__init__.py +66 -0
- meme_generator/memes/china_flag/__init__.py +26 -0
- meme_generator/memes/chino_throw/__init__.py +60 -0
- meme_generator/memes/clauvio_twist/__init__.py +58 -0
- meme_generator/memes/clown/__init__.py +91 -0
- meme_generator/memes/clown_mask/__init__.py +86 -0
- meme_generator/memes/confuse/__init__.py +40 -0
- meme_generator/memes/coupon/__init__.py +45 -0
- meme_generator/memes/cover_face/__init__.py +27 -0
- meme_generator/memes/crawl/__init__.py +63 -0
- meme_generator/memes/cyan/__init__.py +39 -0
- meme_generator/memes/daynight/__init__.py +28 -0
- meme_generator/memes/decent_kiss/__init__.py +26 -0
- meme_generator/memes/dianzhongdian/__init__.py +62 -0
- meme_generator/memes/dinosaur/__init__.py +30 -0
- meme_generator/memes/distracted/__init__.py +33 -0
- meme_generator/memes/divorce/__init__.py +26 -0
- meme_generator/memes/dog_dislike/__init__.py +94 -0
- meme_generator/memes/dog_of_vtb/__init__.py +31 -0
- meme_generator/memes/dont_go_near/__init__.py +30 -0
- meme_generator/memes/dont_press/__init__.py +38 -0
- meme_generator/memes/dont_touch/__init__.py +66 -0
- meme_generator/memes/douyin/__init__.py +80 -0
- meme_generator/memes/eat/__init__.py +30 -0
- meme_generator/memes/emperor_dragon/__init__.py +40 -0
- meme_generator/memes/erised_mirror/__init__.py +39 -0
- meme_generator/memes/fade_away/__init__.py +134 -0
- meme_generator/memes/fanatic/__init__.py +38 -0
- meme_generator/memes/father_work/__init__.py +49 -0
- meme_generator/memes/fencing/__init__.py +45 -0
- meme_generator/memes/fight_with_sunuo/__init__.py +32 -0
- meme_generator/memes/fill_head/__init__.py +48 -0
- meme_generator/memes/find_chips/__init__.py +44 -0
- meme_generator/memes/firefly_holdsign/__init__.py +105 -0
- meme_generator/memes/flash_blind/__init__.py +65 -0
- meme_generator/memes/flick/__init__.py +109 -0
- meme_generator/memes/flush/__init__.py +55 -0
- meme_generator/memes/fogging/__init__.py +51 -0
- meme_generator/memes/follow/__init__.py +46 -0
- meme_generator/memes/frieren_take/__init__.py +50 -0
- meme_generator/memes/funny_mirror/__init__.py +31 -0
- meme_generator/memes/garbage/__init__.py +38 -0
- meme_generator/memes/genshin_eat/__init__.py +99 -0
- meme_generator/memes/genshin_start/__init__.py +61 -0
- meme_generator/memes/gif_subtitle/__init__.py +197 -0
- meme_generator/memes/good_news/__init__.py +41 -0
- meme_generator/memes/google/__init__.py +29 -0
- meme_generator/memes/google_captcha/__init__.py +78 -0
- meme_generator/memes/gorilla_throw/__init__.py +58 -0
- meme_generator/memes/guichu/__init__.py +129 -0
- meme_generator/memes/gun/__init__.py +76 -0
- meme_generator/memes/hammer/__init__.py +38 -0
- meme_generator/memes/haruhi_raise/__init__.py +36 -0
- meme_generator/memes/high_EQ/__init__.py +56 -0
- meme_generator/memes/hit_screen/__init__.py +56 -0
- meme_generator/memes/hold_grudge/__init__.py +37 -0
- meme_generator/memes/hold_tight/__init__.py +26 -0
- meme_generator/memes/hug/__init__.py +45 -0
- meme_generator/memes/hug_leg/__init__.py +40 -0
- meme_generator/memes/hutao_bite/__init__.py +35 -0
- meme_generator/memes/imprison/__init__.py +37 -0
- meme_generator/memes/incivilization/__init__.py +47 -0
- meme_generator/memes/intel_inside/__init__.py +47 -0
- meme_generator/memes/interview/__init__.py +49 -0
- meme_generator/memes/jerry_stare/__init__.py +32 -0
- meme_generator/memes/jiji_king/__init__.py +117 -0
- meme_generator/memes/jinhsi/__init__.py +81 -0
- meme_generator/memes/jiubingfufa/__init__.py +53 -0
- meme_generator/memes/jiujiu/__init__.py +31 -0
- meme_generator/memes/jump/__init__.py +41 -0
- meme_generator/memes/kaleidoscope/__init__.py +73 -0
- meme_generator/memes/karyl_point/__init__.py +28 -0
- meme_generator/memes/keep_away/__init__.py +51 -0
- meme_generator/memes/keep_your_money/__init__.py +70 -0
- meme_generator/memes/kick_ball/__init__.py +38 -0
- meme_generator/memes/kirby_hammer/__init__.py +80 -0
- meme_generator/memes/kiss/__init__.py +43 -0
- meme_generator/memes/klee_eat/__init__.py +43 -0
- meme_generator/memes/knock/__init__.py +38 -0
- meme_generator/memes/kokona_seal/__init__.py +81 -0
- meme_generator/memes/konata_watch/__init__.py +36 -0
- meme_generator/memes/learn/__init__.py +47 -0
- meme_generator/memes/left_right_jump/__init__.py +105 -0
- meme_generator/memes/let_me_in/__init__.py +30 -0
- meme_generator/memes/lick_candy/__init__.py +131 -0
- meme_generator/memes/lim_x_0/__init__.py +43 -0
- meme_generator/memes/listen_music/__init__.py +34 -0
- meme_generator/memes/little_angel/__init__.py +57 -0
- meme_generator/memes/loading/__init__.py +44 -0
- meme_generator/memes/look_flat/__init__.py +79 -0
- meme_generator/memes/look_this_icon/__init__.py +48 -0
- meme_generator/memes/loop/__init__.py +108 -0
- meme_generator/memes/lost_dog/__init__.py +43 -0
- meme_generator/memes/love_you/__init__.py +34 -0
- meme_generator/memes/luotianyi_need/__init__.py +34 -0
- meme_generator/memes/luotianyi_say/__init__.py +44 -0
- meme_generator/memes/luoyonghao_say/__init__.py +44 -0
- meme_generator/memes/luxun_say/__init__.py +39 -0
- meme_generator/memes/mahiro_readbook/__init__.py +45 -0
- meme_generator/memes/maimai_awaken/__init__.py +34 -0
- meme_generator/memes/maimai_join/__init__.py +32 -0
- meme_generator/memes/make_friend/__init__.py +52 -0
- meme_generator/memes/marriage/__init__.py +35 -0
- meme_generator/memes/meteor/__init__.py +38 -0
- meme_generator/memes/mihoyo/__init__.py +33 -0
- meme_generator/memes/mourning/__init__.py +55 -0
- meme_generator/memes/murmur/__init__.py +36 -0
- meme_generator/memes/my_friend/__init__.py +96 -0
- meme_generator/memes/my_opinion/__init__.py +38 -0
- meme_generator/memes/my_wife/__init__.py +102 -0
- meme_generator/memes/nahida_bite/__init__.py +44 -0
- meme_generator/memes/name_generator/__init__.py +48 -0
- meme_generator/memes/need/__init__.py +30 -0
- meme_generator/memes/nekoha_holdsign/__init__.py +42 -0
- meme_generator/memes/nijika_holdsign/__init__.py +40 -0
- meme_generator/memes/no_response/__init__.py +26 -0
- meme_generator/memes/nokia/__init__.py +44 -0
- meme_generator/memes/not_call_me/__init__.py +37 -0
- meme_generator/memes/note_for_leave/__init__.py +107 -0
- meme_generator/memes/oshi_no_ko/__init__.py +75 -0
- meme_generator/memes/osu/__init__.py +42 -0
- meme_generator/memes/out/__init__.py +38 -0
- meme_generator/memes/overtime/__init__.py +26 -0
- meme_generator/memes/p5letter/__init__.py +158 -0
- meme_generator/memes/paint/__init__.py +26 -0
- meme_generator/memes/painter/__init__.py +30 -0
- meme_generator/memes/panda_dragon_figure/__init__.py +88 -0
- meme_generator/memes/pass_the_buck/__init__.py +44 -0
- meme_generator/memes/pat/__init__.py +37 -0
- meme_generator/memes/pepe_raise/__init__.py +51 -0
- meme_generator/memes/perfect/__init__.py +26 -0
- meme_generator/memes/petpet/__init__.py +67 -0
- meme_generator/memes/pinch/__init__.py +33 -0
- meme_generator/memes/pixelate/__init__.py +59 -0
- meme_generator/memes/pjsk/__init__.py +154 -0
- meme_generator/memes/plana_eat/__init__.py +32 -0
- meme_generator/memes/play/__init__.py +54 -0
- meme_generator/memes/play_baseball/__init__.py +53 -0
- meme_generator/memes/play_basketball/__init__.py +84 -0
- meme_generator/memes/play_game/__init__.py +54 -0
- meme_generator/memes/play_together/__init__.py +61 -0
- meme_generator/memes/police/__init__.py +68 -0
- meme_generator/memes/pornhub/__init__.py +41 -0
- meme_generator/memes/potato/__init__.py +26 -0
- meme_generator/memes/pound/__init__.py +38 -0
- meme_generator/memes/printing/__init__.py +39 -0
- meme_generator/memes/prpr/__init__.py +36 -0
- meme_generator/memes/psyduck/__init__.py +78 -0
- meme_generator/memes/punch/__init__.py +39 -0
- meme_generator/memes/pyramid/__init__.py +99 -0
- meme_generator/memes/raise_image/__init__.py +38 -0
- meme_generator/memes/raise_sign/__init__.py +43 -0
- meme_generator/memes/read_book/__init__.py +70 -0
- meme_generator/memes/remote_control/__init__.py +52 -0
- meme_generator/memes/repeat/__init__.py +70 -0
- meme_generator/memes/rip/__init__.py +38 -0
- meme_generator/memes/rip_angrily/__init__.py +27 -0
- meme_generator/memes/rip_clothes/__init__.py +38 -0
- meme_generator/memes/rise_dead/__init__.py +44 -0
- meme_generator/memes/roll/__init__.py +38 -0
- meme_generator/memes/rotate_3d/__init__.py +79 -0
- meme_generator/memes/rub/__init__.py +47 -0
- meme_generator/memes/run/__init__.py +40 -0
- meme_generator/memes/run_away/__init__.py +47 -0
- meme_generator/memes/safe_sense/__init__.py +48 -0
- meme_generator/memes/saimin_app/__init__.py +45 -0
- meme_generator/memes/scratch_head/__init__.py +40 -0
- meme_generator/memes/scratchcard/__init__.py +41 -0
- meme_generator/memes/scroll/__init__.py +61 -0
- meme_generator/memes/seal/__init__.py +38 -0
- meme_generator/memes/sekaiichi_kawaii/__init__.py +45 -0
- meme_generator/memes/shake_head/__init__.py +56 -0
- meme_generator/memes/shiroko_pero/__init__.py +41 -0
- meme_generator/memes/shock/__init__.py +31 -0
- meme_generator/memes/shutup/__init__.py +37 -0
- meme_generator/memes/sit_still/__init__.py +41 -0
- meme_generator/memes/slap/__init__.py +36 -0
- meme_generator/memes/slogan/__init__.py +49 -0
- meme_generator/memes/smash/__init__.py +36 -0
- meme_generator/memes/sold_out/__init__.py +40 -0
- meme_generator/memes/speechless/__init__.py +52 -0
- meme_generator/memes/sphere_rotate/__init__.py +166 -0
- meme_generator/memes/spider/__init__.py +50 -0
- meme_generator/memes/stare_at_you/__init__.py +49 -0
- meme_generator/memes/steam_message/__init__.py +101 -0
- meme_generator/memes/step_on/__init__.py +39 -0
- meme_generator/memes/stew/__init__.py +46 -0
- meme_generator/memes/stickman_dancing/__init__.py +62 -0
- meme_generator/memes/subject3/__init__.py +53 -0
- meme_generator/memes/suck/__init__.py +38 -0
- meme_generator/memes/support/__init__.py +26 -0
- meme_generator/memes/swirl_turn/__init__.py +41 -0
- meme_generator/memes/symmetric/__init__.py +134 -0
- meme_generator/memes/tankuku_raisesign/__init__.py +51 -0
- meme_generator/memes/taunt/__init__.py +30 -0
- meme_generator/memes/teach/__init__.py +50 -0
- meme_generator/memes/tease/__init__.py +61 -0
- meme_generator/memes/telescope/__init__.py +52 -0
- meme_generator/memes/thermometer_gun/__init__.py +61 -0
- meme_generator/memes/think_what/__init__.py +30 -0
- meme_generator/memes/this_chicken/__init__.py +49 -0
- meme_generator/memes/throw/__init__.py +35 -0
- meme_generator/memes/throw_gif/__init__.py +42 -0
- meme_generator/memes/thump/__init__.py +35 -0
- meme_generator/memes/thump_wildly/__init__.py +34 -0
- meme_generator/memes/tightly/__init__.py +38 -0
- meme_generator/memes/time_to_go/__init__.py +43 -0
- meme_generator/memes/together/__init__.py +49 -0
- meme_generator/memes/tom_tease/__init__.py +39 -0
- meme_generator/memes/tomb_yeah/__init__.py +33 -0
- meme_generator/memes/top_notch/__init__.py +37 -0
- meme_generator/memes/trance/__init__.py +31 -0
- meme_generator/memes/trolley/__init__.py +59 -0
- meme_generator/memes/turn/__init__.py +31 -0
- meme_generator/memes/twist/__init__.py +38 -0
- meme_generator/memes/universal/__init__.py +44 -0
- meme_generator/memes/upside_down/__init__.py +48 -0
- meme_generator/memes/vibrate/__init__.py +44 -0
- meme_generator/memes/wakeup/__init__.py +41 -0
- meme_generator/memes/wallpaper/__init__.py +35 -0
- meme_generator/memes/walnut_pad/__init__.py +32 -0
- meme_generator/memes/walnut_zoom/__init__.py +46 -0
- meme_generator/memes/washer/__init__.py +34 -0
- meme_generator/memes/wave/__init__.py +54 -0
- meme_generator/memes/wechat_pay/__init__.py +92 -0
- meme_generator/memes/what_I_want_to_do/__init__.py +30 -0
- meme_generator/memes/what_he_wants/__init__.py +52 -0
- meme_generator/memes/why_at_me/__init__.py +28 -0
- meme_generator/memes/why_have_hands/__init__.py +79 -0
- meme_generator/memes/windmill_turn/__init__.py +35 -0
- meme_generator/memes/wish_fail/__init__.py +37 -0
- meme_generator/memes/wooden_fish/__init__.py +29 -0
- meme_generator/memes/worship/__init__.py +33 -0
- meme_generator/memes/wujing/__init__.py +65 -0
- meme_generator/memes/yeshu/__init__.py +227 -0
- meme_generator/memes/you_dont_get/__init__.py +28 -0
- meme_generator/memes/you_should_call/__init__.py +54 -0
- meme_generator/memes/your_new_years_eve/__init__.py +34 -0
- meme_generator/memes/youtube/__init__.py +65 -0
- meme_generator/redis_cache.py +37 -0
- meme_generator/resources/images/emojis/emoji_u1f602.png +0 -0
- meme_generator/resources/images/emojis/emoji_u1f605.png +0 -0
- meme_generator/resources/images/emojis/emoji_u1f60f.png +0 -0
- meme_generator/resources/images/emojis/emoji_u1f62d.png +0 -0
- meme_generator/resources/images/emojis/emoji_u1f642.png +0 -0
- meme_generator/resources/images/emojis/emoji_u1f913.png +0 -0
- meme_generator/resources/images/emojis/emoji_u1f917.png +0 -0
- meme_generator/resources/images/emojis/emoji_u1f924.png +0 -0
- meme_generator/resources/images/emojis/emoji_u1f970.png +0 -0
- meme_generator/resources/images/emojis/emoji_u1f975.png +0 -0
- meme_generator/resources/images/icons/hot.png +0 -0
- meme_generator/resources/images/icons/image.png +0 -0
- meme_generator/resources/images/icons/image_disabled.png +0 -0
- meme_generator/resources/images/icons/new.png +0 -0
- meme_generator/resources/images/icons/text.png +0 -0
- meme_generator/resources/images/icons/text_disabled.png +0 -0
- meme_generator/storage.py +110 -0
- meme_generator/tags.py +146 -0
- meme_generator/utils.py +517 -0
- meme_generator/version.py +1 -0
- meme_generator_cli.py +36 -0
- meme_generator_next-0.2.0.dist-info/METADATA +169 -0
- meme_generator_next-0.2.0.dist-info/RECORD +320 -0
- meme_generator_next-0.2.0.dist-info/WHEEL +4 -0
- meme_generator_next-0.2.0.dist-info/entry_points.txt +3 -0
- meme_generator_next-0.2.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
import os
|
|
3
|
+
import sys
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
CONFIG_FILE_ENV = "MEME_GENERATOR_CONFIG_FILE"
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def _is_module_invocation(module_name: str) -> bool:
|
|
10
|
+
orig_argv = getattr(sys, "orig_argv", [])
|
|
11
|
+
try:
|
|
12
|
+
option_index = orig_argv.index("-m")
|
|
13
|
+
except ValueError:
|
|
14
|
+
return False
|
|
15
|
+
return (
|
|
16
|
+
option_index + 1 < len(orig_argv) and orig_argv[option_index + 1] == module_name
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def _apply_config_file_option() -> None:
|
|
21
|
+
if not _is_module_invocation("meme_generator.app"):
|
|
22
|
+
return
|
|
23
|
+
|
|
24
|
+
parser = argparse.ArgumentParser(add_help=False, allow_abbrev=False)
|
|
25
|
+
parser.add_argument("--config-file", dest="config_file")
|
|
26
|
+
args, remaining = parser.parse_known_args(sys.argv[1:])
|
|
27
|
+
|
|
28
|
+
if args.config_file is None:
|
|
29
|
+
return
|
|
30
|
+
if not args.config_file:
|
|
31
|
+
parser.error("--config-file must not be empty")
|
|
32
|
+
|
|
33
|
+
os.environ[CONFIG_FILE_ENV] = str(Path(args.config_file).expanduser())
|
|
34
|
+
sys.argv[:] = [sys.argv[0], *remaining]
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
_apply_config_file_option()
|
|
38
|
+
|
|
39
|
+
from meme_generator.config import meme_config as config
|
|
40
|
+
from meme_generator.manager import add_meme as add_meme
|
|
41
|
+
from meme_generator.manager import get_meme as get_meme
|
|
42
|
+
from meme_generator.manager import get_meme_keys as get_meme_keys
|
|
43
|
+
from meme_generator.manager import get_memes as get_memes
|
|
44
|
+
from meme_generator.manager import load_meme as load_meme
|
|
45
|
+
from meme_generator.manager import load_memes as load_memes
|
|
46
|
+
from meme_generator.meme import CommandShortcut as CommandShortcut
|
|
47
|
+
from meme_generator.meme import Meme as Meme
|
|
48
|
+
from meme_generator.meme import MemeArgsModel as MemeArgsModel
|
|
49
|
+
from meme_generator.meme import MemeArgsType as MemeArgsType
|
|
50
|
+
from meme_generator.meme import MemeParamsType as MemeParamsType
|
|
51
|
+
from meme_generator.meme import ParserArg as ParserArg
|
|
52
|
+
from meme_generator.meme import ParserOption as ParserOption
|
|
53
|
+
from meme_generator.version import __version__ as __version__
|
|
54
|
+
|
|
55
|
+
if config.meme.load_builtin_memes:
|
|
56
|
+
for path in (Path(__file__).parent / "memes").iterdir():
|
|
57
|
+
load_meme(f"meme_generator.memes.{path.name}")
|
|
58
|
+
for meme_dir in config.meme.meme_dirs:
|
|
59
|
+
load_memes(meme_dir)
|
meme_generator/app.py
ADDED
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
import json
|
|
2
|
+
from datetime import datetime
|
|
3
|
+
from typing import Any, Literal, Optional
|
|
4
|
+
|
|
5
|
+
import filetype
|
|
6
|
+
from fastapi import Depends, FastAPI, Form, HTTPException, Response, UploadFile
|
|
7
|
+
from pydantic import BaseModel, ValidationError
|
|
8
|
+
from redis.exceptions import RedisError
|
|
9
|
+
|
|
10
|
+
from meme_generator.compat import model_dump, model_json_schema, type_validate_python
|
|
11
|
+
from meme_generator.config import meme_config
|
|
12
|
+
from meme_generator.exception import (
|
|
13
|
+
ArgModelMismatch,
|
|
14
|
+
MemeGeneratorException,
|
|
15
|
+
NoSuchMeme,
|
|
16
|
+
RedisCacheError,
|
|
17
|
+
S3StorageError,
|
|
18
|
+
)
|
|
19
|
+
from meme_generator.log import LOGGING_CONFIG, setup_logger
|
|
20
|
+
from meme_generator.manager import get_meme, get_meme_keys, get_memes
|
|
21
|
+
from meme_generator.meme import CommandShortcut, Meme, MemeArgsModel, ParserOption
|
|
22
|
+
from meme_generator.redis_cache import (
|
|
23
|
+
cache_preview_url,
|
|
24
|
+
create_redis_client,
|
|
25
|
+
get_cached_preview_url,
|
|
26
|
+
)
|
|
27
|
+
from meme_generator.storage import upload_to_s3
|
|
28
|
+
from meme_generator.utils import MemeProperties, render_meme_list, run_sync
|
|
29
|
+
from meme_generator.version import __version__
|
|
30
|
+
|
|
31
|
+
app = FastAPI()
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class MemeArgsResponse(BaseModel):
|
|
35
|
+
args_model: dict[str, Any]
|
|
36
|
+
args_examples: list[dict[str, Any]]
|
|
37
|
+
parser_options: list[ParserOption]
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class MemeParamsResponse(BaseModel):
|
|
41
|
+
min_images: int
|
|
42
|
+
max_images: int
|
|
43
|
+
min_texts: int
|
|
44
|
+
max_texts: int
|
|
45
|
+
default_texts: list[str]
|
|
46
|
+
args_type: Optional[MemeArgsResponse] = None
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class MemeUrlResponse(BaseModel):
|
|
50
|
+
url: str
|
|
51
|
+
meme_key: str
|
|
52
|
+
object_key: str
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class MemePreviewUrlResponse(BaseModel):
|
|
56
|
+
url: str
|
|
57
|
+
meme_key: str
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
class MemeInfoResponse(BaseModel):
|
|
61
|
+
key: str
|
|
62
|
+
params_type: MemeParamsResponse
|
|
63
|
+
keywords: list[str]
|
|
64
|
+
shortcuts: list[CommandShortcut]
|
|
65
|
+
tags: set[str]
|
|
66
|
+
date_created: datetime
|
|
67
|
+
date_modified: datetime
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
async def generate_meme(
|
|
71
|
+
meme: Meme, images: list[bytes], texts: list[str], args: Any
|
|
72
|
+
) -> tuple[bytes, str]:
|
|
73
|
+
try:
|
|
74
|
+
result = await run_sync(meme)(images=images, texts=texts, args=model_dump(args))
|
|
75
|
+
except MemeGeneratorException as e:
|
|
76
|
+
raise HTTPException(status_code=e.status_code, detail=e.message)
|
|
77
|
+
|
|
78
|
+
content = result.getvalue()
|
|
79
|
+
media_type = str(filetype.guess_mime(content)) or "text/plain"
|
|
80
|
+
return content, media_type
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def register_router(meme: Meme):
|
|
84
|
+
if args_type := meme.params_type.args_type:
|
|
85
|
+
args_model = args_type.args_model
|
|
86
|
+
else:
|
|
87
|
+
args_model = MemeArgsModel
|
|
88
|
+
|
|
89
|
+
def args_checker(
|
|
90
|
+
args: Optional[str] = Form(default=json.dumps(model_dump(args_model()))),
|
|
91
|
+
):
|
|
92
|
+
if not args:
|
|
93
|
+
return MemeArgsModel()
|
|
94
|
+
try:
|
|
95
|
+
model = type_validate_python(args_model, json.loads(args))
|
|
96
|
+
except ValidationError as e:
|
|
97
|
+
e = ArgModelMismatch(str(e))
|
|
98
|
+
raise HTTPException(status_code=552, detail=e.message)
|
|
99
|
+
return model
|
|
100
|
+
|
|
101
|
+
@app.post(f"/memes/{meme.key}/")
|
|
102
|
+
async def _(
|
|
103
|
+
images: list[UploadFile] = [],
|
|
104
|
+
texts: list[str] = meme.params_type.default_texts,
|
|
105
|
+
args: args_model = Depends(args_checker), # type: ignore
|
|
106
|
+
):
|
|
107
|
+
imgs: list[bytes] = []
|
|
108
|
+
for image in images:
|
|
109
|
+
imgs.append(await image.read())
|
|
110
|
+
|
|
111
|
+
texts = [text for text in texts if text]
|
|
112
|
+
|
|
113
|
+
assert isinstance(args, args_model)
|
|
114
|
+
|
|
115
|
+
content, media_type = await generate_meme(meme, imgs, texts, args)
|
|
116
|
+
|
|
117
|
+
return Response(content=content, media_type=media_type)
|
|
118
|
+
|
|
119
|
+
@app.post(f"/memes/{meme.key}/url/")
|
|
120
|
+
async def _(
|
|
121
|
+
images: list[UploadFile] = [],
|
|
122
|
+
texts: list[str] = meme.params_type.default_texts,
|
|
123
|
+
args: args_model = Depends(args_checker), # type: ignore
|
|
124
|
+
):
|
|
125
|
+
imgs: list[bytes] = []
|
|
126
|
+
for image in images:
|
|
127
|
+
imgs.append(await image.read())
|
|
128
|
+
|
|
129
|
+
texts = [text for text in texts if text]
|
|
130
|
+
|
|
131
|
+
assert isinstance(args, args_model)
|
|
132
|
+
|
|
133
|
+
content, media_type = await generate_meme(meme, imgs, texts, args)
|
|
134
|
+
|
|
135
|
+
try:
|
|
136
|
+
upload_result = await run_sync(upload_to_s3)(
|
|
137
|
+
content=content,
|
|
138
|
+
media_type=media_type,
|
|
139
|
+
meme_key=meme.key,
|
|
140
|
+
config=meme_config.storage.s3,
|
|
141
|
+
upload_kind="result",
|
|
142
|
+
)
|
|
143
|
+
except S3StorageError as e:
|
|
144
|
+
raise HTTPException(status_code=e.status_code, detail=e.message)
|
|
145
|
+
|
|
146
|
+
return MemeUrlResponse(
|
|
147
|
+
url=upload_result.url,
|
|
148
|
+
meme_key=meme.key,
|
|
149
|
+
object_key=upload_result.object_key,
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
@app.get(f"/memes/{meme.key}/preview/url/")
|
|
153
|
+
async def _():
|
|
154
|
+
redis_config = meme_config.storage.redis
|
|
155
|
+
if not redis_config.enabled:
|
|
156
|
+
error = RedisCacheError(
|
|
157
|
+
"Redis 缓存未启用,请在 config.toml 中配置 storage.redis"
|
|
158
|
+
)
|
|
159
|
+
raise HTTPException(status_code=error.status_code, detail=error.message)
|
|
160
|
+
|
|
161
|
+
try:
|
|
162
|
+
redis_client = create_redis_client(redis_config)
|
|
163
|
+
except RedisCacheError as e:
|
|
164
|
+
raise HTTPException(status_code=e.status_code, detail=e.message)
|
|
165
|
+
|
|
166
|
+
try:
|
|
167
|
+
cached_url = await get_cached_preview_url(
|
|
168
|
+
redis_client, redis_config, meme.key
|
|
169
|
+
)
|
|
170
|
+
if cached_url:
|
|
171
|
+
return MemePreviewUrlResponse(url=cached_url, meme_key=meme.key)
|
|
172
|
+
|
|
173
|
+
try:
|
|
174
|
+
result = await run_sync(meme.generate_preview)()
|
|
175
|
+
except MemeGeneratorException as e:
|
|
176
|
+
raise HTTPException(status_code=e.status_code, detail=e.message)
|
|
177
|
+
|
|
178
|
+
content = result.getvalue()
|
|
179
|
+
media_type = str(filetype.guess_mime(content)) or "text/plain"
|
|
180
|
+
|
|
181
|
+
try:
|
|
182
|
+
upload_result = await run_sync(upload_to_s3)(
|
|
183
|
+
content=content,
|
|
184
|
+
media_type=media_type,
|
|
185
|
+
meme_key=meme.key,
|
|
186
|
+
config=meme_config.storage.s3,
|
|
187
|
+
upload_kind="preview",
|
|
188
|
+
)
|
|
189
|
+
except S3StorageError as e:
|
|
190
|
+
raise HTTPException(status_code=e.status_code, detail=e.message)
|
|
191
|
+
|
|
192
|
+
try:
|
|
193
|
+
await cache_preview_url(
|
|
194
|
+
redis_client, redis_config, meme.key, upload_result.url
|
|
195
|
+
)
|
|
196
|
+
except RedisError as e:
|
|
197
|
+
error = RedisCacheError(f"Redis 缓存写入失败:{e}")
|
|
198
|
+
raise HTTPException(status_code=error.status_code, detail=error.message)
|
|
199
|
+
|
|
200
|
+
return MemePreviewUrlResponse(url=upload_result.url, meme_key=meme.key)
|
|
201
|
+
except RedisError as e:
|
|
202
|
+
error = RedisCacheError(f"Redis 缓存读取失败:{e}")
|
|
203
|
+
raise HTTPException(status_code=error.status_code, detail=error.message)
|
|
204
|
+
finally:
|
|
205
|
+
await redis_client.aclose()
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
class MemeKeyWithProperties(BaseModel):
|
|
209
|
+
meme_key: str
|
|
210
|
+
disabled: bool = False
|
|
211
|
+
labels: list[Literal["new", "hot"]] = []
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
default_meme_list = [
|
|
215
|
+
MemeKeyWithProperties(meme_key=meme.key)
|
|
216
|
+
for meme in sorted(get_memes(), key=lambda meme: meme.key)
|
|
217
|
+
]
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
class RenderMemeListRequest(BaseModel):
|
|
221
|
+
meme_list: list[MemeKeyWithProperties] = default_meme_list
|
|
222
|
+
text_template: str = "{keywords}"
|
|
223
|
+
add_category_icon: bool = True
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
def register_routers():
|
|
227
|
+
@app.post("/memes/render_list")
|
|
228
|
+
def _(params: RenderMemeListRequest = RenderMemeListRequest()):
|
|
229
|
+
try:
|
|
230
|
+
meme_list = [
|
|
231
|
+
(
|
|
232
|
+
get_meme(p.meme_key),
|
|
233
|
+
MemeProperties(disabled=p.disabled, labels=p.labels),
|
|
234
|
+
)
|
|
235
|
+
for p in params.meme_list
|
|
236
|
+
]
|
|
237
|
+
except NoSuchMeme as e:
|
|
238
|
+
raise HTTPException(status_code=e.status_code, detail=e.message)
|
|
239
|
+
|
|
240
|
+
result = render_meme_list(
|
|
241
|
+
meme_list,
|
|
242
|
+
text_template=params.text_template,
|
|
243
|
+
add_category_icon=params.add_category_icon,
|
|
244
|
+
)
|
|
245
|
+
content = result.getvalue()
|
|
246
|
+
media_type = str(filetype.guess_mime(content)) or "text/plain"
|
|
247
|
+
return Response(content=content, media_type=media_type)
|
|
248
|
+
|
|
249
|
+
@app.get("/meme/version")
|
|
250
|
+
def _():
|
|
251
|
+
return __version__
|
|
252
|
+
|
|
253
|
+
@app.get("/memes/keys")
|
|
254
|
+
def _():
|
|
255
|
+
return get_meme_keys()
|
|
256
|
+
|
|
257
|
+
@app.get("/memes/{key}/info")
|
|
258
|
+
def _(key: str):
|
|
259
|
+
try:
|
|
260
|
+
meme = get_meme(key)
|
|
261
|
+
except NoSuchMeme as e:
|
|
262
|
+
raise HTTPException(status_code=e.status_code, detail=e.message)
|
|
263
|
+
|
|
264
|
+
args_type_response = None
|
|
265
|
+
if args_type := meme.params_type.args_type:
|
|
266
|
+
args_model = args_type.args_model
|
|
267
|
+
args_type_response = MemeArgsResponse(
|
|
268
|
+
args_model=model_json_schema(args_model),
|
|
269
|
+
args_examples=[
|
|
270
|
+
model_dump(example) for example in args_type.args_examples
|
|
271
|
+
],
|
|
272
|
+
parser_options=args_type.parser_options,
|
|
273
|
+
)
|
|
274
|
+
|
|
275
|
+
return MemeInfoResponse(
|
|
276
|
+
key=meme.key,
|
|
277
|
+
params_type=MemeParamsResponse(
|
|
278
|
+
min_images=meme.params_type.min_images,
|
|
279
|
+
max_images=meme.params_type.max_images,
|
|
280
|
+
min_texts=meme.params_type.min_texts,
|
|
281
|
+
max_texts=meme.params_type.max_texts,
|
|
282
|
+
default_texts=meme.params_type.default_texts,
|
|
283
|
+
args_type=args_type_response,
|
|
284
|
+
),
|
|
285
|
+
keywords=meme.keywords,
|
|
286
|
+
shortcuts=meme.shortcuts,
|
|
287
|
+
tags=meme.tags,
|
|
288
|
+
date_created=meme.date_created,
|
|
289
|
+
date_modified=meme.date_modified,
|
|
290
|
+
)
|
|
291
|
+
|
|
292
|
+
@app.get("/memes/{key}/preview")
|
|
293
|
+
async def _(key: str):
|
|
294
|
+
try:
|
|
295
|
+
meme = get_meme(key)
|
|
296
|
+
result = await run_sync(meme.generate_preview)()
|
|
297
|
+
except MemeGeneratorException as e:
|
|
298
|
+
raise HTTPException(status_code=e.status_code, detail=e.message)
|
|
299
|
+
|
|
300
|
+
content = result.getvalue()
|
|
301
|
+
media_type = str(filetype.guess_mime(content)) or "text/plain"
|
|
302
|
+
return Response(content=content, media_type=media_type)
|
|
303
|
+
|
|
304
|
+
for meme in sorted(get_memes(), key=lambda meme: meme.key):
|
|
305
|
+
register_router(meme)
|
|
306
|
+
|
|
307
|
+
|
|
308
|
+
def run_server():
|
|
309
|
+
import uvicorn
|
|
310
|
+
|
|
311
|
+
register_routers()
|
|
312
|
+
uvicorn.run(
|
|
313
|
+
app,
|
|
314
|
+
host=meme_config.server.host,
|
|
315
|
+
port=meme_config.server.port,
|
|
316
|
+
log_config=LOGGING_CONFIG,
|
|
317
|
+
)
|
|
318
|
+
|
|
319
|
+
|
|
320
|
+
if __name__ == "__main__":
|
|
321
|
+
setup_logger()
|
|
322
|
+
run_server()
|
meme_generator/cli.py
ADDED
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
import filetype
|
|
6
|
+
from arclet.alconna import (
|
|
7
|
+
Alconna,
|
|
8
|
+
Args,
|
|
9
|
+
CommandMeta,
|
|
10
|
+
MultiVar,
|
|
11
|
+
Option,
|
|
12
|
+
Subcommand,
|
|
13
|
+
TextFormatter,
|
|
14
|
+
)
|
|
15
|
+
from arclet.alconna.exceptions import SpecialOptionTriggered
|
|
16
|
+
from arclet.alconna.tools import RichConsoleFormatter
|
|
17
|
+
|
|
18
|
+
from meme_generator.app import run_server
|
|
19
|
+
from meme_generator.config import meme_config
|
|
20
|
+
from meme_generator.download import check_resources
|
|
21
|
+
from meme_generator.exception import MemeGeneratorException, NoSuchMeme
|
|
22
|
+
from meme_generator.log import setup_logger
|
|
23
|
+
from meme_generator.manager import get_meme, get_memes
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def construct_parser() -> Alconna:
|
|
27
|
+
sub_commands: list[Subcommand] = []
|
|
28
|
+
for meme in get_memes():
|
|
29
|
+
options: list[Option] = []
|
|
30
|
+
if args_type := meme.params_type.args_type:
|
|
31
|
+
for option in args_type.parser_options:
|
|
32
|
+
options.append(option.option())
|
|
33
|
+
sub_command = Subcommand(
|
|
34
|
+
meme.key,
|
|
35
|
+
*options,
|
|
36
|
+
Option(
|
|
37
|
+
"--images",
|
|
38
|
+
Args["images", MultiVar(str, "+")],
|
|
39
|
+
help_text="输入图片路径",
|
|
40
|
+
),
|
|
41
|
+
Option("--texts", Args["texts", MultiVar(str, "+")], help_text="输入文字"),
|
|
42
|
+
help_text="/".join(meme.keywords),
|
|
43
|
+
)
|
|
44
|
+
sub_commands.append(sub_command)
|
|
45
|
+
|
|
46
|
+
parser = Alconna(
|
|
47
|
+
"meme",
|
|
48
|
+
Option(
|
|
49
|
+
"--config-file",
|
|
50
|
+
Args["config_file", str],
|
|
51
|
+
help_text="指定 config.toml 文件路径",
|
|
52
|
+
),
|
|
53
|
+
Subcommand("list", alias=["ls"], help_text="查看表情列表"),
|
|
54
|
+
Subcommand(
|
|
55
|
+
"info", Args["key#表情名", str], alias=["show"], help_text="查看表情详情"
|
|
56
|
+
),
|
|
57
|
+
Subcommand("preview", Args["key#表情名", str], help_text="生成表情预览"),
|
|
58
|
+
Subcommand("generate", *sub_commands, alias=["make"], help_text="制作表情"),
|
|
59
|
+
Subcommand(
|
|
60
|
+
"run",
|
|
61
|
+
Option(
|
|
62
|
+
"--config-file",
|
|
63
|
+
Args["config_file", str],
|
|
64
|
+
help_text="指定 config.toml 文件路径",
|
|
65
|
+
),
|
|
66
|
+
alias=["start"],
|
|
67
|
+
help_text="启动 web server",
|
|
68
|
+
),
|
|
69
|
+
Subcommand(
|
|
70
|
+
"download",
|
|
71
|
+
Option("--url", Args["url", str], help_text="指定资源链接"),
|
|
72
|
+
help_text="下载内置表情图片",
|
|
73
|
+
),
|
|
74
|
+
meta=CommandMeta(
|
|
75
|
+
description="表情包生成器",
|
|
76
|
+
example="meme generate petpet --images /path/to/image/file",
|
|
77
|
+
),
|
|
78
|
+
formatter_type=RichConsoleFormatter,
|
|
79
|
+
)
|
|
80
|
+
return parser
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def list_memes() -> str:
|
|
84
|
+
memes = sorted(get_memes(), key=lambda meme: meme.key)
|
|
85
|
+
return "\n".join(
|
|
86
|
+
f"{i}. {meme.key} ({'/'.join(meme.keywords)})"
|
|
87
|
+
for i, meme in enumerate(memes, start=1)
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def meme_info(key: str) -> str:
|
|
92
|
+
try:
|
|
93
|
+
meme = get_meme(key)
|
|
94
|
+
except NoSuchMeme:
|
|
95
|
+
return f'表情 "{key}" 不存在!'
|
|
96
|
+
|
|
97
|
+
keywords = "、".join([f'"{keyword}"' for keyword in meme.keywords])
|
|
98
|
+
shortcuts = "、".join(
|
|
99
|
+
[f'"{shortcut.humanized or shortcut.key}"' for shortcut in meme.shortcuts]
|
|
100
|
+
)
|
|
101
|
+
tags = "、".join([f'"{tag}"' for tag in sorted(meme.tags)])
|
|
102
|
+
|
|
103
|
+
image_num = f"{meme.params_type.min_images}"
|
|
104
|
+
if meme.params_type.max_images > meme.params_type.min_images:
|
|
105
|
+
image_num += f" ~ {meme.params_type.max_images}"
|
|
106
|
+
|
|
107
|
+
text_num = f"{meme.params_type.min_texts}"
|
|
108
|
+
if meme.params_type.max_texts > meme.params_type.min_texts:
|
|
109
|
+
text_num += f" ~ {meme.params_type.max_texts}"
|
|
110
|
+
|
|
111
|
+
default_texts = ", ".join([f'"{text}"' for text in meme.params_type.default_texts])
|
|
112
|
+
|
|
113
|
+
args_info = ""
|
|
114
|
+
if args_type := meme.params_type.args_type:
|
|
115
|
+
formater = TextFormatter()
|
|
116
|
+
for option in args_type.parser_options:
|
|
117
|
+
opt = option.option()
|
|
118
|
+
alias_text = (
|
|
119
|
+
" ".join(opt.requires)
|
|
120
|
+
+ (" " if opt.requires else "")
|
|
121
|
+
+ "│".join(sorted(opt.aliases, key=len))
|
|
122
|
+
)
|
|
123
|
+
args_info += (
|
|
124
|
+
f"\n * {alias_text}{opt.separators[0]}"
|
|
125
|
+
f"{formater.parameters(opt.args)} {opt.help_text}"
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
return (
|
|
129
|
+
f"表情名:{meme.key}\n"
|
|
130
|
+
+ f"关键词:{keywords}\n"
|
|
131
|
+
+ (f"快捷指令:{shortcuts}\n" if shortcuts else "")
|
|
132
|
+
+ (f"标签:{tags}\n" if tags else "")
|
|
133
|
+
+ f"需要图片数目:{image_num}\n"
|
|
134
|
+
+ f"需要文字数目:{text_num}\n"
|
|
135
|
+
+ (f"默认文字:[{default_texts}]\n" if default_texts else "")
|
|
136
|
+
+ (f"其他参数:{args_info}" if args_info else "")
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def generate_meme_preview(key: str) -> str:
|
|
141
|
+
try:
|
|
142
|
+
meme = get_meme(key)
|
|
143
|
+
except NoSuchMeme:
|
|
144
|
+
return f'表情 "{key}" 不存在!'
|
|
145
|
+
|
|
146
|
+
try:
|
|
147
|
+
result = meme.generate_preview()
|
|
148
|
+
content = result.getvalue()
|
|
149
|
+
ext = filetype.guess_extension(content)
|
|
150
|
+
filename = f"result.{ext}"
|
|
151
|
+
with open(filename, "wb") as f:
|
|
152
|
+
f.write(content)
|
|
153
|
+
return f'表情制作成功!生成的表情文件为 "{filename}"'
|
|
154
|
+
except MemeGeneratorException as e:
|
|
155
|
+
return str(e)
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
def generate_meme(
|
|
159
|
+
key: str, images: list[str], texts: list[str], args: dict[str, Any]
|
|
160
|
+
) -> str:
|
|
161
|
+
try:
|
|
162
|
+
meme = get_meme(key)
|
|
163
|
+
except NoSuchMeme:
|
|
164
|
+
return f'表情 "{key}" 不存在!'
|
|
165
|
+
|
|
166
|
+
for image in images:
|
|
167
|
+
if not Path(image).exists():
|
|
168
|
+
return f'图片路径 "{image}" 不存在!'
|
|
169
|
+
|
|
170
|
+
try:
|
|
171
|
+
result = meme(images=images, texts=texts, args=args)
|
|
172
|
+
content = result.getvalue()
|
|
173
|
+
ext = filetype.guess_extension(content)
|
|
174
|
+
filename = f"result.{ext}"
|
|
175
|
+
with open(filename, "wb") as f:
|
|
176
|
+
f.write(content)
|
|
177
|
+
return f'表情制作成功!生成的表情文件为 "{filename}"'
|
|
178
|
+
except MemeGeneratorException as e:
|
|
179
|
+
return str(e)
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
def main():
|
|
183
|
+
setup_logger()
|
|
184
|
+
parser = construct_parser()
|
|
185
|
+
result = parser()
|
|
186
|
+
|
|
187
|
+
if not result.matched:
|
|
188
|
+
if not isinstance(result.error_info, SpecialOptionTriggered):
|
|
189
|
+
print(result.error_info) # noqa: T201
|
|
190
|
+
return
|
|
191
|
+
|
|
192
|
+
if not result.subcommands:
|
|
193
|
+
print(parser.get_help()) # noqa: T201
|
|
194
|
+
|
|
195
|
+
for subcommand, sub_result in result.subcommands.items():
|
|
196
|
+
if subcommand == "list":
|
|
197
|
+
print(list_memes()) # noqa: T201
|
|
198
|
+
|
|
199
|
+
elif subcommand == "info":
|
|
200
|
+
key = str(sub_result.args["key"])
|
|
201
|
+
print(meme_info(key)) # noqa: T201
|
|
202
|
+
|
|
203
|
+
elif subcommand == "preview":
|
|
204
|
+
key = str(sub_result.args["key"])
|
|
205
|
+
print(generate_meme_preview(key)) # noqa: T201
|
|
206
|
+
|
|
207
|
+
elif subcommand == "generate":
|
|
208
|
+
for key, subsub_result in sub_result.subcommands.items():
|
|
209
|
+
images = (
|
|
210
|
+
list(subsub_result.options["images"].args["images"])
|
|
211
|
+
if "images" in subsub_result.options
|
|
212
|
+
else []
|
|
213
|
+
)
|
|
214
|
+
texts = (
|
|
215
|
+
list(subsub_result.options["texts"].args["texts"])
|
|
216
|
+
if "texts" in subsub_result.options
|
|
217
|
+
else []
|
|
218
|
+
)
|
|
219
|
+
options = subsub_result.options
|
|
220
|
+
options.pop("images", None)
|
|
221
|
+
options.pop("texts", None)
|
|
222
|
+
args = {}
|
|
223
|
+
for option, option_result in options.items():
|
|
224
|
+
if option_result.value is None:
|
|
225
|
+
args.update(option_result.args)
|
|
226
|
+
else:
|
|
227
|
+
args[option] = option_result.value
|
|
228
|
+
print(generate_meme(key, images, texts, args)) # noqa: T201
|
|
229
|
+
|
|
230
|
+
elif subcommand == "run":
|
|
231
|
+
run_server()
|
|
232
|
+
|
|
233
|
+
elif subcommand == "download":
|
|
234
|
+
if "url" in sub_result.options:
|
|
235
|
+
url = sub_result.options["url"].args["url"]
|
|
236
|
+
meme_config.resource.resource_url = url
|
|
237
|
+
loop = asyncio.new_event_loop()
|
|
238
|
+
loop.run_until_complete(check_resources())
|