diff --git a/.gitattributes b/.gitattributes index a6344aac8c09253b3b630fb776ae94478aa0275b..d1c7ae77f38ccac67e713ea52de92108ed71f354 100644 --- a/.gitattributes +++ b/.gitattributes @@ -33,3 +33,26 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text *.zip filter=lfs diff=lfs merge=lfs -text *.zst filter=lfs diff=lfs merge=lfs -text *tfevents* filter=lfs diff=lfs merge=lfs -text +docs/images/bocchi_draft.gif filter=lfs diff=lfs merge=lfs -text +docs/images/caoshen_bite.gif filter=lfs diff=lfs merge=lfs -text +docs/images/confuse.gif filter=lfs diff=lfs merge=lfs -text +docs/images/karyl_point.png filter=lfs diff=lfs merge=lfs -text +docs/images/kirby_hammer_instance0.gif filter=lfs diff=lfs merge=lfs -text +docs/images/kirby_hammer_instance1.gif filter=lfs diff=lfs merge=lfs -text +docs/images/kirby_hammer.gif filter=lfs diff=lfs merge=lfs -text +meme_generator/memes/anti_kidnap/images/0.png filter=lfs diff=lfs merge=lfs -text +meme_generator/memes/dinosaur/images/0.png filter=lfs diff=lfs merge=lfs -text +meme_generator/memes/dog_of_vtb/images/0.png filter=lfs diff=lfs merge=lfs -text +meme_generator/memes/gif_subtitle/images/maikease.gif filter=lfs diff=lfs merge=lfs -text +meme_generator/memes/gif_subtitle/images/qiegewala.gif filter=lfs diff=lfs merge=lfs -text +meme_generator/memes/gif_subtitle/images/shuifandui.gif filter=lfs diff=lfs merge=lfs -text +meme_generator/memes/gif_subtitle/images/wangjingze.gif filter=lfs diff=lfs merge=lfs -text +meme_generator/memes/gif_subtitle/images/weisuoyuwei.gif filter=lfs diff=lfs merge=lfs -text +meme_generator/memes/mourning/images/0.png filter=lfs diff=lfs merge=lfs -text +resources/fonts/FZKATJW.ttf filter=lfs diff=lfs merge=lfs -text +resources/fonts/FZSEJW.ttf filter=lfs diff=lfs merge=lfs -text +resources/fonts/FZSJ-QINGCRJ.ttf filter=lfs diff=lfs merge=lfs -text +resources/fonts/FZXS14.ttf filter=lfs diff=lfs merge=lfs -text +resources/fonts/HiraginoMin-W5-90-RKSJ-H-2.ttc filter=lfs diff=lfs merge=lfs -text +resources/fonts/NotoSansSC-Regular.otf filter=lfs diff=lfs merge=lfs -text +resources/fonts/NotoSerifSC-Regular.otf filter=lfs diff=lfs merge=lfs -text diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml new file mode 100644 index 0000000000000000000000000000000000000000..ee98bca4244e3f4914c639e465835073a2ebc699 --- /dev/null +++ b/.github/workflows/docker.yml @@ -0,0 +1,66 @@ +name: Build docker image + +on: + push: + branches: + - "main" + tags: + - "v*" + workflow_dispatch: + +jobs: + push_to_registry: + name: Push Docker image to GitHub Packages + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + steps: + - name: Check out the repo + uses: actions/checkout@v3 + + - name: Setup Docker + uses: docker/setup-buildx-action@v2 + + - name: Login to Docker Hub + uses: docker/login-action@v2 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Login to Github Container Registry + uses: docker/login-action@v2 + with: + registry: ghcr.io + username: ${{ github.repository_owner }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Generate Tags + uses: docker/metadata-action@v4 + id: metadata + with: + images: | + ${{ github.repository }} + ghcr.io/${{ github.repository }} + tags: | + type=ref,event=branch + type=ref,event=pr + type=semver,pattern={{version}} + + - name: Build and Publish + uses: docker/build-push-action@v3 + with: + context: . + platforms: linux/amd64,linux/arm64 + push: true + tags: ${{ steps.metadata.outputs.tags }} + labels: ${{ steps.metadata.outputs.labels }} + cache-from: type=gha + cache-to: type=gha,mode=max + + - name: Docker Hub Description + uses: peter-evans/dockerhub-description@v3 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_PASSWORD }} + readme-filepath: ./docs/docker.md diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 0000000000000000000000000000000000000000..e13be4ec6c5db77511dec8e26e763b4a45858527 --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,17 @@ +name: publish + +on: + push: + tags: + - v* + +jobs: + release: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + + - name: Publish python package + uses: JRubics/poetry-publish@v1.16 + with: + pypi_token: ${{ secrets.PYPI_TOKEN }} diff --git a/.github/workflows/update.yml b/.github/workflows/update.yml new file mode 100644 index 0000000000000000000000000000000000000000..650573df6ff8ee93838ab2cab9a3e343791c2eac --- /dev/null +++ b/.github/workflows/update.yml @@ -0,0 +1,47 @@ +name: Update resource_list.json & memes.md + +on: + push: + branches: + - "main" + paths: + - "meme_generator/memes/**" + workflow_dispatch: + +jobs: + update-and-push: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + + - name: Setup python + uses: actions/setup-python@v3 + with: + python-version: "3.10" + + - name: Setup Poetry + uses: Gr1N/setup-poetry@v8 + + - name: Install dependencies + run: poetry install + + - name: Install fonts + run: | + sudo apt install fonts-noto-cjk fonts-noto-color-emoji + sudo locale-gen zh_CN zh_CN.UTF-8 + sudo update-locale LC_ALL=zh_CN.UTF-8 LANG=zh_CN.UTF-8 + sudo mkdir /usr/share/fonts/myfonts + sudo cp resources/fonts/* /usr/share/fonts/myfonts/ + fc-cache -fv + + - name: Update resource_list.json + run: poetry run python resources/update_list.py + + - name: Update memes.md + run: poetry run python docs/update_doc.py + + - name: Commit & Push changes + uses: actions-js/push@master + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + branch: "main" diff --git a/.github/workflows/update_poetry_lock.yml b/.github/workflows/update_poetry_lock.yml new file mode 100644 index 0000000000000000000000000000000000000000..db56efbd62ff189ccd2b7c30fe2d325a3f6e4cb9 --- /dev/null +++ b/.github/workflows/update_poetry_lock.yml @@ -0,0 +1,32 @@ +name: Update poetry.lock + +on: + push: + branches: + - "main" + paths: + - "pyproject.toml" + workflow_dispatch: + +jobs: + update-and-push: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + + - name: Setup python + uses: actions/setup-python@v3 + with: + python-version: "3.10" + + - name: Setup Poetry + uses: Gr1N/setup-poetry@v8 + + - name: Update poetry.lock + run: poetry update --lock + + - name: Commit & Push changes + uses: actions-js/push@master + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + branch: "main" diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..7320547895d616a132efd9bd18d1a182357d50fb --- /dev/null +++ b/.gitignore @@ -0,0 +1,10 @@ +*__pycache__/ +dist/ +.vscode/ +.idea/ +venv/ +.venv/ + +result.png +result.jpg +result.gif diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000000000000000000000000000000000000..c7123e76eaae968535af9071fcd087e48dfc634f --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,26 @@ +default_install_hook_types: [pre-commit, prepare-commit-msg] +ci: + autofix_commit_msg: ":rotating_light: auto fix by pre-commit hooks" + autofix_prs: true + autoupdate_branch: master + autoupdate_schedule: monthly + autoupdate_commit_msg: ":arrow_up: auto update by pre-commit hooks" +repos: + - repo: https://github.com/hadialqattan/pycln + rev: v2.1.3 + hooks: + - id: pycln + args: [--config, pyproject.toml] + + - repo: https://github.com/pycqa/isort + rev: 5.12.0 + hooks: + - id: isort + stages: [commit] + + - repo: https://github.com/psf/black + rev: 23.1.0 + hooks: + - id: black + stages: [commit] + diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..8faaba12374cbe6b1d47e3290d57d3da886eff77 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,49 @@ +FROM python:3.10 as tmp + +WORKDIR /tmp + +ENV PATH="${PATH}:/root/.local/bin" + +COPY ./pyproject.toml ./poetry.lock* /tmp/ +RUN pip install poetry \ + && poetry config virtualenvs.in-project true \ + && poetry install --only main --no-interaction --no-ansi + +FROM python:3.10-slim as app + +WORKDIR /app + +EXPOSE 7860 + +VOLUME /data + +COPY --from=tmp /tmp/.venv /app/.venv + +COPY ./resources/fonts/* /usr/share/fonts/meme-fonts/ +RUN apt-get update \ + && apt-get install -y --no-install-recommends locales fontconfig fonts-noto-cjk fonts-noto-color-emoji gettext \ + && localedef -i zh_CN -c -f UTF-8 -A /usr/share/locale/locale.alias zh_CN.UTF-8 \ + && fc-cache -fv \ + && apt-get purge -y --auto-remove \ + && rm -rf /var/lib/apt/lists/* + +ENV TZ=Asia/Shanghai \ + LC_ALL=zh_CN.UTF-8 \ + PATH="/app/.venv/bin:${PATH}" \ + VIRTUAL_ENV="/app/.venv" \ + LOAD_BUILTIN_MEMES=true \ + MEME_DIRS="[\"/data/memes\"]" \ + MEME_DISABLED_LIST="[]" \ + GIF_MAX_SIZE=10.0 \ + GIF_MAX_FRAMES=100 \ + BAIDU_TRANS_APPID="" \ + BAIDU_TRANS_APIKEY="" \ + LOG_LEVEL="INFO" + +COPY ./meme_generator /app/meme_generator + +COPY ./docker/config.toml.template /app/config.toml.template +COPY ./docker/start.sh /app/start.sh +RUN chmod +x /app/start.sh + +CMD ["/app/start.sh"] diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..90fe3197451f643298a4ef2a112f6a6c5b1f859b --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 MeetWq + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/docker/config.toml.template b/docker/config.toml.template new file mode 100644 index 0000000000000000000000000000000000000000..6c8c228abf1ecdb71392c0a7feb1de639a515cbd --- /dev/null +++ b/docker/config.toml.template @@ -0,0 +1,19 @@ +[meme] +load_builtin_memes = $LOAD_BUILTIN_MEMES +meme_dirs = $MEME_DIRS +meme_disabled_list = $MEME_DISABLED_LIST + +[gif] +gif_max_size = $GIF_MAX_SIZE +gif_max_frames = $GIF_MAX_FRAMES + +[translate] +baidu_trans_appid = "$BAIDU_TRANS_APPID" +baidu_trans_apikey = "$BAIDU_TRANS_APIKEY" + +[server] +host = "0.0.0.0" +port = 2233 + +[log] +log_level = "$LOG_LEVEL" diff --git a/docker/start.sh b/docker/start.sh new file mode 100644 index 0000000000000000000000000000000000000000..14748f843f0e205213b6a71209290a5cdd8a15a3 --- /dev/null +++ b/docker/start.sh @@ -0,0 +1,7 @@ +#! /usr/bin/env bash + +mkdir -p ~/.config/meme_generator + +envsubst < /app/config.toml.template > ~/.config/meme_generator/config.toml + +exec python -m meme_generator.app diff --git a/docs/develop.md b/docs/develop.md new file mode 100644 index 0000000000000000000000000000000000000000..89e8641e1b1c0abbcd88ddf31d64be407d1c02e4 --- /dev/null +++ b/docs/develop.md @@ -0,0 +1,135 @@ +# 新表情编写指北 + +## 表情注册 + +meme-generator 会以包的形式加载表情,通过 `add_meme` 函数来“注册”一个表情 + +以 `petpet` 表情为例,文件结构如下: + +``` +meme_generator/memes/petpet +├── __init__.py # 表情制作程序 +└── images # 表情需要的图片文件 + ├── 0.png + ├── 1.png + ├── 2.png + ├── 3.png + └── 4.png +``` + +在不考虑额外参数的情况下,`petpet` 表情的 `__init__.py` 编写如下: + +```python +from typing import List +from pathlib import Path +from pil_utils import BuildImage +from PIL.Image import Image as IMG + +from meme_generator.utils import save_gif +from meme_generator import add_meme + + +img_dir = Path(__file__).parent / "images" + + +def petpet(images: List[BuildImage], texts, args): + """表情制作函数 + + 函数会接收 3 个参数: + - `images`: 传入的图片列表,类型为 `pil_utils.BuildImage` + - `texts`: 传入的文字列表,类型为 `str` + - `args`: 其他参数,类型为 `meme_generator.meme.MemeArgsModel` + """ + img = images[0].convert("RGBA").square() + frames: List[IMG] = [] + locs = [ + (14, 20, 98, 98), + (12, 33, 101, 85), + (8, 40, 110, 76), + (10, 33, 102, 84), + (12, 20, 98, 98), + ] + for i in range(5): + hand = BuildImage.open(img_dir / f"{i}.png") + frame = BuildImage.new("RGBA", hand.size, (255, 255, 255, 0)) + x, y, w, h = locs[i] + frame.paste(img.resize((w, h)), (x, y), alpha=True) + frame.paste(hand, alpha=True) + frames.append(frame.image) + return save_gif(frames, 0.06) + + +add_meme( + "petpet", # 表情唯一名 + petpet, # 表情制作函数 + min_images=1, # 至少需要 1 张图片 + max_images=1, # 另有 `min_texts` 和 `max_texts` 选项来控制传入文字的数量 + keywords=["摸", "摸摸", "摸头", "rua"], # 关键词,填写言简意赅的词语,用于展示表情含义、方便聊天Bot调用等 +) +``` + +通常情况下,建议每个表情一个文件夹,表情所需的图片文件等都放置于该文件夹中,方便增删表情 + +也可以一个文件中注册多个表情,如:[gif_subtitle](../meme_generator/memes/gif_subtitle/__init__.py) + + +## 参数定义 + +部分表情需要额外的参数。表情参数的类型定义如下: + +```python +@dataclass +class MemeArgsType: + parser: MemeArgsParser # 参数解析器,将命令行形式的文本解析为字典形式,方便通过命令行使用 + model: Type[MemeArgsModel] # 参数模型,用于验证字典形式的参数,并传入表情制作函数 + instances: List[MemeArgsModel] = field(default_factory=list) # 可选,参数模型示例,推荐填写,方便生成不同参数下的预览图 +``` + +以 `petpet` 表情为例,需要定义一个控制图片是否变为圆形的参数 `circle` + +可以定义如下的 `pydantic` 模型: + +```python +from pydantic import Field +from meme_generator import MemeArgsModel + +class Model(MemeArgsModel): + circle: bool = Field(False, description="是否将图片变为圆形") +``` + +定义参数时推荐使用 `Field` 定义默认值,可以定义 `description` 描述参数含义,方便生成文档 + +同时定义如下的参数解析器: + +```python +from meme_generator import MemeArgsParser + +parser = MemeArgsParser(prefix_chars="-/") +parser.add_argument("--circle", "/圆", action="store_true", help="是否将图片变为圆形") +``` + +以上参数解析器可以将形如 `["--circle"]` 的参数列表解析为 `{"circle": true}` 的形式,继而通过 `pydantic` 模型验证 + +推荐在定义选项时添加自然语言风格的别名,如 `/圆`,这样可以方便聊天机器人等场合调用,比如可以解析 `摸头 /圆` 这样的文本 + +定义好上述的 `parser` 和 `Model` 后,需要在 `add_meme` 时传入: + +```python +add_meme( + "petpet", + petpet, + min_images=1, + max_images=1, + args_type=MemeArgsType( + parser, + Model, + [ + Model(circle=False), + Model(circle=True), + ], + ), + keywords=["摸", "摸摸", "摸头", "rua"], +) +``` + +这里传入了 `circle=False` 和 `circle=True` 两个模型实例,可以在生成文档时生成不同参数时的预览图,效果如 [memes.md](memes.md#petpet) 所示 diff --git a/docs/docker.md b/docs/docker.md new file mode 100644 index 0000000000000000000000000000000000000000..009ee8ca410293773e8e9acf97d0c0083c987585 --- /dev/null +++ b/docs/docker.md @@ -0,0 +1,56 @@ +https://github.com/MeetWq/meme-generator + +## Docker部署 + +### 运行 + +```shell +docker run -d \ + --name=meme-generator \ + -p 2233:2233 \ + --restart always \ + meetwq/meme-generator:latest +``` + +运行后可通过 api 方式调用 + + +### 环境变量 + +| 变量名 | 默认值 | 说明 | +| --- | --- | --- | +| `MEME_DIRS` | `'["/data/memes"]'` | 额外表情路径 | +| `MEME_DISABLED_LIST` | `'[]'` | 禁用表情列表 | +| `GIF_MAX_SIZE` | `10.0` | 限制生成的 gif 文件大小 | +| `GIF_MAX_FRAMES` | `100` | 限制生成的 gif 文件帧数 | +| `BAIDU_TRANS_APPID` | `''` | 百度翻译 appid | +| `BAIDU_TRANS_APIKEY` | `''` | 百度翻译 apikey | +| `LOG_LEVEL` | `'INFO'` | 日志等级 | + + +### 加载额外表情 + +可通过 `MEME_DIRS` 环境变量指定额外表情路径,默认为 `["/data/memes"]` + +可将 docker 路径 `/data` 映射到本地路径 `` + +将额外表情放置于 `/memes` 即可 + + +完整的运行示例: + +```shell +docker run -d \ + --name=meme-generator \ + -p 2233:2233 \ + --restart always \ + -v :/data \ + -e MEME_DIRS='["/data/memes"]' \ + -e MEME_DISABLED_LIST='[]' \ + -e GIF_MAX_SIZE=10.0 \ + -e GIF_MAX_FRAMES=100 \ + -e BAIDU_TRANS_APPID= \ + -e BAIDU_TRANS_APIKEY= \ + -e LOG_LEVEL='INFO' \ + meetwq/meme-generator:main +``` diff --git a/docs/examples/avatar.jpg b/docs/examples/avatar.jpg new file mode 100644 index 0000000000000000000000000000000000000000..bd189726e7d832e0e89e74d0c300f265d693d7bd Binary files /dev/null and b/docs/examples/avatar.jpg differ diff --git a/docs/examples/test_api.py b/docs/examples/test_api.py new file mode 100644 index 0000000000000000000000000000000000000000..b3beaf6a5fdf0b82fd71583c4043a261b176cdb3 --- /dev/null +++ b/docs/examples/test_api.py @@ -0,0 +1,23 @@ +import asyncio +import json + +import httpx + + +async def main(): + files = [("images", open("avatar.jpg", "rb"))] + texts = [] + args = {"circle": True} + data = {"texts": texts, "args": json.dumps(args)} + + url = "http://127.0.0.1:2233/memes/petpet/" + async with httpx.AsyncClient() as client: + resp = await client.post(url, files=files, data=data) + + with open("result.gif", "wb") as f: + f.write(resp.content) + + +if __name__ == "__main__": + loop = asyncio.new_event_loop() + loop.run_until_complete(main()) diff --git a/docs/examples/test_meme.py b/docs/examples/test_meme.py new file mode 100644 index 0000000000000000000000000000000000000000..a6ef6625e8f0edaffb9413a6c46f1563ba97e905 --- /dev/null +++ b/docs/examples/test_meme.py @@ -0,0 +1,16 @@ +import asyncio + +from meme_generator import get_meme + + +async def main(): + meme = get_meme("petpet") + result = await meme(images=["avatar.jpg"], texts=[], args={"circle": True}) + + with open("result.gif", "wb") as f: + f.write(result.getvalue()) + + +if __name__ == "__main__": + loop = asyncio.new_event_loop() + loop.run_until_complete(main()) diff --git a/docs/images/5000choyen.jpg b/docs/images/5000choyen.jpg new file mode 100644 index 0000000000000000000000000000000000000000..a941fc291de9b9b8e3371060553d5c809e38da14 Binary files /dev/null and b/docs/images/5000choyen.jpg differ diff --git a/docs/images/acg_entrance.jpg b/docs/images/acg_entrance.jpg new file mode 100644 index 0000000000000000000000000000000000000000..8c4e7bf59aebaa3fb2a26de30a1016a20282c437 Binary files /dev/null and b/docs/images/acg_entrance.jpg differ diff --git a/docs/images/add_chaos.jpg b/docs/images/add_chaos.jpg new file mode 100644 index 0000000000000000000000000000000000000000..b1a0600d745b7551e5a2a8788c1d9e76b96175e8 Binary files /dev/null and b/docs/images/add_chaos.jpg differ diff --git a/docs/images/addiction.jpg b/docs/images/addiction.jpg new file mode 100644 index 0000000000000000000000000000000000000000..cb3ec1b1e7dd40b07b16cbe311d108cb03c5bc85 Binary files /dev/null and b/docs/images/addiction.jpg differ diff --git a/docs/images/alike.jpg b/docs/images/alike.jpg new file mode 100644 index 0000000000000000000000000000000000000000..e07689d825eb50999c5d0850a4047551f476b0fc Binary files /dev/null and b/docs/images/alike.jpg differ diff --git a/docs/images/always.jpg b/docs/images/always.jpg new file mode 100644 index 0000000000000000000000000000000000000000..19bf8c4504f8cc555f9e3d2933db33f682e951cd Binary files /dev/null and b/docs/images/always.jpg differ diff --git a/docs/images/always_instance0.jpg b/docs/images/always_instance0.jpg new file mode 100644 index 0000000000000000000000000000000000000000..b1bfb4167b2ae8935315b55fdb10a2f97ed26977 Binary files /dev/null and b/docs/images/always_instance0.jpg differ diff --git a/docs/images/always_instance1.jpg b/docs/images/always_instance1.jpg new file mode 100644 index 0000000000000000000000000000000000000000..e2e3e04519452bf6f4aa9e56aa67e27ee9ab904c Binary files /dev/null and b/docs/images/always_instance1.jpg differ diff --git a/docs/images/always_instance2.gif b/docs/images/always_instance2.gif new file mode 100644 index 0000000000000000000000000000000000000000..fa5b22d71e7059bcfb9b5c2f5ed88d7e1af69722 Binary files /dev/null and b/docs/images/always_instance2.gif differ diff --git a/docs/images/always_like.jpg b/docs/images/always_like.jpg new file mode 100644 index 0000000000000000000000000000000000000000..6db2fd2f0f928eafeaacbf5545dbdb8636de7e7f Binary files /dev/null and b/docs/images/always_like.jpg differ diff --git a/docs/images/anti_kidnap.jpg b/docs/images/anti_kidnap.jpg new file mode 100644 index 0000000000000000000000000000000000000000..a7d74bc3aa59f02ca84654169c567a18b2cf3da3 Binary files /dev/null and b/docs/images/anti_kidnap.jpg differ diff --git a/docs/images/anya_suki.jpg b/docs/images/anya_suki.jpg new file mode 100644 index 0000000000000000000000000000000000000000..0e79fd8dc1417fd7db8774fd2f5d80e300642858 Binary files /dev/null and b/docs/images/anya_suki.jpg differ diff --git a/docs/images/applaud.gif b/docs/images/applaud.gif new file mode 100644 index 0000000000000000000000000000000000000000..629e06b1ac8382eb53c7ea580bb351c4db2343fa Binary files /dev/null and b/docs/images/applaud.gif differ diff --git a/docs/images/ascension.jpg b/docs/images/ascension.jpg new file mode 100644 index 0000000000000000000000000000000000000000..4982157a4a9f44d775ea8f91a5c7c8283a494ce2 Binary files /dev/null and b/docs/images/ascension.jpg differ diff --git a/docs/images/ask.png b/docs/images/ask.png new file mode 100644 index 0000000000000000000000000000000000000000..d39bd3f621af5296a1d7f463d3764446268e90ed Binary files /dev/null and b/docs/images/ask.png differ diff --git a/docs/images/back_to_work.jpg b/docs/images/back_to_work.jpg new file mode 100644 index 0000000000000000000000000000000000000000..586f7243125976d3c3589f0c3f130c826122f23c Binary files /dev/null and b/docs/images/back_to_work.jpg differ diff --git a/docs/images/bad_news.png b/docs/images/bad_news.png new file mode 100644 index 0000000000000000000000000000000000000000..c7dd2900bd9b20cc345db0266b1afdb52fab4bec Binary files /dev/null and b/docs/images/bad_news.png differ diff --git a/docs/images/beat_head.gif b/docs/images/beat_head.gif new file mode 100644 index 0000000000000000000000000000000000000000..63cbfbdcf324995081770ba6887088f8951b47f9 Binary files /dev/null and b/docs/images/beat_head.gif differ diff --git a/docs/images/bite.gif b/docs/images/bite.gif new file mode 100644 index 0000000000000000000000000000000000000000..c3556bb0f4504f0bb9a6ec31168771c6b8feb7a6 Binary files /dev/null and b/docs/images/bite.gif differ diff --git a/docs/images/blood_pressure.jpg b/docs/images/blood_pressure.jpg new file mode 100644 index 0000000000000000000000000000000000000000..98998d35a084dfdd9c60e555ec15ad2e0cb46fa6 Binary files /dev/null and b/docs/images/blood_pressure.jpg differ diff --git a/docs/images/bocchi_draft.gif b/docs/images/bocchi_draft.gif new file mode 100644 index 0000000000000000000000000000000000000000..0148627bb57e2d543fdcfa3308fc8a08231f96f0 --- /dev/null +++ b/docs/images/bocchi_draft.gif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:46442f51bfe7f769220a8f191dfd74b201b0a2a20afe75a5af48f051fcfb2d1a +size 1527095 diff --git a/docs/images/bronya_holdsign.jpg b/docs/images/bronya_holdsign.jpg new file mode 100644 index 0000000000000000000000000000000000000000..14fb632c0cd14a0c382d86221165bd433f36f456 Binary files /dev/null and b/docs/images/bronya_holdsign.jpg differ diff --git a/docs/images/bubble_tea.jpg b/docs/images/bubble_tea.jpg new file mode 100644 index 0000000000000000000000000000000000000000..16b188e4f47da5bed0262485170200aac208b156 Binary files /dev/null and b/docs/images/bubble_tea.jpg differ diff --git a/docs/images/bubble_tea_instance0.jpg b/docs/images/bubble_tea_instance0.jpg new file mode 100644 index 0000000000000000000000000000000000000000..d041693f52eaf121112e4c0f4302f5c1fbd52c37 Binary files /dev/null and b/docs/images/bubble_tea_instance0.jpg differ diff --git a/docs/images/bubble_tea_instance1.jpg b/docs/images/bubble_tea_instance1.jpg new file mode 100644 index 0000000000000000000000000000000000000000..46ddb526c3ae70a7d1f15765ebe5913410ab52be Binary files /dev/null and b/docs/images/bubble_tea_instance1.jpg differ diff --git a/docs/images/bubble_tea_instance2.jpg b/docs/images/bubble_tea_instance2.jpg new file mode 100644 index 0000000000000000000000000000000000000000..8dcf43eb9bbabea896530c150bb9a691de85deeb Binary files /dev/null and b/docs/images/bubble_tea_instance2.jpg differ diff --git a/docs/images/call_110.jpg b/docs/images/call_110.jpg new file mode 100644 index 0000000000000000000000000000000000000000..e1ab9158fcb1ee837b4708a42547fa88e9630093 Binary files /dev/null and b/docs/images/call_110.jpg differ diff --git a/docs/images/caoshen_bite.gif b/docs/images/caoshen_bite.gif new file mode 100644 index 0000000000000000000000000000000000000000..8a007220252503843330dda17251c60bc2f48008 --- /dev/null +++ b/docs/images/caoshen_bite.gif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e2c2cfc866396546b79edf4ac2702ba6713b250fb88d2bfafde53b460b8127ac +size 1829924 diff --git a/docs/images/capoo_draw.gif b/docs/images/capoo_draw.gif new file mode 100644 index 0000000000000000000000000000000000000000..2da610ba13d849ae4585f6a0292eddc49dd3a9b6 Binary files /dev/null and b/docs/images/capoo_draw.gif differ diff --git a/docs/images/capoo_rip.gif b/docs/images/capoo_rip.gif new file mode 100644 index 0000000000000000000000000000000000000000..4ea4b5011884494cede9f9f014966dd3e64536de Binary files /dev/null and b/docs/images/capoo_rip.gif differ diff --git a/docs/images/capoo_rub.gif b/docs/images/capoo_rub.gif new file mode 100644 index 0000000000000000000000000000000000000000..2f2976508294518b12db88a2349d00253b6ca17a Binary files /dev/null and b/docs/images/capoo_rub.gif differ diff --git a/docs/images/capoo_say.gif b/docs/images/capoo_say.gif new file mode 100644 index 0000000000000000000000000000000000000000..f553d261d318a148a99293c39f0b1cec61142ee3 Binary files /dev/null and b/docs/images/capoo_say.gif differ diff --git a/docs/images/capoo_strike.gif b/docs/images/capoo_strike.gif new file mode 100644 index 0000000000000000000000000000000000000000..ff2be2c44717440175791d1f63a4b0ecc2978ec6 Binary files /dev/null and b/docs/images/capoo_strike.gif differ diff --git a/docs/images/captain.jpg b/docs/images/captain.jpg new file mode 100644 index 0000000000000000000000000000000000000000..8a76301d10551b1cb12f99eb153953f32fc7c11a Binary files /dev/null and b/docs/images/captain.jpg differ diff --git a/docs/images/chanshenzi.gif b/docs/images/chanshenzi.gif new file mode 100644 index 0000000000000000000000000000000000000000..bb3a6c487d8fa0a1d630c37fba97a7ab59a64301 Binary files /dev/null and b/docs/images/chanshenzi.gif differ diff --git a/docs/images/charpic.jpg b/docs/images/charpic.jpg new file mode 100644 index 0000000000000000000000000000000000000000..819e4de20be114d1cf1a73b80a7b2719125513b2 Binary files /dev/null and b/docs/images/charpic.jpg differ diff --git a/docs/images/chase_train.gif b/docs/images/chase_train.gif new file mode 100644 index 0000000000000000000000000000000000000000..69c76e7e6d58701d08f182cb48e448b2ae8b947c Binary files /dev/null and b/docs/images/chase_train.gif differ diff --git a/docs/images/china_flag.jpg b/docs/images/china_flag.jpg new file mode 100644 index 0000000000000000000000000000000000000000..415be6813a9df5a8c8378b4415db1ef336035893 Binary files /dev/null and b/docs/images/china_flag.jpg differ diff --git a/docs/images/confuse.gif b/docs/images/confuse.gif new file mode 100644 index 0000000000000000000000000000000000000000..6a44f786f19ae3246dde5a6a9b05de95cde65a70 --- /dev/null +++ b/docs/images/confuse.gif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:cb5342516676e4bf7b6ddfc689695c86ec0ca90dd125f68ee166cc6f16cec2df +size 8053795 diff --git a/docs/images/coupon.jpg b/docs/images/coupon.jpg new file mode 100644 index 0000000000000000000000000000000000000000..b386f8c39da98661e0f3bfd9320a68152b2eece4 Binary files /dev/null and b/docs/images/coupon.jpg differ diff --git a/docs/images/cover_face.jpg b/docs/images/cover_face.jpg new file mode 100644 index 0000000000000000000000000000000000000000..e74c288154127c44379fea1e6ce3b58472dfe9aa Binary files /dev/null and b/docs/images/cover_face.jpg differ diff --git a/docs/images/crawl.jpg b/docs/images/crawl.jpg new file mode 100644 index 0000000000000000000000000000000000000000..ae6e4eed4139ac2ea766180c8cd19cccc83a6813 Binary files /dev/null and b/docs/images/crawl.jpg differ diff --git a/docs/images/cyan.jpg b/docs/images/cyan.jpg new file mode 100644 index 0000000000000000000000000000000000000000..db98bd52cc85d8d3737efee5a7f203b27d03a4f1 Binary files /dev/null and b/docs/images/cyan.jpg differ diff --git a/docs/images/decent_kiss.jpg b/docs/images/decent_kiss.jpg new file mode 100644 index 0000000000000000000000000000000000000000..b90d2279956f59a77f9d6d307690b6a1296bb91c Binary files /dev/null and b/docs/images/decent_kiss.jpg differ diff --git a/docs/images/dianzhongdian.jpg b/docs/images/dianzhongdian.jpg new file mode 100644 index 0000000000000000000000000000000000000000..8c76bbb838e961f644a3c53ca68440d00ffb59b4 Binary files /dev/null and b/docs/images/dianzhongdian.jpg differ diff --git a/docs/images/dinosaur.jpg b/docs/images/dinosaur.jpg new file mode 100644 index 0000000000000000000000000000000000000000..8d831291704879409fab9ca0bf61017ce0120b12 Binary files /dev/null and b/docs/images/dinosaur.jpg differ diff --git a/docs/images/distracted.jpg b/docs/images/distracted.jpg new file mode 100644 index 0000000000000000000000000000000000000000..7ffec0fa6c0a72dbc17b7c5d1c74cc2a29c773d3 Binary files /dev/null and b/docs/images/distracted.jpg differ diff --git a/docs/images/divorce.jpg b/docs/images/divorce.jpg new file mode 100644 index 0000000000000000000000000000000000000000..b9ffcc985d7518fee5450681aa871476a28a9d2d Binary files /dev/null and b/docs/images/divorce.jpg differ diff --git a/docs/images/dog_of_vtb.jpg b/docs/images/dog_of_vtb.jpg new file mode 100644 index 0000000000000000000000000000000000000000..fb1dc64a97477f195651c8aff99288628c08e774 Binary files /dev/null and b/docs/images/dog_of_vtb.jpg differ diff --git a/docs/images/dont_go_near.jpg b/docs/images/dont_go_near.jpg new file mode 100644 index 0000000000000000000000000000000000000000..9e04f34144a902c0c41bc631d63f0d9c25d12c11 Binary files /dev/null and b/docs/images/dont_go_near.jpg differ diff --git a/docs/images/dont_touch.jpg b/docs/images/dont_touch.jpg new file mode 100644 index 0000000000000000000000000000000000000000..bd1265967214962915642c24270aa973cb8f8343 Binary files /dev/null and b/docs/images/dont_touch.jpg differ diff --git a/docs/images/douyin.gif b/docs/images/douyin.gif new file mode 100644 index 0000000000000000000000000000000000000000..c81db32f8a2eb1954b16b623d74f33fe3fab6370 Binary files /dev/null and b/docs/images/douyin.gif differ diff --git a/docs/images/eat.gif b/docs/images/eat.gif new file mode 100644 index 0000000000000000000000000000000000000000..c4bcb0ee52bcfbdbef5a6c80a749f95a283c20c8 Binary files /dev/null and b/docs/images/eat.gif differ diff --git a/docs/images/fanatic.jpg b/docs/images/fanatic.jpg new file mode 100644 index 0000000000000000000000000000000000000000..cbdacfb337d39e2be1a8853c260ef4bb74b13862 Binary files /dev/null and b/docs/images/fanatic.jpg differ diff --git a/docs/images/fencing.gif b/docs/images/fencing.gif new file mode 100644 index 0000000000000000000000000000000000000000..79fc9c3f1dab4d42cf082779cb61de5dc265f717 Binary files /dev/null and b/docs/images/fencing.gif differ diff --git a/docs/images/fill_head.jpg b/docs/images/fill_head.jpg new file mode 100644 index 0000000000000000000000000000000000000000..b5fc0c418a5b74e919253fd01c04372ee377f42b Binary files /dev/null and b/docs/images/fill_head.jpg differ diff --git a/docs/images/find_chips.jpg b/docs/images/find_chips.jpg new file mode 100644 index 0000000000000000000000000000000000000000..06e13e1acc3344e5bcc21aa535e75d461b936c4d Binary files /dev/null and b/docs/images/find_chips.jpg differ diff --git a/docs/images/flash_blind.gif b/docs/images/flash_blind.gif new file mode 100644 index 0000000000000000000000000000000000000000..ce6ddd6fb7e9bafcdb00efe5f9db0d264a13af42 Binary files /dev/null and b/docs/images/flash_blind.gif differ diff --git a/docs/images/follow.jpg b/docs/images/follow.jpg new file mode 100644 index 0000000000000000000000000000000000000000..6550273674d32bda325062ff215c814a41648f73 Binary files /dev/null and b/docs/images/follow.jpg differ diff --git a/docs/images/funny_mirror.gif b/docs/images/funny_mirror.gif new file mode 100644 index 0000000000000000000000000000000000000000..cf0dd93062b120efbd5fda69a8b9768f3aac72ec Binary files /dev/null and b/docs/images/funny_mirror.gif differ diff --git a/docs/images/garbage.gif b/docs/images/garbage.gif new file mode 100644 index 0000000000000000000000000000000000000000..a8f3012f4e49491100f835209c8fcb1dff356143 Binary files /dev/null and b/docs/images/garbage.gif differ diff --git a/docs/images/genshin_start.jpg b/docs/images/genshin_start.jpg new file mode 100644 index 0000000000000000000000000000000000000000..8aaba64f241613b8cda8c9744999e9af06b646de Binary files /dev/null and b/docs/images/genshin_start.jpg differ diff --git a/docs/images/good_news.png b/docs/images/good_news.png new file mode 100644 index 0000000000000000000000000000000000000000..9aa4fc274d919e649e4c969190ed72dfc82699ef Binary files /dev/null and b/docs/images/good_news.png differ diff --git a/docs/images/google.jpg b/docs/images/google.jpg new file mode 100644 index 0000000000000000000000000000000000000000..4f74c0b17542d79302df9d4c69737784326b7511 Binary files /dev/null and b/docs/images/google.jpg differ diff --git a/docs/images/guichu.gif b/docs/images/guichu.gif new file mode 100644 index 0000000000000000000000000000000000000000..421b09064259720dcb22a8ec1f03d64571b6d2bc Binary files /dev/null and b/docs/images/guichu.gif differ diff --git a/docs/images/guichu_instance0.gif b/docs/images/guichu_instance0.gif new file mode 100644 index 0000000000000000000000000000000000000000..e8d2c753c809e420c0cf105556262924d627e5df Binary files /dev/null and b/docs/images/guichu_instance0.gif differ diff --git a/docs/images/guichu_instance1.gif b/docs/images/guichu_instance1.gif new file mode 100644 index 0000000000000000000000000000000000000000..e95fb43faef3b0d68cc2035249797bbd26f3bcce Binary files /dev/null and b/docs/images/guichu_instance1.gif differ diff --git a/docs/images/guichu_instance2.gif b/docs/images/guichu_instance2.gif new file mode 100644 index 0000000000000000000000000000000000000000..40b74a532fa76b66cc6a115db0b1ce009644b9a4 Binary files /dev/null and b/docs/images/guichu_instance2.gif differ diff --git a/docs/images/guichu_instance3.gif b/docs/images/guichu_instance3.gif new file mode 100644 index 0000000000000000000000000000000000000000..b4fe6da0fa9717c98df7313509ef3e50db1252c0 Binary files /dev/null and b/docs/images/guichu_instance3.gif differ diff --git a/docs/images/gun.jpg b/docs/images/gun.jpg new file mode 100644 index 0000000000000000000000000000000000000000..150adcbbe12acc98e3def528d6d8eec1229e4143 Binary files /dev/null and b/docs/images/gun.jpg differ diff --git a/docs/images/gun_instance0.jpg b/docs/images/gun_instance0.jpg new file mode 100644 index 0000000000000000000000000000000000000000..150adcbbe12acc98e3def528d6d8eec1229e4143 Binary files /dev/null and b/docs/images/gun_instance0.jpg differ diff --git a/docs/images/gun_instance1.jpg b/docs/images/gun_instance1.jpg new file mode 100644 index 0000000000000000000000000000000000000000..b77b2b734f602895ac8a535d397f88377411dc4d Binary files /dev/null and b/docs/images/gun_instance1.jpg differ diff --git a/docs/images/gun_instance2.jpg b/docs/images/gun_instance2.jpg new file mode 100644 index 0000000000000000000000000000000000000000..0683eec9b3c74c63b6f0fe875c19116a4aee81de Binary files /dev/null and b/docs/images/gun_instance2.jpg differ diff --git a/docs/images/hammer.gif b/docs/images/hammer.gif new file mode 100644 index 0000000000000000000000000000000000000000..6ce1e29fd071fd2f3c8b72a13c4bd14f07ba7ff1 Binary files /dev/null and b/docs/images/hammer.gif differ diff --git a/docs/images/high_EQ.jpg b/docs/images/high_EQ.jpg new file mode 100644 index 0000000000000000000000000000000000000000..1d8e6c9e5079e61c6312a99b96a624f81ca595aa Binary files /dev/null and b/docs/images/high_EQ.jpg differ diff --git a/docs/images/hit_screen.gif b/docs/images/hit_screen.gif new file mode 100644 index 0000000000000000000000000000000000000000..0006d224df6bf745beece3104c1645b1bfa6d5dc Binary files /dev/null and b/docs/images/hit_screen.gif differ diff --git a/docs/images/hold_grudge.jpg b/docs/images/hold_grudge.jpg new file mode 100644 index 0000000000000000000000000000000000000000..18b2941278f9e87fb8bf32f5ccd1b3f7ac857c74 Binary files /dev/null and b/docs/images/hold_grudge.jpg differ diff --git a/docs/images/hold_tight.jpg b/docs/images/hold_tight.jpg new file mode 100644 index 0000000000000000000000000000000000000000..3cab7e0bb378c38d91c2f90a904a7b188226d517 Binary files /dev/null and b/docs/images/hold_tight.jpg differ diff --git a/docs/images/hug_leg.gif b/docs/images/hug_leg.gif new file mode 100644 index 0000000000000000000000000000000000000000..ff6314247d3296c1ac39696b174602a7aeebfe67 Binary files /dev/null and b/docs/images/hug_leg.gif differ diff --git a/docs/images/hutao_bite.gif b/docs/images/hutao_bite.gif new file mode 100644 index 0000000000000000000000000000000000000000..2c21fe9e640c7f07b7e1a9c6374c53936b46fb34 Binary files /dev/null and b/docs/images/hutao_bite.gif differ diff --git a/docs/images/imprison.jpg b/docs/images/imprison.jpg new file mode 100644 index 0000000000000000000000000000000000000000..00cf973855b30e3bed0ed48ec504096fc5ce83c3 Binary files /dev/null and b/docs/images/imprison.jpg differ diff --git a/docs/images/incivilization.jpg b/docs/images/incivilization.jpg new file mode 100644 index 0000000000000000000000000000000000000000..73f3ed4f85e6b7f13935c212853849a9f35c5b3a Binary files /dev/null and b/docs/images/incivilization.jpg differ diff --git a/docs/images/interview.jpg b/docs/images/interview.jpg new file mode 100644 index 0000000000000000000000000000000000000000..0f1d9bf83e28d5691b40e0d5b9b4e391686526f9 Binary files /dev/null and b/docs/images/interview.jpg differ diff --git a/docs/images/jiji_king.jpg b/docs/images/jiji_king.jpg new file mode 100644 index 0000000000000000000000000000000000000000..755460616cc52aa8e59893208fcacc36d5262d0c Binary files /dev/null and b/docs/images/jiji_king.jpg differ diff --git a/docs/images/jiji_king_instance0.jpg b/docs/images/jiji_king_instance0.jpg new file mode 100644 index 0000000000000000000000000000000000000000..63d8eeef3f59d92e948a468f920049f79a4728cf Binary files /dev/null and b/docs/images/jiji_king_instance0.jpg differ diff --git a/docs/images/jiji_king_instance1.jpg b/docs/images/jiji_king_instance1.jpg new file mode 100644 index 0000000000000000000000000000000000000000..86521cd9a368325ac6eb4edf4d1ba620c7def2f6 Binary files /dev/null and b/docs/images/jiji_king_instance1.jpg differ diff --git a/docs/images/jiujiu.gif b/docs/images/jiujiu.gif new file mode 100644 index 0000000000000000000000000000000000000000..d7d1cab1f28b757a6550aa3230f2ca8b7c4fcfc5 Binary files /dev/null and b/docs/images/jiujiu.gif differ diff --git a/docs/images/kaleidoscope.jpg b/docs/images/kaleidoscope.jpg new file mode 100644 index 0000000000000000000000000000000000000000..54124384cae1db0e047e1f48617b669a59da696a Binary files /dev/null and b/docs/images/kaleidoscope.jpg differ diff --git a/docs/images/kaleidoscope_instance0.jpg b/docs/images/kaleidoscope_instance0.jpg new file mode 100644 index 0000000000000000000000000000000000000000..54124384cae1db0e047e1f48617b669a59da696a Binary files /dev/null and b/docs/images/kaleidoscope_instance0.jpg differ diff --git a/docs/images/kaleidoscope_instance1.jpg b/docs/images/kaleidoscope_instance1.jpg new file mode 100644 index 0000000000000000000000000000000000000000..e7f4ddf1af50df5eae322274ef3f8c7f83388359 Binary files /dev/null and b/docs/images/kaleidoscope_instance1.jpg differ diff --git a/docs/images/karyl_point.png b/docs/images/karyl_point.png new file mode 100644 index 0000000000000000000000000000000000000000..656d028e7ef1d3583d4aea17d9f0c265c9ab0fcd --- /dev/null +++ b/docs/images/karyl_point.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bfc197cc2dc0b9023c76724e2b4c46362861f81504a3827567b8ca3d5255aaf0 +size 1042848 diff --git a/docs/images/keep_away.jpg b/docs/images/keep_away.jpg new file mode 100644 index 0000000000000000000000000000000000000000..9bce7820ae29754b7eb7556d55ce19cdc887c46b Binary files /dev/null and b/docs/images/keep_away.jpg differ diff --git a/docs/images/kick_ball.gif b/docs/images/kick_ball.gif new file mode 100644 index 0000000000000000000000000000000000000000..48ff6f51cba1f973d6ca1e4b67fbb1095b0062f4 Binary files /dev/null and b/docs/images/kick_ball.gif differ diff --git a/docs/images/kirby_hammer.gif b/docs/images/kirby_hammer.gif new file mode 100644 index 0000000000000000000000000000000000000000..7dcb1369cafdb7bc5e3bae64c7cc73ccbc46260a --- /dev/null +++ b/docs/images/kirby_hammer.gif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:271980a54ec2679e10c74be7ead8a28dc87c3ee2a3286214c1a500f6d03fff68 +size 3919975 diff --git a/docs/images/kirby_hammer_instance0.gif b/docs/images/kirby_hammer_instance0.gif new file mode 100644 index 0000000000000000000000000000000000000000..09c177512135721b3c4dfaaed6374e7b27a2c389 --- /dev/null +++ b/docs/images/kirby_hammer_instance0.gif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:09062482e68ef5d447e9fd9ff19f9d4e7a0465124ca66a27dfc16b4145cce4ee +size 3897412 diff --git a/docs/images/kirby_hammer_instance1.gif b/docs/images/kirby_hammer_instance1.gif new file mode 100644 index 0000000000000000000000000000000000000000..01acc2a17adf6aefdaedeeba272954ffdca2ed39 --- /dev/null +++ b/docs/images/kirby_hammer_instance1.gif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e644c3c60481ea7904c600c6792c2ba18ecdfb9a884474e6d1dda200970722d9 +size 3924130 diff --git a/docs/images/kiss.gif b/docs/images/kiss.gif new file mode 100644 index 0000000000000000000000000000000000000000..7d79094a49eca7fcfe6e7c3f8ebc7afcceed0935 Binary files /dev/null and b/docs/images/kiss.gif differ diff --git a/docs/images/klee_eat.gif b/docs/images/klee_eat.gif new file mode 100644 index 0000000000000000000000000000000000000000..360eacafb4968132925258ef26cff4597e079381 Binary files /dev/null and b/docs/images/klee_eat.gif differ diff --git a/docs/images/knock.gif b/docs/images/knock.gif new file mode 100644 index 0000000000000000000000000000000000000000..406751a7055526a1581f6a57a91b78d50c143eb0 Binary files /dev/null and b/docs/images/knock.gif differ diff --git a/docs/images/learn.jpg b/docs/images/learn.jpg new file mode 100644 index 0000000000000000000000000000000000000000..5c6bb1617580361e767a69981493f8147bc9213c Binary files /dev/null and b/docs/images/learn.jpg differ diff --git a/docs/install.md b/docs/install.md new file mode 100644 index 0000000000000000000000000000000000000000..54cce4221e20f53f0dfadefb43b43e3c31197ab0 --- /dev/null +++ b/docs/install.md @@ -0,0 +1,124 @@ +## 本地安装 + +### 使用 pip 安装 + +```bash +pip install meme_generator +``` + +#### 图片下载 + +由于表情包图片体积较大,`meme-generator` 包含的表情中的图片并不随代码一起打包,需要在安装后手动执行下载命令: + +```bash +meme download +``` + +### 直接运行源代码 + +克隆当前仓库: + +```bash +git clone https://github.com/MeetWq/meme-generator +``` + +通过 `python -m meme_generator.app` 运行 web 服务器 + +通过 `python -m meme_generator.cli` 运行命令行程序 + + +### 字体安装 + +为确保表情包中的文字生成正常,需要自行安装字体 + +> **Note** +> +> 字体安装后若文字仍显示不正常,可删掉 `matplotlib` 字体缓存文件重新运行程序 +> +> 缓存文件位置: +> - Windows: `C:\Users\\.matplotlib\fontlist-xxx.json` +> - Linux: `~/.cache/matplotlib/fontlist-xxx.json` +> - Mac: `~/Library/Caches/matplotlib/fontlist-xxx.json` + + +#### 中文字体 和 emoji字体 安装 + +根据系统的不同,推荐安装的字体如下: + +- Windows: + +大部分 Windows 系统自带 [微软雅黑](https://learn.microsoft.com/zh-cn/typography/font-list/microsoft-yahei) 中文字体 和 [Segoe UI Emoji](https://learn.microsoft.com/zh-cn/typography/font-list/segoe-ui-emoji) emoji 字体,一般情况下无需额外安装 + + +- Linux: + +部分系统可能自带 [文泉驿微米黑](http://wenq.org/wqy2/index.cgi?MicroHei) 中文字体; + +对于 Ubuntu 系统,推荐安装 Noto Sans CJK 和 Noto Color Emoji: + +```bash +sudo apt install fonts-noto-cjk fonts-noto-color-emoji +``` + +为避免 Noto Sans CJK 中部分中文显示为异体(日文)字形,可以将简体中文设置为默认语言(详见 [ArchWiki](https://wiki.archlinux.org/title/Localization/Simplified_Chinese?rdfrom=https%3A%2F%2Fwiki.archlinux.org%2Findex.php%3Ftitle%3DLocalization_%28%25E7%25AE%2580%25E4%25BD%2593%25E4%25B8%25AD%25E6%2596%2587%29%2FSimplified_Chinese_%28%25E7%25AE%2580%25E4%25BD%2593%25E4%25B8%25AD%25E6%2596%2587%29%26redirect%3Dno#%E4%BF%AE%E6%AD%A3%E7%AE%80%E4%BD%93%E4%B8%AD%E6%96%87%E6%98%BE%E7%A4%BA%E4%B8%BA%E5%BC%82%E4%BD%93%EF%BC%88%E6%97%A5%E6%96%87%EF%BC%89%E5%AD%97%E5%BD%A2)): + +```bash +sudo locale-gen zh_CN zh_CN.UTF-8 +sudo update-locale LC_ALL=zh_CN.UTF-8 LANG=zh_CN.UTF-8 +fc-cache -fv +``` + +其他 Linux 系统可以自行下载字体文件安装: + +思源黑体:https://github.com/adobe-fonts/source-han-sans + +NotoSansSC:https://fonts.google.com/noto/specimen/Noto+Sans+SC + +Noto Color Emoji:https://github.com/googlefonts/noto-emoji + + +- Mac: + +苹果系统一般自带 "PingFang SC" 中文字体 与 "Apple Color Emoji" emoji 字体 + + +#### 其他字体安装 + +某些表情包需要用到一些额外字体,存放于仓库中 [resources/fonts](https://github.com/MeetWq/meme-generator/tree/main/resources/fonts),需要自行下载安装 + +具体字体及对应的表情如下: + +| 字体名 | 字体文件名 | 用到该字体的表情 | 备注 | +| --- | --- | --- | --- | +| [Consolas](https://learn.microsoft.com/zh-cn/typography/font-list/consolas) | [consola.ttf](https://github.com/MeetWq/meme-generator/blob/main/resources/fonts/consola.ttf) | `charpic` | | +| [FZKaTong-M19S](https://www.foundertype.com/index.php/FontInfo/index/id/136) | [FZKATJW.ttf](https://github.com/MeetWq/meme-generator/blob/main/resources/fonts/FZKATJW.ttf) | `capoo_say` | 方正卡通 | +| [FZXS14](https://www.foundertype.com/index.php/FontInfo/index/id/208) | [FZXS14.ttf](https://github.com/MeetWq/meme-generator/blob/main/resources/fonts/FZXS14.ttf) | `nokia` | 方正像素14 | +| [FZSJ-QINGCRJ](https://www.foundertype.com/index.php/FontInfo/index/id/5178) | [FZSJ-QINGCRJ.ttf](https://github.com/MeetWq/meme-generator/blob/main/resources/fonts/FZSJ-QINGCRJ.ttf) | `psyduck`、`nijika_holdsign` | 方正手迹-青春日记 | +| [FZShaoEr-M11S](https://www.foundertype.com/index.php/FontInfo/index/id/149) | [FZSEJW.ttf](https://github.com/MeetWq/meme-generator/blob/main/resources/fonts/FZSEJW.ttf) | `raise_sign`、`nekoha_holdsign` | 方正少儿 | +| [NotoSansSC](https://fonts.google.com/noto/specimen/Noto+Sans+SC) | [NotoSansSC-Regular.otf](https://github.com/MeetWq/meme-generator/blob/main/resources/fonts/NotoSansSC-Regular.otf) | `5000choyen` | | +| [NotoSerifSC](https://fonts.google.com/noto/specimen/Noto+Serif+SC) | [NotoSerifSC-Regular.otf](https://github.com/MeetWq/meme-generator/blob/main/resources/fonts/NotoSerifSC-Regular.otf) | `5000choyen` | | +| [HiraginoMin](https://www.fonts.net.cn/font-36201269101.html) | [HiraginoMin-W5-90-RKSJ-H-2.ttc](https://github.com/MeetWq/meme-generator/blob/main/resources/fonts/HiraginoMin-W5-90-RKSJ-H-2.ttc) | `oshi_no_ko` | 明朝体 | +| [Aller](https://fonts.adobe.com/fonts/aller) | [Aller_Bd.ttf](https://github.com/MeetWq/meme-generator/blob/main/resources/fonts/Aller_Bd.ttf) | `osu` | | + + +#### 字体安装方式 + +不同系统的字体安装方式: + +- Windows: + - 双击通过字体查看器安装 + - 复制到字体文件夹:`C:\Windows\Fonts` + +- Linux: + +在 `/usr/share/fonts` 目录下新建文件夹,如 `myfonts`,将字体文件复制到该路径下; + +运行如下命令建立字体缓存: + +```bash +fc-cache -fv +``` + +- Mac: + +使用字体册打开字体文件安装 diff --git a/docs/memes.md b/docs/memes.md new file mode 100644 index 0000000000000000000000000000000000000000..2942de12f9e500f5649b543ce78f74d03ac803b9 --- /dev/null +++ b/docs/memes.md @@ -0,0 +1,2532 @@ +# 表情列表 + +以下为内置表情的关键词、所需参数等信息及表情预览 + +按照表情的 `key` 排列 + + +1. [5000choyen (5000兆)](#5000choyen) +2. [acg_entrance (二次元入口)](#acg_entrance) +3. [add_chaos (添乱/给社会添乱)](#add_chaos) +4. [addiction (上瘾/毒瘾发作)](#addiction) +5. [alike (一样)](#alike) +6. [always (一直)](#always) +7. [always_like (我永远喜欢)](#always_like) +8. [anti_kidnap (防诱拐)](#anti_kidnap) +9. [anya_suki (阿尼亚喜欢)](#anya_suki) +10. [applaud (鼓掌)](#applaud) +11. [ascension (升天)](#ascension) +12. [ask (问问)](#ask) +13. [back_to_work (继续干活/打工人)](#back_to_work) +14. [bad_news (悲报)](#bad_news) +15. [beat_head (拍头)](#beat_head) +16. [bite (啃)](#bite) +17. [blood_pressure (高血压)](#blood_pressure) +18. [bocchi_draft (波奇手稿)](#bocchi_draft) +19. [bronya_holdsign (布洛妮娅举牌/大鸭鸭举牌)](#bronya_holdsign) +20. [bubble_tea (奶茶)](#bubble_tea) +21. [call_110 (遇到困难请拨打)](#call_110) +22. [caoshen_bite (草神啃)](#caoshen_bite) +23. [capoo_draw (咖波画)](#capoo_draw) +24. [capoo_rip (咖波撕)](#capoo_rip) +25. [capoo_rub (咖波蹭/咖波贴)](#capoo_rub) +26. [capoo_say (咖波说)](#capoo_say) +27. [capoo_strike (咖波撞/咖波头槌)](#capoo_strike) +28. [captain (舰长)](#captain) +29. [chanshenzi (馋身子)](#chanshenzi) +30. [charpic (字符画)](#charpic) +31. [chase_train (追列车/追火车)](#chase_train) +32. [china_flag (国旗)](#china_flag) +33. [confuse (迷惑)](#confuse) +34. [coupon (兑换券)](#coupon) +35. [cover_face (捂脸)](#cover_face) +36. [crawl (爬)](#crawl) +37. [cyan (群青)](#cyan) +38. [decent_kiss (像样的亲亲)](#decent_kiss) +39. [dianzhongdian (入典/典中典/黑白草图)](#dianzhongdian) +40. [dinosaur (恐龙/小恐龙)](#dinosaur) +41. [distracted (注意力涣散)](#distracted) +42. [divorce (离婚协议/离婚申请)](#divorce) +43. [dog_of_vtb (管人痴)](#dog_of_vtb) +44. [dont_go_near (不要靠近)](#dont_go_near) +45. [dont_touch (别碰)](#dont_touch) +46. [douyin (douyin)](#douyin) +47. [eat (吃)](#eat) +48. [fanatic (狂爱/狂粉)](#fanatic) +49. [fencing (击剑/🤺)](#fencing) +50. [fill_head (满脑子)](#fill_head) +51. [find_chips (整点薯条)](#find_chips) +52. [flash_blind (闪瞎)](#flash_blind) +53. [follow (关注)](#follow) +54. [funny_mirror (哈哈镜)](#funny_mirror) +55. [garbage (垃圾/垃圾桶)](#garbage) +56. [genshin_start (原神启动)](#genshin_start) +57. [good_news (喜报)](#good_news) +58. [google (google)](#google) +59. [guichu (鬼畜)](#guichu) +60. [gun (手枪)](#gun) +61. [hammer (锤)](#hammer) +62. [high_EQ (低情商xx高情商xx)](#high_EQ) +63. [hit_screen (打穿/打穿屏幕)](#hit_screen) +64. [hold_grudge (记仇)](#hold_grudge) +65. [hold_tight (抱紧)](#hold_tight) +66. [hug_leg (抱大腿)](#hug_leg) +67. [hutao_bite (胡桃啃)](#hutao_bite) +68. [imprison (坐牢)](#imprison) +69. [incivilization (不文明)](#incivilization) +70. [interview (采访)](#interview) +71. [jiji_king (急急国王)](#jiji_king) +72. [jiujiu (啾啾)](#jiujiu) +73. [kaleidoscope (万花筒/万花镜)](#kaleidoscope) +74. [karyl_point (凯露指)](#karyl_point) +75. [keep_away (远离)](#keep_away) +76. [kick_ball (踢球)](#kick_ball) +77. [kirby_hammer (卡比锤/卡比重锤)](#kirby_hammer) +78. [kiss (亲/亲亲)](#kiss) +79. [klee_eat (可莉吃)](#klee_eat) +80. [knock (敲)](#knock) +81. [learn (偷学)](#learn) +82. [lim_x_0 (等价无穷小)](#lim_x_0) +83. [listen_music (听音乐)](#listen_music) +84. [little_angel (小天使)](#little_angel) +85. [loading (加载中)](#loading) +86. [look_flat (看扁)](#look_flat) +87. [look_this_icon (看图标)](#look_this_icon) +88. [love_you (永远爱你)](#love_you) +89. [luoyonghao_say (罗永浩说)](#luoyonghao_say) +90. [luxun_say (鲁迅说/鲁迅说过)](#luxun_say) +91. [maikease (麦克阿瑟说)](#maikease) +92. [maimai_awaken (旅行伙伴觉醒)](#maimai_awaken) +93. [maimai_join (旅行伙伴加入)](#maimai_join) +94. [make_friend (交个朋友)](#make_friend) +95. [marriage (结婚申请/结婚登记)](#marriage) +96. [meteor (流星)](#meteor) +97. [mihoyo (米哈游)](#mihoyo) +98. [mourning (上香)](#mourning) +99. [murmur (低语)](#murmur) +100. [my_friend (我朋友说)](#my_friend) +101. [my_wife (我老婆/这是我老婆)](#my_wife) +102. [name_generator (亚文化取名机/亚名)](#name_generator) +103. [need (需要/你可能需要)](#need) +104. [nekoha_holdsign (猫羽雫举牌/猫猫举牌)](#nekoha_holdsign) +105. [nihaosaoa (你好骚啊)](#nihaosaoa) +106. [nijika_holdsign (伊地知虹夏举牌/虹夏举牌)](#nijika_holdsign) +107. [no_response (无响应)](#no_response) +108. [nokia (诺基亚/有内鬼)](#nokia) +109. [not_call_me (不喊我)](#not_call_me) +110. [note_for_leave (请假条)](#note_for_leave) +111. [oshi_no_ko (我推的网友)](#oshi_no_ko) +112. [osu (osu)](#osu) +113. [overtime (加班)](#overtime) +114. [paint (这像画吗)](#paint) +115. [painter (小画家)](#painter) +116. [pass_the_buck (推锅/甩锅)](#pass_the_buck) +117. [pat (拍)](#pat) +118. [perfect (完美)](#perfect) +119. [petpet (摸/摸摸/摸头/rua)](#petpet) +120. [play (顶/玩)](#play) +121. [play_game (玩游戏)](#play_game) +122. [police (出警)](#police) +123. [police1 (警察)](#police1) +124. [pornhub (ph/pornhub)](#pornhub) +125. [potato (土豆)](#potato) +126. [pound (捣)](#pound) +127. [printing (打印)](#printing) +128. [prpr (舔/舔屏/prpr)](#prpr) +129. [psyduck (可达鸭)](#psyduck) +130. [punch (打拳)](#punch) +131. [qiegewala (切格瓦拉)](#qiegewala) +132. [raise_image (举)](#raise_image) +133. [raise_sign (举牌)](#raise_sign) +134. [read_book (看书)](#read_book) +135. [repeat (复读)](#repeat) +136. [rip (撕)](#rip) +137. [rip_angrily (怒撕)](#rip_angrily) +138. [rise_dead (诈尸/秽土转生)](#rise_dead) +139. [roll (滚)](#roll) +140. [rub (贴/贴贴/蹭/蹭蹭)](#rub) +141. [run (快跑)](#run) +142. [safe_sense (安全感)](#safe_sense) +143. [scratch_head (挠头)](#scratch_head) +144. [scratchcard (刮刮乐)](#scratchcard) +145. [scroll (滚屏)](#scroll) +146. [shishilani (食屎啦你)](#shishilani) +147. [shock (震惊)](#shock) +148. [shuifandui (谁反对)](#shuifandui) +149. [shutup (别说了)](#shutup) +150. [sit_still (坐得住/坐的住)](#sit_still) +151. [slap (一巴掌)](#slap) +152. [slogan (口号)](#slogan) +153. [smash (砸)](#smash) +154. [step_on (踩)](#step_on) +155. [suck (吸/嗦)](#suck) +156. [support (精神支柱)](#support) +157. [symmetric (对称)](#symmetric) +158. [tankuku_raisesign (唐可可举牌)](#tankuku_raisesign) +159. [taunt (嘲讽)](#taunt) +160. [teach (讲课/敲黑板)](#teach) +161. [tease (拿捏/戏弄)](#tease) +162. [think_what (想什么)](#think_what) +163. [throw (丢/扔)](#throw) +164. [throw_gif (抛/掷)](#throw_gif) +165. [thump (捶)](#thump) +166. [thump_wildly (捶爆/爆捶)](#thump_wildly) +167. [tightly (紧贴/紧紧贴着)](#tightly) +168. [together (一起)](#together) +169. [trance (恍惚)](#trance) +170. [turn (转)](#turn) +171. [twist (搓)](#twist) +172. [universal (万能表情/空白表情)](#universal) +173. [vibrate (震动)](#vibrate) +174. [wakeup (xx起来了)](#wakeup) +175. [wallpaper (墙纸)](#wallpaper) +176. [walnut_pad (胡桃平板)](#walnut_pad) +177. [walnut_zoom (胡桃放大)](#walnut_zoom) +178. [wangjingze (王境泽)](#wangjingze) +179. [wave (波纹)](#wave) +180. [weisuoyuwei (为所欲为)](#weisuoyuwei) +181. [what_I_want_to_do (我想上的)](#what_I_want_to_do) +182. [what_he_wants (最想要的东西)](#what_he_wants) +183. [why_at_me (为什么@我)](#why_at_me) +184. [why_have_hands (为什么要有手)](#why_have_hands) +185. [windmill_turn (风车转)](#windmill_turn) +186. [wish_fail (许愿失败)](#wish_fail) +187. [wooden_fish (木鱼)](#wooden_fish) +188. [worship (膜/膜拜)](#worship) +189. [wujing (吴京xx中国xx)](#wujing) +190. [wunian (五年怎么过的)](#wunian) +191. [yalidaye (压力大爷)](#yalidaye) +192. [youtube (yt/youtube)](#youtube) +193. [zengxiaoxian (曾小贤)](#zengxiaoxian) + + +## 5000choyen + +- 关键词:`5000兆` +- 需要图片数目:`0` +- 需要文字数目:`2` +- 默认文字:[`我去`, `洛天依`] +- 预览: +
+ +
+ +## acg_entrance + +- 关键词:`二次元入口` +- 需要图片数目:`1` +- 需要文字数目:`0` ~ `1` +- 默认文字:[`走,跟我去二次元吧`] +- 预览: +
+ +
+ +## add_chaos + +- 关键词:`添乱`、`给社会添乱` +- 需要图片数目:`1` +- 需要文字数目:`0` +- 预览: +
+ +
+ +## addiction + +- 关键词:`上瘾`、`毒瘾发作` +- 需要图片数目:`1` +- 需要文字数目:`0` ~ `1` +- 预览: +
+ +
+ +## alike + +- 关键词:`一样` +- 需要图片数目:`1` +- 需要文字数目:`0` +- 预览: +
+ +
+ +## always + +- 关键词:`一直` +- 需要图片数目:`1` +- 需要文字数目:`0` +- 其他参数: + - `mode` + - 描述:生成模式 + - 类型:`string` + - 默认值:`normal` + - 可选值:`normal`、`loop`、`circle` +- 其他参数(命令行选项): +```shell +usage: meme generate always [-h] [--mode {normal,circle,loop} | --circle | --loop] + +options: + -h, --help show this help message and exit + --mode {normal,circle,loop} + 生成模式 + --circle, /套娃 + --loop, /循环 +``` + +- 预览: +> 参数:{"mode":"normal"} +
+ +
+ +> 参数:{"mode":"circle"} +
+ +
+ +> 参数:{"mode":"loop"} +
+ +
+ +## always_like + +- 关键词:`我永远喜欢` +- 需要图片数目:`1` ~ `6` +- 需要文字数目:`0` ~ `6` +- 预览: +
+ +
+ +## anti_kidnap + +- 关键词:`防诱拐` +- 需要图片数目:`1` +- 需要文字数目:`0` +- 预览: +
+ +
+ +## anya_suki + +- 关键词:`阿尼亚喜欢` +- 需要图片数目:`1` +- 需要文字数目:`0` ~ `1` +- 默认文字:[`阿尼亚喜欢这个`] +- 预览: +
+ +
+ +## applaud + +- 关键词:`鼓掌` +- 需要图片数目:`1` +- 需要文字数目:`0` +- 预览: +
+ +
+ +## ascension + +- 关键词:`升天` +- 需要图片数目:`0` +- 需要文字数目:`1` +- 默认文字:[`学的是机械`] +- 预览: +
+ +
+ +## ask + +- 关键词:`问问` +- 需要图片数目:`1` +- 需要文字数目:`0` ~ `1` +- 预览: +
+ +
+ +## back_to_work + +- 关键词:`继续干活`、`打工人` +- 需要图片数目:`1` +- 需要文字数目:`0` +- 预览: +
+ +
+ +## bad_news + +- 关键词:`悲报` +- 需要图片数目:`0` +- 需要文字数目:`1` +- 默认文字:[`喜报`] +- 预览: +
+ +
+ +## beat_head + +- 关键词:`拍头` +- 需要图片数目:`1` +- 需要文字数目:`0` ~ `1` +- 预览: +
+ +
+ +## bite + +- 关键词:`啃` +- 需要图片数目:`1` +- 需要文字数目:`0` +- 预览: +
+ +
+ +## blood_pressure + +- 关键词:`高血压` +- 需要图片数目:`1` +- 需要文字数目:`0` +- 预览: +
+ +
+ +## bocchi_draft + +- 关键词:`波奇手稿` +- 需要图片数目:`1` +- 需要文字数目:`0` +- 预览: +
+ +
+ +## bronya_holdsign + +- 关键词:`布洛妮娅举牌`、`大鸭鸭举牌` +- 需要图片数目:`0` +- 需要文字数目:`1` +- 默认文字:[`V我50`] +- 预览: +
+ +
+ +## bubble_tea + +- 关键词:`奶茶` +- 需要图片数目:`1` +- 需要文字数目:`0` +- 其他参数: + - `position` + - 描述:奶茶的位置 + - 类型:`string` + - 默认值:`right` + - 可选值:`right`、`left`、`both` +- 其他参数(命令行选项): +```shell +usage: meme generate bubble_tea [-h] [-p {right,left,both} | --right | --left | --both] + +options: + -h, --help show this help message and exit + -p {right,left,both}, --position {right,left,both} + 奶茶的位置 + --right, /右手 + --left, /左手 + --both, /双手 +``` + +- 预览: +> 参数:{"position":"right"} +
+ +
+ +> 参数:{"position":"left"} +
+ +
+ +> 参数:{"position":"both"} +
+ +
+ +## call_110 + +- 关键词:`遇到困难请拨打` +- 需要图片数目:`2` +- 需要文字数目:`0` +- 预览: +
+ +
+ +## caoshen_bite + +- 关键词:`草神啃` +- 需要图片数目:`1` +- 需要文字数目:`0` +- 预览: +
+ +
+ +## capoo_draw + +- 关键词:`咖波画` +- 需要图片数目:`1` +- 需要文字数目:`0` +- 预览: +
+ +
+ +## capoo_rip + +- 关键词:`咖波撕` +- 需要图片数目:`1` +- 需要文字数目:`0` +- 预览: +
+ +
+ +## capoo_rub + +- 关键词:`咖波蹭`、`咖波贴` +- 需要图片数目:`1` +- 需要文字数目:`0` +- 预览: +
+ +
+ +## capoo_say + +- 关键词:`咖波说` +- 需要图片数目:`0` +- 需要文字数目:`1` ~ `10` +- 默认文字:[`寄`] +- 预览: +
+ +
+ +## capoo_strike + +- 关键词:`咖波撞`、`咖波头槌` +- 需要图片数目:`1` +- 需要文字数目:`0` +- 预览: +
+ +
+ +## captain + +- 关键词:`舰长` +- 需要图片数目:`2` ~ `5` +- 需要文字数目:`0` +- 预览: +
+ +
+ +## chanshenzi + +- 关键词:`馋身子` +- 需要图片数目:`0` +- 需要文字数目:`3` +- 默认文字:[`你那叫喜欢吗?`, `你那是馋她身子`, `你下贱!`] +- 预览: +
+ +
+ +## charpic + +- 关键词:`字符画` +- 需要图片数目:`1` +- 需要文字数目:`0` +- 预览: +
+ +
+ +## chase_train + +- 关键词:`追列车`、`追火车` +- 需要图片数目:`1` +- 需要文字数目:`0` +- 预览: +
+ +
+ +## china_flag + +- 关键词:`国旗` +- 需要图片数目:`1` +- 需要文字数目:`0` +- 预览: +
+ +
+ +## confuse + +- 关键词:`迷惑` +- 需要图片数目:`1` +- 需要文字数目:`0` +- 预览: +
+ +
+ +## coupon + +- 关键词:`兑换券` +- 需要图片数目:`1` +- 需要文字数目:`0` ~ `1` +- 预览: +
+ +
+ +## cover_face + +- 关键词:`捂脸` +- 需要图片数目:`1` +- 需要文字数目:`0` +- 预览: +
+ +
+ +## crawl + +- 关键词:`爬` +- 需要图片数目:`1` +- 需要文字数目:`0` +- 其他参数: + - `number` + - 描述:图片编号,范围为 1~92 + - 类型:`integer` + - 默认值:`0` +- 其他参数(命令行选项): +```shell +usage: meme generate crawl [-h] [-n NUMBER] + +options: + -h, --help show this help message and exit + -n NUMBER, --number NUMBER + 图片编号,范围为 1~92 +``` + +- 预览: +
+ +
+ +## cyan + +- 关键词:`群青` +- 需要图片数目:`1` +- 需要文字数目:`0` +- 预览: +
+ +
+ +## decent_kiss + +- 关键词:`像样的亲亲` +- 需要图片数目:`1` +- 需要文字数目:`0` +- 预览: +
+ +
+ +## dianzhongdian + +- 关键词:`入典`、`典中典`、`黑白草图` +- 需要图片数目:`1` +- 需要文字数目:`1` ~ `2` +- 默认文字:[`救命啊`] +- 预览: +
+ +
+ +## dinosaur + +- 关键词:`恐龙`、`小恐龙` +- 需要图片数目:`1` +- 需要文字数目:`0` +- 预览: +
+ +
+ +## distracted + +- 关键词:`注意力涣散` +- 需要图片数目:`1` +- 需要文字数目:`0` +- 预览: +
+ +
+ +## divorce + +- 关键词:`离婚协议`、`离婚申请` +- 需要图片数目:`1` +- 需要文字数目:`0` +- 预览: +
+ +
+ +## dog_of_vtb + +- 关键词:`管人痴` +- 需要图片数目:`1` +- 需要文字数目:`0` +- 预览: +
+ +
+ +## dont_go_near + +- 关键词:`不要靠近` +- 需要图片数目:`1` +- 需要文字数目:`0` +- 预览: +
+ +
+ +## dont_touch + +- 关键词:`别碰` +- 需要图片数目:`1` +- 需要文字数目:`0` +- 预览: +
+ +
+ +## douyin + +- 关键词:`douyin` +- 需要图片数目:`0` +- 需要文字数目:`1` +- 默认文字:[`douyin`] +- 预览: +
+ +
+ +## eat + +- 关键词:`吃` +- 需要图片数目:`1` +- 需要文字数目:`0` +- 预览: +
+ +
+ +## fanatic + +- 关键词:`狂爱`、`狂粉` +- 需要图片数目:`0` +- 需要文字数目:`1` +- 默认文字:[`洛天依`] +- 预览: +
+ +
+ +## fencing + +- 关键词:`击剑`、`🤺` +- 需要图片数目:`2` +- 需要文字数目:`0` +- 预览: +
+ +
+ +## fill_head + +- 关键词:`满脑子` +- 正则表达式:`满脑子都是(\S+)` +- 需要图片数目:`1` +- 需要文字数目:`0` ~ `1` +- 预览: +
+ +
+ +## find_chips + +- 关键词:`整点薯条` +- 需要图片数目:`0` +- 需要文字数目:`4` +- 默认文字:[`我们要飞向何方`, `我打算待会去码头整点薯条`, `我说的是归根结底,活着是为了什么`, `为了待会去码头整点薯条`] +- 预览: +
+ +
+ +## flash_blind + +- 关键词:`闪瞎` +- 需要图片数目:`1` +- 需要文字数目:`0` ~ `1` +- 默认文字:[`闪瞎你们的狗眼`] +- 预览: +
+ +
+ +## follow + +- 关键词:`关注` +- 需要图片数目:`1` +- 需要文字数目:`0` ~ `1` +- 预览: +
+ +
+ +## funny_mirror + +- 关键词:`哈哈镜` +- 需要图片数目:`1` +- 需要文字数目:`0` +- 预览: +
+ +
+ +## garbage + +- 关键词:`垃圾`、`垃圾桶` +- 需要图片数目:`1` +- 需要文字数目:`0` +- 预览: +
+ +
+ +## genshin_start + +- 关键词:`原神启动` +- 正则表达式:`(\S+启动[!!]?)` +- 需要图片数目:`1` +- 需要文字数目:`0` ~ `1` +- 默认文字:[`原神,启动!`] +- 预览: +
+ +
+ +## good_news + +- 关键词:`喜报` +- 需要图片数目:`0` +- 需要文字数目:`1` +- 默认文字:[`悲报`] +- 预览: +
+ +
+ +## google + +- 关键词:`google` +- 需要图片数目:`0` +- 需要文字数目:`1` +- 默认文字:[`Google`] +- 预览: +
+ +
+ +## guichu + +- 关键词:`鬼畜` +- 需要图片数目:`1` +- 需要文字数目:`0` +- 其他参数: + - `direction` + - 描述:鬼畜对称方向 + - 类型:`string` + - 默认值:`left` + - 可选值:`left`、`right`、`top`、`bottom` +- 其他参数(命令行选项): +```shell +usage: meme generate guichu [-h] + [-d {left,right,top,bottom} | --left | --right | --top | --bottom] + +options: + -h, --help show this help message and exit + -d {left,right,top,bottom}, --direction {left,right,top,bottom} + 鬼畜对称方向 + --left, /左 + --right, /右 + --top, /上 + --bottom, /下 +``` + +- 预览: +> 参数:{"direction":"left"} +
+ +
+ +> 参数:{"direction":"right"} +
+ +
+ +> 参数:{"direction":"top"} +
+ +
+ +> 参数:{"direction":"bottom"} +
+ +
+ +## gun + +- 关键词:`手枪` +- 需要图片数目:`1` +- 需要文字数目:`0` +- 其他参数: + - `position` + - 描述:枪的位置 + - 类型:`string` + - 默认值:`left` + - 可选值:`left`、`right`、`both` +- 其他参数(命令行选项): +```shell +usage: meme generate gun [-h] [-p {left,right,both} | --left | --right | --both] + +options: + -h, --help show this help message and exit + -p {left,right,both}, --position {left,right,both} + 枪的位置 + --left, /左手 + --right, /右手 + --both, /双手 +``` + +- 预览: +> 参数:{"position":"left"} +
+ +
+ +> 参数:{"position":"right"} +
+ +
+ +> 参数:{"position":"both"} +
+ +
+ +## hammer + +- 关键词:`锤` +- 需要图片数目:`1` +- 需要文字数目:`0` +- 预览: +
+ +
+ +## high_EQ + +- 关键词:`低情商xx高情商xx` +- 正则表达式:`低情商[\s::]*(.+?)\s+高情商[\s::]*(.+)` +- 需要图片数目:`0` +- 需要文字数目:`2` +- 默认文字:[`高情商`, `低情商`] +- 预览: +
+ +
+ +## hit_screen + +- 关键词:`打穿`、`打穿屏幕` +- 需要图片数目:`1` +- 需要文字数目:`0` +- 预览: +
+ +
+ +## hold_grudge + +- 关键词:`记仇` +- 需要图片数目:`0` +- 需要文字数目:`1` +- 默认文字:[`群友不发涩图`] +- 预览: +
+ +
+ +## hold_tight + +- 关键词:`抱紧` +- 需要图片数目:`1` +- 需要文字数目:`0` +- 预览: +
+ +
+ +## hug_leg + +- 关键词:`抱大腿` +- 需要图片数目:`1` +- 需要文字数目:`0` +- 预览: +
+ +
+ +## hutao_bite + +- 关键词:`胡桃啃` +- 需要图片数目:`1` +- 需要文字数目:`0` +- 预览: +
+ +
+ +## imprison + +- 关键词:`坐牢` +- 需要图片数目:`0` +- 需要文字数目:`1` +- 默认文字:[`我发涩图被抓起来了`] +- 预览: +
+ +
+ +## incivilization + +- 关键词:`不文明` +- 需要图片数目:`1` +- 需要文字数目:`0` ~ `1` +- 默认文字:[`你刚才说的话不是很礼貌!`] +- 预览: +
+ +
+ +## interview + +- 关键词:`采访` +- 需要图片数目:`1` ~ `2` +- 需要文字数目:`0` ~ `1` +- 默认文字:[`采访大佬经验`] +- 预览: +
+ +
+ +## jiji_king + +- 关键词:`急急国王` +- 需要图片数目:`1` ~ `11` +- 需要文字数目:`0` ~ `11` +- 其他参数: + - `circle` + - 描述:是否将图片变为圆形 + - 类型:`boolean` + - 默认值:`False` +- 其他参数(命令行选项): +```shell +usage: meme generate jiji_king [-h] [--circle] + +options: + -h, --help show this help message and exit + --circle, /圆 是否将图片变为圆形 +``` + +- 预览: +> 参数:{"circle":false} +
+ +
+ +> 参数:{"circle":true} +
+ +
+ +## jiujiu + +- 关键词:`啾啾` +- 需要图片数目:`1` +- 需要文字数目:`0` +- 预览: +
+ +
+ +## kaleidoscope + +- 关键词:`万花筒`、`万花镜` +- 需要图片数目:`1` +- 需要文字数目:`0` +- 其他参数: + - `circle` + - 描述:是否将图片变为圆形 + - 类型:`boolean` + - 默认值:`False` +- 其他参数(命令行选项): +```shell +usage: meme generate kaleidoscope [-h] [--circle] + +options: + -h, --help show this help message and exit + --circle, /圆 是否将图片变为圆形 +``` + +- 预览: +> 参数:{"circle":false} +
+ +
+ +> 参数:{"circle":true} +
+ +
+ +## karyl_point + +- 关键词:`凯露指` +- 需要图片数目:`1` +- 需要文字数目:`0` +- 预览: +
+ +
+ +## keep_away + +- 关键词:`远离` +- 需要图片数目:`1` ~ `8` +- 需要文字数目:`0` ~ `1` +- 默认文字:[`如何提高社交质量 : +远离以下头像的人`] +- 预览: +
+ +
+ +## kick_ball + +- 关键词:`踢球` +- 需要图片数目:`1` +- 需要文字数目:`0` +- 预览: +
+ +
+ +## kirby_hammer + +- 关键词:`卡比锤`、`卡比重锤` +- 需要图片数目:`1` +- 需要文字数目:`0` +- 其他参数: + - `circle` + - 描述:是否将图片变为圆形 + - 类型:`boolean` + - 默认值:`False` +- 其他参数(命令行选项): +```shell +usage: meme generate kirby_hammer [-h] [--circle] + +options: + -h, --help show this help message and exit + --circle, /圆 是否将图片变为圆形 +``` + +- 预览: +> 参数:{"circle":false} +
+ +
+ +> 参数:{"circle":true} +
+ +
+ +## kiss + +- 关键词:`亲`、`亲亲` +- 需要图片数目:`2` +- 需要文字数目:`0` +- 预览: +
+ +
+ +## klee_eat + +- 关键词:`可莉吃` +- 需要图片数目:`1` +- 需要文字数目:`0` +- 预览: +
+ +
+ +## knock + +- 关键词:`敲` +- 需要图片数目:`1` +- 需要文字数目:`0` +- 预览: +
+ +
+ +## learn + +- 关键词:`偷学` +- 需要图片数目:`1` +- 需要文字数目:`0` ~ `1` +- 默认文字:[`偷学群友数理基础`] +- 预览: +
+ +
+ +## lim_x_0 + +- 关键词:`等价无穷小` +- 需要图片数目:`1` +- 需要文字数目:`0` +- 预览: +
+ +
+ +## listen_music + +- 关键词:`听音乐` +- 需要图片数目:`1` +- 需要文字数目:`0` +- 预览: +
+ +
+ +## little_angel + +- 关键词:`小天使` +- 需要图片数目:`1` +- 需要文字数目:`0` ~ `1` +- 预览: +
+ +
+ +## loading + +- 关键词:`加载中` +- 需要图片数目:`1` +- 需要文字数目:`0` +- 预览: +
+ +
+ +## look_flat + +- 关键词:`看扁` +- 需要图片数目:`1` +- 需要文字数目:`0` ~ `1` +- 默认文字:[`可恶...被人看扁了`] +- 其他参数: + - `ratio` + - 描述:图片“压扁”比例 + - 类型:`integer` + - 默认值:`2` +- 其他参数(命令行选项): +```shell +usage: meme generate look_flat [-h] [-r RATIO] + +options: + -h, --help show this help message and exit + -r RATIO, --ratio RATIO + 图片“压扁”比例 +``` + +- 预览: +
+ +
+ +## look_this_icon + +- 关键词:`看图标` +- 需要图片数目:`1` +- 需要文字数目:`0` ~ `1` +- 默认文字:[`朋友 +先看看这个图标再说话`] +- 预览: +
+ +
+ +## love_you + +- 关键词:`永远爱你` +- 需要图片数目:`1` +- 需要文字数目:`0` +- 预览: +
+ +
+ +## luoyonghao_say + +- 关键词:`罗永浩说` +- 需要图片数目:`0` +- 需要文字数目:`1` +- 默认文字:[`又不是不能用`] +- 预览: +
+ +
+ +## luxun_say + +- 关键词:`鲁迅说`、`鲁迅说过` +- 需要图片数目:`0` +- 需要文字数目:`1` +- 默认文字:[`我没有说过这句话`] +- 预览: +
+ +
+ +## maikease + +- 关键词:`麦克阿瑟说` +- 需要图片数目:`0` +- 需要文字数目:`4` +- 默认文字:[`美国前五星上将麦克阿瑟`, `曾这样评价道`, `如果让我去阻止xxx`, `那么我宁愿去阻止上帝`] +- 预览: +
+ +
+ +## maimai_awaken + +- 关键词:`旅行伙伴觉醒` +- 需要图片数目:`1` +- 需要文字数目:`0` +- 预览: +
+ +
+ +## maimai_join + +- 关键词:`旅行伙伴加入` +- 需要图片数目:`1` +- 需要文字数目:`0` +- 预览: +
+ +
+ +## make_friend + +- 关键词:`交个朋友` +- 需要图片数目:`1` +- 需要文字数目:`0` ~ `1` +- 预览: +
+ +
+ +## marriage + +- 关键词:`结婚申请`、`结婚登记` +- 需要图片数目:`1` +- 需要文字数目:`0` +- 预览: +
+ +
+ +## meteor + +- 关键词:`流星` +- 需要图片数目:`0` +- 需要文字数目:`1` +- 默认文字:[`我要对象`] +- 预览: +
+ +
+ +## mihoyo + +- 关键词:`米哈游` +- 需要图片数目:`1` +- 需要文字数目:`0` +- 预览: +
+ +
+ +## mourning + +- 关键词:`上香` +- 需要图片数目:`1` +- 需要文字数目:`0` +- 其他参数: + - `black` + - 描述:是否将图片变为黑白 + - 类型:`boolean` + - 默认值:`False` +- 其他参数(命令行选项): +```shell +usage: meme generate mourning [-h] [--black] + +options: + -h, --help show this help message and exit + --black, /黑白 是否将图片变为黑白 +``` + +- 预览: +> 参数:{"black":false} +
+ +
+ +> 参数:{"black":true} +
+ +
+ +## murmur + +- 关键词:`低语` +- 需要图片数目:`0` +- 需要文字数目:`1` +- 默认文字:[`你的假期余额不足`] +- 预览: +
+ +
+ +## my_friend + +- 关键词:`我朋友说` +- 需要图片数目:`1` +- 需要文字数目:`1` ~ `10` +- 默认文字:[`让我康康`] +- 其他参数: + - `name` + - 描述:指定名字 + - 类型:`string` + - 默认值:`` +- 其他参数(命令行选项): +```shell +usage: meme generate my_friend [-h] [-n NAME] + +options: + -h, --help show this help message and exit + -n NAME, --name NAME 指定名字 +``` + +- 预览: +
+ +
+ +## my_wife + +- 关键词:`我老婆`、`这是我老婆` +- 需要图片数目:`1` +- 需要文字数目:`0` +- 预览: +
+ +
+ +## name_generator + +- 关键词:`亚文化取名机`、`亚名` +- 需要图片数目:`1` +- 需要文字数目:`0` +- 预览: +
+ +
+ +## need + +- 关键词:`需要`、`你可能需要` +- 需要图片数目:`1` +- 需要文字数目:`0` +- 预览: +
+ +
+ +## nekoha_holdsign + +- 关键词:`猫羽雫举牌`、`猫猫举牌` +- 需要图片数目:`0` +- 需要文字数目:`1` +- 默认文字:[`V我50`] +- 预览: +
+ +
+ +## nihaosaoa + +- 关键词:`你好骚啊` +- 需要图片数目:`0` +- 需要文字数目:`3` +- 默认文字:[`既然追求刺激`, `就贯彻到底了`, `你好骚啊`] +- 预览: +
+ +
+ +## nijika_holdsign + +- 关键词:`伊地知虹夏举牌`、`虹夏举牌` +- 需要图片数目:`0` +- 需要文字数目:`1` +- 默认文字:[`你可少看点二次元吧`] +- 预览: +
+ +
+ +## no_response + +- 关键词:`无响应` +- 需要图片数目:`1` +- 需要文字数目:`0` +- 预览: +
+ +
+ +## nokia + +- 关键词:`诺基亚`、`有内鬼` +- 需要图片数目:`0` +- 需要文字数目:`1` +- 默认文字:[`无内鬼,继续交易`] +- 预览: +
+ +
+ +## not_call_me + +- 关键词:`不喊我` +- 需要图片数目:`0` +- 需要文字数目:`1` +- 默认文字:[`开银趴不喊我是吧`] +- 预览: +
+ +
+ +## note_for_leave + +- 关键词:`请假条` +- 需要图片数目:`1` +- 需要文字数目:`0` ~ `1` +- 默认文字:[`想玩`] +- 其他参数: + - `time` + - 描述:指定时间 + - 类型:`string` + - 默认值:`` + - `name` + - 描述:指定名字 + - 类型:`string` + - 默认值:`` +- 其他参数(命令行选项): +```shell +usage: meme generate note_for_leave [-h] [-t TIME] [-n NAME] + +options: + -h, --help show this help message and exit + -t TIME, --time TIME 指定时间 + -n NAME, --name NAME 指定名字 +``` + +- 预览: +
+ +
+ +## oshi_no_ko + +- 关键词:`我推的网友` +- 正则表达式:`我推的(\S+)` +- 需要图片数目:`1` +- 需要文字数目:`0` ~ `1` +- 默认文字:[`网友`] +- 预览: +
+ +
+ +## osu + +- 关键词:`osu` +- 需要图片数目:`0` +- 需要文字数目:`1` +- 默认文字:[`hso!`] +- 预览: +
+ +
+ +## overtime + +- 关键词:`加班` +- 需要图片数目:`1` +- 需要文字数目:`0` +- 预览: +
+ +
+ +## paint + +- 关键词:`这像画吗` +- 需要图片数目:`1` +- 需要文字数目:`0` +- 预览: +
+ +
+ +## painter + +- 关键词:`小画家` +- 需要图片数目:`1` +- 需要文字数目:`0` +- 预览: +
+ +
+ +## pass_the_buck + +- 关键词:`推锅`、`甩锅` +- 需要图片数目:`1` +- 需要文字数目:`0` ~ `1` +- 默认文字:[`你写!`] +- 预览: +
+ +
+ +## pat + +- 关键词:`拍` +- 需要图片数目:`1` +- 需要文字数目:`0` +- 预览: +
+ +
+ +## perfect + +- 关键词:`完美` +- 需要图片数目:`1` +- 需要文字数目:`0` +- 预览: +
+ +
+ +## petpet + +- 关键词:`摸`、`摸摸`、`摸头`、`rua` +- 需要图片数目:`1` +- 需要文字数目:`0` +- 其他参数: + - `circle` + - 描述:是否将图片变为圆形 + - 类型:`boolean` + - 默认值:`False` +- 其他参数(命令行选项): +```shell +usage: meme generate petpet [-h] [--circle] + +options: + -h, --help show this help message and exit + --circle, /圆 是否将图片变为圆形 +``` + +- 预览: +> 参数:{"circle":false} +
+ +
+ +> 参数:{"circle":true} +
+ +
+ +## play + +- 关键词:`顶`、`玩` +- 需要图片数目:`1` +- 需要文字数目:`0` +- 预览: +
+ +
+ +## play_game + +- 关键词:`玩游戏` +- 需要图片数目:`1` +- 需要文字数目:`0` ~ `1` +- 默认文字:[`来玩休闲游戏啊`] +- 预览: +
+ +
+ +## police + +- 关键词:`出警` +- 需要图片数目:`1` +- 需要文字数目:`0` +- 预览: +
+ +
+ +## police1 + +- 关键词:`警察` +- 需要图片数目:`1` +- 需要文字数目:`0` +- 预览: +
+ +
+ +## pornhub + +- 关键词:`ph`、`pornhub` +- 需要图片数目:`0` +- 需要文字数目:`2` +- 默认文字:[`You`, `Tube`] +- 预览: +
+ +
+ +## potato + +- 关键词:`土豆` +- 需要图片数目:`1` +- 需要文字数目:`0` +- 预览: +
+ +
+ +## pound + +- 关键词:`捣` +- 需要图片数目:`1` +- 需要文字数目:`0` +- 预览: +
+ +
+ +## printing + +- 关键词:`打印` +- 需要图片数目:`1` +- 需要文字数目:`0` +- 预览: +
+ +
+ +## prpr + +- 关键词:`舔`、`舔屏`、`prpr` +- 需要图片数目:`1` +- 需要文字数目:`0` +- 预览: +
+ +
+ +## psyduck + +- 关键词:`可达鸭` +- 需要图片数目:`0` +- 需要文字数目:`2` +- 默认文字:[`来份`, `涩图`] +- 预览: +
+ +
+ +## punch + +- 关键词:`打拳` +- 需要图片数目:`1` +- 需要文字数目:`0` +- 预览: +
+ +
+ +## qiegewala + +- 关键词:`切格瓦拉` +- 需要图片数目:`0` +- 需要文字数目:`6` +- 默认文字:[`没有钱啊 肯定要做的啊`, `不做的话没有钱用`, `那你不会去打工啊`, `有手有脚的`, `打工是不可能打工的`, `这辈子不可能打工的`] +- 预览: +
+ +
+ +## raise_image + +- 关键词:`举` +- 需要图片数目:`1` +- 需要文字数目:`0` +- 预览: +
+ +
+ +## raise_sign + +- 关键词:`举牌` +- 需要图片数目:`0` +- 需要文字数目:`1` +- 默认文字:[`大佬带带我`] +- 预览: +
+ +
+ +## read_book + +- 关键词:`看书` +- 需要图片数目:`1` +- 需要文字数目:`0` +- 预览: +
+ +
+ +## repeat + +- 关键词:`复读` +- 需要图片数目:`1` ~ `5` +- 需要文字数目:`1` +- 默认文字:[`救命啊`] +- 预览: +
+ +
+ +## rip + +- 关键词:`撕` +- 需要图片数目:`1` ~ `2` +- 需要文字数目:`0` +- 预览: +
+ +
+ +## rip_angrily + +- 关键词:`怒撕` +- 需要图片数目:`1` +- 需要文字数目:`0` +- 预览: +
+ +
+ +## rise_dead + +- 关键词:`诈尸`、`秽土转生` +- 需要图片数目:`1` +- 需要文字数目:`0` +- 预览: +
+ +
+ +## roll + +- 关键词:`滚` +- 需要图片数目:`1` +- 需要文字数目:`0` +- 预览: +
+ +
+ +## rub + +- 关键词:`贴`、`贴贴`、`蹭`、`蹭蹭` +- 需要图片数目:`2` +- 需要文字数目:`0` +- 预览: +
+ +
+ +## run + +- 关键词:`快跑` +- 需要图片数目:`0` +- 需要文字数目:`1` +- 默认文字:[`快跑`] +- 预览: +
+ +
+ +## safe_sense + +- 关键词:`安全感` +- 需要图片数目:`1` +- 需要文字数目:`0` ~ `1` +- 默认文字:[`你给我的安全感 +远不及它的万分之一`] +- 预览: +
+ +
+ +## scratch_head + +- 关键词:`挠头` +- 需要图片数目:`1` +- 需要文字数目:`0` +- 预览: +
+ +
+ +## scratchcard + +- 关键词:`刮刮乐` +- 需要图片数目:`0` +- 需要文字数目:`1` +- 默认文字:[`谢谢参与`] +- 预览: +
+ +
+ +## scroll + +- 关键词:`滚屏` +- 需要图片数目:`0` +- 需要文字数目:`1` +- 默认文字:[`你们说话啊`] +- 预览: +
+ +
+ +## shishilani + +- 关键词:`食屎啦你` +- 需要图片数目:`0` +- 需要文字数目:`4` +- 默认文字:[`穿西装打领带`, `拿大哥大有什么用`, `跟着这样的大哥`, `食屎啦你`] +- 预览: +
+ +
+ +## shock + +- 关键词:`震惊` +- 需要图片数目:`1` +- 需要文字数目:`0` +- 预览: +
+ +
+ +## shuifandui + +- 关键词:`谁反对` +- 需要图片数目:`0` +- 需要文字数目:`4` +- 默认文字:[`我话说完了`, `谁赞成`, `谁反对`, `我反对`] +- 预览: +
+ +
+ +## shutup + +- 关键词:`别说了` +- 需要图片数目:`0` +- 需要文字数目:`1` +- 默认文字:[`你不要再说了`] +- 预览: +
+ +
+ +## sit_still + +- 关键词:`坐得住`、`坐的住` +- 需要图片数目:`1` +- 需要文字数目:`0` ~ `1` +- 预览: +
+ +
+ +## slap + +- 关键词:`一巴掌` +- 需要图片数目:`0` +- 需要文字数目:`1` +- 预览: +
+ +
+ +## slogan + +- 关键词:`口号` +- 需要图片数目:`0` +- 需要文字数目:`6` +- 默认文字:[`我们是谁?`, `浙大人!`, `到浙大来做什么?`, `混!`, `将来毕业后要做什么样的人?`, `混混!`] +- 预览: +
+ +
+ +## smash + +- 关键词:`砸` +- 需要图片数目:`1` +- 需要文字数目:`0` +- 预览: +
+ +
+ +## step_on + +- 关键词:`踩` +- 需要图片数目:`1` +- 需要文字数目:`0` +- 预览: +
+ +
+ +## suck + +- 关键词:`吸`、`嗦` +- 需要图片数目:`1` +- 需要文字数目:`0` +- 预览: +
+ +
+ +## support + +- 关键词:`精神支柱` +- 需要图片数目:`1` +- 需要文字数目:`0` +- 预览: +
+ +
+ +## symmetric + +- 关键词:`对称` +- 需要图片数目:`1` +- 需要文字数目:`0` +- 其他参数: + - `direction` + - 描述:对称方向 + - 类型:`string` + - 默认值:`left` + - 可选值:`left`、`right`、`top`、`bottom` +- 其他参数(命令行选项): +```shell +usage: meme generate symmetric [-h] + [-d {left,right,top,bottom} | --left | --right | --top | --bottom] + +options: + -h, --help show this help message and exit + -d {left,right,top,bottom}, --direction {left,right,top,bottom} + 对称方向 + --left, /左 + --right, /右 + --top, /上 + --bottom, /下 +``` + +- 预览: +> 参数:{"direction":"left"} +
+ +
+ +> 参数:{"direction":"right"} +
+ +
+ +> 参数:{"direction":"top"} +
+ +
+ +> 参数:{"direction":"bottom"} +
+ +
+ +## tankuku_raisesign + +- 关键词:`唐可可举牌` +- 需要图片数目:`1` +- 需要文字数目:`0` +- 预览: +
+ +
+ +## taunt + +- 关键词:`嘲讽` +- 需要图片数目:`1` +- 需要文字数目:`0` +- 预览: +
+ +
+ +## teach + +- 关键词:`讲课`、`敲黑板` +- 需要图片数目:`1` +- 需要文字数目:`0` ~ `1` +- 默认文字:[`我老婆`] +- 预览: +
+ +
+ +## tease + +- 关键词:`拿捏`、`戏弄` +- 需要图片数目:`1` +- 需要文字数目:`0` +- 预览: +
+ +
+ +## think_what + +- 关键词:`想什么` +- 需要图片数目:`1` +- 需要文字数目:`0` +- 预览: +
+ +
+ +## throw + +- 关键词:`丢`、`扔` +- 需要图片数目:`1` +- 需要文字数目:`0` +- 预览: +
+ +
+ +## throw_gif + +- 关键词:`抛`、`掷` +- 需要图片数目:`1` +- 需要文字数目:`0` +- 预览: +
+ +
+ +## thump + +- 关键词:`捶` +- 需要图片数目:`1` +- 需要文字数目:`0` +- 预览: +
+ +
+ +## thump_wildly + +- 关键词:`捶爆`、`爆捶` +- 需要图片数目:`1` +- 需要文字数目:`0` +- 预览: +
+ +
+ +## tightly + +- 关键词:`紧贴`、`紧紧贴着` +- 需要图片数目:`1` +- 需要文字数目:`0` +- 预览: +
+ +
+ +## together + +- 关键词:`一起` +- 需要图片数目:`1` +- 需要文字数目:`0` ~ `1` +- 预览: +
+ +
+ +## trance + +- 关键词:`恍惚` +- 需要图片数目:`1` +- 需要文字数目:`0` +- 预览: +
+ +
+ +## turn + +- 关键词:`转` +- 需要图片数目:`1` +- 需要文字数目:`0` +- 预览: +
+ +
+ +## twist + +- 关键词:`搓` +- 需要图片数目:`1` +- 需要文字数目:`0` +- 预览: +
+ +
+ +## universal + +- 关键词:`万能表情`、`空白表情` +- 需要图片数目:`1` +- 需要文字数目:`1` ~ `10` +- 默认文字:[`在此处添加文字`] +- 预览: +
+ +
+ +## vibrate + +- 关键词:`震动` +- 需要图片数目:`1` +- 需要文字数目:`0` +- 预览: +
+ +
+ +## wakeup + +- 关键词:`xx起来了` +- 正则表达式:`(.+?)\s+起来了` +- 需要图片数目:`0` +- 需要文字数目:`1` +- 默认文字:[`好`] +- 预览: +
+ +
+ +## wallpaper + +- 关键词:`墙纸` +- 需要图片数目:`1` +- 需要文字数目:`0` +- 预览: +
+ +
+ +## walnut_pad + +- 关键词:`胡桃平板` +- 需要图片数目:`1` +- 需要文字数目:`0` +- 预览: +
+ +
+ +## walnut_zoom + +- 关键词:`胡桃放大` +- 需要图片数目:`1` +- 需要文字数目:`0` +- 预览: +
+ +
+ +## wangjingze + +- 关键词:`王境泽` +- 需要图片数目:`0` +- 需要文字数目:`4` +- 默认文字:[`我就是饿死`, `死外边 从这里跳下去`, `不会吃你们一点东西`, `真香`] +- 预览: +
+ +
+ +## wave + +- 关键词:`波纹` +- 需要图片数目:`1` +- 需要文字数目:`0` +- 预览: +
+ +
+ +## weisuoyuwei + +- 关键词:`为所欲为` +- 需要图片数目:`0` +- 需要文字数目:`9` +- 默认文字:[`好啊`, `就算你是一流工程师`, `就算你出报告再完美`, `我叫你改报告你就要改`, `毕竟我是客户`, `客户了不起啊`, `Sorry 客户真的了不起`, `以后叫他天天改报告`, `天天改 天天改`] +- 预览: +
+ +
+ +## what_I_want_to_do + +- 关键词:`我想上的` +- 需要图片数目:`1` +- 需要文字数目:`0` +- 预览: +
+ +
+ +## what_he_wants + +- 关键词:`最想要的东西` +- 需要图片数目:`1` +- 需要文字数目:`0` ~ `1` +- 默认文字:[`今年520`] +- 预览: +
+ +
+ +## why_at_me + +- 关键词:`为什么@我` +- 需要图片数目:`1` +- 需要文字数目:`0` +- 预览: +
+ +
+ +## why_have_hands + +- 关键词:`为什么要有手` +- 需要图片数目:`1` +- 需要文字数目:`0` ~ `1` +- 预览: +
+ +
+ +## windmill_turn + +- 关键词:`风车转` +- 需要图片数目:`1` +- 需要文字数目:`0` +- 预览: +
+ +
+ +## wish_fail + +- 关键词:`许愿失败` +- 需要图片数目:`0` +- 需要文字数目:`1` +- 默认文字:[`我要对象`] +- 预览: +
+ +
+ +## wooden_fish + +- 关键词:`木鱼` +- 需要图片数目:`1` +- 需要文字数目:`0` +- 预览: +
+ +
+ +## worship + +- 关键词:`膜`、`膜拜` +- 需要图片数目:`1` +- 需要文字数目:`0` +- 预览: +
+ +
+ +## wujing + +- 关键词:`吴京xx中国xx` +- 正则表达式:`吴京[\s::]*(.*?)中国(.*)` +- 需要图片数目:`0` +- 需要文字数目:`2` +- 默认文字:[`不买华为不是`, `人`] +- 预览: +
+ +
+ +## wunian + +- 关键词:`五年怎么过的` +- 需要图片数目:`0` +- 需要文字数目:`4` +- 默认文字:[`五年`, `你知道我这五年是怎么过的吗`, `我每天躲在家里玩贪玩蓝月`, `你知道有多好玩吗`] +- 预览: +
+ +
+ +## yalidaye + +- 关键词:`压力大爷` +- 需要图片数目:`0` +- 需要文字数目:`3` +- 默认文字:[`外界都说我们压力大`, `我觉得吧压力也没有那么大`, `主要是28岁了还没媳妇儿`] +- 预览: +
+ +
+ +## youtube + +- 关键词:`yt`、`youtube` +- 需要图片数目:`0` +- 需要文字数目:`2` +- 默认文字:[`Porn`, `Hub`] +- 预览: +
+ +
+ +## zengxiaoxian + +- 关键词:`曾小贤` +- 需要图片数目:`0` +- 需要文字数目:`4` +- 默认文字:[`平时你打电子游戏吗`, `偶尔`, `星际还是魔兽`, `连连看`] +- 预览: +
+ +
diff --git a/docs/update_doc.py b/docs/update_doc.py new file mode 100644 index 0000000000000000000000000000000000000000..e912118fd9708c0e511f3df7a2687ba887a567b3 --- /dev/null +++ b/docs/update_doc.py @@ -0,0 +1,145 @@ +import asyncio +from pathlib import Path +from typing import Any, Dict + +import filetype + +from meme_generator import get_memes +from meme_generator.meme import Meme + +memes = sorted(get_memes(), key=lambda meme: meme.key) + +image_path = Path("docs/images") + + +async def generate_preview_images(): + for meme in memes: + + async def generate_image(name: str, args: Dict[str, Any] = {}): + for path in image_path.iterdir(): + if name == path.stem: + return + + result = await meme.generate_preview(args=args) + content = result.getvalue() + ext = filetype.guess_extension(content) + filename = f"{name}.{ext}" + with open(image_path / filename, "wb") as f: + f.write(content) + + await generate_image(f"{meme.key}") + if args := meme.params_type.args_type: + if instances := args.instances: + for i, instance in enumerate(instances): + await generate_image(f"{meme.key}_instance{i}", instance.dict()) + + +def meme_doc(meme: Meme) -> str: + 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 = ( + f"{', '.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 += f"\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", {}).copy() + ) + properties.pop("user_infos") + args_info = "\n" + "\n".join( + [arg_info(name, info) for name, info in properties.items()] + ) + else: + args_info = "" + + if args := meme.params_type.args_type: + parser = args.parser + parser_info = parser.format_help() + parser_info = parser_info.replace("update_doc.py", f"meme generate {meme.key}") + else: + parser_info = "" + + def image_doc(name: str) -> str: + for path in image_path.iterdir(): + if name == path.stem: + img_path = path.relative_to(Path("docs")) + return ( + '
\n' + f' \n' + "
" + ) + return "" + + preview_image = "" + if args := meme.params_type.args_type: + if instances := args.instances: + preview_image = "\n\n".join( + [ + f"> 参数:{instance.json(exclude={'user_infos'})}\n" + + image_doc(meme.key + f"_instance{i}") + for i, instance in enumerate(instances) + ] + ) + if not preview_image: + preview_image = image_doc(meme.key) + + return ( + f"## {meme.key}\n\n" + + f"- 关键词:{keywords}\n" + + (f"- 正则表达式:{patterns}\n" if patterns else "") + + f"- 需要图片数目:{image_num}\n" + + f"- 需要文字数目:{text_num}\n" + + (f"- 默认文字:[{default_texts}]\n" if default_texts else "") + + (f"- 其他参数:{args_info}\n" if args_info else "") + + (f"- 其他参数(命令行选项):\n```shell\n{parser_info}```\n\n" if parser_info else "") + + "- 预览:\n" + + f"{preview_image}" + ) + + +def generate_toc(): + return "\n".join( + f"{i}. [{meme.key} ({'/'.join(meme.keywords)})](#{meme.key})" + for i, meme in enumerate(memes, start=1) + ) + + +def generate_doc(): + doc = "# 表情列表\n\n以下为内置表情的关键词、所需参数等信息及表情预览\n\n按照表情的 `key` 排列\n\n\n" + doc += generate_toc() + "\n\n\n" + doc += "\n\n".join(meme_doc(meme) for meme in memes) + "\n" + with open("docs/memes.md", "w") as f: + f.write(doc) + + +async def main(): + await generate_preview_images() + generate_doc() + + +if __name__ == "__main__": + loop = asyncio.new_event_loop() + loop.run_until_complete(main()) diff --git a/meme_generator/__init__.py b/meme_generator/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..dbc36b5253623a4b3d2f70b692a7f89a4d50c3da --- /dev/null +++ b/meme_generator/__init__.py @@ -0,0 +1,21 @@ +from pathlib import Path + +from meme_generator.config import meme_config as config +from meme_generator.manager import add_meme as add_meme +from meme_generator.manager import get_meme as get_meme +from meme_generator.manager import get_meme_keys as get_meme_keys +from meme_generator.manager import get_memes as get_memes +from meme_generator.manager import load_meme as load_meme +from meme_generator.manager import load_memes as load_memes +from meme_generator.meme import Meme as Meme +from meme_generator.meme import MemeArgsModel as MemeArgsModel +from meme_generator.meme import MemeArgsParser as MemeArgsParser +from meme_generator.meme import MemeArgsType as MemeArgsType +from meme_generator.meme import MemeParamsType as MemeParamsType +from meme_generator.version import __version__ as __version__ + +if config.meme.load_builtin_memes: + for path in (Path(__file__).parent / "memes").iterdir(): + load_meme(f"meme_generator.memes.{path.name}") +for meme_dir in config.meme.meme_dirs: + load_memes(meme_dir) diff --git a/meme_generator/app.py b/meme_generator/app.py new file mode 100644 index 0000000000000000000000000000000000000000..20a27306f38d2e7ea575874a0ff7b061b00526a8 --- /dev/null +++ b/meme_generator/app.py @@ -0,0 +1,226 @@ +from typing import Any, Dict, List, Literal, Optional, Tuple + +import filetype +from fastapi import Depends, FastAPI, Form, HTTPException, Response, UploadFile +from pil_utils.types import ColorType, FontStyle, FontWeight +from pydantic import BaseModel, ValidationError + +from meme_generator.config import meme_config +from meme_generator.exception import MemeGeneratorException, NoSuchMeme +from meme_generator.log import LOGGING_CONFIG, setup_logger +from meme_generator.manager import get_meme, get_meme_keys, get_memes +from meme_generator.meme import Meme, MemeArgsModel +from meme_generator.utils import TextProperties, render_meme_list + +app = FastAPI() + + +class MemeArgsResponse(BaseModel): + name: str + type: str + description: Optional[str] = None + default: Optional[Any] = None + enum: Optional[List[Any]] = None + + +class MemeParamsResponse(BaseModel): + min_images: int + max_images: int + min_texts: int + max_texts: int + default_texts: List[str] + args: List[MemeArgsResponse] + + +class MemeInfoResponse(BaseModel): + key: str + keywords: List[str] + patterns: List[str] + params: MemeParamsResponse + + +def register_router(meme: Meme): + if args_type := meme.params_type.args_type: + args_model = args_type.model + else: + args_model = MemeArgsModel + + def args_checker(args: Optional[str] = Form(default=str(args_model().json()))): + if not args: + return MemeArgsModel() + try: + model = args_model.parse_raw(args) + except ValidationError as e: + raise HTTPException(status_code=552, detail=str(e)) + return model + + @app.post(f"/memes/{meme.key}/") + async def _( + images: List[UploadFile] = [], + texts: List[str] = meme.params_type.default_texts, + args: args_model = Depends(args_checker), # type: ignore + ): + imgs: List[bytes] = [] + for image in images: + imgs.append(await image.read()) + + texts = [text for text in texts if text] + + assert isinstance(args, args_model) + + try: + result = await meme(images=imgs, texts=texts, args=args.dict()) + except MemeGeneratorException as e: + raise HTTPException(status_code=e.status_code, detail=str(e)) + + content = result.getvalue() + media_type = str(filetype.guess_mime(content)) or "text/plain" + return Response(content=content, media_type=media_type) + + +class MemeKeyWithProperties(BaseModel): + meme_key: str + fill: ColorType = "black" + style: FontStyle = "normal" + weight: FontWeight = "normal" + stroke_width: int = 0 + stroke_fill: Optional[ColorType] = None + + +default_meme_list = [ + MemeKeyWithProperties(meme_key=meme.key) + for meme in sorted(get_memes(), key=lambda meme: meme.key) +] + + +class RenderMemeListRequest(BaseModel): + meme_list: List[MemeKeyWithProperties] = default_meme_list + order_direction: Literal["row", "column"] = "column" + columns: int = 4 + column_align: Literal["left", "center", "right"] = "left" + item_padding: Tuple[int, int] = (15, 2) + image_padding: Tuple[int, int] = (50, 50) + bg_color: ColorType = "white" + fontsize: int = 30 + fontname: str = "" + fallback_fonts: List[str] = [] + + +def register_routers(): + @app.post("/memes/render_list") + def _(params: RenderMemeListRequest = RenderMemeListRequest()): + try: + meme_list = [ + ( + get_meme(p.meme_key), + TextProperties( + fill=p.fill, + style=p.style, + weight=p.weight, + stroke_width=p.stroke_width, + stroke_fill=p.stroke_fill, + ), + ) + for p in params.meme_list + ] + except NoSuchMeme as e: + raise HTTPException(status_code=e.status_code, detail=str(e)) + + result = render_meme_list( + meme_list, + order_direction=params.order_direction, + columns=params.columns, + column_align=params.column_align, + item_padding=params.item_padding, + image_padding=params.image_padding, + bg_color=params.bg_color, + fontsize=params.fontsize, + fontname=params.fontname, + fallback_fonts=params.fallback_fonts, + ) + content = result.getvalue() + media_type = str(filetype.guess_mime(content)) or "text/plain" + return Response(content=content, media_type=media_type) + + @app.get("/memes/keys") + def _(): + return get_meme_keys() + + @app.get("/memes/{key}/info") + def _(key: str): + try: + meme = get_meme(key) + except NoSuchMeme as e: + raise HTTPException(status_code=e.status_code, detail=str(e)) + + args_model = ( + meme.params_type.args_type.model + if meme.params_type.args_type + else MemeArgsModel + ) + properties: Dict[str, Dict[str, Any]] = ( + args_model.schema().get("properties", {}).copy() + ) + properties.pop("user_infos") + return MemeInfoResponse( + key=meme.key, + keywords=meme.keywords, + patterns=meme.patterns, + params=MemeParamsResponse( + min_images=meme.params_type.min_images, + max_images=meme.params_type.max_images, + min_texts=meme.params_type.min_texts, + max_texts=meme.params_type.max_texts, + default_texts=meme.params_type.default_texts, + args=[ + MemeArgsResponse( + name=name, + type=info.get("type", ""), + description=info.get("description"), + default=info.get("default"), + enum=info.get("enum"), + ) + for name, info in properties.items() + ], + ), + ) + + @app.get("/memes/{key}/preview") + async def _(key: str): + try: + meme = get_meme(key) + result = await meme.generate_preview() + except MemeGeneratorException as e: + raise HTTPException(status_code=e.status_code, detail=str(e)) + + content = result.getvalue() + media_type = str(filetype.guess_mime(content)) or "text/plain" + return Response(content=content, media_type=media_type) + + @app.post("/memes/{key}/parse_args") + async def _(key: str, args: List[str] = []): + try: + meme = get_meme(key) + return meme.parse_args(args) + except MemeGeneratorException as e: + raise HTTPException(status_code=e.status_code, detail=str(e)) + + for meme in sorted(get_memes(), key=lambda meme: meme.key): + register_router(meme) + + +def run_server(): + import uvicorn + + register_routers() + uvicorn.run( + app, + host=meme_config.server.host, + port=meme_config.server.port, + log_config=LOGGING_CONFIG, + ) + + +if __name__ == "__main__": + setup_logger() + run_server() diff --git a/meme_generator/cli.py b/meme_generator/cli.py new file mode 100644 index 0000000000000000000000000000000000000000..d3398b3ed1edaeba7bf4df106f00ce509c7c364e --- /dev/null +++ b/meme_generator/cli.py @@ -0,0 +1,200 @@ +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() diff --git a/meme_generator/config.py b/meme_generator/config.py new file mode 100644 index 0000000000000000000000000000000000000000..b361c9ab02c8142b80b2d2bd6a2289b33e058122 --- /dev/null +++ b/meme_generator/config.py @@ -0,0 +1,72 @@ +import json +from pathlib import Path +from typing import List, Optional, Union + +import toml +from pydantic import BaseModel, Extra + +from .dirs import get_config_file + +config_file_path = get_config_file("config.toml") + + +class MemeConfig(BaseModel): + load_builtin_memes: bool = True + meme_dirs: List[Path] = [] + meme_disabled_list: List[str] = [] + + +class ResourceConfig(BaseModel): + resource_url: Optional[str] = None + resource_urls: List[str] = [ + "https://raw.githubusercontent.com/MeetWq/meme-generator/", + "https://ghproxy.com/https://raw.githubusercontent.com/MeetWq/meme-generator/", + "https://fastly.jsdelivr.net/gh/MeetWq/meme-generator@", + "https://raw.fastgit.org/MeetWq/meme-generator/", + "https://raw.fgit.ml/MeetWq/meme-generator/", + "https://raw.gitmirror.com/MeetWq/meme-generator/", + "https://raw.kgithub.com/MeetWq/meme-generator/", + ] + + +class GifConfig(BaseModel): + gif_max_size: float = 10 + gif_max_frames: int = 100 + + +class TranslatorConfig(BaseModel): + baidu_trans_appid: str = "" + baidu_trans_apikey: str = "" + + +class ServerConfig(BaseModel): + host: str = "127.0.0.1" + port: int = 2233 + + +class LogConfig(BaseModel): + log_level: Union[int, str] = "INFO" + + +class Config(BaseModel, extra=Extra.ignore): + meme: MemeConfig = MemeConfig() + resource: ResourceConfig = ResourceConfig() + gif: GifConfig = GifConfig() + translate: TranslatorConfig = TranslatorConfig() + server: ServerConfig = ServerConfig() + log: LogConfig = LogConfig() + + @classmethod + def load(cls) -> "Config": + return cls.parse_obj(toml.load(config_file_path)) + + def dump(self): + with open(config_file_path, "w", encoding="utf8") as f: + toml.dump(json.loads(self.json()), f) + + +if not config_file_path.exists(): + meme_config = Config() + config_file_path.write_text("", encoding="utf8") +else: + meme_config = Config.load() diff --git a/meme_generator/dirs.py b/meme_generator/dirs.py new file mode 100644 index 0000000000000000000000000000000000000000..d03aeedb99eff29cc9c0a2f3e9c0a15b9c35781b --- /dev/null +++ b/meme_generator/dirs.py @@ -0,0 +1,225 @@ +# https://github.com/nonebot/plugin-localstore +""" +MIT License + +Copyright (c) 2021 NoneBot + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +""" + + +import os +import sys +from pathlib import Path +from typing import Callable, Literal + +from typing_extensions import ParamSpec + +WINDOWS = sys.platform.startswith("win") or (sys.platform == "cli" and os.name == "nt") + + +def user_cache_dir(appname: str) -> Path: + r""" + Return full path to the user-specific cache dir for this application. + "appname" is the name of application. + Typical user cache directories are: + macOS: ~/Library/Caches/ + Unix: ~/.cache/ (XDG default) + Windows: C:\Users\\AppData\Local\\Cache + On Windows the only suggestion in the MSDN docs is that local settings go + in the `CSIDL_LOCAL_APPDATA` directory. This is identical to the + non-roaming app data dir (the default returned by `user_data_dir`). Apps + typically put cache data somewhere *under* the given dir here. Some + examples: + ...\Mozilla\Firefox\Profiles\\Cache + ...\Acme\SuperApp\Cache\1.0 + OPINION: This function appends "Cache" to the `CSIDL_LOCAL_APPDATA` value. + """ + if WINDOWS: + return _get_win_folder("CSIDL_LOCAL_APPDATA") / appname / "Cache" + elif sys.platform == "darwin": + return Path("~/Library/Caches").expanduser() / appname + else: + return Path(os.getenv("XDG_CACHE_HOME", "~/.cache")).expanduser() / appname + + +def user_data_dir(appname: str, roaming: bool = False) -> Path: + r""" + Return full path to the user-specific data dir for this application. + "appname" is the name of application. + If None, just the system directory is returned. + "roaming" (boolean, default False) can be set True to use the Windows + roaming appdata directory. That means that for users on a Windows + network setup for roaming profiles, this user data will be + sync'd on login. See + + for a discussion of issues. + Typical user data directories are: + macOS: ~/Library/Application Support/ + Unix: ~/.local/share/ # or in + $XDG_DATA_HOME, if defined + Win XP (not roaming): C:\Documents and Settings\\ ... + ...Application Data\ + Win XP (roaming): C:\Documents and Settings\\Local ... + ...Settings\Application Data\ + Win 7 (not roaming): C:\Users\\AppData\Local\ + Win 7 (roaming): C:\Users\\AppData\Roaming\ + For Unix, we follow the XDG spec and support $XDG_DATA_HOME. + That means, by default "~/.local/share/". + """ + if WINDOWS: + const = "CSIDL_APPDATA" if roaming else "CSIDL_LOCAL_APPDATA" + return Path(_get_win_folder(const)) / appname + elif sys.platform == "darwin": + return Path("~/Library/Application Support/").expanduser() / appname + else: + return Path(os.getenv("XDG_DATA_HOME", "~/.local/share")).expanduser() / appname + + +def user_config_dir(appname: str, roaming: bool = True) -> Path: + """Return full path to the user-specific config dir for this application. + "appname" is the name of application. + If None, just the system directory is returned. + "roaming" (boolean, default True) can be set False to not use the + Windows roaming appdata directory. That means that for users on a + Windows network setup for roaming profiles, this user data will be + sync'd on login. See + + for a discussion of issues. + Typical user data directories are: + macOS: same as user_data_dir + Unix: ~/.config/ + Win *: same as user_data_dir + For Unix, we follow the XDG spec and support $XDG_CONFIG_HOME. + That means, by default "~/.config/". + """ + if WINDOWS: + return user_data_dir(appname, roaming=roaming) + elif sys.platform == "darwin": + return user_data_dir(appname) + else: + return Path(os.getenv("XDG_CONFIG_HOME", "~/.config")).expanduser() / appname + + +# -- Windows support functions -- +def _get_win_folder_from_registry( + csidl_name: Literal["CSIDL_APPDATA", "CSIDL_COMMON_APPDATA", "CSIDL_LOCAL_APPDATA"] +) -> Path: + """ + This is a fallback technique at best. I'm not sure if using the + registry for this guarantees us the correct answer for all CSIDL_* + names. + """ + import winreg + + shell_folder_name = { + "CSIDL_APPDATA": "AppData", + "CSIDL_COMMON_APPDATA": "Common AppData", + "CSIDL_LOCAL_APPDATA": "Local AppData", + }[csidl_name] + + key = winreg.OpenKey( + winreg.HKEY_CURRENT_USER, + r"Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders", + ) + directory, _type = winreg.QueryValueEx(key, shell_folder_name) + return Path(directory) + + +def _get_win_folder_with_ctypes( + csidl_name: Literal["CSIDL_APPDATA", "CSIDL_COMMON_APPDATA", "CSIDL_LOCAL_APPDATA"] +) -> Path: + csidl_const = { + "CSIDL_APPDATA": 26, + "CSIDL_COMMON_APPDATA": 35, + "CSIDL_LOCAL_APPDATA": 28, + }[csidl_name] + + buf = ctypes.create_unicode_buffer(1024) + ctypes.windll.shell32.SHGetFolderPathW(None, csidl_const, None, 0, buf) + + # Downgrade to short path name if have highbit chars. See + # . + has_high_char = any(ord(c) > 255 for c in buf) + if has_high_char: + buf2 = ctypes.create_unicode_buffer(1024) + if ctypes.windll.kernel32.GetShortPathNameW(buf.value, buf2, 1024): + buf = buf2 + + return Path(buf.value) + + +if WINDOWS: + try: + import ctypes + + _get_win_folder = _get_win_folder_with_ctypes + except ImportError: + _get_win_folder = _get_win_folder_from_registry + + +P = ParamSpec("P") + +APP_NAME = "meme_generator" +BASE_CACHE_DIR = user_cache_dir(APP_NAME).resolve() +BASE_CONFIG_DIR = user_config_dir(APP_NAME).resolve() +BASE_DATA_DIR = user_data_dir(APP_NAME).resolve() + + +def _ensure_dir(path: Path) -> None: + if not path.exists(): + path.mkdir(parents=True, exist_ok=True) + elif not path.is_dir(): + raise RuntimeError(f"{path} is not a directory") + + +def _auto_create_dir(func: Callable[P, Path]) -> Callable[P, Path]: + def wrapper(*args: P.args, **kwargs: P.kwargs) -> Path: + path = func(*args, **kwargs) + _ensure_dir(path) + return path + + return wrapper + + +@_auto_create_dir +def get_cache_dir() -> Path: + return BASE_CACHE_DIR + + +def get_cache_file(filename: str) -> Path: + return get_cache_dir() / filename + + +@_auto_create_dir +def get_config_dir() -> Path: + return BASE_CONFIG_DIR + + +def get_config_file(filename: str) -> Path: + return get_config_dir() / filename + + +@_auto_create_dir +def get_data_dir() -> Path: + return BASE_DATA_DIR + + +def get_data_file(filename: str) -> Path: + return get_data_dir() / filename diff --git a/meme_generator/download.py b/meme_generator/download.py new file mode 100644 index 0000000000000000000000000000000000000000..6faa23f7c5150ec498fad093f6bf3c10a955d61c --- /dev/null +++ b/meme_generator/download.py @@ -0,0 +1,117 @@ +import asyncio +import hashlib +import json +import time +from pathlib import Path +from typing import List, Tuple + +import httpx +from rich.progress import Progress + +from .config import meme_config +from .log import logger +from .version import __version__ + + +def _resource_url(base_url: str, name: str) -> str: + return f"{base_url}v{__version__}/{name}" + + +# https://github.com/mnixry/nonebot-plugin-gocqhttp/blob/main/nonebot_plugin_gocqhttp/process/download.py +async def get_fastest_mirror() -> List[str]: + assert meme_config.resource.resource_urls, "No resource url specified." + + async def head_mirror(client: httpx.AsyncClient, base_url: str): + begin_time = time.time() + response = await client.head( + _resource_url(base_url, "resources/fonts/NotoSansSC-Regular.otf"), timeout=5 + ) + response.raise_for_status() + elapsed_time = (time.time() - begin_time) * 1000 + return {"base_url": base_url, "elapsed_time": elapsed_time} + + async with httpx.AsyncClient() as client: + results = await asyncio.gather( + *( + head_mirror(client, domain) + for domain in meme_config.resource.resource_urls + ), + return_exceptions=True, + ) + results = sorted( + (result for result in results if not isinstance(result, Exception)), + key=lambda r: r["elapsed_time"], + ) + return [result["base_url"] for result in results] + + +async def check_resources(): + semaphore = asyncio.Semaphore(10) + + available_urls = ( + [meme_config.resource.resource_url] + if meme_config.resource.resource_url + else (await get_fastest_mirror()) + ) + logger.debug(f"Available resource urls: {available_urls}") + if not available_urls: + logger.warning("No resource url available.") + return + + async def _download(client: httpx.AsyncClient, name: str): + async with semaphore: + for base_url in available_urls: + url = _resource_url(base_url, name) + try: + resp = await client.get(url, timeout=20, follow_redirects=True) + resp.raise_for_status() + return resp.content + except httpx.HTTPError: + pass + logger.warning(f"{name} download failed!") + + async with httpx.AsyncClient() as client: + if content := await _download(client, "resources/resource_list.json"): + resource_list = json.loads(content.decode("utf-8")) + else: + return + + download_list: List[Tuple[Path, str]] = [] + for resource in resource_list: + file_name = str(resource["path"]) + file_hash = str(resource["hash"]) + file_path = Path(__file__).parent / "memes" / file_name + if ( + file_path.exists() + and hashlib.md5(file_path.read_bytes()).hexdigest() == file_hash + ): + continue + else: + download_list.append((file_path, f"meme_generator/memes/{file_name}")) + + if len(download_list): + logger.info("Downloading images ...") + else: + return + + async with httpx.AsyncClient() as client: + + async def download_image(file_path: Path, file_name: str): + if content := await _download(client, file_name): + file_path.parent.mkdir(parents=True, exist_ok=True) + with file_path.open("wb") as f: + f.write(content) + + with Progress( + *Progress.get_default_columns(), "[yellow]{task.completed}/{task.total}" + ) as progress: + progress_task = progress.add_task( + "[green]Downloading...", total=len(download_list) + ) + tasks = [ + download_image(file_path, file_name) + for file_path, file_name in download_list + ] + for task in asyncio.as_completed(tasks): + await task + progress.update(progress_task, advance=1) diff --git a/meme_generator/exception.py b/meme_generator/exception.py new file mode 100644 index 0000000000000000000000000000000000000000..eb2a5bc462ee6cd35780e2da9ad8344a1a57cb31 --- /dev/null +++ b/meme_generator/exception.py @@ -0,0 +1,119 @@ +from typing import Optional + + +class MemeGeneratorException(Exception): + status_code: int = 520 + + def __init__(self, message: str): + self.message = message + + def __str__(self) -> str: + return self.__repr__() + + def __repr__(self) -> str: + return f"Error in meme_generator: {self.message}" + + +class NoSuchMeme(MemeGeneratorException): + status_code: int = 531 + + def __init__(self, meme_key: str): + self.meme_key = meme_key + message = f'No such meme with key="{self.meme_key}"' + super().__init__(message) + + +class TextOverLength(MemeGeneratorException): + status_code: int = 532 + + def __init__(self, text: str): + self.text = text + message = f'Text "{self.text}" is too long!' + super().__init__(message) + + +class OpenImageFailed(MemeGeneratorException): + status_code: int = 533 + + def __init__(self, error_message: str): + self.error_message = error_message + message = f'Error opening images: "{self.error_message}"' + super().__init__(message) + + +class ParserExit(MemeGeneratorException): + status_code: int = 534 + + def __init__(self, status: int = 0, error_message: Optional[str] = None): + self.status = status + self.error_message = error_message or "" + message = ( + f"Argument parser failed to parse. (status={self.status}" + + (f", message={self.error_message!r}" if self.error_message else "") + + ")" + ) + super().__init__(message) + + +class ParamsMismatch(MemeGeneratorException): + status_code: int = 540 + + def __init__(self, meme_key: str, message: str): + self.meme_key = meme_key + self.message = message + + def __repr__(self) -> str: + return f'ParamsMismatch(key="{self.meme_key}", message="{self.message}")' + + +class ImageNumberMismatch(ParamsMismatch): + status_code: int = 541 + + def __init__(self, meme_key: str, min_images: int = 0, max_images: int = 0): + message = ( + "The number of images is incorrect, " + f"it should be no less than {min_images} and no more than {max_images}" + ) + super().__init__(meme_key, message) + + +class TextNumberMismatch(ParamsMismatch): + status_code: int = 542 + + def __init__(self, meme_key: str, min_texts: int = 0, max_texts: int = 0): + message = ( + "The number of texts is incorrect, " + f"it should be no less than {min_texts} and no more than {max_texts}" + ) + super().__init__(meme_key, message) + + +class TextOrNameNotEnough(ParamsMismatch): + status_code: int = 543 + + def __init__(self, meme_key: str, message: Optional[str] = None): + message = message or "The number of texts or user names is not enough" + super().__init__(meme_key, message) + + +class ArgMismatch(ParamsMismatch): + status_code: int = 550 + pass + + +class ArgParserExit(ArgMismatch): + status_code: int = 551 + + def __init__(self, meme_key: str, error_message: str): + self.error_message = error_message + message = f"Argument parser failed to parse: {self.error_message}" + super().__init__(meme_key, message) + + +class ArgModelMismatch(ArgMismatch): + status_code: int = 552 + + def __init__(self, meme_key: str, error_message: str): + self.error_message = error_message + message = f"Argument model validation failed: {self.error_message}" + super().__init__(meme_key, message) diff --git a/meme_generator/log.py b/meme_generator/log.py new file mode 100644 index 0000000000000000000000000000000000000000..60591ef0cf2a2f4587a8f449c8ed49ccf93c04ba --- /dev/null +++ b/meme_generator/log.py @@ -0,0 +1,85 @@ +# https://github.com/nonebot/nonebot2/blob/master/nonebot/log.py +import logging +import sys +from typing import TYPE_CHECKING + +import loguru + +if TYPE_CHECKING: + from loguru import Logger, Record + +logger: "Logger" = loguru.logger + + +class LoguruHandler(logging.Handler): + """logging 与 loguru 之间的桥梁,将 logging 的日志转发到 loguru。""" + + def emit(self, record: logging.LogRecord): + try: + level = logger.level(record.levelname).name + except ValueError: + level = record.levelno + + frame, depth = logging.currentframe(), 2 + while frame and frame.f_code.co_filename == logging.__file__: + frame = frame.f_back + depth += 1 + + logger.opt(depth=depth, exception=record.exc_info).log( + level, record.getMessage() + ) + + +# https://github.com/nonebot/nonebot2/blob/master/nonebot/drivers/fastapi.py#L182 +LOGGING_CONFIG = { + "version": 1, + "disable_existing_loggers": False, + "handlers": { + "default": { + "class": "meme_generator.log.LoguruHandler", + }, + }, + "loggers": { + "uvicorn.error": {"handlers": ["default"], "level": "INFO"}, + "uvicorn.access": { + "handlers": ["default"], + "level": "INFO", + }, + }, +} + + +def setup_logger(): + from .config import config_file_path, meme_config + + def default_filter(record: "Record"): + """默认的日志过滤器,根据 `log_level` 配置改变日志等级。""" + log_level = meme_config.log.log_level + levelno = ( + logger.level(log_level).no if isinstance(log_level, str) else log_level + ) + return record["level"].no >= levelno + + default_format: str = ( + "{time:MM-DD HH:mm:ss} " + "[{level}] " + "{name} | " + # "{function}:{line}| " + "{message}" + ) + + logger.remove() + logger.add( + sys.stdout, + level=0, + diagnose=False, + filter=default_filter, + format=default_format, + ) + + logger.opt(colors=True).info( + f"Config file path: {config_file_path.resolve()}" + ) + logger.opt(colors=True).debug( + f"Loaded config: {str(meme_config.dict())}" + ) diff --git a/meme_generator/manager.py b/meme_generator/manager.py new file mode 100644 index 0000000000000000000000000000000000000000..78d1477ffdb9c7c0b7a7640e5e12ace363672680 --- /dev/null +++ b/meme_generator/manager.py @@ -0,0 +1,104 @@ +import importlib +import importlib.util +import pkgutil +from pathlib import Path +from typing import Dict, List, Optional, Union + +from .config import meme_config +from .exception import NoSuchMeme +from .log import logger +from .meme import Meme, MemeArgsType, MemeFunction, MemeParamsType + +_memes: Dict[str, Meme] = {} + + +def path_to_module_name(path: Path) -> str: + rel_path = path.resolve().relative_to(Path.cwd().resolve()) + if rel_path.stem == "__init__": + return ".".join(rel_path.parts[:-1]) + else: + return ".".join(rel_path.parts[:-1] + (rel_path.stem,)) + + +def load_meme(module_path: Union[str, Path]): + module_name = ( + path_to_module_name(module_path) + if isinstance(module_path, Path) + else module_path + ) + try: + importlib.import_module(module_name) + except Exception as e: + logger.opt(colors=True, exception=e).error(f"Failed to import {module_path}!") + + +def load_memes(dir_path: Union[str, Path]): + if isinstance(dir_path, Path): + dir_path = str(dir_path.resolve()) + + for module_info in pkgutil.iter_modules([dir_path]): + if module_info.name.startswith("_"): + continue + if not ( + module_spec := module_info.module_finder.find_spec(module_info.name, None) + ): + continue + if not (module_path := module_spec.origin): + continue + if not (module_loader := module_spec.loader): + continue + try: + module = importlib.util.module_from_spec(module_spec) + module_loader.exec_module(module) + except Exception as e: + logger.opt(colors=True, exception=e).error( + f"Failed to import {module_path}!" + ) + + +def add_meme( + key: str, + function: MemeFunction, + *, + min_images: int = 0, + max_images: int = 0, + min_texts: int = 0, + max_texts: int = 0, + default_texts: List[str] = [], + args_type: Optional[MemeArgsType] = None, + keywords: List[str] = [], + patterns: List[str] = [], +): + if key in _memes: + logger.warning(f'Meme with key "{key}" already exists!') + return + + if key in meme_config.meme.meme_disabled_list: + logger.warning(f'The key "{key}" is in the disabled list!') + return + + meme = Meme( + key, + function, + MemeParamsType( + min_images, max_images, min_texts, max_texts, default_texts, args_type + ), + keywords=keywords, + patterns=patterns, + ) + + _memes[key] = meme + + +def get_meme(key: str) -> Meme: + if key not in _memes: + raise NoSuchMeme(key) + return _memes[key] + + +def get_memes() -> List[Meme]: + return list(_memes.values()) + + +def get_meme_keys() -> List[str]: + return list(_memes.keys()) diff --git a/meme_generator/meme.py b/meme_generator/meme.py new file mode 100644 index 0000000000000000000000000000000000000000..632a622ce11b312cd150dc1738fa8159bd020b12 --- /dev/null +++ b/meme_generator/meme.py @@ -0,0 +1,185 @@ +import copy +from argparse import ArgumentError, ArgumentParser +from contextvars import ContextVar +from dataclasses import dataclass, field +from io import BytesIO +from pathlib import Path +from typing import ( + IO, + Any, + Awaitable, + Callable, + Dict, + List, + Literal, + Optional, + Type, + TypeVar, + Union, + cast, +) + +from pil_utils import BuildImage +from pydantic import BaseModel, ValidationError + +from .exception import ( + ArgModelMismatch, + ArgParserExit, + ImageNumberMismatch, + OpenImageFailed, + ParserExit, + TextNumberMismatch, + TextOrNameNotEnough, +) +from .utils import is_coroutine_callable, random_image, random_text, run_sync + + +class UserInfo(BaseModel): + name: str = "" + gender: Literal["male", "female", "unknown"] = "unknown" + + +class MemeArgsModel(BaseModel): + user_infos: List[UserInfo] = [] + + +ArgsModel = TypeVar("ArgsModel", bound=MemeArgsModel) + +MemeFunction = Union[ + Callable[[List[BuildImage], List[str], ArgsModel], BytesIO], + Callable[[List[BuildImage], List[str], ArgsModel], Awaitable[BytesIO]], +] + + +parser_message: ContextVar[str] = ContextVar("parser_message") + + +class MemeArgsParser(ArgumentParser): + """`shell_like` 命令参数解析器,解析出错时不会退出程序。 + + 用法: + 用法与 `argparse.ArgumentParser` 相同, + 参考文档: [argparse](https://docs.python.org/3/library/argparse.html) + """ + + def _print_message(self, message: str, file: Optional[IO[str]] = None): + if (msg := parser_message.get(None)) is not None: + parser_message.set(msg + message) + else: + super()._print_message(message, file) + + def exit(self, status: int = 0, message: Optional[str] = None): + if message: + self._print_message(message) + raise ParserExit(status=status, error_message=parser_message.get(None)) + + +@dataclass +class MemeArgsType: + parser: MemeArgsParser + model: Type[MemeArgsModel] + instances: List[MemeArgsModel] = field(default_factory=list) + + +@dataclass +class MemeParamsType: + min_images: int = 0 + max_images: int = 0 + min_texts: int = 0 + max_texts: int = 0 + default_texts: List[str] = field(default_factory=list) + args_type: Optional[MemeArgsType] = None + + +@dataclass +class Meme: + key: str + function: MemeFunction + params_type: MemeParamsType + keywords: List[str] = field(default_factory=list) + patterns: List[str] = field(default_factory=list) + + async def __call__( + self, + *, + images: Union[List[str], List[Path], List[bytes], List[BytesIO]] = [], + texts: List[str] = [], + args: Dict[str, Any] = {}, + ) -> BytesIO: + if not ( + self.params_type.min_images <= len(images) <= self.params_type.max_images + ): + raise ImageNumberMismatch( + self.key, self.params_type.min_images, self.params_type.max_images + ) + + if not (self.params_type.min_texts <= len(texts) <= self.params_type.max_texts): + raise TextNumberMismatch( + self.key, self.params_type.min_texts, self.params_type.max_texts + ) + + if args_type := self.params_type.args_type: + args_model = args_type.model + else: + args_model = MemeArgsModel + + try: + model = args_model.parse_obj(args) + except ValidationError as e: + raise ArgModelMismatch(self.key, str(e)) + + imgs: List[BuildImage] = [] + try: + for image in images: + if isinstance(image, bytes): + image = BytesIO(image) + imgs.append(BuildImage.open(image)) + except Exception as e: + raise OpenImageFailed(str(e)) + + values = {"images": imgs, "texts": texts, "args": model} + + if is_coroutine_callable(self.function): + return await cast(Callable[..., Awaitable[BytesIO]], self.function)( + **values + ) + else: + return await run_sync(cast(Callable[..., BytesIO], self.function))(**values) + + def parse_args(self, args: List[str] = []) -> Dict[str, Any]: + parser = ( + copy.deepcopy(self.params_type.args_type.parser) + if self.params_type.args_type + else MemeArgsParser() + ) + parser.add_argument("texts", nargs="*", default=[]) + t = parser_message.set("") + try: + return vars(parser.parse_args(args)) + except ArgumentError as e: + raise ArgParserExit(self.key, str(e)) + except ParserExit as e: + raise ArgParserExit(self.key, e.error_message) + finally: + parser_message.reset(t) + + async def generate_preview(self, *, args: Dict[str, Any] = {}) -> BytesIO: + default_images = [random_image() for _ in range(self.params_type.min_images)] + default_texts = ( + self.params_type.default_texts.copy() + if ( + self.params_type.min_texts + <= len(self.params_type.default_texts) + <= self.params_type.max_texts + ) + else [random_text() for _ in range(self.params_type.min_texts)] + ) + + async def _generate_preview(images: List[BytesIO], texts: List[str]): + try: + return await self.__call__(images=images, texts=texts, args=args) + except TextOrNameNotEnough: + texts.append(random_text()) + return await _generate_preview(images, texts) + + return await _generate_preview(default_images, default_texts) diff --git a/meme_generator/memes/5000choyen/__init__.py b/meme_generator/memes/5000choyen/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..2f25662279cef91e9c425fbd9f488b0cc3bf3939 --- /dev/null +++ b/meme_generator/memes/5000choyen/__init__.py @@ -0,0 +1,198 @@ +from typing import List, Tuple + +from PIL.Image import Image as IMG +from PIL.Image import Resampling, Transform +from pil_utils import BuildImage, Text2Image +from pil_utils.gradient import ColorStop, LinearGradient + +from meme_generator import add_meme + + +def fivethousand_choyen(images, texts: List[str], args): + fontsize = 200 + fontname = "Noto Sans SC" + text = texts[0] + pos_x = 40 + pos_y = 220 + imgs: List[Tuple[IMG, Tuple[int, int]]] = [] + + def transform(img: IMG) -> IMG: + skew = 0.45 + dw = round(img.height * skew) + return img.transform( + (img.width + dw, img.height), + Transform.AFFINE, + (1, skew, -dw, 0, 1, 0), + Resampling.BILINEAR, + ) + + def shift(t2m: Text2Image) -> Tuple[int, int]: + return ( + pos_x + - t2m.lines[0].chars[0].stroke_width + - max(char.stroke_width for char in t2m.lines[0].chars), + pos_y - t2m.lines[0].ascent, + ) + + def add_color_text(stroke_width: int, fill: str, pos: Tuple[int, int]): + t2m = Text2Image.from_text( + text, fontsize, fontname=fontname, stroke_width=stroke_width, fill=fill + ) + dx, dy = shift(t2m) + imgs.append((transform(t2m.to_image()), (dx + pos[0], dy + pos[1]))) + + def add_gradient_text( + stroke_width: int, + dir: Tuple[int, int, int, int], + color_stops: List[Tuple[float, Tuple[int, int, int]]], + pos: Tuple[int, int], + ): + t2m = Text2Image.from_text( + text, fontsize, fontname=fontname, stroke_width=stroke_width, fill="white" + ) + mask = transform(t2m.to_image()).convert("L") + dx, dy = shift(t2m) + gradient = LinearGradient( + (dir[0] - dx, dir[1] - dy, dir[2] - dx, dir[3] - dy), + [ColorStop(*color_stop) for color_stop in color_stops], + ) + bg = gradient.create_image(mask.size) + bg.putalpha(mask) + imgs.append((bg, (dx + pos[0], dy + pos[1]))) + + # 黑 + add_color_text(22, "black", (8, 8)) + # 银 + add_gradient_text( + 20, + (0, 38, 0, 234), + [ + (0.0, (0, 15, 36)), + (0.1, (255, 255, 255)), + (0.18, (55, 58, 59)), + (0.25, (55, 58, 59)), + (0.5, (200, 200, 200)), + (0.75, (55, 58, 59)), + (0.85, (25, 20, 31)), + (0.91, (240, 240, 240)), + (0.95, (166, 175, 194)), + (1, (50, 50, 50)), + ], + (8, 8), + ) + # 黑 + add_color_text(16, "black", (0, 0)) + # 金 + add_gradient_text( + 10, + (0, 40, 0, 200), + [ + (0, (253, 241, 0)), + (0.25, (245, 253, 187)), + (0.4, (255, 255, 255)), + (0.75, (253, 219, 9)), + (0.9, (127, 53, 0)), + (1, (243, 196, 11)), + ], + (0, 0), + ) + # 黑 + add_color_text(6, "black", (4, -6)) + # 白 + add_color_text(6, "white", (0, -6)) + # 红 + add_gradient_text( + 4, + (0, 50, 0, 200), + [ + (0, (255, 100, 0)), + (0.5, (123, 0, 0)), + (0.51, (240, 0, 0)), + (1, (5, 0, 0)), + ], + (0, -6), + ) + # 红 + add_gradient_text( + 0, + (0, 50, 0, 200), + [ + (0, (230, 0, 0)), + (0.5, (123, 0, 0)), + (0.51, (240, 0, 0)), + (1, (5, 0, 0)), + ], + (0, -6), + ) + + text = texts[1] + fontname = "Noto Serif SC" + pos_x = 300 + pos_y = 480 + # 黑 + add_color_text(22, "black", (10, 4)) + # 银 + add_gradient_text( + 19, + (0, 320, 0, 506), + [ + (0, (0, 15, 36)), + (0.25, (250, 250, 250)), + (0.5, (150, 150, 150)), + (0.75, (55, 58, 59)), + (0.85, (25, 20, 31)), + (0.91, (240, 240, 240)), + (0.95, (166, 175, 194)), + (1, (50, 50, 50)), + ], + (10, 4), + ) + # 黑 + add_color_text(17, "#10193A", (0, 0)) + # 白 + add_color_text(8, "#D0D0D0", (0, 0)) + # 绀 + add_gradient_text( + 7, + (0, 320, 0, 480), + [ + (0, (16, 25, 58)), + (0.03, (255, 255, 255)), + (0.08, (16, 25, 58)), + (0.2, (16, 25, 58)), + (1, (16, 25, 58)), + ], + (0, 0), + ) + # 银 + add_gradient_text( + 0, + (0, 320, 0, 480), + [ + (0, (245, 246, 248)), + (0.15, (255, 255, 255)), + (0.35, (195, 213, 220)), + (0.5, (160, 190, 201)), + (0.51, (160, 190, 201)), + (0.52, (196, 215, 222)), + (1.0, (255, 255, 255)), + ], + (0, -6), + ) + + img_h = 580 + img_w = max([img.width + pos[0] for img, pos in imgs]) + frame = BuildImage.new("RGBA", (img_w, img_h), "white") + for img, pos in imgs: + frame.paste(img, pos, alpha=True) + return frame.save_jpg() + + +add_meme( + "5000choyen", + fivethousand_choyen, + min_texts=2, + max_texts=2, + default_texts=["我去", "洛天依"], + keywords=["5000兆"], +) diff --git a/meme_generator/memes/acg_entrance/__init__.py b/meme_generator/memes/acg_entrance/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..ead2405cc677132f304515a38a4b3bf6113cd91c --- /dev/null +++ b/meme_generator/memes/acg_entrance/__init__.py @@ -0,0 +1,43 @@ +from pathlib import Path +from typing import List + +from pil_utils import BuildImage + +from meme_generator import add_meme +from meme_generator.exception import TextOverLength +from meme_generator.utils import make_jpg_or_gif + +img_dir = Path(__file__).parent / "images" + + +def acg_entrance(images: List[BuildImage], texts: List[str], args): + text = texts[0] if texts else "走,跟我去二次元吧" + frame = BuildImage.open(img_dir / "0.png") + try: + frame.draw_text( + (30, 720, frame.width - 30, 810), + text, + max_fontsize=50, + min_fontsize=25, + fill="white", + ) + except ValueError: + raise TextOverLength(text) + + def make(img: BuildImage) -> BuildImage: + img = img.convert("RGBA").resize((290, 410), keep_ratio=True) + return frame.copy().paste(img, (190, 265), below=True) + + return make_jpg_or_gif(images[0], make) + + +add_meme( + "acg_entrance", + acg_entrance, + min_images=1, + max_images=1, + min_texts=0, + max_texts=1, + default_texts=["走,跟我去二次元吧"], + keywords=["二次元入口"], +) diff --git a/meme_generator/memes/acg_entrance/images/0.png b/meme_generator/memes/acg_entrance/images/0.png new file mode 100644 index 0000000000000000000000000000000000000000..636d369842dc36fc3eb3162a56290d1effaf652f Binary files /dev/null and b/meme_generator/memes/acg_entrance/images/0.png differ diff --git a/meme_generator/memes/add_chaos/__init__.py b/meme_generator/memes/add_chaos/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..48c37f837af3cc93778d2d9d9e89047827940e18 --- /dev/null +++ b/meme_generator/memes/add_chaos/__init__.py @@ -0,0 +1,27 @@ +from pathlib import Path +from typing import List + +from pil_utils import BuildImage + +from meme_generator import add_meme +from meme_generator.utils import make_jpg_or_gif + +img_dir = Path(__file__).parent / "images" + + +def add_chaos(images: List[BuildImage], texts, args): + banner = BuildImage.open(img_dir / "0.png") + + def make(img: BuildImage) -> BuildImage: + return img.convert("RGBA").resize_width(240).paste(banner) + + return make_jpg_or_gif(images[0], make) + + +add_meme( + "add_chaos", + add_chaos, + min_images=1, + max_images=1, + keywords=["添乱", "给社会添乱"], +) diff --git a/meme_generator/memes/add_chaos/images/0.png b/meme_generator/memes/add_chaos/images/0.png new file mode 100644 index 0000000000000000000000000000000000000000..14937b5997702c9b662724d981abf1af2f046446 Binary files /dev/null and b/meme_generator/memes/add_chaos/images/0.png differ diff --git a/meme_generator/memes/addiction/__init__.py b/meme_generator/memes/addiction/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..650ea6fa7fa2b58d42daa8a6e0514aea8c5c8a50 --- /dev/null +++ b/meme_generator/memes/addiction/__init__.py @@ -0,0 +1,39 @@ +from pathlib import Path +from typing import List + +from pil_utils import BuildImage + +from meme_generator import add_meme +from meme_generator.exception import TextOverLength +from meme_generator.utils import make_jpg_or_gif + +img_dir = Path(__file__).parent / "images" + + +def addiction(images: List[BuildImage], texts: List[str], args): + frame = BuildImage.open(img_dir / "0.png") + + if texts: + text = texts[0] + frame = frame.resize_canvas((246, 286), direction="north", bg_color="white") + try: + frame.draw_text((10, 246, 236, 286), texts[0], max_fontsize=45) + except ValueError: + raise TextOverLength(text) + + def make(img: BuildImage) -> BuildImage: + img = img.convert("RGBA").resize((91, 91), keep_ratio=True) + return frame.copy().paste(img, alpha=True) + + return make_jpg_or_gif(images[0], make) + + +add_meme( + "addiction", + addiction, + min_images=1, + max_images=1, + min_texts=0, + max_texts=1, + keywords=["上瘾", "毒瘾发作"], +) diff --git a/meme_generator/memes/addiction/images/0.png b/meme_generator/memes/addiction/images/0.png new file mode 100644 index 0000000000000000000000000000000000000000..646f9630796c295fbc683c8f1388b0a8d375126c Binary files /dev/null and b/meme_generator/memes/addiction/images/0.png differ diff --git a/meme_generator/memes/alike/__init__.py b/meme_generator/memes/alike/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..658d2c2fa03958a9871b454b38b50b5f1afa437c --- /dev/null +++ b/meme_generator/memes/alike/__init__.py @@ -0,0 +1,24 @@ +from typing import List + +from pil_utils import BuildImage + +from meme_generator import add_meme +from meme_generator.utils import make_jpg_or_gif + + +def alike(images: List[BuildImage], texts, args): + frame = BuildImage.new("RGBA", (470, 180), "white") + frame.draw_text( + (10, 10, 185, 140), "你怎么跟", max_fontsize=40, min_fontsize=30, halign="right" + ).draw_text( + (365, 10, 460, 140), "一样", max_fontsize=40, min_fontsize=30, halign="left" + ) + + def make(img: BuildImage) -> BuildImage: + img = img.convert("RGBA").resize((150, 150), keep_ratio=True) + return frame.copy().paste(img, (200, 15), alpha=True) + + return make_jpg_or_gif(images[0], make) + + +add_meme("alike", alike, min_images=1, max_images=1, keywords=["一样"]) diff --git a/meme_generator/memes/always/__init__.py b/meme_generator/memes/always/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..271c77fdfaaa7b86ea98714b12b267b56ed38155 --- /dev/null +++ b/meme_generator/memes/always/__init__.py @@ -0,0 +1,114 @@ +from typing import List, Literal + +from pil_utils import BuildImage +from pydantic import Field + +from meme_generator import MemeArgsModel, MemeArgsParser, MemeArgsType, add_meme +from meme_generator.utils import ( + FrameAlignPolicy, + Maker, + make_gif_or_combined_gif, + make_jpg_or_gif, +) + +help = "生成模式" + +parser = MemeArgsParser(prefix_chars="-/") +group = parser.add_mutually_exclusive_group() +group.add_argument( + "--mode", + type=str, + choices=["normal", "circle", "loop"], + default="normal", + help=help, +) +group.add_argument("--circle", "/套娃", action="store_const", const="circle", dest="mode") +group.add_argument("--loop", "/循环", action="store_const", const="loop", dest="mode") + + +class Model(MemeArgsModel): + mode: Literal["normal", "loop", "circle"] = Field("normal", description=help) + + +def always_normal(img: BuildImage): + def make(img: BuildImage) -> BuildImage: + img_big = img.convert("RGBA").resize_width(500) + img_small = img.convert("RGBA").resize_width(100) + h1 = img_big.height + h2 = max(img_small.height, 80) + frame = BuildImage.new("RGBA", (500, h1 + h2 + 10), "white") + frame.paste(img_big, alpha=True).paste( + img_small, (290, h1 + 5 + (h2 - img_small.height) // 2), alpha=True + ) + frame.draw_text( + (20, h1 + 5, 280, h1 + h2 + 5), "要我一直", halign="right", max_fontsize=60 + ) + frame.draw_text( + (400, h1 + 5, 480, h1 + h2 + 5), "吗", halign="left", max_fontsize=60 + ) + return frame + + return make_jpg_or_gif(img, make) + + +def always_always(img: BuildImage, loop: bool = False): + tmp_img = img.convert("RGBA").resize_width(500) + img_h = tmp_img.height + text_h = tmp_img.resize_width(100).height + tmp_img.resize_width(20).height + 10 + text_h = max(text_h, 80) + frame_h = img_h + text_h + text_frame = BuildImage.new("RGBA", (500, frame_h), "white") + text_frame.draw_text( + (0, img_h, 280, frame_h), "要我一直", halign="right", max_fontsize=60 + ).draw_text((400, img_h, 500, frame_h), "吗", halign="left", max_fontsize=60) + + frame_num = 20 + coeff = 5 ** (1 / frame_num) + + def maker(i: int) -> Maker: + def make(img: BuildImage) -> BuildImage: + img = img.convert("RGBA").resize_width(500) + base_frame = text_frame.copy().paste(img, alpha=True) + frame = BuildImage.new("RGBA", base_frame.size, "white") + r = coeff**i + for _ in range(4): + x = round(358 * (1 - r)) + y = round(frame_h * (1 - r)) + w = round(500 * r) + h = round(frame_h * r) + frame.paste(base_frame.resize((w, h)), (x, y)) + r /= 5 + return frame + + return make + + if not loop: + return make_jpg_or_gif(img, maker(0)) + + return make_gif_or_combined_gif( + img, maker, frame_num, 0.1, FrameAlignPolicy.extend_loop + ) + + +def always(images: List[BuildImage], texts, args: Model): + img = images[0] + mode = args.mode + + if mode == "normal": + return always_normal(img) + elif mode == "circle": + return always_always(img, loop=False) + else: + return always_always(img, loop=True) + + +add_meme( + "always", + always, + min_images=1, + max_images=1, + args_type=MemeArgsType( + parser, Model, [Model(mode="normal"), Model(mode="circle"), Model(mode="loop")] + ), + keywords=["一直"], +) diff --git a/meme_generator/memes/always_like/__init__.py b/meme_generator/memes/always_like/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..55edf0349806ff39c085d517087ff5544d907615 --- /dev/null +++ b/meme_generator/memes/always_like/__init__.py @@ -0,0 +1,94 @@ +import random +from pathlib import Path +from typing import List + +from pil_utils import BuildImage, Text2Image + +from meme_generator import MemeArgsModel, add_meme +from meme_generator.exception import TextOrNameNotEnough, TextOverLength + +img_dir = Path(__file__).parent / "images" + + +def always_like(images: List[BuildImage], texts: List[str], args: MemeArgsModel): + names = [info.name for info in args.user_infos] + + if len(images) > len(texts) + len(names): + raise TextOrNameNotEnough("always_like") + texts = texts + names + + img = images[0].convert("RGBA") + name = texts[0] + text = "我永远喜欢" + name + + frame = BuildImage.open(img_dir / "0.png") + frame.paste( + img.resize((350, 400), keep_ratio=True, inside=True), (25, 35), alpha=True + ) + try: + frame.draw_text( + (20, 470, frame.width - 20, 570), + text, + max_fontsize=70, + min_fontsize=30, + weight="bold", + ) + except ValueError: + raise TextOverLength(text) + + def random_color(): + return random.choice( + ["red", "darkorange", "gold", "darkgreen", "blue", "cyan", "purple"] + ) + + if len(images) > 1: + text_w = Text2Image.from_text(text, 70).width + ratio = min((frame.width - 40) / text_w, 1) + text_w *= ratio + name_w = Text2Image.from_text(name, 70).width * ratio + start_w = text_w - name_w + (frame.width - text_w) // 2 + frame.draw_line( + (start_w, 525, start_w + name_w, 525), fill=random_color(), width=10 + ) + + current_h = 400 + for i, (image, name) in enumerate(zip(images[1:], texts[1:]), start=1): + img = image.convert("RGBA") + frame.paste( + img.resize((350, 400), keep_ratio=True, inside=True), + (10 + random.randint(0, 50), 20 + random.randint(0, 70)), + alpha=True, + ) + try: + frame.draw_text( + (400, current_h, frame.width - 20, current_h + 80), + name, + max_fontsize=70, + min_fontsize=30, + weight="bold", + ) + except ValueError: + raise TextOverLength(text) + + if len(images) > i + 1: + name_w = min(Text2Image.from_text(name, 70).width, 380) + start_w = 400 + (410 - name_w) // 2 + line_h = current_h + 40 + frame.draw_line( + (start_w, line_h, start_w + name_w, line_h), + fill=random_color(), + width=10, + ) + current_h -= 70 + return frame.save_jpg() + + +add_meme( + "always_like", + always_like, + min_images=1, + max_images=6, + min_texts=0, + max_texts=6, + keywords=["我永远喜欢"], +) diff --git a/meme_generator/memes/always_like/images/0.png b/meme_generator/memes/always_like/images/0.png new file mode 100644 index 0000000000000000000000000000000000000000..d128b88fd178146e937fe549872c46934ef21023 Binary files /dev/null and b/meme_generator/memes/always_like/images/0.png differ diff --git a/meme_generator/memes/anti_kidnap/__init__.py b/meme_generator/memes/anti_kidnap/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..c29cb6339c7f5ea09d92f87a0fceb98d25e0f67c --- /dev/null +++ b/meme_generator/memes/anti_kidnap/__init__.py @@ -0,0 +1,18 @@ +from pathlib import Path +from typing import List + +from pil_utils import BuildImage + +from meme_generator import add_meme + +img_dir = Path(__file__).parent / "images" + + +def anti_kidnap(images: List[BuildImage], texts, args): + img = images[0].convert("RGBA").resize((450, 450), keep_ratio=True) + frame = BuildImage.open(img_dir / "0.png") + frame.paste(img, (30, 78), below=True) + return frame.save_jpg() + + +add_meme("anti_kidnap", anti_kidnap, min_images=1, max_images=1, keywords=["防诱拐"]) diff --git a/meme_generator/memes/anti_kidnap/images/0.png b/meme_generator/memes/anti_kidnap/images/0.png new file mode 100644 index 0000000000000000000000000000000000000000..7aad4f869dc9eae712f1bd729ee21731fb88e512 --- /dev/null +++ b/meme_generator/memes/anti_kidnap/images/0.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5d82a378ed7b8103b80e12b30f3fb812afc17812eb5e8424f5a7d10d836191bb +size 1067686 diff --git a/meme_generator/memes/anya_suki/__init__.py b/meme_generator/memes/anya_suki/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..1ed3aea45e5dc604e5081e4d4c0234b5ab5ebdc3 --- /dev/null +++ b/meme_generator/memes/anya_suki/__init__.py @@ -0,0 +1,44 @@ +from pathlib import Path +from typing import List + +from pil_utils import BuildImage + +from meme_generator import add_meme +from meme_generator.exception import TextOverLength +from meme_generator.utils import make_jpg_or_gif + +img_dir = Path(__file__).parent / "images" + + +def anya_suki(images: List[BuildImage], texts: List[str], args): + text = texts[0] if texts else "阿尼亚喜欢这个" + frame = BuildImage.open(img_dir / "0.png") + try: + frame.draw_text( + (5, frame.height - 60, frame.width - 5, frame.height - 10), + text, + max_fontsize=40, + fill="white", + stroke_fill="black", + stroke_ratio=0.06, + ) + except ValueError: + raise TextOverLength(text) + + def make(img: BuildImage) -> BuildImage: + img = img.convert("RGBA").resize((305, 235), keep_ratio=True) + return frame.copy().paste(img, (106, 72), below=True) + + return make_jpg_or_gif(images[0], make) + + +add_meme( + "anya_suki", + anya_suki, + min_images=1, + max_images=1, + min_texts=0, + max_texts=1, + default_texts=["阿尼亚喜欢这个"], + keywords=["阿尼亚喜欢"], +) diff --git a/meme_generator/memes/anya_suki/images/0.png b/meme_generator/memes/anya_suki/images/0.png new file mode 100644 index 0000000000000000000000000000000000000000..1e84b51c97380358616cfb217256304978c33449 Binary files /dev/null and b/meme_generator/memes/anya_suki/images/0.png differ diff --git a/meme_generator/memes/applaud/__init__.py b/meme_generator/memes/applaud/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..d5ef0eb24c1fb13fd81ee8031c4e480391cc44ed --- /dev/null +++ b/meme_generator/memes/applaud/__init__.py @@ -0,0 +1,31 @@ +from pathlib import Path +from typing import List + +from PIL.Image import Image as IMG +from pil_utils import BuildImage + +from meme_generator import add_meme +from meme_generator.utils import save_gif + +img_dir = Path(__file__).parent / "images" + + +def applaud(images: List[BuildImage], texts, args): + img = images[0].convert("RGBA").square().resize((110, 110)) + frames: List[IMG] = [] + locs = [ + (109, 102, 27, 17), + (107, 105, 28, 15), + (110, 106, 27, 14), + (109, 106, 27, 14), + (107, 108, 29, 12), + ] + for i in range(5): + frame = BuildImage.open(img_dir / f"{i}.png") + w, h, x, y = locs[i] + frame.paste(img.resize((w, h)), (x, y), below=True) + frames.append(frame.image) + return save_gif(frames, 0.1) + + +add_meme("applaud", applaud, min_images=1, max_images=1, keywords=["鼓掌"]) diff --git a/meme_generator/memes/applaud/images/0.png b/meme_generator/memes/applaud/images/0.png new file mode 100644 index 0000000000000000000000000000000000000000..0b87d11bbe8586837a8851a6646541b3e7a0d971 Binary files /dev/null and b/meme_generator/memes/applaud/images/0.png differ diff --git a/meme_generator/memes/applaud/images/1.png b/meme_generator/memes/applaud/images/1.png new file mode 100644 index 0000000000000000000000000000000000000000..45e40754c6491c16c1185e26dcea0e597e61791b Binary files /dev/null and b/meme_generator/memes/applaud/images/1.png differ diff --git a/meme_generator/memes/applaud/images/2.png b/meme_generator/memes/applaud/images/2.png new file mode 100644 index 0000000000000000000000000000000000000000..bde54c33ac025001f8157e9244c2fc38b4bbd0fb Binary files /dev/null and b/meme_generator/memes/applaud/images/2.png differ diff --git a/meme_generator/memes/applaud/images/3.png b/meme_generator/memes/applaud/images/3.png new file mode 100644 index 0000000000000000000000000000000000000000..8c0b25f0d2cfeb75514ddebfb4dbb312ebb4189f Binary files /dev/null and b/meme_generator/memes/applaud/images/3.png differ diff --git a/meme_generator/memes/applaud/images/4.png b/meme_generator/memes/applaud/images/4.png new file mode 100644 index 0000000000000000000000000000000000000000..a39efc7a085ae8797e4b62318338581d29e61017 Binary files /dev/null and b/meme_generator/memes/applaud/images/4.png differ diff --git a/meme_generator/memes/ascension/__init__.py b/meme_generator/memes/ascension/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..933c575873e7af8e9fca21c857a2c19f99f0cbe1 --- /dev/null +++ b/meme_generator/memes/ascension/__init__.py @@ -0,0 +1,35 @@ +from pathlib import Path +from typing import List + +from pil_utils import BuildImage + +from meme_generator import add_meme +from meme_generator.exception import TextOverLength + +img_dir = Path(__file__).parent / "images" + + +def ascension(images, texts: List[str], args): + frame = BuildImage.open(img_dir / "0.png") + text = f"你原本应该要去地狱的,但因为你生前{texts[0]},我们就当作你已经服完刑期了" + try: + frame.draw_text( + (40, 30, 482, 135), + text, + allow_wrap=True, + max_fontsize=50, + min_fontsize=20, + ) + except ValueError: + raise TextOverLength(texts[0]) + return frame.save_jpg() + + +add_meme( + "ascension", + ascension, + min_texts=1, + max_texts=1, + default_texts=["学的是机械"], + keywords=["升天"], +) diff --git a/meme_generator/memes/ascension/images/0.png b/meme_generator/memes/ascension/images/0.png new file mode 100644 index 0000000000000000000000000000000000000000..bbdcbad784b5e8b4decd575f536606b354e54307 Binary files /dev/null and b/meme_generator/memes/ascension/images/0.png differ diff --git a/meme_generator/memes/ask/__init__.py b/meme_generator/memes/ask/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..71fd1828c3286eee2e2a4ad2884bdca84e88df11 --- /dev/null +++ b/meme_generator/memes/ask/__init__.py @@ -0,0 +1,83 @@ +from typing import List + +from PIL import ImageFilter +from pil_utils import BuildImage, Text2Image +from pil_utils.gradient import ColorStop, LinearGradient + +from meme_generator import MemeArgsModel, add_meme +from meme_generator.exception import TextOrNameNotEnough, TextOverLength + + +def ask(images: List[BuildImage], texts: List[str], args: MemeArgsModel): + if not texts and not args.user_infos: + raise TextOrNameNotEnough("ask") + + name = texts[0] if texts else args.user_infos[0].name + ta = "他" if args.user_infos and args.user_infos[0].gender == "male" else "她" + + img = images[0].resize_width(640) + img_w, img_h = img.size + gradient_h = 150 + gradient = LinearGradient( + (0, 0, 0, gradient_h), + [ColorStop(0, (0, 0, 0, 220)), ColorStop(1, (0, 0, 0, 30))], + ) + gradient_img = gradient.create_image((img_w, gradient_h)) + mask = BuildImage.new("RGBA", img.size) + mask.paste(gradient_img, (0, img_h - gradient_h), alpha=True) + mask = mask.filter(ImageFilter.GaussianBlur(radius=3)) + img.paste(mask, alpha=True) + + start_w = 20 + start_h = img_h - gradient_h + 5 + text1 = name + text2 = f"{name}不知道哦。" + text2img1 = Text2Image.from_text(text1, 28, weight="bold") + text2img2 = Text2Image.from_text(text2, 28, weight="bold") + img.draw_text( + (start_w + 40 + (text2img2.width - text2img1.width) // 2, start_h), + text1, + fontsize=28, + fill="orange", + weight="bold", + ) + img.draw_text( + (start_w + 40, start_h + text2img1.height + 10), + text2, + fontsize=28, + fill="white", + weight="bold", + ) + + line_h = start_h + text2img1.height + 5 + img.draw_line( + (start_w, line_h, start_w + text2img2.width + 80, line_h), + fill="orange", + width=2, + ) + + sep_w = 30 + sep_h = 80 + frame = BuildImage.new("RGBA", (img_w + sep_w * 2, img_h + sep_h * 2), "white") + try: + frame.draw_text( + (sep_w, 0, img_w + sep_w, sep_h), + f"让{name}告诉你吧", + max_fontsize=35, + halign="left", + ) + frame.draw_text( + (sep_w, img_h + sep_h, img_w + sep_w, img_h + sep_h * 2), + f"啊这,{ta}说不知道", + max_fontsize=35, + halign="left", + ) + except ValueError: + raise TextOverLength(name) + frame.paste(img, (sep_w, sep_h), alpha=True) + return frame.save_png() + + +add_meme( + "ask", ask, min_images=1, max_images=1, min_texts=0, max_texts=1, keywords=["问问"] +) diff --git a/meme_generator/memes/back_to_work/__init__.py b/meme_generator/memes/back_to_work/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..25bd82ef140677eb4463d51d07192c575c211d97 --- /dev/null +++ b/meme_generator/memes/back_to_work/__init__.py @@ -0,0 +1,22 @@ +from pathlib import Path +from typing import List + +from pil_utils import BuildImage + +from meme_generator import add_meme + +img_dir = Path(__file__).parent / "images" + + +def back_to_work(images: List[BuildImage], texts, args): + frame = BuildImage.open(img_dir / "0.png") + img = ( + images[0].convert("RGBA").resize((220, 310), keep_ratio=True, direction="north") + ) + frame.paste(img.rotate(25, expand=True), (56, 32), below=True) + return frame.save_jpg() + + +add_meme( + "back_to_work", back_to_work, min_images=1, max_images=1, keywords=["继续干活", "打工人"] +) diff --git a/meme_generator/memes/back_to_work/images/0.png b/meme_generator/memes/back_to_work/images/0.png new file mode 100644 index 0000000000000000000000000000000000000000..ab0fadc04776c3b096b1105edbedb07c46414f83 Binary files /dev/null and b/meme_generator/memes/back_to_work/images/0.png differ diff --git a/meme_generator/memes/bad_news/__init__.py b/meme_generator/memes/bad_news/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..9a56c6354289015f6404ee4ed77dce84a117d5e3 --- /dev/null +++ b/meme_generator/memes/bad_news/__init__.py @@ -0,0 +1,39 @@ +from pathlib import Path +from typing import List + +from pil_utils import BuildImage + +from meme_generator import add_meme +from meme_generator.exception import TextOverLength + +img_dir = Path(__file__).parent / "images" + + +def bad_news(images, texts: List[str], args): + text = texts[0] + frame = BuildImage.open(img_dir / "0.png") + try: + frame.draw_text( + (50, 100, frame.width - 50, frame.height - 100), + text, + allow_wrap=True, + lines_align="center", + max_fontsize=60, + min_fontsize=30, + fill=(0, 0, 0), + stroke_ratio=1 / 15, + stroke_fill="white", + ) + except ValueError: + raise TextOverLength(text) + return frame.save_png() + + +add_meme( + "bad_news", + bad_news, + min_texts=1, + max_texts=1, + default_texts=["喜报"], + keywords=["悲报"], +) diff --git a/meme_generator/memes/bad_news/images/0.png b/meme_generator/memes/bad_news/images/0.png new file mode 100644 index 0000000000000000000000000000000000000000..edfe71c1767edb21437f9623f12824fef2076e22 Binary files /dev/null and b/meme_generator/memes/bad_news/images/0.png differ diff --git a/meme_generator/memes/beat_head/__init__.py b/meme_generator/memes/beat_head/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..bc139b07ae703f255375b4fdd146adf55c16bbea --- /dev/null +++ b/meme_generator/memes/beat_head/__init__.py @@ -0,0 +1,47 @@ +from pathlib import Path +from typing import List + +from PIL.Image import Image as IMG +from pil_utils import BuildImage + +from meme_generator import add_meme +from meme_generator.exception import TextOverLength +from meme_generator.utils import save_gif + +img_dir = Path(__file__).parent / "images" + + +def beat_head(images: List[BuildImage], texts: List[str], args): + text = texts[0] if texts else "怎么说话的你" + img = images[0].convert("RGBA") + locs = [(160, 121, 76, 76), (172, 124, 69, 69), (208, 166, 52, 52)] + frames: List[IMG] = [] + for i in range(3): + x, y, w, h = locs[i] + head = img.resize((w, h), keep_ratio=True).circle() + frame = BuildImage.open(img_dir / f"{i}.png") + frame.paste(head, (x, y), below=True) + try: + frame.draw_text( + (175, 28, 316, 82), + text, + max_fontsize=50, + min_fontsize=10, + allow_wrap=True, + ) + except ValueError: + raise TextOverLength(text) + + frames.append(frame.image) + return save_gif(frames, 0.05) + + +add_meme( + "beat_head", + beat_head, + min_images=1, + max_images=1, + min_texts=0, + max_texts=1, + keywords=["拍头"], +) diff --git a/meme_generator/memes/beat_head/images/0.png b/meme_generator/memes/beat_head/images/0.png new file mode 100644 index 0000000000000000000000000000000000000000..a40d09aaf3812be49fc47ae40865637c34e619ec Binary files /dev/null and b/meme_generator/memes/beat_head/images/0.png differ diff --git a/meme_generator/memes/beat_head/images/1.png b/meme_generator/memes/beat_head/images/1.png new file mode 100644 index 0000000000000000000000000000000000000000..5d11e7973928b95b3c63e5577a19cb563e0752c3 Binary files /dev/null and b/meme_generator/memes/beat_head/images/1.png differ diff --git a/meme_generator/memes/beat_head/images/2.png b/meme_generator/memes/beat_head/images/2.png new file mode 100644 index 0000000000000000000000000000000000000000..3b8485f832ae56f05e8eb74150d29c0d73dcf9b8 Binary files /dev/null and b/meme_generator/memes/beat_head/images/2.png differ diff --git a/meme_generator/memes/bite/__init__.py b/meme_generator/memes/bite/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..b04285449cce30c420ea7e822114af76abb6c4e3 --- /dev/null +++ b/meme_generator/memes/bite/__init__.py @@ -0,0 +1,33 @@ +from pathlib import Path +from typing import List + +from PIL.Image import Image as IMG +from pil_utils import BuildImage + +from meme_generator import add_meme +from meme_generator.utils import save_gif + +img_dir = Path(__file__).parent / "images" + + +def bite(images: List[BuildImage], texts, args): + img = images[0].convert("RGBA").square() + frames: List[IMG] = [] + # fmt: off + locs = [ + (90, 90, 105, 150), (90, 83, 96, 172), (90, 90, 106, 148), + (88, 88, 97, 167), (90, 85, 89, 179), (90, 90, 106, 151) + ] + # fmt: on + for i in range(6): + frame = BuildImage.open(img_dir / f"{i}.png") + w, h, x, y = locs[i] + frame.paste(img.resize((w, h)), (x, y), below=True) + frames.append(frame.image) + for i in range(6, 16): + frame = BuildImage.open(img_dir / f"{i}.png") + frames.append(frame.image) + return save_gif(frames, 0.07) + + +add_meme("bite", bite, min_images=1, max_images=1, keywords=["啃"]) diff --git a/meme_generator/memes/bite/images/0.png b/meme_generator/memes/bite/images/0.png new file mode 100644 index 0000000000000000000000000000000000000000..cc5cfb69a6e5a2d0361ad71aa351e17f753217d0 Binary files /dev/null and b/meme_generator/memes/bite/images/0.png differ diff --git a/meme_generator/memes/bite/images/1.png b/meme_generator/memes/bite/images/1.png new file mode 100644 index 0000000000000000000000000000000000000000..7cc81306093efb51a0a8995eb03725345db1a3c9 Binary files /dev/null and b/meme_generator/memes/bite/images/1.png differ diff --git a/meme_generator/memes/bite/images/10.png b/meme_generator/memes/bite/images/10.png new file mode 100644 index 0000000000000000000000000000000000000000..57d96ef4e2d5e6b1ef0564bc86868f71081f37be Binary files /dev/null and b/meme_generator/memes/bite/images/10.png differ diff --git a/meme_generator/memes/bite/images/11.png b/meme_generator/memes/bite/images/11.png new file mode 100644 index 0000000000000000000000000000000000000000..e8efdb3823760e3596a786a23554a7772f33ea40 Binary files /dev/null and b/meme_generator/memes/bite/images/11.png differ diff --git a/meme_generator/memes/bite/images/12.png b/meme_generator/memes/bite/images/12.png new file mode 100644 index 0000000000000000000000000000000000000000..74201a4c3b86119a9a1fa1ab354e7cbc625da3cb Binary files /dev/null and b/meme_generator/memes/bite/images/12.png differ diff --git a/meme_generator/memes/bite/images/13.png b/meme_generator/memes/bite/images/13.png new file mode 100644 index 0000000000000000000000000000000000000000..579b1e0dc0e9fb54fc0accec64629312b3e93f39 Binary files /dev/null and b/meme_generator/memes/bite/images/13.png differ diff --git a/meme_generator/memes/bite/images/14.png b/meme_generator/memes/bite/images/14.png new file mode 100644 index 0000000000000000000000000000000000000000..57d96ef4e2d5e6b1ef0564bc86868f71081f37be Binary files /dev/null and b/meme_generator/memes/bite/images/14.png differ diff --git a/meme_generator/memes/bite/images/15.png b/meme_generator/memes/bite/images/15.png new file mode 100644 index 0000000000000000000000000000000000000000..e8efdb3823760e3596a786a23554a7772f33ea40 Binary files /dev/null and b/meme_generator/memes/bite/images/15.png differ diff --git a/meme_generator/memes/bite/images/2.png b/meme_generator/memes/bite/images/2.png new file mode 100644 index 0000000000000000000000000000000000000000..a602601997d2b32d409d04d2335dd8f01ee18700 Binary files /dev/null and b/meme_generator/memes/bite/images/2.png differ diff --git a/meme_generator/memes/bite/images/3.png b/meme_generator/memes/bite/images/3.png new file mode 100644 index 0000000000000000000000000000000000000000..b40256d68d2d78c9e3e9237c2f10c8f7a45ab631 Binary files /dev/null and b/meme_generator/memes/bite/images/3.png differ diff --git a/meme_generator/memes/bite/images/4.png b/meme_generator/memes/bite/images/4.png new file mode 100644 index 0000000000000000000000000000000000000000..e9006f29742040578f523ba7956368a3dbda79dd Binary files /dev/null and b/meme_generator/memes/bite/images/4.png differ diff --git a/meme_generator/memes/bite/images/5.png b/meme_generator/memes/bite/images/5.png new file mode 100644 index 0000000000000000000000000000000000000000..adffbd3a061fe74ccfd48788af4a2f222508446a Binary files /dev/null and b/meme_generator/memes/bite/images/5.png differ diff --git a/meme_generator/memes/bite/images/6.png b/meme_generator/memes/bite/images/6.png new file mode 100644 index 0000000000000000000000000000000000000000..8a071611508de85ee264d56a2ce7b74cf2938399 Binary files /dev/null and b/meme_generator/memes/bite/images/6.png differ diff --git a/meme_generator/memes/bite/images/7.png b/meme_generator/memes/bite/images/7.png new file mode 100644 index 0000000000000000000000000000000000000000..60becb0cf0e6b9c913435a0dbca01b809b1855fa Binary files /dev/null and b/meme_generator/memes/bite/images/7.png differ diff --git a/meme_generator/memes/bite/images/8.png b/meme_generator/memes/bite/images/8.png new file mode 100644 index 0000000000000000000000000000000000000000..74201a4c3b86119a9a1fa1ab354e7cbc625da3cb Binary files /dev/null and b/meme_generator/memes/bite/images/8.png differ diff --git a/meme_generator/memes/bite/images/9.png b/meme_generator/memes/bite/images/9.png new file mode 100644 index 0000000000000000000000000000000000000000..579b1e0dc0e9fb54fc0accec64629312b3e93f39 Binary files /dev/null and b/meme_generator/memes/bite/images/9.png differ diff --git a/meme_generator/memes/blood_pressure/__init__.py b/meme_generator/memes/blood_pressure/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..ae263919b2f352875ad353d397aba3f9b37e15e3 --- /dev/null +++ b/meme_generator/memes/blood_pressure/__init__.py @@ -0,0 +1,22 @@ +from pathlib import Path +from typing import List + +from pil_utils import BuildImage + +from meme_generator import add_meme +from meme_generator.utils import make_jpg_or_gif + +img_dir = Path(__file__).parent / "images" + + +def blood_pressure(images: List[BuildImage], texts, args): + frame = BuildImage.open(img_dir / "0.png") + + def make(img: BuildImage) -> BuildImage: + img = img.convert("RGBA").resize((414, 450), keep_ratio=True) + return frame.copy().paste(img, (16, 17), below=True) + + return make_jpg_or_gif(images[0], make) + + +add_meme("blood_pressure", blood_pressure, min_images=1, max_images=1, keywords=["高血压"]) diff --git a/meme_generator/memes/blood_pressure/images/0.png b/meme_generator/memes/blood_pressure/images/0.png new file mode 100644 index 0000000000000000000000000000000000000000..804ecdb205170bc53e6dc907f79d35e4089155fa Binary files /dev/null and b/meme_generator/memes/blood_pressure/images/0.png differ diff --git a/meme_generator/memes/bocchi_draft/__init__.py b/meme_generator/memes/bocchi_draft/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e740c626395da18c79052b83ee0074b98f14d2c9 --- /dev/null +++ b/meme_generator/memes/bocchi_draft/__init__.py @@ -0,0 +1,42 @@ +from pathlib import Path +from typing import List + +from PIL.Image import Image as IMG +from pil_utils import BuildImage + +from meme_generator import add_meme +from meme_generator.utils import save_gif + +img_dir = Path(__file__).parent / "images" + + +def bocchi_draft(images: List[BuildImage], texts, args): + img = images[0].convert("RGBA").resize((350, 400), keep_ratio=True) + params = [ + (((54, 62), (353, 1), (379, 382), (1, 399)), (146, 173)), + (((54, 61), (349, 1), (379, 381), (1, 398)), (146, 174)), + (((54, 61), (349, 1), (379, 381), (1, 398)), (152, 174)), + (((54, 61), (335, 1), (379, 381), (1, 398)), (158, 167)), + (((54, 61), (335, 1), (370, 381), (1, 398)), (157, 149)), + (((41, 59), (321, 1), (357, 379), (1, 396)), (167, 108)), + (((41, 57), (315, 1), (357, 377), (1, 394)), (173, 69)), + (((41, 56), (309, 1), (353, 380), (1, 393)), (175, 43)), + (((41, 56), (314, 1), (353, 380), (1, 393)), (174, 30)), + (((41, 50), (312, 1), (348, 367), (1, 387)), (171, 18)), + (((35, 50), (306, 1), (342, 367), (1, 386)), (178, 14)), + ] + # fmt: off + idx = [ + 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 2, 2, 3, 4, 5, 6, 7, 8, 9, 10, 10, 10, 10, + ] + # fmt: on + frames: List[IMG] = [] + for i in range(23): + frame = BuildImage.open(img_dir / f"{i}.png") + points, pos = params[idx[i]] + frame.paste(img.perspective(points), pos, below=True) + frames.append(frame.image) + return save_gif(frames, 0.08) + + +add_meme("bocchi_draft", bocchi_draft, min_images=1, max_images=1, keywords=["波奇手稿"]) diff --git a/meme_generator/memes/bocchi_draft/images/0.png b/meme_generator/memes/bocchi_draft/images/0.png new file mode 100644 index 0000000000000000000000000000000000000000..4992d31934d372eaea96fe2ce0170bc70b15c0c0 Binary files /dev/null and b/meme_generator/memes/bocchi_draft/images/0.png differ diff --git a/meme_generator/memes/bocchi_draft/images/1.png b/meme_generator/memes/bocchi_draft/images/1.png new file mode 100644 index 0000000000000000000000000000000000000000..4992d31934d372eaea96fe2ce0170bc70b15c0c0 Binary files /dev/null and b/meme_generator/memes/bocchi_draft/images/1.png differ diff --git a/meme_generator/memes/bocchi_draft/images/10.png b/meme_generator/memes/bocchi_draft/images/10.png new file mode 100644 index 0000000000000000000000000000000000000000..e0bf8c63fa9ee890208ca444a39ed221979584d9 Binary files /dev/null and b/meme_generator/memes/bocchi_draft/images/10.png differ diff --git a/meme_generator/memes/bocchi_draft/images/11.png b/meme_generator/memes/bocchi_draft/images/11.png new file mode 100644 index 0000000000000000000000000000000000000000..fb2f2c70c8c8d1e19c6e89e8fc450de4fd6f1e87 Binary files /dev/null and b/meme_generator/memes/bocchi_draft/images/11.png differ diff --git a/meme_generator/memes/bocchi_draft/images/12.png b/meme_generator/memes/bocchi_draft/images/12.png new file mode 100644 index 0000000000000000000000000000000000000000..6981f79468a995028905b840ede10b84bf28bdf4 Binary files /dev/null and b/meme_generator/memes/bocchi_draft/images/12.png differ diff --git a/meme_generator/memes/bocchi_draft/images/13.png b/meme_generator/memes/bocchi_draft/images/13.png new file mode 100644 index 0000000000000000000000000000000000000000..3cb149bcefa942f91631104522f1b802b8e7fc69 Binary files /dev/null and b/meme_generator/memes/bocchi_draft/images/13.png differ diff --git a/meme_generator/memes/bocchi_draft/images/14.png b/meme_generator/memes/bocchi_draft/images/14.png new file mode 100644 index 0000000000000000000000000000000000000000..317b3c002c883ec3c3e1836d7f9c8c25a98375f4 Binary files /dev/null and b/meme_generator/memes/bocchi_draft/images/14.png differ diff --git a/meme_generator/memes/bocchi_draft/images/15.png b/meme_generator/memes/bocchi_draft/images/15.png new file mode 100644 index 0000000000000000000000000000000000000000..e56414fab4f467255bc26335b9b9dc4535d7be52 Binary files /dev/null and b/meme_generator/memes/bocchi_draft/images/15.png differ diff --git a/meme_generator/memes/bocchi_draft/images/16.png b/meme_generator/memes/bocchi_draft/images/16.png new file mode 100644 index 0000000000000000000000000000000000000000..4dba7c28fa16ce4f4e8f08f50343c3c8dc1f7310 Binary files /dev/null and b/meme_generator/memes/bocchi_draft/images/16.png differ diff --git a/meme_generator/memes/bocchi_draft/images/17.png b/meme_generator/memes/bocchi_draft/images/17.png new file mode 100644 index 0000000000000000000000000000000000000000..0d35534f47f7d563e5cc2cda87353b201278ce77 Binary files /dev/null and b/meme_generator/memes/bocchi_draft/images/17.png differ diff --git a/meme_generator/memes/bocchi_draft/images/18.png b/meme_generator/memes/bocchi_draft/images/18.png new file mode 100644 index 0000000000000000000000000000000000000000..06aeb063f755b7339f17454460d19872ae899533 Binary files /dev/null and b/meme_generator/memes/bocchi_draft/images/18.png differ diff --git a/meme_generator/memes/bocchi_draft/images/19.png b/meme_generator/memes/bocchi_draft/images/19.png new file mode 100644 index 0000000000000000000000000000000000000000..f3a30512538222041ee805523f0f2861f5f32b77 Binary files /dev/null and b/meme_generator/memes/bocchi_draft/images/19.png differ diff --git a/meme_generator/memes/bocchi_draft/images/2.png b/meme_generator/memes/bocchi_draft/images/2.png new file mode 100644 index 0000000000000000000000000000000000000000..4992d31934d372eaea96fe2ce0170bc70b15c0c0 Binary files /dev/null and b/meme_generator/memes/bocchi_draft/images/2.png differ diff --git a/meme_generator/memes/bocchi_draft/images/20.png b/meme_generator/memes/bocchi_draft/images/20.png new file mode 100644 index 0000000000000000000000000000000000000000..b0ca7bdf273c71dd684fdb138cd9e83ce788eaa9 Binary files /dev/null and b/meme_generator/memes/bocchi_draft/images/20.png differ diff --git a/meme_generator/memes/bocchi_draft/images/21.png b/meme_generator/memes/bocchi_draft/images/21.png new file mode 100644 index 0000000000000000000000000000000000000000..b0ca7bdf273c71dd684fdb138cd9e83ce788eaa9 Binary files /dev/null and b/meme_generator/memes/bocchi_draft/images/21.png differ diff --git a/meme_generator/memes/bocchi_draft/images/22.png b/meme_generator/memes/bocchi_draft/images/22.png new file mode 100644 index 0000000000000000000000000000000000000000..b0ca7bdf273c71dd684fdb138cd9e83ce788eaa9 Binary files /dev/null and b/meme_generator/memes/bocchi_draft/images/22.png differ diff --git a/meme_generator/memes/bocchi_draft/images/3.png b/meme_generator/memes/bocchi_draft/images/3.png new file mode 100644 index 0000000000000000000000000000000000000000..a4f1294891073b3a08db5ddf3f222fcde5dfbb12 Binary files /dev/null and b/meme_generator/memes/bocchi_draft/images/3.png differ diff --git a/meme_generator/memes/bocchi_draft/images/4.png b/meme_generator/memes/bocchi_draft/images/4.png new file mode 100644 index 0000000000000000000000000000000000000000..097ad44b0e5502a383a967d03d13aeb61464875f Binary files /dev/null and b/meme_generator/memes/bocchi_draft/images/4.png differ diff --git a/meme_generator/memes/bocchi_draft/images/5.png b/meme_generator/memes/bocchi_draft/images/5.png new file mode 100644 index 0000000000000000000000000000000000000000..389c2b07677762faea99506839c1539bfd841713 Binary files /dev/null and b/meme_generator/memes/bocchi_draft/images/5.png differ diff --git a/meme_generator/memes/bocchi_draft/images/6.png b/meme_generator/memes/bocchi_draft/images/6.png new file mode 100644 index 0000000000000000000000000000000000000000..9d5e3d4de4634b381f0a42a6d22aa9192806e04e Binary files /dev/null and b/meme_generator/memes/bocchi_draft/images/6.png differ diff --git a/meme_generator/memes/bocchi_draft/images/7.png b/meme_generator/memes/bocchi_draft/images/7.png new file mode 100644 index 0000000000000000000000000000000000000000..87230b589d01a1ad493357592de2c78fa5164ce6 Binary files /dev/null and b/meme_generator/memes/bocchi_draft/images/7.png differ diff --git a/meme_generator/memes/bocchi_draft/images/8.png b/meme_generator/memes/bocchi_draft/images/8.png new file mode 100644 index 0000000000000000000000000000000000000000..b9fefa6e78fc6c092f82360629a52eb05adad536 Binary files /dev/null and b/meme_generator/memes/bocchi_draft/images/8.png differ diff --git a/meme_generator/memes/bocchi_draft/images/9.png b/meme_generator/memes/bocchi_draft/images/9.png new file mode 100644 index 0000000000000000000000000000000000000000..e201d45d45382040becb0da014d3af5462c1ac2d Binary files /dev/null and b/meme_generator/memes/bocchi_draft/images/9.png differ diff --git a/meme_generator/memes/bronya_holdsign/__init__.py b/meme_generator/memes/bronya_holdsign/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..88477c851a82a4e6dda427495f4c45a4224894e6 --- /dev/null +++ b/meme_generator/memes/bronya_holdsign/__init__.py @@ -0,0 +1,37 @@ +from pathlib import Path +from typing import List + +from pil_utils import BuildImage + +from meme_generator import add_meme +from meme_generator.exception import TextOverLength + +img_dir = Path(__file__).parent / "images" + + +def bronya_holdsign(images, texts: List[str], args): + text = texts[0] + frame = BuildImage.open(img_dir / "0.jpg") + try: + frame.draw_text( + (190, 675, 640, 930), + text, + fill=(111, 95, 95), + allow_wrap=True, + max_fontsize=60, + min_fontsize=25, + lines_align="center", + ) + except ValueError: + raise TextOverLength(text) + return frame.save_jpg() + + +add_meme( + "bronya_holdsign", + bronya_holdsign, + min_texts=1, + max_texts=1, + default_texts=["V我50"], + keywords=["布洛妮娅举牌", "大鸭鸭举牌"], +) diff --git a/meme_generator/memes/bronya_holdsign/images/0.jpg b/meme_generator/memes/bronya_holdsign/images/0.jpg new file mode 100644 index 0000000000000000000000000000000000000000..3b7d393b2b4217fdd5573e7801282e6e571d89fc Binary files /dev/null and b/meme_generator/memes/bronya_holdsign/images/0.jpg differ diff --git a/meme_generator/memes/bubble_tea/__init__.py b/meme_generator/memes/bubble_tea/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..129e2fe9e596ba88260bb7fa68e57af58119751e --- /dev/null +++ b/meme_generator/memes/bubble_tea/__init__.py @@ -0,0 +1,61 @@ +from pathlib import Path +from typing import List, Literal + +from PIL.Image import Transpose +from pil_utils import BuildImage +from pydantic import Field + +from meme_generator import MemeArgsModel, MemeArgsParser, MemeArgsType, add_meme + +img_dir = Path(__file__).parent / "images" + + +help = "奶茶的位置" + +parser = MemeArgsParser(prefix_chars="-/") +group = parser.add_mutually_exclusive_group() +group.add_argument( + "-p", + "--position", + dest="position", + type=str, + choices=["right", "left", "both"], + default="right", + help=help, +) +group.add_argument( + "--right", "/右手", action="store_const", const="right", dest="position" +) +group.add_argument("--left", "/左手", action="store_const", const="left", dest="position") +group.add_argument("--both", "/双手", action="store_const", const="both", dest="position") + + +class Model(MemeArgsModel): + position: Literal["right", "left", "both"] = Field("right", description=help) + + +def bubble_tea(images: List[BuildImage], texts, args: Model): + frame = images[0].convert("RGBA").resize((500, 500), keep_ratio=True) + bubble_tea = BuildImage.open(img_dir / "0.png") + position = args.position + left = position in ["left", "both"] + right = position in ["right", "both"] + if right: + frame.paste(bubble_tea, alpha=True) + if left: + frame.paste(bubble_tea.transpose(Transpose.FLIP_LEFT_RIGHT), alpha=True) + return frame.save_jpg() + + +add_meme( + "bubble_tea", + bubble_tea, + min_images=1, + max_images=1, + args_type=MemeArgsType( + parser, + Model, + [Model(position="right"), Model(position="left"), Model(position="both")], + ), + keywords=["奶茶"], +) diff --git a/meme_generator/memes/bubble_tea/images/0.png b/meme_generator/memes/bubble_tea/images/0.png new file mode 100644 index 0000000000000000000000000000000000000000..785d142178e5732a49511af198deba9a4f504eb0 Binary files /dev/null and b/meme_generator/memes/bubble_tea/images/0.png differ diff --git a/meme_generator/memes/call_110/__init__.py b/meme_generator/memes/call_110/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..241f6fcc37209a66dbd767b6630a211065f6780d --- /dev/null +++ b/meme_generator/memes/call_110/__init__.py @@ -0,0 +1,20 @@ +from typing import List + +from pil_utils import BuildImage + +from meme_generator import add_meme + + +def call_110(images: List[BuildImage], texts, args): + img1 = images[0].convert("RGBA").square().resize((250, 250)) + img0 = images[1].convert("RGBA").square().resize((250, 250)) + + frame = BuildImage.new("RGB", (900, 500), "white") + frame.draw_text((0, 0, 900, 200), "遇到困难请拨打", max_fontsize=100) + frame.paste(img1, (50, 200), alpha=True) + frame.paste(img1, (325, 200), alpha=True) + frame.paste(img0, (600, 200), alpha=True) + return frame.save_jpg() + + +add_meme("call_110", call_110, min_images=2, max_images=2, keywords=["遇到困难请拨打"]) diff --git a/meme_generator/memes/caoshen_bite/__init__.py b/meme_generator/memes/caoshen_bite/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..ebae6800a42ea8b8b708525129952b713b569979 --- /dev/null +++ b/meme_generator/memes/caoshen_bite/__init__.py @@ -0,0 +1,34 @@ +from pathlib import Path +from typing import List + +from PIL.Image import Image as IMG +from pil_utils import BuildImage + +from meme_generator import add_meme +from meme_generator.utils import save_gif + +img_dir = Path(__file__).parent / "images" + + +def caoshen_bite(images: List[BuildImage], texts, args): + img = images[0].convert("RGBA").resize((160, 140), keep_ratio=True) + # fmt: off + locs = [ + (123, 356, 158, 124), (123, 356, 158, 124), (123, 355, 158, 125), + (122, 352, 159, 128), (122, 350, 159, 130), (122, 348, 159, 132), + (122, 345, 159, 135), (121, 343, 160, 137), (121, 342, 160, 138), + (121, 341, 160, 139), (121, 341, 160, 139), (121, 342, 160, 138), + (121, 344, 160, 136), (121, 346, 160, 134), (122, 349, 159, 131), + (122, 351, 159, 129), (122, 353, 159, 127), (123, 355, 158, 125), + ] + # fmt: on + frames: List[IMG] = [] + for i in range(38): + frame = BuildImage.open(img_dir / f"{i}.png") + x, y, w, h = locs[i % len(locs)] + frame.paste(img.resize((w, h)), (x, y), below=True) + frames.append(frame.image) + return save_gif(frames, 0.1) + + +add_meme("caoshen_bite", caoshen_bite, min_images=1, max_images=1, keywords=["草神啃"]) diff --git a/meme_generator/memes/caoshen_bite/images/0.png b/meme_generator/memes/caoshen_bite/images/0.png new file mode 100644 index 0000000000000000000000000000000000000000..c50702913a2f5cb58683da5565f20395139c2a45 Binary files /dev/null and b/meme_generator/memes/caoshen_bite/images/0.png differ diff --git a/meme_generator/memes/caoshen_bite/images/1.png b/meme_generator/memes/caoshen_bite/images/1.png new file mode 100644 index 0000000000000000000000000000000000000000..866499ed0fa4aae16b20daac8d5be43460fa9d87 Binary files /dev/null and b/meme_generator/memes/caoshen_bite/images/1.png differ diff --git a/meme_generator/memes/caoshen_bite/images/10.png b/meme_generator/memes/caoshen_bite/images/10.png new file mode 100644 index 0000000000000000000000000000000000000000..33866a712c768c09273df62dc1454d5a53335829 Binary files /dev/null and b/meme_generator/memes/caoshen_bite/images/10.png differ diff --git a/meme_generator/memes/caoshen_bite/images/11.png b/meme_generator/memes/caoshen_bite/images/11.png new file mode 100644 index 0000000000000000000000000000000000000000..8561004f463d0acc301c75e5a3a1f50dc91cc2e5 Binary files /dev/null and b/meme_generator/memes/caoshen_bite/images/11.png differ diff --git a/meme_generator/memes/caoshen_bite/images/12.png b/meme_generator/memes/caoshen_bite/images/12.png new file mode 100644 index 0000000000000000000000000000000000000000..7afa03a18ffb66d1365ab94af51854677aac2b9b Binary files /dev/null and b/meme_generator/memes/caoshen_bite/images/12.png differ diff --git a/meme_generator/memes/caoshen_bite/images/13.png b/meme_generator/memes/caoshen_bite/images/13.png new file mode 100644 index 0000000000000000000000000000000000000000..8258ab3adc055ef5492a686c2c03fe26f77978c8 Binary files /dev/null and b/meme_generator/memes/caoshen_bite/images/13.png differ diff --git a/meme_generator/memes/caoshen_bite/images/14.png b/meme_generator/memes/caoshen_bite/images/14.png new file mode 100644 index 0000000000000000000000000000000000000000..5aaf2938d404f66a73282cd25bb866347f6b3f56 Binary files /dev/null and b/meme_generator/memes/caoshen_bite/images/14.png differ diff --git a/meme_generator/memes/caoshen_bite/images/15.png b/meme_generator/memes/caoshen_bite/images/15.png new file mode 100644 index 0000000000000000000000000000000000000000..80a1373d2009b76e1f0fae0fab5070fc16c0754b Binary files /dev/null and b/meme_generator/memes/caoshen_bite/images/15.png differ diff --git a/meme_generator/memes/caoshen_bite/images/16.png b/meme_generator/memes/caoshen_bite/images/16.png new file mode 100644 index 0000000000000000000000000000000000000000..ab9f489d040f82f29e4464e019c56186b4853013 Binary files /dev/null and b/meme_generator/memes/caoshen_bite/images/16.png differ diff --git a/meme_generator/memes/caoshen_bite/images/17.png b/meme_generator/memes/caoshen_bite/images/17.png new file mode 100644 index 0000000000000000000000000000000000000000..117b3c89f7616340228008cd85ef55ff416e6f4d Binary files /dev/null and b/meme_generator/memes/caoshen_bite/images/17.png differ diff --git a/meme_generator/memes/caoshen_bite/images/18.png b/meme_generator/memes/caoshen_bite/images/18.png new file mode 100644 index 0000000000000000000000000000000000000000..79d015c73d3ee620cbf29323969c651a95c43999 Binary files /dev/null and b/meme_generator/memes/caoshen_bite/images/18.png differ diff --git a/meme_generator/memes/caoshen_bite/images/19.png b/meme_generator/memes/caoshen_bite/images/19.png new file mode 100644 index 0000000000000000000000000000000000000000..d35ac4ab8413f824afe23d0796a6e60e062c43ec Binary files /dev/null and b/meme_generator/memes/caoshen_bite/images/19.png differ diff --git a/meme_generator/memes/caoshen_bite/images/2.png b/meme_generator/memes/caoshen_bite/images/2.png new file mode 100644 index 0000000000000000000000000000000000000000..a348a2ba778293d72cd3f315b9a43e55209e58ad Binary files /dev/null and b/meme_generator/memes/caoshen_bite/images/2.png differ diff --git a/meme_generator/memes/caoshen_bite/images/20.png b/meme_generator/memes/caoshen_bite/images/20.png new file mode 100644 index 0000000000000000000000000000000000000000..74673ce340b7eb9f49d2dd4d43b1e593daf2429e Binary files /dev/null and b/meme_generator/memes/caoshen_bite/images/20.png differ diff --git a/meme_generator/memes/caoshen_bite/images/21.png b/meme_generator/memes/caoshen_bite/images/21.png new file mode 100644 index 0000000000000000000000000000000000000000..d0ed47a8d73de3a6a3a6a4f83c44c6b8fd8ef070 Binary files /dev/null and b/meme_generator/memes/caoshen_bite/images/21.png differ diff --git a/meme_generator/memes/caoshen_bite/images/22.png b/meme_generator/memes/caoshen_bite/images/22.png new file mode 100644 index 0000000000000000000000000000000000000000..4bac1beb28311dd57bed661704db153a6d6d36b2 Binary files /dev/null and b/meme_generator/memes/caoshen_bite/images/22.png differ diff --git a/meme_generator/memes/caoshen_bite/images/23.png b/meme_generator/memes/caoshen_bite/images/23.png new file mode 100644 index 0000000000000000000000000000000000000000..0d32a2355af40c1ef815354fa1d2cc022f7737fd Binary files /dev/null and b/meme_generator/memes/caoshen_bite/images/23.png differ diff --git a/meme_generator/memes/caoshen_bite/images/24.png b/meme_generator/memes/caoshen_bite/images/24.png new file mode 100644 index 0000000000000000000000000000000000000000..0694042b5efda459fe49dc29d6ae1aa02b3296d8 Binary files /dev/null and b/meme_generator/memes/caoshen_bite/images/24.png differ diff --git a/meme_generator/memes/caoshen_bite/images/25.png b/meme_generator/memes/caoshen_bite/images/25.png new file mode 100644 index 0000000000000000000000000000000000000000..8c92a0c16ba8b1b575833346cbcbe3ce06a543ee Binary files /dev/null and b/meme_generator/memes/caoshen_bite/images/25.png differ diff --git a/meme_generator/memes/caoshen_bite/images/26.png b/meme_generator/memes/caoshen_bite/images/26.png new file mode 100644 index 0000000000000000000000000000000000000000..ada0bb0e028e4a1401380c7f94ce0202ed608f9c Binary files /dev/null and b/meme_generator/memes/caoshen_bite/images/26.png differ diff --git a/meme_generator/memes/caoshen_bite/images/27.png b/meme_generator/memes/caoshen_bite/images/27.png new file mode 100644 index 0000000000000000000000000000000000000000..ce7c990b19dbfb07018d1774c0beaf003df4aa55 Binary files /dev/null and b/meme_generator/memes/caoshen_bite/images/27.png differ diff --git a/meme_generator/memes/caoshen_bite/images/28.png b/meme_generator/memes/caoshen_bite/images/28.png new file mode 100644 index 0000000000000000000000000000000000000000..831b427cbbeecbd580ae7bc811c5fa103003442b Binary files /dev/null and b/meme_generator/memes/caoshen_bite/images/28.png differ diff --git a/meme_generator/memes/caoshen_bite/images/29.png b/meme_generator/memes/caoshen_bite/images/29.png new file mode 100644 index 0000000000000000000000000000000000000000..cb18573c5ece514739b592f7072b4b85c506a926 Binary files /dev/null and b/meme_generator/memes/caoshen_bite/images/29.png differ diff --git a/meme_generator/memes/caoshen_bite/images/3.png b/meme_generator/memes/caoshen_bite/images/3.png new file mode 100644 index 0000000000000000000000000000000000000000..cd8d6499f1d9e768393d574c8bfff20c03f30081 Binary files /dev/null and b/meme_generator/memes/caoshen_bite/images/3.png differ diff --git a/meme_generator/memes/caoshen_bite/images/30.png b/meme_generator/memes/caoshen_bite/images/30.png new file mode 100644 index 0000000000000000000000000000000000000000..dbe07e669e2b7c5757390a87959386e5613d58da Binary files /dev/null and b/meme_generator/memes/caoshen_bite/images/30.png differ diff --git a/meme_generator/memes/caoshen_bite/images/31.png b/meme_generator/memes/caoshen_bite/images/31.png new file mode 100644 index 0000000000000000000000000000000000000000..24f7b19421862abad3e5e28aef1bc7c5e1d64dec Binary files /dev/null and b/meme_generator/memes/caoshen_bite/images/31.png differ diff --git a/meme_generator/memes/caoshen_bite/images/32.png b/meme_generator/memes/caoshen_bite/images/32.png new file mode 100644 index 0000000000000000000000000000000000000000..9f2973ff8cab4d521febeb793a94e520c9890c0e Binary files /dev/null and b/meme_generator/memes/caoshen_bite/images/32.png differ diff --git a/meme_generator/memes/caoshen_bite/images/33.png b/meme_generator/memes/caoshen_bite/images/33.png new file mode 100644 index 0000000000000000000000000000000000000000..c5687bfc64021b968dd56b76e4ec3e11cfdc61a6 Binary files /dev/null and b/meme_generator/memes/caoshen_bite/images/33.png differ diff --git a/meme_generator/memes/caoshen_bite/images/34.png b/meme_generator/memes/caoshen_bite/images/34.png new file mode 100644 index 0000000000000000000000000000000000000000..cea805ed3eb994727e69d6195b84223daeda95bd Binary files /dev/null and b/meme_generator/memes/caoshen_bite/images/34.png differ diff --git a/meme_generator/memes/caoshen_bite/images/35.png b/meme_generator/memes/caoshen_bite/images/35.png new file mode 100644 index 0000000000000000000000000000000000000000..e9883805071cc7cdd6629dec17bd78c8164a98c6 Binary files /dev/null and b/meme_generator/memes/caoshen_bite/images/35.png differ diff --git a/meme_generator/memes/caoshen_bite/images/36.png b/meme_generator/memes/caoshen_bite/images/36.png new file mode 100644 index 0000000000000000000000000000000000000000..de54c3a13d5eb5113d72be3f87bb6b0af0f68c74 Binary files /dev/null and b/meme_generator/memes/caoshen_bite/images/36.png differ diff --git a/meme_generator/memes/caoshen_bite/images/37.png b/meme_generator/memes/caoshen_bite/images/37.png new file mode 100644 index 0000000000000000000000000000000000000000..d8036c492e820e801167d145381ac1ae8412661e Binary files /dev/null and b/meme_generator/memes/caoshen_bite/images/37.png differ diff --git a/meme_generator/memes/caoshen_bite/images/4.png b/meme_generator/memes/caoshen_bite/images/4.png new file mode 100644 index 0000000000000000000000000000000000000000..8eac509661790cae84e558a093d36654a1938fc5 Binary files /dev/null and b/meme_generator/memes/caoshen_bite/images/4.png differ diff --git a/meme_generator/memes/caoshen_bite/images/5.png b/meme_generator/memes/caoshen_bite/images/5.png new file mode 100644 index 0000000000000000000000000000000000000000..f054c96a3183c708c23c8e192c0769361da52c55 Binary files /dev/null and b/meme_generator/memes/caoshen_bite/images/5.png differ diff --git a/meme_generator/memes/caoshen_bite/images/6.png b/meme_generator/memes/caoshen_bite/images/6.png new file mode 100644 index 0000000000000000000000000000000000000000..be046de29ab1f9603da9095b782f70f93da7c17d Binary files /dev/null and b/meme_generator/memes/caoshen_bite/images/6.png differ diff --git a/meme_generator/memes/caoshen_bite/images/7.png b/meme_generator/memes/caoshen_bite/images/7.png new file mode 100644 index 0000000000000000000000000000000000000000..cb461d58fd57fa726570ba0bd714f46284b39b6b Binary files /dev/null and b/meme_generator/memes/caoshen_bite/images/7.png differ diff --git a/meme_generator/memes/caoshen_bite/images/8.png b/meme_generator/memes/caoshen_bite/images/8.png new file mode 100644 index 0000000000000000000000000000000000000000..bc85136d2d5d45ff3ac6cc13e9861cd738fa193b Binary files /dev/null and b/meme_generator/memes/caoshen_bite/images/8.png differ diff --git a/meme_generator/memes/caoshen_bite/images/9.png b/meme_generator/memes/caoshen_bite/images/9.png new file mode 100644 index 0000000000000000000000000000000000000000..63517658c9257145498b90eb15de71a7a1eefe32 Binary files /dev/null and b/meme_generator/memes/caoshen_bite/images/9.png differ diff --git a/meme_generator/memes/capoo_draw/__init__.py b/meme_generator/memes/capoo_draw/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..da3c138d52e7f8ff8a311816bcf6fc629264738d --- /dev/null +++ b/meme_generator/memes/capoo_draw/__init__.py @@ -0,0 +1,43 @@ +from pathlib import Path +from typing import List + +from PIL.Image import Image as IMG +from pil_utils import BuildImage + +from meme_generator import add_meme +from meme_generator.utils import save_gif + +img_dir = Path(__file__).parent / "images" + + +def capoo_draw(images: List[BuildImage], texts, args): + img = images[0].convert("RGBA").resize((175, 120), keep_ratio=True) + params = ( + (((27, 0), (207, 12), (179, 142), (0, 117)), (30, 16)), + (((28, 0), (207, 13), (180, 137), (0, 117)), (34, 17)), + ) + raw_frames = [BuildImage.open(img_dir / f"{i}.png") for i in range(6)] + for i in range(2): + points, pos = params[i] + raw_frames[4 + i].paste(img.perspective(points), pos, below=True) + + frames: List[IMG] = [] + frames.append(raw_frames[0].image) + for i in range(4): + frames.append(raw_frames[1].image) + frames.append(raw_frames[2].image) + frames.append(raw_frames[3].image) + for i in range(6): + frames.append(raw_frames[4].image) + frames.append(raw_frames[5].image) + + return save_gif(frames, 0.1) + + +add_meme( + "capoo_draw", + capoo_draw, + min_images=1, + max_images=1, + keywords=["咖波画"], +) diff --git a/meme_generator/memes/capoo_draw/images/0.png b/meme_generator/memes/capoo_draw/images/0.png new file mode 100644 index 0000000000000000000000000000000000000000..2346a1431a11959d5dcdcf0183249c9900e0152d Binary files /dev/null and b/meme_generator/memes/capoo_draw/images/0.png differ diff --git a/meme_generator/memes/capoo_draw/images/1.png b/meme_generator/memes/capoo_draw/images/1.png new file mode 100644 index 0000000000000000000000000000000000000000..162afd3c7222cd6659b1496a006ed34da793a3de Binary files /dev/null and b/meme_generator/memes/capoo_draw/images/1.png differ diff --git a/meme_generator/memes/capoo_draw/images/2.png b/meme_generator/memes/capoo_draw/images/2.png new file mode 100644 index 0000000000000000000000000000000000000000..5c42195ae1af8eac14cb5eb81d34efd7a9c28e10 Binary files /dev/null and b/meme_generator/memes/capoo_draw/images/2.png differ diff --git a/meme_generator/memes/capoo_draw/images/3.png b/meme_generator/memes/capoo_draw/images/3.png new file mode 100644 index 0000000000000000000000000000000000000000..b693529b1065e5de06832d2df6758453ae3d57af Binary files /dev/null and b/meme_generator/memes/capoo_draw/images/3.png differ diff --git a/meme_generator/memes/capoo_draw/images/4.png b/meme_generator/memes/capoo_draw/images/4.png new file mode 100644 index 0000000000000000000000000000000000000000..e810aedd4071325031f879431f83b8209f885aab Binary files /dev/null and b/meme_generator/memes/capoo_draw/images/4.png differ diff --git a/meme_generator/memes/capoo_draw/images/5.png b/meme_generator/memes/capoo_draw/images/5.png new file mode 100644 index 0000000000000000000000000000000000000000..4ccd5c3e8d1ecc2fec5b9437d1abb5d810dc17d6 Binary files /dev/null and b/meme_generator/memes/capoo_draw/images/5.png differ diff --git a/meme_generator/memes/capoo_rip/__init__.py b/meme_generator/memes/capoo_rip/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..5711280bc1c3fd1efed76725ff6698e7813067c1 --- /dev/null +++ b/meme_generator/memes/capoo_rip/__init__.py @@ -0,0 +1,59 @@ +from pathlib import Path +from typing import List + +from pil_utils import BuildImage + +from meme_generator import add_meme +from meme_generator.utils import save_gif + +img_dir = Path(__file__).parent / "images" + + +def capoo_rip(images: List[BuildImage], texts, args): + img = images[0].convert("RGBA").resize((150, 100), keep_ratio=True) + img_left = img.crop((0, 0, 75, 100)) + img_right = img.crop((75, 0, 150, 100)) + params1 = [ + [(61, 196), ((140, 68), (0, 59), (33, 0), (165, 8))], + [(63, 196), ((136, 68), (0, 59), (29, 0), (158, 13))], + [(62, 195), ((137, 72), (0, 58), (27, 0), (167, 11))], + [(95, 152), ((0, 8), (155, 0), (163, 107), (13, 112))], + [(108, 129), ((0, 6), (128, 0), (136, 113), (10, 117))], + [(84, 160), ((0, 6), (184, 0), (190, 90), (10, 97))], + ] + params2 = [ + ( + [(78, 158), ((0, 3), (86, 0), (97, 106), (16, 106))], + [(195, 156), ((0, 4), (82, 0), (85, 106), (15, 110))], + ), + ( + [(89, 156), ((0, 0), (80, 0), (94, 100), (14, 100))], + [(192, 151), ((0, 7), (79, 3), (82, 107), (11, 112))], + ), + ] + raw_frames = [BuildImage.open(img_dir / f"{i}.png") for i in range(8)] + for i in range(6): + pos, points = params1[i] + raw_frames[i].paste(img.perspective(points), pos, below=True) + for i in range(2): + (pos1, points1), (pos2, points2) = params2[i] + raw_frames[i + 6].paste(img_left.perspective(points1), pos1, below=True) + raw_frames[i + 6].paste(img_right.perspective(points2), pos2, below=True) + + new_frames: List[BuildImage] = [] + for i in range(3): + new_frames += raw_frames[0:3] + new_frames += raw_frames[3:] + new_frames.append(raw_frames[-1]) + + frames = [frame.image for frame in new_frames] + return save_gif(frames, 0.1) + + +add_meme( + "capoo_rip", + capoo_rip, + min_images=1, + max_images=1, + keywords=["咖波撕"], +) diff --git a/meme_generator/memes/capoo_rip/images/0.png b/meme_generator/memes/capoo_rip/images/0.png new file mode 100644 index 0000000000000000000000000000000000000000..8b253c236bc9b911cde37300486d88ba03ffd307 Binary files /dev/null and b/meme_generator/memes/capoo_rip/images/0.png differ diff --git a/meme_generator/memes/capoo_rip/images/1.png b/meme_generator/memes/capoo_rip/images/1.png new file mode 100644 index 0000000000000000000000000000000000000000..f1f067a585535a2a63d2184b44526f3d32da1f6b Binary files /dev/null and b/meme_generator/memes/capoo_rip/images/1.png differ diff --git a/meme_generator/memes/capoo_rip/images/2.png b/meme_generator/memes/capoo_rip/images/2.png new file mode 100644 index 0000000000000000000000000000000000000000..04d403c7987f1cbe67f4b2f74089f44c5a51d186 Binary files /dev/null and b/meme_generator/memes/capoo_rip/images/2.png differ diff --git a/meme_generator/memes/capoo_rip/images/3.png b/meme_generator/memes/capoo_rip/images/3.png new file mode 100644 index 0000000000000000000000000000000000000000..4c7c21b583c82ed9e19091e687e841412d6a5800 Binary files /dev/null and b/meme_generator/memes/capoo_rip/images/3.png differ diff --git a/meme_generator/memes/capoo_rip/images/4.png b/meme_generator/memes/capoo_rip/images/4.png new file mode 100644 index 0000000000000000000000000000000000000000..e2a5f3949e6db3c54d701f5b1415f9a097bd823c Binary files /dev/null and b/meme_generator/memes/capoo_rip/images/4.png differ diff --git a/meme_generator/memes/capoo_rip/images/5.png b/meme_generator/memes/capoo_rip/images/5.png new file mode 100644 index 0000000000000000000000000000000000000000..0c082faeda699d0611ca5c9dd8e30086e3f17895 Binary files /dev/null and b/meme_generator/memes/capoo_rip/images/5.png differ diff --git a/meme_generator/memes/capoo_rip/images/6.png b/meme_generator/memes/capoo_rip/images/6.png new file mode 100644 index 0000000000000000000000000000000000000000..d96eda453228b1aa3d98f235a7366a289e6e2848 Binary files /dev/null and b/meme_generator/memes/capoo_rip/images/6.png differ diff --git a/meme_generator/memes/capoo_rip/images/7.png b/meme_generator/memes/capoo_rip/images/7.png new file mode 100644 index 0000000000000000000000000000000000000000..36e7ade349146d0bbb870ef4ea2ce2a76973db23 Binary files /dev/null and b/meme_generator/memes/capoo_rip/images/7.png differ diff --git a/meme_generator/memes/capoo_rub/__init__.py b/meme_generator/memes/capoo_rub/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..4f25149fd03fce16ff8dba66670a422ebb3381fc --- /dev/null +++ b/meme_generator/memes/capoo_rub/__init__.py @@ -0,0 +1,30 @@ +from pathlib import Path +from typing import List + +from PIL.Image import Image as IMG +from pil_utils import BuildImage + +from meme_generator import add_meme +from meme_generator.utils import save_gif + +img_dir = Path(__file__).parent / "images" + + +def capoo_rub(images: List[BuildImage], texts, args): + img = images[0].convert("RGBA").square().resize((180, 180)) + frames: List[IMG] = [] + locs = [ + (178, 184, 78, 260), + (178, 174, 84, 269), + (178, 174, 84, 269), + (178, 178, 84, 264), + ] + for i in range(4): + frame = BuildImage.open(img_dir / f"{i}.png") + w, h, x, y = locs[i] + frame.paste(img.resize((w, h)), (x, y), below=True) + frames.append(frame.image) + return save_gif(frames, 0.1) + + +add_meme("capoo_rub", capoo_rub, min_images=1, max_images=1, keywords=["咖波蹭", "咖波贴"]) diff --git a/meme_generator/memes/capoo_rub/images/0.png b/meme_generator/memes/capoo_rub/images/0.png new file mode 100644 index 0000000000000000000000000000000000000000..6bd6c6d7db1d9b7fc0f2259ea6cf19f18bdf1f29 Binary files /dev/null and b/meme_generator/memes/capoo_rub/images/0.png differ diff --git a/meme_generator/memes/capoo_rub/images/1.png b/meme_generator/memes/capoo_rub/images/1.png new file mode 100644 index 0000000000000000000000000000000000000000..4251b8eb58f537549a410566d0cb8e80eac1fd8b Binary files /dev/null and b/meme_generator/memes/capoo_rub/images/1.png differ diff --git a/meme_generator/memes/capoo_rub/images/2.png b/meme_generator/memes/capoo_rub/images/2.png new file mode 100644 index 0000000000000000000000000000000000000000..a2f574a01eb3b4743d33e989a5b84e2109c886e9 Binary files /dev/null and b/meme_generator/memes/capoo_rub/images/2.png differ diff --git a/meme_generator/memes/capoo_rub/images/3.png b/meme_generator/memes/capoo_rub/images/3.png new file mode 100644 index 0000000000000000000000000000000000000000..2cc8d08fcef2bc84e292b35503714a4892efedaf Binary files /dev/null and b/meme_generator/memes/capoo_rub/images/3.png differ diff --git a/meme_generator/memes/capoo_say/__init__.py b/meme_generator/memes/capoo_say/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..8cfe2c9a7a75e57197336fa1a2313c463e70e21f --- /dev/null +++ b/meme_generator/memes/capoo_say/__init__.py @@ -0,0 +1,67 @@ +from pathlib import Path +from typing import List + +from PIL.Image import Image as IMG +from pil_utils import BuildImage + +from meme_generator import add_meme +from meme_generator.exception import TextOverLength +from meme_generator.utils import save_gif + +img_dir = Path(__file__).parent / "images" + + +def capoo_say_one_loop(text: str) -> List[IMG]: + text_frame = BuildImage.new("RGBA", (80, 80)) + try: + text_frame.draw_text( + (0, 0, 80, 80), + text, + max_fontsize=80, + min_fontsize=20, + allow_wrap=True, + fontname="FZKaTong-M19S", + lines_align="center", + ) + except ValueError: + raise TextOverLength(text) + + params = [ + None, + None, + None, + (45, 45, 74, 112, 25), + (73, 73, 41, 42, 17), + (80, 80, 43, 36, 0), + (80, 80, 43, 30, 0), + (78, 78, 44, 30, 0), + (78, 78, 44, 29, 0), + None, + ] + + frames: List[IMG] = [] + for i in range(10): + frame = BuildImage.open(img_dir / f"{i}.png") + param = params[i] + if param: + x, y, w, h, angle = param + frame.paste( + text_frame.resize((x, y)).rotate(angle, expand=True), (w, h), alpha=True + ) + frames.append(frame.image) + return frames + + +def capoo_say(images, texts: List[str], args): + frames = sum([capoo_say_one_loop(text) for text in texts], []) + return save_gif(frames, 0.1) + + +add_meme( + "capoo_say", + capoo_say, + min_texts=1, + max_texts=10, + default_texts=["寄"], + keywords=["咖波说"], +) diff --git a/meme_generator/memes/capoo_say/images/0.png b/meme_generator/memes/capoo_say/images/0.png new file mode 100644 index 0000000000000000000000000000000000000000..ff7123a76cf8c54f46caf4b80b3d8a90b0b7dbc1 Binary files /dev/null and b/meme_generator/memes/capoo_say/images/0.png differ diff --git a/meme_generator/memes/capoo_say/images/1.png b/meme_generator/memes/capoo_say/images/1.png new file mode 100644 index 0000000000000000000000000000000000000000..dee1057b9b06cd55345c9047447b9c9c253cefa9 Binary files /dev/null and b/meme_generator/memes/capoo_say/images/1.png differ diff --git a/meme_generator/memes/capoo_say/images/2.png b/meme_generator/memes/capoo_say/images/2.png new file mode 100644 index 0000000000000000000000000000000000000000..4129451ed7c7fb10bb31831aff8999f47a483bf4 Binary files /dev/null and b/meme_generator/memes/capoo_say/images/2.png differ diff --git a/meme_generator/memes/capoo_say/images/3.png b/meme_generator/memes/capoo_say/images/3.png new file mode 100644 index 0000000000000000000000000000000000000000..d734530c242149a8b0057a325058dadfaffa5a74 Binary files /dev/null and b/meme_generator/memes/capoo_say/images/3.png differ diff --git a/meme_generator/memes/capoo_say/images/4.png b/meme_generator/memes/capoo_say/images/4.png new file mode 100644 index 0000000000000000000000000000000000000000..90bf5de647b2a7dd1b00b05b8acc3f32266f7b91 Binary files /dev/null and b/meme_generator/memes/capoo_say/images/4.png differ diff --git a/meme_generator/memes/capoo_say/images/5.png b/meme_generator/memes/capoo_say/images/5.png new file mode 100644 index 0000000000000000000000000000000000000000..f3f18e42adc2c16daf877a37fe5025f29391b733 Binary files /dev/null and b/meme_generator/memes/capoo_say/images/5.png differ diff --git a/meme_generator/memes/capoo_say/images/6.png b/meme_generator/memes/capoo_say/images/6.png new file mode 100644 index 0000000000000000000000000000000000000000..486039822d7690f479ef4ba9a1268c9f3a292ae5 Binary files /dev/null and b/meme_generator/memes/capoo_say/images/6.png differ diff --git a/meme_generator/memes/capoo_say/images/7.png b/meme_generator/memes/capoo_say/images/7.png new file mode 100644 index 0000000000000000000000000000000000000000..fe6a413bd6bfce72b6f9b86acaabf4658a3d44f5 Binary files /dev/null and b/meme_generator/memes/capoo_say/images/7.png differ diff --git a/meme_generator/memes/capoo_say/images/8.png b/meme_generator/memes/capoo_say/images/8.png new file mode 100644 index 0000000000000000000000000000000000000000..dd8e20ae071bb9d3d3b65bb0239c7080b33c2190 Binary files /dev/null and b/meme_generator/memes/capoo_say/images/8.png differ diff --git a/meme_generator/memes/capoo_say/images/9.png b/meme_generator/memes/capoo_say/images/9.png new file mode 100644 index 0000000000000000000000000000000000000000..75418a88d0446018555ed8f3d13e6860b3e0444e Binary files /dev/null and b/meme_generator/memes/capoo_say/images/9.png differ diff --git a/meme_generator/memes/capoo_strike/__init__.py b/meme_generator/memes/capoo_strike/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..f19055d85fcab12b981bd39a043aff88b768df6a --- /dev/null +++ b/meme_generator/memes/capoo_strike/__init__.py @@ -0,0 +1,44 @@ +from pathlib import Path +from typing import List + +from pil_utils import BuildImage + +from meme_generator import add_meme +from meme_generator.utils import FrameAlignPolicy, Maker, make_gif_or_combined_gif + +img_dir = Path(__file__).parent / "images" + + +def capoo_strike(images: List[BuildImage], texts, args): + params = ( + (((0, 4), (153, 0), (138, 105), (0, 157)), (28, 47)), + (((1, 13), (151, 0), (130, 104), (0, 156)), (28, 48)), + (((9, 10), (156, 0), (152, 108), (0, 155)), (18, 51)), + (((0, 21), (150, 0), (146, 115), (7, 145)), (17, 53)), + (((0, 19), (156, 0), (199, 109), (31, 145)), (2, 62)), + (((0, 28), (156, 0), (171, 115), (12, 154)), (16, 58)), + (((0, 25), (157, 0), (169, 113), (13, 147)), (18, 63)), + ) + + def maker(i: int) -> Maker: + def make(img: BuildImage) -> BuildImage: + img = img.convert("RGBA").resize((200, 160), keep_ratio=True) + points, pos = params[i] + frame = BuildImage.open(img_dir / f"{i}.png") + frame.paste(img.perspective(points), pos, below=True) + return frame + + return make + + return make_gif_or_combined_gif( + images[0], maker, 7, 0.05, FrameAlignPolicy.extend_loop + ) + + +add_meme( + "capoo_strike", + capoo_strike, + min_images=1, + max_images=1, + keywords=["咖波撞", "咖波头槌"], +) diff --git a/meme_generator/memes/capoo_strike/images/0.png b/meme_generator/memes/capoo_strike/images/0.png new file mode 100644 index 0000000000000000000000000000000000000000..92e08bbfb364b55d49c95a39e67c899e1a42eadf Binary files /dev/null and b/meme_generator/memes/capoo_strike/images/0.png differ diff --git a/meme_generator/memes/capoo_strike/images/1.png b/meme_generator/memes/capoo_strike/images/1.png new file mode 100644 index 0000000000000000000000000000000000000000..1a28b7781c631fe75d0a0fe52f85048ef6f28115 Binary files /dev/null and b/meme_generator/memes/capoo_strike/images/1.png differ diff --git a/meme_generator/memes/capoo_strike/images/2.png b/meme_generator/memes/capoo_strike/images/2.png new file mode 100644 index 0000000000000000000000000000000000000000..bb523ff78f4c258bf2ebd998c6adf386a9a3ebe5 Binary files /dev/null and b/meme_generator/memes/capoo_strike/images/2.png differ diff --git a/meme_generator/memes/capoo_strike/images/3.png b/meme_generator/memes/capoo_strike/images/3.png new file mode 100644 index 0000000000000000000000000000000000000000..f48036be89eb7f0b556c89e5d8be001d318c3456 Binary files /dev/null and b/meme_generator/memes/capoo_strike/images/3.png differ diff --git a/meme_generator/memes/capoo_strike/images/4.png b/meme_generator/memes/capoo_strike/images/4.png new file mode 100644 index 0000000000000000000000000000000000000000..8f654200aa431ba1291c19042817db66d26fbdc7 Binary files /dev/null and b/meme_generator/memes/capoo_strike/images/4.png differ diff --git a/meme_generator/memes/capoo_strike/images/5.png b/meme_generator/memes/capoo_strike/images/5.png new file mode 100644 index 0000000000000000000000000000000000000000..b4e9de41f99c2b98eab5d101a88b6d749e6144fc Binary files /dev/null and b/meme_generator/memes/capoo_strike/images/5.png differ diff --git a/meme_generator/memes/capoo_strike/images/6.png b/meme_generator/memes/capoo_strike/images/6.png new file mode 100644 index 0000000000000000000000000000000000000000..a0ade51776e73d6ebdc364e95a7353d99fc54b78 Binary files /dev/null and b/meme_generator/memes/capoo_strike/images/6.png differ diff --git a/meme_generator/memes/captain/__init__.py b/meme_generator/memes/captain/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..0256186fa6a9a0df8a394086e5b11cee6e87a0f3 --- /dev/null +++ b/meme_generator/memes/captain/__init__.py @@ -0,0 +1,29 @@ +from pathlib import Path +from typing import List + +from pil_utils import BuildImage + +from meme_generator import add_meme + +img_dir = Path(__file__).parent / "images" + + +def captain(images: List[BuildImage], texts, args): + if len(images) == 2: + images.append(images[-1]) + + bg0 = BuildImage.open(img_dir / "0.png") + bg1 = BuildImage.open(img_dir / "1.png") + bg2 = BuildImage.open(img_dir / "2.png") + + frame = BuildImage.new("RGBA", (640, 440 * len(images)), "white") + for i in range(len(images)): + bg = bg0 if i < len(images) - 2 else bg1 if i == len(images) - 2 else bg2 + images[i] = images[i].convert("RGBA").square().resize((250, 250)) + bg = bg.copy().paste(images[i], (350, 85)) + frame.paste(bg, (0, 440 * i)) + + return frame.save_jpg() + + +add_meme("captain", captain, min_images=2, max_images=5, keywords=["舰长"]) diff --git a/meme_generator/memes/captain/images/0.png b/meme_generator/memes/captain/images/0.png new file mode 100644 index 0000000000000000000000000000000000000000..3bb6aacbd741bc285c177a2a7a17f9fe6a083f8a Binary files /dev/null and b/meme_generator/memes/captain/images/0.png differ diff --git a/meme_generator/memes/captain/images/1.png b/meme_generator/memes/captain/images/1.png new file mode 100644 index 0000000000000000000000000000000000000000..85d97a6f548a96047ff216a2c4aefdfcd11e4143 Binary files /dev/null and b/meme_generator/memes/captain/images/1.png differ diff --git a/meme_generator/memes/captain/images/2.png b/meme_generator/memes/captain/images/2.png new file mode 100644 index 0000000000000000000000000000000000000000..b5a18ba1e036c81cd8b939aaa057070b1808c3d8 Binary files /dev/null and b/meme_generator/memes/captain/images/2.png differ diff --git a/meme_generator/memes/charpic/__init__.py b/meme_generator/memes/charpic/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..8ba8761022e9d421ea5b183d922672efc1a88491 --- /dev/null +++ b/meme_generator/memes/charpic/__init__.py @@ -0,0 +1,38 @@ +from typing import List + +from PIL import Image, ImageDraw +from pil_utils import BuildImage +from pil_utils.fonts import Font + +from meme_generator import add_meme +from meme_generator.utils import make_jpg_or_gif + + +def charpic(images: List[BuildImage], texts, args): + img = images[0] + str_map = "@@$$&B88QMMGW##EE93SPPDOOU**==()+^,\"--''. " + num = len(str_map) + font = Font.find("Consolas").load_font(15) + + def make(img: BuildImage) -> BuildImage: + img = img.convert("L").resize_width(150) + img = img.resize((img.width, img.height // 2)) + lines = [] + for y in range(img.height): + line = "" + for x in range(img.width): + gray = img.image.getpixel((x, y)) + line += str_map[int(num * gray / 256)] + lines.append(line) + text = "\n".join(lines) + text_img = Image.new("RGB", (2000, 2000), "white") + draw = ImageDraw.Draw(text_img) + _, _, w, h = draw.multiline_textbbox((0, 0), text, font=font) + draw.multiline_text((0, 0), text, font=font, fill="black") + text_img = text_img.crop((0, 0, w, h)) + return BuildImage(text_img) + + return make_jpg_or_gif(img, make) + + +add_meme("charpic", charpic, min_images=1, max_images=1, keywords=["字符画"]) diff --git a/meme_generator/memes/chase_train/__init__.py b/meme_generator/memes/chase_train/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..3a3d2b69958eae4be6afbe627c713b2459db87b1 --- /dev/null +++ b/meme_generator/memes/chase_train/__init__.py @@ -0,0 +1,60 @@ +from pathlib import Path +from typing import List + +from PIL.Image import Image as IMG +from pil_utils import BuildImage + +from meme_generator import add_meme +from meme_generator.utils import save_gif + +img_dir = Path(__file__).parent / "images" + + +def chase_train(images: List[BuildImage], texts, args): + img = images[0].convert("RGBA").square().resize((42, 42)) + frames: List[IMG] = [] + # fmt: off + locs = [ + (35, 34, 128, 44), (35, 33, 132, 40), (33, 34, 133, 36), (33, 38, 135, 41), + (34, 34, 136, 38), (35, 35, 136, 33), (33, 34, 138, 38), (36, 35, 138, 34), + (38, 34, 139, 32), (40, 35, 139, 37), (36, 35, 139, 33), (39, 36, 138, 28), + (40, 35, 138, 33), (37, 34, 138, 31), (43, 36, 135, 27), (36, 37, 136, 32), + (38, 40, 135, 26), (37, 35, 133, 26), (33, 36, 132, 30), (33, 39, 132, 25), + (32, 36, 131, 23), (33, 36, 130, 31), (35, 39, 128, 25), (33, 35, 127, 23), + (34, 36, 126, 29), (34, 40, 124, 25), (39, 36, 119, 23), (35, 36, 119, 32), + (35, 37, 116, 27), (36, 38, 113, 23), (34, 35, 113, 32), (39, 36, 113, 23), + (36, 35, 114, 17), (36, 38, 111, 13), (34, 37, 114, 15), (34, 39, 111, 10), + (33, 39, 109, 11), (36, 35, 104, 17), (34, 36, 102, 14), (34, 35, 99, 14), + (35, 38, 96, 16), (35, 35, 93, 14), (36, 35, 89, 15), (36, 36, 86, 18), + (36, 39, 83, 14), (34, 36, 81, 16), (40, 41, 74, 17), (38, 36, 74, 15), + (39, 35, 70, 16), (33, 35, 69, 20), (36, 35, 66, 17), (36, 35, 62, 17), + (37, 36, 57, 21), (35, 39, 57, 15), (35, 36, 53, 17), (35, 38, 51, 20), + (37, 36, 47, 19), (37, 35, 47, 18), (40, 36, 43, 19), (38, 35, 42, 22), + (40, 34, 38, 20), (38, 34, 37, 21), (39, 32, 35, 24), (39, 33, 33, 22), + (39, 36, 32, 22), (38, 35, 32, 25), (35, 37, 31, 22), (37, 37, 31, 23), + (36, 31, 31, 28), (37, 34, 32, 25), (36, 37, 32, 23), (36, 33, 33, 30), + (35, 34, 33, 27), (38, 33, 33, 28), (37, 34, 33, 29), (36, 35, 35, 28), + (36, 37, 36, 27), (43, 39, 33, 30), (35, 34, 38, 31), (37, 34, 39, 30), + (36, 34, 40, 30), (39, 35, 41, 30), (41, 36, 41, 29), (40, 37, 44, 32), + (40, 37, 45, 29), (39, 38, 48, 28), (38, 33, 50, 33), (35, 38, 53, 28), + (37, 34, 54, 31), (38, 34, 57, 32), (41, 35, 57, 29), (35, 34, 63, 29), + (41, 35, 62, 29), (38, 35, 66, 28), (35, 33, 70, 29), (40, 39, 70, 28), + (36, 36, 74, 28), (37, 35, 77, 26), (37, 35, 79, 28), (38, 35, 81, 27), + (36, 35, 85, 27), (37, 36, 88, 29), (36, 34, 91, 27), (38, 39, 94, 24), + (39, 34, 95, 27), (37, 34, 98, 26), (36, 35, 103, 24), (37, 36, 99, 28), + (34, 36, 97, 34), (34, 38, 102, 38), (37, 37, 99, 40), (39, 36, 101, 47), + (36, 36, 106, 43), (35, 35, 109, 40), (35, 39, 112, 43), (33, 36, 116, 41), + (36, 36, 116, 39), (34, 37, 121, 45), (35, 41, 123, 38), (34, 37, 126, 35), + ] + # fmt: on + for i in range(120): + frame = BuildImage.open(img_dir / f"{i}.png") + w, h, x, y = locs[i] + frame.paste(img.resize((w, h)), (x, y), below=True) + frames.append(frame.image) + return save_gif(frames, 0.05) + + +add_meme( + "chase_train", chase_train, min_images=1, max_images=1, keywords=["追列车", "追火车"] +) diff --git a/meme_generator/memes/chase_train/images/0.png b/meme_generator/memes/chase_train/images/0.png new file mode 100644 index 0000000000000000000000000000000000000000..478f8a831179b43e44f03a6da1b6332edcdd836a Binary files /dev/null and b/meme_generator/memes/chase_train/images/0.png differ diff --git a/meme_generator/memes/chase_train/images/1.png b/meme_generator/memes/chase_train/images/1.png new file mode 100644 index 0000000000000000000000000000000000000000..3ba439bc51837b1f78b2eafe868a39773ba563e6 Binary files /dev/null and b/meme_generator/memes/chase_train/images/1.png differ diff --git a/meme_generator/memes/chase_train/images/10.png b/meme_generator/memes/chase_train/images/10.png new file mode 100644 index 0000000000000000000000000000000000000000..01d6914288c85bf5d39a0973181a2f06107d5b76 Binary files /dev/null and b/meme_generator/memes/chase_train/images/10.png differ diff --git a/meme_generator/memes/chase_train/images/100.png b/meme_generator/memes/chase_train/images/100.png new file mode 100644 index 0000000000000000000000000000000000000000..e6a00eb34a4783f18008f848f675991c1aec5f88 Binary files /dev/null and b/meme_generator/memes/chase_train/images/100.png differ diff --git a/meme_generator/memes/chase_train/images/101.png b/meme_generator/memes/chase_train/images/101.png new file mode 100644 index 0000000000000000000000000000000000000000..277e3b266ad8b521b71a9dcb76b23a9c767e0502 Binary files /dev/null and b/meme_generator/memes/chase_train/images/101.png differ diff --git a/meme_generator/memes/chase_train/images/102.png b/meme_generator/memes/chase_train/images/102.png new file mode 100644 index 0000000000000000000000000000000000000000..7f2ea350f4bb085b6a36313ab20b1d7f698fba4e Binary files /dev/null and b/meme_generator/memes/chase_train/images/102.png differ diff --git a/meme_generator/memes/chase_train/images/103.png b/meme_generator/memes/chase_train/images/103.png new file mode 100644 index 0000000000000000000000000000000000000000..26d4894ce3564558d1b58126d25b764fbdb5dc95 Binary files /dev/null and b/meme_generator/memes/chase_train/images/103.png differ diff --git a/meme_generator/memes/chase_train/images/104.png b/meme_generator/memes/chase_train/images/104.png new file mode 100644 index 0000000000000000000000000000000000000000..16eba66c068746ca7231c7019c54f139822fd150 Binary files /dev/null and b/meme_generator/memes/chase_train/images/104.png differ diff --git a/meme_generator/memes/chase_train/images/105.png b/meme_generator/memes/chase_train/images/105.png new file mode 100644 index 0000000000000000000000000000000000000000..78b6f6840c32d074129b812da331ea0f529cd27c Binary files /dev/null and b/meme_generator/memes/chase_train/images/105.png differ diff --git a/meme_generator/memes/chase_train/images/106.png b/meme_generator/memes/chase_train/images/106.png new file mode 100644 index 0000000000000000000000000000000000000000..0519440a57f011008d5f9102d7d39de59f20ae2a Binary files /dev/null and b/meme_generator/memes/chase_train/images/106.png differ diff --git a/meme_generator/memes/chase_train/images/107.png b/meme_generator/memes/chase_train/images/107.png new file mode 100644 index 0000000000000000000000000000000000000000..c76ed4762d63ceac329e361966e2bc7830655a72 Binary files /dev/null and b/meme_generator/memes/chase_train/images/107.png differ diff --git a/meme_generator/memes/chase_train/images/108.png b/meme_generator/memes/chase_train/images/108.png new file mode 100644 index 0000000000000000000000000000000000000000..fde7cb4c3b42d8c0ab94507e3d6bd9df71463a56 Binary files /dev/null and b/meme_generator/memes/chase_train/images/108.png differ diff --git a/meme_generator/memes/chase_train/images/109.png b/meme_generator/memes/chase_train/images/109.png new file mode 100644 index 0000000000000000000000000000000000000000..119afa86d89e1d0b215583f3c61d865e066db5d4 Binary files /dev/null and b/meme_generator/memes/chase_train/images/109.png differ diff --git a/meme_generator/memes/chase_train/images/11.png b/meme_generator/memes/chase_train/images/11.png new file mode 100644 index 0000000000000000000000000000000000000000..a50d075ba6504e48ce3f1d498c6d06412920bb6d Binary files /dev/null and b/meme_generator/memes/chase_train/images/11.png differ diff --git a/meme_generator/memes/chase_train/images/110.png b/meme_generator/memes/chase_train/images/110.png new file mode 100644 index 0000000000000000000000000000000000000000..a1ff05e472e798b822799222d1056cd7e7bc612b Binary files /dev/null and b/meme_generator/memes/chase_train/images/110.png differ diff --git a/meme_generator/memes/chase_train/images/111.png b/meme_generator/memes/chase_train/images/111.png new file mode 100644 index 0000000000000000000000000000000000000000..31a827369a26241d3bc58d0d9d34930f3a77a95a Binary files /dev/null and b/meme_generator/memes/chase_train/images/111.png differ diff --git a/meme_generator/memes/chase_train/images/112.png b/meme_generator/memes/chase_train/images/112.png new file mode 100644 index 0000000000000000000000000000000000000000..324679a0799d10d0cc8060300624297209120148 Binary files /dev/null and b/meme_generator/memes/chase_train/images/112.png differ diff --git a/meme_generator/memes/chase_train/images/113.png b/meme_generator/memes/chase_train/images/113.png new file mode 100644 index 0000000000000000000000000000000000000000..492e8fa8d75b3b9d080897706fa56d5cf626b734 Binary files /dev/null and b/meme_generator/memes/chase_train/images/113.png differ diff --git a/meme_generator/memes/chase_train/images/114.png b/meme_generator/memes/chase_train/images/114.png new file mode 100644 index 0000000000000000000000000000000000000000..1011831e650bec65fb27632f2ed8451569816849 Binary files /dev/null and b/meme_generator/memes/chase_train/images/114.png differ diff --git a/meme_generator/memes/chase_train/images/115.png b/meme_generator/memes/chase_train/images/115.png new file mode 100644 index 0000000000000000000000000000000000000000..45d5ac92c233210be51d6ae6d4e03e74f5c0eedb Binary files /dev/null and b/meme_generator/memes/chase_train/images/115.png differ diff --git a/meme_generator/memes/chase_train/images/116.png b/meme_generator/memes/chase_train/images/116.png new file mode 100644 index 0000000000000000000000000000000000000000..254c7d4b94ea9fc6ca16b88b9c8233585f051cd8 Binary files /dev/null and b/meme_generator/memes/chase_train/images/116.png differ diff --git a/meme_generator/memes/chase_train/images/117.png b/meme_generator/memes/chase_train/images/117.png new file mode 100644 index 0000000000000000000000000000000000000000..ceb7dfa799f9bb4e85ebb68811c8bfdfd7f8bce9 Binary files /dev/null and b/meme_generator/memes/chase_train/images/117.png differ diff --git a/meme_generator/memes/chase_train/images/118.png b/meme_generator/memes/chase_train/images/118.png new file mode 100644 index 0000000000000000000000000000000000000000..0835e11517a5cf6fd0e249317753df228c409aa2 Binary files /dev/null and b/meme_generator/memes/chase_train/images/118.png differ diff --git a/meme_generator/memes/chase_train/images/119.png b/meme_generator/memes/chase_train/images/119.png new file mode 100644 index 0000000000000000000000000000000000000000..5fb205d24885531f30f639cd20a17b653a04094b Binary files /dev/null and b/meme_generator/memes/chase_train/images/119.png differ diff --git a/meme_generator/memes/chase_train/images/12.png b/meme_generator/memes/chase_train/images/12.png new file mode 100644 index 0000000000000000000000000000000000000000..1676ef6f2b837bdc0196f8065b871c345ada9068 Binary files /dev/null and b/meme_generator/memes/chase_train/images/12.png differ diff --git a/meme_generator/memes/chase_train/images/13.png b/meme_generator/memes/chase_train/images/13.png new file mode 100644 index 0000000000000000000000000000000000000000..8836680b7e1fe03956dd0ce6d56fa2a27de7a4ed Binary files /dev/null and b/meme_generator/memes/chase_train/images/13.png differ diff --git a/meme_generator/memes/chase_train/images/14.png b/meme_generator/memes/chase_train/images/14.png new file mode 100644 index 0000000000000000000000000000000000000000..9262a3872d50b111cad81815afff96106b133c3f Binary files /dev/null and b/meme_generator/memes/chase_train/images/14.png differ diff --git a/meme_generator/memes/chase_train/images/15.png b/meme_generator/memes/chase_train/images/15.png new file mode 100644 index 0000000000000000000000000000000000000000..ae8e8558e3dfc00c684b3368ebca49fc0411d44f Binary files /dev/null and b/meme_generator/memes/chase_train/images/15.png differ diff --git a/meme_generator/memes/chase_train/images/16.png b/meme_generator/memes/chase_train/images/16.png new file mode 100644 index 0000000000000000000000000000000000000000..00c22b51527e5bd4541bcc070236d4d12dc65df3 Binary files /dev/null and b/meme_generator/memes/chase_train/images/16.png differ diff --git a/meme_generator/memes/chase_train/images/17.png b/meme_generator/memes/chase_train/images/17.png new file mode 100644 index 0000000000000000000000000000000000000000..e59f1576ae24a7c264d1f7b2524ef621b0f7fe3d Binary files /dev/null and b/meme_generator/memes/chase_train/images/17.png differ diff --git a/meme_generator/memes/chase_train/images/18.png b/meme_generator/memes/chase_train/images/18.png new file mode 100644 index 0000000000000000000000000000000000000000..4ad1752a31ea62b2bf25716e40d10172d8c024b9 Binary files /dev/null and b/meme_generator/memes/chase_train/images/18.png differ diff --git a/meme_generator/memes/chase_train/images/19.png b/meme_generator/memes/chase_train/images/19.png new file mode 100644 index 0000000000000000000000000000000000000000..4b14e32752961b6e24ce10fe9a723fe6fb5c7987 Binary files /dev/null and b/meme_generator/memes/chase_train/images/19.png differ diff --git a/meme_generator/memes/chase_train/images/2.png b/meme_generator/memes/chase_train/images/2.png new file mode 100644 index 0000000000000000000000000000000000000000..9177c30cbc5fd689626e1a2749dbbedd74e551ab Binary files /dev/null and b/meme_generator/memes/chase_train/images/2.png differ diff --git a/meme_generator/memes/chase_train/images/20.png b/meme_generator/memes/chase_train/images/20.png new file mode 100644 index 0000000000000000000000000000000000000000..68c38317a7b4f916a5f3e186ee179b5fa81f6c6c Binary files /dev/null and b/meme_generator/memes/chase_train/images/20.png differ diff --git a/meme_generator/memes/chase_train/images/21.png b/meme_generator/memes/chase_train/images/21.png new file mode 100644 index 0000000000000000000000000000000000000000..90ae4b3785fbe9e72c77f35549bc79d3057c17f4 Binary files /dev/null and b/meme_generator/memes/chase_train/images/21.png differ diff --git a/meme_generator/memes/chase_train/images/22.png b/meme_generator/memes/chase_train/images/22.png new file mode 100644 index 0000000000000000000000000000000000000000..66398611f74c45c6841e48e4e0fb6006ab4d061e Binary files /dev/null and b/meme_generator/memes/chase_train/images/22.png differ diff --git a/meme_generator/memes/chase_train/images/23.png b/meme_generator/memes/chase_train/images/23.png new file mode 100644 index 0000000000000000000000000000000000000000..864402309e3e13dcfc9e8a7384d4a171798061a1 Binary files /dev/null and b/meme_generator/memes/chase_train/images/23.png differ diff --git a/meme_generator/memes/chase_train/images/24.png b/meme_generator/memes/chase_train/images/24.png new file mode 100644 index 0000000000000000000000000000000000000000..8982506ec61127ce7a1d0d7de7d12595af865d1c Binary files /dev/null and b/meme_generator/memes/chase_train/images/24.png differ diff --git a/meme_generator/memes/chase_train/images/25.png b/meme_generator/memes/chase_train/images/25.png new file mode 100644 index 0000000000000000000000000000000000000000..35825fa1dcd5ea9bda4170ee60c5c7977b19ff2e Binary files /dev/null and b/meme_generator/memes/chase_train/images/25.png differ diff --git a/meme_generator/memes/chase_train/images/26.png b/meme_generator/memes/chase_train/images/26.png new file mode 100644 index 0000000000000000000000000000000000000000..d49abfcd6e25ecdbe620a154ef6e75ae552ce4b0 Binary files /dev/null and b/meme_generator/memes/chase_train/images/26.png differ diff --git a/meme_generator/memes/chase_train/images/27.png b/meme_generator/memes/chase_train/images/27.png new file mode 100644 index 0000000000000000000000000000000000000000..13305210e4e10578c0b8e6216e8291cdc7460689 Binary files /dev/null and b/meme_generator/memes/chase_train/images/27.png differ diff --git a/meme_generator/memes/chase_train/images/28.png b/meme_generator/memes/chase_train/images/28.png new file mode 100644 index 0000000000000000000000000000000000000000..46fcfedb2502171062fe4eab533f9224ce35f410 Binary files /dev/null and b/meme_generator/memes/chase_train/images/28.png differ diff --git a/meme_generator/memes/chase_train/images/29.png b/meme_generator/memes/chase_train/images/29.png new file mode 100644 index 0000000000000000000000000000000000000000..bcffd5e9692a6b37d7ee01dce2587fd926427853 Binary files /dev/null and b/meme_generator/memes/chase_train/images/29.png differ diff --git a/meme_generator/memes/chase_train/images/3.png b/meme_generator/memes/chase_train/images/3.png new file mode 100644 index 0000000000000000000000000000000000000000..d28751e98953c0b440af57ee76e2b3ae7678f776 Binary files /dev/null and b/meme_generator/memes/chase_train/images/3.png differ diff --git a/meme_generator/memes/chase_train/images/30.png b/meme_generator/memes/chase_train/images/30.png new file mode 100644 index 0000000000000000000000000000000000000000..0e7d4769e737c28f9b15bba6d3fbca394a2848bf Binary files /dev/null and b/meme_generator/memes/chase_train/images/30.png differ diff --git a/meme_generator/memes/chase_train/images/31.png b/meme_generator/memes/chase_train/images/31.png new file mode 100644 index 0000000000000000000000000000000000000000..31f69f56adcae0019cc2d475b05cc87d38b07a43 Binary files /dev/null and b/meme_generator/memes/chase_train/images/31.png differ diff --git a/meme_generator/memes/chase_train/images/32.png b/meme_generator/memes/chase_train/images/32.png new file mode 100644 index 0000000000000000000000000000000000000000..0d7fc7573cdcc66e156dd4859ac8fd80a731640c Binary files /dev/null and b/meme_generator/memes/chase_train/images/32.png differ diff --git a/meme_generator/memes/chase_train/images/33.png b/meme_generator/memes/chase_train/images/33.png new file mode 100644 index 0000000000000000000000000000000000000000..1c378429011767ca9aa74322004770f3ab69de7f Binary files /dev/null and b/meme_generator/memes/chase_train/images/33.png differ diff --git a/meme_generator/memes/chase_train/images/34.png b/meme_generator/memes/chase_train/images/34.png new file mode 100644 index 0000000000000000000000000000000000000000..9045e4cfac2c518cf02df91a33a3e74bd69f8a2f Binary files /dev/null and b/meme_generator/memes/chase_train/images/34.png differ diff --git a/meme_generator/memes/chase_train/images/35.png b/meme_generator/memes/chase_train/images/35.png new file mode 100644 index 0000000000000000000000000000000000000000..1731f99dc7c435d7d3dea01026164429f6c44c16 Binary files /dev/null and b/meme_generator/memes/chase_train/images/35.png differ diff --git a/meme_generator/memes/chase_train/images/36.png b/meme_generator/memes/chase_train/images/36.png new file mode 100644 index 0000000000000000000000000000000000000000..06009605f5a1d6bdb7fb12591c14d20f29c8723a Binary files /dev/null and b/meme_generator/memes/chase_train/images/36.png differ diff --git a/meme_generator/memes/chase_train/images/37.png b/meme_generator/memes/chase_train/images/37.png new file mode 100644 index 0000000000000000000000000000000000000000..2074c9cf7c0f40ed118563c06e62073a7a7ba705 Binary files /dev/null and b/meme_generator/memes/chase_train/images/37.png differ diff --git a/meme_generator/memes/chase_train/images/38.png b/meme_generator/memes/chase_train/images/38.png new file mode 100644 index 0000000000000000000000000000000000000000..836c2e2289c8026ecfe4e6c83ebe340de7040f1d Binary files /dev/null and b/meme_generator/memes/chase_train/images/38.png differ diff --git a/meme_generator/memes/chase_train/images/39.png b/meme_generator/memes/chase_train/images/39.png new file mode 100644 index 0000000000000000000000000000000000000000..dff8b3a18daf29b326df9b97f2573de02e3bc2d7 Binary files /dev/null and b/meme_generator/memes/chase_train/images/39.png differ diff --git a/meme_generator/memes/chase_train/images/4.png b/meme_generator/memes/chase_train/images/4.png new file mode 100644 index 0000000000000000000000000000000000000000..2c53c21581f6b1fe5e5a963e2482f18a5b6593b3 Binary files /dev/null and b/meme_generator/memes/chase_train/images/4.png differ diff --git a/meme_generator/memes/chase_train/images/40.png b/meme_generator/memes/chase_train/images/40.png new file mode 100644 index 0000000000000000000000000000000000000000..cb0dacf92c782c850b306981ff76610390315576 Binary files /dev/null and b/meme_generator/memes/chase_train/images/40.png differ diff --git a/meme_generator/memes/chase_train/images/41.png b/meme_generator/memes/chase_train/images/41.png new file mode 100644 index 0000000000000000000000000000000000000000..8e26174e975ae3eab1d38abd9788a13c0f451e42 Binary files /dev/null and b/meme_generator/memes/chase_train/images/41.png differ diff --git a/meme_generator/memes/chase_train/images/42.png b/meme_generator/memes/chase_train/images/42.png new file mode 100644 index 0000000000000000000000000000000000000000..64e2c057d37f2bf201dba199a7b539c3c463204f Binary files /dev/null and b/meme_generator/memes/chase_train/images/42.png differ diff --git a/meme_generator/memes/chase_train/images/43.png b/meme_generator/memes/chase_train/images/43.png new file mode 100644 index 0000000000000000000000000000000000000000..ffdd2e3aaafb130fa749d8521f0768f3a7b6dc86 Binary files /dev/null and b/meme_generator/memes/chase_train/images/43.png differ diff --git a/meme_generator/memes/chase_train/images/44.png b/meme_generator/memes/chase_train/images/44.png new file mode 100644 index 0000000000000000000000000000000000000000..1dc1deb66f6dba64eeb02b5935d166c9d67f0dae Binary files /dev/null and b/meme_generator/memes/chase_train/images/44.png differ diff --git a/meme_generator/memes/chase_train/images/45.png b/meme_generator/memes/chase_train/images/45.png new file mode 100644 index 0000000000000000000000000000000000000000..ef4d13d0f9260df8ed4b006902b4135f12e09018 Binary files /dev/null and b/meme_generator/memes/chase_train/images/45.png differ diff --git a/meme_generator/memes/chase_train/images/46.png b/meme_generator/memes/chase_train/images/46.png new file mode 100644 index 0000000000000000000000000000000000000000..43a2d634a876d1b245984916962f6c0dca6156b5 Binary files /dev/null and b/meme_generator/memes/chase_train/images/46.png differ diff --git a/meme_generator/memes/chase_train/images/47.png b/meme_generator/memes/chase_train/images/47.png new file mode 100644 index 0000000000000000000000000000000000000000..b783e22eb9fd29d9b2f5e03e03b9bf134253ba24 Binary files /dev/null and b/meme_generator/memes/chase_train/images/47.png differ diff --git a/meme_generator/memes/chase_train/images/48.png b/meme_generator/memes/chase_train/images/48.png new file mode 100644 index 0000000000000000000000000000000000000000..d60af0b9ae5eac4401081bc3db7a164dd492eeb8 Binary files /dev/null and b/meme_generator/memes/chase_train/images/48.png differ diff --git a/meme_generator/memes/chase_train/images/49.png b/meme_generator/memes/chase_train/images/49.png new file mode 100644 index 0000000000000000000000000000000000000000..825d284a78403731576342cd2553811a740a0483 Binary files /dev/null and b/meme_generator/memes/chase_train/images/49.png differ diff --git a/meme_generator/memes/chase_train/images/5.png b/meme_generator/memes/chase_train/images/5.png new file mode 100644 index 0000000000000000000000000000000000000000..5a8cca4d43afe5ef384bc4d7ddb64d7143d41553 Binary files /dev/null and b/meme_generator/memes/chase_train/images/5.png differ diff --git a/meme_generator/memes/chase_train/images/50.png b/meme_generator/memes/chase_train/images/50.png new file mode 100644 index 0000000000000000000000000000000000000000..b837035f3c599d48a570d660c17e14fc339bb37d Binary files /dev/null and b/meme_generator/memes/chase_train/images/50.png differ diff --git a/meme_generator/memes/chase_train/images/51.png b/meme_generator/memes/chase_train/images/51.png new file mode 100644 index 0000000000000000000000000000000000000000..0496deb1f7bf26e0a1d18addbdbfa1bf2ca77f47 Binary files /dev/null and b/meme_generator/memes/chase_train/images/51.png differ diff --git a/meme_generator/memes/chase_train/images/52.png b/meme_generator/memes/chase_train/images/52.png new file mode 100644 index 0000000000000000000000000000000000000000..a9fd9c066bdecbbeca3aee88c6bbbd1d00402e2e Binary files /dev/null and b/meme_generator/memes/chase_train/images/52.png differ diff --git a/meme_generator/memes/chase_train/images/53.png b/meme_generator/memes/chase_train/images/53.png new file mode 100644 index 0000000000000000000000000000000000000000..8ade682f4bd22f047ae6f8464fa45fddc9c303f7 Binary files /dev/null and b/meme_generator/memes/chase_train/images/53.png differ diff --git a/meme_generator/memes/chase_train/images/54.png b/meme_generator/memes/chase_train/images/54.png new file mode 100644 index 0000000000000000000000000000000000000000..e0181fb0f3b2bc451a17d32b32ef2894d0e619af Binary files /dev/null and b/meme_generator/memes/chase_train/images/54.png differ diff --git a/meme_generator/memes/chase_train/images/55.png b/meme_generator/memes/chase_train/images/55.png new file mode 100644 index 0000000000000000000000000000000000000000..f346e67868998ab5870d66523a78c1289cb039f8 Binary files /dev/null and b/meme_generator/memes/chase_train/images/55.png differ diff --git a/meme_generator/memes/chase_train/images/56.png b/meme_generator/memes/chase_train/images/56.png new file mode 100644 index 0000000000000000000000000000000000000000..e06daabc9e33fb57eeed964cfb16bbecec3cbcfb Binary files /dev/null and b/meme_generator/memes/chase_train/images/56.png differ diff --git a/meme_generator/memes/chase_train/images/57.png b/meme_generator/memes/chase_train/images/57.png new file mode 100644 index 0000000000000000000000000000000000000000..c204c48149ae69d309c22980e000263ef45c96ce Binary files /dev/null and b/meme_generator/memes/chase_train/images/57.png differ diff --git a/meme_generator/memes/chase_train/images/58.png b/meme_generator/memes/chase_train/images/58.png new file mode 100644 index 0000000000000000000000000000000000000000..6dff318be382f67a7dc7fec6616c5b01883e3bd1 Binary files /dev/null and b/meme_generator/memes/chase_train/images/58.png differ diff --git a/meme_generator/memes/chase_train/images/59.png b/meme_generator/memes/chase_train/images/59.png new file mode 100644 index 0000000000000000000000000000000000000000..2538188a443d7e53dae42d66a8ea0d3416a10d00 Binary files /dev/null and b/meme_generator/memes/chase_train/images/59.png differ diff --git a/meme_generator/memes/chase_train/images/6.png b/meme_generator/memes/chase_train/images/6.png new file mode 100644 index 0000000000000000000000000000000000000000..4ad0945a2d15d7d86cd68ea7cd4d8e392a4fc361 Binary files /dev/null and b/meme_generator/memes/chase_train/images/6.png differ diff --git a/meme_generator/memes/chase_train/images/60.png b/meme_generator/memes/chase_train/images/60.png new file mode 100644 index 0000000000000000000000000000000000000000..850c43a961bf0da9cafd78a69170b6f4ab99b3f9 Binary files /dev/null and b/meme_generator/memes/chase_train/images/60.png differ diff --git a/meme_generator/memes/chase_train/images/61.png b/meme_generator/memes/chase_train/images/61.png new file mode 100644 index 0000000000000000000000000000000000000000..445d82ebc4ed4fd77501e706ed6b3be34f904191 Binary files /dev/null and b/meme_generator/memes/chase_train/images/61.png differ diff --git a/meme_generator/memes/chase_train/images/62.png b/meme_generator/memes/chase_train/images/62.png new file mode 100644 index 0000000000000000000000000000000000000000..251ac92323f9f9abb6e1935a889027ee02c21014 Binary files /dev/null and b/meme_generator/memes/chase_train/images/62.png differ diff --git a/meme_generator/memes/chase_train/images/63.png b/meme_generator/memes/chase_train/images/63.png new file mode 100644 index 0000000000000000000000000000000000000000..0fc31f5151f5ba0bf496bbc549f6ceecd9c61c09 Binary files /dev/null and b/meme_generator/memes/chase_train/images/63.png differ diff --git a/meme_generator/memes/chase_train/images/64.png b/meme_generator/memes/chase_train/images/64.png new file mode 100644 index 0000000000000000000000000000000000000000..86e2d6357c43e72b4002b9234243a86f9b617f25 Binary files /dev/null and b/meme_generator/memes/chase_train/images/64.png differ diff --git a/meme_generator/memes/chase_train/images/65.png b/meme_generator/memes/chase_train/images/65.png new file mode 100644 index 0000000000000000000000000000000000000000..d128e72863c5f863ae9a3b3eac8e4cea052ae86f Binary files /dev/null and b/meme_generator/memes/chase_train/images/65.png differ diff --git a/meme_generator/memes/chase_train/images/66.png b/meme_generator/memes/chase_train/images/66.png new file mode 100644 index 0000000000000000000000000000000000000000..785d9da29b4a6dd9b80631c2c0d63a7e6fc47b09 Binary files /dev/null and b/meme_generator/memes/chase_train/images/66.png differ diff --git a/meme_generator/memes/chase_train/images/67.png b/meme_generator/memes/chase_train/images/67.png new file mode 100644 index 0000000000000000000000000000000000000000..cd8ebbd2ff7408a12ff8f3895b4b1eaaf65d11a5 Binary files /dev/null and b/meme_generator/memes/chase_train/images/67.png differ diff --git a/meme_generator/memes/chase_train/images/68.png b/meme_generator/memes/chase_train/images/68.png new file mode 100644 index 0000000000000000000000000000000000000000..e9427331919e1e4abbc836d95b5db676d8cc9886 Binary files /dev/null and b/meme_generator/memes/chase_train/images/68.png differ diff --git a/meme_generator/memes/chase_train/images/69.png b/meme_generator/memes/chase_train/images/69.png new file mode 100644 index 0000000000000000000000000000000000000000..951ccadec8cb95d7bf6ed18c1ceeb8f1f1dac35d Binary files /dev/null and b/meme_generator/memes/chase_train/images/69.png differ diff --git a/meme_generator/memes/chase_train/images/7.png b/meme_generator/memes/chase_train/images/7.png new file mode 100644 index 0000000000000000000000000000000000000000..81d46fc02e9288f968c1d34c81c26b486200cb86 Binary files /dev/null and b/meme_generator/memes/chase_train/images/7.png differ diff --git a/meme_generator/memes/chase_train/images/70.png b/meme_generator/memes/chase_train/images/70.png new file mode 100644 index 0000000000000000000000000000000000000000..bb885f22480a7f22747cdb37f069f80ddc57cf14 Binary files /dev/null and b/meme_generator/memes/chase_train/images/70.png differ diff --git a/meme_generator/memes/chase_train/images/71.png b/meme_generator/memes/chase_train/images/71.png new file mode 100644 index 0000000000000000000000000000000000000000..1f2e2b39b644817ce5e6153c2c02b4b08b419995 Binary files /dev/null and b/meme_generator/memes/chase_train/images/71.png differ diff --git a/meme_generator/memes/chase_train/images/72.png b/meme_generator/memes/chase_train/images/72.png new file mode 100644 index 0000000000000000000000000000000000000000..c5f40dfd2f98c9843b5ab593423c9759a6edb28c Binary files /dev/null and b/meme_generator/memes/chase_train/images/72.png differ diff --git a/meme_generator/memes/chase_train/images/73.png b/meme_generator/memes/chase_train/images/73.png new file mode 100644 index 0000000000000000000000000000000000000000..0d2d71386a50b67d12c1a1e1220acd92f05268f8 Binary files /dev/null and b/meme_generator/memes/chase_train/images/73.png differ diff --git a/meme_generator/memes/chase_train/images/74.png b/meme_generator/memes/chase_train/images/74.png new file mode 100644 index 0000000000000000000000000000000000000000..7e2926a13d28d92b537df95bbc56c4555e876781 Binary files /dev/null and b/meme_generator/memes/chase_train/images/74.png differ diff --git a/meme_generator/memes/chase_train/images/75.png b/meme_generator/memes/chase_train/images/75.png new file mode 100644 index 0000000000000000000000000000000000000000..09a67b8827be0cb2a02889fe507931f8a9dff371 Binary files /dev/null and b/meme_generator/memes/chase_train/images/75.png differ diff --git a/meme_generator/memes/chase_train/images/76.png b/meme_generator/memes/chase_train/images/76.png new file mode 100644 index 0000000000000000000000000000000000000000..be01c4c83335cb2d1d8cd8ead2fbc3927c17dea5 Binary files /dev/null and b/meme_generator/memes/chase_train/images/76.png differ diff --git a/meme_generator/memes/chase_train/images/77.png b/meme_generator/memes/chase_train/images/77.png new file mode 100644 index 0000000000000000000000000000000000000000..01b32f1cee71df240ab6dfc24c96f2d6597a2fb6 Binary files /dev/null and b/meme_generator/memes/chase_train/images/77.png differ diff --git a/meme_generator/memes/chase_train/images/78.png b/meme_generator/memes/chase_train/images/78.png new file mode 100644 index 0000000000000000000000000000000000000000..7ee48cebeece98a7c28fc9ab9382cb6623232397 Binary files /dev/null and b/meme_generator/memes/chase_train/images/78.png differ diff --git a/meme_generator/memes/chase_train/images/79.png b/meme_generator/memes/chase_train/images/79.png new file mode 100644 index 0000000000000000000000000000000000000000..0d139112004d5b404adfe43e7fea931d06bfd39a Binary files /dev/null and b/meme_generator/memes/chase_train/images/79.png differ diff --git a/meme_generator/memes/chase_train/images/8.png b/meme_generator/memes/chase_train/images/8.png new file mode 100644 index 0000000000000000000000000000000000000000..6a31eca7ee56dfed46965bcd28e41d64d1f51bc7 Binary files /dev/null and b/meme_generator/memes/chase_train/images/8.png differ diff --git a/meme_generator/memes/chase_train/images/80.png b/meme_generator/memes/chase_train/images/80.png new file mode 100644 index 0000000000000000000000000000000000000000..23e4736f9417d2c23b4a2336bfbce707738823e0 Binary files /dev/null and b/meme_generator/memes/chase_train/images/80.png differ diff --git a/meme_generator/memes/china_flag/__init__.py b/meme_generator/memes/china_flag/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..f02e2724b4702ed900a8885df7ea7c587a6f0aa3 --- /dev/null +++ b/meme_generator/memes/china_flag/__init__.py @@ -0,0 +1,18 @@ +from pathlib import Path +from typing import List + +from pil_utils import BuildImage + +from meme_generator import add_meme + +img_dir = Path(__file__).parent / "images" + + +def china_flag(images: List[BuildImage], texts, args): + img = images[0].convert("RGBA") + frame = BuildImage.open(img_dir / "0.png") + frame.paste(img.resize(frame.size, keep_ratio=True), below=True) + return frame.save_jpg() + + +add_meme("china_flag", china_flag, min_images=1, max_images=1, keywords=["国旗"]) diff --git a/meme_generator/memes/china_flag/images/0.png b/meme_generator/memes/china_flag/images/0.png new file mode 100644 index 0000000000000000000000000000000000000000..e2755ae97e7fa1d1c665498814c32d7b33341fb5 Binary files /dev/null and b/meme_generator/memes/china_flag/images/0.png differ diff --git a/meme_generator/memes/confuse/__init__.py b/meme_generator/memes/confuse/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..8bed6616b7c63a19eda84c1e429ea5486031b322 --- /dev/null +++ b/meme_generator/memes/confuse/__init__.py @@ -0,0 +1,32 @@ +from pathlib import Path +from typing import List + +from pil_utils import BuildImage + +from meme_generator import add_meme +from meme_generator.utils import FrameAlignPolicy, Maker, make_gif_or_combined_gif + +img_dir = Path(__file__).parent / "images" + + +def confuse(images: List[BuildImage], texts, args): + img_w = min(images[0].width, 500) + + def maker(i: int) -> Maker: + def make(img: BuildImage) -> BuildImage: + img = img.convert("RGBA").resize_width(img_w) + frame = BuildImage.open(img_dir / f"{i}.png").resize( + img.size, keep_ratio=True + ) + bg = BuildImage.new("RGB", img.size, "white") + bg.paste(img, alpha=True).paste(frame, alpha=True) + return bg + + return make + + return make_gif_or_combined_gif( + images[0], maker, 100, 0.02, FrameAlignPolicy.extend_loop, input_based=True + ) + + +add_meme("confuse", confuse, min_images=1, max_images=1, keywords=["迷惑"]) diff --git a/meme_generator/memes/confuse/images/0.png b/meme_generator/memes/confuse/images/0.png new file mode 100644 index 0000000000000000000000000000000000000000..bbd8f6be4c9ddcf74fd8b5e4279706c8133a5e34 Binary files /dev/null and b/meme_generator/memes/confuse/images/0.png differ diff --git a/meme_generator/memes/confuse/images/1.png b/meme_generator/memes/confuse/images/1.png new file mode 100644 index 0000000000000000000000000000000000000000..099cc888679a241deea485d6a616e779031701c0 Binary files /dev/null and b/meme_generator/memes/confuse/images/1.png differ diff --git a/meme_generator/memes/confuse/images/10.png b/meme_generator/memes/confuse/images/10.png new file mode 100644 index 0000000000000000000000000000000000000000..2e15373c3ab4038330c7b37490117a302e1e748e Binary files /dev/null and b/meme_generator/memes/confuse/images/10.png differ diff --git a/meme_generator/memes/confuse/images/11.png b/meme_generator/memes/confuse/images/11.png new file mode 100644 index 0000000000000000000000000000000000000000..6ceed0615ca6481f8df27809fed1c6ed740c17fa Binary files /dev/null and b/meme_generator/memes/confuse/images/11.png differ diff --git a/meme_generator/memes/confuse/images/12.png b/meme_generator/memes/confuse/images/12.png new file mode 100644 index 0000000000000000000000000000000000000000..b70ec1e622237e95465877728612d1e1718c64ac Binary files /dev/null and b/meme_generator/memes/confuse/images/12.png differ diff --git a/meme_generator/memes/confuse/images/13.png b/meme_generator/memes/confuse/images/13.png new file mode 100644 index 0000000000000000000000000000000000000000..ba0f94187e1c4490ba8138db7c5d5f3d8b54781e Binary files /dev/null and b/meme_generator/memes/confuse/images/13.png differ diff --git a/meme_generator/memes/confuse/images/14.png b/meme_generator/memes/confuse/images/14.png new file mode 100644 index 0000000000000000000000000000000000000000..76a9f387e04fd9a62531e1289502e58f6932317f Binary files /dev/null and b/meme_generator/memes/confuse/images/14.png differ diff --git a/meme_generator/memes/confuse/images/15.png b/meme_generator/memes/confuse/images/15.png new file mode 100644 index 0000000000000000000000000000000000000000..3b92ced411e9c1aed51cddb745d2a33e490a2cff Binary files /dev/null and b/meme_generator/memes/confuse/images/15.png differ diff --git a/meme_generator/memes/confuse/images/16.png b/meme_generator/memes/confuse/images/16.png new file mode 100644 index 0000000000000000000000000000000000000000..a584fff2d457fa05992d9c4545514375ceb86f0b Binary files /dev/null and b/meme_generator/memes/confuse/images/16.png differ diff --git a/meme_generator/memes/confuse/images/17.png b/meme_generator/memes/confuse/images/17.png new file mode 100644 index 0000000000000000000000000000000000000000..88f6d62436bf9362bc9a6d11c35f444b2243502b Binary files /dev/null and b/meme_generator/memes/confuse/images/17.png differ diff --git a/meme_generator/memes/confuse/images/18.png b/meme_generator/memes/confuse/images/18.png new file mode 100644 index 0000000000000000000000000000000000000000..2a111dcd94e6e768dba1e86f8c4fea7661b131fe Binary files /dev/null and b/meme_generator/memes/confuse/images/18.png differ diff --git a/meme_generator/memes/confuse/images/19.png b/meme_generator/memes/confuse/images/19.png new file mode 100644 index 0000000000000000000000000000000000000000..1098e475b0e40e0c1ab740f821c6b9dbcf2d6f14 Binary files /dev/null and b/meme_generator/memes/confuse/images/19.png differ diff --git a/meme_generator/memes/confuse/images/2.png b/meme_generator/memes/confuse/images/2.png new file mode 100644 index 0000000000000000000000000000000000000000..a0e10280c35defdd6420852eb3517c9c47a4c322 Binary files /dev/null and b/meme_generator/memes/confuse/images/2.png differ diff --git a/meme_generator/memes/confuse/images/20.png b/meme_generator/memes/confuse/images/20.png new file mode 100644 index 0000000000000000000000000000000000000000..83a8cb97d4dfc1e79a7def2136f1180f2ad09f0d Binary files /dev/null and b/meme_generator/memes/confuse/images/20.png differ diff --git a/meme_generator/memes/confuse/images/21.png b/meme_generator/memes/confuse/images/21.png new file mode 100644 index 0000000000000000000000000000000000000000..44e3a4e3e96871e542e02c1e44593f7a6a59333e Binary files /dev/null and b/meme_generator/memes/confuse/images/21.png differ diff --git a/meme_generator/memes/confuse/images/22.png b/meme_generator/memes/confuse/images/22.png new file mode 100644 index 0000000000000000000000000000000000000000..1d358207c8ed67d20a41c6a4b1b0886551b6b412 Binary files /dev/null and b/meme_generator/memes/confuse/images/22.png differ diff --git a/meme_generator/memes/confuse/images/23.png b/meme_generator/memes/confuse/images/23.png new file mode 100644 index 0000000000000000000000000000000000000000..1800f20b40abde4f8f4710437438f75124addadb Binary files /dev/null and b/meme_generator/memes/confuse/images/23.png differ diff --git a/meme_generator/memes/confuse/images/24.png b/meme_generator/memes/confuse/images/24.png new file mode 100644 index 0000000000000000000000000000000000000000..90e8e02afc73ff602e49c87cc37c1326a6caf0e9 Binary files /dev/null and b/meme_generator/memes/confuse/images/24.png differ diff --git a/meme_generator/memes/confuse/images/25.png b/meme_generator/memes/confuse/images/25.png new file mode 100644 index 0000000000000000000000000000000000000000..0d62891229f227a7dd8b51b9bbe5f96251ac82ef Binary files /dev/null and b/meme_generator/memes/confuse/images/25.png differ diff --git a/meme_generator/memes/confuse/images/26.png b/meme_generator/memes/confuse/images/26.png new file mode 100644 index 0000000000000000000000000000000000000000..98031f17d66d806ccf26cdf721758bbe07467164 Binary files /dev/null and b/meme_generator/memes/confuse/images/26.png differ diff --git a/meme_generator/memes/confuse/images/27.png b/meme_generator/memes/confuse/images/27.png new file mode 100644 index 0000000000000000000000000000000000000000..8645f6cdaac15c7c345393751fc8a8e7d5c17528 Binary files /dev/null and b/meme_generator/memes/confuse/images/27.png differ diff --git a/meme_generator/memes/confuse/images/28.png b/meme_generator/memes/confuse/images/28.png new file mode 100644 index 0000000000000000000000000000000000000000..ee22b8d2fd58687746ba08580931fc026262b19e Binary files /dev/null and b/meme_generator/memes/confuse/images/28.png differ diff --git a/meme_generator/memes/confuse/images/29.png b/meme_generator/memes/confuse/images/29.png new file mode 100644 index 0000000000000000000000000000000000000000..5b2e00a2b84080a40a7f806c58f0ec184c8788e5 Binary files /dev/null and b/meme_generator/memes/confuse/images/29.png differ diff --git a/meme_generator/memes/confuse/images/3.png b/meme_generator/memes/confuse/images/3.png new file mode 100644 index 0000000000000000000000000000000000000000..48beacae35c58dc7d5f0068bd5f5c5bd3e160853 Binary files /dev/null and b/meme_generator/memes/confuse/images/3.png differ diff --git a/meme_generator/memes/confuse/images/30.png b/meme_generator/memes/confuse/images/30.png new file mode 100644 index 0000000000000000000000000000000000000000..c97e415d92dde8dfbd1c224f5b426881a8839e30 Binary files /dev/null and b/meme_generator/memes/confuse/images/30.png differ diff --git a/meme_generator/memes/confuse/images/31.png b/meme_generator/memes/confuse/images/31.png new file mode 100644 index 0000000000000000000000000000000000000000..67c5119a25341c373533136829dafa70e8786856 Binary files /dev/null and b/meme_generator/memes/confuse/images/31.png differ diff --git a/meme_generator/memes/confuse/images/32.png b/meme_generator/memes/confuse/images/32.png new file mode 100644 index 0000000000000000000000000000000000000000..1d73273866a933701d7d2977a99159150ff0f44c Binary files /dev/null and b/meme_generator/memes/confuse/images/32.png differ diff --git a/meme_generator/memes/confuse/images/33.png b/meme_generator/memes/confuse/images/33.png new file mode 100644 index 0000000000000000000000000000000000000000..703e4d615f8029d7e78b2380a8f006360916468b Binary files /dev/null and b/meme_generator/memes/confuse/images/33.png differ diff --git a/meme_generator/memes/confuse/images/34.png b/meme_generator/memes/confuse/images/34.png new file mode 100644 index 0000000000000000000000000000000000000000..53110a504c2e15805512199c29e37353de500441 Binary files /dev/null and b/meme_generator/memes/confuse/images/34.png differ diff --git a/meme_generator/memes/confuse/images/35.png b/meme_generator/memes/confuse/images/35.png new file mode 100644 index 0000000000000000000000000000000000000000..cce72ca08c038bc3da74845d27a663f38494695d Binary files /dev/null and b/meme_generator/memes/confuse/images/35.png differ diff --git a/meme_generator/memes/confuse/images/36.png b/meme_generator/memes/confuse/images/36.png new file mode 100644 index 0000000000000000000000000000000000000000..40e3a1b3ac9fe61227774f407a03191cb471ec9d Binary files /dev/null and b/meme_generator/memes/confuse/images/36.png differ diff --git a/meme_generator/memes/confuse/images/37.png b/meme_generator/memes/confuse/images/37.png new file mode 100644 index 0000000000000000000000000000000000000000..b0dca66b7ad53b937d9bc2b01779dd4646ed882d Binary files /dev/null and b/meme_generator/memes/confuse/images/37.png differ diff --git a/meme_generator/memes/confuse/images/38.png b/meme_generator/memes/confuse/images/38.png new file mode 100644 index 0000000000000000000000000000000000000000..52c0669ca2086dd0a0c0a2e7410496556e452ce6 Binary files /dev/null and b/meme_generator/memes/confuse/images/38.png differ diff --git a/meme_generator/memes/confuse/images/39.png b/meme_generator/memes/confuse/images/39.png new file mode 100644 index 0000000000000000000000000000000000000000..c022d1294940cf025be674628476f6e43ff7fceb Binary files /dev/null and b/meme_generator/memes/confuse/images/39.png differ diff --git a/meme_generator/memes/confuse/images/4.png b/meme_generator/memes/confuse/images/4.png new file mode 100644 index 0000000000000000000000000000000000000000..e6ca56f2b1d07324785f86ca9b3776d38e6e818b Binary files /dev/null and b/meme_generator/memes/confuse/images/4.png differ diff --git a/meme_generator/memes/confuse/images/40.png b/meme_generator/memes/confuse/images/40.png new file mode 100644 index 0000000000000000000000000000000000000000..c425860d5815579a45b126e5bd37286ed9159a81 Binary files /dev/null and b/meme_generator/memes/confuse/images/40.png differ diff --git a/meme_generator/memes/confuse/images/41.png b/meme_generator/memes/confuse/images/41.png new file mode 100644 index 0000000000000000000000000000000000000000..2a112e3a3e5d0f06e4b030183712a76ba7749a88 Binary files /dev/null and b/meme_generator/memes/confuse/images/41.png differ diff --git a/meme_generator/memes/confuse/images/42.png b/meme_generator/memes/confuse/images/42.png new file mode 100644 index 0000000000000000000000000000000000000000..e88f2c62a125687e6564422a02a033b0dcf062df Binary files /dev/null and b/meme_generator/memes/confuse/images/42.png differ diff --git a/meme_generator/memes/confuse/images/43.png b/meme_generator/memes/confuse/images/43.png new file mode 100644 index 0000000000000000000000000000000000000000..743b9da47f88d662ea488e2cce0135f151d434b5 Binary files /dev/null and b/meme_generator/memes/confuse/images/43.png differ diff --git a/meme_generator/memes/confuse/images/44.png b/meme_generator/memes/confuse/images/44.png new file mode 100644 index 0000000000000000000000000000000000000000..8032b7b8fea0da31d8986208e97a3d57feee7662 Binary files /dev/null and b/meme_generator/memes/confuse/images/44.png differ diff --git a/meme_generator/memes/confuse/images/45.png b/meme_generator/memes/confuse/images/45.png new file mode 100644 index 0000000000000000000000000000000000000000..5f1d17ac2b5c4cc355b078743ff0e20e19592cc5 Binary files /dev/null and b/meme_generator/memes/confuse/images/45.png differ diff --git a/meme_generator/memes/confuse/images/46.png b/meme_generator/memes/confuse/images/46.png new file mode 100644 index 0000000000000000000000000000000000000000..244fe7c9b52ab2eda8acaac17b8ef4574a06743b Binary files /dev/null and b/meme_generator/memes/confuse/images/46.png differ diff --git a/meme_generator/memes/confuse/images/47.png b/meme_generator/memes/confuse/images/47.png new file mode 100644 index 0000000000000000000000000000000000000000..19e3c70c873e511b9db9bdcadc3ecb55f991b83d Binary files /dev/null and b/meme_generator/memes/confuse/images/47.png differ diff --git a/meme_generator/memes/confuse/images/48.png b/meme_generator/memes/confuse/images/48.png new file mode 100644 index 0000000000000000000000000000000000000000..b89ddd5433d7cfa75c5a85d64ce6912f44a20b9f Binary files /dev/null and b/meme_generator/memes/confuse/images/48.png differ diff --git a/meme_generator/memes/confuse/images/49.png b/meme_generator/memes/confuse/images/49.png new file mode 100644 index 0000000000000000000000000000000000000000..842636eaacff9fb7dbb8dc976487208ebf5ee2bf Binary files /dev/null and b/meme_generator/memes/confuse/images/49.png differ diff --git a/meme_generator/memes/confuse/images/5.png b/meme_generator/memes/confuse/images/5.png new file mode 100644 index 0000000000000000000000000000000000000000..bc369fcff0040ac3daed89d2aab2855820679d5a Binary files /dev/null and b/meme_generator/memes/confuse/images/5.png differ diff --git a/meme_generator/memes/confuse/images/50.png b/meme_generator/memes/confuse/images/50.png new file mode 100644 index 0000000000000000000000000000000000000000..bf8bcf342dcf907073b75ce479c5eeeac37e3ad7 Binary files /dev/null and b/meme_generator/memes/confuse/images/50.png differ diff --git a/meme_generator/memes/confuse/images/51.png b/meme_generator/memes/confuse/images/51.png new file mode 100644 index 0000000000000000000000000000000000000000..1eecbeecb12b237bd2824c12a665ca8fa774cc5f Binary files /dev/null and b/meme_generator/memes/confuse/images/51.png differ diff --git a/meme_generator/memes/confuse/images/52.png b/meme_generator/memes/confuse/images/52.png new file mode 100644 index 0000000000000000000000000000000000000000..add49948c9d22fb4956f69767ffc6ef4532b8700 Binary files /dev/null and b/meme_generator/memes/confuse/images/52.png differ diff --git a/meme_generator/memes/confuse/images/53.png b/meme_generator/memes/confuse/images/53.png new file mode 100644 index 0000000000000000000000000000000000000000..605cc7a66bd43b5d7515f71e6cece93d2df1f60b Binary files /dev/null and b/meme_generator/memes/confuse/images/53.png differ diff --git a/meme_generator/memes/confuse/images/54.png b/meme_generator/memes/confuse/images/54.png new file mode 100644 index 0000000000000000000000000000000000000000..8918066d38655eb10c642a39d2132c74cf5b2544 Binary files /dev/null and b/meme_generator/memes/confuse/images/54.png differ diff --git a/meme_generator/memes/confuse/images/55.png b/meme_generator/memes/confuse/images/55.png new file mode 100644 index 0000000000000000000000000000000000000000..c3ca773fdf26dd75ab73603244ec548538941030 Binary files /dev/null and b/meme_generator/memes/confuse/images/55.png differ diff --git a/meme_generator/memes/confuse/images/56.png b/meme_generator/memes/confuse/images/56.png new file mode 100644 index 0000000000000000000000000000000000000000..64e4c09602a37961976014fcb404e4b2f59a808f Binary files /dev/null and b/meme_generator/memes/confuse/images/56.png differ diff --git a/meme_generator/memes/confuse/images/57.png b/meme_generator/memes/confuse/images/57.png new file mode 100644 index 0000000000000000000000000000000000000000..622439f7c82a4c59b4cb00dc67b98d03136b6f22 Binary files /dev/null and b/meme_generator/memes/confuse/images/57.png differ diff --git a/meme_generator/memes/confuse/images/58.png b/meme_generator/memes/confuse/images/58.png new file mode 100644 index 0000000000000000000000000000000000000000..02a23a2f4d48d60b0070ba9f6f411f2190860a9a Binary files /dev/null and b/meme_generator/memes/confuse/images/58.png differ diff --git a/meme_generator/memes/confuse/images/59.png b/meme_generator/memes/confuse/images/59.png new file mode 100644 index 0000000000000000000000000000000000000000..7d3e10c1f8712f5e2331b0d6179c3a2069921a9e Binary files /dev/null and b/meme_generator/memes/confuse/images/59.png differ diff --git a/meme_generator/memes/confuse/images/6.png b/meme_generator/memes/confuse/images/6.png new file mode 100644 index 0000000000000000000000000000000000000000..3dc2e19ba3b58248d21b9496d53849d842c14d88 Binary files /dev/null and b/meme_generator/memes/confuse/images/6.png differ diff --git a/meme_generator/memes/confuse/images/60.png b/meme_generator/memes/confuse/images/60.png new file mode 100644 index 0000000000000000000000000000000000000000..daf73ac3e5950757e3f6993115cdd32864c91f4c Binary files /dev/null and b/meme_generator/memes/confuse/images/60.png differ diff --git a/meme_generator/memes/confuse/images/61.png b/meme_generator/memes/confuse/images/61.png new file mode 100644 index 0000000000000000000000000000000000000000..b398747d4a27ef57941046b4776b4304b2ddddae Binary files /dev/null and b/meme_generator/memes/confuse/images/61.png differ diff --git a/meme_generator/memes/confuse/images/62.png b/meme_generator/memes/confuse/images/62.png new file mode 100644 index 0000000000000000000000000000000000000000..ef75263d2ff358dd108cff09de5778a0e2f96560 Binary files /dev/null and b/meme_generator/memes/confuse/images/62.png differ diff --git a/meme_generator/memes/confuse/images/63.png b/meme_generator/memes/confuse/images/63.png new file mode 100644 index 0000000000000000000000000000000000000000..1d7bb0c61b541853b8c35e4f1861b7e3bb52fdad Binary files /dev/null and b/meme_generator/memes/confuse/images/63.png differ diff --git a/meme_generator/memes/confuse/images/64.png b/meme_generator/memes/confuse/images/64.png new file mode 100644 index 0000000000000000000000000000000000000000..10e54fd3f0fd495fcfc6d25978ef21b3a434692f Binary files /dev/null and b/meme_generator/memes/confuse/images/64.png differ diff --git a/meme_generator/memes/confuse/images/65.png b/meme_generator/memes/confuse/images/65.png new file mode 100644 index 0000000000000000000000000000000000000000..b3170785d1e402652d6c13e5f46f7ada344b9701 Binary files /dev/null and b/meme_generator/memes/confuse/images/65.png differ diff --git a/meme_generator/memes/confuse/images/66.png b/meme_generator/memes/confuse/images/66.png new file mode 100644 index 0000000000000000000000000000000000000000..ee96b51f9fa6cb70f581676d2eb18ec21d4ea0bb Binary files /dev/null and b/meme_generator/memes/confuse/images/66.png differ diff --git a/meme_generator/memes/confuse/images/67.png b/meme_generator/memes/confuse/images/67.png new file mode 100644 index 0000000000000000000000000000000000000000..bede24344ff07047458d94189ac72618fe59d6b3 Binary files /dev/null and b/meme_generator/memes/confuse/images/67.png differ diff --git a/meme_generator/memes/confuse/images/68.png b/meme_generator/memes/confuse/images/68.png new file mode 100644 index 0000000000000000000000000000000000000000..4761e8deb7631f54e5c8641176d3ab1f9ccee785 Binary files /dev/null and b/meme_generator/memes/confuse/images/68.png differ diff --git a/meme_generator/memes/confuse/images/69.png b/meme_generator/memes/confuse/images/69.png new file mode 100644 index 0000000000000000000000000000000000000000..6c7ea469f041d6ec000eed75c588c1d9cbbc1d12 Binary files /dev/null and b/meme_generator/memes/confuse/images/69.png differ diff --git a/meme_generator/memes/confuse/images/7.png b/meme_generator/memes/confuse/images/7.png new file mode 100644 index 0000000000000000000000000000000000000000..a5d75bac0fd973ab1dbb72e12e153f8c4187b06b Binary files /dev/null and b/meme_generator/memes/confuse/images/7.png differ diff --git a/meme_generator/memes/confuse/images/70.png b/meme_generator/memes/confuse/images/70.png new file mode 100644 index 0000000000000000000000000000000000000000..6a2c52b3cd844af82a690a1ef0b091e748508c4a Binary files /dev/null and b/meme_generator/memes/confuse/images/70.png differ diff --git a/meme_generator/memes/confuse/images/71.png b/meme_generator/memes/confuse/images/71.png new file mode 100644 index 0000000000000000000000000000000000000000..47d6083b257f01d5d8354e7c9c92cfd94463a864 Binary files /dev/null and b/meme_generator/memes/confuse/images/71.png differ diff --git a/meme_generator/memes/confuse/images/72.png b/meme_generator/memes/confuse/images/72.png new file mode 100644 index 0000000000000000000000000000000000000000..bfcb3fe4732f1318cea71e6549f5dcebd05732af Binary files /dev/null and b/meme_generator/memes/confuse/images/72.png differ diff --git a/meme_generator/memes/confuse/images/73.png b/meme_generator/memes/confuse/images/73.png new file mode 100644 index 0000000000000000000000000000000000000000..26b595c0fafb049f079ef25315b8b03d7f8f3878 Binary files /dev/null and b/meme_generator/memes/confuse/images/73.png differ diff --git a/meme_generator/memes/confuse/images/74.png b/meme_generator/memes/confuse/images/74.png new file mode 100644 index 0000000000000000000000000000000000000000..cf7ba09939cea433a85d69e1d8cdeb66da030214 Binary files /dev/null and b/meme_generator/memes/confuse/images/74.png differ diff --git a/meme_generator/memes/confuse/images/75.png b/meme_generator/memes/confuse/images/75.png new file mode 100644 index 0000000000000000000000000000000000000000..ba5fa9b1aa8fc78da7ba6fb96f916761f86e648c Binary files /dev/null and b/meme_generator/memes/confuse/images/75.png differ diff --git a/meme_generator/memes/confuse/images/76.png b/meme_generator/memes/confuse/images/76.png new file mode 100644 index 0000000000000000000000000000000000000000..91a976d003d2fb7612019eb6c2ea482924a86f95 Binary files /dev/null and b/meme_generator/memes/confuse/images/76.png differ diff --git a/meme_generator/memes/confuse/images/77.png b/meme_generator/memes/confuse/images/77.png new file mode 100644 index 0000000000000000000000000000000000000000..1c9e8b77843610aee5ce612b77ecf985ae0cb084 Binary files /dev/null and b/meme_generator/memes/confuse/images/77.png differ diff --git a/meme_generator/memes/confuse/images/78.png b/meme_generator/memes/confuse/images/78.png new file mode 100644 index 0000000000000000000000000000000000000000..1779a0b8a3b3e82a7c4ee7627001554d22ff1888 Binary files /dev/null and b/meme_generator/memes/confuse/images/78.png differ diff --git a/meme_generator/memes/confuse/images/79.png b/meme_generator/memes/confuse/images/79.png new file mode 100644 index 0000000000000000000000000000000000000000..75ff345443e7e4800bc77193874cffa52a44c8b2 Binary files /dev/null and b/meme_generator/memes/confuse/images/79.png differ diff --git a/meme_generator/memes/confuse/images/8.png b/meme_generator/memes/confuse/images/8.png new file mode 100644 index 0000000000000000000000000000000000000000..97022192f8c938f7757b6336a7ef3b59e297fe5a Binary files /dev/null and b/meme_generator/memes/confuse/images/8.png differ diff --git a/meme_generator/memes/confuse/images/80.png b/meme_generator/memes/confuse/images/80.png new file mode 100644 index 0000000000000000000000000000000000000000..3dd0442a878b76811f1e510042d0dd6bbeb2784a Binary files /dev/null and b/meme_generator/memes/confuse/images/80.png differ diff --git a/meme_generator/memes/confuse/images/81.png b/meme_generator/memes/confuse/images/81.png new file mode 100644 index 0000000000000000000000000000000000000000..d56f6798bb19e492649ef87de267aa4891f52e24 Binary files /dev/null and b/meme_generator/memes/confuse/images/81.png differ diff --git a/meme_generator/memes/confuse/images/82.png b/meme_generator/memes/confuse/images/82.png new file mode 100644 index 0000000000000000000000000000000000000000..c5e1cddb36362837df6564b7b18085a5686147d3 Binary files /dev/null and b/meme_generator/memes/confuse/images/82.png differ diff --git a/meme_generator/memes/confuse/images/83.png b/meme_generator/memes/confuse/images/83.png new file mode 100644 index 0000000000000000000000000000000000000000..b2c77a0f7a268339548630ecc8deb755056c1af2 Binary files /dev/null and b/meme_generator/memes/confuse/images/83.png differ diff --git a/meme_generator/memes/confuse/images/84.png b/meme_generator/memes/confuse/images/84.png new file mode 100644 index 0000000000000000000000000000000000000000..e1bf800d55ca53b60dc62b0cbf074b6161a7afd8 Binary files /dev/null and b/meme_generator/memes/confuse/images/84.png differ diff --git a/meme_generator/memes/confuse/images/85.png b/meme_generator/memes/confuse/images/85.png new file mode 100644 index 0000000000000000000000000000000000000000..cba7edb33fe07936b857a7921ee0f226f2c798fa Binary files /dev/null and b/meme_generator/memes/confuse/images/85.png differ diff --git a/meme_generator/memes/confuse/images/86.png b/meme_generator/memes/confuse/images/86.png new file mode 100644 index 0000000000000000000000000000000000000000..332413ee15c0fef6c5d864efcb884ffb73f48aa5 Binary files /dev/null and b/meme_generator/memes/confuse/images/86.png differ diff --git a/meme_generator/memes/confuse/images/87.png b/meme_generator/memes/confuse/images/87.png new file mode 100644 index 0000000000000000000000000000000000000000..1c1967891b6ba2625a8dd044680de1ccc8038d8c Binary files /dev/null and b/meme_generator/memes/confuse/images/87.png differ diff --git a/meme_generator/memes/confuse/images/88.png b/meme_generator/memes/confuse/images/88.png new file mode 100644 index 0000000000000000000000000000000000000000..62273b9ac49123b3f72347769c7af069cf4dacc5 Binary files /dev/null and b/meme_generator/memes/confuse/images/88.png differ diff --git a/meme_generator/memes/confuse/images/89.png b/meme_generator/memes/confuse/images/89.png new file mode 100644 index 0000000000000000000000000000000000000000..c58481e1057434c799105fcf61f314be6876d616 Binary files /dev/null and b/meme_generator/memes/confuse/images/89.png differ diff --git a/meme_generator/memes/confuse/images/9.png b/meme_generator/memes/confuse/images/9.png new file mode 100644 index 0000000000000000000000000000000000000000..a88e52b53b742b40cb9b43d9ba8960c932a426e7 Binary files /dev/null and b/meme_generator/memes/confuse/images/9.png differ diff --git a/meme_generator/memes/confuse/images/90.png b/meme_generator/memes/confuse/images/90.png new file mode 100644 index 0000000000000000000000000000000000000000..b0882ca9eaaae4f4685e17d9bf96346d81d74971 Binary files /dev/null and b/meme_generator/memes/confuse/images/90.png differ diff --git a/meme_generator/memes/confuse/images/91.png b/meme_generator/memes/confuse/images/91.png new file mode 100644 index 0000000000000000000000000000000000000000..99c7c8618822fa3dd15a358d30e2b1e1e8c29d89 Binary files /dev/null and b/meme_generator/memes/confuse/images/91.png differ diff --git a/meme_generator/memes/confuse/images/92.png b/meme_generator/memes/confuse/images/92.png new file mode 100644 index 0000000000000000000000000000000000000000..097978a524f3daf682e54a75ea0cb385e75ff072 Binary files /dev/null and b/meme_generator/memes/confuse/images/92.png differ diff --git a/meme_generator/memes/confuse/images/93.png b/meme_generator/memes/confuse/images/93.png new file mode 100644 index 0000000000000000000000000000000000000000..948ed37c0fe1c8ba336c5333989028f2568ab0ac Binary files /dev/null and b/meme_generator/memes/confuse/images/93.png differ diff --git a/meme_generator/memes/confuse/images/94.png b/meme_generator/memes/confuse/images/94.png new file mode 100644 index 0000000000000000000000000000000000000000..29fac303eee9e68a717b14810bfdd2b1a40db6e5 Binary files /dev/null and b/meme_generator/memes/confuse/images/94.png differ diff --git a/meme_generator/memes/confuse/images/95.png b/meme_generator/memes/confuse/images/95.png new file mode 100644 index 0000000000000000000000000000000000000000..59355b6790f7fc7fe91e73f374cf33a54b53d45e Binary files /dev/null and b/meme_generator/memes/confuse/images/95.png differ diff --git a/meme_generator/memes/confuse/images/96.png b/meme_generator/memes/confuse/images/96.png new file mode 100644 index 0000000000000000000000000000000000000000..e218a7d7369370093af2682412536fdb9bdcd41c Binary files /dev/null and b/meme_generator/memes/confuse/images/96.png differ diff --git a/meme_generator/memes/confuse/images/97.png b/meme_generator/memes/confuse/images/97.png new file mode 100644 index 0000000000000000000000000000000000000000..bf57b52052c9ffd6b81edcf9467e8a65564297b6 Binary files /dev/null and b/meme_generator/memes/confuse/images/97.png differ diff --git a/meme_generator/memes/confuse/images/98.png b/meme_generator/memes/confuse/images/98.png new file mode 100644 index 0000000000000000000000000000000000000000..beaa8bf7de13c3f9cf285d1c4c8012405566fd7f Binary files /dev/null and b/meme_generator/memes/confuse/images/98.png differ diff --git a/meme_generator/memes/confuse/images/99.png b/meme_generator/memes/confuse/images/99.png new file mode 100644 index 0000000000000000000000000000000000000000..24fc104e1e00b42dc441f6e096f712edf8e92bff Binary files /dev/null and b/meme_generator/memes/confuse/images/99.png differ diff --git a/meme_generator/memes/coupon/__init__.py b/meme_generator/memes/coupon/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..4616e994a866738cb5249ee1fe3e71a697f7c8bd --- /dev/null +++ b/meme_generator/memes/coupon/__init__.py @@ -0,0 +1,43 @@ +from pathlib import Path +from typing import List + +from pil_utils import BuildImage + +from meme_generator import MemeArgsModel, add_meme +from meme_generator.exception import TextOverLength + +img_dir = Path(__file__).parent / "images" + + +def coupon(images: List[BuildImage], texts: List[str], args: MemeArgsModel): + img = images[0].convert("RGBA").circle().resize((60, 60)) + name = args.user_infos[0].name if args.user_infos else "" + text = (texts[0] if texts else f"{name}陪睡券") + "\n(永久有效)" + + text_img = BuildImage.new("RGBA", (250, 100)) + try: + text_img.draw_text( + (0, 0, text_img.width, text_img.height), + text, + lines_align="center", + max_fontsize=30, + min_fontsize=15, + ) + except ValueError: + raise TextOverLength(text) + + frame = BuildImage.open(img_dir / "0.png") + frame.paste(img.rotate(22, expand=True), (164, 85), alpha=True) + frame.paste(text_img.rotate(22, expand=True), (94, 108), alpha=True) + return frame.save_jpg() + + +add_meme( + "coupon", + coupon, + min_images=1, + max_images=1, + min_texts=0, + max_texts=1, + keywords=["兑换券"], +) diff --git a/meme_generator/memes/coupon/images/0.png b/meme_generator/memes/coupon/images/0.png new file mode 100644 index 0000000000000000000000000000000000000000..22b42b311689d872cde2f386314dd0b3c19bc2e8 Binary files /dev/null and b/meme_generator/memes/coupon/images/0.png differ diff --git a/meme_generator/memes/cover_face/__init__.py b/meme_generator/memes/cover_face/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..b28aa8e58319f5b20dd3594a7678137f240bf851 --- /dev/null +++ b/meme_generator/memes/cover_face/__init__.py @@ -0,0 +1,19 @@ +from pathlib import Path +from typing import List + +from pil_utils import BuildImage + +from meme_generator import add_meme + +img_dir = Path(__file__).parent / "images" + + +def cover_face(images: List[BuildImage], texts, args): + points = ((15, 15), (448, 0), (445, 456), (0, 465)) + img = images[0].convert("RGBA").square().resize((450, 450)).perspective(points) + frame = BuildImage.open(img_dir / "0.png") + frame.paste(img, (120, 150), below=True) + return frame.save_jpg() + + +add_meme("cover_face", cover_face, min_images=1, max_images=1, keywords=["捂脸"]) diff --git a/meme_generator/memes/cover_face/images/0.png b/meme_generator/memes/cover_face/images/0.png new file mode 100644 index 0000000000000000000000000000000000000000..2c6d982a39ce1e8b2e79a188ac03e7ed6a2dde48 Binary files /dev/null and b/meme_generator/memes/cover_face/images/0.png differ diff --git a/meme_generator/memes/crawl/__init__.py b/meme_generator/memes/crawl/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..f4aced4e57c3232e75044fe47fbeb0b076805081 --- /dev/null +++ b/meme_generator/memes/crawl/__init__.py @@ -0,0 +1,43 @@ +import random +from pathlib import Path +from typing import List + +from pil_utils import BuildImage +from pydantic import Field + +from meme_generator import MemeArgsModel, MemeArgsParser, MemeArgsType, add_meme + +img_dir = Path(__file__).parent / "images" + + +help = "图片编号,范围为 1~92" + +parser = MemeArgsParser() +parser.add_argument("-n", "--number", type=int, default=0, help=help) + + +class Model(MemeArgsModel): + number: int = Field(0, description=help) + + +def crawl(images: List[BuildImage], texts: List[str], args: Model): + total_num = 92 + if 1 <= args.number <= total_num: + num = args.number + else: + num = random.randint(1, total_num) + + img = images[0].convert("RGBA").circle().resize((100, 100)) + frame = BuildImage.open(img_dir / f"{num:02d}.jpg") + frame.paste(img, (0, 400), alpha=True) + return frame.save_jpg() + + +add_meme( + "crawl", + crawl, + min_images=1, + max_images=1, + args_type=MemeArgsType(parser, Model), + keywords=["爬"], +) diff --git a/meme_generator/memes/crawl/images/01.jpg b/meme_generator/memes/crawl/images/01.jpg new file mode 100644 index 0000000000000000000000000000000000000000..39f5ea67be055f6809c9aafd40b3fdbcefb9067b Binary files /dev/null and b/meme_generator/memes/crawl/images/01.jpg differ diff --git a/meme_generator/memes/crawl/images/02.jpg b/meme_generator/memes/crawl/images/02.jpg new file mode 100644 index 0000000000000000000000000000000000000000..18ff5c0d8f266df77b91b5631c683196e8847f2f Binary files /dev/null and b/meme_generator/memes/crawl/images/02.jpg differ diff --git a/meme_generator/memes/crawl/images/03.jpg b/meme_generator/memes/crawl/images/03.jpg new file mode 100644 index 0000000000000000000000000000000000000000..9c800007c2fc355b8df7bbcfb6f4e3b61a471eab Binary files /dev/null and b/meme_generator/memes/crawl/images/03.jpg differ diff --git a/meme_generator/memes/crawl/images/04.jpg b/meme_generator/memes/crawl/images/04.jpg new file mode 100644 index 0000000000000000000000000000000000000000..05a0dd322331ace54b7263dbcc97259a5d49f73d Binary files /dev/null and b/meme_generator/memes/crawl/images/04.jpg differ diff --git a/meme_generator/memes/crawl/images/05.jpg b/meme_generator/memes/crawl/images/05.jpg new file mode 100644 index 0000000000000000000000000000000000000000..214829ccdf3d4fd788cff524f19f59277f3702b2 Binary files /dev/null and b/meme_generator/memes/crawl/images/05.jpg differ diff --git a/meme_generator/memes/crawl/images/06.jpg b/meme_generator/memes/crawl/images/06.jpg new file mode 100644 index 0000000000000000000000000000000000000000..de754aaefb12b5913bcab5675c5d59dda3bf026f Binary files /dev/null and b/meme_generator/memes/crawl/images/06.jpg differ diff --git a/meme_generator/memes/crawl/images/07.jpg b/meme_generator/memes/crawl/images/07.jpg new file mode 100644 index 0000000000000000000000000000000000000000..fab88ab2ae39b029d0e06d8e52ffa1a043c30899 Binary files /dev/null and b/meme_generator/memes/crawl/images/07.jpg differ diff --git a/meme_generator/memes/crawl/images/08.jpg b/meme_generator/memes/crawl/images/08.jpg new file mode 100644 index 0000000000000000000000000000000000000000..dabc513d5fca7551c937c636ac5f620c391b1a67 Binary files /dev/null and b/meme_generator/memes/crawl/images/08.jpg differ diff --git a/meme_generator/memes/crawl/images/09.jpg b/meme_generator/memes/crawl/images/09.jpg new file mode 100644 index 0000000000000000000000000000000000000000..a9d39c67def1731b55ce58695a083b1da78a8092 Binary files /dev/null and b/meme_generator/memes/crawl/images/09.jpg differ diff --git a/meme_generator/memes/crawl/images/10.jpg b/meme_generator/memes/crawl/images/10.jpg new file mode 100644 index 0000000000000000000000000000000000000000..4bad12f7e95261af0580aadbcdf6dcf9408edd17 Binary files /dev/null and b/meme_generator/memes/crawl/images/10.jpg differ diff --git a/meme_generator/memes/crawl/images/11.jpg b/meme_generator/memes/crawl/images/11.jpg new file mode 100644 index 0000000000000000000000000000000000000000..fb7c887fcbdc500dfcdecb361704c6756630df72 Binary files /dev/null and b/meme_generator/memes/crawl/images/11.jpg differ diff --git a/meme_generator/memes/crawl/images/12.jpg b/meme_generator/memes/crawl/images/12.jpg new file mode 100644 index 0000000000000000000000000000000000000000..c7e5bf1bad144d9bd0475fffab550a1407bfc584 Binary files /dev/null and b/meme_generator/memes/crawl/images/12.jpg differ diff --git a/meme_generator/memes/crawl/images/13.jpg b/meme_generator/memes/crawl/images/13.jpg new file mode 100644 index 0000000000000000000000000000000000000000..c2b6a3d309df9ea9353de14ca5ef52876474b43c Binary files /dev/null and b/meme_generator/memes/crawl/images/13.jpg differ diff --git a/meme_generator/memes/crawl/images/14.jpg b/meme_generator/memes/crawl/images/14.jpg new file mode 100644 index 0000000000000000000000000000000000000000..56002ec139f38a424390db13e15c24144c037b8b Binary files /dev/null and b/meme_generator/memes/crawl/images/14.jpg differ diff --git a/meme_generator/memes/crawl/images/15.jpg b/meme_generator/memes/crawl/images/15.jpg new file mode 100644 index 0000000000000000000000000000000000000000..9d638267227f560c7d1b34abc64bde823f268640 Binary files /dev/null and b/meme_generator/memes/crawl/images/15.jpg differ diff --git a/meme_generator/memes/crawl/images/16.jpg b/meme_generator/memes/crawl/images/16.jpg new file mode 100644 index 0000000000000000000000000000000000000000..0737c91e63ac1c92f000cf68ba812f5f2f861f0f Binary files /dev/null and b/meme_generator/memes/crawl/images/16.jpg differ diff --git a/meme_generator/memes/crawl/images/17.jpg b/meme_generator/memes/crawl/images/17.jpg new file mode 100644 index 0000000000000000000000000000000000000000..46833cd19efdb0869d5406ece72b460bc840d39a Binary files /dev/null and b/meme_generator/memes/crawl/images/17.jpg differ diff --git a/meme_generator/memes/crawl/images/18.jpg b/meme_generator/memes/crawl/images/18.jpg new file mode 100644 index 0000000000000000000000000000000000000000..c51efe1ebdb47f7a459c8d59290bee688ce44252 Binary files /dev/null and b/meme_generator/memes/crawl/images/18.jpg differ diff --git a/meme_generator/memes/crawl/images/19.jpg b/meme_generator/memes/crawl/images/19.jpg new file mode 100644 index 0000000000000000000000000000000000000000..6e5cfc9fcd675ee129c8fd4f45f470d16778d5ba Binary files /dev/null and b/meme_generator/memes/crawl/images/19.jpg differ diff --git a/meme_generator/memes/crawl/images/20.jpg b/meme_generator/memes/crawl/images/20.jpg new file mode 100644 index 0000000000000000000000000000000000000000..8e136c93ae47ece3f1af62e858399ebc1c997c60 Binary files /dev/null and b/meme_generator/memes/crawl/images/20.jpg differ diff --git a/meme_generator/memes/crawl/images/21.jpg b/meme_generator/memes/crawl/images/21.jpg new file mode 100644 index 0000000000000000000000000000000000000000..06bff870849f5c2a6d535449d8c513dce7233d31 Binary files /dev/null and b/meme_generator/memes/crawl/images/21.jpg differ diff --git a/meme_generator/memes/crawl/images/22.jpg b/meme_generator/memes/crawl/images/22.jpg new file mode 100644 index 0000000000000000000000000000000000000000..5a8d910a67b30905d23caa9709dd8d901c408a28 Binary files /dev/null and b/meme_generator/memes/crawl/images/22.jpg differ diff --git a/meme_generator/memes/crawl/images/23.jpg b/meme_generator/memes/crawl/images/23.jpg new file mode 100644 index 0000000000000000000000000000000000000000..bde5c6d946d5647f0ceea58fce8d61501f13e99c Binary files /dev/null and b/meme_generator/memes/crawl/images/23.jpg differ diff --git a/meme_generator/memes/crawl/images/24.jpg b/meme_generator/memes/crawl/images/24.jpg new file mode 100644 index 0000000000000000000000000000000000000000..98c3a1da7b145431b92170dd3209cb7d2fe5b5f5 Binary files /dev/null and b/meme_generator/memes/crawl/images/24.jpg differ diff --git a/meme_generator/memes/crawl/images/25.jpg b/meme_generator/memes/crawl/images/25.jpg new file mode 100644 index 0000000000000000000000000000000000000000..ea2d734603cd55be329c27eb460a6a45b74e703a Binary files /dev/null and b/meme_generator/memes/crawl/images/25.jpg differ diff --git a/meme_generator/memes/crawl/images/26.jpg b/meme_generator/memes/crawl/images/26.jpg new file mode 100644 index 0000000000000000000000000000000000000000..11d04226558c396889637fed2b517537b863d0c5 Binary files /dev/null and b/meme_generator/memes/crawl/images/26.jpg differ diff --git a/meme_generator/memes/crawl/images/27.jpg b/meme_generator/memes/crawl/images/27.jpg new file mode 100644 index 0000000000000000000000000000000000000000..c569d21f058e51bdb2088ba2699bc4f979b4a9b4 Binary files /dev/null and b/meme_generator/memes/crawl/images/27.jpg differ diff --git a/meme_generator/memes/crawl/images/28.jpg b/meme_generator/memes/crawl/images/28.jpg new file mode 100644 index 0000000000000000000000000000000000000000..c0a4df3d702c58af039e182fc17a6681a11fed48 Binary files /dev/null and b/meme_generator/memes/crawl/images/28.jpg differ diff --git a/meme_generator/memes/crawl/images/29.jpg b/meme_generator/memes/crawl/images/29.jpg new file mode 100644 index 0000000000000000000000000000000000000000..93bdd1b226abeb5f1b1b4941e2c3dfbcb98361b1 Binary files /dev/null and b/meme_generator/memes/crawl/images/29.jpg differ diff --git a/meme_generator/memes/crawl/images/30.jpg b/meme_generator/memes/crawl/images/30.jpg new file mode 100644 index 0000000000000000000000000000000000000000..abc78b6f8d873e45a47040f70a30debe6a9f7553 Binary files /dev/null and b/meme_generator/memes/crawl/images/30.jpg differ diff --git a/meme_generator/memes/crawl/images/31.jpg b/meme_generator/memes/crawl/images/31.jpg new file mode 100644 index 0000000000000000000000000000000000000000..fd1dac7882efb99a3610924d8619cb437ebdf60a Binary files /dev/null and b/meme_generator/memes/crawl/images/31.jpg differ diff --git a/meme_generator/memes/crawl/images/32.jpg b/meme_generator/memes/crawl/images/32.jpg new file mode 100644 index 0000000000000000000000000000000000000000..a72b61b7ab943213c661978ba11d26a3de74c029 Binary files /dev/null and b/meme_generator/memes/crawl/images/32.jpg differ diff --git a/meme_generator/memes/crawl/images/33.jpg b/meme_generator/memes/crawl/images/33.jpg new file mode 100644 index 0000000000000000000000000000000000000000..738d0fdcc02ee1acee3eead99c6c4d23c4c5e062 Binary files /dev/null and b/meme_generator/memes/crawl/images/33.jpg differ diff --git a/meme_generator/memes/crawl/images/34.jpg b/meme_generator/memes/crawl/images/34.jpg new file mode 100644 index 0000000000000000000000000000000000000000..64ab4977561e75b95ad878df1d18a53e86487ba4 Binary files /dev/null and b/meme_generator/memes/crawl/images/34.jpg differ diff --git a/meme_generator/memes/crawl/images/35.jpg b/meme_generator/memes/crawl/images/35.jpg new file mode 100644 index 0000000000000000000000000000000000000000..cc071ba66354560ca7db04690d9cee409dc0c983 Binary files /dev/null and b/meme_generator/memes/crawl/images/35.jpg differ diff --git a/meme_generator/memes/crawl/images/36.jpg b/meme_generator/memes/crawl/images/36.jpg new file mode 100644 index 0000000000000000000000000000000000000000..87f47946e334ce614113fb88a65a306cf0d1eee4 Binary files /dev/null and b/meme_generator/memes/crawl/images/36.jpg differ diff --git a/meme_generator/memes/crawl/images/37.jpg b/meme_generator/memes/crawl/images/37.jpg new file mode 100644 index 0000000000000000000000000000000000000000..18728d36b22f79bd0b3c21657aa897fc0be8e08d Binary files /dev/null and b/meme_generator/memes/crawl/images/37.jpg differ diff --git a/meme_generator/memes/crawl/images/38.jpg b/meme_generator/memes/crawl/images/38.jpg new file mode 100644 index 0000000000000000000000000000000000000000..7b2dba68cda98873bdab97ec71cad108eaa0ab91 Binary files /dev/null and b/meme_generator/memes/crawl/images/38.jpg differ diff --git a/meme_generator/memes/crawl/images/39.jpg b/meme_generator/memes/crawl/images/39.jpg new file mode 100644 index 0000000000000000000000000000000000000000..ef7e563c105a0ab62f4f19bb1e3d78c8f7c13a6a Binary files /dev/null and b/meme_generator/memes/crawl/images/39.jpg differ diff --git a/meme_generator/memes/crawl/images/40.jpg b/meme_generator/memes/crawl/images/40.jpg new file mode 100644 index 0000000000000000000000000000000000000000..9bdea7a7c59a739f11931c756a6f8d1f1c879abb Binary files /dev/null and b/meme_generator/memes/crawl/images/40.jpg differ diff --git a/meme_generator/memes/crawl/images/41.jpg b/meme_generator/memes/crawl/images/41.jpg new file mode 100644 index 0000000000000000000000000000000000000000..849223987aefc66b686252b68d5acf21cc3c719e Binary files /dev/null and b/meme_generator/memes/crawl/images/41.jpg differ diff --git a/meme_generator/memes/crawl/images/42.jpg b/meme_generator/memes/crawl/images/42.jpg new file mode 100644 index 0000000000000000000000000000000000000000..f9775460e3c718f93a6174842a235d64bbf7a224 Binary files /dev/null and b/meme_generator/memes/crawl/images/42.jpg differ diff --git a/meme_generator/memes/crawl/images/43.jpg b/meme_generator/memes/crawl/images/43.jpg new file mode 100644 index 0000000000000000000000000000000000000000..c0088e0dd34fa2d1b4f68eecbec8aa5d00276954 Binary files /dev/null and b/meme_generator/memes/crawl/images/43.jpg differ diff --git a/meme_generator/memes/crawl/images/44.jpg b/meme_generator/memes/crawl/images/44.jpg new file mode 100644 index 0000000000000000000000000000000000000000..a6caddbd3af3d32f1b048007d8a4bbe470be03eb Binary files /dev/null and b/meme_generator/memes/crawl/images/44.jpg differ diff --git a/meme_generator/memes/crawl/images/45.jpg b/meme_generator/memes/crawl/images/45.jpg new file mode 100644 index 0000000000000000000000000000000000000000..0038d15c82c71c0b86fd464dd03fa3f175343678 Binary files /dev/null and b/meme_generator/memes/crawl/images/45.jpg differ diff --git a/meme_generator/memes/crawl/images/46.jpg b/meme_generator/memes/crawl/images/46.jpg new file mode 100644 index 0000000000000000000000000000000000000000..0b8c1a43bb1e9660f00be052aaca87f29833c028 Binary files /dev/null and b/meme_generator/memes/crawl/images/46.jpg differ diff --git a/meme_generator/memes/crawl/images/47.jpg b/meme_generator/memes/crawl/images/47.jpg new file mode 100644 index 0000000000000000000000000000000000000000..7c15c83550957118bccd5e1a2b9806d0094dc9ec Binary files /dev/null and b/meme_generator/memes/crawl/images/47.jpg differ diff --git a/meme_generator/memes/crawl/images/48.jpg b/meme_generator/memes/crawl/images/48.jpg new file mode 100644 index 0000000000000000000000000000000000000000..817aa618f63067d21332897c466be2e421f9f2c5 Binary files /dev/null and b/meme_generator/memes/crawl/images/48.jpg differ diff --git a/meme_generator/memes/crawl/images/49.jpg b/meme_generator/memes/crawl/images/49.jpg new file mode 100644 index 0000000000000000000000000000000000000000..4bd8be1f4672ae9897829189bc096af3df9b7390 Binary files /dev/null and b/meme_generator/memes/crawl/images/49.jpg differ diff --git a/meme_generator/memes/crawl/images/50.jpg b/meme_generator/memes/crawl/images/50.jpg new file mode 100644 index 0000000000000000000000000000000000000000..33453f04f3b1eefefdb3df2d95e20c4ebd705f36 Binary files /dev/null and b/meme_generator/memes/crawl/images/50.jpg differ diff --git a/meme_generator/memes/crawl/images/51.jpg b/meme_generator/memes/crawl/images/51.jpg new file mode 100644 index 0000000000000000000000000000000000000000..94db098a95ef364ad299c22982a55b59c9bfe382 Binary files /dev/null and b/meme_generator/memes/crawl/images/51.jpg differ diff --git a/meme_generator/memes/crawl/images/52.jpg b/meme_generator/memes/crawl/images/52.jpg new file mode 100644 index 0000000000000000000000000000000000000000..eb42a029d0b8bc25226a36b3073fa1bcde8ab6d4 Binary files /dev/null and b/meme_generator/memes/crawl/images/52.jpg differ diff --git a/meme_generator/memes/crawl/images/53.jpg b/meme_generator/memes/crawl/images/53.jpg new file mode 100644 index 0000000000000000000000000000000000000000..ce1174177b01d52a5e317a1b3acb6c115da1d84b Binary files /dev/null and b/meme_generator/memes/crawl/images/53.jpg differ diff --git a/meme_generator/memes/crawl/images/54.jpg b/meme_generator/memes/crawl/images/54.jpg new file mode 100644 index 0000000000000000000000000000000000000000..7e1f3efe5e9e35323d51317470ad99000565db7f Binary files /dev/null and b/meme_generator/memes/crawl/images/54.jpg differ diff --git a/meme_generator/memes/crawl/images/55.jpg b/meme_generator/memes/crawl/images/55.jpg new file mode 100644 index 0000000000000000000000000000000000000000..2e03b135686824796bc0c91a8fc06dad82c0ee11 Binary files /dev/null and b/meme_generator/memes/crawl/images/55.jpg differ diff --git a/meme_generator/memes/crawl/images/56.jpg b/meme_generator/memes/crawl/images/56.jpg new file mode 100644 index 0000000000000000000000000000000000000000..16aceb3486e5a6aafa32beefa64c84446de72781 Binary files /dev/null and b/meme_generator/memes/crawl/images/56.jpg differ diff --git a/meme_generator/memes/crawl/images/57.jpg b/meme_generator/memes/crawl/images/57.jpg new file mode 100644 index 0000000000000000000000000000000000000000..3b1300e29d4e21a4c23e267a23790bb74333459d Binary files /dev/null and b/meme_generator/memes/crawl/images/57.jpg differ diff --git a/meme_generator/memes/crawl/images/58.jpg b/meme_generator/memes/crawl/images/58.jpg new file mode 100644 index 0000000000000000000000000000000000000000..6b539cc1f5975a87af03eefeecc8fdf8358c1239 Binary files /dev/null and b/meme_generator/memes/crawl/images/58.jpg differ diff --git a/meme_generator/memes/crawl/images/59.jpg b/meme_generator/memes/crawl/images/59.jpg new file mode 100644 index 0000000000000000000000000000000000000000..46036cd1c03e096da0ad94ff30c56274ed82167a Binary files /dev/null and b/meme_generator/memes/crawl/images/59.jpg differ diff --git a/meme_generator/memes/crawl/images/60.jpg b/meme_generator/memes/crawl/images/60.jpg new file mode 100644 index 0000000000000000000000000000000000000000..65da447039b17569fdb0deaa12408fc5cea3512a Binary files /dev/null and b/meme_generator/memes/crawl/images/60.jpg differ diff --git a/meme_generator/memes/crawl/images/61.jpg b/meme_generator/memes/crawl/images/61.jpg new file mode 100644 index 0000000000000000000000000000000000000000..017259f42c9321447ad380d32cece5e0952140ec Binary files /dev/null and b/meme_generator/memes/crawl/images/61.jpg differ diff --git a/meme_generator/memes/crawl/images/62.jpg b/meme_generator/memes/crawl/images/62.jpg new file mode 100644 index 0000000000000000000000000000000000000000..4836f3567e9dc440dfee91456b9a2c2972f0db2a Binary files /dev/null and b/meme_generator/memes/crawl/images/62.jpg differ diff --git a/meme_generator/memes/crawl/images/63.jpg b/meme_generator/memes/crawl/images/63.jpg new file mode 100644 index 0000000000000000000000000000000000000000..e3bb402b4ec9aa0d44dbb81af3b9b6aeb647c9eb Binary files /dev/null and b/meme_generator/memes/crawl/images/63.jpg differ diff --git a/meme_generator/memes/crawl/images/64.jpg b/meme_generator/memes/crawl/images/64.jpg new file mode 100644 index 0000000000000000000000000000000000000000..50c5a73a470308e867e7e87ed387cc54488be7e4 Binary files /dev/null and b/meme_generator/memes/crawl/images/64.jpg differ diff --git a/meme_generator/memes/crawl/images/65.jpg b/meme_generator/memes/crawl/images/65.jpg new file mode 100644 index 0000000000000000000000000000000000000000..5e828c0f7c7f92696c9225eaf122eba3b2cdb309 Binary files /dev/null and b/meme_generator/memes/crawl/images/65.jpg differ diff --git a/meme_generator/memes/crawl/images/66.jpg b/meme_generator/memes/crawl/images/66.jpg new file mode 100644 index 0000000000000000000000000000000000000000..59097fb06271b0dded1343ea30e45b254d578b99 Binary files /dev/null and b/meme_generator/memes/crawl/images/66.jpg differ diff --git a/meme_generator/memes/crawl/images/67.jpg b/meme_generator/memes/crawl/images/67.jpg new file mode 100644 index 0000000000000000000000000000000000000000..940a11e231c8ad9a25fe1d591211cb850f3146aa Binary files /dev/null and b/meme_generator/memes/crawl/images/67.jpg differ diff --git a/meme_generator/memes/crawl/images/68.jpg b/meme_generator/memes/crawl/images/68.jpg new file mode 100644 index 0000000000000000000000000000000000000000..a36cc3bd95be13aa351049ed7130a5a45e66abd2 Binary files /dev/null and b/meme_generator/memes/crawl/images/68.jpg differ diff --git a/meme_generator/memes/crawl/images/69.jpg b/meme_generator/memes/crawl/images/69.jpg new file mode 100644 index 0000000000000000000000000000000000000000..a9439a73196ab3bb417c5814825ef5ee973a13e4 Binary files /dev/null and b/meme_generator/memes/crawl/images/69.jpg differ diff --git a/meme_generator/memes/crawl/images/70.jpg b/meme_generator/memes/crawl/images/70.jpg new file mode 100644 index 0000000000000000000000000000000000000000..c023d0f259a16915bdbb77f87dcfddd6c06d13aa Binary files /dev/null and b/meme_generator/memes/crawl/images/70.jpg differ diff --git a/meme_generator/memes/crawl/images/71.jpg b/meme_generator/memes/crawl/images/71.jpg new file mode 100644 index 0000000000000000000000000000000000000000..839c7ea9956e5cf8313639a7300f63b71025a041 Binary files /dev/null and b/meme_generator/memes/crawl/images/71.jpg differ diff --git a/meme_generator/memes/crawl/images/72.jpg b/meme_generator/memes/crawl/images/72.jpg new file mode 100644 index 0000000000000000000000000000000000000000..5ed38b9c1af88c044691e3eb507e01a564efc362 Binary files /dev/null and b/meme_generator/memes/crawl/images/72.jpg differ diff --git a/meme_generator/memes/crawl/images/73.jpg b/meme_generator/memes/crawl/images/73.jpg new file mode 100644 index 0000000000000000000000000000000000000000..cf0ecf27534bd9a318d99ade1a7a3a61a5670a85 Binary files /dev/null and b/meme_generator/memes/crawl/images/73.jpg differ diff --git a/meme_generator/memes/crawl/images/74.jpg b/meme_generator/memes/crawl/images/74.jpg new file mode 100644 index 0000000000000000000000000000000000000000..8c9ff7e1f907377e1858ea8d5019d72f69740aad Binary files /dev/null and b/meme_generator/memes/crawl/images/74.jpg differ diff --git a/meme_generator/memes/crawl/images/75.jpg b/meme_generator/memes/crawl/images/75.jpg new file mode 100644 index 0000000000000000000000000000000000000000..d44f44b903bfa3e55f08435e39ee48af54318df1 Binary files /dev/null and b/meme_generator/memes/crawl/images/75.jpg differ diff --git a/meme_generator/memes/crawl/images/76.jpg b/meme_generator/memes/crawl/images/76.jpg new file mode 100644 index 0000000000000000000000000000000000000000..011418bc31691bd7f5d9aaac361019194becc201 Binary files /dev/null and b/meme_generator/memes/crawl/images/76.jpg differ diff --git a/meme_generator/memes/crawl/images/77.jpg b/meme_generator/memes/crawl/images/77.jpg new file mode 100644 index 0000000000000000000000000000000000000000..f107850a0daa0acda4670c211134a475cd62aa01 Binary files /dev/null and b/meme_generator/memes/crawl/images/77.jpg differ diff --git a/meme_generator/memes/crawl/images/78.jpg b/meme_generator/memes/crawl/images/78.jpg new file mode 100644 index 0000000000000000000000000000000000000000..49acbb735be55a7e061528d3d3de4a33d11c6c1f Binary files /dev/null and b/meme_generator/memes/crawl/images/78.jpg differ diff --git a/meme_generator/memes/crawl/images/79.jpg b/meme_generator/memes/crawl/images/79.jpg new file mode 100644 index 0000000000000000000000000000000000000000..7a554d12db297c3957be229bf37e217535f696a0 Binary files /dev/null and b/meme_generator/memes/crawl/images/79.jpg differ diff --git a/meme_generator/memes/crawl/images/80.jpg b/meme_generator/memes/crawl/images/80.jpg new file mode 100644 index 0000000000000000000000000000000000000000..21d596fd608a2ea4ade57a6c456e7624392d0698 Binary files /dev/null and b/meme_generator/memes/crawl/images/80.jpg differ diff --git a/meme_generator/memes/crawl/images/81.jpg b/meme_generator/memes/crawl/images/81.jpg new file mode 100644 index 0000000000000000000000000000000000000000..19f37d3cda8260e3be9c6bdc0079925f491d45dc Binary files /dev/null and b/meme_generator/memes/crawl/images/81.jpg differ diff --git a/meme_generator/memes/crawl/images/82.jpg b/meme_generator/memes/crawl/images/82.jpg new file mode 100644 index 0000000000000000000000000000000000000000..1f2dda664bd4db3883fc92bc338f140b4cbc058a Binary files /dev/null and b/meme_generator/memes/crawl/images/82.jpg differ diff --git a/meme_generator/memes/crawl/images/83.jpg b/meme_generator/memes/crawl/images/83.jpg new file mode 100644 index 0000000000000000000000000000000000000000..5384ed5c95e376da8300e4896fcd6b00f665c536 Binary files /dev/null and b/meme_generator/memes/crawl/images/83.jpg differ diff --git a/meme_generator/memes/crawl/images/84.jpg b/meme_generator/memes/crawl/images/84.jpg new file mode 100644 index 0000000000000000000000000000000000000000..166e14476e91596003b4554c486ce3b19b291d69 Binary files /dev/null and b/meme_generator/memes/crawl/images/84.jpg differ diff --git a/meme_generator/memes/crawl/images/85.jpg b/meme_generator/memes/crawl/images/85.jpg new file mode 100644 index 0000000000000000000000000000000000000000..79b916b3a9eccf44655ac544c824341eca93b7c1 Binary files /dev/null and b/meme_generator/memes/crawl/images/85.jpg differ diff --git a/meme_generator/memes/crawl/images/86.jpg b/meme_generator/memes/crawl/images/86.jpg new file mode 100644 index 0000000000000000000000000000000000000000..aeab08e1cff975768b7cd608a48dfdbc06ca377f Binary files /dev/null and b/meme_generator/memes/crawl/images/86.jpg differ diff --git a/meme_generator/memes/crawl/images/87.jpg b/meme_generator/memes/crawl/images/87.jpg new file mode 100644 index 0000000000000000000000000000000000000000..6629ab070c50263320c6e44ff8dcaf136f3031dc Binary files /dev/null and b/meme_generator/memes/crawl/images/87.jpg differ diff --git a/meme_generator/memes/crawl/images/88.jpg b/meme_generator/memes/crawl/images/88.jpg new file mode 100644 index 0000000000000000000000000000000000000000..d2684d9ef9bee62bd0ad55e38a254ecd81b9f9d3 Binary files /dev/null and b/meme_generator/memes/crawl/images/88.jpg differ diff --git a/meme_generator/memes/crawl/images/89.jpg b/meme_generator/memes/crawl/images/89.jpg new file mode 100644 index 0000000000000000000000000000000000000000..c4de1873957d1e45351f692318fd29ba07cdbab4 Binary files /dev/null and b/meme_generator/memes/crawl/images/89.jpg differ diff --git a/meme_generator/memes/crawl/images/90.jpg b/meme_generator/memes/crawl/images/90.jpg new file mode 100644 index 0000000000000000000000000000000000000000..07ca0703cc038570e0b9a903b8097ec287e0f446 Binary files /dev/null and b/meme_generator/memes/crawl/images/90.jpg differ diff --git a/meme_generator/memes/crawl/images/91.jpg b/meme_generator/memes/crawl/images/91.jpg new file mode 100644 index 0000000000000000000000000000000000000000..5ed96bbc9c2dcaa3c67ab43b44425f7ae8ec041b Binary files /dev/null and b/meme_generator/memes/crawl/images/91.jpg differ diff --git a/meme_generator/memes/crawl/images/92.jpg b/meme_generator/memes/crawl/images/92.jpg new file mode 100644 index 0000000000000000000000000000000000000000..a7c94be83c4092df9600749ecf46901b6883f232 Binary files /dev/null and b/meme_generator/memes/crawl/images/92.jpg differ diff --git a/meme_generator/memes/cyan/__init__.py b/meme_generator/memes/cyan/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..62e511971bf32f7c928b17dc977ab3df4066544b --- /dev/null +++ b/meme_generator/memes/cyan/__init__.py @@ -0,0 +1,31 @@ +from typing import List + +from pil_utils import BuildImage + +from meme_generator import add_meme + + +def cyan(images: List[BuildImage], texts, args): + color = (78, 114, 184) + frame = images[0].convert("RGB").square().resize((500, 500)).color_mask(color) + frame.draw_text( + (400, 40, 480, 280), + "群\n青", + max_fontsize=80, + weight="bold", + fill="white", + stroke_ratio=0.04, + stroke_fill=color, + ).draw_text( + (200, 270, 480, 350), + "YOASOBI", + halign="right", + max_fontsize=40, + fill="white", + stroke_ratio=0.06, + stroke_fill=color, + ) + return frame.save_jpg() + + +add_meme("cyan", cyan, min_images=1, max_images=1, keywords=["群青"]) diff --git a/meme_generator/memes/decent_kiss/__init__.py b/meme_generator/memes/decent_kiss/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..d08423bcd0a1197f3439882b022dfe2ba61eebf1 --- /dev/null +++ b/meme_generator/memes/decent_kiss/__init__.py @@ -0,0 +1,18 @@ +from pathlib import Path +from typing import List + +from pil_utils import BuildImage + +from meme_generator import add_meme + +img_dir = Path(__file__).parent / "images" + + +def decent_kiss(images: List[BuildImage], texts, args): + img = images[0].convert("RGBA").resize((589, 340), keep_ratio=True) + frame = BuildImage.open(img_dir / "0.png") + frame.paste(img, (0, 91), below=True) + return frame.save_jpg() + + +add_meme("decent_kiss", decent_kiss, min_images=1, max_images=1, keywords=["像样的亲亲"]) diff --git a/meme_generator/memes/decent_kiss/images/0.png b/meme_generator/memes/decent_kiss/images/0.png new file mode 100644 index 0000000000000000000000000000000000000000..8f1e365c58c2a3708faf6a5a4878d679951a20d1 Binary files /dev/null and b/meme_generator/memes/decent_kiss/images/0.png differ diff --git a/meme_generator/memes/dianzhongdian/__init__.py b/meme_generator/memes/dianzhongdian/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..22afc158c4335191c3aecdc15bfbe96df6a0e639 --- /dev/null +++ b/meme_generator/memes/dianzhongdian/__init__.py @@ -0,0 +1,65 @@ +from typing import List + +from pil_utils import BuildImage + +from meme_generator import add_meme +from meme_generator.exception import TextOverLength +from meme_generator.utils import run_sync, translate + + +@run_sync +def _dianzhongdian(img: BuildImage, text: str, trans: str): + img = img.convert("L").resize_width(500) + text_img1 = BuildImage.new("RGBA", (500, 60)) + text_img2 = BuildImage.new("RGBA", (500, 35)) + + try: + text_img1.draw_text( + (20, 0, text_img1.width - 20, text_img1.height), + text, + max_fontsize=50, + min_fontsize=25, + fill="white", + ) + except ValueError: + raise TextOverLength(text) + + try: + text_img2.draw_text( + (20, 0, text_img2.width - 20, text_img2.height), + trans, + max_fontsize=25, + min_fontsize=10, + fill="white", + ) + except ValueError: + raise TextOverLength(text) + + frame = BuildImage.new("RGBA", (500, img.height + 100), "black") + frame.paste(img, alpha=True) + frame.paste(text_img1, (0, img.height), alpha=True) + frame.paste(text_img2, (0, img.height + 60), alpha=True) + return frame.save_jpg() + + +async def dianzhongdian(images: List[BuildImage], texts: List[str], args): + if len(texts) == 1: + text = texts[0] + trans = await translate(text, lang_to="jp") + else: + text = texts[0] + trans = texts[1] + + return await _dianzhongdian(images[0], text, trans) + + +add_meme( + "dianzhongdian", + dianzhongdian, + min_images=1, + max_images=1, + min_texts=1, + max_texts=2, + default_texts=["救命啊"], + keywords=["入典", "典中典", "黑白草图"], +) diff --git a/meme_generator/memes/dinosaur/__init__.py b/meme_generator/memes/dinosaur/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..6c870345c1814114e837e237ace51e05d6d7f6d2 --- /dev/null +++ b/meme_generator/memes/dinosaur/__init__.py @@ -0,0 +1,22 @@ +from pathlib import Path +from typing import List + +from pil_utils import BuildImage + +from meme_generator import add_meme +from meme_generator.utils import make_jpg_or_gif + +img_dir = Path(__file__).parent / "images" + + +def dinosaur(images: List[BuildImage], texts, args): + frame = BuildImage.open(img_dir / "0.png") + + def make(img: BuildImage) -> BuildImage: + img = img.convert("RGBA").resize((680, 578), keep_ratio=True) + return frame.copy().paste(img, (294, 369), below=True) + + return make_jpg_or_gif(images[0], make) + + +add_meme("dinosaur", dinosaur, min_images=1, max_images=1, keywords=["恐龙", "小恐龙"]) diff --git a/meme_generator/memes/dinosaur/images/0.png b/meme_generator/memes/dinosaur/images/0.png new file mode 100644 index 0000000000000000000000000000000000000000..efa81ec682554fc9f5cc79c42961ea2f627c166b --- /dev/null +++ b/meme_generator/memes/dinosaur/images/0.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:63d7e73536de927cd8be538e9aef94121740b857e612778421497f096e4c9f21 +size 1307472 diff --git a/meme_generator/memes/distracted/__init__.py b/meme_generator/memes/distracted/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..eb52ad5156ca1277639a3bd27c14960662605635 --- /dev/null +++ b/meme_generator/memes/distracted/__init__.py @@ -0,0 +1,23 @@ +from pathlib import Path +from typing import List + +from pil_utils import BuildImage + +from meme_generator import add_meme +from meme_generator.utils import make_jpg_or_gif + +img_dir = Path(__file__).parent / "images" + + +def distracted(images: List[BuildImage], texts, args): + frame = BuildImage.open(img_dir / "1.png") + label = BuildImage.open(img_dir / "0.png") + + def make(img: BuildImage) -> BuildImage: + img = img.convert("RGBA").square().resize((500, 500)) + return frame.copy().paste(img, below=True).paste(label, (140, 320), alpha=True) + + return make_jpg_or_gif(images[0], make) + + +add_meme("distracted", distracted, min_images=1, max_images=1, keywords=["注意力涣散"]) diff --git a/meme_generator/memes/distracted/images/0.png b/meme_generator/memes/distracted/images/0.png new file mode 100644 index 0000000000000000000000000000000000000000..ec7a27d2c8242687b208494b1f250793dac037a9 Binary files /dev/null and b/meme_generator/memes/distracted/images/0.png differ diff --git a/meme_generator/memes/distracted/images/1.png b/meme_generator/memes/distracted/images/1.png new file mode 100644 index 0000000000000000000000000000000000000000..a9a725d12eca553a8f2a8bdb992bf30f2fa3c47b Binary files /dev/null and b/meme_generator/memes/distracted/images/1.png differ diff --git a/meme_generator/memes/distracted/images/2.png b/meme_generator/memes/distracted/images/2.png new file mode 100644 index 0000000000000000000000000000000000000000..6205947b0c768e92fce1078f791bc6e81726ebf1 Binary files /dev/null and b/meme_generator/memes/distracted/images/2.png differ diff --git a/meme_generator/memes/distracted/images/3.png b/meme_generator/memes/distracted/images/3.png new file mode 100644 index 0000000000000000000000000000000000000000..54817c8a13cb5360135d8f4351f65db3885fc645 Binary files /dev/null and b/meme_generator/memes/distracted/images/3.png differ diff --git a/meme_generator/memes/divorce/__init__.py b/meme_generator/memes/divorce/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..c2150680c935cdb262d5f7159dd8bd7638e7759f --- /dev/null +++ b/meme_generator/memes/divorce/__init__.py @@ -0,0 +1,18 @@ +from pathlib import Path +from typing import List + +from pil_utils import BuildImage + +from meme_generator import add_meme + +img_dir = Path(__file__).parent / "images" + + +def divorce(images: List[BuildImage], texts, args): + frame = BuildImage.open(img_dir / "0.png") + img = images[0].convert("RGBA").resize(frame.size, keep_ratio=True) + frame.paste(img, below=True) + return frame.save_jpg() + + +add_meme("divorce", divorce, min_images=1, max_images=1, keywords=["离婚协议", "离婚申请"]) diff --git a/meme_generator/memes/divorce/images/0.png b/meme_generator/memes/divorce/images/0.png new file mode 100644 index 0000000000000000000000000000000000000000..2cedd0aa700107171d15d8e851688e0bb6f9c935 Binary files /dev/null and b/meme_generator/memes/divorce/images/0.png differ diff --git a/meme_generator/memes/dog_of_vtb/__init__.py b/meme_generator/memes/dog_of_vtb/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..a4a98cff0f4c1091f671956131599590751bed12 --- /dev/null +++ b/meme_generator/memes/dog_of_vtb/__init__.py @@ -0,0 +1,29 @@ +from pathlib import Path +from typing import List + +from pil_utils import BuildImage + +from meme_generator import add_meme +from meme_generator.utils import make_jpg_or_gif + +img_dir = Path(__file__).parent / "images" + + +def dog_of_vtb(images: List[BuildImage], texts, args): + frame = BuildImage.open(img_dir / "0.png") + + def make(img: BuildImage) -> BuildImage: + points = ((0, 0), (579, 0), (584, 430), (5, 440)) + img = img.convert("RGBA").resize((600, 450), keep_ratio=True) + return frame.copy().paste(img.perspective(points), (97, 32), below=True) + + return make_jpg_or_gif(images[0], make) + + +add_meme( + "dog_of_vtb", + dog_of_vtb, + min_images=1, + max_images=1, + keywords=["管人痴"], +) diff --git a/meme_generator/memes/dog_of_vtb/images/0.png b/meme_generator/memes/dog_of_vtb/images/0.png new file mode 100644 index 0000000000000000000000000000000000000000..405236fce2c34188c8fe5b8a38d9490da3432acc --- /dev/null +++ b/meme_generator/memes/dog_of_vtb/images/0.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9cdf6a338719e2cd7429e9d6f46ab5afa44e474a72b2df2c8c2717b4c465428c +size 1445665 diff --git a/meme_generator/memes/dont_go_near/__init__.py b/meme_generator/memes/dont_go_near/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..8675f01518412a8c0dd98887ed15586000308f03 --- /dev/null +++ b/meme_generator/memes/dont_go_near/__init__.py @@ -0,0 +1,22 @@ +from pathlib import Path +from typing import List + +from pil_utils import BuildImage + +from meme_generator import add_meme +from meme_generator.utils import make_jpg_or_gif + +img_dir = Path(__file__).parent / "images" + + +def dont_go_near(images: List[BuildImage], texts, args): + frame = BuildImage.open(img_dir / "0.png") + + def make(img: BuildImage) -> BuildImage: + img = img.convert("RGBA").resize((170, 170), keep_ratio=True) + return frame.copy().paste(img, (23, 231), alpha=True) + + return make_jpg_or_gif(images[0], make) + + +add_meme("dont_go_near", dont_go_near, min_images=1, max_images=1, keywords=["不要靠近"]) diff --git a/meme_generator/memes/dont_go_near/images/0.png b/meme_generator/memes/dont_go_near/images/0.png new file mode 100644 index 0000000000000000000000000000000000000000..0e50de4722e47e900a07811604638945aab42547 Binary files /dev/null and b/meme_generator/memes/dont_go_near/images/0.png differ diff --git a/meme_generator/memes/dont_touch/__init__.py b/meme_generator/memes/dont_touch/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..22743662ed38de955166deb43852194e470e78e6 --- /dev/null +++ b/meme_generator/memes/dont_touch/__init__.py @@ -0,0 +1,57 @@ +import random +from pathlib import Path +from typing import List, Tuple + +from PIL.Image import Image as IMG +from PIL.Image import Palette +from pil_utils import BuildImage + +from meme_generator import add_meme +from meme_generator.utils import make_jpg_or_gif + +img_dir = Path(__file__).parent / "images" + + +def get_dominant_colors(img: IMG) -> List[Tuple[int, int, int]]: + img = img.convert("P", palette=Palette.ADAPTIVE, colors=20) + palette = img.getpalette() + assert palette + color_indexs = sorted(img.getcolors(), reverse=True) + colors = [tuple(palette[i * 3 : i * 3 + 3]) for _, i in color_indexs] + colors = list( + filter(lambda c: c[0] * 0.299 + c[1] * 0.578 + c[2] * 0.114 < 200, colors) + ) + return colors + + +def dont_touch(images: List[BuildImage], texts, args): + frame = BuildImage.open(img_dir / "0.png") + mask = BuildImage.open(img_dir / "mask.png").convert("L") + + def paste_random_blocks(img: BuildImage, colors: List[Tuple[int, int, int]]): + x1, y1, x2, y2 = 200, 300, 400, 650 + block_locs = [] + for _ in range(150): + x = random.randint(x1, x2) + y = random.randint(y1, y2) + if mask.image.getpixel((x, y)) == 0: + continue + if any(abs(x - x_) < 13 and abs(y - y_) < 13 for x_, y_ in block_locs): + continue + block_locs.append((x, y)) + color = random.choice(colors) + block = BuildImage.new("RGBA", (10, 10), color) + block = block.rotate(45, expand=True) + img.paste(block, (x, y), alpha=True) + + def make(img: BuildImage) -> BuildImage: + img_frame = frame.copy() + colors = get_dominant_colors(img.image) + paste_random_blocks(img_frame, colors) + img = img.convert("RGBA").resize((250, 250), keep_ratio=True, inside=True) + return img_frame.paste(img, (25, 460), alpha=True) + + return make_jpg_or_gif(images[0], make) + + +add_meme("dont_touch", dont_touch, min_images=1, max_images=1, keywords=["别碰"]) diff --git a/meme_generator/memes/dont_touch/images/0.png b/meme_generator/memes/dont_touch/images/0.png new file mode 100644 index 0000000000000000000000000000000000000000..a12a2447d61cd1bd06e2c9009c0caadf49d5acfd Binary files /dev/null and b/meme_generator/memes/dont_touch/images/0.png differ diff --git a/meme_generator/memes/dont_touch/images/mask.png b/meme_generator/memes/dont_touch/images/mask.png new file mode 100644 index 0000000000000000000000000000000000000000..fabceb7a4c171dd5ec09f4f98e199ee9bc1c59bd Binary files /dev/null and b/meme_generator/memes/dont_touch/images/mask.png differ diff --git a/meme_generator/memes/douyin/__init__.py b/meme_generator/memes/douyin/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..abed8dfa3fdc600f09971ee479f8facad0c164cd --- /dev/null +++ b/meme_generator/memes/douyin/__init__.py @@ -0,0 +1,78 @@ +import math +import random +from typing import List + +from PIL.Image import Image as IMG +from pil_utils import BuildImage, Text2Image + +from meme_generator import add_meme +from meme_generator.utils import save_gif + + +def douyin(images, texts: List[str], args): + text = texts[0] + text = " ".join(text.splitlines()) + fontsize = 200 + offset = round(fontsize * 0.05) + px = 70 + py = 30 + bg_color = "#1C0B1B" + frame = Text2Image.from_text( + text, fontsize, fill="#FF0050", stroke_fill="#FF0050", stroke_width=5 + ).to_image(bg_color=bg_color, padding=(px + offset * 2, py + offset * 2, px, py)) + Text2Image.from_text( + text, fontsize, fill="#00F5EB", stroke_fill="#00F5EB", stroke_width=5 + ).draw_on_image(frame, (px, py)) + Text2Image.from_text( + text, fontsize, fill="white", stroke_fill="white", stroke_width=5 + ).draw_on_image(frame, (px + offset, py + offset)) + frame = BuildImage(frame) + + width = frame.width - px + height = frame.height - py + frame_num = 10 + devide_num = 6 + seed = 20 * 0.05 + frames: List[IMG] = [] + for _ in range(frame_num): + new_frame = frame.copy() + h_seeds = [ + math.fabs(math.sin(random.random() * devide_num)) for _ in range(devide_num) + ] + h_seed_sum = sum(h_seeds) + h_seeds = [s / h_seed_sum for s in h_seeds] + direction = 1 + last_yn = 0 + last_h = 0 + for i in range(devide_num): + yn = last_yn + last_h + h = max(round(height * h_seeds[i]), 2) + last_yn = yn + last_h = h + direction = -direction + piece = new_frame.copy().crop((px, yn, px + width, yn + h)) + new_frame.paste(piece, (px + round(i * direction * seed), yn)) + # 透视变换 + move_x = 64 + points = ( + (move_x, 0), + (new_frame.width + move_x, 0), + (new_frame.width, new_frame.height), + (0, new_frame.height), + ) + new_frame = new_frame.perspective(points) + bg = BuildImage.new("RGBA", new_frame.size, bg_color) + bg.paste(new_frame, alpha=True) + frames.append(bg.image) + + return save_gif(frames, 0.2) + + +add_meme( + "douyin", + douyin, + min_texts=1, + max_texts=1, + default_texts=["douyin"], + keywords=["douyin"], +) diff --git a/meme_generator/memes/eat/__init__.py b/meme_generator/memes/eat/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..066a7278ac3c849938eb54d0353bbd0217c1e0d5 --- /dev/null +++ b/meme_generator/memes/eat/__init__.py @@ -0,0 +1,22 @@ +from pathlib import Path +from typing import List + +from pil_utils import BuildImage + +from meme_generator import add_meme +from meme_generator.utils import save_gif + +img_dir = Path(__file__).parent / "images" + + +def eat(images: List[BuildImage], texts, args): + img = images[0].convert("RGBA").square().resize((34, 34)) + frames = [] + for i in range(3): + frame = BuildImage.open(img_dir / f"{i}.png") + frame.paste(img, (2, 38), below=True) + frames.append(frame.image) + return save_gif(frames, 0.05) + + +add_meme("eat", eat, min_images=1, max_images=1, keywords=["吃"]) diff --git a/meme_generator/memes/eat/images/0.png b/meme_generator/memes/eat/images/0.png new file mode 100644 index 0000000000000000000000000000000000000000..b9d5c931e744a5fa7cb1634c477a69e655776ae9 Binary files /dev/null and b/meme_generator/memes/eat/images/0.png differ diff --git a/meme_generator/memes/eat/images/1.png b/meme_generator/memes/eat/images/1.png new file mode 100644 index 0000000000000000000000000000000000000000..55ba596628a217ba133a6f04c2e82cdee0bbe080 Binary files /dev/null and b/meme_generator/memes/eat/images/1.png differ diff --git a/meme_generator/memes/eat/images/2.png b/meme_generator/memes/eat/images/2.png new file mode 100644 index 0000000000000000000000000000000000000000..1b67bb27c83ad0b79c56a4914859acfac6fd15d3 Binary files /dev/null and b/meme_generator/memes/eat/images/2.png differ diff --git a/meme_generator/memes/fanatic/__init__.py b/meme_generator/memes/fanatic/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..66cbfcfffa3901e0992421d7012fdd1a4378b6b4 --- /dev/null +++ b/meme_generator/memes/fanatic/__init__.py @@ -0,0 +1,36 @@ +from pathlib import Path +from typing import List + +from pil_utils import BuildImage + +from meme_generator import add_meme +from meme_generator.exception import TextOverLength + +img_dir = Path(__file__).parent / "images" + + +def fanatic(images, texts: List[str], args): + text = texts[0] + frame = BuildImage.open(img_dir / "0.jpg") + try: + frame.draw_text( + (145, 40, 343, 160), + text, + allow_wrap=True, + lines_align="center", + max_fontsize=70, + min_fontsize=30, + ) + except ValueError: + raise TextOverLength(text) + return frame.save_jpg() + + +add_meme( + "fanatic", + fanatic, + min_texts=1, + max_texts=1, + default_texts=["洛天依"], + keywords=["狂爱", "狂粉"], +) diff --git a/meme_generator/memes/fanatic/images/0.jpg b/meme_generator/memes/fanatic/images/0.jpg new file mode 100644 index 0000000000000000000000000000000000000000..bc8b439374f3801929c1afc90acfd2f35be9259a Binary files /dev/null and b/meme_generator/memes/fanatic/images/0.jpg differ diff --git a/meme_generator/memes/fencing/__init__.py b/meme_generator/memes/fencing/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..dfe29c480f9d348db6719c6f545244f19b0ba7c4 --- /dev/null +++ b/meme_generator/memes/fencing/__init__.py @@ -0,0 +1,37 @@ +from pathlib import Path +from typing import List + +from PIL.Image import Image as IMG +from pil_utils import BuildImage + +from meme_generator import add_meme +from meme_generator.utils import save_gif + +img_dir = Path(__file__).parent / "images" + + +def fencing(images: List[BuildImage], texts, args): + self_head = images[0].convert("RGBA").circle().resize((27, 27)) + user_head = images[1].convert("RGBA").circle().resize((27, 27)) + # fmt: off + user_locs = [ + (57, 4), (55, 5), (58, 7), (57, 5), (53, 8), (54, 9), + (64, 5), (66, 8), (70, 9), (73, 8), (81, 10), (77, 10), + (72, 4), (79, 8), (50, 8), (60, 7), (67, 6), (60, 6), (50, 9) + ] + self_locs = [ + (10, 6), (3, 6), (32, 7), (22, 7), (13, 4), (21, 6), + (30, 6), (22, 2), (22, 3), (26, 8), (23, 8), (27, 10), + (30, 9), (17, 6), (12, 8), (11, 7), (8, 6), (-2, 10), (4, 9) + ] + # fmt: on + frames: List[IMG] = [] + for i in range(19): + frame = BuildImage.open(img_dir / f"{i}.png") + frame.paste(user_head, user_locs[i], alpha=True) + frame.paste(self_head, self_locs[i], alpha=True) + frames.append(frame.image) + return save_gif(frames, 0.05) + + +add_meme("fencing", fencing, min_images=2, max_images=2, keywords=["击剑", "🤺"]) diff --git a/meme_generator/memes/fencing/images/0.png b/meme_generator/memes/fencing/images/0.png new file mode 100644 index 0000000000000000000000000000000000000000..ed47882454855698cdb7b16d7c33f9bfc23fa78e Binary files /dev/null and b/meme_generator/memes/fencing/images/0.png differ diff --git a/meme_generator/memes/fencing/images/1.png b/meme_generator/memes/fencing/images/1.png new file mode 100644 index 0000000000000000000000000000000000000000..184b7f94c4592633f7ea152546b8d5b86c33397d Binary files /dev/null and b/meme_generator/memes/fencing/images/1.png differ diff --git a/meme_generator/memes/fencing/images/10.png b/meme_generator/memes/fencing/images/10.png new file mode 100644 index 0000000000000000000000000000000000000000..b9e4e9650395f9c53f6eca25a749d1f46c25c545 Binary files /dev/null and b/meme_generator/memes/fencing/images/10.png differ diff --git a/meme_generator/memes/fencing/images/11.png b/meme_generator/memes/fencing/images/11.png new file mode 100644 index 0000000000000000000000000000000000000000..1732979790546b62677a34ce661ae2f0db44e590 Binary files /dev/null and b/meme_generator/memes/fencing/images/11.png differ diff --git a/meme_generator/memes/fencing/images/12.png b/meme_generator/memes/fencing/images/12.png new file mode 100644 index 0000000000000000000000000000000000000000..82584828a134bc843e89de91274b5c6a4e2d4d53 Binary files /dev/null and b/meme_generator/memes/fencing/images/12.png differ diff --git a/meme_generator/memes/fencing/images/13.png b/meme_generator/memes/fencing/images/13.png new file mode 100644 index 0000000000000000000000000000000000000000..914d02d04582be421a39ac824384e0e206ffe8b9 Binary files /dev/null and b/meme_generator/memes/fencing/images/13.png differ diff --git a/meme_generator/memes/fencing/images/14.png b/meme_generator/memes/fencing/images/14.png new file mode 100644 index 0000000000000000000000000000000000000000..c5b8cf496bcfa2084f424cac1892a3fc3a5d5185 Binary files /dev/null and b/meme_generator/memes/fencing/images/14.png differ diff --git a/meme_generator/memes/fencing/images/15.png b/meme_generator/memes/fencing/images/15.png new file mode 100644 index 0000000000000000000000000000000000000000..eb7f620f1c4f7cdc2d75c559c735f2f362c6ebe3 Binary files /dev/null and b/meme_generator/memes/fencing/images/15.png differ diff --git a/meme_generator/memes/fencing/images/16.png b/meme_generator/memes/fencing/images/16.png new file mode 100644 index 0000000000000000000000000000000000000000..41588ac731daf81c3c3f1e9130d01adde60e7a13 Binary files /dev/null and b/meme_generator/memes/fencing/images/16.png differ diff --git a/meme_generator/memes/fencing/images/17.png b/meme_generator/memes/fencing/images/17.png new file mode 100644 index 0000000000000000000000000000000000000000..4f9480ceb2e0531c32cf845923cdfccd501d8d20 Binary files /dev/null and b/meme_generator/memes/fencing/images/17.png differ diff --git a/meme_generator/memes/fencing/images/18.png b/meme_generator/memes/fencing/images/18.png new file mode 100644 index 0000000000000000000000000000000000000000..a803eaf15f309b79fe8edf70f1e0fe2e0507a199 Binary files /dev/null and b/meme_generator/memes/fencing/images/18.png differ diff --git a/meme_generator/memes/fencing/images/2.png b/meme_generator/memes/fencing/images/2.png new file mode 100644 index 0000000000000000000000000000000000000000..7f98de093823ba414b6a6e1e9b5e702d4e561717 Binary files /dev/null and b/meme_generator/memes/fencing/images/2.png differ diff --git a/meme_generator/memes/fencing/images/3.png b/meme_generator/memes/fencing/images/3.png new file mode 100644 index 0000000000000000000000000000000000000000..fe23ce14400b3451dfc0054383a61e3ed2a0d3f1 Binary files /dev/null and b/meme_generator/memes/fencing/images/3.png differ diff --git a/meme_generator/memes/fencing/images/4.png b/meme_generator/memes/fencing/images/4.png new file mode 100644 index 0000000000000000000000000000000000000000..306686992b95ff0c10b16ba8cebd604c37fdb6f6 Binary files /dev/null and b/meme_generator/memes/fencing/images/4.png differ diff --git a/meme_generator/memes/fencing/images/5.png b/meme_generator/memes/fencing/images/5.png new file mode 100644 index 0000000000000000000000000000000000000000..b3d4eb998eb378ca01f430e026111ee79d217c39 Binary files /dev/null and b/meme_generator/memes/fencing/images/5.png differ diff --git a/meme_generator/memes/fencing/images/6.png b/meme_generator/memes/fencing/images/6.png new file mode 100644 index 0000000000000000000000000000000000000000..0d3d339f63bed82046d944c2896dab00424b95b1 Binary files /dev/null and b/meme_generator/memes/fencing/images/6.png differ diff --git a/meme_generator/memes/fencing/images/7.png b/meme_generator/memes/fencing/images/7.png new file mode 100644 index 0000000000000000000000000000000000000000..c21afd7f6db37a5d3e3ccdc40081f3125e892d6f Binary files /dev/null and b/meme_generator/memes/fencing/images/7.png differ diff --git a/meme_generator/memes/fencing/images/8.png b/meme_generator/memes/fencing/images/8.png new file mode 100644 index 0000000000000000000000000000000000000000..6629c7bb50e0edbb1c0387b454315ed059fba1e9 Binary files /dev/null and b/meme_generator/memes/fencing/images/8.png differ diff --git a/meme_generator/memes/fencing/images/9.png b/meme_generator/memes/fencing/images/9.png new file mode 100644 index 0000000000000000000000000000000000000000..5e085e3d1b8e017486b5b2c1fe69b3fd00e9158f Binary files /dev/null and b/meme_generator/memes/fencing/images/9.png differ diff --git a/meme_generator/memes/fill_head/__init__.py b/meme_generator/memes/fill_head/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..40320e1a407ac28e01f598f3e575e100aa42057c --- /dev/null +++ b/meme_generator/memes/fill_head/__init__.py @@ -0,0 +1,40 @@ +from pathlib import Path +from typing import List + +from pil_utils import BuildImage + +from meme_generator import MemeArgsModel, add_meme +from meme_generator.exception import TextOverLength +from meme_generator.utils import make_jpg_or_gif + +img_dir = Path(__file__).parent / "images" + + +def fill_head(images: List[BuildImage], texts: List[str], args: MemeArgsModel): + name = texts[0] if texts else (args.user_infos[0].name if args.user_infos else "它") + text = f"满脑子都是{name}" + frame = BuildImage.open(img_dir / "0.jpg") + try: + frame.draw_text( + (20, 458, frame.width - 20, 550), text, max_fontsize=65, min_fontsize=30 + ) + except: + raise TextOverLength(name) + + def make(img: BuildImage) -> BuildImage: + img = img.convert("RGBA").resize((210, 170), keep_ratio=True, inside=True) + return frame.copy().paste(img, (150, 2), alpha=True) + + return make_jpg_or_gif(images[0], make) + + +add_meme( + "fill_head", + fill_head, + min_images=1, + max_images=1, + min_texts=0, + max_texts=1, + keywords=["满脑子"], + patterns=[r"满脑子都是(\S+)"], +) diff --git a/meme_generator/memes/fill_head/images/0.jpg b/meme_generator/memes/fill_head/images/0.jpg new file mode 100644 index 0000000000000000000000000000000000000000..bf199cacbd3dee5ef550857e939d55421d2fcc7d Binary files /dev/null and b/meme_generator/memes/fill_head/images/0.jpg differ diff --git a/meme_generator/memes/find_chips/__init__.py b/meme_generator/memes/find_chips/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..837504f347601f3ea0cf0eb01dc9d168ea0974e7 --- /dev/null +++ b/meme_generator/memes/find_chips/__init__.py @@ -0,0 +1,42 @@ +from pathlib import Path +from typing import List, Tuple + +from pil_utils import BuildImage + +from meme_generator import add_meme +from meme_generator.exception import TextOverLength + +img_dir = Path(__file__).parent / "images" + + +def find_chips(images, texts: List[str], args): + frame = BuildImage.open(img_dir / "0.jpg") + + def draw(pos: Tuple[float, float, float, float], text: str): + try: + frame.draw_text( + pos, text, max_fontsize=30, min_fontsize=12, allow_wrap=True + ) + except ValueError: + raise TextOverLength(text) + + draw((405, 54, 530, 130), texts[0]) + draw((570, 62, 667, 160), texts[1]) + draw((65, 400, 325, 463), texts[2]) + draw((430, 400, 630, 470), texts[3]) + return frame.save_jpg() + + +add_meme( + "find_chips", + find_chips, + min_texts=4, + max_texts=4, + default_texts=[ + "我们要飞向何方", + "我打算待会去码头整点薯条", + "我说的是归根结底,活着是为了什么", + "为了待会去码头整点薯条", + ], + keywords=["整点薯条"], +) diff --git a/meme_generator/memes/find_chips/images/0.jpg b/meme_generator/memes/find_chips/images/0.jpg new file mode 100644 index 0000000000000000000000000000000000000000..870e58af6f1a9c6150c63a67241f664fe46840a4 Binary files /dev/null and b/meme_generator/memes/find_chips/images/0.jpg differ diff --git a/meme_generator/memes/flash_blind/__init__.py b/meme_generator/memes/flash_blind/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..f639e288b10ec7949409809f34e48a7a770a440f --- /dev/null +++ b/meme_generator/memes/flash_blind/__init__.py @@ -0,0 +1,63 @@ +from typing import List + +from PIL import ImageOps +from PIL.Image import Image as IMG +from pil_utils import BuildImage + +from meme_generator import add_meme +from meme_generator.exception import TextOverLength +from meme_generator.utils import save_gif + + +def flash_blind(images: List[BuildImage], texts: List[str], args): + img = images[0].convert("RGB").resize_width(500) + frames: List[IMG] = [] + frames.append(img.image) + frames.append(ImageOps.invert(img.image)) + img_enlarge = img.resize_canvas((450, img.height * 450 // 500)).resize( + (500, img.height) + ) + frames.append(img_enlarge.image) + frames.append(ImageOps.invert(img.image)) + + if texts and texts[0]: + text = texts[0] + text_h = 65 + + try: + text_frame_black = BuildImage.new("RGB", (500, text_h), "black") + text_frame_white = BuildImage.new("RGB", (500, text_h), "white") + text_frame_black.draw_text( + (10, 0, 490, text_h), + text, + max_fontsize=50, + min_fontsize=20, + fill="white", + ) + text_frame_white.draw_text( + (10, 0, 490, text_h), + text, + max_fontsize=50, + min_fontsize=20, + fill="black", + ) + except ValueError: + raise TextOverLength(text) + frames[0].paste(text_frame_black.image, (0, img.height - text_h)) + frames[1].paste(text_frame_white.image, (0, img.height - text_h)) + frames[2].paste(text_frame_black.image, (0, img.height - text_h)) + frames[3].paste(text_frame_white.image, (0, img.height - text_h)) + + return save_gif(frames, 0.03) + + +add_meme( + "flash_blind", + flash_blind, + min_images=1, + max_images=1, + min_texts=0, + max_texts=1, + default_texts=["闪瞎你们的狗眼"], + keywords=["闪瞎"], +) diff --git a/meme_generator/memes/follow/__init__.py b/meme_generator/memes/follow/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e71a0b030d5c3eb89a46e19a7a7b2c784d084dc0 --- /dev/null +++ b/meme_generator/memes/follow/__init__.py @@ -0,0 +1,44 @@ +from typing import List + +from pil_utils import BuildImage, Text2Image + +from meme_generator import MemeArgsModel, add_meme +from meme_generator.exception import TextOverLength + + +def follow(images: List[BuildImage], texts: List[str], args: MemeArgsModel): + img = images[0].circle().resize((200, 200)) + + if texts: + name = texts[0] + else: + if args.user_infos: + user_info = args.user_infos[0] + name = user_info.name + if not name: + name = "女同" if user_info.gender == "female" else "男同" + else: + name = "男同" + + name_img = Text2Image.from_text(name, 60).to_image() + follow_img = Text2Image.from_text("关注了你", 60, fill="grey").to_image() + text_width = max(name_img.width, follow_img.width) + if text_width >= 1000: + raise TextOverLength(name) + + frame = BuildImage.new("RGBA", (300 + text_width + 50, 300), (255, 255, 255, 0)) + frame.paste(img, (50, 50), alpha=True) + frame.paste(name_img, (300, 135 - name_img.height), alpha=True) + frame.paste(follow_img, (300, 145), alpha=True) + return frame.save_jpg() + + +add_meme( + "follow", + follow, + min_images=1, + max_images=1, + min_texts=0, + max_texts=1, + keywords=["关注"], +) diff --git a/meme_generator/memes/funny_mirror/__init__.py b/meme_generator/memes/funny_mirror/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..5f9f425588845831be7dd284e2f3a1beb4af4f69 --- /dev/null +++ b/meme_generator/memes/funny_mirror/__init__.py @@ -0,0 +1,23 @@ +from typing import List + +from PIL.Image import Image as IMG +from pil_utils import BuildImage + +from meme_generator import add_meme +from meme_generator.utils import save_gif + + +def funny_mirror(images: List[BuildImage], texts, args): + img = images[0].convert("RGBA").square().resize((500, 500)) + frames: List[IMG] = [img.image] + coeffs = [0.01, 0.03, 0.05, 0.08, 0.12, 0.17, 0.23, 0.3, 0.4, 0.6] + borders = [25, 52, 67, 83, 97, 108, 118, 128, 138, 148] + for i in range(10): + new_size = 500 - borders[i] * 2 + new_img = img.distort((coeffs[i], 0, 0, 0)).resize_canvas((new_size, new_size)) + frames.append(new_img.resize((500, 500)).image) + frames.extend(frames[::-1]) + return save_gif(frames, 0.05) + + +add_meme("funny_mirror", funny_mirror, min_images=1, max_images=1, keywords=["哈哈镜"]) diff --git a/meme_generator/memes/garbage/__init__.py b/meme_generator/memes/garbage/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..c0c75d8c4055c555419af21fece78e12e3da94c6 --- /dev/null +++ b/meme_generator/memes/garbage/__init__.py @@ -0,0 +1,30 @@ +from pathlib import Path +from typing import List + +from PIL.Image import Image as IMG +from pil_utils import BuildImage + +from meme_generator import add_meme +from meme_generator.utils import save_gif + +img_dir = Path(__file__).parent / "images" + + +def garbage(images: List[BuildImage], texts, args): + img = images[0].convert("RGBA").square().resize((79, 79)) + # fmt: off + locs = ( + [] + [(39, 40)] * 3 + [(39, 30)] * 2 + [(39, 32)] * 10 + + [(39, 30), (39, 27), (39, 32), (37, 49), (37, 64), + (37, 67), (37, 67), (39, 69), (37, 70), (37, 70)] + ) + # fmt: on + frames: List[IMG] = [] + for i in range(25): + frame = BuildImage.open(img_dir / f"{i}.png") + frame.paste(img, locs[i], below=True) + frames.append(frame.image) + return save_gif(frames, 0.04) + + +add_meme("garbage", garbage, min_images=1, max_images=1, keywords=["垃圾", "垃圾桶"]) diff --git a/meme_generator/memes/garbage/images/0.png b/meme_generator/memes/garbage/images/0.png new file mode 100644 index 0000000000000000000000000000000000000000..5656ce45ac31aeb8a17ff9b515b808a96a86a98e Binary files /dev/null and b/meme_generator/memes/garbage/images/0.png differ diff --git a/meme_generator/memes/garbage/images/1.png b/meme_generator/memes/garbage/images/1.png new file mode 100644 index 0000000000000000000000000000000000000000..cf9dbbb61c5472369fd71ef585e4d6081bf0bdff Binary files /dev/null and b/meme_generator/memes/garbage/images/1.png differ diff --git a/meme_generator/memes/garbage/images/10.png b/meme_generator/memes/garbage/images/10.png new file mode 100644 index 0000000000000000000000000000000000000000..944f4f2b41ce4a3b042561e74cd2e51b12deee67 Binary files /dev/null and b/meme_generator/memes/garbage/images/10.png differ diff --git a/meme_generator/memes/garbage/images/11.png b/meme_generator/memes/garbage/images/11.png new file mode 100644 index 0000000000000000000000000000000000000000..c39166615a3d1a17400dd8075d01a723ac81b743 Binary files /dev/null and b/meme_generator/memes/garbage/images/11.png differ diff --git a/meme_generator/memes/garbage/images/12.png b/meme_generator/memes/garbage/images/12.png new file mode 100644 index 0000000000000000000000000000000000000000..07c687f4560aa10ae29c209fbe0d2ef40136c110 Binary files /dev/null and b/meme_generator/memes/garbage/images/12.png differ diff --git a/meme_generator/memes/garbage/images/13.png b/meme_generator/memes/garbage/images/13.png new file mode 100644 index 0000000000000000000000000000000000000000..8a039db5bd38f635013c0c8f8da233de088aa80f Binary files /dev/null and b/meme_generator/memes/garbage/images/13.png differ diff --git a/meme_generator/memes/garbage/images/14.png b/meme_generator/memes/garbage/images/14.png new file mode 100644 index 0000000000000000000000000000000000000000..73c4c7ef66325f006335227170e383172f7a45d9 Binary files /dev/null and b/meme_generator/memes/garbage/images/14.png differ diff --git a/meme_generator/memes/garbage/images/15.png b/meme_generator/memes/garbage/images/15.png new file mode 100644 index 0000000000000000000000000000000000000000..cbb9499b854ed1d00bdc13f17bdfccabd6f25348 Binary files /dev/null and b/meme_generator/memes/garbage/images/15.png differ diff --git a/meme_generator/memes/garbage/images/16.png b/meme_generator/memes/garbage/images/16.png new file mode 100644 index 0000000000000000000000000000000000000000..6aff9616f767b62ddf195dac1276a866e1acc0ee Binary files /dev/null and b/meme_generator/memes/garbage/images/16.png differ diff --git a/meme_generator/memes/garbage/images/17.png b/meme_generator/memes/garbage/images/17.png new file mode 100644 index 0000000000000000000000000000000000000000..b290e859b94b14ca97cf08b4fa2e9fc07e33f242 Binary files /dev/null and b/meme_generator/memes/garbage/images/17.png differ diff --git a/meme_generator/memes/garbage/images/18.png b/meme_generator/memes/garbage/images/18.png new file mode 100644 index 0000000000000000000000000000000000000000..a1622ee2c560565cddd5653f74d95f781ee92953 Binary files /dev/null and b/meme_generator/memes/garbage/images/18.png differ diff --git a/meme_generator/memes/garbage/images/19.png b/meme_generator/memes/garbage/images/19.png new file mode 100644 index 0000000000000000000000000000000000000000..0f09b6000d59215482bd2066a4e6408482094691 Binary files /dev/null and b/meme_generator/memes/garbage/images/19.png differ diff --git a/meme_generator/memes/garbage/images/2.png b/meme_generator/memes/garbage/images/2.png new file mode 100644 index 0000000000000000000000000000000000000000..211ee5bbd9a0d2fddcb8e94fc7efc0078d7cb593 Binary files /dev/null and b/meme_generator/memes/garbage/images/2.png differ diff --git a/meme_generator/memes/garbage/images/20.png b/meme_generator/memes/garbage/images/20.png new file mode 100644 index 0000000000000000000000000000000000000000..da3351f03245c98f27988e44213d0f5db65b8e94 Binary files /dev/null and b/meme_generator/memes/garbage/images/20.png differ diff --git a/meme_generator/memes/garbage/images/21.png b/meme_generator/memes/garbage/images/21.png new file mode 100644 index 0000000000000000000000000000000000000000..3888bb2cb512d6404e458c20175692c3a48d2bf1 Binary files /dev/null and b/meme_generator/memes/garbage/images/21.png differ diff --git a/meme_generator/memes/garbage/images/22.png b/meme_generator/memes/garbage/images/22.png new file mode 100644 index 0000000000000000000000000000000000000000..9782e33da1b0c8ef32de8597f3854b0a78f79cac Binary files /dev/null and b/meme_generator/memes/garbage/images/22.png differ diff --git a/meme_generator/memes/garbage/images/23.png b/meme_generator/memes/garbage/images/23.png new file mode 100644 index 0000000000000000000000000000000000000000..c30b01c21296babe835af7d0b5c45bc3b518864a Binary files /dev/null and b/meme_generator/memes/garbage/images/23.png differ diff --git a/meme_generator/memes/garbage/images/24.png b/meme_generator/memes/garbage/images/24.png new file mode 100644 index 0000000000000000000000000000000000000000..b31a985020f7b7281b8fb9bc84fa54c36d9a207f Binary files /dev/null and b/meme_generator/memes/garbage/images/24.png differ diff --git a/meme_generator/memes/garbage/images/3.png b/meme_generator/memes/garbage/images/3.png new file mode 100644 index 0000000000000000000000000000000000000000..17522feb32e033f1668af989c56ea6e494353668 Binary files /dev/null and b/meme_generator/memes/garbage/images/3.png differ diff --git a/meme_generator/memes/garbage/images/4.png b/meme_generator/memes/garbage/images/4.png new file mode 100644 index 0000000000000000000000000000000000000000..1dbd80615c40ec427573a305baba804fb17b8bc6 Binary files /dev/null and b/meme_generator/memes/garbage/images/4.png differ diff --git a/meme_generator/memes/garbage/images/5.png b/meme_generator/memes/garbage/images/5.png new file mode 100644 index 0000000000000000000000000000000000000000..6f43a028c02d1643001d175458356526596ffdd7 Binary files /dev/null and b/meme_generator/memes/garbage/images/5.png differ diff --git a/meme_generator/memes/garbage/images/6.png b/meme_generator/memes/garbage/images/6.png new file mode 100644 index 0000000000000000000000000000000000000000..930a02b6838c646e97980047279db8aaa251b0e8 Binary files /dev/null and b/meme_generator/memes/garbage/images/6.png differ diff --git a/meme_generator/memes/garbage/images/7.png b/meme_generator/memes/garbage/images/7.png new file mode 100644 index 0000000000000000000000000000000000000000..0099937c09b37dac7ac07af8c522220275266fdd Binary files /dev/null and b/meme_generator/memes/garbage/images/7.png differ diff --git a/meme_generator/memes/garbage/images/8.png b/meme_generator/memes/garbage/images/8.png new file mode 100644 index 0000000000000000000000000000000000000000..5854f0e35e84478e18835a2f609cf7e1b9784720 Binary files /dev/null and b/meme_generator/memes/garbage/images/8.png differ diff --git a/meme_generator/memes/garbage/images/9.png b/meme_generator/memes/garbage/images/9.png new file mode 100644 index 0000000000000000000000000000000000000000..da8c0942f24fd68219d74b489e55f17321da5f35 Binary files /dev/null and b/meme_generator/memes/garbage/images/9.png differ diff --git a/meme_generator/memes/genshin_start/__init__.py b/meme_generator/memes/genshin_start/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..507fdd22dfff4045eb99e68afbf6f747c7732b5c --- /dev/null +++ b/meme_generator/memes/genshin_start/__init__.py @@ -0,0 +1,50 @@ +from pathlib import Path +from typing import List + +from pil_utils import BuildImage + +from meme_generator import add_meme +from meme_generator.exception import TextOverLength +from meme_generator.utils import make_jpg_or_gif + +img_dir = Path(__file__).parent / "images" + + +def genshin_start(images: List[BuildImage], texts: List[str], args): + frame = BuildImage.open(img_dir / "0.png") + if texts: + text = texts[0] + try: + frame.draw_text( + (100, frame.height - 150, frame.width - 100, frame.height), + text, + max_fontsize=100, + min_fontsize=70, + fill="white", + stroke_fill="black", + stroke_ratio=0.05, + ) + except: + raise TextOverLength(text) + + def make(img: BuildImage) -> BuildImage: + points = ((0, 116), (585, 0), (584, 319), (43, 385)) + screen = ( + img.convert("RGBA").resize((600, 330), keep_ratio=True).perspective(points) + ) + return frame.copy().paste(screen, (412, 121), below=True) + + return make_jpg_or_gif(images[0], make) + + +add_meme( + "genshin_start", + genshin_start, + min_images=1, + max_images=1, + min_texts=0, + max_texts=1, + default_texts=["原神,启动!"], + keywords=["原神启动"], + patterns=[r"(\S+启动[!!]?)"], +) diff --git a/meme_generator/memes/genshin_start/images/0.png b/meme_generator/memes/genshin_start/images/0.png new file mode 100644 index 0000000000000000000000000000000000000000..f19f1d46b58a6ec8d44065a7bb847451813aca9a Binary files /dev/null and b/meme_generator/memes/genshin_start/images/0.png differ diff --git a/meme_generator/memes/gif_subtitle/__init__.py b/meme_generator/memes/gif_subtitle/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..11de55ac9e503f777f7fc6580708bd10437096fc --- /dev/null +++ b/meme_generator/memes/gif_subtitle/__init__.py @@ -0,0 +1,153 @@ +from pathlib import Path +from typing import List, Tuple + +from pil_utils import BuildImage + +from meme_generator import add_meme +from meme_generator.exception import TextOverLength +from meme_generator.utils import save_gif + +img_dir = Path(__file__).parent / "images" + + +def make_gif( + key: str, + texts: List[str], + pieces: Tuple[Tuple[int, int], ...], + fontsize: int = 20, + padding_x: int = 5, + padding_y: int = 5, +): + img = BuildImage.open(img_dir / f"{key}.gif").image + frames: List[BuildImage] = [] + for i in range(img.n_frames): + img.seek(i) + frames.append(BuildImage(img.convert("RGB"))) + + parts = [frames[start:end] for start, end in pieces] + for part, text in zip(parts, texts): + for frame in part: + try: + frame.draw_text( + (padding_x, 0, frame.width - padding_x, frame.height - padding_y), + text, + max_fontsize=fontsize, + min_fontsize=fontsize, + fill="white", + stroke_ratio=0.05, + stroke_fill="black", + valign="bottom", + ) + except ValueError: + raise TextOverLength(text) + + return save_gif([frame.image for frame in frames], img.info["duration"] / 1000) + + +def add_gif_meme( + key: str, + keywords: List[str], + pieces: Tuple[Tuple[int, int], ...], + examples: Tuple[str, ...], + **kwargs, +): + def gif_func(images, texts: List[str], args): + return make_gif(key, texts, pieces, **kwargs) + + text_num = len(pieces) + add_meme( + key, + gif_func, + min_texts=text_num, + max_texts=text_num, + default_texts=list(examples), + keywords=keywords, + ) + + +add_gif_meme( + "wangjingze", + ["王境泽"], + ((0, 9), (12, 24), (25, 35), (37, 48)), + ("我就是饿死", "死外边 从这里跳下去", "不会吃你们一点东西", "真香"), +) + +# fmt: off +add_gif_meme( + "weisuoyuwei", + ["为所欲为"], + ((11, 14), (27, 38), (42, 61), (63, 81), (82, 95), (96, 105), (111, 131), (145, 157), (157, 167),), + ("好啊", "就算你是一流工程师", "就算你出报告再完美", "我叫你改报告你就要改", "毕竟我是客户", "客户了不起啊", "Sorry 客户真的了不起", "以后叫他天天改报告", "天天改 天天改"), + fontsize=19, +) +# fmt: on + +add_gif_meme( + "chanshenzi", + ["馋身子"], + ((0, 16), (16, 31), (33, 40)), + ("你那叫喜欢吗?", "你那是馋她身子", "你下贱!"), + fontsize=18, +) + +add_gif_meme( + "qiegewala", + ["切格瓦拉"], + ((0, 15), (16, 31), (31, 38), (38, 48), (49, 68), (68, 86)), + ("没有钱啊 肯定要做的啊", "不做的话没有钱用", "那你不会去打工啊", "有手有脚的", "打工是不可能打工的", "这辈子不可能打工的"), +) + +add_gif_meme( + "shuifandui", + ["谁反对"], + ((3, 14), (21, 26), (31, 38), (40, 45)), + ("我话说完了", "谁赞成", "谁反对", "我反对"), + fontsize=19, +) + +add_gif_meme( + "zengxiaoxian", + ["曾小贤"], + ((3, 15), (24, 30), (30, 46), (56, 63)), + ("平时你打电子游戏吗", "偶尔", "星际还是魔兽", "连连看"), + fontsize=21, +) + +add_gif_meme( + "yalidaye", + ["压力大爷"], + ((0, 16), (21, 47), (52, 77)), + ("外界都说我们压力大", "我觉得吧压力也没有那么大", "主要是28岁了还没媳妇儿"), + fontsize=21, +) + +add_gif_meme( + "nihaosaoa", + ["你好骚啊"], + ((0, 14), (16, 26), (42, 61)), + ("既然追求刺激", "就贯彻到底了", "你好骚啊"), + fontsize=17, +) + +add_gif_meme( + "shishilani", + ["食屎啦你"], + ((14, 21), (23, 36), (38, 46), (60, 66)), + ("穿西装打领带", "拿大哥大有什么用", "跟着这样的大哥", "食屎啦你"), + fontsize=17, +) + +add_gif_meme( + "wunian", + ["五年怎么过的"], + ((11, 20), (35, 50), (59, 77), (82, 95)), + ("五年", "你知道我这五年是怎么过的吗", "我每天躲在家里玩贪玩蓝月", "你知道有多好玩吗"), + fontsize=16, +) + +add_gif_meme( + "maikease", + ["麦克阿瑟说"], + ((0, 22), (24, 46), (48, 70), (72, 84)), + ("美国前五星上将麦克阿瑟", "曾这样评价道", "如果让我去阻止xxx", "那么我宁愿去阻止上帝"), +) diff --git a/meme_generator/memes/gif_subtitle/images/chanshenzi.gif b/meme_generator/memes/gif_subtitle/images/chanshenzi.gif new file mode 100644 index 0000000000000000000000000000000000000000..905dee87b48a9e2e7e78e59116dadca0dc0f83f3 Binary files /dev/null and b/meme_generator/memes/gif_subtitle/images/chanshenzi.gif differ diff --git a/meme_generator/memes/gif_subtitle/images/maikease.gif b/meme_generator/memes/gif_subtitle/images/maikease.gif new file mode 100644 index 0000000000000000000000000000000000000000..c5f04cdc36b5883b9e2e3f3a464ec9c39bb453ab --- /dev/null +++ b/meme_generator/memes/gif_subtitle/images/maikease.gif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:aa70c78e78434e3d0327bdba6de2a525b7ea68f5413eb2ad963162549051e0a0 +size 2908999 diff --git a/meme_generator/memes/gif_subtitle/images/nihaosaoa.gif b/meme_generator/memes/gif_subtitle/images/nihaosaoa.gif new file mode 100644 index 0000000000000000000000000000000000000000..9621f152bc778ab2601e3eaeb808798b9925ad9a Binary files /dev/null and b/meme_generator/memes/gif_subtitle/images/nihaosaoa.gif differ diff --git a/meme_generator/memes/gif_subtitle/images/qiegewala.gif b/meme_generator/memes/gif_subtitle/images/qiegewala.gif new file mode 100644 index 0000000000000000000000000000000000000000..a3b9efa87ca5454d014f361d43f494dd5de0c0bf --- /dev/null +++ b/meme_generator/memes/gif_subtitle/images/qiegewala.gif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:eb96d93a6f4469ddb76e2c51860358cd2051b9327ea206d6ed9afe69c13c4afd +size 1080215 diff --git a/meme_generator/memes/gif_subtitle/images/shishilani.gif b/meme_generator/memes/gif_subtitle/images/shishilani.gif new file mode 100644 index 0000000000000000000000000000000000000000..3dfce92278a06574ebec7cc2f61441964e4504ce Binary files /dev/null and b/meme_generator/memes/gif_subtitle/images/shishilani.gif differ diff --git a/meme_generator/memes/gif_subtitle/images/shuifandui.gif b/meme_generator/memes/gif_subtitle/images/shuifandui.gif new file mode 100644 index 0000000000000000000000000000000000000000..bafa6c0aa8ecdcc1a50b1c07e977adda9d0321ac --- /dev/null +++ b/meme_generator/memes/gif_subtitle/images/shuifandui.gif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2cf1d8c0585692587db5e0d851b7990662adaf4d0707391d25ac7b108260818a +size 1149010 diff --git a/meme_generator/memes/gif_subtitle/images/wangjingze.gif b/meme_generator/memes/gif_subtitle/images/wangjingze.gif new file mode 100644 index 0000000000000000000000000000000000000000..ea080b956d21e9c036e2e5d972cff54275f5dd5e --- /dev/null +++ b/meme_generator/memes/gif_subtitle/images/wangjingze.gif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:81cc2ee9c01366b3d560bfd380c17d60996d2655abaaeee0ea163ef030d05a20 +size 1029252 diff --git a/meme_generator/memes/gif_subtitle/images/weisuoyuwei.gif b/meme_generator/memes/gif_subtitle/images/weisuoyuwei.gif new file mode 100644 index 0000000000000000000000000000000000000000..e17f2d8e18aab903415dbebaa156c7e13357a188 --- /dev/null +++ b/meme_generator/memes/gif_subtitle/images/weisuoyuwei.gif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5885aa4e35692ee4b5c74ae1a946f429a2672e7633587cca8db3dd25bb096b34 +size 1946629 diff --git a/meme_generator/memes/gif_subtitle/images/wunian.gif b/meme_generator/memes/gif_subtitle/images/wunian.gif new file mode 100644 index 0000000000000000000000000000000000000000..10f8ceb75b907e246822c4b9f63e695f2c052873 Binary files /dev/null and b/meme_generator/memes/gif_subtitle/images/wunian.gif differ diff --git a/meme_generator/memes/gif_subtitle/images/yalidaye.gif b/meme_generator/memes/gif_subtitle/images/yalidaye.gif new file mode 100644 index 0000000000000000000000000000000000000000..398292c9d6a07474bc77fa0676bf844fd657988d Binary files /dev/null and b/meme_generator/memes/gif_subtitle/images/yalidaye.gif differ diff --git a/meme_generator/memes/gif_subtitle/images/zengxiaoxian.gif b/meme_generator/memes/gif_subtitle/images/zengxiaoxian.gif new file mode 100644 index 0000000000000000000000000000000000000000..ba9e35e06694806fdf19b231d930eb5d302ad572 Binary files /dev/null and b/meme_generator/memes/gif_subtitle/images/zengxiaoxian.gif differ diff --git a/meme_generator/memes/good_news/__init__.py b/meme_generator/memes/good_news/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..18137eda7e8099b2550557021e75e854f54a4d4f --- /dev/null +++ b/meme_generator/memes/good_news/__init__.py @@ -0,0 +1,39 @@ +from pathlib import Path +from typing import List + +from pil_utils import BuildImage + +from meme_generator import add_meme +from meme_generator.exception import TextOverLength + +img_dir = Path(__file__).parent / "images" + + +def good_news(images, texts: List[str], args): + text = texts[0] + frame = BuildImage.open(img_dir / "0.jpg") + try: + frame.draw_text( + (50, 100, frame.width - 50, frame.height - 100), + text, + allow_wrap=True, + lines_align="center", + max_fontsize=60, + min_fontsize=30, + fill=(238, 0, 0), + stroke_ratio=1 / 15, + stroke_fill=(255, 255, 153), + ) + except ValueError: + raise TextOverLength(text) + return frame.save_png() + + +add_meme( + "good_news", + good_news, + min_texts=1, + max_texts=1, + default_texts=["悲报"], + keywords=["喜报"], +) diff --git a/meme_generator/memes/good_news/images/0.jpg b/meme_generator/memes/good_news/images/0.jpg new file mode 100644 index 0000000000000000000000000000000000000000..b6e54b56468326f255429d4dfd461dafa2a23f20 Binary files /dev/null and b/meme_generator/memes/good_news/images/0.jpg differ diff --git a/meme_generator/memes/google/__init__.py b/meme_generator/memes/google/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..2e0544d44ba347a347f97f8bd017a3295d08d3c1 --- /dev/null +++ b/meme_generator/memes/google/__init__.py @@ -0,0 +1,28 @@ +from typing import List + +from pil_utils import BuildImage, Text2Image + +from meme_generator import add_meme + + +def google(images, texts: List[str], args): + text = texts[0] + text = " ".join(text.splitlines()) + colors = ["#4285f4", "#db4437", "#f4b400", "#4285f4", "#0f9d58", "#db4437"] + t2m = Text2Image.from_text(text, 200) + index = 0 + for char in t2m.lines[0].chars: + char.fill = colors[index % len(colors)] + if char.char.strip(): + index += 1 + return BuildImage(t2m.to_image(bg_color="white", padding=(50, 50))).save_jpg() + + +add_meme( + "google", + google, + min_texts=1, + max_texts=1, + default_texts=["Google"], + keywords=["google"], +) diff --git a/meme_generator/memes/guichu/__init__.py b/meme_generator/memes/guichu/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e7281b4b484206fc09f9aeeab3a8f6727bc7362a --- /dev/null +++ b/meme_generator/memes/guichu/__init__.py @@ -0,0 +1,117 @@ +from typing import Dict, List, Literal, NamedTuple, Tuple + +from PIL.Image import Image as IMG +from PIL.Image import Transpose +from pil_utils import BuildImage +from pydantic import Field + +from meme_generator import MemeArgsModel, MemeArgsParser, MemeArgsType, add_meme +from meme_generator.utils import save_gif + +help = "鬼畜对称方向" + +parser = MemeArgsParser(prefix_chars="-/") +group = parser.add_mutually_exclusive_group() +group.add_argument( + "-d", + "--direction", + type=str, + choices=["left", "right", "top", "bottom"], + default="left", + help=help, +) +group.add_argument("--left", "/左", action="store_const", const="left", dest="direction") +group.add_argument( + "--right", "/右", action="store_const", const="right", dest="direction" +) +group.add_argument("--top", "/上", action="store_const", const="top", dest="direction") +group.add_argument( + "--bottom", "/下", action="store_const", const="bottom", dest="direction" +) + + +class Model(MemeArgsModel): + direction: Literal["left", "right", "top", "bottom"] = Field( + "left", description=help + ) + + +def guichu(images: List[BuildImage], texts, args: Model): + img = images[0].convert("RGBA") + img_w, img_h = img.size + + class Mode(NamedTuple): + method: Transpose + size1: Tuple[int, int, int, int] + pos1: Tuple[int, int] + size2: Tuple[int, int, int, int] + pos2: Tuple[int, int] + + modes: Dict[str, Mode] = { + "left": Mode( + Transpose.FLIP_LEFT_RIGHT, + (0, 0, img_w // 2, img_h), + (0, 0), + (img_w // 2, 0, img_w // 2 * 2, img_h), + (img_w // 2, 0), + ), + "right": Mode( + Transpose.FLIP_LEFT_RIGHT, + (img_w // 2, 0, img_w // 2 * 2, img_h), + (img_w // 2, 0), + (0, 0, img_w // 2, img_h), + (0, 0), + ), + "top": Mode( + Transpose.FLIP_TOP_BOTTOM, + (0, 0, img_w, img_h // 2), + (0, 0), + (0, img_h // 2, img_w, img_h // 2 * 2), + (0, img_h // 2), + ), + "bottom": Mode( + Transpose.FLIP_TOP_BOTTOM, + (0, img_h // 2, img_w, img_h // 2 * 2), + (0, img_h // 2), + (0, 0, img_w, img_h // 2), + (0, 0), + ), + } + mode = modes[args.direction] + + img_flip = img.transpose(mode.method) + img_symmetric = BuildImage.new("RGBA", img.size) + img_symmetric.paste(img.crop(mode.size1), mode.pos1, alpha=True) + img_symmetric.paste(img_flip.crop(mode.size2), mode.pos2, alpha=True) + img_symmetric_big = BuildImage.new("RGBA", img.size) + img_symmetric_big.paste( + img_symmetric.copy().resize_width(img_w * 2), (-img_w // 2, -img_h // 2) + ) + + frames: List[IMG] = [] + frames += ( + ([img.image] * 3 + [img_flip.image] * 3) * 3 + + [img.image, img_flip.image] * 3 + + ([img_symmetric.image] * 2 + [img_symmetric_big.image] * 2) * 2 + ) + + return save_gif(frames, 0.20) + + +add_meme( + "guichu", + guichu, + min_images=1, + max_images=1, + args_type=MemeArgsType( + parser, + Model, + [ + Model(direction="left"), + Model(direction="right"), + Model(direction="top"), + Model(direction="bottom"), + ], + ), + keywords=["鬼畜"], +) diff --git a/meme_generator/memes/gun/__init__.py b/meme_generator/memes/gun/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..59ef415204ee6d88dba5d2ecf6e474b815e47d1e --- /dev/null +++ b/meme_generator/memes/gun/__init__.py @@ -0,0 +1,61 @@ +from pathlib import Path +from typing import List, Literal + +from PIL.Image import Transpose +from pil_utils import BuildImage +from pydantic import Field + +from meme_generator import MemeArgsModel, MemeArgsParser, MemeArgsType, add_meme + +img_dir = Path(__file__).parent / "images" + + +help = "枪的位置" + +parser = MemeArgsParser(prefix_chars="-/") +group = parser.add_mutually_exclusive_group() +group.add_argument( + "-p", + "--position", + dest="position", + type=str, + choices=["left", "right", "both"], + default="left", + help=help, +) +group.add_argument("--left", "/左手", action="store_const", const="left", dest="position") +group.add_argument( + "--right", "/右手", action="store_const", const="right", dest="position" +) +group.add_argument("--both", "/双手", action="store_const", const="both", dest="position") + + +class Model(MemeArgsModel): + position: Literal["left", "right", "both"] = Field("left", description=help) + + +def gun(images: List[BuildImage], texts, args: Model): + frame = images[0].convert("RGBA").resize((500, 500), keep_ratio=True) + gun = BuildImage.open(img_dir / "0.png") + position = args.position + left = position in ["left", "both"] + right = position in ["right", "both"] + if left: + frame.paste(gun, alpha=True) + if right: + frame.paste(gun.transpose(Transpose.FLIP_LEFT_RIGHT), alpha=True) + return frame.save_jpg() + + +add_meme( + "gun", + gun, + min_images=1, + max_images=1, + args_type=MemeArgsType( + parser, + Model, + [Model(position="left"), Model(position="right"), Model(position="both")], + ), + keywords=["手枪"], +) diff --git a/meme_generator/memes/gun/images/0.png b/meme_generator/memes/gun/images/0.png new file mode 100644 index 0000000000000000000000000000000000000000..8b86d12ed06b133a3c067a4fe24d521e16de5ce6 Binary files /dev/null and b/meme_generator/memes/gun/images/0.png differ diff --git a/meme_generator/memes/hammer/__init__.py b/meme_generator/memes/hammer/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..9a5b31a955e8d12ceee501a3dca30a48bcba0817 --- /dev/null +++ b/meme_generator/memes/hammer/__init__.py @@ -0,0 +1,30 @@ +from pathlib import Path +from typing import List + +from PIL.Image import Image as IMG +from pil_utils import BuildImage + +from meme_generator import add_meme +from meme_generator.utils import save_gif + +img_dir = Path(__file__).parent / "images" + + +def hammer(images: List[BuildImage], texts, args): + img = images[0].convert("RGBA").square() + # fmt: off + locs = [ + (62, 143, 158, 113), (52, 177, 173, 105), (42, 192, 192, 92), (46, 182, 184, 100), + (54, 169, 174, 110), (69, 128, 144, 135), (65, 130, 152, 124), + ] + # fmt: on + frames: List[IMG] = [] + for i in range(7): + frame = BuildImage.open(img_dir / f"{i}.png") + x, y, w, h = locs[i] + frame.paste(img.resize((w, h)), (x, y), below=True) + frames.append(frame.image) + return save_gif(frames, 0.07) + + +add_meme("hammer", hammer, min_images=1, max_images=1, keywords=["锤"]) diff --git a/meme_generator/memes/hammer/images/0.png b/meme_generator/memes/hammer/images/0.png new file mode 100644 index 0000000000000000000000000000000000000000..10353d3fb244c53ecb69efe9273510114b05aeba Binary files /dev/null and b/meme_generator/memes/hammer/images/0.png differ diff --git a/meme_generator/memes/hammer/images/1.png b/meme_generator/memes/hammer/images/1.png new file mode 100644 index 0000000000000000000000000000000000000000..771d71bcc8465240474974dd316aa2565186c120 Binary files /dev/null and b/meme_generator/memes/hammer/images/1.png differ diff --git a/meme_generator/memes/hammer/images/2.png b/meme_generator/memes/hammer/images/2.png new file mode 100644 index 0000000000000000000000000000000000000000..edfc0dcc123350661ac19f40f1c39a0add158312 Binary files /dev/null and b/meme_generator/memes/hammer/images/2.png differ diff --git a/meme_generator/memes/hammer/images/3.png b/meme_generator/memes/hammer/images/3.png new file mode 100644 index 0000000000000000000000000000000000000000..ff96b3f147f7f4472a8782f120637e2bf10a7ac6 Binary files /dev/null and b/meme_generator/memes/hammer/images/3.png differ diff --git a/meme_generator/memes/hammer/images/4.png b/meme_generator/memes/hammer/images/4.png new file mode 100644 index 0000000000000000000000000000000000000000..a06a86640eadaf095fece6cc1087d02e6b9abce7 Binary files /dev/null and b/meme_generator/memes/hammer/images/4.png differ diff --git a/meme_generator/memes/hammer/images/5.png b/meme_generator/memes/hammer/images/5.png new file mode 100644 index 0000000000000000000000000000000000000000..0d9a2f1c4f408ba309f77ab6b6fb9545fd38a398 Binary files /dev/null and b/meme_generator/memes/hammer/images/5.png differ diff --git a/meme_generator/memes/hammer/images/6.png b/meme_generator/memes/hammer/images/6.png new file mode 100644 index 0000000000000000000000000000000000000000..88f609ab164f8b9842ee97c26793096ea35bf90f Binary files /dev/null and b/meme_generator/memes/hammer/images/6.png differ diff --git a/meme_generator/memes/high_EQ/__init__.py b/meme_generator/memes/high_EQ/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..febab3af7f19928cffdae37991309780f99e489b --- /dev/null +++ b/meme_generator/memes/high_EQ/__init__.py @@ -0,0 +1,43 @@ +from pathlib import Path +from typing import List, Tuple + +from pil_utils import BuildImage + +from meme_generator import add_meme +from meme_generator.exception import TextOverLength + +img_dir = Path(__file__).parent / "images" + + +def high_EQ(images, texts: List[str], args): + frame = BuildImage.open(img_dir / "0.jpg") + + def draw(pos: Tuple[float, float, float, float], text: str): + try: + frame.draw_text( + pos, + text, + max_fontsize=100, + min_fontsize=50, + allow_wrap=True, + fill="white", + stroke_fill="black", + stroke_ratio=0.05, + ) + except ValueError: + raise TextOverLength(text) + + draw((40, 540, 602, 1140), texts[0]) + draw((682, 540, 1244, 1140), texts[1]) + return frame.save_jpg() + + +add_meme( + "high_EQ", + high_EQ, + min_texts=2, + max_texts=2, + default_texts=["高情商", "低情商"], + keywords=["低情商xx高情商xx"], + patterns=[r"低情商[\s::]*(.+?)\s+高情商[\s::]*(.+)"], +) diff --git a/meme_generator/memes/high_EQ/images/0.jpg b/meme_generator/memes/high_EQ/images/0.jpg new file mode 100644 index 0000000000000000000000000000000000000000..b8b38329a62671768d851ce1aae2338aa138c0e6 Binary files /dev/null and b/meme_generator/memes/high_EQ/images/0.jpg differ diff --git a/meme_generator/memes/hit_screen/__init__.py b/meme_generator/memes/hit_screen/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..1ff85781315f5802402a3dab620f26d14bf1c55c --- /dev/null +++ b/meme_generator/memes/hit_screen/__init__.py @@ -0,0 +1,48 @@ +from pathlib import Path +from typing import List + +from pil_utils import BuildImage + +from meme_generator import add_meme +from meme_generator.utils import FrameAlignPolicy, Maker, make_gif_or_combined_gif + +img_dir = Path(__file__).parent / "images" + + +def hit_screen(images: List[BuildImage], texts, args): + params = ( + (((1, 10), (138, 1), (140, 119), (7, 154)), (32, 37)), + (((1, 10), (138, 1), (140, 121), (7, 154)), (32, 37)), + (((1, 10), (138, 1), (139, 125), (10, 159)), (32, 37)), + (((1, 12), (136, 1), (137, 125), (8, 159)), (34, 37)), + (((1, 9), (137, 1), (139, 122), (9, 154)), (35, 41)), + (((1, 8), (144, 1), (144, 123), (12, 155)), (30, 45)), + (((1, 8), (140, 1), (141, 121), (10, 155)), (29, 49)), + (((1, 9), (140, 1), (139, 118), (10, 153)), (27, 53)), + (((1, 7), (144, 1), (145, 117), (13, 153)), (19, 57)), + (((1, 7), (144, 1), (143, 116), (13, 153)), (19, 57)), + (((1, 8), (139, 1), (141, 119), (12, 154)), (19, 55)), + (((1, 13), (140, 1), (143, 117), (12, 156)), (16, 57)), + (((1, 10), (138, 1), (142, 117), (11, 149)), (14, 61)), + (((1, 10), (141, 1), (148, 125), (13, 153)), (11, 57)), + (((1, 12), (141, 1), (147, 130), (16, 150)), (11, 60)), + (((1, 15), (165, 1), (175, 135), (1, 171)), (-6, 46)), + ) + + def maker(i: int) -> Maker: + def make(img: BuildImage) -> BuildImage: + img = img.convert("RGBA").resize((140, 120), keep_ratio=True) + frame = BuildImage.open(img_dir / f"{i}.png") + if 6 <= i < 22: + points, pos = params[i - 6] + frame.paste(img.perspective(points), pos, below=True) + return frame + + return make + + return make_gif_or_combined_gif( + images[0], maker, 29, 0.2, FrameAlignPolicy.extend_first + ) + + +add_meme("hit_screen", hit_screen, min_images=1, max_images=1, keywords=["打穿", "打穿屏幕"]) diff --git a/meme_generator/memes/hit_screen/images/0.png b/meme_generator/memes/hit_screen/images/0.png new file mode 100644 index 0000000000000000000000000000000000000000..042ffbe3395db6a55ae99db385f2aabcdc085ec3 Binary files /dev/null and b/meme_generator/memes/hit_screen/images/0.png differ diff --git a/meme_generator/memes/hit_screen/images/1.png b/meme_generator/memes/hit_screen/images/1.png new file mode 100644 index 0000000000000000000000000000000000000000..9cf9e8489618e94aa4e533770030641b884fc565 Binary files /dev/null and b/meme_generator/memes/hit_screen/images/1.png differ diff --git a/meme_generator/memes/hit_screen/images/10.png b/meme_generator/memes/hit_screen/images/10.png new file mode 100644 index 0000000000000000000000000000000000000000..c8b175d965e4547e98a2fcb9b72aac58bd51b614 Binary files /dev/null and b/meme_generator/memes/hit_screen/images/10.png differ diff --git a/meme_generator/memes/hit_screen/images/11.png b/meme_generator/memes/hit_screen/images/11.png new file mode 100644 index 0000000000000000000000000000000000000000..99cd0e5d5c3f8ca295010a844dfcb43e8b9501ad Binary files /dev/null and b/meme_generator/memes/hit_screen/images/11.png differ diff --git a/meme_generator/memes/hit_screen/images/12.png b/meme_generator/memes/hit_screen/images/12.png new file mode 100644 index 0000000000000000000000000000000000000000..bfdaa4d38b9639cdac360d2660db7a35abb2c961 Binary files /dev/null and b/meme_generator/memes/hit_screen/images/12.png differ diff --git a/meme_generator/memes/hit_screen/images/13.png b/meme_generator/memes/hit_screen/images/13.png new file mode 100644 index 0000000000000000000000000000000000000000..9dbbeb6ee010124d5161a7f3273b6ef2dfaaa8be Binary files /dev/null and b/meme_generator/memes/hit_screen/images/13.png differ diff --git a/meme_generator/memes/hit_screen/images/14.png b/meme_generator/memes/hit_screen/images/14.png new file mode 100644 index 0000000000000000000000000000000000000000..513b3a252e5adae2035387704ef61f48c9c90834 Binary files /dev/null and b/meme_generator/memes/hit_screen/images/14.png differ diff --git a/meme_generator/memes/hit_screen/images/15.png b/meme_generator/memes/hit_screen/images/15.png new file mode 100644 index 0000000000000000000000000000000000000000..60f82d6d19fec770110ccdef24afc09e0cd16e39 Binary files /dev/null and b/meme_generator/memes/hit_screen/images/15.png differ diff --git a/meme_generator/memes/hit_screen/images/16.png b/meme_generator/memes/hit_screen/images/16.png new file mode 100644 index 0000000000000000000000000000000000000000..8dadf10a11ff226e7b269c97f15adc58d4c51620 Binary files /dev/null and b/meme_generator/memes/hit_screen/images/16.png differ diff --git a/meme_generator/memes/hit_screen/images/17.png b/meme_generator/memes/hit_screen/images/17.png new file mode 100644 index 0000000000000000000000000000000000000000..80d3365df7024d1bbca7456568643af4be1f2831 Binary files /dev/null and b/meme_generator/memes/hit_screen/images/17.png differ diff --git a/meme_generator/memes/hit_screen/images/18.png b/meme_generator/memes/hit_screen/images/18.png new file mode 100644 index 0000000000000000000000000000000000000000..80524eaf1c1d680465ced2f4bf9a6c697c8246f7 Binary files /dev/null and b/meme_generator/memes/hit_screen/images/18.png differ diff --git a/meme_generator/memes/hit_screen/images/19.png b/meme_generator/memes/hit_screen/images/19.png new file mode 100644 index 0000000000000000000000000000000000000000..dac731b67900093e8aebb31c786056193496368f Binary files /dev/null and b/meme_generator/memes/hit_screen/images/19.png differ diff --git a/meme_generator/memes/hit_screen/images/2.png b/meme_generator/memes/hit_screen/images/2.png new file mode 100644 index 0000000000000000000000000000000000000000..84ab43f105034533498c83c42e44878ac5418dc0 Binary files /dev/null and b/meme_generator/memes/hit_screen/images/2.png differ diff --git a/meme_generator/memes/hit_screen/images/20.png b/meme_generator/memes/hit_screen/images/20.png new file mode 100644 index 0000000000000000000000000000000000000000..ae7ce3ea10bd8f2f462b205252f4ecbb03ce3873 Binary files /dev/null and b/meme_generator/memes/hit_screen/images/20.png differ diff --git a/meme_generator/memes/hit_screen/images/21.png b/meme_generator/memes/hit_screen/images/21.png new file mode 100644 index 0000000000000000000000000000000000000000..d067347cf45e5d719a77d5098bb965e0b772a55c Binary files /dev/null and b/meme_generator/memes/hit_screen/images/21.png differ diff --git a/meme_generator/memes/hit_screen/images/22.png b/meme_generator/memes/hit_screen/images/22.png new file mode 100644 index 0000000000000000000000000000000000000000..6c08cbaa028d9c4cd3f76be9df4f596c2b9b95ae Binary files /dev/null and b/meme_generator/memes/hit_screen/images/22.png differ diff --git a/meme_generator/memes/hit_screen/images/23.png b/meme_generator/memes/hit_screen/images/23.png new file mode 100644 index 0000000000000000000000000000000000000000..c53e7af9cccea425e351713fa29bbdc6ea172e08 Binary files /dev/null and b/meme_generator/memes/hit_screen/images/23.png differ diff --git a/meme_generator/memes/hit_screen/images/24.png b/meme_generator/memes/hit_screen/images/24.png new file mode 100644 index 0000000000000000000000000000000000000000..3cc11cfe01fc4dd37015e34b36d5afa9b6c6c359 Binary files /dev/null and b/meme_generator/memes/hit_screen/images/24.png differ diff --git a/meme_generator/memes/hit_screen/images/25.png b/meme_generator/memes/hit_screen/images/25.png new file mode 100644 index 0000000000000000000000000000000000000000..fba926ab3ae47f8e478e52389b4afc7738f019f9 Binary files /dev/null and b/meme_generator/memes/hit_screen/images/25.png differ diff --git a/meme_generator/memes/hit_screen/images/26.png b/meme_generator/memes/hit_screen/images/26.png new file mode 100644 index 0000000000000000000000000000000000000000..fdb414be99f65c0abaeee53231d343bd58e4daed Binary files /dev/null and b/meme_generator/memes/hit_screen/images/26.png differ diff --git a/meme_generator/memes/hit_screen/images/27.png b/meme_generator/memes/hit_screen/images/27.png new file mode 100644 index 0000000000000000000000000000000000000000..ed2c991899dc7a651d8ea7330d3c7d4521cc26c4 Binary files /dev/null and b/meme_generator/memes/hit_screen/images/27.png differ diff --git a/meme_generator/memes/hit_screen/images/28.png b/meme_generator/memes/hit_screen/images/28.png new file mode 100644 index 0000000000000000000000000000000000000000..b9d7f6ba2a7f1e522a4f2cf2f6cb78050a1a5b37 Binary files /dev/null and b/meme_generator/memes/hit_screen/images/28.png differ diff --git a/meme_generator/memes/hit_screen/images/3.png b/meme_generator/memes/hit_screen/images/3.png new file mode 100644 index 0000000000000000000000000000000000000000..a56d2e5e3654c536d95c1277a3ff4cc6b813bec6 Binary files /dev/null and b/meme_generator/memes/hit_screen/images/3.png differ diff --git a/meme_generator/memes/hit_screen/images/4.png b/meme_generator/memes/hit_screen/images/4.png new file mode 100644 index 0000000000000000000000000000000000000000..7c330d24ee9db683d60e428bf375a6acd6cb291d Binary files /dev/null and b/meme_generator/memes/hit_screen/images/4.png differ diff --git a/meme_generator/memes/hit_screen/images/5.png b/meme_generator/memes/hit_screen/images/5.png new file mode 100644 index 0000000000000000000000000000000000000000..38d16a69fe66f4a4cebdd7d48cb2c9da125c5e9d Binary files /dev/null and b/meme_generator/memes/hit_screen/images/5.png differ diff --git a/meme_generator/memes/hit_screen/images/6.png b/meme_generator/memes/hit_screen/images/6.png new file mode 100644 index 0000000000000000000000000000000000000000..b71cb173dea1350429147602851e916c1c5aa0d4 Binary files /dev/null and b/meme_generator/memes/hit_screen/images/6.png differ diff --git a/meme_generator/memes/hit_screen/images/7.png b/meme_generator/memes/hit_screen/images/7.png new file mode 100644 index 0000000000000000000000000000000000000000..1d9d2b65b2e9db029188883540540884c9cb15ae Binary files /dev/null and b/meme_generator/memes/hit_screen/images/7.png differ diff --git a/meme_generator/memes/hit_screen/images/8.png b/meme_generator/memes/hit_screen/images/8.png new file mode 100644 index 0000000000000000000000000000000000000000..c51ed876ff1d2a13f4e5f62d6570fcd26032308c Binary files /dev/null and b/meme_generator/memes/hit_screen/images/8.png differ diff --git a/meme_generator/memes/hit_screen/images/9.png b/meme_generator/memes/hit_screen/images/9.png new file mode 100644 index 0000000000000000000000000000000000000000..8517d765638f250b9632de6db53a69e8239e21cd Binary files /dev/null and b/meme_generator/memes/hit_screen/images/9.png differ diff --git a/meme_generator/memes/hold_grudge/__init__.py b/meme_generator/memes/hold_grudge/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..be7c196b6cba6522e927429bf64274ed5cf34ca8 --- /dev/null +++ b/meme_generator/memes/hold_grudge/__init__.py @@ -0,0 +1,36 @@ +from datetime import datetime +from pathlib import Path +from typing import List + +from pil_utils import BuildImage, Text2Image + +from meme_generator import add_meme +from meme_generator.exception import TextOverLength + +img_dir = Path(__file__).parent / "images" + + +def hold_grudge(images, texts: List[str], args): + date = datetime.today().strftime("%Y{}%m{}%d{}").format("年", "月", "日") + text = f"{date} 晴\n{texts[0]}\n这个仇我先记下了" + text2image = Text2Image.from_text(text, 45, fill="black", spacing=10).wrap(440) + if len(text2image.lines) > 10: + raise TextOverLength(texts[0]) + text_img = text2image.to_image() + + frame = BuildImage.open(img_dir / "0.png") + bg = BuildImage.new( + "RGB", (frame.width, frame.height + text_img.height + 20), "white" + ) + bg.paste(frame).paste(text_img, (30, frame.height + 5), alpha=True) + return bg.save_jpg() + + +add_meme( + "hold_grudge", + hold_grudge, + min_texts=1, + max_texts=1, + default_texts=["群友不发涩图"], + keywords=["记仇"], +) diff --git a/meme_generator/memes/hold_grudge/images/0.png b/meme_generator/memes/hold_grudge/images/0.png new file mode 100644 index 0000000000000000000000000000000000000000..01615f73cee31363c72ae35c4c708e4743d6a39b Binary files /dev/null and b/meme_generator/memes/hold_grudge/images/0.png differ diff --git a/meme_generator/memes/hold_tight/__init__.py b/meme_generator/memes/hold_tight/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..98dc735a4250c2e6e8b93cc89ce90646dad7fc15 --- /dev/null +++ b/meme_generator/memes/hold_tight/__init__.py @@ -0,0 +1,18 @@ +from pathlib import Path +from typing import List + +from pil_utils import BuildImage + +from meme_generator import add_meme + +img_dir = Path(__file__).parent / "images" + + +def hold_tight(images: List[BuildImage], texts, args): + img = images[0].convert("RGBA").resize((159, 171), keep_ratio=True) + frame = BuildImage.open(img_dir / "0.png") + frame.paste(img, (113, 205), below=True) + return frame.save_jpg() + + +add_meme("hold_tight", hold_tight, min_images=1, max_images=1, keywords=["抱紧"]) diff --git a/meme_generator/memes/hold_tight/images/0.png b/meme_generator/memes/hold_tight/images/0.png new file mode 100644 index 0000000000000000000000000000000000000000..314a232a308be28640926aaa6c97bef30e76e7e9 Binary files /dev/null and b/meme_generator/memes/hold_tight/images/0.png differ diff --git a/meme_generator/memes/hug_leg/__init__.py b/meme_generator/memes/hug_leg/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..18ddf1c0bdce5e500d96dbc61a3a69d6db8ba4e8 --- /dev/null +++ b/meme_generator/memes/hug_leg/__init__.py @@ -0,0 +1,32 @@ +from pathlib import Path +from typing import List + +from PIL.Image import Image as IMG +from pil_utils import BuildImage + +from meme_generator import add_meme +from meme_generator.utils import save_gif + +img_dir = Path(__file__).parent / "images" + + +def hug_leg(images: List[BuildImage], texts, args): + img = images[0].convert("RGBA").square() + locs = [ + (50, 73, 68, 92), + (58, 60, 62, 95), + (65, 10, 67, 118), + (61, 20, 77, 97), + (55, 44, 65, 106), + (66, 85, 60, 98), + ] + frames: List[IMG] = [] + for i in range(6): + frame = BuildImage.open(img_dir / f"{i}.png") + x, y, w, h = locs[i] + frame.paste(img.resize((w, h)), (x, y), below=True) + frames.append(frame.image) + return save_gif(frames, 0.06) + + +add_meme("hug_leg", hug_leg, min_images=1, max_images=1, keywords=["抱大腿"]) diff --git a/meme_generator/memes/hug_leg/images/0.png b/meme_generator/memes/hug_leg/images/0.png new file mode 100644 index 0000000000000000000000000000000000000000..2f158831bdd67a5bdf0224a0975db12b63759e49 Binary files /dev/null and b/meme_generator/memes/hug_leg/images/0.png differ diff --git a/meme_generator/memes/hug_leg/images/1.png b/meme_generator/memes/hug_leg/images/1.png new file mode 100644 index 0000000000000000000000000000000000000000..06e4dd62ea26e928e60b2f5b5c80e6891b9d5c6b Binary files /dev/null and b/meme_generator/memes/hug_leg/images/1.png differ diff --git a/meme_generator/memes/hug_leg/images/2.png b/meme_generator/memes/hug_leg/images/2.png new file mode 100644 index 0000000000000000000000000000000000000000..d68037864c9674270e54eb22cbb91654434cec07 Binary files /dev/null and b/meme_generator/memes/hug_leg/images/2.png differ diff --git a/meme_generator/memes/hug_leg/images/3.png b/meme_generator/memes/hug_leg/images/3.png new file mode 100644 index 0000000000000000000000000000000000000000..17dfdc2eb403df48af18933429cc7930b6a7a049 Binary files /dev/null and b/meme_generator/memes/hug_leg/images/3.png differ diff --git a/meme_generator/memes/hug_leg/images/4.png b/meme_generator/memes/hug_leg/images/4.png new file mode 100644 index 0000000000000000000000000000000000000000..2bc1498cc0c27f2cbe163d67aa904a4ca988e870 Binary files /dev/null and b/meme_generator/memes/hug_leg/images/4.png differ diff --git a/meme_generator/memes/hug_leg/images/5.png b/meme_generator/memes/hug_leg/images/5.png new file mode 100644 index 0000000000000000000000000000000000000000..7495eb046190c11a2493fdf2157f608d01abb462 Binary files /dev/null and b/meme_generator/memes/hug_leg/images/5.png differ diff --git a/meme_generator/memes/hutao_bite/__init__.py b/meme_generator/memes/hutao_bite/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..9cfbf7497f6374d77bf28e2b5e5cde1de0c0850b --- /dev/null +++ b/meme_generator/memes/hutao_bite/__init__.py @@ -0,0 +1,25 @@ +from pathlib import Path +from typing import List + +from PIL.Image import Image as IMG +from pil_utils import BuildImage + +from meme_generator import add_meme +from meme_generator.utils import save_gif + +img_dir = Path(__file__).parent / "images" + + +def hutao_bite(images: List[BuildImage], texts, args): + img = images[0].convert("RGBA").square().resize((100, 100)) + frames: List[IMG] = [] + locs = [(98, 101, 108, 234), (96, 100, 108, 237)] + for i in range(2): + frame = BuildImage.open(img_dir / f"{i}.png") + w, h, x, y = locs[i] + frame.paste(img.resize((w, h)), (x, y), below=True) + frames.append(frame.image) + return save_gif(frames, 0.1) + + +add_meme("hutao_bite", hutao_bite, min_images=1, max_images=1, keywords=["胡桃啃"]) diff --git a/meme_generator/memes/hutao_bite/images/0.png b/meme_generator/memes/hutao_bite/images/0.png new file mode 100644 index 0000000000000000000000000000000000000000..3c16da68d5973dc095fb5a27c59896303f598166 Binary files /dev/null and b/meme_generator/memes/hutao_bite/images/0.png differ diff --git a/meme_generator/memes/hutao_bite/images/1.png b/meme_generator/memes/hutao_bite/images/1.png new file mode 100644 index 0000000000000000000000000000000000000000..114121705bd9d75ada5cff8ebbdd3665f38f8eca Binary files /dev/null and b/meme_generator/memes/hutao_bite/images/1.png differ diff --git a/meme_generator/memes/imprison/__init__.py b/meme_generator/memes/imprison/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..4033971bf33c636b961fbddce334f3b36ee9486f --- /dev/null +++ b/meme_generator/memes/imprison/__init__.py @@ -0,0 +1,35 @@ +from pathlib import Path +from typing import List + +from pil_utils import BuildImage + +from meme_generator import add_meme +from meme_generator.exception import TextOverLength + +img_dir = Path(__file__).parent / "images" + + +def imprison(images, texts: List[str], args): + text = texts[0] + frame = BuildImage.open(img_dir / "0.jpg") + try: + frame.draw_text( + (10, 157, 230, 197), + text, + allow_wrap=True, + max_fontsize=35, + min_fontsize=15, + ) + except ValueError: + raise TextOverLength(text) + return frame.save_jpg() + + +add_meme( + "imprison", + imprison, + min_texts=1, + max_texts=1, + default_texts=["我发涩图被抓起来了"], + keywords=["坐牢"], +) diff --git a/meme_generator/memes/imprison/images/0.jpg b/meme_generator/memes/imprison/images/0.jpg new file mode 100644 index 0000000000000000000000000000000000000000..2f5bd134a2f847b850198855574d3b3aeb6f721d Binary files /dev/null and b/meme_generator/memes/imprison/images/0.jpg differ diff --git a/meme_generator/memes/incivilization/__init__.py b/meme_generator/memes/incivilization/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..5491bc824ab12ebe3db5abeb34a5c13869af0ce6 --- /dev/null +++ b/meme_generator/memes/incivilization/__init__.py @@ -0,0 +1,43 @@ +from pathlib import Path +from typing import List + +from PIL import ImageEnhance +from pil_utils import BuildImage + +from meme_generator import add_meme +from meme_generator.exception import TextOverLength + +img_dir = Path(__file__).parent / "images" + + +def incivilization(images: List[BuildImage], texts: List[str], args): + frame = BuildImage.open(img_dir / "0.png") + points = ((0, 20), (154, 0), (164, 153), (22, 180)) + img = images[0].convert("RGBA").circle().resize((150, 150)).perspective(points) + image = ImageEnhance.Brightness(img.image).enhance(0.8) + frame.paste(image, (137, 151), alpha=True) + text = texts[0] if texts else "你刚才说的话不是很礼貌!" + try: + frame.draw_text( + (57, 42, 528, 117), + text, + weight="bold", + max_fontsize=50, + min_fontsize=20, + allow_wrap=True, + ) + except ValueError: + raise TextOverLength(text) + return frame.save_jpg() + + +add_meme( + "incivilization", + incivilization, + min_images=1, + max_images=1, + min_texts=0, + max_texts=1, + default_texts=["你刚才说的话不是很礼貌!"], + keywords=["不文明"], +) diff --git a/meme_generator/memes/incivilization/images/0.png b/meme_generator/memes/incivilization/images/0.png new file mode 100644 index 0000000000000000000000000000000000000000..d4d0a5f71deacfc3946c64f278474e88383715ec Binary files /dev/null and b/meme_generator/memes/incivilization/images/0.png differ diff --git a/meme_generator/memes/interview/__init__.py b/meme_generator/memes/interview/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..a8d226fad8b20c09cbedc2e5dd0ecab0beb9b904 --- /dev/null +++ b/meme_generator/memes/interview/__init__.py @@ -0,0 +1,45 @@ +from pathlib import Path +from typing import List + +from pil_utils import BuildImage + +from meme_generator import add_meme +from meme_generator.exception import TextOverLength + +img_dir = Path(__file__).parent / "images" + + +def interview(images: List[BuildImage], texts: List[str], args): + if len(images) == 2: + self_img = images[0] + user_img = images[1] + else: + self_img = BuildImage.open(img_dir / "huaji.png") + user_img = images[0] + self_img = self_img.convert("RGBA").square().resize((124, 124)) + user_img = user_img.convert("RGBA").square().resize((124, 124)) + + text = texts[0] if texts else "采访大佬经验" + + frame = BuildImage.new("RGBA", (600, 310), "white") + microphone = BuildImage.open(img_dir / "microphone.png") + frame.paste(microphone, (330, 103), alpha=True) + frame.paste(self_img, (419, 40), alpha=True) + frame.paste(user_img, (57, 40), alpha=True) + try: + frame.draw_text((20, 200, 580, 310), text, max_fontsize=50, min_fontsize=20) + except ValueError: + raise TextOverLength(text) + return frame.save_jpg() + + +add_meme( + "interview", + interview, + min_images=1, + max_images=2, + min_texts=0, + max_texts=1, + default_texts=["采访大佬经验"], + keywords=["采访"], +) diff --git a/meme_generator/memes/interview/images/huaji.png b/meme_generator/memes/interview/images/huaji.png new file mode 100644 index 0000000000000000000000000000000000000000..a98bac89096617160e7f534d8969bea959cd0a46 Binary files /dev/null and b/meme_generator/memes/interview/images/huaji.png differ diff --git a/meme_generator/memes/interview/images/microphone.png b/meme_generator/memes/interview/images/microphone.png new file mode 100644 index 0000000000000000000000000000000000000000..a566e5e2d2a5d42297f0b8e23f01f1a68f5b5111 Binary files /dev/null and b/meme_generator/memes/interview/images/microphone.png differ diff --git a/meme_generator/memes/jiji_king/__init__.py b/meme_generator/memes/jiji_king/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..dd67f38d3a46b2d77a84514173aa963a142bf15a --- /dev/null +++ b/meme_generator/memes/jiji_king/__init__.py @@ -0,0 +1,103 @@ +import math +from pathlib import Path +from typing import List + +from pil_utils import BuildImage +from pydantic import Field + +from meme_generator import MemeArgsModel, MemeArgsParser, MemeArgsType, add_meme +from meme_generator.exception import TextOverLength + +img_dir = Path(__file__).parent / "images" + +help = "是否将图片变为圆形" + +parser = MemeArgsParser(prefix_chars="-/") +parser.add_argument("--circle", "/圆", action="store_true", help=help) + + +class Model(MemeArgsModel): + circle: bool = Field(False, description=help) + + +def jiji_king(images: List[BuildImage], texts: List[str], args: Model): + block_num = 5 + if len(images) >= 7 or len(texts) >= 7: + block_num = max(len(images), len(texts)) - 1 + + chars = ["急"] + text = "我是急急国王" + + if len(texts) == 1: + if len(images) == 1: + chars = [texts[0]] * block_num + text = f"我是{texts[0]*2}国王" + else: + text = texts[0] + elif len(texts) == 2: + chars = [texts[0]] * block_num + text = texts[1] + elif texts: + chars = sum( + [[arg] * math.ceil(block_num / len(texts[:-1])) for arg in texts[:-1]], [] + ) + text = texts[-1] + + frame = BuildImage.new("RGBA", (10 + 100 * block_num, 400), "white") + king = BuildImage.open(img_dir / "0.png") + head = images[0].convert("RGBA").square().resize((125, 125)) + if args.circle: + head = head.circle() + king.paste(head, (237, 5), alpha=True) + frame.paste(king, ((frame.width - king.width) // 2, 0)) + + if len(images) > 1: + imgs = images[1:] + imgs = [img.convert("RGBA").square().resize((90, 90)) for img in imgs] + else: + imgs = [] + for char in chars: + block = BuildImage.new("RGBA", (90, 90), "black") + try: + block.draw_text( + (0, 0, 90, 90), + char, + lines_align="center", + weight="bold", + max_fontsize=60, + min_fontsize=30, + fill="white", + ) + except ValueError: + raise TextOverLength(char) + imgs.append(block) + + imgs = sum([[img] * math.ceil(block_num / len(imgs)) for img in imgs], []) + for i in range(block_num): + frame.paste(imgs[i], (10 + 100 * i, 200)) + + try: + frame.draw_text( + (10, 300, frame.width - 10, 390), + text, + lines_align="center", + weight="bold", + max_fontsize=100, + min_fontsize=30, + ) + except ValueError: + raise TextOverLength(text) + + return frame.save_jpg() + + +add_meme( + "jiji_king", + jiji_king, + min_images=1, + max_images=11, + min_texts=0, + max_texts=11, + args_type=MemeArgsType(parser, Model, [Model(circle=False), Model(circle=True)]), + keywords=["急急国王"], +) diff --git a/meme_generator/memes/jiji_king/images/0.png b/meme_generator/memes/jiji_king/images/0.png new file mode 100644 index 0000000000000000000000000000000000000000..5c81e3c7eb4dc9bf8c80f0797d8751c168c68190 Binary files /dev/null and b/meme_generator/memes/jiji_king/images/0.png differ diff --git a/meme_generator/memes/jiujiu/__init__.py b/meme_generator/memes/jiujiu/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..22bc183b6ed83a32db82eb0619a358d97146785f --- /dev/null +++ b/meme_generator/memes/jiujiu/__init__.py @@ -0,0 +1,23 @@ +from pathlib import Path +from typing import List + +from PIL.Image import Image as IMG +from pil_utils import BuildImage + +from meme_generator import add_meme +from meme_generator.utils import save_gif + +img_dir = Path(__file__).parent / "images" + + +def jiujiu(images: List[BuildImage], texts, args): + img = images[0].convert("RGBA").resize((75, 51), keep_ratio=True) + frames: List[IMG] = [] + for i in range(8): + frame = BuildImage.open(img_dir / f"{i}.png") + frame.paste(img, below=True) + frames.append(frame.image) + return save_gif(frames, 0.06) + + +add_meme("jiujiu", jiujiu, min_images=1, max_images=1, keywords=["啾啾"]) diff --git a/meme_generator/memes/jiujiu/images/0.png b/meme_generator/memes/jiujiu/images/0.png new file mode 100644 index 0000000000000000000000000000000000000000..c983ac7a7aecf952d0aa42b49f1ea08e2101a898 Binary files /dev/null and b/meme_generator/memes/jiujiu/images/0.png differ diff --git a/meme_generator/memes/jiujiu/images/1.png b/meme_generator/memes/jiujiu/images/1.png new file mode 100644 index 0000000000000000000000000000000000000000..a5b0d26b67f749e32df97542da5c2b776208c3e7 Binary files /dev/null and b/meme_generator/memes/jiujiu/images/1.png differ diff --git a/meme_generator/memes/jiujiu/images/2.png b/meme_generator/memes/jiujiu/images/2.png new file mode 100644 index 0000000000000000000000000000000000000000..1d3fa21c03a2bd1b1846febeb30aea035e054917 Binary files /dev/null and b/meme_generator/memes/jiujiu/images/2.png differ diff --git a/meme_generator/memes/jiujiu/images/3.png b/meme_generator/memes/jiujiu/images/3.png new file mode 100644 index 0000000000000000000000000000000000000000..5d65d0f2e734a1e04d23f02894570041c3fe5b8a Binary files /dev/null and b/meme_generator/memes/jiujiu/images/3.png differ diff --git a/meme_generator/memes/jiujiu/images/4.png b/meme_generator/memes/jiujiu/images/4.png new file mode 100644 index 0000000000000000000000000000000000000000..3191d8765dad67eb8d8b7b424b98bc7172ffbf5e Binary files /dev/null and b/meme_generator/memes/jiujiu/images/4.png differ diff --git a/meme_generator/memes/jiujiu/images/5.png b/meme_generator/memes/jiujiu/images/5.png new file mode 100644 index 0000000000000000000000000000000000000000..0b19475aedadf54eb0e0f18e728a7c9f30ecd730 Binary files /dev/null and b/meme_generator/memes/jiujiu/images/5.png differ diff --git a/meme_generator/memes/jiujiu/images/6.png b/meme_generator/memes/jiujiu/images/6.png new file mode 100644 index 0000000000000000000000000000000000000000..566db45ef77c34efd14efa7df2730d9fd94ef18c Binary files /dev/null and b/meme_generator/memes/jiujiu/images/6.png differ diff --git a/meme_generator/memes/jiujiu/images/7.png b/meme_generator/memes/jiujiu/images/7.png new file mode 100644 index 0000000000000000000000000000000000000000..00675452d09d18710c6eda0c520796020601699f Binary files /dev/null and b/meme_generator/memes/jiujiu/images/7.png differ diff --git a/meme_generator/memes/kaleidoscope/__init__.py b/meme_generator/memes/kaleidoscope/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..d88c48f959b6651b66319ea7a58c7add927f0b2c --- /dev/null +++ b/meme_generator/memes/kaleidoscope/__init__.py @@ -0,0 +1,56 @@ +import math +from typing import List + +from pil_utils import BuildImage +from pydantic import Field + +from meme_generator import MemeArgsModel, MemeArgsParser, MemeArgsType, add_meme +from meme_generator.utils import make_jpg_or_gif + +help = "是否将图片变为圆形" + +parser = MemeArgsParser(prefix_chars="-/") +parser.add_argument("--circle", "/圆", action="store_true", help=help) + + +class Model(MemeArgsModel): + circle: bool = Field(False, description=help) + + +def kaleidoscope(images: List[BuildImage], texts, args: Model): + def make(img: BuildImage) -> BuildImage: + circle_num = 10 + img_per_circle = 4 + init_angle = 0 + angle_step = 360 / img_per_circle + radius = lambda n: n * 50 + 100 + cx = cy = radius(circle_num) + + img = img.convert("RGBA") + frame = BuildImage.new("RGBA", (cx * 2, cy * 2), "white") + for i in range(circle_num): + r = radius(i) + img_w = i * 35 + 100 + im = img.resize_width(img_w) + if args.circle: + im = im.circle() + for j in range(img_per_circle): + angle = init_angle + angle_step * j + im_rot = im.rotate(angle - 90, expand=True) + x = round(cx + r * math.cos(math.radians(angle)) - im_rot.width / 2) + y = round(cy - r * math.sin(math.radians(angle)) - im_rot.height / 2) + frame.paste(im_rot, (x, y), alpha=True) + init_angle += angle_step / 2 + return frame + + return make_jpg_or_gif(images[0], make) + + +add_meme( + "kaleidoscope", + kaleidoscope, + min_images=1, + max_images=1, + args_type=MemeArgsType(parser, Model, [Model(circle=False), Model(circle=True)]), + keywords=["万花筒", "万花镜"], +) diff --git a/meme_generator/memes/karyl_point/__init__.py b/meme_generator/memes/karyl_point/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..5824b41f72d42ebe1bb8fb4533a68fec839ea3b7 --- /dev/null +++ b/meme_generator/memes/karyl_point/__init__.py @@ -0,0 +1,18 @@ +from pathlib import Path +from typing import List + +from pil_utils import BuildImage + +from meme_generator import add_meme + +img_dir = Path(__file__).parent / "images" + + +def karyl_point(images: List[BuildImage], texts, args): + img = images[0].convert("RGBA").rotate(7.5, expand=True).resize((225, 225)) + frame = BuildImage.open(img_dir / "0.png") + frame.paste(img, (87, 790), alpha=True) + return frame.save_png() + + +add_meme("karyl_point", karyl_point, min_images=1, max_images=1, keywords=["凯露指"]) diff --git a/meme_generator/memes/karyl_point/images/0.png b/meme_generator/memes/karyl_point/images/0.png new file mode 100644 index 0000000000000000000000000000000000000000..dd4f779fb11beb9464c56e8a9db5d3af4851beff Binary files /dev/null and b/meme_generator/memes/karyl_point/images/0.png differ diff --git a/meme_generator/memes/keep_away/__init__.py b/meme_generator/memes/keep_away/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..ed47b292dd9d1b70d4f248a65e394c21e03d3c88 --- /dev/null +++ b/meme_generator/memes/keep_away/__init__.py @@ -0,0 +1,47 @@ +from typing import List + +from PIL.Image import Transpose +from pil_utils import BuildImage + +from meme_generator import add_meme + + +def keep_away(images: List[BuildImage], texts: List[str], args): + def trans(img: BuildImage, n: int) -> BuildImage: + img = img.convert("RGBA").square().resize((100, 100)) + if n < 4: + return img.rotate(n * 90) + else: + return img.transpose(Transpose.FLIP_LEFT_RIGHT).rotate((n - 4) * 90) + + def paste(img: BuildImage): + nonlocal count + y = 90 if count < 4 else 190 + frame.paste(img, ((count % 4) * 100, y)) + count += 1 + + text = texts[0] if texts else "如何提高社交质量 : \n远离以下头像的人" + frame = BuildImage.new("RGB", (400, 290), "white") + frame.draw_text((10, 10, 390, 80), text, max_fontsize=40, halign="left") + count = 0 + num_per_user = 8 // len(images) + for image in images: + for n in range(num_per_user): + paste(trans(image, n)) + num_left = 8 - num_per_user * len(images) + for n in range(num_left): + paste(trans(images[-1], n + num_per_user)) + + return frame.save_jpg() + + +add_meme( + "keep_away", + keep_away, + min_images=1, + max_images=8, + min_texts=0, + max_texts=1, + default_texts=["如何提高社交质量 : \n远离以下头像的人"], + keywords=["远离"], +) diff --git a/meme_generator/memes/kick_ball/__init__.py b/meme_generator/memes/kick_ball/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..47fec9f3f8e50c733e6cd22084146d94325fe0b5 --- /dev/null +++ b/meme_generator/memes/kick_ball/__init__.py @@ -0,0 +1,30 @@ +from pathlib import Path +from typing import List + +from PIL.Image import Image as IMG +from pil_utils import BuildImage + +from meme_generator import add_meme +from meme_generator.utils import save_gif + +img_dir = Path(__file__).parent / "images" + + +def kick_ball(images: List[BuildImage], texts, args): + img = images[0].convert("RGBA").square().resize((78, 78)) + # fmt: off + locs = [ + (57, 136), (56, 117), (55, 99), (52, 113), (50, 126), + (48, 139), (47, 112), (47, 85), (47, 57), (48, 97), + (50, 136), (51, 176), (52, 169), (55, 181), (58, 153) + ] + # fmt: on + frames: List[IMG] = [] + for i in range(15): + frame = BuildImage.open(img_dir / f"{i}.png") + frame.paste(img.rotate(-24 * i), locs[i], below=True) + frames.append(frame.image) + return save_gif(frames, 0.1) + + +add_meme("kick_ball", kick_ball, min_images=1, max_images=1, keywords=["踢球"]) diff --git a/meme_generator/memes/kick_ball/images/0.png b/meme_generator/memes/kick_ball/images/0.png new file mode 100644 index 0000000000000000000000000000000000000000..519ef693a08c7f0dc2c8dfc3cddd422d4dc0b0f5 Binary files /dev/null and b/meme_generator/memes/kick_ball/images/0.png differ diff --git a/meme_generator/memes/kick_ball/images/1.png b/meme_generator/memes/kick_ball/images/1.png new file mode 100644 index 0000000000000000000000000000000000000000..1a4f3748bdc6934e5f2d4ee9f2e2484367433268 Binary files /dev/null and b/meme_generator/memes/kick_ball/images/1.png differ diff --git a/meme_generator/memes/kick_ball/images/10.png b/meme_generator/memes/kick_ball/images/10.png new file mode 100644 index 0000000000000000000000000000000000000000..1d573b7e2f71846aa0b16d28444ed83285fc848f Binary files /dev/null and b/meme_generator/memes/kick_ball/images/10.png differ diff --git a/meme_generator/memes/kick_ball/images/11.png b/meme_generator/memes/kick_ball/images/11.png new file mode 100644 index 0000000000000000000000000000000000000000..dd74d9b519fa444c0e9b174d7d93bb21330b3aca Binary files /dev/null and b/meme_generator/memes/kick_ball/images/11.png differ diff --git a/meme_generator/memes/kick_ball/images/12.png b/meme_generator/memes/kick_ball/images/12.png new file mode 100644 index 0000000000000000000000000000000000000000..4343aa62df6fb73e7e699fa7e1e168039a0589f2 Binary files /dev/null and b/meme_generator/memes/kick_ball/images/12.png differ diff --git a/meme_generator/memes/kick_ball/images/13.png b/meme_generator/memes/kick_ball/images/13.png new file mode 100644 index 0000000000000000000000000000000000000000..5b0ee62e21c3f82bb03b1737eeb90af724f1e7e4 Binary files /dev/null and b/meme_generator/memes/kick_ball/images/13.png differ diff --git a/meme_generator/memes/kick_ball/images/14.png b/meme_generator/memes/kick_ball/images/14.png new file mode 100644 index 0000000000000000000000000000000000000000..b912b66ea052d66bd6dd092cc83b14f0417f2ef9 Binary files /dev/null and b/meme_generator/memes/kick_ball/images/14.png differ diff --git a/meme_generator/memes/kick_ball/images/2.png b/meme_generator/memes/kick_ball/images/2.png new file mode 100644 index 0000000000000000000000000000000000000000..8c80b5f9a82abf442056bbbd29487019eabf5aa1 Binary files /dev/null and b/meme_generator/memes/kick_ball/images/2.png differ diff --git a/meme_generator/memes/kick_ball/images/3.png b/meme_generator/memes/kick_ball/images/3.png new file mode 100644 index 0000000000000000000000000000000000000000..50437d5fda5af431667579b337c4b5e83522cc88 Binary files /dev/null and b/meme_generator/memes/kick_ball/images/3.png differ diff --git a/meme_generator/memes/kick_ball/images/4.png b/meme_generator/memes/kick_ball/images/4.png new file mode 100644 index 0000000000000000000000000000000000000000..d3d3f286aafa3aafc22f1b160d923a39b5cb2bc3 Binary files /dev/null and b/meme_generator/memes/kick_ball/images/4.png differ diff --git a/meme_generator/memes/kick_ball/images/5.png b/meme_generator/memes/kick_ball/images/5.png new file mode 100644 index 0000000000000000000000000000000000000000..cdeb009f1156b7b79a0131de34ebf13e19b4fe1e Binary files /dev/null and b/meme_generator/memes/kick_ball/images/5.png differ diff --git a/meme_generator/memes/kick_ball/images/6.png b/meme_generator/memes/kick_ball/images/6.png new file mode 100644 index 0000000000000000000000000000000000000000..92e64335d06ff42d4e958843a7c83b0f399894ae Binary files /dev/null and b/meme_generator/memes/kick_ball/images/6.png differ diff --git a/meme_generator/memes/kick_ball/images/7.png b/meme_generator/memes/kick_ball/images/7.png new file mode 100644 index 0000000000000000000000000000000000000000..7a7ff712ca95a2cf39aa7506967444eef0555c20 Binary files /dev/null and b/meme_generator/memes/kick_ball/images/7.png differ diff --git a/meme_generator/memes/kick_ball/images/8.png b/meme_generator/memes/kick_ball/images/8.png new file mode 100644 index 0000000000000000000000000000000000000000..5ee31e41b260af56e7d13da4e5ae3ae1a3f09faf Binary files /dev/null and b/meme_generator/memes/kick_ball/images/8.png differ diff --git a/meme_generator/memes/kick_ball/images/9.png b/meme_generator/memes/kick_ball/images/9.png new file mode 100644 index 0000000000000000000000000000000000000000..5b7e5e2b9eac8bd8bdea9728d367aaa878efac7d Binary files /dev/null and b/meme_generator/memes/kick_ball/images/9.png differ diff --git a/meme_generator/memes/kirby_hammer/__init__.py b/meme_generator/memes/kirby_hammer/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..9bc850c0d9e7f143d86bd6371baceeede2bcdffc --- /dev/null +++ b/meme_generator/memes/kirby_hammer/__init__.py @@ -0,0 +1,64 @@ +from pathlib import Path +from typing import List + +from pil_utils import BuildImage +from pydantic import Field + +from meme_generator import MemeArgsModel, MemeArgsParser, MemeArgsType, add_meme +from meme_generator.utils import FrameAlignPolicy, Maker, make_gif_or_combined_gif + +help = "是否将图片变为圆形" + +img_dir = Path(__file__).parent / "images" + +parser = MemeArgsParser(prefix_chars="-/") +parser.add_argument("--circle", "/圆", action="store_true", help=help) + + +class Model(MemeArgsModel): + circle: bool = Field(False, description=help) + + +def kirby_hammer(images: List[BuildImage], texts, args: Model): + # fmt: off + positions = [ + (318, 163), (319, 173), (320, 183), (317, 193), (312, 199), + (297, 212), (289, 218), (280, 224), (278, 223), (278, 220), + (280, 215), (280, 213), (280, 210), (280, 206), (280, 201), + (280, 192), (280, 188), (280, 184), (280, 179) + ] + # fmt: on + def maker(i: int) -> Maker: + def make(img: BuildImage) -> BuildImage: + img = img.convert("RGBA") + if args.circle: + img = img.circle() + img = img.resize_height(80) + if img.width < 80: + img = img.resize((80, 80), keep_ratio=True) + frame = BuildImage.open(img_dir / f"{i}.png") + if i <= 18: + x, y = positions[i] + x = x + 40 - img.width // 2 + frame.paste(img, (x, y), alpha=True) + elif i <= 39: + x, y = positions[18] + x = x + 40 - img.width // 2 + frame.paste(img, (x, y), alpha=True) + return frame + + return make + + return make_gif_or_combined_gif( + images[0], maker, 62, 0.05, FrameAlignPolicy.extend_loop + ) + + +add_meme( + "kirby_hammer", + kirby_hammer, + min_images=1, + max_images=1, + args_type=MemeArgsType(parser, Model, [Model(circle=False), Model(circle=True)]), + keywords=["卡比锤", "卡比重锤"], +) diff --git a/meme_generator/memes/kirby_hammer/images/0.png b/meme_generator/memes/kirby_hammer/images/0.png new file mode 100644 index 0000000000000000000000000000000000000000..48e1edadb34de60392309aa5b0e28aff1ae7158f Binary files /dev/null and b/meme_generator/memes/kirby_hammer/images/0.png differ diff --git a/meme_generator/memes/kirby_hammer/images/1.png b/meme_generator/memes/kirby_hammer/images/1.png new file mode 100644 index 0000000000000000000000000000000000000000..30abd81f6ed8ebefcf28feb6b2dd859f2a8a0c70 Binary files /dev/null and b/meme_generator/memes/kirby_hammer/images/1.png differ diff --git a/meme_generator/memes/kirby_hammer/images/10.png b/meme_generator/memes/kirby_hammer/images/10.png new file mode 100644 index 0000000000000000000000000000000000000000..4d4842ea53b8deec546ec524c84fc06384028508 Binary files /dev/null and b/meme_generator/memes/kirby_hammer/images/10.png differ diff --git a/meme_generator/memes/kirby_hammer/images/11.png b/meme_generator/memes/kirby_hammer/images/11.png new file mode 100644 index 0000000000000000000000000000000000000000..30bc3e46a1f4fa325e2ecab5634bec92a2106703 Binary files /dev/null and b/meme_generator/memes/kirby_hammer/images/11.png differ diff --git a/meme_generator/memes/kirby_hammer/images/12.png b/meme_generator/memes/kirby_hammer/images/12.png new file mode 100644 index 0000000000000000000000000000000000000000..06b243f68aba02a78fcb718da294e46e08102a8a Binary files /dev/null and b/meme_generator/memes/kirby_hammer/images/12.png differ diff --git a/meme_generator/memes/kirby_hammer/images/13.png b/meme_generator/memes/kirby_hammer/images/13.png new file mode 100644 index 0000000000000000000000000000000000000000..3aed1f2f47791e26167201d4e4e6211ba2a4f866 Binary files /dev/null and b/meme_generator/memes/kirby_hammer/images/13.png differ diff --git a/meme_generator/memes/kirby_hammer/images/14.png b/meme_generator/memes/kirby_hammer/images/14.png new file mode 100644 index 0000000000000000000000000000000000000000..7e1844235b86d4e2a73cabfaf7c4d157fc9dc4ca Binary files /dev/null and b/meme_generator/memes/kirby_hammer/images/14.png differ diff --git a/meme_generator/memes/kirby_hammer/images/15.png b/meme_generator/memes/kirby_hammer/images/15.png new file mode 100644 index 0000000000000000000000000000000000000000..7e517de63fac4fe690afb89352e93b34702231fa Binary files /dev/null and b/meme_generator/memes/kirby_hammer/images/15.png differ diff --git a/meme_generator/memes/kirby_hammer/images/16.png b/meme_generator/memes/kirby_hammer/images/16.png new file mode 100644 index 0000000000000000000000000000000000000000..f896366d4f42874b1991e1f0d7ff087f74946176 Binary files /dev/null and b/meme_generator/memes/kirby_hammer/images/16.png differ diff --git a/meme_generator/memes/kirby_hammer/images/17.png b/meme_generator/memes/kirby_hammer/images/17.png new file mode 100644 index 0000000000000000000000000000000000000000..cce4aede56e64198e0fb3bee4ce088358bbccfba Binary files /dev/null and b/meme_generator/memes/kirby_hammer/images/17.png differ diff --git a/meme_generator/memes/kirby_hammer/images/18.png b/meme_generator/memes/kirby_hammer/images/18.png new file mode 100644 index 0000000000000000000000000000000000000000..8efec0afc6f8f450b2f3b7224b98e0e2ca400abf Binary files /dev/null and b/meme_generator/memes/kirby_hammer/images/18.png differ diff --git a/meme_generator/memes/kirby_hammer/images/19.png b/meme_generator/memes/kirby_hammer/images/19.png new file mode 100644 index 0000000000000000000000000000000000000000..c2ad618bf695801f29a55549203a37f5d151e8cd Binary files /dev/null and b/meme_generator/memes/kirby_hammer/images/19.png differ diff --git a/meme_generator/memes/kirby_hammer/images/2.png b/meme_generator/memes/kirby_hammer/images/2.png new file mode 100644 index 0000000000000000000000000000000000000000..94cf485a0680a60e20a0a7cc8ec9f3d67f00cc74 Binary files /dev/null and b/meme_generator/memes/kirby_hammer/images/2.png differ diff --git a/meme_generator/memes/kirby_hammer/images/20.png b/meme_generator/memes/kirby_hammer/images/20.png new file mode 100644 index 0000000000000000000000000000000000000000..f6d1f130dbbb10a08388b68efa5b51b1c6d421b0 Binary files /dev/null and b/meme_generator/memes/kirby_hammer/images/20.png differ diff --git a/meme_generator/memes/kirby_hammer/images/21.png b/meme_generator/memes/kirby_hammer/images/21.png new file mode 100644 index 0000000000000000000000000000000000000000..4e145a3f6f2f2e4454618e9859fb289d3c585e64 Binary files /dev/null and b/meme_generator/memes/kirby_hammer/images/21.png differ diff --git a/meme_generator/memes/kirby_hammer/images/22.png b/meme_generator/memes/kirby_hammer/images/22.png new file mode 100644 index 0000000000000000000000000000000000000000..02472281c8f4496c6b6e68667cc56941d927b481 Binary files /dev/null and b/meme_generator/memes/kirby_hammer/images/22.png differ diff --git a/meme_generator/memes/kirby_hammer/images/23.png b/meme_generator/memes/kirby_hammer/images/23.png new file mode 100644 index 0000000000000000000000000000000000000000..c8e197f0e6a6e0a1a27b00d9812fd4d8905ff497 Binary files /dev/null and b/meme_generator/memes/kirby_hammer/images/23.png differ diff --git a/meme_generator/memes/kirby_hammer/images/24.png b/meme_generator/memes/kirby_hammer/images/24.png new file mode 100644 index 0000000000000000000000000000000000000000..4089727512d33349906ed55c0acfd06533825777 Binary files /dev/null and b/meme_generator/memes/kirby_hammer/images/24.png differ diff --git a/meme_generator/memes/kirby_hammer/images/25.png b/meme_generator/memes/kirby_hammer/images/25.png new file mode 100644 index 0000000000000000000000000000000000000000..df665c5fa663e3873ad7fa6cf1e7f47effb5af72 Binary files /dev/null and b/meme_generator/memes/kirby_hammer/images/25.png differ diff --git a/meme_generator/memes/kirby_hammer/images/26.png b/meme_generator/memes/kirby_hammer/images/26.png new file mode 100644 index 0000000000000000000000000000000000000000..bff77e464132acedf7d555ddb8b4f22390e7a1d1 Binary files /dev/null and b/meme_generator/memes/kirby_hammer/images/26.png differ diff --git a/meme_generator/memes/kirby_hammer/images/27.png b/meme_generator/memes/kirby_hammer/images/27.png new file mode 100644 index 0000000000000000000000000000000000000000..fe3328cc5f865a8fb863c39d79d62afe3e34f559 Binary files /dev/null and b/meme_generator/memes/kirby_hammer/images/27.png differ diff --git a/meme_generator/memes/kirby_hammer/images/28.png b/meme_generator/memes/kirby_hammer/images/28.png new file mode 100644 index 0000000000000000000000000000000000000000..3674eb34709ce4ab83a7b41826277d742b2b6693 Binary files /dev/null and b/meme_generator/memes/kirby_hammer/images/28.png differ diff --git a/meme_generator/memes/kirby_hammer/images/29.png b/meme_generator/memes/kirby_hammer/images/29.png new file mode 100644 index 0000000000000000000000000000000000000000..4908e5986c85aeb768984b2164d4beb5921b0aef Binary files /dev/null and b/meme_generator/memes/kirby_hammer/images/29.png differ diff --git a/meme_generator/memes/kirby_hammer/images/3.png b/meme_generator/memes/kirby_hammer/images/3.png new file mode 100644 index 0000000000000000000000000000000000000000..3e706cc0593862ea38150fb890ecdbb3adb401f2 Binary files /dev/null and b/meme_generator/memes/kirby_hammer/images/3.png differ diff --git a/meme_generator/memes/kirby_hammer/images/30.png b/meme_generator/memes/kirby_hammer/images/30.png new file mode 100644 index 0000000000000000000000000000000000000000..1a8f72e8e21eb3cac961367594695f37e7990066 Binary files /dev/null and b/meme_generator/memes/kirby_hammer/images/30.png differ diff --git a/meme_generator/memes/kirby_hammer/images/31.png b/meme_generator/memes/kirby_hammer/images/31.png new file mode 100644 index 0000000000000000000000000000000000000000..9885e204fa69cd0538faf3177cd7e103140a0cd0 Binary files /dev/null and b/meme_generator/memes/kirby_hammer/images/31.png differ diff --git a/meme_generator/memes/kirby_hammer/images/32.png b/meme_generator/memes/kirby_hammer/images/32.png new file mode 100644 index 0000000000000000000000000000000000000000..2c9e957ee53e9832ab201d29357b0909bceec199 Binary files /dev/null and b/meme_generator/memes/kirby_hammer/images/32.png differ diff --git a/meme_generator/memes/kirby_hammer/images/33.png b/meme_generator/memes/kirby_hammer/images/33.png new file mode 100644 index 0000000000000000000000000000000000000000..76c158d5d3c8bed56593344a9c5223872574931f Binary files /dev/null and b/meme_generator/memes/kirby_hammer/images/33.png differ diff --git a/meme_generator/memes/kirby_hammer/images/34.png b/meme_generator/memes/kirby_hammer/images/34.png new file mode 100644 index 0000000000000000000000000000000000000000..855ffd955e28e6b0b0212c123617c45390db034e Binary files /dev/null and b/meme_generator/memes/kirby_hammer/images/34.png differ diff --git a/meme_generator/memes/kirby_hammer/images/35.png b/meme_generator/memes/kirby_hammer/images/35.png new file mode 100644 index 0000000000000000000000000000000000000000..97fb03d54f0094a95bcf253f83aa528970c1d204 Binary files /dev/null and b/meme_generator/memes/kirby_hammer/images/35.png differ diff --git a/meme_generator/memes/kirby_hammer/images/36.png b/meme_generator/memes/kirby_hammer/images/36.png new file mode 100644 index 0000000000000000000000000000000000000000..f57b3254553e3346373cc09c59dc30c54d83aec4 Binary files /dev/null and b/meme_generator/memes/kirby_hammer/images/36.png differ diff --git a/meme_generator/memes/kirby_hammer/images/37.png b/meme_generator/memes/kirby_hammer/images/37.png new file mode 100644 index 0000000000000000000000000000000000000000..d54e1a80ab681528c4f2d0e0a53d7f33c835b4e1 Binary files /dev/null and b/meme_generator/memes/kirby_hammer/images/37.png differ diff --git a/meme_generator/memes/kirby_hammer/images/38.png b/meme_generator/memes/kirby_hammer/images/38.png new file mode 100644 index 0000000000000000000000000000000000000000..c7ea05e198cb74bc5bc36bad77ac248bd3e551f7 Binary files /dev/null and b/meme_generator/memes/kirby_hammer/images/38.png differ diff --git a/meme_generator/memes/kirby_hammer/images/39.png b/meme_generator/memes/kirby_hammer/images/39.png new file mode 100644 index 0000000000000000000000000000000000000000..ec4cd17d6b8db48f5e453268b440bc969c6de3ad Binary files /dev/null and b/meme_generator/memes/kirby_hammer/images/39.png differ diff --git a/meme_generator/memes/kirby_hammer/images/4.png b/meme_generator/memes/kirby_hammer/images/4.png new file mode 100644 index 0000000000000000000000000000000000000000..d15a58fd3ab2e7e5497c83fff0b8ec7b5ed51c9f Binary files /dev/null and b/meme_generator/memes/kirby_hammer/images/4.png differ diff --git a/meme_generator/memes/kirby_hammer/images/40.png b/meme_generator/memes/kirby_hammer/images/40.png new file mode 100644 index 0000000000000000000000000000000000000000..2f3173facbe6ec77d723fa1715f1765070e46ab4 Binary files /dev/null and b/meme_generator/memes/kirby_hammer/images/40.png differ diff --git a/meme_generator/memes/kirby_hammer/images/41.png b/meme_generator/memes/kirby_hammer/images/41.png new file mode 100644 index 0000000000000000000000000000000000000000..4b5d071fd5b7251407ab22aa0d7adbea307fea8f Binary files /dev/null and b/meme_generator/memes/kirby_hammer/images/41.png differ diff --git a/meme_generator/memes/kirby_hammer/images/42.png b/meme_generator/memes/kirby_hammer/images/42.png new file mode 100644 index 0000000000000000000000000000000000000000..f63ef0488d9010273707bdc2fd150f5fd0bf737e Binary files /dev/null and b/meme_generator/memes/kirby_hammer/images/42.png differ diff --git a/meme_generator/memes/kirby_hammer/images/43.png b/meme_generator/memes/kirby_hammer/images/43.png new file mode 100644 index 0000000000000000000000000000000000000000..294624ca41b8c6565fd7cb84b2f035aeeaf44721 Binary files /dev/null and b/meme_generator/memes/kirby_hammer/images/43.png differ diff --git a/meme_generator/memes/kirby_hammer/images/44.png b/meme_generator/memes/kirby_hammer/images/44.png new file mode 100644 index 0000000000000000000000000000000000000000..8a82bb863b75c69dee12637f9457c969fee436e6 Binary files /dev/null and b/meme_generator/memes/kirby_hammer/images/44.png differ diff --git a/meme_generator/memes/kirby_hammer/images/45.png b/meme_generator/memes/kirby_hammer/images/45.png new file mode 100644 index 0000000000000000000000000000000000000000..02e4c5002ddf414e8cafb9ee0212597a920bb6b9 Binary files /dev/null and b/meme_generator/memes/kirby_hammer/images/45.png differ diff --git a/meme_generator/memes/kirby_hammer/images/46.png b/meme_generator/memes/kirby_hammer/images/46.png new file mode 100644 index 0000000000000000000000000000000000000000..24adae3831d9e5caedfec8ffb9fefb411488c93f Binary files /dev/null and b/meme_generator/memes/kirby_hammer/images/46.png differ diff --git a/meme_generator/memes/kirby_hammer/images/47.png b/meme_generator/memes/kirby_hammer/images/47.png new file mode 100644 index 0000000000000000000000000000000000000000..6a03c0732d91561158ce46c30ccee892e93de9ad Binary files /dev/null and b/meme_generator/memes/kirby_hammer/images/47.png differ diff --git a/meme_generator/memes/kirby_hammer/images/48.png b/meme_generator/memes/kirby_hammer/images/48.png new file mode 100644 index 0000000000000000000000000000000000000000..f5e657212a133ddd4a7b55cc7f66585a03f6b5a3 Binary files /dev/null and b/meme_generator/memes/kirby_hammer/images/48.png differ diff --git a/meme_generator/memes/kirby_hammer/images/49.png b/meme_generator/memes/kirby_hammer/images/49.png new file mode 100644 index 0000000000000000000000000000000000000000..84ff9a24081b503530dc5430c84f818c3051df90 Binary files /dev/null and b/meme_generator/memes/kirby_hammer/images/49.png differ diff --git a/meme_generator/memes/kirby_hammer/images/5.png b/meme_generator/memes/kirby_hammer/images/5.png new file mode 100644 index 0000000000000000000000000000000000000000..6627785b956e7cf4a7f61c6141bec344fa6f34e9 Binary files /dev/null and b/meme_generator/memes/kirby_hammer/images/5.png differ diff --git a/meme_generator/memes/kirby_hammer/images/50.png b/meme_generator/memes/kirby_hammer/images/50.png new file mode 100644 index 0000000000000000000000000000000000000000..b312b5d19734713405030f304403aa914e978441 Binary files /dev/null and b/meme_generator/memes/kirby_hammer/images/50.png differ diff --git a/meme_generator/memes/kirby_hammer/images/51.png b/meme_generator/memes/kirby_hammer/images/51.png new file mode 100644 index 0000000000000000000000000000000000000000..67affe7fe25ea38b4734de74b0d0e50d0476e06a Binary files /dev/null and b/meme_generator/memes/kirby_hammer/images/51.png differ diff --git a/meme_generator/memes/kirby_hammer/images/52.png b/meme_generator/memes/kirby_hammer/images/52.png new file mode 100644 index 0000000000000000000000000000000000000000..9570ef3d957881e6501add876478472dbfacb061 Binary files /dev/null and b/meme_generator/memes/kirby_hammer/images/52.png differ diff --git a/meme_generator/memes/kirby_hammer/images/53.png b/meme_generator/memes/kirby_hammer/images/53.png new file mode 100644 index 0000000000000000000000000000000000000000..9cb3971873649719dbad40b9d8df65d7ee209640 Binary files /dev/null and b/meme_generator/memes/kirby_hammer/images/53.png differ diff --git a/meme_generator/memes/kirby_hammer/images/54.png b/meme_generator/memes/kirby_hammer/images/54.png new file mode 100644 index 0000000000000000000000000000000000000000..d30979190daa9b5e401df794254fb050cbe492be Binary files /dev/null and b/meme_generator/memes/kirby_hammer/images/54.png differ diff --git a/meme_generator/memes/kirby_hammer/images/55.png b/meme_generator/memes/kirby_hammer/images/55.png new file mode 100644 index 0000000000000000000000000000000000000000..d6e6fa2f74947c4b02272f17629dede4bd8f1e58 Binary files /dev/null and b/meme_generator/memes/kirby_hammer/images/55.png differ diff --git a/meme_generator/memes/kirby_hammer/images/56.png b/meme_generator/memes/kirby_hammer/images/56.png new file mode 100644 index 0000000000000000000000000000000000000000..4adf4076813f2cae420886f44a1a8b8b85758b4b Binary files /dev/null and b/meme_generator/memes/kirby_hammer/images/56.png differ diff --git a/meme_generator/memes/kirby_hammer/images/57.png b/meme_generator/memes/kirby_hammer/images/57.png new file mode 100644 index 0000000000000000000000000000000000000000..fcf70b6703593819b5f588c2562967a4c895d68f Binary files /dev/null and b/meme_generator/memes/kirby_hammer/images/57.png differ diff --git a/meme_generator/memes/kirby_hammer/images/58.png b/meme_generator/memes/kirby_hammer/images/58.png new file mode 100644 index 0000000000000000000000000000000000000000..1f1d36eb488f880fe86d1c37adc7a8a8920db03f Binary files /dev/null and b/meme_generator/memes/kirby_hammer/images/58.png differ diff --git a/meme_generator/memes/kirby_hammer/images/59.png b/meme_generator/memes/kirby_hammer/images/59.png new file mode 100644 index 0000000000000000000000000000000000000000..26879789b3f1a34f6c66d9f8fc7c5f7116f510b9 Binary files /dev/null and b/meme_generator/memes/kirby_hammer/images/59.png differ diff --git a/meme_generator/memes/kirby_hammer/images/6.png b/meme_generator/memes/kirby_hammer/images/6.png new file mode 100644 index 0000000000000000000000000000000000000000..bcb05fb1059d2bb0b5c6dda0977b3c64797bc20d Binary files /dev/null and b/meme_generator/memes/kirby_hammer/images/6.png differ diff --git a/meme_generator/memes/kirby_hammer/images/60.png b/meme_generator/memes/kirby_hammer/images/60.png new file mode 100644 index 0000000000000000000000000000000000000000..3abe4e061ff7ccaba742faad1a733c93bab6867a Binary files /dev/null and b/meme_generator/memes/kirby_hammer/images/60.png differ diff --git a/meme_generator/memes/kirby_hammer/images/61.png b/meme_generator/memes/kirby_hammer/images/61.png new file mode 100644 index 0000000000000000000000000000000000000000..39ea9f46f488c11aaa37ea8c6b1750a8ad793bdc Binary files /dev/null and b/meme_generator/memes/kirby_hammer/images/61.png differ diff --git a/meme_generator/memes/kirby_hammer/images/7.png b/meme_generator/memes/kirby_hammer/images/7.png new file mode 100644 index 0000000000000000000000000000000000000000..16a395b58d41e9e8a663354535a8567a2839f1e1 Binary files /dev/null and b/meme_generator/memes/kirby_hammer/images/7.png differ diff --git a/meme_generator/memes/kirby_hammer/images/8.png b/meme_generator/memes/kirby_hammer/images/8.png new file mode 100644 index 0000000000000000000000000000000000000000..028c7fd17b0c624ece08fb8df6af3ba51bc294f9 Binary files /dev/null and b/meme_generator/memes/kirby_hammer/images/8.png differ diff --git a/meme_generator/memes/kirby_hammer/images/9.png b/meme_generator/memes/kirby_hammer/images/9.png new file mode 100644 index 0000000000000000000000000000000000000000..8e5e596b0ce67173f062af05d87aa2ac1f7066bd Binary files /dev/null and b/meme_generator/memes/kirby_hammer/images/9.png differ diff --git a/meme_generator/memes/kiss/__init__.py b/meme_generator/memes/kiss/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..d3263c16353c59490d0506b6f476d985cda3710a --- /dev/null +++ b/meme_generator/memes/kiss/__init__.py @@ -0,0 +1,35 @@ +from pathlib import Path +from typing import List + +from PIL.Image import Image as IMG +from pil_utils import BuildImage + +from meme_generator import add_meme +from meme_generator.utils import save_gif + +img_dir = Path(__file__).parent / "images" + + +def kiss(images: List[BuildImage], texts, args): + self_head = images[0].convert("RGBA").circle().resize((40, 40)) + user_head = images[1].convert("RGBA").circle().resize((50, 50)) + # fmt: off + user_locs = [ + (58, 90), (62, 95), (42, 100), (50, 100), (56, 100), (18, 120), (28, 110), + (54, 100), (46, 100), (60, 100), (35, 115), (20, 120), (40, 96) + ] + self_locs = [ + (92, 64), (135, 40), (84, 105), (80, 110), (155, 82), (60, 96), (50, 80), + (98, 55), (35, 65), (38, 100), (70, 80), (84, 65), (75, 65) + ] + # fmt: on + frames: List[IMG] = [] + for i in range(13): + frame = BuildImage.open(img_dir / f"{i}.png") + frame.paste(user_head, user_locs[i], alpha=True) + frame.paste(self_head, self_locs[i], alpha=True) + frames.append(frame.image) + return save_gif(frames, 0.05) + + +add_meme("kiss", kiss, min_images=2, max_images=2, keywords=["亲", "亲亲"]) diff --git a/meme_generator/memes/kiss/images/0.png b/meme_generator/memes/kiss/images/0.png new file mode 100644 index 0000000000000000000000000000000000000000..7d6e3c14e66859019844cce03d76a76521363d79 Binary files /dev/null and b/meme_generator/memes/kiss/images/0.png differ diff --git a/meme_generator/memes/kiss/images/1.png b/meme_generator/memes/kiss/images/1.png new file mode 100644 index 0000000000000000000000000000000000000000..11a00de22bf9f47fbfc33dc84b0dc3e5401715f3 Binary files /dev/null and b/meme_generator/memes/kiss/images/1.png differ diff --git a/meme_generator/memes/kiss/images/10.png b/meme_generator/memes/kiss/images/10.png new file mode 100644 index 0000000000000000000000000000000000000000..35aad7016dc27aa57500df3eecc76ef7bb956037 Binary files /dev/null and b/meme_generator/memes/kiss/images/10.png differ diff --git a/meme_generator/memes/kiss/images/11.png b/meme_generator/memes/kiss/images/11.png new file mode 100644 index 0000000000000000000000000000000000000000..751afe37f7fc1252a5f573239e7559ec0f30aae8 Binary files /dev/null and b/meme_generator/memes/kiss/images/11.png differ diff --git a/meme_generator/memes/kiss/images/12.png b/meme_generator/memes/kiss/images/12.png new file mode 100644 index 0000000000000000000000000000000000000000..a83295044ad1369a623b8e88d036213a40c64d53 Binary files /dev/null and b/meme_generator/memes/kiss/images/12.png differ diff --git a/meme_generator/memes/kiss/images/2.png b/meme_generator/memes/kiss/images/2.png new file mode 100644 index 0000000000000000000000000000000000000000..a1c42322cc4a8a963643da1c1cf8acf881e39f04 Binary files /dev/null and b/meme_generator/memes/kiss/images/2.png differ diff --git a/meme_generator/memes/kiss/images/3.png b/meme_generator/memes/kiss/images/3.png new file mode 100644 index 0000000000000000000000000000000000000000..aa53f0eb1215883bb858a5c8aa55220c6933154e Binary files /dev/null and b/meme_generator/memes/kiss/images/3.png differ diff --git a/meme_generator/memes/kiss/images/4.png b/meme_generator/memes/kiss/images/4.png new file mode 100644 index 0000000000000000000000000000000000000000..a1e73582b8a8e85a426e145bceeba72abf6b5b7c Binary files /dev/null and b/meme_generator/memes/kiss/images/4.png differ diff --git a/meme_generator/memes/kiss/images/5.png b/meme_generator/memes/kiss/images/5.png new file mode 100644 index 0000000000000000000000000000000000000000..e9585db62f3e5132effe1da16a70117128c05b7b Binary files /dev/null and b/meme_generator/memes/kiss/images/5.png differ diff --git a/meme_generator/memes/kiss/images/6.png b/meme_generator/memes/kiss/images/6.png new file mode 100644 index 0000000000000000000000000000000000000000..789ae6593e76601b72d8a8ce310be9286e27a7dd Binary files /dev/null and b/meme_generator/memes/kiss/images/6.png differ diff --git a/meme_generator/memes/kiss/images/7.png b/meme_generator/memes/kiss/images/7.png new file mode 100644 index 0000000000000000000000000000000000000000..f477d684597fa1a77747623dd20ba085475dfea8 Binary files /dev/null and b/meme_generator/memes/kiss/images/7.png differ diff --git a/meme_generator/memes/kiss/images/8.png b/meme_generator/memes/kiss/images/8.png new file mode 100644 index 0000000000000000000000000000000000000000..231761e8f7cb41c35d26393715f947aab0fa7cf8 Binary files /dev/null and b/meme_generator/memes/kiss/images/8.png differ diff --git a/meme_generator/memes/kiss/images/9.png b/meme_generator/memes/kiss/images/9.png new file mode 100644 index 0000000000000000000000000000000000000000..bcd2a7cc3517a95bb8ed2ac0b446b1b0e5d38bb3 Binary files /dev/null and b/meme_generator/memes/kiss/images/9.png differ diff --git a/meme_generator/memes/klee_eat/__init__.py b/meme_generator/memes/klee_eat/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..b34429b01d14f77e3f76a7a33bbed1438ca5ae21 --- /dev/null +++ b/meme_generator/memes/klee_eat/__init__.py @@ -0,0 +1,33 @@ +from pathlib import Path +from typing import List + +from PIL.Image import Image as IMG +from pil_utils import BuildImage + +from meme_generator import add_meme +from meme_generator.utils import save_gif + +img_dir = Path(__file__).parent / "images" + + +def klee_eat(images: List[BuildImage], texts, args): + img = images[0].convert("RGBA").square().resize((83, 83)) + # fmt: off + locs = [ + (0, 174), (0, 174), (0, 174), (0, 174), (0, 174), + (12, 160), (19, 152), (23, 148), (26, 145), (32, 140), + (37, 136), (42, 131), (49, 127), (70, 126), (88, 128), + (-30, 210), (-19, 207), (-14, 200), (-10, 188), (-7, 179), + (-3, 170), (-3, 175), (-1, 174), (0, 174), (0, 174), + (0, 174), (0, 174), (0, 174), (0, 174), (0, 174), (0, 174) + ] + # fmt: on + frames: List[IMG] = [] + for i in range(31): + frame = BuildImage.open(img_dir / f"{i}.png") + frame.paste(img, locs[i], below=True) + frames.append(frame.image) + return save_gif(frames, 0.1) + + +add_meme("klee_eat", klee_eat, min_images=1, max_images=1, keywords=["可莉吃"]) diff --git a/meme_generator/memes/klee_eat/images/0.png b/meme_generator/memes/klee_eat/images/0.png new file mode 100644 index 0000000000000000000000000000000000000000..f9b7f315c6df797d8d6fdba100a1d8263bc643d1 Binary files /dev/null and b/meme_generator/memes/klee_eat/images/0.png differ diff --git a/meme_generator/memes/klee_eat/images/1.png b/meme_generator/memes/klee_eat/images/1.png new file mode 100644 index 0000000000000000000000000000000000000000..cd03c7a75ed2c1418ceb1452ff02bc2b994c66cf Binary files /dev/null and b/meme_generator/memes/klee_eat/images/1.png differ diff --git a/meme_generator/memes/klee_eat/images/10.png b/meme_generator/memes/klee_eat/images/10.png new file mode 100644 index 0000000000000000000000000000000000000000..a56d4eb7a94666a6d3c760fe893410bf0ec4a696 Binary files /dev/null and b/meme_generator/memes/klee_eat/images/10.png differ diff --git a/meme_generator/memes/klee_eat/images/11.png b/meme_generator/memes/klee_eat/images/11.png new file mode 100644 index 0000000000000000000000000000000000000000..0709b5ac8bf6b7450f21956b8e2db990bc4049d3 Binary files /dev/null and b/meme_generator/memes/klee_eat/images/11.png differ diff --git a/meme_generator/memes/klee_eat/images/12.png b/meme_generator/memes/klee_eat/images/12.png new file mode 100644 index 0000000000000000000000000000000000000000..04ebe1764969e1ec50d6f6b26faa63e7a35bce90 Binary files /dev/null and b/meme_generator/memes/klee_eat/images/12.png differ diff --git a/meme_generator/memes/klee_eat/images/13.png b/meme_generator/memes/klee_eat/images/13.png new file mode 100644 index 0000000000000000000000000000000000000000..60036d4a41ed2ee4674e2058e0d33e23982f5c6e Binary files /dev/null and b/meme_generator/memes/klee_eat/images/13.png differ diff --git a/meme_generator/memes/klee_eat/images/14.png b/meme_generator/memes/klee_eat/images/14.png new file mode 100644 index 0000000000000000000000000000000000000000..a67cf81ab7e516c0c35e597d45b93396d3d9965a Binary files /dev/null and b/meme_generator/memes/klee_eat/images/14.png differ diff --git a/meme_generator/memes/klee_eat/images/15.png b/meme_generator/memes/klee_eat/images/15.png new file mode 100644 index 0000000000000000000000000000000000000000..a33c182e6c63892bcca978e0802be818b72e500d Binary files /dev/null and b/meme_generator/memes/klee_eat/images/15.png differ diff --git a/meme_generator/memes/klee_eat/images/16.png b/meme_generator/memes/klee_eat/images/16.png new file mode 100644 index 0000000000000000000000000000000000000000..db94368defbbee78d8984da2a8ead2a2be8097b7 Binary files /dev/null and b/meme_generator/memes/klee_eat/images/16.png differ diff --git a/meme_generator/memes/klee_eat/images/17.png b/meme_generator/memes/klee_eat/images/17.png new file mode 100644 index 0000000000000000000000000000000000000000..2c4dc73f1a6f8ef95a279d6f25c0ca24f81b4e01 Binary files /dev/null and b/meme_generator/memes/klee_eat/images/17.png differ diff --git a/meme_generator/memes/klee_eat/images/18.png b/meme_generator/memes/klee_eat/images/18.png new file mode 100644 index 0000000000000000000000000000000000000000..b6f5eefb8a372aff049c9ef80676ab8f65816349 Binary files /dev/null and b/meme_generator/memes/klee_eat/images/18.png differ diff --git a/meme_generator/memes/klee_eat/images/19.png b/meme_generator/memes/klee_eat/images/19.png new file mode 100644 index 0000000000000000000000000000000000000000..581fa101436456ac94469a3fe400c6d10c452fc2 Binary files /dev/null and b/meme_generator/memes/klee_eat/images/19.png differ diff --git a/meme_generator/memes/klee_eat/images/2.png b/meme_generator/memes/klee_eat/images/2.png new file mode 100644 index 0000000000000000000000000000000000000000..2f0e19ea2630566249c8821e877634e83cfbdca2 Binary files /dev/null and b/meme_generator/memes/klee_eat/images/2.png differ diff --git a/meme_generator/memes/klee_eat/images/20.png b/meme_generator/memes/klee_eat/images/20.png new file mode 100644 index 0000000000000000000000000000000000000000..edbad5959042dd99cdb4a973ed873cb2827849e0 Binary files /dev/null and b/meme_generator/memes/klee_eat/images/20.png differ diff --git a/meme_generator/memes/klee_eat/images/21.png b/meme_generator/memes/klee_eat/images/21.png new file mode 100644 index 0000000000000000000000000000000000000000..3f725afa78856bcc1ee722dae1380582b64f97ad Binary files /dev/null and b/meme_generator/memes/klee_eat/images/21.png differ diff --git a/meme_generator/memes/klee_eat/images/22.png b/meme_generator/memes/klee_eat/images/22.png new file mode 100644 index 0000000000000000000000000000000000000000..08afa3c5f88a34fb74de7edcf5a12b30a3dd7067 Binary files /dev/null and b/meme_generator/memes/klee_eat/images/22.png differ diff --git a/meme_generator/memes/klee_eat/images/23.png b/meme_generator/memes/klee_eat/images/23.png new file mode 100644 index 0000000000000000000000000000000000000000..c248d3547098c900fb0e55197a8ae40ef2d500d7 Binary files /dev/null and b/meme_generator/memes/klee_eat/images/23.png differ diff --git a/meme_generator/memes/klee_eat/images/24.png b/meme_generator/memes/klee_eat/images/24.png new file mode 100644 index 0000000000000000000000000000000000000000..0a9f0054e6af214e49176a1d85093b55d727c683 Binary files /dev/null and b/meme_generator/memes/klee_eat/images/24.png differ diff --git a/meme_generator/memes/klee_eat/images/25.png b/meme_generator/memes/klee_eat/images/25.png new file mode 100644 index 0000000000000000000000000000000000000000..78ab9d6b100fd87d451c30ea8056b080288261f9 Binary files /dev/null and b/meme_generator/memes/klee_eat/images/25.png differ diff --git a/meme_generator/memes/klee_eat/images/26.png b/meme_generator/memes/klee_eat/images/26.png new file mode 100644 index 0000000000000000000000000000000000000000..77035e7bee5bd5ea80d0c8e74ef90cae37124b26 Binary files /dev/null and b/meme_generator/memes/klee_eat/images/26.png differ diff --git a/meme_generator/memes/klee_eat/images/27.png b/meme_generator/memes/klee_eat/images/27.png new file mode 100644 index 0000000000000000000000000000000000000000..f80a0867c6c2c457bb457e65e90e5c49078e0f62 Binary files /dev/null and b/meme_generator/memes/klee_eat/images/27.png differ diff --git a/meme_generator/memes/klee_eat/images/28.png b/meme_generator/memes/klee_eat/images/28.png new file mode 100644 index 0000000000000000000000000000000000000000..4b85e47f686e58ba71dbb139e79737d15ee08cc8 Binary files /dev/null and b/meme_generator/memes/klee_eat/images/28.png differ diff --git a/meme_generator/memes/klee_eat/images/29.png b/meme_generator/memes/klee_eat/images/29.png new file mode 100644 index 0000000000000000000000000000000000000000..8da3dfaab74d7378814e7ae300975b111a883f5e Binary files /dev/null and b/meme_generator/memes/klee_eat/images/29.png differ diff --git a/meme_generator/memes/klee_eat/images/3.png b/meme_generator/memes/klee_eat/images/3.png new file mode 100644 index 0000000000000000000000000000000000000000..703d7b4eb83fb60c54c1dff791e274efd727fe51 Binary files /dev/null and b/meme_generator/memes/klee_eat/images/3.png differ diff --git a/meme_generator/memes/klee_eat/images/30.png b/meme_generator/memes/klee_eat/images/30.png new file mode 100644 index 0000000000000000000000000000000000000000..885361da1e0679a3b4942c1713a1b2602ed9b21e Binary files /dev/null and b/meme_generator/memes/klee_eat/images/30.png differ diff --git a/meme_generator/memes/klee_eat/images/4.png b/meme_generator/memes/klee_eat/images/4.png new file mode 100644 index 0000000000000000000000000000000000000000..69e0c90d2c02ace1bf7353b15b3d085bb91432fc Binary files /dev/null and b/meme_generator/memes/klee_eat/images/4.png differ diff --git a/meme_generator/memes/klee_eat/images/5.png b/meme_generator/memes/klee_eat/images/5.png new file mode 100644 index 0000000000000000000000000000000000000000..2588e040f38d2a6c573882b6cc1be0012edf5be6 Binary files /dev/null and b/meme_generator/memes/klee_eat/images/5.png differ diff --git a/meme_generator/memes/klee_eat/images/6.png b/meme_generator/memes/klee_eat/images/6.png new file mode 100644 index 0000000000000000000000000000000000000000..cc95f08b6e993687d891b5100e5977a023d3f6b0 Binary files /dev/null and b/meme_generator/memes/klee_eat/images/6.png differ diff --git a/meme_generator/memes/klee_eat/images/7.png b/meme_generator/memes/klee_eat/images/7.png new file mode 100644 index 0000000000000000000000000000000000000000..49d13fac3a4d8b6007098cf7574449717cc4c735 Binary files /dev/null and b/meme_generator/memes/klee_eat/images/7.png differ diff --git a/meme_generator/memes/klee_eat/images/8.png b/meme_generator/memes/klee_eat/images/8.png new file mode 100644 index 0000000000000000000000000000000000000000..6df49fb2963d7e3683189d1a89ea10f9245afa59 Binary files /dev/null and b/meme_generator/memes/klee_eat/images/8.png differ diff --git a/meme_generator/memes/klee_eat/images/9.png b/meme_generator/memes/klee_eat/images/9.png new file mode 100644 index 0000000000000000000000000000000000000000..b5097fb54ed1edd7bab0b4f1d5a4182d6243c79f Binary files /dev/null and b/meme_generator/memes/klee_eat/images/9.png differ diff --git a/meme_generator/memes/knock/__init__.py b/meme_generator/memes/knock/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..0a44b37fc5563deea8406c6ddf109860a56e9485 --- /dev/null +++ b/meme_generator/memes/knock/__init__.py @@ -0,0 +1,28 @@ +from pathlib import Path +from typing import List + +from PIL.Image import Image as IMG +from pil_utils import BuildImage + +from meme_generator import add_meme +from meme_generator.utils import save_gif + +img_dir = Path(__file__).parent / "images" + + +def knock(images: List[BuildImage], texts, args): + img = images[0].convert("RGBA").square() + # fmt: off + locs = [(60, 308, 210, 195), (60, 308, 210, 198), (45, 330, 250, 172), (58, 320, 218, 180), + (60, 310, 215, 193), (40, 320, 250, 285), (48, 308, 226, 192), (51, 301, 223, 200)] + # fmt: on + frames: List[IMG] = [] + for i in range(8): + frame = BuildImage.open(img_dir / f"{i}.png") + x, y, w, h = locs[i] + frame.paste(img.resize((w, h)), (x, y), below=True) + frames.append(frame.image) + return save_gif(frames, 0.04) + + +add_meme("knock", knock, min_images=1, max_images=1, keywords=["敲"]) diff --git a/meme_generator/memes/knock/images/0.png b/meme_generator/memes/knock/images/0.png new file mode 100644 index 0000000000000000000000000000000000000000..7ae425d116b454d532b25dece43e113fbb1a5a8c Binary files /dev/null and b/meme_generator/memes/knock/images/0.png differ diff --git a/meme_generator/memes/knock/images/1.png b/meme_generator/memes/knock/images/1.png new file mode 100644 index 0000000000000000000000000000000000000000..aadfc693f3172a8f214c86d8fed188a6f93b5511 Binary files /dev/null and b/meme_generator/memes/knock/images/1.png differ diff --git a/meme_generator/memes/knock/images/2.png b/meme_generator/memes/knock/images/2.png new file mode 100644 index 0000000000000000000000000000000000000000..a6f31d07199a04e64cd7993bd088247ccf952b43 Binary files /dev/null and b/meme_generator/memes/knock/images/2.png differ diff --git a/meme_generator/memes/knock/images/3.png b/meme_generator/memes/knock/images/3.png new file mode 100644 index 0000000000000000000000000000000000000000..372723ed028e25c9084a2214e937e9999ec84cff Binary files /dev/null and b/meme_generator/memes/knock/images/3.png differ diff --git a/meme_generator/memes/knock/images/4.png b/meme_generator/memes/knock/images/4.png new file mode 100644 index 0000000000000000000000000000000000000000..ab9639d01ffdcabd913792ed02db04e3dae585bb Binary files /dev/null and b/meme_generator/memes/knock/images/4.png differ diff --git a/meme_generator/memes/knock/images/5.png b/meme_generator/memes/knock/images/5.png new file mode 100644 index 0000000000000000000000000000000000000000..9f39df622de911252b5909149548dc1f3e5da2f9 Binary files /dev/null and b/meme_generator/memes/knock/images/5.png differ diff --git a/meme_generator/memes/knock/images/6.png b/meme_generator/memes/knock/images/6.png new file mode 100644 index 0000000000000000000000000000000000000000..acb69d557cc2a4cb292bee98372407b4638cf2c5 Binary files /dev/null and b/meme_generator/memes/knock/images/6.png differ diff --git a/meme_generator/memes/knock/images/7.png b/meme_generator/memes/knock/images/7.png new file mode 100644 index 0000000000000000000000000000000000000000..4f576ca9005eb4dfab06987c7e54f54e142d85d2 Binary files /dev/null and b/meme_generator/memes/knock/images/7.png differ diff --git a/meme_generator/memes/learn/__init__.py b/meme_generator/memes/learn/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..a3c2fad58b5d39e8f781fc6bc6dac7d03f0c3377 --- /dev/null +++ b/meme_generator/memes/learn/__init__.py @@ -0,0 +1,43 @@ +from pathlib import Path +from typing import List + +from pil_utils import BuildImage + +from meme_generator import add_meme +from meme_generator.exception import TextOverLength +from meme_generator.utils import make_jpg_or_gif + +img_dir = Path(__file__).parent / "images" + + +def learn(images: List[BuildImage], texts: List[str], args): + text = texts[0] if texts else "偷学群友数理基础" + frame = BuildImage.open(img_dir / "0.png") + try: + frame.draw_text( + (100, 1360, frame.width - 100, 1730), + text, + max_fontsize=350, + min_fontsize=200, + weight="bold", + ) + except ValueError: + raise TextOverLength(text) + + def make(img: BuildImage) -> BuildImage: + img = img.convert("RGBA").resize((1751, 1347), keep_ratio=True) + return frame.copy().paste(img, (1440, 0), alpha=True) + + return make_jpg_or_gif(images[0], make) + + +add_meme( + "learn", + learn, + min_images=1, + max_images=1, + min_texts=0, + max_texts=1, + default_texts=["偷学群友数理基础"], + keywords=["偷学"], +) diff --git a/meme_generator/memes/learn/images/0.png b/meme_generator/memes/learn/images/0.png new file mode 100644 index 0000000000000000000000000000000000000000..ff8f8325eaa71cfa0abd90a45a8db9b16753fc96 Binary files /dev/null and b/meme_generator/memes/learn/images/0.png differ diff --git a/meme_generator/memes/lim_x_0/__init__.py b/meme_generator/memes/lim_x_0/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..0eafec0ed477caa4924ce8a4ee31b0f9732a522f --- /dev/null +++ b/meme_generator/memes/lim_x_0/__init__.py @@ -0,0 +1,35 @@ +from pathlib import Path +from typing import List + +from pil_utils import BuildImage + +from meme_generator import add_meme + +img_dir = Path(__file__).parent / "images" + + +def lim_x_0(images: List[BuildImage], texts, args): + img = images[0] + frame = BuildImage.open(img_dir / "0.png") + img_c = img.convert("RGBA").circle().resize((72, 72)) + img_tp = img.convert("RGBA").circle().resize((51, 51)) + frame.paste(img_tp, (948, 247), alpha=True) + # fmt: off + locs = [ + (143, 32), (155, 148), (334, 149), (275, 266), (486, 266), + (258, 383), (439, 382), (343, 539), (577, 487), (296, 717), + (535, 717), (64, 896), (340, 896), (578, 897), (210, 1038), + (644, 1039), (64, 1192), (460, 1192), (698, 1192), (1036, 141), + (1217, 141), (1243, 263), (1140, 378), (1321, 378), (929, 531), + (1325, 531), (1592, 531), (1007, 687), (1390, 687), (1631, 686), + (1036, 840), (1209, 839), (1447, 839), (1141, 1018), (1309, 1019), + (1546, 1019), (1037, 1197), (1317, 1198), (1555, 1197), + ] + # fmt: on + for i in range(39): + x, y = locs[i] + frame.paste(img_c, (x, y), alpha=True) + return frame.save_jpg() + + +add_meme("lim_x_0", lim_x_0, min_images=1, max_images=1, keywords=["等价无穷小"]) diff --git a/meme_generator/memes/lim_x_0/images/0.png b/meme_generator/memes/lim_x_0/images/0.png new file mode 100644 index 0000000000000000000000000000000000000000..335f99587cb17f2aff934c4bb75e9f903d07783d Binary files /dev/null and b/meme_generator/memes/lim_x_0/images/0.png differ diff --git a/meme_generator/memes/listen_music/__init__.py b/meme_generator/memes/listen_music/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..9e4f1de0954ad06afd641cc9ebe390170e0981db --- /dev/null +++ b/meme_generator/memes/listen_music/__init__.py @@ -0,0 +1,26 @@ +from pathlib import Path +from typing import List + +from PIL.Image import Image as IMG +from pil_utils import BuildImage + +from meme_generator import add_meme +from meme_generator.utils import save_gif + +img_dir = Path(__file__).parent / "images" + + +def listen_music(images: List[BuildImage], texts, args): + img = images[0].convert("RGBA") + frame = BuildImage.open(img_dir / "0.png") + frames: List[IMG] = [] + for i in range(0, 360, 10): + frames.append( + frame.copy() + .paste(img.rotate(-i).resize((215, 215)), (100, 100), below=True) + .image + ) + return save_gif(frames, 0.05) + + +add_meme("listen_music", listen_music, min_images=1, max_images=1, keywords=["听音乐"]) diff --git a/meme_generator/memes/listen_music/images/0.png b/meme_generator/memes/listen_music/images/0.png new file mode 100644 index 0000000000000000000000000000000000000000..8d894aba7d927a37f85e58d32e6ea8c445043288 Binary files /dev/null and b/meme_generator/memes/listen_music/images/0.png differ diff --git a/meme_generator/memes/little_angel/__init__.py b/meme_generator/memes/little_angel/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..bbfe9c60b425be26ec9b1560f20f26fcbc948ede --- /dev/null +++ b/meme_generator/memes/little_angel/__init__.py @@ -0,0 +1,55 @@ +from typing import List + +from pil_utils import BuildImage + +from meme_generator import MemeArgsModel, add_meme +from meme_generator.exception import TextOverLength +from meme_generator.utils import make_jpg_or_gif + + +def little_angel(images: List[BuildImage], texts: List[str], args: MemeArgsModel): + img_w, img_h = images[0].convert("RGBA").resize_width(500).size + frame = BuildImage.new("RGBA", (600, img_h + 230), "white") + text = "非常可爱!简直就是小天使" + frame.draw_text( + (10, img_h + 120, 590, img_h + 185), text, max_fontsize=48, weight="bold" + ) + + ta = "她" + name = ta + if texts: + name = texts[0] + elif args.user_infos: + info = args.user_infos[0] + ta = "他" if info.gender == "male" else "她" + name = info.name or ta + + text = f"{ta}没失踪也没怎么样 我只是觉得你们都该看一下" + frame.draw_text( + (20, img_h + 180, 580, img_h + 215), text, max_fontsize=26, weight="bold" + ) + + text = f"请问你们看到{name}了吗?" + try: + frame.draw_text( + (20, 0, 580, 110), text, max_fontsize=70, min_fontsize=25, weight="bold" + ) + except ValueError: + raise TextOverLength(name) + + def make(img: BuildImage) -> BuildImage: + img = img.convert("RGBA").resize_width(500) + return frame.copy().paste(img, (int(300 - img_w / 2), 110), alpha=True) + + return make_jpg_or_gif(images[0], make) + + +add_meme( + "little_angel", + little_angel, + min_images=1, + max_images=1, + min_texts=0, + max_texts=1, + keywords=["小天使"], +) diff --git a/meme_generator/memes/loading/__init__.py b/meme_generator/memes/loading/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..32eb49f4f4df1a8023b4fdda26b2e63b2fad5ec1 --- /dev/null +++ b/meme_generator/memes/loading/__init__.py @@ -0,0 +1,36 @@ +from pathlib import Path +from typing import List + +from PIL import ImageFilter +from pil_utils import BuildImage + +from meme_generator import add_meme +from meme_generator.utils import make_jpg_or_gif + +img_dir = Path(__file__).parent / "images" + + +def loading(images: List[BuildImage], texts, args): + img_big = images[0].convert("RGBA").resize_width(500) + img_big = img_big.filter(ImageFilter.GaussianBlur(radius=3)) + h1 = img_big.height + mask = BuildImage.new("RGBA", img_big.size, (0, 0, 0, 32)) + icon = BuildImage.open(img_dir / "icon.png") + img_big.paste(mask, alpha=True).paste(icon, (200, int(h1 / 2) - 50), alpha=True) + + def make(img: BuildImage) -> BuildImage: + img_small = img.convert("RGBA").resize_width(100) + h2 = max(img_small.height, 80) + frame = BuildImage.new("RGBA", (500, h1 + h2 + 10), "white") + frame.paste(img_big, alpha=True).paste( + img_small, (100, h1 + 5 + (h2 - img_small.height) // 2), alpha=True + ) + frame.draw_text( + (210, h1 + 5, 480, h1 + h2 + 5), "不出来", halign="left", max_fontsize=60 + ) + return frame + + return make_jpg_or_gif(images[0], make) + + +add_meme("loading", loading, min_images=1, max_images=1, keywords=["加载中"]) diff --git a/meme_generator/memes/loading/images/icon.png b/meme_generator/memes/loading/images/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..361beb548788a48483631570930563f79f7c4eb3 Binary files /dev/null and b/meme_generator/memes/loading/images/icon.png differ diff --git a/meme_generator/memes/look_flat/__init__.py b/meme_generator/memes/look_flat/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..fd2af58ab7748628f2ac162778d76cdd314beed9 --- /dev/null +++ b/meme_generator/memes/look_flat/__init__.py @@ -0,0 +1,58 @@ +from typing import List + +from pil_utils import BuildImage +from pydantic import Field + +from meme_generator import MemeArgsModel, MemeArgsParser, MemeArgsType, add_meme +from meme_generator.exception import TextOverLength +from meme_generator.utils import make_jpg_or_gif + +help = "图片“压扁”比例" + +parser = MemeArgsParser() +parser.add_argument("-r", "--ratio", type=int, default=2, help=help) + + +class Model(MemeArgsModel): + ratio: int = Field(2, description=help) + + +def look_flat(images: List[BuildImage], texts: List[str], args: Model): + text = texts[0] if texts else "可恶...被人看扁了" + ratio = args.ratio + + img_w = 500 + text_h = 80 + text_frame = BuildImage.new("RGBA", (img_w, text_h), "white") + try: + text_frame.draw_text( + (10, 0, img_w - 10, text_h), + text, + max_fontsize=55, + min_fontsize=30, + weight="bold", + ) + except ValueError: + raise TextOverLength(text) + + def make(img: BuildImage) -> BuildImage: + img = img.convert("RGBA").resize_width(img_w) + img = img.resize((img_w, img.height // ratio)) + img_h = img.height + frame = BuildImage.new("RGBA", (img_w, img_h + text_h), "white") + return frame.paste(img, alpha=True).paste(text_frame, (0, img_h), alpha=True) + + return make_jpg_or_gif(images[0], make) + + +add_meme( + "look_flat", + look_flat, + min_images=1, + max_images=1, + min_texts=0, + max_texts=1, + default_texts=["可恶...被人看扁了"], + args_type=MemeArgsType(parser, Model), + keywords=["看扁"], +) diff --git a/meme_generator/memes/look_this_icon/__init__.py b/meme_generator/memes/look_this_icon/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..fab54612a3c469185e43f49682b3d7d2f2fda73b --- /dev/null +++ b/meme_generator/memes/look_this_icon/__init__.py @@ -0,0 +1,44 @@ +from pathlib import Path +from typing import List + +from pil_utils import BuildImage + +from meme_generator import add_meme +from meme_generator.exception import TextOverLength +from meme_generator.utils import make_jpg_or_gif + +img_dir = Path(__file__).parent / "images" + + +def look_this_icon(images: List[BuildImage], texts: List[str], args): + text = texts[0] if texts else "朋友\n先看看这个图标再说话" + frame = BuildImage.open(img_dir / "nmsl.png") + try: + frame.draw_text( + (0, 933, 1170, 1143), + text, + lines_align="center", + weight="bold", + max_fontsize=100, + min_fontsize=50, + ) + except ValueError: + raise TextOverLength(text) + + def make(img: BuildImage) -> BuildImage: + img = img.convert("RGBA").resize((515, 515), keep_ratio=True) + return frame.copy().paste(img, (599, 403), below=True) + + return make_jpg_or_gif(images[0], make) + + +add_meme( + "look_this_icon", + look_this_icon, + min_images=1, + max_images=1, + min_texts=0, + max_texts=1, + default_texts=["朋友\n先看看这个图标再说话"], + keywords=["看图标"], +) diff --git a/meme_generator/memes/look_this_icon/images/nmsl.png b/meme_generator/memes/look_this_icon/images/nmsl.png new file mode 100644 index 0000000000000000000000000000000000000000..3bd8f344298dfadda62554d4857d0b2d16c6e2fd Binary files /dev/null and b/meme_generator/memes/look_this_icon/images/nmsl.png differ diff --git a/meme_generator/memes/love_you/__init__.py b/meme_generator/memes/love_you/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..2a579467811e3c8708416f2501c5df26a9fe094e --- /dev/null +++ b/meme_generator/memes/love_you/__init__.py @@ -0,0 +1,26 @@ +from pathlib import Path +from typing import List + +from PIL.Image import Image as IMG +from pil_utils import BuildImage + +from meme_generator import add_meme +from meme_generator.utils import save_gif + +img_dir = Path(__file__).parent / "images" + + +def love_you(images: List[BuildImage], texts, args): + img = images[0].convert("RGBA").square() + frames: List[IMG] = [] + locs = [(68, 65, 70, 70), (63, 59, 80, 80)] + for i in range(2): + heart = BuildImage.open(img_dir / f"{i}.png") + frame = BuildImage.new("RGBA", heart.size, "white") + x, y, w, h = locs[i] + frame.paste(img.resize((w, h)), (x, y), alpha=True).paste(heart, alpha=True) + frames.append(frame.image) + return save_gif(frames, 0.2) + + +add_meme("love_you", love_you, min_images=1, max_images=1, keywords=["永远爱你"]) diff --git a/meme_generator/memes/love_you/images/0.png b/meme_generator/memes/love_you/images/0.png new file mode 100644 index 0000000000000000000000000000000000000000..2f37c51877c8be2785315aab5924d751ff0b2229 Binary files /dev/null and b/meme_generator/memes/love_you/images/0.png differ diff --git a/meme_generator/memes/love_you/images/1.png b/meme_generator/memes/love_you/images/1.png new file mode 100644 index 0000000000000000000000000000000000000000..5cf3764b9577adfce56828e7e5f020dc2b555e2b Binary files /dev/null and b/meme_generator/memes/love_you/images/1.png differ diff --git a/meme_generator/memes/luoyonghao_say/__init__.py b/meme_generator/memes/luoyonghao_say/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..f09d378a09127843804bb79fbf9e1e3370ac88fb --- /dev/null +++ b/meme_generator/memes/luoyonghao_say/__init__.py @@ -0,0 +1,42 @@ +from pathlib import Path +from typing import List + +from PIL import ImageFilter +from pil_utils import BuildImage + +from meme_generator import add_meme +from meme_generator.exception import TextOverLength + +img_dir = Path(__file__).parent / "images" + + +def luoyonghao_say(images, texts: List[str], args): + text = texts[0] + frame = BuildImage.open(img_dir / "0.jpg") + text_frame = BuildImage.new("RGBA", (365, 120)) + try: + text_frame.draw_text( + (40, 10, 325, 110), + text, + allow_wrap=True, + max_fontsize=50, + min_fontsize=10, + valign="top", + ) + except ValueError: + raise TextOverLength(text) + text_frame = text_frame.perspective( + ((52, 10), (391, 0), (364, 110), (0, 120)) + ).filter(ImageFilter.GaussianBlur(radius=0.8)) + frame.paste(text_frame, (48, 246), alpha=True) + return frame.save_jpg() + + +add_meme( + "luoyonghao_say", + luoyonghao_say, + min_texts=1, + max_texts=1, + default_texts=["又不是不能用"], + keywords=["罗永浩说"], +) diff --git a/meme_generator/memes/luoyonghao_say/images/0.jpg b/meme_generator/memes/luoyonghao_say/images/0.jpg new file mode 100644 index 0000000000000000000000000000000000000000..aa77e97463c8862dc7ae20474b30224368a96980 Binary files /dev/null and b/meme_generator/memes/luoyonghao_say/images/0.jpg differ diff --git a/meme_generator/memes/luxun_say/__init__.py b/meme_generator/memes/luxun_say/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..44db464aeded0cb248d917e30dd630fa25037856 --- /dev/null +++ b/meme_generator/memes/luxun_say/__init__.py @@ -0,0 +1,37 @@ +from pathlib import Path +from typing import List + +from pil_utils import BuildImage + +from meme_generator import add_meme +from meme_generator.exception import TextOverLength + +img_dir = Path(__file__).parent / "images" + + +def luxun_say(images, texts: List[str], args): + text = texts[0] + frame = BuildImage.open(img_dir / "0.jpg") + try: + frame.draw_text( + (40, frame.height - 200, frame.width - 40, frame.height - 100), + text, + allow_wrap=True, + max_fontsize=40, + min_fontsize=30, + fill="white", + ) + except ValueError: + raise TextOverLength(text) + frame.draw_text((320, 400), "--鲁迅", fontsize=30, fill="white") + return frame.save_jpg() + + +add_meme( + "luxun_say", + luxun_say, + min_texts=1, + max_texts=1, + default_texts=["我没有说过这句话"], + keywords=["鲁迅说", "鲁迅说过"], +) diff --git a/meme_generator/memes/luxun_say/images/0.jpg b/meme_generator/memes/luxun_say/images/0.jpg new file mode 100644 index 0000000000000000000000000000000000000000..8b10eaf69da59e8cc969b0611eae35cfbb90b3ba Binary files /dev/null and b/meme_generator/memes/luxun_say/images/0.jpg differ diff --git a/meme_generator/memes/maimai_awaken/__init__.py b/meme_generator/memes/maimai_awaken/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..ec62ba20f89ca9a3c853f9fa97b97c030d58c891 --- /dev/null +++ b/meme_generator/memes/maimai_awaken/__init__.py @@ -0,0 +1,24 @@ +from pathlib import Path +from typing import List + +from pil_utils import BuildImage + +from meme_generator import add_meme +from meme_generator.utils import make_jpg_or_gif + +img_dir = Path(__file__).parent / "images" + + +def maimai_awaken(images: List[BuildImage], texts, args): + frame = BuildImage.open(img_dir / "0.png") + + def make(img: BuildImage) -> BuildImage: + img = img.convert("RGBA").square().resize((250, 250)).rotate(-25, expand=True) + return frame.copy().paste(img, (134, 134), alpha=True, below=True) + + return make_jpg_or_gif(images[0], make) + + +add_meme( + "maimai_awaken", maimai_awaken, min_images=1, max_images=1, keywords=["旅行伙伴觉醒"] +) diff --git a/meme_generator/memes/maimai_awaken/images/0.png b/meme_generator/memes/maimai_awaken/images/0.png new file mode 100644 index 0000000000000000000000000000000000000000..70f7e1711b62dff4a050a6b0e958663a97005633 Binary files /dev/null and b/meme_generator/memes/maimai_awaken/images/0.png differ diff --git a/meme_generator/memes/maimai_join/__init__.py b/meme_generator/memes/maimai_join/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..b923e7fb5dbc944a0560aa89ccf8ce215d5b8c58 --- /dev/null +++ b/meme_generator/memes/maimai_join/__init__.py @@ -0,0 +1,22 @@ +from pathlib import Path +from typing import List + +from pil_utils import BuildImage + +from meme_generator import add_meme +from meme_generator.utils import make_jpg_or_gif + +img_dir = Path(__file__).parent / "images" + + +def maimai_join(images: List[BuildImage], texts, args): + frame = BuildImage.open(img_dir / "0.png") + + def make(img: BuildImage) -> BuildImage: + img = img.convert("RGBA").square().resize((400, 400)) + return frame.copy().paste(img, (50, 50), alpha=True, below=True) + + return make_jpg_or_gif(images[0], make) + + +add_meme("maimai_join", maimai_join, min_images=1, max_images=1, keywords=["旅行伙伴加入"]) diff --git a/meme_generator/memes/maimai_join/images/0.png b/meme_generator/memes/maimai_join/images/0.png new file mode 100644 index 0000000000000000000000000000000000000000..5556147567fc8a232f6ee0b9c4775f8611d6bde8 Binary files /dev/null and b/meme_generator/memes/maimai_join/images/0.png differ diff --git a/meme_generator/memes/make_friend/__init__.py b/meme_generator/memes/make_friend/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..a927339bd1d53e7e5869d1915200a3b03f165c47 --- /dev/null +++ b/meme_generator/memes/make_friend/__init__.py @@ -0,0 +1,50 @@ +from pathlib import Path +from typing import List + +from pil_utils import BuildImage, Text2Image + +from meme_generator import MemeArgsModel, add_meme +from meme_generator.exception import TextOrNameNotEnough, TextOverLength + +img_dir = Path(__file__).parent / "images" + + +def make_friend(images: List[BuildImage], texts: List[str], args: MemeArgsModel): + img = images[0].convert("RGBA") + + if not texts and not args.user_infos: + raise TextOrNameNotEnough("make_friend") + name = texts[0] if texts else args.user_infos[0].name + + bg = BuildImage.open(img_dir / "0.png") + frame = img.resize_width(1000) + frame.paste( + img.resize_width(250).rotate(9, expand=True), + (743, frame.height - 155), + alpha=True, + ) + frame.paste( + img.square().resize((55, 55)).rotate(9, expand=True), + (836, frame.height - 278), + alpha=True, + ) + frame.paste(bg, (0, frame.height - 1000), alpha=True) + + text_img = Text2Image.from_text(name, 20, fill="white").to_image() + if text_img.width > 230: + raise TextOverLength(name) + + text_img = BuildImage(text_img).rotate(9, expand=True) + frame.paste(text_img, (710, frame.height - 308), alpha=True) + return frame.save_jpg() + + +add_meme( + "make_friend", + make_friend, + min_images=1, + max_images=1, + min_texts=0, + max_texts=1, + keywords=["交个朋友"], +) diff --git a/meme_generator/memes/make_friend/images/0.png b/meme_generator/memes/make_friend/images/0.png new file mode 100644 index 0000000000000000000000000000000000000000..80187c35e7c127a7526ecabca63624088547d788 Binary files /dev/null and b/meme_generator/memes/make_friend/images/0.png differ diff --git a/meme_generator/memes/marriage/__init__.py b/meme_generator/memes/marriage/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..bf74da11f9645071ab158b904f16e21c54027399 --- /dev/null +++ b/meme_generator/memes/marriage/__init__.py @@ -0,0 +1,27 @@ +from pathlib import Path +from typing import List + +from pil_utils import BuildImage + +from meme_generator import add_meme + +img_dir = Path(__file__).parent / "images" + + +def marriage(images: List[BuildImage], texts, args): + img = images[0].convert("RGBA").resize_height(1080) + img_w, img_h = img.size + if img_w > 1500: + img_w = 1500 + elif img_w < 800: + img_h = int(img_h * img_w / 800) + frame = img.resize_canvas((img_w, img_h)).resize_height(1080) + left = BuildImage.open(img_dir / "0.png") + right = BuildImage.open(img_dir / "1.png") + frame.paste(left, alpha=True).paste( + right, (frame.width - right.width, 0), alpha=True + ) + return frame.save_jpg() + + +add_meme("marriage", marriage, min_images=1, max_images=1, keywords=["结婚申请", "结婚登记"]) diff --git a/meme_generator/memes/marriage/images/0.png b/meme_generator/memes/marriage/images/0.png new file mode 100644 index 0000000000000000000000000000000000000000..a2537f03c3f6ce54eb0b2f005b0ff031356679ee Binary files /dev/null and b/meme_generator/memes/marriage/images/0.png differ diff --git a/meme_generator/memes/marriage/images/1.png b/meme_generator/memes/marriage/images/1.png new file mode 100644 index 0000000000000000000000000000000000000000..95c4c552ef7a0fd342bd52ba45622f80425222d0 Binary files /dev/null and b/meme_generator/memes/marriage/images/1.png differ diff --git a/meme_generator/memes/meteor/__init__.py b/meme_generator/memes/meteor/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..cba5e488eb20a2027bf21c04db8931b47470f9b6 --- /dev/null +++ b/meme_generator/memes/meteor/__init__.py @@ -0,0 +1,31 @@ +from pathlib import Path +from typing import List + +from pil_utils import BuildImage + +from meme_generator import add_meme +from meme_generator.exception import TextOverLength + +img_dir = Path(__file__).parent / "images" + + +def meteor(images, texts: List[str], args): + text = texts[0] + frame = BuildImage.open(img_dir / "0.png") + try: + frame.draw_text( + (220, 230, 920, 315), + text, + allow_wrap=True, + max_fontsize=80, + min_fontsize=20, + fill="white", + ) + except ValueError: + raise TextOverLength(text) + return frame.save_jpg() + + +add_meme( + "meteor", meteor, min_texts=1, max_texts=1, default_texts=["我要对象"], keywords=["流星"] +) diff --git a/meme_generator/memes/meteor/images/0.png b/meme_generator/memes/meteor/images/0.png new file mode 100644 index 0000000000000000000000000000000000000000..154029a695f327b076feb274f563202c2c9a75df Binary files /dev/null and b/meme_generator/memes/meteor/images/0.png differ diff --git a/meme_generator/memes/mihoyo/__init__.py b/meme_generator/memes/mihoyo/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..13d31a63c982377c571678c0aba342c7d1123833 --- /dev/null +++ b/meme_generator/memes/mihoyo/__init__.py @@ -0,0 +1,25 @@ +from pathlib import Path +from typing import List + +from pil_utils import BuildImage + +from meme_generator import add_meme +from meme_generator.utils import make_png_or_gif + +img_dir = Path(__file__).parent / "images" + + +def mihoyo(images: List[BuildImage], texts, args): + mask = BuildImage.new("RGBA", (500, 60), (53, 49, 65, 230)) + logo = BuildImage.open(img_dir / "logo.png").resize_height(50) + + def make(img: BuildImage) -> BuildImage: + img = img.convert("RGBA").resize((500, 500), keep_ratio=True) + img.paste(mask, (0, 440), alpha=True) + img.paste(logo, ((img.width - logo.width) // 2, 445), alpha=True) + return img.circle_corner(100) + + return make_png_or_gif(images[0], make) + + +add_meme("mihoyo", mihoyo, min_images=1, max_images=1, keywords=["米哈游"]) diff --git a/meme_generator/memes/mihoyo/images/logo.png b/meme_generator/memes/mihoyo/images/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..8d815eeb85b94753a46b6e871a36f302a4388956 Binary files /dev/null and b/meme_generator/memes/mihoyo/images/logo.png differ diff --git a/meme_generator/memes/mourning/__init__.py b/meme_generator/memes/mourning/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..c38c96cc015b8828db92f8ab1bade9ff56134a6c --- /dev/null +++ b/meme_generator/memes/mourning/__init__.py @@ -0,0 +1,40 @@ +from pathlib import Path +from typing import List + +from pil_utils import BuildImage +from pydantic import Field + +from meme_generator import MemeArgsModel, MemeArgsParser, MemeArgsType, add_meme +from meme_generator.utils import make_jpg_or_gif + +img_dir = Path(__file__).parent / "images" + +help = "是否将图片变为黑白" + +parser = MemeArgsParser(prefix_chars="-/") +parser.add_argument("--black", "/黑白", action="store_true", help=help) + + +class Model(MemeArgsModel): + black: bool = Field(False, description=help) + + +def mourning(images: List[BuildImage], texts, args: Model): + frame = BuildImage.open(img_dir / "0.png") + + def make(img: BuildImage) -> BuildImage: + img = img.convert("L") if args.black else img.convert("RGBA") + img = img.resize((635, 725), keep_ratio=True) + return frame.copy().paste(img, (645, 145), below=True) + + return make_jpg_or_gif(images[0], make) + + +add_meme( + "mourning", + mourning, + min_images=1, + max_images=1, + args_type=MemeArgsType(parser, Model, [Model(black=False), Model(black=True)]), + keywords=["上香"], +) diff --git a/meme_generator/memes/mourning/images/0.png b/meme_generator/memes/mourning/images/0.png new file mode 100644 index 0000000000000000000000000000000000000000..cf7c2c460974e1b1c775245064bd23c55c81252e --- /dev/null +++ b/meme_generator/memes/mourning/images/0.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8aa92f940c909a3d3a60c53539c32be3f7e5eadb65617fa9f8b7a27aa5332bc7 +size 1415101 diff --git a/meme_generator/memes/murmur/__init__.py b/meme_generator/memes/murmur/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..6c96123393d06e60e9d9ffc1f2923b7483af964a --- /dev/null +++ b/meme_generator/memes/murmur/__init__.py @@ -0,0 +1,34 @@ +from pathlib import Path +from typing import List + +from pil_utils import BuildImage + +from meme_generator import add_meme +from meme_generator.exception import TextOverLength + +img_dir = Path(__file__).parent / "images" + + +def murmur(images, texts: List[str], args): + text = texts[0] + frame = BuildImage.open(img_dir / "0.jpg") + try: + frame.draw_text( + (10, 255, 430, 300), + text, + max_fontsize=40, + min_fontsize=15, + ) + except ValueError: + raise TextOverLength(text) + return frame.save_jpg() + + +add_meme( + "murmur", + murmur, + min_texts=1, + max_texts=1, + default_texts=["你的假期余额不足"], + keywords=["低语"], +) diff --git a/meme_generator/memes/murmur/images/0.jpg b/meme_generator/memes/murmur/images/0.jpg new file mode 100644 index 0000000000000000000000000000000000000000..ea9d80a7c2b08bf25056360487b99f1c2cbf3222 Binary files /dev/null and b/meme_generator/memes/murmur/images/0.jpg differ diff --git a/meme_generator/memes/my_friend/__init__.py b/meme_generator/memes/my_friend/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..21b8aa44954389a72c029a2bf52ddd3a4b6b190a --- /dev/null +++ b/meme_generator/memes/my_friend/__init__.py @@ -0,0 +1,79 @@ +from pathlib import Path +from typing import List + +from pil_utils import BuildImage, Text2Image +from pydantic import Field + +from meme_generator import MemeArgsModel, MemeArgsParser, MemeArgsType, add_meme +from meme_generator.exception import TextOverLength + +img_dir = Path(__file__).parent / "images" + +help = "指定名字" + +parser = MemeArgsParser() +parser.add_argument("-n", "--name", type=str, default="", help=help) + + +class Model(MemeArgsModel): + name: str = Field("", description=help) + + +def my_friend(images: List[BuildImage], texts: List[str], args: Model): + name = args.name or (args.user_infos[-1].name if args.user_infos else "") or "朋友" + img = images[0].convert("RGBA").circle().resize((100, 100)) + + name_img = Text2Image.from_text(name, 25, fill="#868894").to_image() + name_w, name_h = name_img.size + if name_w >= 600: + raise TextOverLength(name) + + corner1 = BuildImage.open(img_dir / "corner1.png") + corner2 = BuildImage.open(img_dir / "corner2.png") + corner3 = BuildImage.open(img_dir / "corner3.png") + corner4 = BuildImage.open(img_dir / "corner4.png") + label = BuildImage.open(img_dir / "label.png") + + def make_dialog(text: str) -> BuildImage: + text_img = Text2Image.from_text(text, 40).wrap(600).to_image() + text_w, text_h = text_img.size + box_w = max(text_w, name_w + 15) + 140 + box_h = max(text_h + 103, 150) + box = BuildImage.new("RGBA", (box_w, box_h)) + box.paste(corner1, (0, 0)) + box.paste(corner2, (0, box_h - 75)) + box.paste(corner3, (text_w + 70, 0)) + box.paste(corner4, (text_w + 70, box_h - 75)) + box.paste(BuildImage.new("RGBA", (text_w, box_h - 40), "white"), (70, 20)) + box.paste(BuildImage.new("RGBA", (text_w + 88, box_h - 150), "white"), (27, 75)) + box.paste(text_img, (70, 17 + (box_h - 40 - text_h) // 2), alpha=True) + + dialog = BuildImage.new("RGBA", (box.width + 130, box.height + 60), "#eaedf4") + dialog.paste(img, (20, 20), alpha=True) + dialog.paste(box, (130, 60), alpha=True) + dialog.paste(label, (160, 25)) + dialog.paste(name_img, (260, 22 + (35 - name_h) // 2), alpha=True) + return dialog + + dialogs = [make_dialog(text) for text in texts] + frame_w = max((dialog.width for dialog in dialogs)) + frame_h = sum((dialog.height for dialog in dialogs)) + frame = BuildImage.new("RGBA", (frame_w, frame_h), "#eaedf4") + current_h = 0 + for dialog in dialogs: + frame.paste(dialog, (0, current_h)) + current_h += dialog.height + return frame.save_jpg() + + +add_meme( + "my_friend", + my_friend, + min_images=1, + max_images=1, + min_texts=1, + max_texts=10, + default_texts=["让我康康"], + args_type=MemeArgsType(parser, Model), + keywords=["我朋友说"], +) diff --git a/meme_generator/memes/my_friend/images/corner1.png b/meme_generator/memes/my_friend/images/corner1.png new file mode 100644 index 0000000000000000000000000000000000000000..9e97f68449244dfa48865ebf79b29ac31036274c Binary files /dev/null and b/meme_generator/memes/my_friend/images/corner1.png differ diff --git a/meme_generator/memes/my_friend/images/corner2.png b/meme_generator/memes/my_friend/images/corner2.png new file mode 100644 index 0000000000000000000000000000000000000000..876f88b3ac17631645a8351f6e33171bf8e449bb Binary files /dev/null and b/meme_generator/memes/my_friend/images/corner2.png differ diff --git a/meme_generator/memes/my_friend/images/corner3.png b/meme_generator/memes/my_friend/images/corner3.png new file mode 100644 index 0000000000000000000000000000000000000000..a7e183c7c6eb14d11082f28f4b74f47dc8b95066 Binary files /dev/null and b/meme_generator/memes/my_friend/images/corner3.png differ diff --git a/meme_generator/memes/my_friend/images/corner4.png b/meme_generator/memes/my_friend/images/corner4.png new file mode 100644 index 0000000000000000000000000000000000000000..7f476643043713b67008d5c68499de40fcc35ad4 Binary files /dev/null and b/meme_generator/memes/my_friend/images/corner4.png differ diff --git a/meme_generator/memes/my_friend/images/label.png b/meme_generator/memes/my_friend/images/label.png new file mode 100644 index 0000000000000000000000000000000000000000..b7d5c4bede4943d32cc1f211b73358854649591d Binary files /dev/null and b/meme_generator/memes/my_friend/images/label.png differ diff --git a/meme_generator/memes/my_wife/__init__.py b/meme_generator/memes/my_wife/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..727bffabea0b8fcdb5d683c0bb24ca69d0211c2c --- /dev/null +++ b/meme_generator/memes/my_wife/__init__.py @@ -0,0 +1,53 @@ +from pathlib import Path +from typing import List + +from pil_utils import BuildImage + +from meme_generator import add_meme + +img_dir = Path(__file__).parent / "images" + + +def my_wife(images: List[BuildImage], texts, args): + img = images[0].convert("RGBA").resize_width(400) + img_w, img_h = img.size + frame = BuildImage.new("RGBA", (650, img_h + 500), "white") + frame.paste(img, (int(325 - img_w / 2), 105), alpha=True) + + text = "如果你的老婆长这样" + frame.draw_text( + (27, 12, 27 + 596, 12 + 79), + text, + max_fontsize=70, + min_fontsize=30, + allow_wrap=True, + lines_align="center", + weight="bold", + ) + text = "那么这就不是你的老婆\n这是我的老婆" + frame.draw_text( + (27, img_h + 120, 27 + 593, img_h + 120 + 135), + text, + max_fontsize=70, + min_fontsize=30, + allow_wrap=True, + weight="bold", + ) + text = "滚去找你\n自己的老婆去" + frame.draw_text( + (27, img_h + 295, 27 + 374, img_h + 295 + 135), + text, + max_fontsize=70, + min_fontsize=30, + allow_wrap=True, + lines_align="center", + weight="bold", + ) + + img_point = BuildImage.open(img_dir / "1.png").resize_width(200) + frame.paste(img_point, (421, img_h + 270)) + + return frame.save_jpg() + + +add_meme("my_wife", my_wife, min_images=1, max_images=1, keywords=["我老婆", "这是我老婆"]) diff --git a/meme_generator/memes/my_wife/images/0.png b/meme_generator/memes/my_wife/images/0.png new file mode 100644 index 0000000000000000000000000000000000000000..aa04e1455b629066e5a55b325eb68336e36b1d55 Binary files /dev/null and b/meme_generator/memes/my_wife/images/0.png differ diff --git a/meme_generator/memes/my_wife/images/1.png b/meme_generator/memes/my_wife/images/1.png new file mode 100644 index 0000000000000000000000000000000000000000..44c96e3d9ce2276cd64855253ad6ca88e542b115 Binary files /dev/null and b/meme_generator/memes/my_wife/images/1.png differ diff --git a/meme_generator/utils.py b/meme_generator/utils.py new file mode 100644 index 0000000000000000000000000000000000000000..4cc2294bd19df0646fa666385a380f050b1c24f5 --- /dev/null +++ b/meme_generator/utils.py @@ -0,0 +1,436 @@ +import asyncio +import hashlib +import inspect +import math +import random +import time +from dataclasses import dataclass +from enum import Enum +from functools import partial, wraps +from io import BytesIO +from typing import ( + TYPE_CHECKING, + Any, + Callable, + Coroutine, + List, + Literal, + Optional, + Protocol, + Tuple, + TypeVar, +) + +import httpx +from PIL.Image import Image as IMG +from pil_utils import BuildImage, Text2Image +from pil_utils.types import ColorType, FontStyle, FontWeight +from typing_extensions import ParamSpec + +from .config import meme_config +from .exception import MemeGeneratorException + +if TYPE_CHECKING: + from .meme import Meme + +P = ParamSpec("P") +R = TypeVar("R") + + +def run_sync(call: Callable[P, R]) -> Callable[P, Coroutine[None, None, R]]: + """一个用于包装 sync function 为 async function 的装饰器 + 参数: + call: 被装饰的同步函数 + """ + + @wraps(call) + async def _wrapper(*args: P.args, **kwargs: P.kwargs) -> R: + loop = asyncio.get_running_loop() + pfunc = partial(call, *args, **kwargs) + result = await loop.run_in_executor(None, pfunc) + return result + + return _wrapper + + +def is_coroutine_callable(call: Callable[..., Any]) -> bool: + """检查 call 是否是一个 callable 协程函数""" + if inspect.isroutine(call): + return inspect.iscoroutinefunction(call) + if inspect.isclass(call): + return False + func_ = getattr(call, "__call__", None) + return inspect.iscoroutinefunction(func_) + + +def save_gif(frames: List[IMG], duration: float) -> BytesIO: + output = BytesIO() + frames[0].save( + output, + format="GIF", + save_all=True, + append_images=frames[1:], + duration=duration * 1000, + loop=0, + disposal=2, + optimize=False, + ) + + # 没有超出最大大小,直接返回 + nbytes = output.getbuffer().nbytes + if nbytes <= meme_config.gif.gif_max_size * 10**6: + return output + + # 超出最大大小,帧数超出最大帧数时,缩减帧数 + n_frames = len(frames) + gif_max_frames = meme_config.gif.gif_max_frames + if n_frames > gif_max_frames: + index = range(n_frames) + ratio = n_frames / gif_max_frames + index = (int(i * ratio) for i in range(gif_max_frames)) + new_duration = duration * ratio + new_frames = [frames[i] for i in index] + return save_gif(new_frames, new_duration) + + # 超出最大大小,帧数没有超出最大帧数时,缩小尺寸 + new_frames = [ + frame.resize((int(frame.width * 0.9), int(frame.height * 0.9))) + for frame in frames + ] + return save_gif(new_frames, duration) + + +class Maker(Protocol): + def __call__(self, img: BuildImage) -> BuildImage: + ... + + +class GifMaker(Protocol): + def __call__(self, i: int) -> Maker: + ... + + +def get_avg_duration(image: IMG) -> float: + if not getattr(image, "is_animated", False): + return 0 + total_duration = 0 + for i in range(image.n_frames): + image.seek(i) + total_duration += image.info["duration"] + return total_duration / image.n_frames + + +def split_gif(image: IMG) -> List[IMG]: + frames: List[IMG] = [] + + update_mode = "full" + for i in range(image.n_frames): + image.seek(i) + if image.tile: # type: ignore + update_region = image.tile[0][1][2:] # type: ignore + if update_region != image.size: + update_mode = "partial" + break + + last_frame: Optional[IMG] = None + for i in range(image.n_frames): + image.seek(i) + frame = image.copy() + if update_mode == "partial" and last_frame: + frame = last_frame.copy().paste(frame) + frames.append(frame) + image.seek(0) + if image.info.__contains__("transparency"): + frames[0].info["transparency"] = image.info["transparency"] + return frames + + +def make_jpg_or_gif( + img: BuildImage, func: Maker, keep_transparency: bool = False +) -> BytesIO: + """ + 制作静图或者动图 + :params + * ``img``: 输入图片 + * ``func``: 图片处理函数,输入img,返回处理后的图片 + * ``keep_transparency``: 传入gif时,是否保留该gif的透明度 + """ + image = img.image + if not getattr(image, "is_animated", False): + return func(img).save_jpg() + else: + frames = split_gif(image) + duration = get_avg_duration(image) / 1000 + frames = [func(BuildImage(frame)).image for frame in frames] + if keep_transparency: + image.seek(0) + if image.info.__contains__("transparency"): + frames[0].info["transparency"] = image.info["transparency"] + return save_gif(frames, duration) + + +def make_png_or_gif( + img: BuildImage, func: Maker, keep_transparency: bool = False +) -> BytesIO: + """ + 制作静图或者动图 + :params + * ``img``: 输入图片 + * ``func``: 图片处理函数,输入img,返回处理后的图片 + * ``keep_transparency``: 传入gif时,是否保留该gif的透明度 + """ + image = img.image + if not getattr(image, "is_animated", False): + return func(img).save_png() + else: + frames = split_gif(image) + duration = get_avg_duration(image) / 1000 + frames = [func(BuildImage(frame)).image for frame in frames] + if keep_transparency: + image.seek(0) + if image.info.__contains__("transparency"): + frames[0].info["transparency"] = image.info["transparency"] + return save_gif(frames, duration) + + +class FrameAlignPolicy(Enum): + """ + 要叠加的gif长度大于基准gif时,是否延长基准gif长度以对齐两个gif + """ + + no_extend = 0 + """不延长""" + extend_first = 1 + """延长第一帧""" + extend_last = 2 + """延长最后一帧""" + extend_loop = 3 + """以循环方式延长""" + + +def make_gif_or_combined_gif( + img: BuildImage, + maker: GifMaker, + frame_num: int, + duration: float, + frame_align: FrameAlignPolicy = FrameAlignPolicy.no_extend, + input_based: bool = False, + keep_transparency: bool = False, +) -> BytesIO: + """ + 使用静图或动图制作gif + :params + * ``img``: 输入图片,如头像 + * ``maker``: 图片处理函数生成,传入第几帧,返回对应的图片处理函数 + * ``frame_num``: 目标gif的帧数 + * ``duration``: 相邻帧之间的时间间隔,单位为秒 + * ``frame_align``: 要叠加的gif长度大于基准gif时,gif长度对齐方式 + * ``input_based``: 是否以输入gif为基准合成gif,默认为`False`,即以目标gif为基准 + * ``keep_transparency``: 传入gif时,是否保留该gif的透明度 + """ + image = img.image + if not getattr(image, "is_animated", False): + return save_gif([maker(i)(img).image for i in range(frame_num)], duration) + + frame_num_in = image.n_frames + duration_in = get_avg_duration(image) / 1000 + total_duration_in = frame_num_in * duration_in + total_duration = frame_num * duration + + if input_based: + frame_num_base = frame_num_in + frame_num_fit = frame_num + duration_base = duration_in + duration_fit = duration + total_duration_base = total_duration_in + total_duration_fit = total_duration + else: + frame_num_base = frame_num + frame_num_fit = frame_num_in + duration_base = duration + duration_fit = duration_in + total_duration_base = total_duration + total_duration_fit = total_duration_in + + frame_idxs: List[int] = list(range(frame_num_base)) + diff_duration = total_duration_fit - total_duration_base + diff_num = int(diff_duration / duration_base) + + if diff_duration >= duration_base: + if frame_align == FrameAlignPolicy.extend_first: + frame_idxs = [0] * diff_num + frame_idxs + + elif frame_align == FrameAlignPolicy.extend_last: + frame_idxs += [frame_num_base - 1] * diff_num + + elif frame_align == FrameAlignPolicy.extend_loop: + frame_num_total = frame_num_base + # 重复基准gif,直到两个gif总时长之差在1个间隔以内,或总帧数超出最大帧数 + while frame_num_total + frame_num_base <= meme_config.gif.gif_max_frames: + frame_num_total += frame_num_base + frame_idxs += list(range(frame_num_base)) + multiple = round(frame_num_total * duration_base / total_duration_fit) + if ( + math.fabs( + total_duration_fit * multiple - frame_num_total * duration_base + ) + <= duration_base + ): + break + + frames: List[IMG] = [] + frame_idx_fit = 0 + time_start = 0 + for i, idx in enumerate(frame_idxs): + while frame_idx_fit < frame_num_fit: + if ( + frame_idx_fit * duration_fit + <= i * duration_base - time_start + < (frame_idx_fit + 1) * duration_fit + ): + if input_based: + idx_in = idx + idx_maker = frame_idx_fit + else: + idx_in = frame_idx_fit + idx_maker = idx + + func = maker(idx_maker) + image.seek(idx_in) + frames.append(func(BuildImage(image.copy())).image) + break + else: + frame_idx_fit += 1 + if frame_idx_fit >= frame_num_fit: + frame_idx_fit = 0 + time_start += total_duration_fit + + if keep_transparency: + image.seek(0) + if image.info.__contains__("transparency"): + frames[0].info["transparency"] = image.info["transparency"] + + return save_gif(frames, duration) + + +async def translate(text: str, lang_from: str = "auto", lang_to: str = "zh") -> str: + appid = meme_config.translate.baidu_trans_appid + apikey = meme_config.translate.baidu_trans_apikey + if not appid or not apikey: + raise MemeGeneratorException( + "The `baidu_trans_appid` or `baidu_trans_apikey` is not set." + "Please check your config file!" + ) + salt = str(round(time.time() * 1000)) + sign_raw = appid + text + salt + apikey + sign = hashlib.md5(sign_raw.encode("utf8")).hexdigest() + params = { + "q": text, + "from": lang_from, + "to": lang_to, + "appid": appid, + "salt": salt, + "sign": sign, + } + url = "https://fanyi-api.baidu.com/api/trans/vip/translate" + async with httpx.AsyncClient() as client: + resp = await client.get(url, params=params) + result = resp.json() + return result["trans_result"][0]["dst"] + + +def random_text() -> str: + return random.choice(["刘一", "陈二", "张三", "李四", "王五", "赵六", "孙七", "周八", "吴九", "郑十"]) + + +def random_image() -> BytesIO: + text = random.choice(["😂", "😅", "🤗", "🤤", "🥵", "🥰", "😍", "😭", "😋", "😏"]) + return ( + BuildImage.new("RGBA", (500, 500), "white") + .draw_text((0, 0, 500, 500), text, max_fontsize=400) + .save_png() + ) + + +@dataclass +class TextProperties: + fill: ColorType = "black" + style: FontStyle = "normal" + weight: FontWeight = "normal" + stroke_width: int = 0 + stroke_fill: Optional[ColorType] = None + + +def default_template(meme: "Meme", number: int) -> str: + return f"{number}. {'/'.join(meme.keywords)}" + + +def render_meme_list( + meme_list: List[Tuple["Meme", TextProperties]], + *, + template: Callable[["Meme", int], str] = default_template, + order_direction: Literal["row", "column"] = "column", + columns: int = 4, + column_align: Literal["left", "center", "right"] = "left", + item_padding: Tuple[int, int] = (15, 6), + image_padding: Tuple[int, int] = (50, 50), + bg_color: ColorType = "white", + fontsize: int = 30, + fontname: str = "", + fallback_fonts: List[str] = [], +) -> BytesIO: + item_images: List[Text2Image] = [] + for i, (meme, properties) in enumerate(meme_list, start=1): + text = template(meme, i) + t2m = Text2Image.from_text( + text, + fontsize=fontsize, + style=properties.style, + weight=properties.weight, + fill=properties.fill, + stroke_width=properties.stroke_width, + stroke_fill=properties.stroke_fill, + fontname=fontname, + fallback_fonts=fallback_fonts, + ) + item_images.append(t2m) + char_A = ( + Text2Image.from_text( + "A", fontsize=fontsize, fontname=fontname, fallback_fonts=fallback_fonts + ) + .lines[0] + .chars[0] + ) + num_per_col = math.ceil(len(item_images) / columns) + column_images: List[BuildImage] = [] + for col in range(columns): + if order_direction == "column": + images = item_images[col * num_per_col : (col + 1) * num_per_col] + else: + images = [ + item_images[num * columns + col] + for num in range((len(item_images) - col - 1) // columns + 1) + ] + img_w = max((t2m.width for t2m in images)) + item_padding[0] * 2 + img_h = (char_A.ascent + item_padding[1] * 2) * len(images) + char_A.descent + image = BuildImage.new("RGB", (img_w, img_h), bg_color) + y = item_padding[1] + for t2m in images: + if column_align == "left": + x = 0 + elif column_align == "center": + x = (img_w - t2m.width - item_padding[0] * 2) // 2 + else: + x = img_w - t2m.width - item_padding[0] * 2 + t2m.draw_on_image(image.image, (x, y)) + y += char_A.ascent + item_padding[1] * 2 + column_images.append(image) + + img_w = sum((img.width for img in column_images)) + image_padding[0] * 2 + img_h = max((img.height for img in column_images)) + image_padding[1] * 2 + image = BuildImage.new("RGB", (img_w, img_h), bg_color) + x, y = image_padding + for img in column_images: + image.paste(img, (x, y)) + x += img.width + return image.save_jpg() diff --git a/meme_generator/version.py b/meme_generator/version.py new file mode 100644 index 0000000000000000000000000000000000000000..6561790f155f6bfd436e5b19b2f0a1e7f20c0259 --- /dev/null +++ b/meme_generator/version.py @@ -0,0 +1 @@ +__version__ = "0.0.15" diff --git a/poetry.lock b/poetry.lock new file mode 100644 index 0000000000000000000000000000000000000000..c8091f1d2ae4d12ea42661824fe9802b2241b292 --- /dev/null +++ b/poetry.lock @@ -0,0 +1,1324 @@ +# This file is automatically @generated by Poetry 1.6.1 and should not be changed by hand. + +[[package]] +name = "annotated-types" +version = "0.5.0" +description = "Reusable constraint types to use with typing.Annotated" +optional = false +python-versions = ">=3.7" +files = [ + {file = "annotated_types-0.5.0-py3-none-any.whl", hash = "sha256:58da39888f92c276ad970249761ebea80ba544b77acddaa1a4d6cf78287d45fd"}, + {file = "annotated_types-0.5.0.tar.gz", hash = "sha256:47cdc3490d9ac1506ce92c7aaa76c579dc3509ff11e098fc867e5130ab7be802"}, +] + +[package.dependencies] +typing-extensions = {version = ">=4.0.0", markers = "python_version < \"3.9\""} + +[[package]] +name = "anyio" +version = "3.7.1" +description = "High level compatibility layer for multiple asynchronous event loop implementations" +optional = false +python-versions = ">=3.7" +files = [ + {file = "anyio-3.7.1-py3-none-any.whl", hash = "sha256:91dee416e570e92c64041bd18b900d1d6fa78dff7048769ce5ac5ddad004fbb5"}, + {file = "anyio-3.7.1.tar.gz", hash = "sha256:44a3c9aba0f5defa43261a8b3efb97891f2bd7d804e0e1f56419befa1adfc780"}, +] + +[package.dependencies] +exceptiongroup = {version = "*", markers = "python_version < \"3.11\""} +idna = ">=2.8" +sniffio = ">=1.1" + +[package.extras] +doc = ["Sphinx", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme (>=1.2.2)", "sphinxcontrib-jquery"] +test = ["anyio[trio]", "coverage[toml] (>=4.5)", "hypothesis (>=4.0)", "mock (>=4)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (>=0.17)"] +trio = ["trio (<0.22)"] + +[[package]] +name = "backports-zoneinfo" +version = "0.2.1" +description = "Backport of the standard library zoneinfo module" +optional = false +python-versions = ">=3.6" +files = [ + {file = "backports.zoneinfo-0.2.1-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:da6013fd84a690242c310d77ddb8441a559e9cb3d3d59ebac9aca1a57b2e18bc"}, + {file = "backports.zoneinfo-0.2.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:89a48c0d158a3cc3f654da4c2de1ceba85263fafb861b98b59040a5086259722"}, + {file = "backports.zoneinfo-0.2.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:1c5742112073a563c81f786e77514969acb58649bcdf6cdf0b4ed31a348d4546"}, + {file = "backports.zoneinfo-0.2.1-cp36-cp36m-win32.whl", hash = "sha256:e8236383a20872c0cdf5a62b554b27538db7fa1bbec52429d8d106effbaeca08"}, + {file = "backports.zoneinfo-0.2.1-cp36-cp36m-win_amd64.whl", hash = "sha256:8439c030a11780786a2002261569bdf362264f605dfa4d65090b64b05c9f79a7"}, + {file = "backports.zoneinfo-0.2.1-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:f04e857b59d9d1ccc39ce2da1021d196e47234873820cbeaad210724b1ee28ac"}, + {file = "backports.zoneinfo-0.2.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:17746bd546106fa389c51dbea67c8b7c8f0d14b5526a579ca6ccf5ed72c526cf"}, + {file = "backports.zoneinfo-0.2.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:5c144945a7752ca544b4b78c8c41544cdfaf9786f25fe5ffb10e838e19a27570"}, + {file = "backports.zoneinfo-0.2.1-cp37-cp37m-win32.whl", hash = "sha256:e55b384612d93be96506932a786bbcde5a2db7a9e6a4bb4bffe8b733f5b9036b"}, + {file = "backports.zoneinfo-0.2.1-cp37-cp37m-win_amd64.whl", hash = "sha256:a76b38c52400b762e48131494ba26be363491ac4f9a04c1b7e92483d169f6582"}, + {file = "backports.zoneinfo-0.2.1-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:8961c0f32cd0336fb8e8ead11a1f8cd99ec07145ec2931122faaac1c8f7fd987"}, + {file = "backports.zoneinfo-0.2.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:e81b76cace8eda1fca50e345242ba977f9be6ae3945af8d46326d776b4cf78d1"}, + {file = "backports.zoneinfo-0.2.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:7b0a64cda4145548fed9efc10322770f929b944ce5cee6c0dfe0c87bf4c0c8c9"}, + {file = "backports.zoneinfo-0.2.1-cp38-cp38-win32.whl", hash = "sha256:1b13e654a55cd45672cb54ed12148cd33628f672548f373963b0bff67b217328"}, + {file = "backports.zoneinfo-0.2.1-cp38-cp38-win_amd64.whl", hash = "sha256:4a0f800587060bf8880f954dbef70de6c11bbe59c673c3d818921f042f9954a6"}, + {file = "backports.zoneinfo-0.2.1.tar.gz", hash = "sha256:fadbfe37f74051d024037f223b8e001611eac868b5c5b06144ef4d8b799862f2"}, +] + +[package.extras] +tzdata = ["tzdata"] + +[[package]] +name = "bbcode" +version = "1.1.0" +description = "A pure python bbcode parser and formatter." +optional = false +python-versions = "*" +files = [ + {file = "bbcode-1.1.0-py2.py3-none-any.whl", hash = "sha256:83802f4b40c92426841a98350bd6ff9ea8fdf8f9b37df1968a88c5864fd225fa"}, + {file = "bbcode-1.1.0.tar.gz", hash = "sha256:eac4fb1d0f6c7ce5c41e4b5c0522562b15a1ac036fb9131adc59e9a28c7dc1d0"}, +] + +[[package]] +name = "black" +version = "22.12.0" +description = "The uncompromising code formatter." +optional = false +python-versions = ">=3.7" +files = [ + {file = "black-22.12.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9eedd20838bd5d75b80c9f5487dbcb06836a43833a37846cf1d8c1cc01cef59d"}, + {file = "black-22.12.0-cp310-cp310-win_amd64.whl", hash = "sha256:159a46a4947f73387b4d83e87ea006dbb2337eab6c879620a3ba52699b1f4351"}, + {file = "black-22.12.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d30b212bffeb1e252b31dd269dfae69dd17e06d92b87ad26e23890f3efea366f"}, + {file = "black-22.12.0-cp311-cp311-win_amd64.whl", hash = "sha256:7412e75863aa5c5411886804678b7d083c7c28421210180d67dfd8cf1221e1f4"}, + {file = "black-22.12.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c116eed0efb9ff870ded8b62fe9f28dd61ef6e9ddd28d83d7d264a38417dcee2"}, + {file = "black-22.12.0-cp37-cp37m-win_amd64.whl", hash = "sha256:1f58cbe16dfe8c12b7434e50ff889fa479072096d79f0a7f25e4ab8e94cd8350"}, + {file = "black-22.12.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:77d86c9f3db9b1bf6761244bc0b3572a546f5fe37917a044e02f3166d5aafa7d"}, + {file = "black-22.12.0-cp38-cp38-win_amd64.whl", hash = "sha256:82d9fe8fee3401e02e79767016b4907820a7dc28d70d137eb397b92ef3cc5bfc"}, + {file = "black-22.12.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:101c69b23df9b44247bd88e1d7e90154336ac4992502d4197bdac35dd7ee3320"}, + {file = "black-22.12.0-cp39-cp39-win_amd64.whl", hash = "sha256:559c7a1ba9a006226f09e4916060982fd27334ae1998e7a38b3f33a37f7a2148"}, + {file = "black-22.12.0-py3-none-any.whl", hash = "sha256:436cc9167dd28040ad90d3b404aec22cedf24a6e4d7de221bec2730ec0c97bcf"}, + {file = "black-22.12.0.tar.gz", hash = "sha256:229351e5a18ca30f447bf724d007f890f97e13af070bb6ad4c0a441cd7596a2f"}, +] + +[package.dependencies] +click = ">=8.0.0" +mypy-extensions = ">=0.4.3" +pathspec = ">=0.9.0" +platformdirs = ">=2" +tomli = {version = ">=1.1.0", markers = "python_full_version < \"3.11.0a7\""} +typing-extensions = {version = ">=3.10.0.0", markers = "python_version < \"3.10\""} + +[package.extras] +colorama = ["colorama (>=0.4.3)"] +d = ["aiohttp (>=3.7.4)"] +jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] +uvloop = ["uvloop (>=0.15.2)"] + +[[package]] +name = "certifi" +version = "2023.7.22" +description = "Python package for providing Mozilla's CA Bundle." +optional = false +python-versions = ">=3.6" +files = [ + {file = "certifi-2023.7.22-py3-none-any.whl", hash = "sha256:92d6037539857d8206b8f6ae472e8b77db8058fec5937a1ef3f54304089edbb9"}, + {file = "certifi-2023.7.22.tar.gz", hash = "sha256:539cc1d13202e33ca466e88b2807e29f4c13049d6d87031a3c110744495cb082"}, +] + +[[package]] +name = "click" +version = "8.1.7" +description = "Composable command line interface toolkit" +optional = false +python-versions = ">=3.7" +files = [ + {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, + {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + +[[package]] +name = "colorama" +version = "0.4.6" +description = "Cross-platform colored terminal text." +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +files = [ + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +] + +[[package]] +name = "contourpy" +version = "1.1.0" +description = "Python library for calculating contours of 2D quadrilateral grids" +optional = false +python-versions = ">=3.8" +files = [ + {file = "contourpy-1.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:89f06eff3ce2f4b3eb24c1055a26981bffe4e7264acd86f15b97e40530b794bc"}, + {file = "contourpy-1.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:dffcc2ddec1782dd2f2ce1ef16f070861af4fb78c69862ce0aab801495dda6a3"}, + {file = "contourpy-1.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25ae46595e22f93592d39a7eac3d638cda552c3e1160255258b695f7b58e5655"}, + {file = "contourpy-1.1.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:17cfaf5ec9862bc93af1ec1f302457371c34e688fbd381f4035a06cd47324f48"}, + {file = "contourpy-1.1.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:18a64814ae7bce73925131381603fff0116e2df25230dfc80d6d690aa6e20b37"}, + {file = "contourpy-1.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90c81f22b4f572f8a2110b0b741bb64e5a6427e0a198b2cdc1fbaf85f352a3aa"}, + {file = "contourpy-1.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:53cc3a40635abedbec7f1bde60f8c189c49e84ac180c665f2cd7c162cc454baa"}, + {file = "contourpy-1.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:1f795597073b09d631782e7245016a4323cf1cf0b4e06eef7ea6627e06a37ff2"}, + {file = "contourpy-1.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0b7b04ed0961647691cfe5d82115dd072af7ce8846d31a5fac6c142dcce8b882"}, + {file = "contourpy-1.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:27bc79200c742f9746d7dd51a734ee326a292d77e7d94c8af6e08d1e6c15d545"}, + {file = "contourpy-1.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:052cc634bf903c604ef1a00a5aa093c54f81a2612faedaa43295809ffdde885e"}, + {file = "contourpy-1.1.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9382a1c0bc46230fb881c36229bfa23d8c303b889b788b939365578d762b5c18"}, + {file = "contourpy-1.1.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e5cec36c5090e75a9ac9dbd0ff4a8cf7cecd60f1b6dc23a374c7d980a1cd710e"}, + {file = "contourpy-1.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f0cbd657e9bde94cd0e33aa7df94fb73c1ab7799378d3b3f902eb8eb2e04a3a"}, + {file = "contourpy-1.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:181cbace49874f4358e2929aaf7ba84006acb76694102e88dd15af861996c16e"}, + {file = "contourpy-1.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:fb3b7d9e6243bfa1efb93ccfe64ec610d85cfe5aec2c25f97fbbd2e58b531256"}, + {file = "contourpy-1.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:bcb41692aa09aeb19c7c213411854402f29f6613845ad2453d30bf421fe68fed"}, + {file = "contourpy-1.1.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:5d123a5bc63cd34c27ff9c7ac1cd978909e9c71da12e05be0231c608048bb2ae"}, + {file = "contourpy-1.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:62013a2cf68abc80dadfd2307299bfa8f5aa0dcaec5b2954caeb5fa094171103"}, + {file = "contourpy-1.1.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0b6616375d7de55797d7a66ee7d087efe27f03d336c27cf1f32c02b8c1a5ac70"}, + {file = "contourpy-1.1.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:317267d915490d1e84577924bd61ba71bf8681a30e0d6c545f577363157e5e94"}, + {file = "contourpy-1.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d551f3a442655f3dcc1285723f9acd646ca5858834efeab4598d706206b09c9f"}, + {file = "contourpy-1.1.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:e7a117ce7df5a938fe035cad481b0189049e8d92433b4b33aa7fc609344aafa1"}, + {file = "contourpy-1.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:d4f26b25b4f86087e7d75e63212756c38546e70f2a92d2be44f80114826e1cd4"}, + {file = "contourpy-1.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:bc00bb4225d57bff7ebb634646c0ee2a1298402ec10a5fe7af79df9a51c1bfd9"}, + {file = "contourpy-1.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:189ceb1525eb0655ab8487a9a9c41f42a73ba52d6789754788d1883fb06b2d8a"}, + {file = "contourpy-1.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9f2931ed4741f98f74b410b16e5213f71dcccee67518970c42f64153ea9313b9"}, + {file = "contourpy-1.1.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:30f511c05fab7f12e0b1b7730ebdc2ec8deedcfb505bc27eb570ff47c51a8f15"}, + {file = "contourpy-1.1.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:143dde50520a9f90e4a2703f367cf8ec96a73042b72e68fcd184e1279962eb6f"}, + {file = "contourpy-1.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e94bef2580e25b5fdb183bf98a2faa2adc5b638736b2c0a4da98691da641316a"}, + {file = "contourpy-1.1.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ed614aea8462735e7d70141374bd7650afd1c3f3cb0c2dbbcbe44e14331bf002"}, + {file = "contourpy-1.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:438ba416d02f82b692e371858143970ed2eb6337d9cdbbede0d8ad9f3d7dd17d"}, + {file = "contourpy-1.1.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:a698c6a7a432789e587168573a864a7ea374c6be8d4f31f9d87c001d5a843493"}, + {file = "contourpy-1.1.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:397b0ac8a12880412da3551a8cb5a187d3298a72802b45a3bd1805e204ad8439"}, + {file = "contourpy-1.1.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:a67259c2b493b00e5a4d0f7bfae51fb4b3371395e47d079a4446e9b0f4d70e76"}, + {file = "contourpy-1.1.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:2b836d22bd2c7bb2700348e4521b25e077255ebb6ab68e351ab5aa91ca27e027"}, + {file = "contourpy-1.1.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:084eaa568400cfaf7179b847ac871582199b1b44d5699198e9602ecbbb5f6104"}, + {file = "contourpy-1.1.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:911ff4fd53e26b019f898f32db0d4956c9d227d51338fb3b03ec72ff0084ee5f"}, + {file = "contourpy-1.1.0.tar.gz", hash = "sha256:e53046c3863828d21d531cc3b53786e6580eb1ba02477e8681009b6aa0870b21"}, +] + +[package.dependencies] +numpy = ">=1.16" + +[package.extras] +bokeh = ["bokeh", "selenium"] +docs = ["furo", "sphinx-copybutton"] +mypy = ["contourpy[bokeh,docs]", "docutils-stubs", "mypy (==1.2.0)", "types-Pillow"] +test = ["Pillow", "contourpy[test-no-images]", "matplotlib"] +test-no-images = ["pytest", "pytest-cov", "wurlitzer"] + +[[package]] +name = "cycler" +version = "0.11.0" +description = "Composable style cycles" +optional = false +python-versions = ">=3.6" +files = [ + {file = "cycler-0.11.0-py3-none-any.whl", hash = "sha256:3a27e95f763a428a739d2add979fa7494c912a32c17c4c38c4d5f082cad165a3"}, + {file = "cycler-0.11.0.tar.gz", hash = "sha256:9c87405839a19696e837b3b818fed3f5f69f16f1eec1a1ad77e043dcea9c772f"}, +] + +[[package]] +name = "dateparser" +version = "1.1.8" +description = "Date parsing library designed to parse dates from HTML pages" +optional = false +python-versions = ">=3.7" +files = [ + {file = "dateparser-1.1.8-py2.py3-none-any.whl", hash = "sha256:070b29b5bbf4b1ec2cd51c96ea040dc68a614de703910a91ad1abba18f9f379f"}, + {file = "dateparser-1.1.8.tar.gz", hash = "sha256:86b8b7517efcc558f085a142cdb7620f0921543fcabdb538c8a4c4001d8178e3"}, +] + +[package.dependencies] +python-dateutil = "*" +pytz = "*" +regex = "<2019.02.19 || >2019.02.19,<2021.8.27 || >2021.8.27" +tzlocal = "*" + +[package.extras] +calendars = ["convertdate", "hijri-converter"] +fasttext = ["fasttext"] +langdetect = ["langdetect"] + +[[package]] +name = "exceptiongroup" +version = "1.1.3" +description = "Backport of PEP 654 (exception groups)" +optional = false +python-versions = ">=3.7" +files = [ + {file = "exceptiongroup-1.1.3-py3-none-any.whl", hash = "sha256:343280667a4585d195ca1cf9cef84a4e178c4b6cf2274caef9859782b567d5e3"}, + {file = "exceptiongroup-1.1.3.tar.gz", hash = "sha256:097acd85d473d75af5bb98e41b61ff7fe35efe6675e4f9370ec6ec5126d160e9"}, +] + +[package.extras] +test = ["pytest (>=6)"] + +[[package]] +name = "fastapi" +version = "0.103.0" +description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production" +optional = false +python-versions = ">=3.7" +files = [ + {file = "fastapi-0.103.0-py3-none-any.whl", hash = "sha256:61ab72c6c281205dd0cbaccf503e829a37e0be108d965ac223779a8479243665"}, + {file = "fastapi-0.103.0.tar.gz", hash = "sha256:4166732f5ddf61c33e9fa4664f73780872511e0598d4d5434b1816dc1e6d9421"}, +] + +[package.dependencies] +pydantic = ">=1.7.4,<1.8 || >1.8,<1.8.1 || >1.8.1,<2.0.0 || >2.0.0,<2.0.1 || >2.0.1,<2.1.0 || >2.1.0,<3.0.0" +starlette = ">=0.27.0,<0.28.0" +typing-extensions = ">=4.5.0" + +[package.extras] +all = ["email-validator (>=2.0.0)", "httpx (>=0.23.0)", "itsdangerous (>=1.1.0)", "jinja2 (>=2.11.2)", "orjson (>=3.2.1)", "pydantic-extra-types (>=2.0.0)", "pydantic-settings (>=2.0.0)", "python-multipart (>=0.0.5)", "pyyaml (>=5.3.1)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0)", "uvicorn[standard] (>=0.12.0)"] + +[[package]] +name = "filetype" +version = "1.2.0" +description = "Infer file type and MIME type of any file/buffer. No external dependencies." +optional = false +python-versions = "*" +files = [ + {file = "filetype-1.2.0-py2.py3-none-any.whl", hash = "sha256:7ce71b6880181241cf7ac8697a2f1eb6a8bd9b429f7ad6d27b8db9ba5f1c2d25"}, + {file = "filetype-1.2.0.tar.gz", hash = "sha256:66b56cd6474bf41d8c54660347d37afcc3f7d1970648de365c102ef77548aadb"}, +] + +[[package]] +name = "fonttools" +version = "4.42.1" +description = "Tools to manipulate font files" +optional = false +python-versions = ">=3.8" +files = [ + {file = "fonttools-4.42.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ed1a13a27f59d1fc1920394a7f596792e9d546c9ca5a044419dca70c37815d7c"}, + {file = "fonttools-4.42.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c9b1ce7a45978b821a06d375b83763b27a3a5e8a2e4570b3065abad240a18760"}, + {file = "fonttools-4.42.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f720fa82a11c0f9042376fd509b5ed88dab7e3cd602eee63a1af08883b37342b"}, + {file = "fonttools-4.42.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db55cbaea02a20b49fefbd8e9d62bd481aaabe1f2301dabc575acc6b358874fa"}, + {file = "fonttools-4.42.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:3a35981d90feebeaef05e46e33e6b9e5b5e618504672ca9cd0ff96b171e4bfff"}, + {file = "fonttools-4.42.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:68a02bbe020dc22ee0540e040117535f06df9358106d3775e8817d826047f3fd"}, + {file = "fonttools-4.42.1-cp310-cp310-win32.whl", hash = "sha256:12a7c247d1b946829bfa2f331107a629ea77dc5391dfd34fdcd78efa61f354ca"}, + {file = "fonttools-4.42.1-cp310-cp310-win_amd64.whl", hash = "sha256:a398bdadb055f8de69f62b0fc70625f7cbdab436bbb31eef5816e28cab083ee8"}, + {file = "fonttools-4.42.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:689508b918332fb40ce117131633647731d098b1b10d092234aa959b4251add5"}, + {file = "fonttools-4.42.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9e36344e48af3e3bde867a1ca54f97c308735dd8697005c2d24a86054a114a71"}, + {file = "fonttools-4.42.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:19b7db825c8adee96fac0692e6e1ecd858cae9affb3b4812cdb9d934a898b29e"}, + {file = "fonttools-4.42.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:113337c2d29665839b7d90b39f99b3cac731f72a0eda9306165a305c7c31d341"}, + {file = "fonttools-4.42.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:37983b6bdab42c501202500a2be3a572f50d4efe3237e0686ee9d5f794d76b35"}, + {file = "fonttools-4.42.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:6ed2662a3d9c832afa36405f8748c250be94ae5dfc5283d668308391f2102861"}, + {file = "fonttools-4.42.1-cp311-cp311-win32.whl", hash = "sha256:179737095eb98332a2744e8f12037b2977f22948cf23ff96656928923ddf560a"}, + {file = "fonttools-4.42.1-cp311-cp311-win_amd64.whl", hash = "sha256:f2b82f46917d8722e6b5eafeefb4fb585d23babd15d8246c664cd88a5bddd19c"}, + {file = "fonttools-4.42.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:62f481ac772fd68901573956231aea3e4b1ad87b9b1089a61613a91e2b50bb9b"}, + {file = "fonttools-4.42.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f2f806990160d1ce42d287aa419df3ffc42dfefe60d473695fb048355fe0c6a0"}, + {file = "fonttools-4.42.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:db372213d39fa33af667c2aa586a0c1235e88e9c850f5dd5c8e1f17515861868"}, + {file = "fonttools-4.42.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d18fc642fd0ac29236ff88ecfccff229ec0386090a839dd3f1162e9a7944a40"}, + {file = "fonttools-4.42.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:8708b98c278012ad267ee8a7433baeb809948855e81922878118464b274c909d"}, + {file = "fonttools-4.42.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:c95b0724a6deea2c8c5d3222191783ced0a2f09bd6d33f93e563f6f1a4b3b3a4"}, + {file = "fonttools-4.42.1-cp38-cp38-win32.whl", hash = "sha256:4aa79366e442dbca6e2c8595645a3a605d9eeabdb7a094d745ed6106816bef5d"}, + {file = "fonttools-4.42.1-cp38-cp38-win_amd64.whl", hash = "sha256:acb47f6f8680de24c1ab65ebde39dd035768e2a9b571a07c7b8da95f6c8815fd"}, + {file = "fonttools-4.42.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:5fb289b7a815638a7613d46bcf324c9106804725b2bb8ad913c12b6958ffc4ec"}, + {file = "fonttools-4.42.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:53eb5091ddc8b1199330bb7b4a8a2e7995ad5d43376cadce84523d8223ef3136"}, + {file = "fonttools-4.42.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:46a0ec8adbc6ff13494eb0c9c2e643b6f009ce7320cf640de106fb614e4d4360"}, + {file = "fonttools-4.42.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7cc7d685b8eeca7ae69dc6416833fbfea61660684b7089bca666067cb2937dcf"}, + {file = "fonttools-4.42.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:be24fcb80493b2c94eae21df70017351851652a37de514de553435b256b2f249"}, + {file = "fonttools-4.42.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:515607ec756d7865f23070682622c49d922901943697871fc292277cf1e71967"}, + {file = "fonttools-4.42.1-cp39-cp39-win32.whl", hash = "sha256:0eb79a2da5eb6457a6f8ab904838454accc7d4cccdaff1fd2bd3a0679ea33d64"}, + {file = "fonttools-4.42.1-cp39-cp39-win_amd64.whl", hash = "sha256:7286aed4ea271df9eab8d7a9b29e507094b51397812f7ce051ecd77915a6e26b"}, + {file = "fonttools-4.42.1-py3-none-any.whl", hash = "sha256:9398f244e28e0596e2ee6024f808b06060109e33ed38dcc9bded452fd9bbb853"}, + {file = "fonttools-4.42.1.tar.gz", hash = "sha256:c391cd5af88aacaf41dd7cfb96eeedfad297b5899a39e12f4c2c3706d0a3329d"}, +] + +[package.extras] +all = ["brotli (>=1.0.1)", "brotlicffi (>=0.8.0)", "fs (>=2.2.0,<3)", "lxml (>=4.0,<5)", "lz4 (>=1.7.4.2)", "matplotlib", "munkres", "scipy", "skia-pathops (>=0.5.0)", "sympy", "uharfbuzz (>=0.23.0)", "unicodedata2 (>=15.0.0)", "xattr", "zopfli (>=0.1.4)"] +graphite = ["lz4 (>=1.7.4.2)"] +interpolatable = ["munkres", "scipy"] +lxml = ["lxml (>=4.0,<5)"] +pathops = ["skia-pathops (>=0.5.0)"] +plot = ["matplotlib"] +repacker = ["uharfbuzz (>=0.23.0)"] +symfont = ["sympy"] +type1 = ["xattr"] +ufo = ["fs (>=2.2.0,<3)"] +unicode = ["unicodedata2 (>=15.0.0)"] +woff = ["brotli (>=1.0.1)", "brotlicffi (>=0.8.0)", "zopfli (>=0.1.4)"] + +[[package]] +name = "h11" +version = "0.14.0" +description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" +optional = false +python-versions = ">=3.7" +files = [ + {file = "h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"}, + {file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"}, +] + +[[package]] +name = "httpcore" +version = "0.17.3" +description = "A minimal low-level HTTP client." +optional = false +python-versions = ">=3.7" +files = [ + {file = "httpcore-0.17.3-py3-none-any.whl", hash = "sha256:c2789b767ddddfa2a5782e3199b2b7f6894540b17b16ec26b2c4d8e103510b87"}, + {file = "httpcore-0.17.3.tar.gz", hash = "sha256:a6f30213335e34c1ade7be6ec7c47f19f50c56db36abef1a9dfa3815b1cb3888"}, +] + +[package.dependencies] +anyio = ">=3.0,<5.0" +certifi = "*" +h11 = ">=0.13,<0.15" +sniffio = "==1.*" + +[package.extras] +http2 = ["h2 (>=3,<5)"] +socks = ["socksio (==1.*)"] + +[[package]] +name = "httpx" +version = "0.24.1" +description = "The next generation HTTP client." +optional = false +python-versions = ">=3.7" +files = [ + {file = "httpx-0.24.1-py3-none-any.whl", hash = "sha256:06781eb9ac53cde990577af654bd990a4949de37a28bdb4a230d434f3a30b9bd"}, + {file = "httpx-0.24.1.tar.gz", hash = "sha256:5853a43053df830c20f8110c5e69fe44d035d850b2dfe795e196f00fdb774bdd"}, +] + +[package.dependencies] +certifi = "*" +httpcore = ">=0.15.0,<0.18.0" +idna = "*" +sniffio = "*" + +[package.extras] +brotli = ["brotli", "brotlicffi"] +cli = ["click (==8.*)", "pygments (==2.*)", "rich (>=10,<14)"] +http2 = ["h2 (>=3,<5)"] +socks = ["socksio (==1.*)"] + +[[package]] +name = "idna" +version = "3.4" +description = "Internationalized Domain Names in Applications (IDNA)" +optional = false +python-versions = ">=3.5" +files = [ + {file = "idna-3.4-py3-none-any.whl", hash = "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2"}, + {file = "idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"}, +] + +[[package]] +name = "importlib-resources" +version = "6.0.1" +description = "Read resources from Python packages" +optional = false +python-versions = ">=3.8" +files = [ + {file = "importlib_resources-6.0.1-py3-none-any.whl", hash = "sha256:134832a506243891221b88b4ae1213327eea96ceb4e407a00d790bb0626f45cf"}, + {file = "importlib_resources-6.0.1.tar.gz", hash = "sha256:4359457e42708462b9626a04657c6208ad799ceb41e5c58c57ffa0e6a098a5d4"}, +] + +[package.dependencies] +zipp = {version = ">=3.1.0", markers = "python_version < \"3.10\""} + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +testing = ["pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-ruff"] + +[[package]] +name = "kiwisolver" +version = "1.4.5" +description = "A fast implementation of the Cassowary constraint solver" +optional = false +python-versions = ">=3.7" +files = [ + {file = "kiwisolver-1.4.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:05703cf211d585109fcd72207a31bb170a0f22144d68298dc5e61b3c946518af"}, + {file = "kiwisolver-1.4.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:146d14bebb7f1dc4d5fbf74f8a6cb15ac42baadee8912eb84ac0b3b2a3dc6ac3"}, + {file = "kiwisolver-1.4.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6ef7afcd2d281494c0a9101d5c571970708ad911d028137cd558f02b851c08b4"}, + {file = "kiwisolver-1.4.5-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:9eaa8b117dc8337728e834b9c6e2611f10c79e38f65157c4c38e9400286f5cb1"}, + {file = "kiwisolver-1.4.5-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ec20916e7b4cbfb1f12380e46486ec4bcbaa91a9c448b97023fde0d5bbf9e4ff"}, + {file = "kiwisolver-1.4.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:39b42c68602539407884cf70d6a480a469b93b81b7701378ba5e2328660c847a"}, + {file = "kiwisolver-1.4.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:aa12042de0171fad672b6c59df69106d20d5596e4f87b5e8f76df757a7c399aa"}, + {file = "kiwisolver-1.4.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2a40773c71d7ccdd3798f6489aaac9eee213d566850a9533f8d26332d626b82c"}, + {file = "kiwisolver-1.4.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:19df6e621f6d8b4b9c4d45f40a66839294ff2bb235e64d2178f7522d9170ac5b"}, + {file = "kiwisolver-1.4.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:83d78376d0d4fd884e2c114d0621624b73d2aba4e2788182d286309ebdeed770"}, + {file = "kiwisolver-1.4.5-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:e391b1f0a8a5a10ab3b9bb6afcfd74f2175f24f8975fb87ecae700d1503cdee0"}, + {file = "kiwisolver-1.4.5-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:852542f9481f4a62dbb5dd99e8ab7aedfeb8fb6342349a181d4036877410f525"}, + {file = "kiwisolver-1.4.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:59edc41b24031bc25108e210c0def6f6c2191210492a972d585a06ff246bb79b"}, + {file = "kiwisolver-1.4.5-cp310-cp310-win32.whl", hash = "sha256:a6aa6315319a052b4ee378aa171959c898a6183f15c1e541821c5c59beaa0238"}, + {file = "kiwisolver-1.4.5-cp310-cp310-win_amd64.whl", hash = "sha256:d0ef46024e6a3d79c01ff13801cb19d0cad7fd859b15037aec74315540acc276"}, + {file = "kiwisolver-1.4.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:11863aa14a51fd6ec28688d76f1735f8f69ab1fabf388851a595d0721af042f5"}, + {file = "kiwisolver-1.4.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8ab3919a9997ab7ef2fbbed0cc99bb28d3c13e6d4b1ad36e97e482558a91be90"}, + {file = "kiwisolver-1.4.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fcc700eadbbccbf6bc1bcb9dbe0786b4b1cb91ca0dcda336eef5c2beed37b797"}, + {file = "kiwisolver-1.4.5-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dfdd7c0b105af050eb3d64997809dc21da247cf44e63dc73ff0fd20b96be55a9"}, + {file = "kiwisolver-1.4.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:76c6a5964640638cdeaa0c359382e5703e9293030fe730018ca06bc2010c4437"}, + {file = "kiwisolver-1.4.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bbea0db94288e29afcc4c28afbf3a7ccaf2d7e027489c449cf7e8f83c6346eb9"}, + {file = "kiwisolver-1.4.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ceec1a6bc6cab1d6ff5d06592a91a692f90ec7505d6463a88a52cc0eb58545da"}, + {file = "kiwisolver-1.4.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:040c1aebeda72197ef477a906782b5ab0d387642e93bda547336b8957c61022e"}, + {file = "kiwisolver-1.4.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:f91de7223d4c7b793867797bacd1ee53bfe7359bd70d27b7b58a04efbb9436c8"}, + {file = "kiwisolver-1.4.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:faae4860798c31530dd184046a900e652c95513796ef51a12bc086710c2eec4d"}, + {file = "kiwisolver-1.4.5-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:b0157420efcb803e71d1b28e2c287518b8808b7cf1ab8af36718fd0a2c453eb0"}, + {file = "kiwisolver-1.4.5-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:06f54715b7737c2fecdbf140d1afb11a33d59508a47bf11bb38ecf21dc9ab79f"}, + {file = "kiwisolver-1.4.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:fdb7adb641a0d13bdcd4ef48e062363d8a9ad4a182ac7647ec88f695e719ae9f"}, + {file = "kiwisolver-1.4.5-cp311-cp311-win32.whl", hash = "sha256:bb86433b1cfe686da83ce32a9d3a8dd308e85c76b60896d58f082136f10bffac"}, + {file = "kiwisolver-1.4.5-cp311-cp311-win_amd64.whl", hash = "sha256:6c08e1312a9cf1074d17b17728d3dfce2a5125b2d791527f33ffbe805200a355"}, + {file = "kiwisolver-1.4.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:32d5cf40c4f7c7b3ca500f8985eb3fb3a7dfc023215e876f207956b5ea26632a"}, + {file = "kiwisolver-1.4.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f846c260f483d1fd217fe5ed7c173fb109efa6b1fc8381c8b7552c5781756192"}, + {file = "kiwisolver-1.4.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5ff5cf3571589b6d13bfbfd6bcd7a3f659e42f96b5fd1c4830c4cf21d4f5ef45"}, + {file = "kiwisolver-1.4.5-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7269d9e5f1084a653d575c7ec012ff57f0c042258bf5db0954bf551c158466e7"}, + {file = "kiwisolver-1.4.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da802a19d6e15dffe4b0c24b38b3af68e6c1a68e6e1d8f30148c83864f3881db"}, + {file = "kiwisolver-1.4.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3aba7311af82e335dd1e36ffff68aaca609ca6290c2cb6d821a39aa075d8e3ff"}, + {file = "kiwisolver-1.4.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:763773d53f07244148ccac5b084da5adb90bfaee39c197554f01b286cf869228"}, + {file = "kiwisolver-1.4.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2270953c0d8cdab5d422bee7d2007f043473f9d2999631c86a223c9db56cbd16"}, + {file = "kiwisolver-1.4.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d099e745a512f7e3bbe7249ca835f4d357c586d78d79ae8f1dcd4d8adeb9bda9"}, + {file = "kiwisolver-1.4.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:74db36e14a7d1ce0986fa104f7d5637aea5c82ca6326ed0ec5694280942d1162"}, + {file = "kiwisolver-1.4.5-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:7e5bab140c309cb3a6ce373a9e71eb7e4873c70c2dda01df6820474f9889d6d4"}, + {file = "kiwisolver-1.4.5-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:0f114aa76dc1b8f636d077979c0ac22e7cd8f3493abbab152f20eb8d3cda71f3"}, + {file = "kiwisolver-1.4.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:88a2df29d4724b9237fc0c6eaf2a1adae0cdc0b3e9f4d8e7dc54b16812d2d81a"}, + {file = "kiwisolver-1.4.5-cp312-cp312-win32.whl", hash = "sha256:72d40b33e834371fd330fb1472ca19d9b8327acb79a5821d4008391db8e29f20"}, + {file = "kiwisolver-1.4.5-cp312-cp312-win_amd64.whl", hash = "sha256:2c5674c4e74d939b9d91dda0fae10597ac7521768fec9e399c70a1f27e2ea2d9"}, + {file = "kiwisolver-1.4.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:3a2b053a0ab7a3960c98725cfb0bf5b48ba82f64ec95fe06f1d06c99b552e130"}, + {file = "kiwisolver-1.4.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3cd32d6c13807e5c66a7cbb79f90b553642f296ae4518a60d8d76243b0ad2898"}, + {file = "kiwisolver-1.4.5-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:59ec7b7c7e1a61061850d53aaf8e93db63dce0c936db1fda2658b70e4a1be709"}, + {file = "kiwisolver-1.4.5-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:da4cfb373035def307905d05041c1d06d8936452fe89d464743ae7fb8371078b"}, + {file = "kiwisolver-1.4.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2400873bccc260b6ae184b2b8a4fec0e4082d30648eadb7c3d9a13405d861e89"}, + {file = "kiwisolver-1.4.5-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:1b04139c4236a0f3aff534479b58f6f849a8b351e1314826c2d230849ed48985"}, + {file = "kiwisolver-1.4.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:4e66e81a5779b65ac21764c295087de82235597a2293d18d943f8e9e32746265"}, + {file = "kiwisolver-1.4.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:7931d8f1f67c4be9ba1dd9c451fb0eeca1a25b89e4d3f89e828fe12a519b782a"}, + {file = "kiwisolver-1.4.5-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:b3f7e75f3015df442238cca659f8baa5f42ce2a8582727981cbfa15fee0ee205"}, + {file = "kiwisolver-1.4.5-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:bbf1d63eef84b2e8c89011b7f2235b1e0bf7dacc11cac9431fc6468e99ac77fb"}, + {file = "kiwisolver-1.4.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:4c380469bd3f970ef677bf2bcba2b6b0b4d5c75e7a020fb863ef75084efad66f"}, + {file = "kiwisolver-1.4.5-cp37-cp37m-win32.whl", hash = "sha256:9408acf3270c4b6baad483865191e3e582b638b1654a007c62e3efe96f09a9a3"}, + {file = "kiwisolver-1.4.5-cp37-cp37m-win_amd64.whl", hash = "sha256:5b94529f9b2591b7af5f3e0e730a4e0a41ea174af35a4fd067775f9bdfeee01a"}, + {file = "kiwisolver-1.4.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:11c7de8f692fc99816e8ac50d1d1aef4f75126eefc33ac79aac02c099fd3db71"}, + {file = "kiwisolver-1.4.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:53abb58632235cd154176ced1ae8f0d29a6657aa1aa9decf50b899b755bc2b93"}, + {file = "kiwisolver-1.4.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:88b9f257ca61b838b6f8094a62418421f87ac2a1069f7e896c36a7d86b5d4c29"}, + {file = "kiwisolver-1.4.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3195782b26fc03aa9c6913d5bad5aeb864bdc372924c093b0f1cebad603dd712"}, + {file = "kiwisolver-1.4.5-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fc579bf0f502e54926519451b920e875f433aceb4624a3646b3252b5caa9e0b6"}, + {file = "kiwisolver-1.4.5-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5a580c91d686376f0f7c295357595c5a026e6cbc3d77b7c36e290201e7c11ecb"}, + {file = "kiwisolver-1.4.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:cfe6ab8da05c01ba6fbea630377b5da2cd9bcbc6338510116b01c1bc939a2c18"}, + {file = "kiwisolver-1.4.5-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:d2e5a98f0ec99beb3c10e13b387f8db39106d53993f498b295f0c914328b1333"}, + {file = "kiwisolver-1.4.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:a51a263952b1429e429ff236d2f5a21c5125437861baeed77f5e1cc2d2c7c6da"}, + {file = "kiwisolver-1.4.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:3edd2fa14e68c9be82c5b16689e8d63d89fe927e56debd6e1dbce7a26a17f81b"}, + {file = "kiwisolver-1.4.5-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:74d1b44c6cfc897df648cc9fdaa09bc3e7679926e6f96df05775d4fb3946571c"}, + {file = "kiwisolver-1.4.5-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:76d9289ed3f7501012e05abb8358bbb129149dbd173f1f57a1bf1c22d19ab7cc"}, + {file = "kiwisolver-1.4.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:92dea1ffe3714fa8eb6a314d2b3c773208d865a0e0d35e713ec54eea08a66250"}, + {file = "kiwisolver-1.4.5-cp38-cp38-win32.whl", hash = "sha256:5c90ae8c8d32e472be041e76f9d2f2dbff4d0b0be8bd4041770eddb18cf49a4e"}, + {file = "kiwisolver-1.4.5-cp38-cp38-win_amd64.whl", hash = "sha256:c7940c1dc63eb37a67721b10d703247552416f719c4188c54e04334321351ced"}, + {file = "kiwisolver-1.4.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:9407b6a5f0d675e8a827ad8742e1d6b49d9c1a1da5d952a67d50ef5f4170b18d"}, + {file = "kiwisolver-1.4.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:15568384086b6df3c65353820a4473575dbad192e35010f622c6ce3eebd57af9"}, + {file = "kiwisolver-1.4.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0dc9db8e79f0036e8173c466d21ef18e1befc02de8bf8aa8dc0813a6dc8a7046"}, + {file = "kiwisolver-1.4.5-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:cdc8a402aaee9a798b50d8b827d7ecf75edc5fb35ea0f91f213ff927c15f4ff0"}, + {file = "kiwisolver-1.4.5-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:6c3bd3cde54cafb87d74d8db50b909705c62b17c2099b8f2e25b461882e544ff"}, + {file = "kiwisolver-1.4.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:955e8513d07a283056b1396e9a57ceddbd272d9252c14f154d450d227606eb54"}, + {file = "kiwisolver-1.4.5-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:346f5343b9e3f00b8db8ba359350eb124b98c99efd0b408728ac6ebf38173958"}, + {file = "kiwisolver-1.4.5-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b9098e0049e88c6a24ff64545cdfc50807818ba6c1b739cae221bbbcbc58aad3"}, + {file = "kiwisolver-1.4.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:00bd361b903dc4bbf4eb165f24d1acbee754fce22ded24c3d56eec268658a5cf"}, + {file = "kiwisolver-1.4.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:7b8b454bac16428b22560d0a1cf0a09875339cab69df61d7805bf48919415901"}, + {file = "kiwisolver-1.4.5-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:f1d072c2eb0ad60d4c183f3fb44ac6f73fb7a8f16a2694a91f988275cbf352f9"}, + {file = "kiwisolver-1.4.5-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:31a82d498054cac9f6d0b53d02bb85811185bcb477d4b60144f915f3b3126342"}, + {file = "kiwisolver-1.4.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:6512cb89e334e4700febbffaaa52761b65b4f5a3cf33f960213d5656cea36a77"}, + {file = "kiwisolver-1.4.5-cp39-cp39-win32.whl", hash = "sha256:9db8ea4c388fdb0f780fe91346fd438657ea602d58348753d9fb265ce1bca67f"}, + {file = "kiwisolver-1.4.5-cp39-cp39-win_amd64.whl", hash = "sha256:59415f46a37f7f2efeec758353dd2eae1b07640d8ca0f0c42548ec4125492635"}, + {file = "kiwisolver-1.4.5-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:5c7b3b3a728dc6faf3fc372ef24f21d1e3cee2ac3e9596691d746e5a536de920"}, + {file = "kiwisolver-1.4.5-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:620ced262a86244e2be10a676b646f29c34537d0d9cc8eb26c08f53d98013390"}, + {file = "kiwisolver-1.4.5-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:378a214a1e3bbf5ac4a8708304318b4f890da88c9e6a07699c4ae7174c09a68d"}, + {file = "kiwisolver-1.4.5-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aaf7be1207676ac608a50cd08f102f6742dbfc70e8d60c4db1c6897f62f71523"}, + {file = "kiwisolver-1.4.5-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:ba55dce0a9b8ff59495ddd050a0225d58bd0983d09f87cfe2b6aec4f2c1234e4"}, + {file = "kiwisolver-1.4.5-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:fd32ea360bcbb92d28933fc05ed09bffcb1704ba3fc7942e81db0fd4f81a7892"}, + {file = "kiwisolver-1.4.5-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:5e7139af55d1688f8b960ee9ad5adafc4ac17c1c473fe07133ac092310d76544"}, + {file = "kiwisolver-1.4.5-pp38-pypy38_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:dced8146011d2bc2e883f9bd68618b8247387f4bbec46d7392b3c3b032640126"}, + {file = "kiwisolver-1.4.5-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c9bf3325c47b11b2e51bca0824ea217c7cd84491d8ac4eefd1e409705ef092bd"}, + {file = "kiwisolver-1.4.5-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:5794cf59533bc3f1b1c821f7206a3617999db9fbefc345360aafe2e067514929"}, + {file = "kiwisolver-1.4.5-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:e368f200bbc2e4f905b8e71eb38b3c04333bddaa6a2464a6355487b02bb7fb09"}, + {file = "kiwisolver-1.4.5-pp39-pypy39_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e5d706eba36b4c4d5bc6c6377bb6568098765e990cfc21ee16d13963fab7b3e7"}, + {file = "kiwisolver-1.4.5-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:85267bd1aa8880a9c88a8cb71e18d3d64d2751a790e6ca6c27b8ccc724bcd5ad"}, + {file = "kiwisolver-1.4.5-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:210ef2c3a1f03272649aff1ef992df2e724748918c4bc2d5a90352849eb40bea"}, + {file = "kiwisolver-1.4.5-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:11d011a7574eb3b82bcc9c1a1d35c1d7075677fdd15de527d91b46bd35e935ee"}, + {file = "kiwisolver-1.4.5.tar.gz", hash = "sha256:e57e563a57fb22a142da34f38acc2fc1a5c864bc29ca1517a88abc963e60d6ec"}, +] + +[[package]] +name = "loguru" +version = "0.7.0" +description = "Python logging made (stupidly) simple" +optional = false +python-versions = ">=3.5" +files = [ + {file = "loguru-0.7.0-py3-none-any.whl", hash = "sha256:b93aa30099fa6860d4727f1b81f8718e965bb96253fa190fab2077aaad6d15d3"}, + {file = "loguru-0.7.0.tar.gz", hash = "sha256:1612053ced6ae84d7959dd7d5e431a0532642237ec21f7fd83ac73fe539e03e1"}, +] + +[package.dependencies] +colorama = {version = ">=0.3.4", markers = "sys_platform == \"win32\""} +win32-setctime = {version = ">=1.0.0", markers = "sys_platform == \"win32\""} + +[package.extras] +dev = ["Sphinx (==5.3.0)", "colorama (==0.4.5)", "colorama (==0.4.6)", "freezegun (==1.1.0)", "freezegun (==1.2.2)", "mypy (==v0.910)", "mypy (==v0.971)", "mypy (==v0.990)", "pre-commit (==3.2.1)", "pytest (==6.1.2)", "pytest (==7.2.1)", "pytest-cov (==2.12.1)", "pytest-cov (==4.0.0)", "pytest-mypy-plugins (==1.10.1)", "pytest-mypy-plugins (==1.9.3)", "sphinx-autobuild (==2021.3.14)", "sphinx-rtd-theme (==1.2.0)", "tox (==3.27.1)", "tox (==4.4.6)"] + +[[package]] +name = "markdown-it-py" +version = "3.0.0" +description = "Python port of markdown-it. Markdown parsing, done right!" +optional = false +python-versions = ">=3.8" +files = [ + {file = "markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb"}, + {file = "markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1"}, +] + +[package.dependencies] +mdurl = ">=0.1,<1.0" + +[package.extras] +benchmarking = ["psutil", "pytest", "pytest-benchmark"] +code-style = ["pre-commit (>=3.0,<4.0)"] +compare = ["commonmark (>=0.9,<1.0)", "markdown (>=3.4,<4.0)", "mistletoe (>=1.0,<2.0)", "mistune (>=2.0,<3.0)", "panflute (>=2.3,<3.0)"] +linkify = ["linkify-it-py (>=1,<3)"] +plugins = ["mdit-py-plugins"] +profiling = ["gprof2dot"] +rtd = ["jupyter_sphinx", "mdit-py-plugins", "myst-parser", "pyyaml", "sphinx", "sphinx-copybutton", "sphinx-design", "sphinx_book_theme"] +testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] + +[[package]] +name = "matplotlib" +version = "3.7.2" +description = "Python plotting package" +optional = false +python-versions = ">=3.8" +files = [ + {file = "matplotlib-3.7.2-cp310-cp310-macosx_10_12_universal2.whl", hash = "sha256:2699f7e73a76d4c110f4f25be9d2496d6ab4f17345307738557d345f099e07de"}, + {file = "matplotlib-3.7.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:a8035ba590658bae7562786c9cc6ea1a84aa49d3afab157e414c9e2ea74f496d"}, + {file = "matplotlib-3.7.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2f8e4a49493add46ad4a8c92f63e19d548b2b6ebbed75c6b4c7f46f57d36cdd1"}, + {file = "matplotlib-3.7.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:71667eb2ccca4c3537d9414b1bc00554cb7f91527c17ee4ec38027201f8f1603"}, + {file = "matplotlib-3.7.2-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:152ee0b569a37630d8628534c628456b28686e085d51394da6b71ef84c4da201"}, + {file = "matplotlib-3.7.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:070f8dddd1f5939e60aacb8fa08f19551f4b0140fab16a3669d5cd6e9cb28fc8"}, + {file = "matplotlib-3.7.2-cp310-cp310-win32.whl", hash = "sha256:fdbb46fad4fb47443b5b8ac76904b2e7a66556844f33370861b4788db0f8816a"}, + {file = "matplotlib-3.7.2-cp310-cp310-win_amd64.whl", hash = "sha256:23fb1750934e5f0128f9423db27c474aa32534cec21f7b2153262b066a581fd1"}, + {file = "matplotlib-3.7.2-cp311-cp311-macosx_10_12_universal2.whl", hash = "sha256:30e1409b857aa8a747c5d4f85f63a79e479835f8dffc52992ac1f3f25837b544"}, + {file = "matplotlib-3.7.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:50e0a55ec74bf2d7a0ebf50ac580a209582c2dd0f7ab51bc270f1b4a0027454e"}, + {file = "matplotlib-3.7.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ac60daa1dc83e8821eed155796b0f7888b6b916cf61d620a4ddd8200ac70cd64"}, + {file = "matplotlib-3.7.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:305e3da477dc8607336ba10bac96986d6308d614706cae2efe7d3ffa60465b24"}, + {file = "matplotlib-3.7.2-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1c308b255efb9b06b23874236ec0f10f026673ad6515f602027cc8ac7805352d"}, + {file = "matplotlib-3.7.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:60c521e21031632aa0d87ca5ba0c1c05f3daacadb34c093585a0be6780f698e4"}, + {file = "matplotlib-3.7.2-cp311-cp311-win32.whl", hash = "sha256:26bede320d77e469fdf1bde212de0ec889169b04f7f1179b8930d66f82b30cbc"}, + {file = "matplotlib-3.7.2-cp311-cp311-win_amd64.whl", hash = "sha256:af4860132c8c05261a5f5f8467f1b269bf1c7c23902d75f2be57c4a7f2394b3e"}, + {file = "matplotlib-3.7.2-cp38-cp38-macosx_10_12_universal2.whl", hash = "sha256:a1733b8e84e7e40a9853e505fe68cc54339f97273bdfe6f3ed980095f769ddc7"}, + {file = "matplotlib-3.7.2-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:d9881356dc48e58910c53af82b57183879129fa30492be69058c5b0d9fddf391"}, + {file = "matplotlib-3.7.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:f081c03f413f59390a80b3e351cc2b2ea0205839714dbc364519bcf51f4b56ca"}, + {file = "matplotlib-3.7.2-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:1cd120fca3407a225168238b790bd5c528f0fafde6172b140a2f3ab7a4ea63e9"}, + {file = "matplotlib-3.7.2-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:a2c1590b90aa7bd741b54c62b78de05d4186271e34e2377e0289d943b3522273"}, + {file = "matplotlib-3.7.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6d2ff3c984b8a569bc1383cd468fc06b70d7b59d5c2854ca39f1436ae8394117"}, + {file = "matplotlib-3.7.2-cp38-cp38-win32.whl", hash = "sha256:5dea00b62d28654b71ca92463656d80646675628d0828e08a5f3b57e12869e13"}, + {file = "matplotlib-3.7.2-cp38-cp38-win_amd64.whl", hash = "sha256:0f506a1776ee94f9e131af1ac6efa6e5bc7cb606a3e389b0ccb6e657f60bb676"}, + {file = "matplotlib-3.7.2-cp39-cp39-macosx_10_12_universal2.whl", hash = "sha256:6515e878f91894c2e4340d81f0911857998ccaf04dbc1bba781e3d89cbf70608"}, + {file = "matplotlib-3.7.2-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:71f7a8c6b124e904db550f5b9fe483d28b896d4135e45c4ea381ad3b8a0e3256"}, + {file = "matplotlib-3.7.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:12f01b92ecd518e0697da4d97d163b2b3aa55eb3eb4e2c98235b3396d7dad55f"}, + {file = "matplotlib-3.7.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a7e28d6396563955f7af437894a36bf2b279462239a41028323e04b85179058b"}, + {file = "matplotlib-3.7.2-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dbcf59334ff645e6a67cd5f78b4b2cdb76384cdf587fa0d2dc85f634a72e1a3e"}, + {file = "matplotlib-3.7.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:318c89edde72ff95d8df67d82aca03861240512994a597a435a1011ba18dbc7f"}, + {file = "matplotlib-3.7.2-cp39-cp39-win32.whl", hash = "sha256:ce55289d5659b5b12b3db4dc9b7075b70cef5631e56530f14b2945e8836f2d20"}, + {file = "matplotlib-3.7.2-cp39-cp39-win_amd64.whl", hash = "sha256:2ecb5be2b2815431c81dc115667e33da0f5a1bcf6143980d180d09a717c4a12e"}, + {file = "matplotlib-3.7.2-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:fdcd28360dbb6203fb5219b1a5658df226ac9bebc2542a9e8f457de959d713d0"}, + {file = "matplotlib-3.7.2-pp38-pypy38_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0c3cca3e842b11b55b52c6fb8bd6a4088693829acbfcdb3e815fa9b7d5c92c1b"}, + {file = "matplotlib-3.7.2-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ebf577c7a6744e9e1bd3fee45fc74a02710b214f94e2bde344912d85e0c9af7c"}, + {file = "matplotlib-3.7.2-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:936bba394682049919dda062d33435b3be211dc3dcaa011e09634f060ec878b2"}, + {file = "matplotlib-3.7.2-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:bc221ffbc2150458b1cd71cdd9ddd5bb37962b036e41b8be258280b5b01da1dd"}, + {file = "matplotlib-3.7.2-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:35d74ebdb3f71f112b36c2629cf32323adfbf42679e2751252acd468f5001c07"}, + {file = "matplotlib-3.7.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:717157e61b3a71d3d26ad4e1770dc85156c9af435659a25ee6407dc866cb258d"}, + {file = "matplotlib-3.7.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:20f844d6be031948148ba49605c8b96dfe7d3711d1b63592830d650622458c11"}, + {file = "matplotlib-3.7.2.tar.gz", hash = "sha256:a8cdb91dddb04436bd2f098b8fdf4b81352e68cf4d2c6756fcc414791076569b"}, +] + +[package.dependencies] +contourpy = ">=1.0.1" +cycler = ">=0.10" +fonttools = ">=4.22.0" +importlib-resources = {version = ">=3.2.0", markers = "python_version < \"3.10\""} +kiwisolver = ">=1.0.1" +numpy = ">=1.20" +packaging = ">=20.0" +pillow = ">=6.2.0" +pyparsing = ">=2.3.1,<3.1" +python-dateutil = ">=2.7" + +[[package]] +name = "mdurl" +version = "0.1.2" +description = "Markdown URL utilities" +optional = false +python-versions = ">=3.7" +files = [ + {file = "mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"}, + {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"}, +] + +[[package]] +name = "mypy-extensions" +version = "1.0.0" +description = "Type system extensions for programs checked with the mypy type checker." +optional = false +python-versions = ">=3.5" +files = [ + {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, + {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, +] + +[[package]] +name = "numpy" +version = "1.24.4" +description = "Fundamental package for array computing in Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "numpy-1.24.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c0bfb52d2169d58c1cdb8cc1f16989101639b34c7d3ce60ed70b19c63eba0b64"}, + {file = "numpy-1.24.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ed094d4f0c177b1b8e7aa9cba7d6ceed51c0e569a5318ac0ca9a090680a6a1b1"}, + {file = "numpy-1.24.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:79fc682a374c4a8ed08b331bef9c5f582585d1048fa6d80bc6c35bc384eee9b4"}, + {file = "numpy-1.24.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7ffe43c74893dbf38c2b0a1f5428760a1a9c98285553c89e12d70a96a7f3a4d6"}, + {file = "numpy-1.24.4-cp310-cp310-win32.whl", hash = "sha256:4c21decb6ea94057331e111a5bed9a79d335658c27ce2adb580fb4d54f2ad9bc"}, + {file = "numpy-1.24.4-cp310-cp310-win_amd64.whl", hash = "sha256:b4bea75e47d9586d31e892a7401f76e909712a0fd510f58f5337bea9572c571e"}, + {file = "numpy-1.24.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f136bab9c2cfd8da131132c2cf6cc27331dd6fae65f95f69dcd4ae3c3639c810"}, + {file = "numpy-1.24.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e2926dac25b313635e4d6cf4dc4e51c8c0ebfed60b801c799ffc4c32bf3d1254"}, + {file = "numpy-1.24.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:222e40d0e2548690405b0b3c7b21d1169117391c2e82c378467ef9ab4c8f0da7"}, + {file = "numpy-1.24.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7215847ce88a85ce39baf9e89070cb860c98fdddacbaa6c0da3ffb31b3350bd5"}, + {file = "numpy-1.24.4-cp311-cp311-win32.whl", hash = "sha256:4979217d7de511a8d57f4b4b5b2b965f707768440c17cb70fbf254c4b225238d"}, + {file = "numpy-1.24.4-cp311-cp311-win_amd64.whl", hash = "sha256:b7b1fc9864d7d39e28f41d089bfd6353cb5f27ecd9905348c24187a768c79694"}, + {file = "numpy-1.24.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1452241c290f3e2a312c137a9999cdbf63f78864d63c79039bda65ee86943f61"}, + {file = "numpy-1.24.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:04640dab83f7c6c85abf9cd729c5b65f1ebd0ccf9de90b270cd61935eef0197f"}, + {file = "numpy-1.24.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5425b114831d1e77e4b5d812b69d11d962e104095a5b9c3b641a218abcc050e"}, + {file = "numpy-1.24.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd80e219fd4c71fc3699fc1dadac5dcf4fd882bfc6f7ec53d30fa197b8ee22dc"}, + {file = "numpy-1.24.4-cp38-cp38-win32.whl", hash = "sha256:4602244f345453db537be5314d3983dbf5834a9701b7723ec28923e2889e0bb2"}, + {file = "numpy-1.24.4-cp38-cp38-win_amd64.whl", hash = "sha256:692f2e0f55794943c5bfff12b3f56f99af76f902fc47487bdfe97856de51a706"}, + {file = "numpy-1.24.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:2541312fbf09977f3b3ad449c4e5f4bb55d0dbf79226d7724211acc905049400"}, + {file = "numpy-1.24.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9667575fb6d13c95f1b36aca12c5ee3356bf001b714fc354eb5465ce1609e62f"}, + {file = "numpy-1.24.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f3a86ed21e4f87050382c7bc96571755193c4c1392490744ac73d660e8f564a9"}, + {file = "numpy-1.24.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d11efb4dbecbdf22508d55e48d9c8384db795e1b7b51ea735289ff96613ff74d"}, + {file = "numpy-1.24.4-cp39-cp39-win32.whl", hash = "sha256:6620c0acd41dbcb368610bb2f4d83145674040025e5536954782467100aa8835"}, + {file = "numpy-1.24.4-cp39-cp39-win_amd64.whl", hash = "sha256:befe2bf740fd8373cf56149a5c23a0f601e82869598d41f8e188a0e9869926f8"}, + {file = "numpy-1.24.4-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:31f13e25b4e304632a4619d0e0777662c2ffea99fcae2029556b17d8ff958aef"}, + {file = "numpy-1.24.4-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95f7ac6540e95bc440ad77f56e520da5bf877f87dca58bd095288dce8940532a"}, + {file = "numpy-1.24.4-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:e98f220aa76ca2a977fe435f5b04d7b3470c0a2e6312907b37ba6068f26787f2"}, + {file = "numpy-1.24.4.tar.gz", hash = "sha256:80f5e3a4e498641401868df4208b74581206afbee7cf7b8329daae82676d9463"}, +] + +[[package]] +name = "opencv-python-headless" +version = "4.8.0.76" +description = "Wrapper package for OpenCV python bindings." +optional = false +python-versions = ">=3.6" +files = [ + {file = "opencv-python-headless-4.8.0.76.tar.gz", hash = "sha256:bc15726187dae26d8a08777faf6bc71d38f20c785c102677f58ba0e935003afb"}, + {file = "opencv_python_headless-4.8.0.76-cp37-abi3-macosx_10_16_x86_64.whl", hash = "sha256:f85d2e3b9d952db35d31f9db8882d073c903921b72b8db1cfed8bbc75e8d3e63"}, + {file = "opencv_python_headless-4.8.0.76-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:8ee3bf1c9086493c340c6a87899f1c7778d729de92bce8560b8c31ab8a9cdf79"}, + {file = "opencv_python_headless-4.8.0.76-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c675b8dec6298ba6a1eec2ce24077a393b4236a043f68dfacb06bf594354ce06"}, + {file = "opencv_python_headless-4.8.0.76-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:220d2e292fa45ef0582aab730460bbc15cfe61f2089208167a372ccf76f01e21"}, + {file = "opencv_python_headless-4.8.0.76-cp37-abi3-win32.whl", hash = "sha256:df0608de207ae9b094ad9eaf1a475cf6e9a069fb12cd289d4a18cefdab2f8aa8"}, + {file = "opencv_python_headless-4.8.0.76-cp37-abi3-win_amd64.whl", hash = "sha256:9c094faf6ec7bd360244647b26ebdf8f54edec1d9292cb9179fff9badcca7be8"}, +] + +[package.dependencies] +numpy = [ + {version = ">=1.21.0", markers = "python_version <= \"3.9\" and platform_system == \"Darwin\" and platform_machine == \"arm64\" and python_version >= \"3.8\""}, + {version = ">=1.23.5", markers = "python_version >= \"3.11\""}, + {version = ">=1.21.4", markers = "python_version >= \"3.10\" and platform_system == \"Darwin\" and python_version < \"3.11\""}, + {version = ">=1.21.2", markers = "platform_system != \"Darwin\" and python_version >= \"3.10\" and python_version < \"3.11\""}, + {version = ">=1.19.3", markers = "platform_system == \"Linux\" and platform_machine == \"aarch64\" and python_version >= \"3.8\" and python_version < \"3.10\" or python_version > \"3.9\" and python_version < \"3.10\" or python_version >= \"3.9\" and platform_system != \"Darwin\" and python_version < \"3.10\" or python_version >= \"3.9\" and platform_machine != \"arm64\" and python_version < \"3.10\""}, + {version = ">=1.17.3", markers = "(platform_system != \"Darwin\" and platform_system != \"Linux\") and python_version >= \"3.8\" and python_version < \"3.9\" or platform_system != \"Darwin\" and python_version >= \"3.8\" and python_version < \"3.9\" and platform_machine != \"aarch64\" or platform_machine != \"arm64\" and python_version >= \"3.8\" and python_version < \"3.9\" and platform_system != \"Linux\" or (platform_machine != \"arm64\" and platform_machine != \"aarch64\") and python_version >= \"3.8\" and python_version < \"3.9\""}, +] + +[[package]] +name = "packaging" +version = "23.1" +description = "Core utilities for Python packages" +optional = false +python-versions = ">=3.7" +files = [ + {file = "packaging-23.1-py3-none-any.whl", hash = "sha256:994793af429502c4ea2ebf6bf664629d07c1a9fe974af92966e4b8d2df7edc61"}, + {file = "packaging-23.1.tar.gz", hash = "sha256:a392980d2b6cffa644431898be54b0045151319d1e7ec34f0cfed48767dd334f"}, +] + +[[package]] +name = "pathspec" +version = "0.11.2" +description = "Utility library for gitignore style pattern matching of file paths." +optional = false +python-versions = ">=3.7" +files = [ + {file = "pathspec-0.11.2-py3-none-any.whl", hash = "sha256:1d6ed233af05e679efb96b1851550ea95bbb64b7c490b0f5aa52996c11e92a20"}, + {file = "pathspec-0.11.2.tar.gz", hash = "sha256:e0d8d0ac2f12da61956eb2306b69f9469b42f4deb0f3cb6ed47b9cce9996ced3"}, +] + +[[package]] +name = "pil-utils" +version = "0.1.8" +description = "A simple PIL wrapper and text-to-image tool" +optional = false +python-versions = ">=3.8,<4.0" +files = [ + {file = "pil_utils-0.1.8-py3-none-any.whl", hash = "sha256:22b0ed4eeb532386fb61658795c88c77919440f6169ccea76428c83bb0257f72"}, + {file = "pil_utils-0.1.8.tar.gz", hash = "sha256:6223cb1451449a4c9ebc48d6a9e3705711ecf7c120db97d9cd4ca2bc9e0f0618"}, +] + +[package.dependencies] +bbcode = ">=1.1.0,<2.0.0" +fonttools = ">=4.0.0,<5.0.0" +matplotlib = ">=3.0.0,<4.0.0" +numpy = ">=1.20.0,<2.0.0" +opencv-python-headless = ">=4.0.0,<5.0.0" +Pillow = ">=9.2.0,<11.0.0" + +[[package]] +name = "pillow" +version = "10.0.0" +description = "Python Imaging Library (Fork)" +optional = false +python-versions = ">=3.8" +files = [ + {file = "Pillow-10.0.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:1f62406a884ae75fb2f818694469519fb685cc7eaff05d3451a9ebe55c646891"}, + {file = "Pillow-10.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d5db32e2a6ccbb3d34d87c87b432959e0db29755727afb37290e10f6e8e62614"}, + {file = "Pillow-10.0.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:edf4392b77bdc81f36e92d3a07a5cd072f90253197f4a52a55a8cec48a12483b"}, + {file = "Pillow-10.0.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:520f2a520dc040512699f20fa1c363eed506e94248d71f85412b625026f6142c"}, + {file = "Pillow-10.0.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:8c11160913e3dd06c8ffdb5f233a4f254cb449f4dfc0f8f4549eda9e542c93d1"}, + {file = "Pillow-10.0.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:a74ba0c356aaa3bb8e3eb79606a87669e7ec6444be352870623025d75a14a2bf"}, + {file = "Pillow-10.0.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d5d0dae4cfd56969d23d94dc8e89fb6a217be461c69090768227beb8ed28c0a3"}, + {file = "Pillow-10.0.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:22c10cc517668d44b211717fd9775799ccec4124b9a7f7b3635fc5386e584992"}, + {file = "Pillow-10.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:dffe31a7f47b603318c609f378ebcd57f1554a3a6a8effbc59c3c69f804296de"}, + {file = "Pillow-10.0.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:9fb218c8a12e51d7ead2a7c9e101a04982237d4855716af2e9499306728fb485"}, + {file = "Pillow-10.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d35e3c8d9b1268cbf5d3670285feb3528f6680420eafe35cccc686b73c1e330f"}, + {file = "Pillow-10.0.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3ed64f9ca2f0a95411e88a4efbd7a29e5ce2cea36072c53dd9d26d9c76f753b3"}, + {file = "Pillow-10.0.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b6eb5502f45a60a3f411c63187db83a3d3107887ad0d036c13ce836f8a36f1d"}, + {file = "Pillow-10.0.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:c1fbe7621c167ecaa38ad29643d77a9ce7311583761abf7836e1510c580bf3dd"}, + {file = "Pillow-10.0.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:cd25d2a9d2b36fcb318882481367956d2cf91329f6892fe5d385c346c0649629"}, + {file = "Pillow-10.0.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:3b08d4cc24f471b2c8ca24ec060abf4bebc6b144cb89cba638c720546b1cf538"}, + {file = "Pillow-10.0.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d737a602fbd82afd892ca746392401b634e278cb65d55c4b7a8f48e9ef8d008d"}, + {file = "Pillow-10.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:3a82c40d706d9aa9734289740ce26460a11aeec2d9c79b7af87bb35f0073c12f"}, + {file = "Pillow-10.0.0-cp311-cp311-win_arm64.whl", hash = "sha256:bc2ec7c7b5d66b8ec9ce9f720dbb5fa4bace0f545acd34870eff4a369b44bf37"}, + {file = "Pillow-10.0.0-cp312-cp312-macosx_10_10_x86_64.whl", hash = "sha256:d80cf684b541685fccdd84c485b31ce73fc5c9b5d7523bf1394ce134a60c6883"}, + {file = "Pillow-10.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:76de421f9c326da8f43d690110f0e79fe3ad1e54be811545d7d91898b4c8493e"}, + {file = "Pillow-10.0.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:81ff539a12457809666fef6624684c008e00ff6bf455b4b89fd00a140eecd640"}, + {file = "Pillow-10.0.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce543ed15570eedbb85df19b0a1a7314a9c8141a36ce089c0a894adbfccb4568"}, + {file = "Pillow-10.0.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:685ac03cc4ed5ebc15ad5c23bc555d68a87777586d970c2c3e216619a5476223"}, + {file = "Pillow-10.0.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:d72e2ecc68a942e8cf9739619b7f408cc7b272b279b56b2c83c6123fcfa5cdff"}, + {file = "Pillow-10.0.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d50b6aec14bc737742ca96e85d6d0a5f9bfbded018264b3b70ff9d8c33485551"}, + {file = "Pillow-10.0.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:00e65f5e822decd501e374b0650146063fbb30a7264b4d2744bdd7b913e0cab5"}, + {file = "Pillow-10.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:f31f9fdbfecb042d046f9d91270a0ba28368a723302786c0009ee9b9f1f60199"}, + {file = "Pillow-10.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:1ce91b6ec08d866b14413d3f0bbdea7e24dfdc8e59f562bb77bc3fe60b6144ca"}, + {file = "Pillow-10.0.0-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:349930d6e9c685c089284b013478d6f76e3a534e36ddfa912cde493f235372f3"}, + {file = "Pillow-10.0.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:3a684105f7c32488f7153905a4e3015a3b6c7182e106fe3c37fbb5ef3e6994c3"}, + {file = "Pillow-10.0.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b4f69b3700201b80bb82c3a97d5e9254084f6dd5fb5b16fc1a7b974260f89f43"}, + {file = "Pillow-10.0.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3f07ea8d2f827d7d2a49ecf1639ec02d75ffd1b88dcc5b3a61bbb37a8759ad8d"}, + {file = "Pillow-10.0.0-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:040586f7d37b34547153fa383f7f9aed68b738992380ac911447bb78f2abe530"}, + {file = "Pillow-10.0.0-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:f88a0b92277de8e3ca715a0d79d68dc82807457dae3ab8699c758f07c20b3c51"}, + {file = "Pillow-10.0.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:c7cf14a27b0d6adfaebb3ae4153f1e516df54e47e42dcc073d7b3d76111a8d86"}, + {file = "Pillow-10.0.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:3400aae60685b06bb96f99a21e1ada7bc7a413d5f49bce739828ecd9391bb8f7"}, + {file = "Pillow-10.0.0-cp38-cp38-win_amd64.whl", hash = "sha256:dbc02381779d412145331789b40cc7b11fdf449e5d94f6bc0b080db0a56ea3f0"}, + {file = "Pillow-10.0.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:9211e7ad69d7c9401cfc0e23d49b69ca65ddd898976d660a2fa5904e3d7a9baa"}, + {file = "Pillow-10.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:faaf07ea35355b01a35cb442dd950d8f1bb5b040a7787791a535de13db15ed90"}, + {file = "Pillow-10.0.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c9f72a021fbb792ce98306ffb0c348b3c9cb967dce0f12a49aa4c3d3fdefa967"}, + {file = "Pillow-10.0.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9f7c16705f44e0504a3a2a14197c1f0b32a95731d251777dcb060aa83022cb2d"}, + {file = "Pillow-10.0.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:76edb0a1fa2b4745fb0c99fb9fb98f8b180a1bbceb8be49b087e0b21867e77d3"}, + {file = "Pillow-10.0.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:368ab3dfb5f49e312231b6f27b8820c823652b7cd29cfbd34090565a015e99ba"}, + {file = "Pillow-10.0.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:608bfdee0d57cf297d32bcbb3c728dc1da0907519d1784962c5f0c68bb93e5a3"}, + {file = "Pillow-10.0.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5c6e3df6bdd396749bafd45314871b3d0af81ff935b2d188385e970052091017"}, + {file = "Pillow-10.0.0-cp39-cp39-win_amd64.whl", hash = "sha256:7be600823e4c8631b74e4a0d38384c73f680e6105a7d3c6824fcf226c178c7e6"}, + {file = "Pillow-10.0.0-pp310-pypy310_pp73-macosx_10_10_x86_64.whl", hash = "sha256:92be919bbc9f7d09f7ae343c38f5bb21c973d2576c1d45600fce4b74bafa7ac0"}, + {file = "Pillow-10.0.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f8182b523b2289f7c415f589118228d30ac8c355baa2f3194ced084dac2dbba"}, + {file = "Pillow-10.0.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:38250a349b6b390ee6047a62c086d3817ac69022c127f8a5dc058c31ccef17f3"}, + {file = "Pillow-10.0.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:88af2003543cc40c80f6fca01411892ec52b11021b3dc22ec3bc9d5afd1c5334"}, + {file = "Pillow-10.0.0-pp39-pypy39_pp73-macosx_10_10_x86_64.whl", hash = "sha256:c189af0545965fa8d3b9613cfdb0cd37f9d71349e0f7750e1fd704648d475ed2"}, + {file = "Pillow-10.0.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce7b031a6fc11365970e6a5686d7ba8c63e4c1cf1ea143811acbb524295eabed"}, + {file = "Pillow-10.0.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:db24668940f82321e746773a4bc617bfac06ec831e5c88b643f91f122a785684"}, + {file = "Pillow-10.0.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:efe8c0681042536e0d06c11f48cebe759707c9e9abf880ee213541c5b46c5bf3"}, + {file = "Pillow-10.0.0.tar.gz", hash = "sha256:9c82b5b3e043c7af0d95792d0d20ccf68f61a1fec6b3530e718b688422727396"}, +] + +[package.extras] +docs = ["furo", "olefile", "sphinx (>=2.4)", "sphinx-copybutton", "sphinx-inline-tabs", "sphinx-removed-in", "sphinxext-opengraph"] +tests = ["check-manifest", "coverage", "defusedxml", "markdown2", "olefile", "packaging", "pyroma", "pytest", "pytest-cov", "pytest-timeout"] + +[[package]] +name = "platformdirs" +version = "3.10.0" +description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." +optional = false +python-versions = ">=3.7" +files = [ + {file = "platformdirs-3.10.0-py3-none-any.whl", hash = "sha256:d7c24979f292f916dc9cbf8648319032f551ea8c49a4c9bf2fb556a02070ec1d"}, + {file = "platformdirs-3.10.0.tar.gz", hash = "sha256:b45696dab2d7cc691a3226759c0d3b00c47c8b6e293d96f6436f733303f77f6d"}, +] + +[package.extras] +docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.1)", "sphinx-autodoc-typehints (>=1.24)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4)", "pytest-cov (>=4.1)", "pytest-mock (>=3.11.1)"] + +[[package]] +name = "pydantic" +version = "2.3.0" +description = "Data validation using Python type hints" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pydantic-2.3.0-py3-none-any.whl", hash = "sha256:45b5e446c6dfaad9444819a293b921a40e1db1aa61ea08aede0522529ce90e81"}, + {file = "pydantic-2.3.0.tar.gz", hash = "sha256:1607cc106602284cd4a00882986570472f193fde9cb1259bceeaedb26aa79a6d"}, +] + +[package.dependencies] +annotated-types = ">=0.4.0" +pydantic-core = "2.6.3" +typing-extensions = ">=4.6.1" + +[package.extras] +email = ["email-validator (>=2.0.0)"] + +[[package]] +name = "pydantic-core" +version = "2.6.3" +description = "" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pydantic_core-2.6.3-cp310-cp310-macosx_10_7_x86_64.whl", hash = "sha256:1a0ddaa723c48af27d19f27f1c73bdc615c73686d763388c8683fe34ae777bad"}, + {file = "pydantic_core-2.6.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5cfde4fab34dd1e3a3f7f3db38182ab6c95e4ea91cf322242ee0be5c2f7e3d2f"}, + {file = "pydantic_core-2.6.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5493a7027bfc6b108e17c3383959485087d5942e87eb62bbac69829eae9bc1f7"}, + {file = "pydantic_core-2.6.3-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:84e87c16f582f5c753b7f39a71bd6647255512191be2d2dbf49458c4ef024588"}, + {file = "pydantic_core-2.6.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:522a9c4a4d1924facce7270c84b5134c5cabcb01513213662a2e89cf28c1d309"}, + {file = "pydantic_core-2.6.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:aaafc776e5edc72b3cad1ccedb5fd869cc5c9a591f1213aa9eba31a781be9ac1"}, + {file = "pydantic_core-2.6.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a750a83b2728299ca12e003d73d1264ad0440f60f4fc9cee54acc489249b728"}, + {file = "pydantic_core-2.6.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9e8b374ef41ad5c461efb7a140ce4730661aadf85958b5c6a3e9cf4e040ff4bb"}, + {file = "pydantic_core-2.6.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b594b64e8568cf09ee5c9501ede37066b9fc41d83d58f55b9952e32141256acd"}, + {file = "pydantic_core-2.6.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:2a20c533cb80466c1d42a43a4521669ccad7cf2967830ac62c2c2f9cece63e7e"}, + {file = "pydantic_core-2.6.3-cp310-none-win32.whl", hash = "sha256:04fe5c0a43dec39aedba0ec9579001061d4653a9b53a1366b113aca4a3c05ca7"}, + {file = "pydantic_core-2.6.3-cp310-none-win_amd64.whl", hash = "sha256:6bf7d610ac8f0065a286002a23bcce241ea8248c71988bda538edcc90e0c39ad"}, + {file = "pydantic_core-2.6.3-cp311-cp311-macosx_10_7_x86_64.whl", hash = "sha256:6bcc1ad776fffe25ea5c187a028991c031a00ff92d012ca1cc4714087e575973"}, + {file = "pydantic_core-2.6.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:df14f6332834444b4a37685810216cc8fe1fe91f447332cd56294c984ecbff1c"}, + {file = "pydantic_core-2.6.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0b7486d85293f7f0bbc39b34e1d8aa26210b450bbd3d245ec3d732864009819"}, + {file = "pydantic_core-2.6.3-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a892b5b1871b301ce20d40b037ffbe33d1407a39639c2b05356acfef5536d26a"}, + {file = "pydantic_core-2.6.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:883daa467865e5766931e07eb20f3e8152324f0adf52658f4d302242c12e2c32"}, + {file = "pydantic_core-2.6.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d4eb77df2964b64ba190eee00b2312a1fd7a862af8918ec70fc2d6308f76ac64"}, + {file = "pydantic_core-2.6.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ce8c84051fa292a5dc54018a40e2a1926fd17980a9422c973e3ebea017aa8da"}, + {file = "pydantic_core-2.6.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:22134a4453bd59b7d1e895c455fe277af9d9d9fbbcb9dc3f4a97b8693e7e2c9b"}, + {file = "pydantic_core-2.6.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:02e1c385095efbd997311d85c6021d32369675c09bcbfff3b69d84e59dc103f6"}, + {file = "pydantic_core-2.6.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d79f1f2f7ebdb9b741296b69049ff44aedd95976bfee38eb4848820628a99b50"}, + {file = "pydantic_core-2.6.3-cp311-none-win32.whl", hash = "sha256:430ddd965ffd068dd70ef4e4d74f2c489c3a313adc28e829dd7262cc0d2dd1e8"}, + {file = "pydantic_core-2.6.3-cp311-none-win_amd64.whl", hash = "sha256:84f8bb34fe76c68c9d96b77c60cef093f5e660ef8e43a6cbfcd991017d375950"}, + {file = "pydantic_core-2.6.3-cp311-none-win_arm64.whl", hash = "sha256:5a2a3c9ef904dcdadb550eedf3291ec3f229431b0084666e2c2aa8ff99a103a2"}, + {file = "pydantic_core-2.6.3-cp312-cp312-macosx_10_7_x86_64.whl", hash = "sha256:8421cf496e746cf8d6b677502ed9a0d1e4e956586cd8b221e1312e0841c002d5"}, + {file = "pydantic_core-2.6.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:bb128c30cf1df0ab78166ded1ecf876620fb9aac84d2413e8ea1594b588c735d"}, + {file = "pydantic_core-2.6.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:37a822f630712817b6ecc09ccc378192ef5ff12e2c9bae97eb5968a6cdf3b862"}, + {file = "pydantic_core-2.6.3-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:240a015102a0c0cc8114f1cba6444499a8a4d0333e178bc504a5c2196defd456"}, + {file = "pydantic_core-2.6.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3f90e5e3afb11268628c89f378f7a1ea3f2fe502a28af4192e30a6cdea1e7d5e"}, + {file = "pydantic_core-2.6.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:340e96c08de1069f3d022a85c2a8c63529fd88709468373b418f4cf2c949fb0e"}, + {file = "pydantic_core-2.6.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1480fa4682e8202b560dcdc9eeec1005f62a15742b813c88cdc01d44e85308e5"}, + {file = "pydantic_core-2.6.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f14546403c2a1d11a130b537dda28f07eb6c1805a43dae4617448074fd49c282"}, + {file = "pydantic_core-2.6.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:a87c54e72aa2ef30189dc74427421e074ab4561cf2bf314589f6af5b37f45e6d"}, + {file = "pydantic_core-2.6.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:f93255b3e4d64785554e544c1c76cd32f4a354fa79e2eeca5d16ac2e7fdd57aa"}, + {file = "pydantic_core-2.6.3-cp312-none-win32.whl", hash = "sha256:f70dc00a91311a1aea124e5f64569ea44c011b58433981313202c46bccbec0e1"}, + {file = "pydantic_core-2.6.3-cp312-none-win_amd64.whl", hash = "sha256:23470a23614c701b37252618e7851e595060a96a23016f9a084f3f92f5ed5881"}, + {file = "pydantic_core-2.6.3-cp312-none-win_arm64.whl", hash = "sha256:1ac1750df1b4339b543531ce793b8fd5c16660a95d13aecaab26b44ce11775e9"}, + {file = "pydantic_core-2.6.3-cp37-cp37m-macosx_10_7_x86_64.whl", hash = "sha256:a53e3195f134bde03620d87a7e2b2f2046e0e5a8195e66d0f244d6d5b2f6d31b"}, + {file = "pydantic_core-2.6.3-cp37-cp37m-macosx_11_0_arm64.whl", hash = "sha256:f2969e8f72c6236c51f91fbb79c33821d12a811e2a94b7aa59c65f8dbdfad34a"}, + {file = "pydantic_core-2.6.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:672174480a85386dd2e681cadd7d951471ad0bb028ed744c895f11f9d51b9ebe"}, + {file = "pydantic_core-2.6.3-cp37-cp37m-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:002d0ea50e17ed982c2d65b480bd975fc41086a5a2f9c924ef8fc54419d1dea3"}, + {file = "pydantic_core-2.6.3-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3ccc13afee44b9006a73d2046068d4df96dc5b333bf3509d9a06d1b42db6d8bf"}, + {file = "pydantic_core-2.6.3-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:439a0de139556745ae53f9cc9668c6c2053444af940d3ef3ecad95b079bc9987"}, + {file = "pydantic_core-2.6.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d63b7545d489422d417a0cae6f9898618669608750fc5e62156957e609e728a5"}, + {file = "pydantic_core-2.6.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b44c42edc07a50a081672e25dfe6022554b47f91e793066a7b601ca290f71e42"}, + {file = "pydantic_core-2.6.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:1c721bfc575d57305dd922e6a40a8fe3f762905851d694245807a351ad255c58"}, + {file = "pydantic_core-2.6.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:5e4a2cf8c4543f37f5dc881de6c190de08096c53986381daebb56a355be5dfe6"}, + {file = "pydantic_core-2.6.3-cp37-none-win32.whl", hash = "sha256:d9b4916b21931b08096efed090327f8fe78e09ae8f5ad44e07f5c72a7eedb51b"}, + {file = "pydantic_core-2.6.3-cp37-none-win_amd64.whl", hash = "sha256:a8acc9dedd304da161eb071cc7ff1326aa5b66aadec9622b2574ad3ffe225525"}, + {file = "pydantic_core-2.6.3-cp38-cp38-macosx_10_7_x86_64.whl", hash = "sha256:5e9c068f36b9f396399d43bfb6defd4cc99c36215f6ff33ac8b9c14ba15bdf6b"}, + {file = "pydantic_core-2.6.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:e61eae9b31799c32c5f9b7be906be3380e699e74b2db26c227c50a5fc7988698"}, + {file = "pydantic_core-2.6.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d85463560c67fc65cd86153a4975d0b720b6d7725cf7ee0b2d291288433fc21b"}, + {file = "pydantic_core-2.6.3-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9616567800bdc83ce136e5847d41008a1d602213d024207b0ff6cab6753fe645"}, + {file = "pydantic_core-2.6.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9e9b65a55bbabda7fccd3500192a79f6e474d8d36e78d1685496aad5f9dbd92c"}, + {file = "pydantic_core-2.6.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f468d520f47807d1eb5d27648393519655eadc578d5dd862d06873cce04c4d1b"}, + {file = "pydantic_core-2.6.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9680dd23055dd874173a3a63a44e7f5a13885a4cfd7e84814be71be24fba83db"}, + {file = "pydantic_core-2.6.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9a718d56c4d55efcfc63f680f207c9f19c8376e5a8a67773535e6f7e80e93170"}, + {file = "pydantic_core-2.6.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:8ecbac050856eb6c3046dea655b39216597e373aa8e50e134c0e202f9c47efec"}, + {file = "pydantic_core-2.6.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:788be9844a6e5c4612b74512a76b2153f1877cd845410d756841f6c3420230eb"}, + {file = "pydantic_core-2.6.3-cp38-none-win32.whl", hash = "sha256:07a1aec07333bf5adebd8264047d3dc518563d92aca6f2f5b36f505132399efc"}, + {file = "pydantic_core-2.6.3-cp38-none-win_amd64.whl", hash = "sha256:621afe25cc2b3c4ba05fff53525156d5100eb35c6e5a7cf31d66cc9e1963e378"}, + {file = "pydantic_core-2.6.3-cp39-cp39-macosx_10_7_x86_64.whl", hash = "sha256:813aab5bfb19c98ae370952b6f7190f1e28e565909bfc219a0909db168783465"}, + {file = "pydantic_core-2.6.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:50555ba3cb58f9861b7a48c493636b996a617db1a72c18da4d7f16d7b1b9952b"}, + {file = "pydantic_core-2.6.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:19e20f8baedd7d987bd3f8005c146e6bcbda7cdeefc36fad50c66adb2dd2da48"}, + {file = "pydantic_core-2.6.3-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b0a5d7edb76c1c57b95df719af703e796fc8e796447a1da939f97bfa8a918d60"}, + {file = "pydantic_core-2.6.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f06e21ad0b504658a3a9edd3d8530e8cea5723f6ea5d280e8db8efc625b47e49"}, + {file = "pydantic_core-2.6.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ea053cefa008fda40f92aab937fb9f183cf8752e41dbc7bc68917884454c6362"}, + {file = "pydantic_core-2.6.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:171a4718860790f66d6c2eda1d95dd1edf64f864d2e9f9115840840cf5b5713f"}, + {file = "pydantic_core-2.6.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5ed7ceca6aba5331ece96c0e328cd52f0dcf942b8895a1ed2642de50800b79d3"}, + {file = "pydantic_core-2.6.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:acafc4368b289a9f291e204d2c4c75908557d4f36bd3ae937914d4529bf62a76"}, + {file = "pydantic_core-2.6.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:1aa712ba150d5105814e53cb141412217146fedc22621e9acff9236d77d2a5ef"}, + {file = "pydantic_core-2.6.3-cp39-none-win32.whl", hash = "sha256:44b4f937b992394a2e81a5c5ce716f3dcc1237281e81b80c748b2da6dd5cf29a"}, + {file = "pydantic_core-2.6.3-cp39-none-win_amd64.whl", hash = "sha256:9b33bf9658cb29ac1a517c11e865112316d09687d767d7a0e4a63d5c640d1b17"}, + {file = "pydantic_core-2.6.3-pp310-pypy310_pp73-macosx_10_7_x86_64.whl", hash = "sha256:d7050899026e708fb185e174c63ebc2c4ee7a0c17b0a96ebc50e1f76a231c057"}, + {file = "pydantic_core-2.6.3-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:99faba727727b2e59129c59542284efebbddade4f0ae6a29c8b8d3e1f437beb7"}, + {file = "pydantic_core-2.6.3-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5fa159b902d22b283b680ef52b532b29554ea2a7fc39bf354064751369e9dbd7"}, + {file = "pydantic_core-2.6.3-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:046af9cfb5384f3684eeb3f58a48698ddab8dd870b4b3f67f825353a14441418"}, + {file = "pydantic_core-2.6.3-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:930bfe73e665ebce3f0da2c6d64455098aaa67e1a00323c74dc752627879fc67"}, + {file = "pydantic_core-2.6.3-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:85cc4d105747d2aa3c5cf3e37dac50141bff779545ba59a095f4a96b0a460e70"}, + {file = "pydantic_core-2.6.3-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:b25afe9d5c4f60dcbbe2b277a79be114e2e65a16598db8abee2a2dcde24f162b"}, + {file = "pydantic_core-2.6.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:e49ce7dc9f925e1fb010fc3d555250139df61fa6e5a0a95ce356329602c11ea9"}, + {file = "pydantic_core-2.6.3-pp37-pypy37_pp73-macosx_10_7_x86_64.whl", hash = "sha256:2dd50d6a1aef0426a1d0199190c6c43ec89812b1f409e7fe44cb0fbf6dfa733c"}, + {file = "pydantic_core-2.6.3-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c6595b0d8c8711e8e1dc389d52648b923b809f68ac1c6f0baa525c6440aa0daa"}, + {file = "pydantic_core-2.6.3-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4ef724a059396751aef71e847178d66ad7fc3fc969a1a40c29f5aac1aa5f8784"}, + {file = "pydantic_core-2.6.3-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:3c8945a105f1589ce8a693753b908815e0748f6279959a4530f6742e1994dcb6"}, + {file = "pydantic_core-2.6.3-pp37-pypy37_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:c8c6660089a25d45333cb9db56bb9e347241a6d7509838dbbd1931d0e19dbc7f"}, + {file = "pydantic_core-2.6.3-pp37-pypy37_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:692b4ff5c4e828a38716cfa92667661a39886e71136c97b7dac26edef18767f7"}, + {file = "pydantic_core-2.6.3-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:f1a5d8f18877474c80b7711d870db0eeef9442691fcdb00adabfc97e183ee0b0"}, + {file = "pydantic_core-2.6.3-pp38-pypy38_pp73-macosx_10_7_x86_64.whl", hash = "sha256:3796a6152c545339d3b1652183e786df648ecdf7c4f9347e1d30e6750907f5bb"}, + {file = "pydantic_core-2.6.3-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:b962700962f6e7a6bd77e5f37320cabac24b4c0f76afeac05e9f93cf0c620014"}, + {file = "pydantic_core-2.6.3-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:56ea80269077003eaa59723bac1d8bacd2cd15ae30456f2890811efc1e3d4413"}, + {file = "pydantic_core-2.6.3-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75c0ebbebae71ed1e385f7dfd9b74c1cff09fed24a6df43d326dd7f12339ec34"}, + {file = "pydantic_core-2.6.3-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:252851b38bad3bfda47b104ffd077d4f9604a10cb06fe09d020016a25107bf98"}, + {file = "pydantic_core-2.6.3-pp38-pypy38_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:6656a0ae383d8cd7cc94e91de4e526407b3726049ce8d7939049cbfa426518c8"}, + {file = "pydantic_core-2.6.3-pp38-pypy38_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:d9140ded382a5b04a1c030b593ed9bf3088243a0a8b7fa9f071a5736498c5483"}, + {file = "pydantic_core-2.6.3-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:d38bbcef58220f9c81e42c255ef0bf99735d8f11edef69ab0b499da77105158a"}, + {file = "pydantic_core-2.6.3-pp39-pypy39_pp73-macosx_10_7_x86_64.whl", hash = "sha256:c9d469204abcca28926cbc28ce98f28e50e488767b084fb3fbdf21af11d3de26"}, + {file = "pydantic_core-2.6.3-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:48c1ed8b02ffea4d5c9c220eda27af02b8149fe58526359b3c07eb391cb353a2"}, + {file = "pydantic_core-2.6.3-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b2b1bfed698fa410ab81982f681f5b1996d3d994ae8073286515ac4d165c2e7"}, + {file = "pydantic_core-2.6.3-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf9d42a71a4d7a7c1f14f629e5c30eac451a6fc81827d2beefd57d014c006c4a"}, + {file = "pydantic_core-2.6.3-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4292ca56751aebbe63a84bbfc3b5717abb09b14d4b4442cc43fd7c49a1529efd"}, + {file = "pydantic_core-2.6.3-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:7dc2ce039c7290b4ef64334ec7e6ca6494de6eecc81e21cb4f73b9b39991408c"}, + {file = "pydantic_core-2.6.3-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:615a31b1629e12445c0e9fc8339b41aaa6cc60bd53bf802d5fe3d2c0cda2ae8d"}, + {file = "pydantic_core-2.6.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:1fa1f6312fb84e8c281f32b39affe81984ccd484da6e9d65b3d18c202c666149"}, + {file = "pydantic_core-2.6.3.tar.gz", hash = "sha256:1508f37ba9e3ddc0189e6ff4e2228bd2d3c3a4641cbe8c07177162f76ed696c7"}, +] + +[package.dependencies] +typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" + +[[package]] +name = "pygments" +version = "2.16.1" +description = "Pygments is a syntax highlighting package written in Python." +optional = false +python-versions = ">=3.7" +files = [ + {file = "Pygments-2.16.1-py3-none-any.whl", hash = "sha256:13fc09fa63bc8d8671a6d247e1eb303c4b343eaee81d861f3404db2935653692"}, + {file = "Pygments-2.16.1.tar.gz", hash = "sha256:1daff0494820c69bc8941e407aa20f577374ee88364ee10a98fdbe0aece96e29"}, +] + +[package.extras] +plugins = ["importlib-metadata"] + +[[package]] +name = "pyparsing" +version = "3.0.9" +description = "pyparsing module - Classes and methods to define and execute parsing grammars" +optional = false +python-versions = ">=3.6.8" +files = [ + {file = "pyparsing-3.0.9-py3-none-any.whl", hash = "sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc"}, + {file = "pyparsing-3.0.9.tar.gz", hash = "sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb"}, +] + +[package.extras] +diagrams = ["jinja2", "railroad-diagrams"] + +[[package]] +name = "python-dateutil" +version = "2.8.2" +description = "Extensions to the standard Python datetime module" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +files = [ + {file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"}, + {file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"}, +] + +[package.dependencies] +six = ">=1.5" + +[[package]] +name = "python-multipart" +version = "0.0.6" +description = "A streaming multipart parser for Python" +optional = false +python-versions = ">=3.7" +files = [ + {file = "python_multipart-0.0.6-py3-none-any.whl", hash = "sha256:ee698bab5ef148b0a760751c261902cd096e57e10558e11aca17646b74ee1c18"}, + {file = "python_multipart-0.0.6.tar.gz", hash = "sha256:e9925a80bb668529f1b67c7fdb0a5dacdd7cbfc6fb0bff3ea443fe22bdd62132"}, +] + +[package.extras] +dev = ["atomicwrites (==1.2.1)", "attrs (==19.2.0)", "coverage (==6.5.0)", "hatch", "invoke (==1.7.3)", "more-itertools (==4.3.0)", "pbr (==4.3.0)", "pluggy (==1.0.0)", "py (==1.11.0)", "pytest (==7.2.0)", "pytest-cov (==4.0.0)", "pytest-timeout (==2.1.0)", "pyyaml (==5.1)"] + +[[package]] +name = "pytz" +version = "2023.3" +description = "World timezone definitions, modern and historical" +optional = false +python-versions = "*" +files = [ + {file = "pytz-2023.3-py2.py3-none-any.whl", hash = "sha256:a151b3abb88eda1d4e34a9814df37de2a80e301e68ba0fd856fb9b46bfbbbffb"}, + {file = "pytz-2023.3.tar.gz", hash = "sha256:1d8ce29db189191fb55338ee6d0387d82ab59f3d00eac103412d64e0ebd0c588"}, +] + +[[package]] +name = "regex" +version = "2023.8.8" +description = "Alternative regular expression module, to replace re." +optional = false +python-versions = ">=3.6" +files = [ + {file = "regex-2023.8.8-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:88900f521c645f784260a8d346e12a1590f79e96403971241e64c3a265c8ecdb"}, + {file = "regex-2023.8.8-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3611576aff55918af2697410ff0293d6071b7e00f4b09e005d614686ac4cd57c"}, + {file = "regex-2023.8.8-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b8a0ccc8f2698f120e9e5742f4b38dc944c38744d4bdfc427616f3a163dd9de5"}, + {file = "regex-2023.8.8-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c662a4cbdd6280ee56f841f14620787215a171c4e2d1744c9528bed8f5816c96"}, + {file = "regex-2023.8.8-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cf0633e4a1b667bfe0bb10b5e53fe0d5f34a6243ea2530eb342491f1adf4f739"}, + {file = "regex-2023.8.8-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:551ad543fa19e94943c5b2cebc54c73353ffff08228ee5f3376bd27b3d5b9800"}, + {file = "regex-2023.8.8-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:54de2619f5ea58474f2ac211ceea6b615af2d7e4306220d4f3fe690c91988a61"}, + {file = "regex-2023.8.8-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:5ec4b3f0aebbbe2fc0134ee30a791af522a92ad9f164858805a77442d7d18570"}, + {file = "regex-2023.8.8-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:3ae646c35cb9f820491760ac62c25b6d6b496757fda2d51be429e0e7b67ae0ab"}, + {file = "regex-2023.8.8-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:ca339088839582d01654e6f83a637a4b8194d0960477b9769d2ff2cfa0fa36d2"}, + {file = "regex-2023.8.8-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:d9b6627408021452dcd0d2cdf8da0534e19d93d070bfa8b6b4176f99711e7f90"}, + {file = "regex-2023.8.8-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:bd3366aceedf274f765a3a4bc95d6cd97b130d1dda524d8f25225d14123c01db"}, + {file = "regex-2023.8.8-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7aed90a72fc3654fba9bc4b7f851571dcc368120432ad68b226bd593f3f6c0b7"}, + {file = "regex-2023.8.8-cp310-cp310-win32.whl", hash = "sha256:80b80b889cb767cc47f31d2b2f3dec2db8126fbcd0cff31b3925b4dc6609dcdb"}, + {file = "regex-2023.8.8-cp310-cp310-win_amd64.whl", hash = "sha256:b82edc98d107cbc7357da7a5a695901b47d6eb0420e587256ba3ad24b80b7d0b"}, + {file = "regex-2023.8.8-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1e7d84d64c84ad97bf06f3c8cb5e48941f135ace28f450d86af6b6512f1c9a71"}, + {file = "regex-2023.8.8-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ce0f9fbe7d295f9922c0424a3637b88c6c472b75eafeaff6f910494a1fa719ef"}, + {file = "regex-2023.8.8-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:06c57e14ac723b04458df5956cfb7e2d9caa6e9d353c0b4c7d5d54fcb1325c46"}, + {file = "regex-2023.8.8-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e7a9aaa5a1267125eef22cef3b63484c3241aaec6f48949b366d26c7250e0357"}, + {file = "regex-2023.8.8-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b7408511fca48a82a119d78a77c2f5eb1b22fe88b0d2450ed0756d194fe7a9a"}, + {file = "regex-2023.8.8-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:14dc6f2d88192a67d708341f3085df6a4f5a0c7b03dec08d763ca2cd86e9f559"}, + {file = "regex-2023.8.8-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:48c640b99213643d141550326f34f0502fedb1798adb3c9eb79650b1ecb2f177"}, + {file = "regex-2023.8.8-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0085da0f6c6393428bf0d9c08d8b1874d805bb55e17cb1dfa5ddb7cfb11140bf"}, + {file = "regex-2023.8.8-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:964b16dcc10c79a4a2be9f1273fcc2684a9eedb3906439720598029a797b46e6"}, + {file = "regex-2023.8.8-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:7ce606c14bb195b0e5108544b540e2c5faed6843367e4ab3deb5c6aa5e681208"}, + {file = "regex-2023.8.8-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:40f029d73b10fac448c73d6eb33d57b34607f40116e9f6e9f0d32e9229b147d7"}, + {file = "regex-2023.8.8-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3b8e6ea6be6d64104d8e9afc34c151926f8182f84e7ac290a93925c0db004bfd"}, + {file = "regex-2023.8.8-cp311-cp311-win32.whl", hash = "sha256:942f8b1f3b223638b02df7df79140646c03938d488fbfb771824f3d05fc083a8"}, + {file = "regex-2023.8.8-cp311-cp311-win_amd64.whl", hash = "sha256:51d8ea2a3a1a8fe4f67de21b8b93757005213e8ac3917567872f2865185fa7fb"}, + {file = "regex-2023.8.8-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:e951d1a8e9963ea51efd7f150450803e3b95db5939f994ad3d5edac2b6f6e2b4"}, + {file = "regex-2023.8.8-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:704f63b774218207b8ccc6c47fcef5340741e5d839d11d606f70af93ee78e4d4"}, + {file = "regex-2023.8.8-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:22283c769a7b01c8ac355d5be0715bf6929b6267619505e289f792b01304d898"}, + {file = "regex-2023.8.8-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:91129ff1bb0619bc1f4ad19485718cc623a2dc433dff95baadbf89405c7f6b57"}, + {file = "regex-2023.8.8-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:de35342190deb7b866ad6ba5cbcccb2d22c0487ee0cbb251efef0843d705f0d4"}, + {file = "regex-2023.8.8-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b993b6f524d1e274a5062488a43e3f9f8764ee9745ccd8e8193df743dbe5ee61"}, + {file = "regex-2023.8.8-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:3026cbcf11d79095a32d9a13bbc572a458727bd5b1ca332df4a79faecd45281c"}, + {file = "regex-2023.8.8-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:293352710172239bf579c90a9864d0df57340b6fd21272345222fb6371bf82b3"}, + {file = "regex-2023.8.8-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:d909b5a3fff619dc7e48b6b1bedc2f30ec43033ba7af32f936c10839e81b9217"}, + {file = "regex-2023.8.8-cp36-cp36m-musllinux_1_1_ppc64le.whl", hash = "sha256:3d370ff652323c5307d9c8e4c62efd1956fb08051b0e9210212bc51168b4ff56"}, + {file = "regex-2023.8.8-cp36-cp36m-musllinux_1_1_s390x.whl", hash = "sha256:b076da1ed19dc37788f6a934c60adf97bd02c7eea461b73730513921a85d4235"}, + {file = "regex-2023.8.8-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:e9941a4ada58f6218694f382e43fdd256e97615db9da135e77359da257a7168b"}, + {file = "regex-2023.8.8-cp36-cp36m-win32.whl", hash = "sha256:a8c65c17aed7e15a0c824cdc63a6b104dfc530f6fa8cb6ac51c437af52b481c7"}, + {file = "regex-2023.8.8-cp36-cp36m-win_amd64.whl", hash = "sha256:aadf28046e77a72f30dcc1ab185639e8de7f4104b8cb5c6dfa5d8ed860e57236"}, + {file = "regex-2023.8.8-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:423adfa872b4908843ac3e7a30f957f5d5282944b81ca0a3b8a7ccbbfaa06103"}, + {file = "regex-2023.8.8-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4ae594c66f4a7e1ea67232a0846649a7c94c188d6c071ac0210c3e86a5f92109"}, + {file = "regex-2023.8.8-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e51c80c168074faa793685656c38eb7a06cbad7774c8cbc3ea05552d615393d8"}, + {file = "regex-2023.8.8-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:09b7f4c66aa9d1522b06e31a54f15581c37286237208df1345108fcf4e050c18"}, + {file = "regex-2023.8.8-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2e73e5243af12d9cd6a9d6a45a43570dbe2e5b1cdfc862f5ae2b031e44dd95a8"}, + {file = "regex-2023.8.8-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:941460db8fe3bd613db52f05259c9336f5a47ccae7d7def44cc277184030a116"}, + {file = "regex-2023.8.8-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f0ccf3e01afeb412a1a9993049cb160d0352dba635bbca7762b2dc722aa5742a"}, + {file = "regex-2023.8.8-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:2e9216e0d2cdce7dbc9be48cb3eacb962740a09b011a116fd7af8c832ab116ca"}, + {file = "regex-2023.8.8-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:5cd9cd7170459b9223c5e592ac036e0704bee765706445c353d96f2890e816c8"}, + {file = "regex-2023.8.8-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:4873ef92e03a4309b3ccd8281454801b291b689f6ad45ef8c3658b6fa761d7ac"}, + {file = "regex-2023.8.8-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:239c3c2a339d3b3ddd51c2daef10874410917cd2b998f043c13e2084cb191684"}, + {file = "regex-2023.8.8-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:1005c60ed7037be0d9dea1f9c53cc42f836188227366370867222bda4c3c6bd7"}, + {file = "regex-2023.8.8-cp37-cp37m-win32.whl", hash = "sha256:e6bd1e9b95bc5614a7a9c9c44fde9539cba1c823b43a9f7bc11266446dd568e3"}, + {file = "regex-2023.8.8-cp37-cp37m-win_amd64.whl", hash = "sha256:9a96edd79661e93327cfeac4edec72a4046e14550a1d22aa0dd2e3ca52aec921"}, + {file = "regex-2023.8.8-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f2181c20ef18747d5f4a7ea513e09ea03bdd50884a11ce46066bb90fe4213675"}, + {file = "regex-2023.8.8-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:a2ad5add903eb7cdde2b7c64aaca405f3957ab34f16594d2b78d53b8b1a6a7d6"}, + {file = "regex-2023.8.8-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9233ac249b354c54146e392e8a451e465dd2d967fc773690811d3a8c240ac601"}, + {file = "regex-2023.8.8-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:920974009fb37b20d32afcdf0227a2e707eb83fe418713f7a8b7de038b870d0b"}, + {file = "regex-2023.8.8-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd2b6c5dfe0929b6c23dde9624483380b170b6e34ed79054ad131b20203a1a63"}, + {file = "regex-2023.8.8-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:96979d753b1dc3b2169003e1854dc67bfc86edf93c01e84757927f810b8c3c93"}, + {file = "regex-2023.8.8-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2ae54a338191e1356253e7883d9d19f8679b6143703086245fb14d1f20196be9"}, + {file = "regex-2023.8.8-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:2162ae2eb8b079622176a81b65d486ba50b888271302190870b8cc488587d280"}, + {file = "regex-2023.8.8-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:c884d1a59e69e03b93cf0dfee8794c63d7de0ee8f7ffb76e5f75be8131b6400a"}, + {file = "regex-2023.8.8-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:cf9273e96f3ee2ac89ffcb17627a78f78e7516b08f94dc435844ae72576a276e"}, + {file = "regex-2023.8.8-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:83215147121e15d5f3a45d99abeed9cf1fe16869d5c233b08c56cdf75f43a504"}, + {file = "regex-2023.8.8-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:3f7454aa427b8ab9101f3787eb178057c5250478e39b99540cfc2b889c7d0586"}, + {file = "regex-2023.8.8-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:f0640913d2c1044d97e30d7c41728195fc37e54d190c5385eacb52115127b882"}, + {file = "regex-2023.8.8-cp38-cp38-win32.whl", hash = "sha256:0c59122ceccb905a941fb23b087b8eafc5290bf983ebcb14d2301febcbe199c7"}, + {file = "regex-2023.8.8-cp38-cp38-win_amd64.whl", hash = "sha256:c12f6f67495ea05c3d542d119d270007090bad5b843f642d418eb601ec0fa7be"}, + {file = "regex-2023.8.8-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:82cd0a69cd28f6cc3789cc6adeb1027f79526b1ab50b1f6062bbc3a0ccb2dbc3"}, + {file = "regex-2023.8.8-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:bb34d1605f96a245fc39790a117ac1bac8de84ab7691637b26ab2c5efb8f228c"}, + {file = "regex-2023.8.8-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:987b9ac04d0b38ef4f89fbc035e84a7efad9cdd5f1e29024f9289182c8d99e09"}, + {file = "regex-2023.8.8-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9dd6082f4e2aec9b6a0927202c85bc1b09dcab113f97265127c1dc20e2e32495"}, + {file = "regex-2023.8.8-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7eb95fe8222932c10d4436e7a6f7c99991e3fdd9f36c949eff16a69246dee2dc"}, + {file = "regex-2023.8.8-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7098c524ba9f20717a56a8d551d2ed491ea89cbf37e540759ed3b776a4f8d6eb"}, + {file = "regex-2023.8.8-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4b694430b3f00eb02c594ff5a16db30e054c1b9589a043fe9174584c6efa8033"}, + {file = "regex-2023.8.8-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:b2aeab3895d778155054abea5238d0eb9a72e9242bd4b43f42fd911ef9a13470"}, + {file = "regex-2023.8.8-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:988631b9d78b546e284478c2ec15c8a85960e262e247b35ca5eaf7ee22f6050a"}, + {file = "regex-2023.8.8-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:67ecd894e56a0c6108ec5ab1d8fa8418ec0cff45844a855966b875d1039a2e34"}, + {file = "regex-2023.8.8-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:14898830f0a0eb67cae2bbbc787c1a7d6e34ecc06fbd39d3af5fe29a4468e2c9"}, + {file = "regex-2023.8.8-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:f2200e00b62568cfd920127782c61bc1c546062a879cdc741cfcc6976668dfcf"}, + {file = "regex-2023.8.8-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:9691a549c19c22d26a4f3b948071e93517bdf86e41b81d8c6ac8a964bb71e5a6"}, + {file = "regex-2023.8.8-cp39-cp39-win32.whl", hash = "sha256:6ab2ed84bf0137927846b37e882745a827458689eb969028af8032b1b3dac78e"}, + {file = "regex-2023.8.8-cp39-cp39-win_amd64.whl", hash = "sha256:5543c055d8ec7801901e1193a51570643d6a6ab8751b1f7dd9af71af467538bb"}, + {file = "regex-2023.8.8.tar.gz", hash = "sha256:fcbdc5f2b0f1cd0f6a56cdb46fe41d2cce1e644e3b68832f3eeebc5fb0f7712e"}, +] + +[[package]] +name = "rich" +version = "13.5.2" +description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" +optional = false +python-versions = ">=3.7.0" +files = [ + {file = "rich-13.5.2-py3-none-any.whl", hash = "sha256:146a90b3b6b47cac4a73c12866a499e9817426423f57c5a66949c086191a8808"}, + {file = "rich-13.5.2.tar.gz", hash = "sha256:fb9d6c0a0f643c99eed3875b5377a184132ba9be4d61516a55273d3554d75a39"}, +] + +[package.dependencies] +markdown-it-py = ">=2.2.0" +pygments = ">=2.13.0,<3.0.0" +typing-extensions = {version = ">=4.0.0,<5.0", markers = "python_version < \"3.9\""} + +[package.extras] +jupyter = ["ipywidgets (>=7.5.1,<9)"] + +[[package]] +name = "six" +version = "1.16.0" +description = "Python 2 and 3 compatibility utilities" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" +files = [ + {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, + {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, +] + +[[package]] +name = "sniffio" +version = "1.3.0" +description = "Sniff out which async library your code is running under" +optional = false +python-versions = ">=3.7" +files = [ + {file = "sniffio-1.3.0-py3-none-any.whl", hash = "sha256:eecefdce1e5bbfb7ad2eeaabf7c1eeb404d7757c379bd1f7e5cce9d8bf425384"}, + {file = "sniffio-1.3.0.tar.gz", hash = "sha256:e60305c5e5d314f5389259b7f22aaa33d8f7dee49763119234af3755c55b9101"}, +] + +[[package]] +name = "starlette" +version = "0.27.0" +description = "The little ASGI library that shines." +optional = false +python-versions = ">=3.7" +files = [ + {file = "starlette-0.27.0-py3-none-any.whl", hash = "sha256:918416370e846586541235ccd38a474c08b80443ed31c578a418e2209b3eef91"}, + {file = "starlette-0.27.0.tar.gz", hash = "sha256:6a6b0d042acb8d469a01eba54e9cda6cbd24ac602c4cd016723117d6a7e73b75"}, +] + +[package.dependencies] +anyio = ">=3.4.0,<5" +typing-extensions = {version = ">=3.10.0", markers = "python_version < \"3.10\""} + +[package.extras] +full = ["httpx (>=0.22.0)", "itsdangerous", "jinja2", "python-multipart", "pyyaml"] + +[[package]] +name = "toml" +version = "0.10.2" +description = "Python Library for Tom's Obvious, Minimal Language" +optional = false +python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" +files = [ + {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, + {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, +] + +[[package]] +name = "tomli" +version = "2.0.1" +description = "A lil' TOML parser" +optional = false +python-versions = ">=3.7" +files = [ + {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, + {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, +] + +[[package]] +name = "typing-extensions" +version = "4.7.1" +description = "Backported and Experimental Type Hints for Python 3.7+" +optional = false +python-versions = ">=3.7" +files = [ + {file = "typing_extensions-4.7.1-py3-none-any.whl", hash = "sha256:440d5dd3af93b060174bf433bccd69b0babc3b15b1a8dca43789fd7f61514b36"}, + {file = "typing_extensions-4.7.1.tar.gz", hash = "sha256:b75ddc264f0ba5615db7ba217daeb99701ad295353c45f9e95963337ceeeffb2"}, +] + +[[package]] +name = "tzdata" +version = "2023.3" +description = "Provider of IANA time zone data" +optional = false +python-versions = ">=2" +files = [ + {file = "tzdata-2023.3-py2.py3-none-any.whl", hash = "sha256:7e65763eef3120314099b6939b5546db7adce1e7d6f2e179e3df563c70511eda"}, + {file = "tzdata-2023.3.tar.gz", hash = "sha256:11ef1e08e54acb0d4f95bdb1be05da659673de4acbd21bf9c69e94cc5e907a3a"}, +] + +[[package]] +name = "tzlocal" +version = "5.0.1" +description = "tzinfo object for the local timezone" +optional = false +python-versions = ">=3.7" +files = [ + {file = "tzlocal-5.0.1-py3-none-any.whl", hash = "sha256:f3596e180296aaf2dbd97d124fe76ae3a0e3d32b258447de7b939b3fd4be992f"}, + {file = "tzlocal-5.0.1.tar.gz", hash = "sha256:46eb99ad4bdb71f3f72b7d24f4267753e240944ecfc16f25d2719ba89827a803"}, +] + +[package.dependencies] +"backports.zoneinfo" = {version = "*", markers = "python_version < \"3.9\""} +tzdata = {version = "*", markers = "platform_system == \"Windows\""} + +[package.extras] +devenv = ["black", "check-manifest", "flake8", "pyroma", "pytest (>=4.3)", "pytest-cov", "pytest-mock (>=3.3)", "zest.releaser"] + +[[package]] +name = "uvicorn" +version = "0.23.2" +description = "The lightning-fast ASGI server." +optional = false +python-versions = ">=3.8" +files = [ + {file = "uvicorn-0.23.2-py3-none-any.whl", hash = "sha256:1f9be6558f01239d4fdf22ef8126c39cb1ad0addf76c40e760549d2c2f43ab53"}, + {file = "uvicorn-0.23.2.tar.gz", hash = "sha256:4d3cc12d7727ba72b64d12d3cc7743124074c0a69f7b201512fc50c3e3f1569a"}, +] + +[package.dependencies] +click = ">=7.0" +h11 = ">=0.8" +typing-extensions = {version = ">=4.0", markers = "python_version < \"3.11\""} + +[package.extras] +standard = ["colorama (>=0.4)", "httptools (>=0.5.0)", "python-dotenv (>=0.13)", "pyyaml (>=5.1)", "uvloop (>=0.14.0,!=0.15.0,!=0.15.1)", "watchfiles (>=0.13)", "websockets (>=10.4)"] + +[[package]] +name = "win32-setctime" +version = "1.1.0" +description = "A small Python utility to set file creation time on Windows" +optional = false +python-versions = ">=3.5" +files = [ + {file = "win32_setctime-1.1.0-py3-none-any.whl", hash = "sha256:231db239e959c2fe7eb1d7dc129f11172354f98361c4fa2d6d2d7e278baa8aad"}, + {file = "win32_setctime-1.1.0.tar.gz", hash = "sha256:15cf5750465118d6929ae4de4eb46e8edae9a5634350c01ba582df868e932cb2"}, +] + +[package.extras] +dev = ["black (>=19.3b0)", "pytest (>=4.6.2)"] + +[[package]] +name = "zipp" +version = "3.16.2" +description = "Backport of pathlib-compatible object wrapper for zip files" +optional = false +python-versions = ">=3.8" +files = [ + {file = "zipp-3.16.2-py3-none-any.whl", hash = "sha256:679e51dd4403591b2d6838a48de3d283f3d188412a9782faadf845f298736ba0"}, + {file = "zipp-3.16.2.tar.gz", hash = "sha256:ebc15946aa78bd63458992fc81ec3b6f7b1e92d51c35e6de1c3804e73b799147"}, +] + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-ignore-flaky", "pytest-mypy (>=0.9.1)", "pytest-ruff"] + +[metadata] +lock-version = "2.0" +python-versions = "^3.8" +content-hash = "a01e6b86fb598d6b337016a0b9c76a29ca49a9fde074b1ef18122aa785bb09ef" diff --git a/poetry.toml b/poetry.toml new file mode 100644 index 0000000000000000000000000000000000000000..ab1033bd37224ee84b5862fb25f094db73809b74 --- /dev/null +++ b/poetry.toml @@ -0,0 +1,2 @@ +[virtualenvs] +in-project = true diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000000000000000000000000000000000000..82d9efc2a5d00546d876d09f14159a55f7141844 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,42 @@ +[tool.poetry] +name = "meme-generator" +version = "0.0.15" +description = "Python package for making fun pictures" +authors = ["meetwq "] +license = "MIT" +readme = "README.md" +homepage = "https://github.com/MeetWq/meme-generator" +repository = "https://github.com/MeetWq/meme-generator" +exclude = [ + "meme_generator/memes/**/*.jpg", + "meme_generator/memes/**/*.png", + "meme_generator/memes/**/*.gif", +] + +[tool.poetry.dependencies] +python = "^3.8" +httpx = ">=0.20.0,<1.0.0" +loguru = ">=0.6.0,<1.0.0" +pil-utils = "^0.1.8" +toml = "^0.10.2" +fastapi = ">=0.93.0,<1.0.0" +uvicorn = ">=0.20.0,<1.0.0" +python-multipart = "^0.0.6" +filetype = "^1.2.0" +rich = "^13.0.0" +dateparser = "^1.1.0" +typing-extensions = ">=4.0.0,<5.0.0" + +[tool.poetry.group.dev.dependencies] +black = "^22.1.0" + +[tool.poetry.scripts] +meme = "meme_generator.cli:main" + +[tool.isort] +profile = "black" +skip_gitignore = true + +[build-system] +requires = ["poetry-core>=1.0.0"] +build-backend = "poetry.core.masonry.api" diff --git a/resources/fonts/Aller_Bd.ttf b/resources/fonts/Aller_Bd.ttf new file mode 100644 index 0000000000000000000000000000000000000000..b0513c46abdd613c3548d4023c6afe2670e440f4 Binary files /dev/null and b/resources/fonts/Aller_Bd.ttf differ diff --git a/resources/fonts/FZKATJW.ttf b/resources/fonts/FZKATJW.ttf new file mode 100644 index 0000000000000000000000000000000000000000..d9da38dc2b4f6a4901bbf43c637d5afba333e68e --- /dev/null +++ b/resources/fonts/FZKATJW.ttf @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d3988cdca576ea293487944aac33855fa6f92ab9a163f42b198339db00b0b596 +size 2852808 diff --git a/resources/fonts/FZSEJW.ttf b/resources/fonts/FZSEJW.ttf new file mode 100644 index 0000000000000000000000000000000000000000..0b6868e8afddf27619da16044d516e6b59cc62f7 --- /dev/null +++ b/resources/fonts/FZSEJW.ttf @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:dc9862417e867e7018c0e87a6bf9677a8fde25ab504bfcb05e5926c24a02e3c8 +size 3561264 diff --git a/resources/fonts/FZSJ-QINGCRJ.ttf b/resources/fonts/FZSJ-QINGCRJ.ttf new file mode 100644 index 0000000000000000000000000000000000000000..29760a597b5bab93e1bcb7961204d9775b5e75e0 --- /dev/null +++ b/resources/fonts/FZSJ-QINGCRJ.ttf @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:40df2ae9d2ea46549b141947492e7111240fd2385fd2cc5355bf4708bb086983 +size 4341676 diff --git a/resources/fonts/FZXS14.ttf b/resources/fonts/FZXS14.ttf new file mode 100644 index 0000000000000000000000000000000000000000..f3d7af6395a8aaedddaca4165e457cb291248066 --- /dev/null +++ b/resources/fonts/FZXS14.ttf @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c1bfb94df96b5e907a0a07c87ab79d50703bd887c0d953e75aa2ea8620e25193 +size 2258080 diff --git a/resources/fonts/HiraginoMin-W5-90-RKSJ-H-2.ttc b/resources/fonts/HiraginoMin-W5-90-RKSJ-H-2.ttc new file mode 100644 index 0000000000000000000000000000000000000000..d53a93864b28ebe210a1c3e8c890e83e4103013e --- /dev/null +++ b/resources/fonts/HiraginoMin-W5-90-RKSJ-H-2.ttc @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:52b1b0e94d3a8af284ca0d57c23e74aaed003859c597d047c4b0c3411f451ed7 +size 6896064 diff --git a/resources/fonts/NotoSansSC-Regular.otf b/resources/fonts/NotoSansSC-Regular.otf new file mode 100644 index 0000000000000000000000000000000000000000..7a5dc1b22de4089fc86d3009d83c26c87ebe3d84 --- /dev/null +++ b/resources/fonts/NotoSansSC-Regular.otf @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:63fdadb47c21197170f3cda6c60e98e481b8a1eb28e5f44102da51bec17d123b +size 8481960 diff --git a/resources/fonts/NotoSerifSC-Regular.otf b/resources/fonts/NotoSerifSC-Regular.otf new file mode 100644 index 0000000000000000000000000000000000000000..954b7883ab94a250f1fee8c614bd43cdc5ce94e4 --- /dev/null +++ b/resources/fonts/NotoSerifSC-Regular.otf @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:66080541a111a7a31179700496013aac4b64a53cc41f570bd21c2628c75e4628 +size 11214568 diff --git a/resources/fonts/consola.ttf b/resources/fonts/consola.ttf new file mode 100644 index 0000000000000000000000000000000000000000..e881ca4b59fee70c0b3c87b258a5ab09065eb5e5 Binary files /dev/null and b/resources/fonts/consola.ttf differ diff --git a/resources/resource_list.json b/resources/resource_list.json new file mode 100644 index 0000000000000000000000000000000000000000..2ac3485fc6cec96a3668374de60cdef79071f054 --- /dev/null +++ b/resources/resource_list.json @@ -0,0 +1,5238 @@ +[ + { + "path": "acg_entrance/images/0.png", + "hash": "50d92b64bfdd6b7aa3872ba24bface4f" + }, + { + "path": "add_chaos/images/0.png", + "hash": "c9265d1d01b8d0746a544640f924e2c2" + }, + { + "path": "addiction/images/0.png", + "hash": "dc201145111b74eca4d09429afe0dfb9" + }, + { + "path": "always_like/images/0.png", + "hash": "7e4b3845cf80fa46942269d58bd64e9d" + }, + { + "path": "anti_kidnap/images/0.png", + "hash": "57991b7103cecbe1c13dc03cf4b7ed38" + }, + { + "path": "anya_suki/images/0.png", + "hash": "9a46dd33cc7a511a8368e768c6d4abb0" + }, + { + "path": "applaud/images/0.png", + "hash": "0e5f561f3abc093f8089ed5e3b4d4e51" + }, + { + "path": "applaud/images/1.png", + "hash": "948ee568b13be8427c0dd8d6de5740c3" + }, + { + "path": "applaud/images/2.png", + "hash": "ab191735e9c523517deca3e4c444bd81" + }, + { + "path": "applaud/images/3.png", + "hash": "46579d1329fa40685a264720bdb97593" + }, + { + "path": "applaud/images/4.png", + "hash": "26b72b155b645d5e8ed4f96948c48ba7" + }, + { + "path": "ascension/images/0.png", + "hash": "e09e50c266c1e8babfe2c73612ac4200" + }, + { + "path": "back_to_work/images/0.png", + "hash": "d85cf009fac3ce49ed90883e0c61e737" + }, + { + "path": "bad_news/images/0.png", + "hash": "960baf23d9d516c5ea6d13f48b67f070" + }, + { + "path": "beat_head/images/0.png", + "hash": "eacd07809b6ead153d7edd6630c18b57" + }, + { + "path": "beat_head/images/1.png", + "hash": "9c9c2eeee4dd11ce6af0e5ac9810ff9b" + }, + { + "path": "beat_head/images/2.png", + "hash": "94e491fbd91d9a8714ad87bd6dd2259f" + }, + { + "path": "bite/images/0.png", + "hash": "1bf01e9d335cea07f0fa41ac9b7cf1c4" + }, + { + "path": "bite/images/1.png", + "hash": "f39275eb62478b134d334cd12816ea8b" + }, + { + "path": "bite/images/10.png", + "hash": "3803c4e601226c29d9d5649fe4a14035" + }, + { + "path": "bite/images/11.png", + "hash": "0ecbeccbb0b56aba5141482308749688" + }, + { + "path": "bite/images/12.png", + "hash": "33294ff41e6106e9f058cb4c12774ecf" + }, + { + "path": "bite/images/13.png", + "hash": "0c146c08708ff40246876f6aab9ea7e5" + }, + { + "path": "bite/images/14.png", + "hash": "3803c4e601226c29d9d5649fe4a14035" + }, + { + "path": "bite/images/15.png", + "hash": "0ecbeccbb0b56aba5141482308749688" + }, + { + "path": "bite/images/2.png", + "hash": "ed49249ac247d237310faffb7d11c11e" + }, + { + "path": "bite/images/3.png", + "hash": "95d8fe8bfe6a6bafc0a02b7be64e7ea5" + }, + { + "path": "bite/images/4.png", + "hash": "a95502dae9f2a18b8be3dd2c68af10aa" + }, + { + "path": "bite/images/5.png", + "hash": "04449c3729ff7422bbd2f811e7ec9d89" + }, + { + "path": "bite/images/6.png", + "hash": "1924b0e63242af4a0280a5458e9cafda" + }, + { + "path": "bite/images/7.png", + "hash": "90bc4c8ad295a7a63123ebf98a39fe0f" + }, + { + "path": "bite/images/8.png", + "hash": "33294ff41e6106e9f058cb4c12774ecf" + }, + { + "path": "bite/images/9.png", + "hash": "0c146c08708ff40246876f6aab9ea7e5" + }, + { + "path": "blood_pressure/images/0.png", + "hash": "d5dd1ea44908952885ff14a283f99695" + }, + { + "path": "bocchi_draft/images/0.png", + "hash": "680aea676a5f3d510ddee5c30201e59e" + }, + { + "path": "bocchi_draft/images/1.png", + "hash": "680aea676a5f3d510ddee5c30201e59e" + }, + { + "path": "bocchi_draft/images/10.png", + "hash": "7223660dff08058ebfd18abe91e3b4c1" + }, + { + "path": "bocchi_draft/images/11.png", + "hash": "5be40608585af14e81d1290a822bb765" + }, + { + "path": "bocchi_draft/images/12.png", + "hash": "10fe7523c888e2f9d58f63b529138c67" + }, + { + "path": "bocchi_draft/images/13.png", + "hash": "f04a0254f4cf76de4dcb15e006ce5251" + }, + { + "path": "bocchi_draft/images/14.png", + "hash": "e02a5bf9fd8c010dbbdbdfcfd68c88b2" + }, + { + "path": "bocchi_draft/images/15.png", + "hash": "c627cf848a3258c3f60f387f94424594" + }, + { + "path": "bocchi_draft/images/16.png", + "hash": "3ae22d0f4d647123a5a3d6463615894f" + }, + { + "path": "bocchi_draft/images/17.png", + "hash": "d3b524c173dae3f6f99a9280348c7c9f" + }, + { + "path": "bocchi_draft/images/18.png", + "hash": "89138ffaae09314d0d6ace6ea084dd35" + }, + { + "path": "bocchi_draft/images/19.png", + "hash": "5369cc29e4deab14936cae35cf6a039e" + }, + { + "path": "bocchi_draft/images/2.png", + "hash": "680aea676a5f3d510ddee5c30201e59e" + }, + { + "path": "bocchi_draft/images/20.png", + "hash": "23b17e86c34e6762c774f71cebef119b" + }, + { + "path": "bocchi_draft/images/21.png", + "hash": "23b17e86c34e6762c774f71cebef119b" + }, + { + "path": "bocchi_draft/images/22.png", + "hash": "23b17e86c34e6762c774f71cebef119b" + }, + { + "path": "bocchi_draft/images/3.png", + "hash": "d2fe56d893a8bf44d12fa43dc0d9616f" + }, + { + "path": "bocchi_draft/images/4.png", + "hash": "8c9b0855d8a752fb0c1969e5962035e2" + }, + { + "path": "bocchi_draft/images/5.png", + "hash": "3e1fe9c59bd56721b8e306b30fec11a1" + }, + { + "path": "bocchi_draft/images/6.png", + "hash": "81c83f74c8fb24c7aaf47c794c42f5b7" + }, + { + "path": "bocchi_draft/images/7.png", + "hash": "12d192f4487f6caf72c14328fbe62915" + }, + { + "path": "bocchi_draft/images/8.png", + "hash": "30c655e223bb9e6d2f5f60deec416941" + }, + { + "path": "bocchi_draft/images/9.png", + "hash": "ecd068db32969f4b587b992acce1fd65" + }, + { + "path": "bronya_holdsign/images/0.jpg", + "hash": "fc035e0ac02ee3bd44f4409f8df14ed9" + }, + { + "path": "bubble_tea/images/0.png", + "hash": "31c42e10985c1efa934d6b616b155c92" + }, + { + "path": "caoshen_bite/images/0.png", + "hash": "08420f435eb8e9b395fad3bc11823c70" + }, + { + "path": "caoshen_bite/images/1.png", + "hash": "f340d01ad0763eb5600a41c8008e03d5" + }, + { + "path": "caoshen_bite/images/10.png", + "hash": "a1d267b8c2dc8960145e809a6f369fab" + }, + { + "path": "caoshen_bite/images/11.png", + "hash": "9f9441dcc91f0062ca846ad3b0b8620c" + }, + { + "path": "caoshen_bite/images/12.png", + "hash": "ff19d6745c655440c5694ef5f295a65d" + }, + { + "path": "caoshen_bite/images/13.png", + "hash": "ef07e844622561ad41f0f385d1c6c9b3" + }, + { + "path": "caoshen_bite/images/14.png", + "hash": "897d566b8141a1bfc2511028cee4fe1f" + }, + { + "path": "caoshen_bite/images/15.png", + "hash": "fd6c58767115008172ab831f45094e17" + }, + { + "path": "caoshen_bite/images/16.png", + "hash": "601e9c86aa5c88f8f61eb1f864f646ed" + }, + { + "path": "caoshen_bite/images/17.png", + "hash": "7bd7c71a858fb17a79e9edad48c9eaa7" + }, + { + "path": "caoshen_bite/images/18.png", + "hash": "9cd95aa172f1ed3c7f05b09cc79ab782" + }, + { + "path": "caoshen_bite/images/19.png", + "hash": "75a0f0e5743b463fe2720fd2af485faf" + }, + { + "path": "caoshen_bite/images/2.png", + "hash": "6aad3b8f4033dbd8e8d3337543e13021" + }, + { + "path": "caoshen_bite/images/20.png", + "hash": "ea4d6d11dba51342bd1dabe6d88ba5e3" + }, + { + "path": "caoshen_bite/images/21.png", + "hash": "a567e86a31fcbe0253deb3feb774d4c5" + }, + { + "path": "caoshen_bite/images/22.png", + "hash": "05e08e612e618e33439e1d1d6e8fa175" + }, + { + "path": "caoshen_bite/images/23.png", + "hash": "18389bd829edb63d0ceae078bb1763ce" + }, + { + "path": "caoshen_bite/images/24.png", + "hash": "7891aac64cf87dc9d9434fac371a3407" + }, + { + "path": "caoshen_bite/images/25.png", + "hash": "ae02845905cb4dabc9fb895abd401c00" + }, + { + "path": "caoshen_bite/images/26.png", + "hash": "64edbdbe41176edca386c65f0f8af96f" + }, + { + "path": "caoshen_bite/images/27.png", + "hash": "f4cb93a0a4bec4e7d083a3d9933cf09c" + }, + { + "path": "caoshen_bite/images/28.png", + "hash": "97b63b42a2185e68226a7169d87b0b6e" + }, + { + "path": "caoshen_bite/images/29.png", + "hash": "1fd421c45721fd2e0d0f332b7f04292e" + }, + { + "path": "caoshen_bite/images/3.png", + "hash": "2e1d13692bf091b5c6f6f085949d68f8" + }, + { + "path": "caoshen_bite/images/30.png", + "hash": "b6eecaaab53ea23f05257ac999c418d0" + }, + { + "path": "caoshen_bite/images/31.png", + "hash": "7895a3b96748d299471d5ea7e29acd1a" + }, + { + "path": "caoshen_bite/images/32.png", + "hash": "651c3ed9a98818165d18e0c324824460" + }, + { + "path": "caoshen_bite/images/33.png", + "hash": "a040f062a3efc9a234cb086f434dafbf" + }, + { + "path": "caoshen_bite/images/34.png", + "hash": "34d89ed45313e5251253af64f616e8a1" + }, + { + "path": "caoshen_bite/images/35.png", + "hash": "655c475ad47f0badb51dccfca6e1aac7" + }, + { + "path": "caoshen_bite/images/36.png", + "hash": "0c5f969d05f730bedc7e2878e273b874" + }, + { + "path": "caoshen_bite/images/37.png", + "hash": "2d224e773965415953b7b4b8841b595c" + }, + { + "path": "caoshen_bite/images/4.png", + "hash": "ea4c10cdfb97252224814c92d8ecc175" + }, + { + "path": "caoshen_bite/images/5.png", + "hash": "cc4c6a030b0ba9a77ba559a49ce9d9be" + }, + { + "path": "caoshen_bite/images/6.png", + "hash": "d6f30de64fadec982c1e988a71dff7ed" + }, + { + "path": "caoshen_bite/images/7.png", + "hash": "b817fc07aa3cde720162bb693caca64c" + }, + { + "path": "caoshen_bite/images/8.png", + "hash": "5be01bb928ba4b553a7eb2256b5ec198" + }, + { + "path": "caoshen_bite/images/9.png", + "hash": "737d6062cd53c0133b361cb2c4543ffc" + }, + { + "path": "capoo_draw/images/0.png", + "hash": "f912144ee503cb4c193e6c109eb83637" + }, + { + "path": "capoo_draw/images/1.png", + "hash": "d85d94489c3a4ad1c418782ff5a9dafa" + }, + { + "path": "capoo_draw/images/2.png", + "hash": "dc3f3d37a5b985088388253ff7abecf4" + }, + { + "path": "capoo_draw/images/3.png", + "hash": "97034f01eed7f12767f224911e667a6f" + }, + { + "path": "capoo_draw/images/4.png", + "hash": "137787ff3e3df61f9238874ebe8ce46b" + }, + { + "path": "capoo_draw/images/5.png", + "hash": "f0480a00928db523221265376dfe7c07" + }, + { + "path": "capoo_rip/images/0.png", + "hash": "b9071375f262197ebb2a646875b8d819" + }, + { + "path": "capoo_rip/images/1.png", + "hash": "1e57ec96c106f137106eaf263f28ef4b" + }, + { + "path": "capoo_rip/images/2.png", + "hash": "6b30315d53652333e1587b7478242d27" + }, + { + "path": "capoo_rip/images/3.png", + "hash": "a949947118b16140c7efb35c5ed59abf" + }, + { + "path": "capoo_rip/images/4.png", + "hash": "c4337ead3d063740014be45c6b14eb1f" + }, + { + "path": "capoo_rip/images/5.png", + "hash": "983f0d7e0c9080479d29f646c0a406f8" + }, + { + "path": "capoo_rip/images/6.png", + "hash": "1258ad3db7914f7f2f955a6f2c910595" + }, + { + "path": "capoo_rip/images/7.png", + "hash": "c4d7613a7d578dd956e66edfbac23c89" + }, + { + "path": "capoo_rub/images/0.png", + "hash": "6fc3f9dd10f0a7de7f8c4c853166de26" + }, + { + "path": "capoo_rub/images/1.png", + "hash": "4ee09f4c274dac2d33c59f1a464591df" + }, + { + "path": "capoo_rub/images/2.png", + "hash": "3e4d4babbd4b9081682651510ad70680" + }, + { + "path": "capoo_rub/images/3.png", + "hash": "6d35b343f1bde5f1de4a5df94921b634" + }, + { + "path": "capoo_say/images/0.png", + "hash": "f068d28034bde7515340a58da0e4d9bf" + }, + { + "path": "capoo_say/images/1.png", + "hash": "3135e96c106a035153400d2ebe8f0be5" + }, + { + "path": "capoo_say/images/2.png", + "hash": "13c0e3e5db935829df8216518b951ebd" + }, + { + "path": "capoo_say/images/3.png", + "hash": "371b2e086c5949945a896c63a091fa7a" + }, + { + "path": "capoo_say/images/4.png", + "hash": "0c2bd48833523efb31917ad3d3b9b639" + }, + { + "path": "capoo_say/images/5.png", + "hash": "e55a0f719d1e6cd644f5ccff2cd783c0" + }, + { + "path": "capoo_say/images/6.png", + "hash": "a834e50a071769825efa5d37bc983f3e" + }, + { + "path": "capoo_say/images/7.png", + "hash": "7d3d97118846dea27983479710d91194" + }, + { + "path": "capoo_say/images/8.png", + "hash": "1079fa13aa6adcfb2d90c1a58d5d092d" + }, + { + "path": "capoo_say/images/9.png", + "hash": "b9b1b73405dd68d923930425a04fc26e" + }, + { + "path": "capoo_strike/images/0.png", + "hash": "3b7f60521294a0fd93c9a73b8d67a4e7" + }, + { + "path": "capoo_strike/images/1.png", + "hash": "c73899d2b67010fb4171d4aedcdaed81" + }, + { + "path": "capoo_strike/images/2.png", + "hash": "4c6643ac048d75df4103af4788be9053" + }, + { + "path": "capoo_strike/images/3.png", + "hash": "865082240baad33b6a1d58ba6187c026" + }, + { + "path": "capoo_strike/images/4.png", + "hash": "d9ae348ed2306b1fe04f7004078bb41f" + }, + { + "path": "capoo_strike/images/5.png", + "hash": "e33771e54e4204c5881ec955fa6a021d" + }, + { + "path": "capoo_strike/images/6.png", + "hash": "a4f9b749b5481a65220581eb7be03d8b" + }, + { + "path": "captain/images/0.png", + "hash": "f6cca0f44aa54c721f8b172f8ebb7f40" + }, + { + "path": "captain/images/1.png", + "hash": "2885566e47ef4fb40560149c795dca7f" + }, + { + "path": "captain/images/2.png", + "hash": "e230240d0929b774a985a301962d36b5" + }, + { + "path": "chase_train/images/0.png", + "hash": "7bf7c41898169f70973a249905e1086c" + }, + { + "path": "chase_train/images/1.png", + "hash": "1db1364a8dbd84e6ef74079bb5ff17a8" + }, + { + "path": "chase_train/images/10.png", + "hash": "292dbe5e5d0e420b4ef258b19ac52172" + }, + { + "path": "chase_train/images/100.png", + "hash": "4ed64a045b8ab95aafea0eb4c5079fa6" + }, + { + "path": "chase_train/images/101.png", + "hash": "335739eb957573a9dec19dba2b16b3e7" + }, + { + "path": "chase_train/images/102.png", + "hash": "dd879e677cb54ea91a7e68f776c523a6" + }, + { + "path": "chase_train/images/103.png", + "hash": "739f501c7826edd93c77b66ec868b194" + }, + { + "path": "chase_train/images/104.png", + "hash": "ba51852201ecad21a3f76d7603bba4a5" + }, + { + "path": "chase_train/images/105.png", + "hash": "f165e50e13ba5ec1806ba56111ebadd9" + }, + { + "path": "chase_train/images/106.png", + "hash": "dddec974b82ae647fbdf1c2a09a3ee61" + }, + { + "path": "chase_train/images/107.png", + "hash": "542d308cbcf85cc202622a79588d3a49" + }, + { + "path": "chase_train/images/108.png", + "hash": "2b10c147fc987e17e8898f77e2bdcb14" + }, + { + "path": "chase_train/images/109.png", + "hash": "69f87ef8712a717ce6dbaa7aa95d8eea" + }, + { + "path": "chase_train/images/11.png", + "hash": "9c046b3902292b4737503161f2b62d02" + }, + { + "path": "chase_train/images/110.png", + "hash": "4e91a1f042ff3f4adb1a823b1e1f1fe2" + }, + { + "path": "chase_train/images/111.png", + "hash": "edd3d9ee22bf9e700ba6ddb4827afd3e" + }, + { + "path": "chase_train/images/112.png", + "hash": "ed5902b62cd2e1ac54d32ff19e30505e" + }, + { + "path": "chase_train/images/113.png", + "hash": "e65e849be18bfc610b60ac862cf9e1e5" + }, + { + "path": "chase_train/images/114.png", + "hash": "4d464c70883177e31f05a65ade9de13e" + }, + { + "path": "chase_train/images/115.png", + "hash": "353675889c312a1416055d01317069ba" + }, + { + "path": "chase_train/images/116.png", + "hash": "b207d15fdbb26087d5bf527f27e4f087" + }, + { + "path": "chase_train/images/117.png", + "hash": "36fe4bb2cce9974141030210144c3ad8" + }, + { + "path": "chase_train/images/118.png", + "hash": "4235c27a89563fea2d7f5b114233ef17" + }, + { + "path": "chase_train/images/119.png", + "hash": "dd2c42261479419e676f09c55ed8e79a" + }, + { + "path": "chase_train/images/12.png", + "hash": "98ea083aa53ff3f3af606e7e67033476" + }, + { + "path": "chase_train/images/13.png", + "hash": "a6f43b04aa305e5a320e20db5d850199" + }, + { + "path": "chase_train/images/14.png", + "hash": "37074ede0dea70240baf2d26dd77a373" + }, + { + "path": "chase_train/images/15.png", + "hash": "fae7dbfbfff28b3eae665d48c656d4ff" + }, + { + "path": "chase_train/images/16.png", + "hash": "47b500c96c779d4ac335b5c46bdb2e0a" + }, + { + "path": "chase_train/images/17.png", + "hash": "e69029fc7f4ed692e06c4794d6dac4ae" + }, + { + "path": "chase_train/images/18.png", + "hash": "71de33d9e84189ab458df8c053317b91" + }, + { + "path": "chase_train/images/19.png", + "hash": "8d36823b898e52b32cc25b9ffabcf339" + }, + { + "path": "chase_train/images/2.png", + "hash": "65a0830bc6d6a3127faf8e50822ac8b3" + }, + { + "path": "chase_train/images/20.png", + "hash": "f5734adff0f98aa2efa2f0b7d70884bc" + }, + { + "path": "chase_train/images/21.png", + "hash": "6815118e286fdbf9078e20f41e6112af" + }, + { + "path": "chase_train/images/22.png", + "hash": "01f3a8eb8983bc6e24634839bee85b55" + }, + { + "path": "chase_train/images/23.png", + "hash": "b02e49bd23246d91efac12cc777d7f78" + }, + { + "path": "chase_train/images/24.png", + "hash": "a0ee2dc52c4c1c473cc57092d942f7b2" + }, + { + "path": "chase_train/images/25.png", + "hash": "ab7a9e557e09314a8e5109dcf809e296" + }, + { + "path": "chase_train/images/26.png", + "hash": "b1cadf11b827d74f0ddf48ef0a5a5d28" + }, + { + "path": "chase_train/images/27.png", + "hash": "6348519826d61b471a8fece92e4ee516" + }, + { + "path": "chase_train/images/28.png", + "hash": "58f9a74fa74d3a4198510e005c4484ca" + }, + { + "path": "chase_train/images/29.png", + "hash": "521571b8b5b5654129ebb195066480c6" + }, + { + "path": "chase_train/images/3.png", + "hash": "3b9f749bc39286b7b5e0d9e726a1d4bd" + }, + { + "path": "chase_train/images/30.png", + "hash": "973241e42057f711fd370c36d466d683" + }, + { + "path": "chase_train/images/31.png", + "hash": "57bcfb0936d920907e0c84d8091f2384" + }, + { + "path": "chase_train/images/32.png", + "hash": "a475f33be6cff4fd435429165e7a89ab" + }, + { + "path": "chase_train/images/33.png", + "hash": "9fe7fc8f8b51e75b55ce58990615b8ae" + }, + { + "path": "chase_train/images/34.png", + "hash": "ca099830bd27ea5a263ec413032a731a" + }, + { + "path": "chase_train/images/35.png", + "hash": "48a37c15cb3f553178b8a322ee5498a4" + }, + { + "path": "chase_train/images/36.png", + "hash": "83ddc24949269dacee985c5dd08dfef0" + }, + { + "path": "chase_train/images/37.png", + "hash": "502d977a23285c42690e04e48133ef45" + }, + { + "path": "chase_train/images/38.png", + "hash": "c34f48052c67d8bd9eb57499da3fce44" + }, + { + "path": "chase_train/images/39.png", + "hash": "f8da1e9c2dea0e6d227412a70a556ada" + }, + { + "path": "chase_train/images/4.png", + "hash": "5869633c96666ef1bd22a23177e41a11" + }, + { + "path": "chase_train/images/40.png", + "hash": "4976ea014147cdf6beebb22e26011ad6" + }, + { + "path": "chase_train/images/41.png", + "hash": "00a733e04c08f01af4a76d470c9fa3e0" + }, + { + "path": "chase_train/images/42.png", + "hash": "4bbc5205e8c8368ef5aeaf964128c79d" + }, + { + "path": "chase_train/images/43.png", + "hash": "aea76edecc7a6115cca6a75e98005adc" + }, + { + "path": "chase_train/images/44.png", + "hash": "916f4fe46a610ad7a36fbd2dfe2b1a71" + }, + { + "path": "chase_train/images/45.png", + "hash": "ba8f249cbdb55c25b467134c0c486bd3" + }, + { + "path": "chase_train/images/46.png", + "hash": "c9b25bc88e55cfffc0fa86615fcfe81c" + }, + { + "path": "chase_train/images/47.png", + "hash": "65d9a4ca467b3586f07e51cc1eabbc98" + }, + { + "path": "chase_train/images/48.png", + "hash": "7206195dbce1db53e8824d6e9aec3080" + }, + { + "path": "chase_train/images/49.png", + "hash": "2b4c83f6bc3b23ee293e1aac1a7ccd03" + }, + { + "path": "chase_train/images/5.png", + "hash": "1752695309e244dbd1d9717da63c9129" + }, + { + "path": "chase_train/images/50.png", + "hash": "3e2a46aa34f81dcd11fd4231c6b05160" + }, + { + "path": "chase_train/images/51.png", + "hash": "3b52abcf06fef40c45f373791f38a7ae" + }, + { + "path": "chase_train/images/52.png", + "hash": "db480438656ac78c053bd986f88b8e20" + }, + { + "path": "chase_train/images/53.png", + "hash": "efca259de1ced624cf9f24f78ca4cec8" + }, + { + "path": "chase_train/images/54.png", + "hash": "dc0b5c1a5971e2141cbe4d9c56d0c128" + }, + { + "path": "chase_train/images/55.png", + "hash": "ad9da6c1dc641a6bede0f4b7c8e38f30" + }, + { + "path": "chase_train/images/56.png", + "hash": "235c74c051701ca383eb8dc390934ac3" + }, + { + "path": "chase_train/images/57.png", + "hash": "257a0e3debc5b5176dc5ea61880a9d2d" + }, + { + "path": "chase_train/images/58.png", + "hash": "e4aa8b6784dd697280065e967315afc9" + }, + { + "path": "chase_train/images/59.png", + "hash": "455619e428f24bc7297246ea509cdc8e" + }, + { + "path": "chase_train/images/6.png", + "hash": "a2e63edc5c56f8dc5a1f749eaba9ad1c" + }, + { + "path": "chase_train/images/60.png", + "hash": "06658ba5901219637dcb86687e878488" + }, + { + "path": "chase_train/images/61.png", + "hash": "7fbab76e20924d6d655d256cb2a7618e" + }, + { + "path": "chase_train/images/62.png", + "hash": "f00691c5671383d2040ebb3cc6d0fce6" + }, + { + "path": "chase_train/images/63.png", + "hash": "22dc12001b5237b196859a9f57c00486" + }, + { + "path": "chase_train/images/64.png", + "hash": "fb529e76863bc338b7c01d8d5d152a01" + }, + { + "path": "chase_train/images/65.png", + "hash": "d9da6224c137a9f20998acbe92740dc2" + }, + { + "path": "chase_train/images/66.png", + "hash": "b3d7c85a3af021ae09ccc111a271f67d" + }, + { + "path": "chase_train/images/67.png", + "hash": "f9e1bb5ab9160458dabfb2c814e5c96c" + }, + { + "path": "chase_train/images/68.png", + "hash": "34629b82b74ea3064f91cb5e2d3d2c35" + }, + { + "path": "chase_train/images/69.png", + "hash": "a3cc3bd6f43a9d57f7165db69409b785" + }, + { + "path": "chase_train/images/7.png", + "hash": "81a63cd5a80146039ead67df4dc6a00f" + }, + { + "path": "chase_train/images/70.png", + "hash": "25f0e2695b503b9712aa97586e2d2d68" + }, + { + "path": "chase_train/images/71.png", + "hash": "a730868e157a3cf19d522c5c522a6ebe" + }, + { + "path": "chase_train/images/72.png", + "hash": "5a2d4cad093f62729029e7b46267912b" + }, + { + "path": "chase_train/images/73.png", + "hash": "ded3cb4a8d95243f160aec398b1478d6" + }, + { + "path": "chase_train/images/74.png", + "hash": "779beb6f0109623be10b2384e14e5170" + }, + { + "path": "chase_train/images/75.png", + "hash": "e04087097cf67d87e5a1a845a7f4f2e4" + }, + { + "path": "chase_train/images/76.png", + "hash": "398d14e6939d3af6a56382d5e4798ea7" + }, + { + "path": "chase_train/images/77.png", + "hash": "04e0eab8bbe7812c340e6973796b5852" + }, + { + "path": "chase_train/images/78.png", + "hash": "24a19ab361acbceda3b74a920c116cf4" + }, + { + "path": "chase_train/images/79.png", + "hash": "c64f1273c6f0838c82f63644ba61f2b0" + }, + { + "path": "chase_train/images/8.png", + "hash": "6f905804a2dd792088fb020ed4ba1ec5" + }, + { + "path": "chase_train/images/80.png", + "hash": "9dbf7b98b1f900666a284ff8c2a53bb5" + }, + { + "path": "chase_train/images/81.png", + "hash": "b1fac283bebd34b8299b4f07796882d1" + }, + { + "path": "chase_train/images/82.png", + "hash": "838e37bd37a44ae626057b7f4112a4f4" + }, + { + "path": "chase_train/images/83.png", + "hash": "1fc3c2aac840addab4a7232fce8d2618" + }, + { + "path": "chase_train/images/84.png", + "hash": "11e70793a048c508b1fd7ce200f2305c" + }, + { + "path": "chase_train/images/85.png", + "hash": "c9dc1d6b9880738b34ed8c2523ab28fa" + }, + { + "path": "chase_train/images/86.png", + "hash": "7accb0d98b7a09033238757722c06481" + }, + { + "path": "chase_train/images/87.png", + "hash": "ad7a36af263b89b9fd93c560cd93c734" + }, + { + "path": "chase_train/images/88.png", + "hash": "78c6254f5bc50edae2ad5ae77276ad37" + }, + { + "path": "chase_train/images/89.png", + "hash": "bc84844fa4af35b09773caaa78fefda0" + }, + { + "path": "chase_train/images/9.png", + "hash": "ac728bb2724f1b87a858225df47fe1a7" + }, + { + "path": "chase_train/images/90.png", + "hash": "50dc68f1ce171eea7186f1c80a868ad8" + }, + { + "path": "chase_train/images/91.png", + "hash": "8781c486e83faad346335c7f10fcd4ec" + }, + { + "path": "chase_train/images/92.png", + "hash": "6bb85189d43d162ff52e37da13a2c090" + }, + { + "path": "chase_train/images/93.png", + "hash": "4b7bb7d0423d12aba6b52153cefd8a82" + }, + { + "path": "chase_train/images/94.png", + "hash": "43ce0d9a874c4d4452f19e87411b1b24" + }, + { + "path": "chase_train/images/95.png", + "hash": "f61a6df0cb1977cdbc0413b249d2fec1" + }, + { + "path": "chase_train/images/96.png", + "hash": "2ba4cdef4cd8822b36c6afdf65aa2ccf" + }, + { + "path": "chase_train/images/97.png", + "hash": "7334779ae38e8a0f1d76e7e6d576af5c" + }, + { + "path": "chase_train/images/98.png", + "hash": "d249d2f3aab8f4eec578465b76c50cad" + }, + { + "path": "chase_train/images/99.png", + "hash": "4571cc545e6885c3b564ab87336cfe8e" + }, + { + "path": "china_flag/images/0.png", + "hash": "aa64adae6f7ede5bceafe81a5273cafc" + }, + { + "path": "confuse/images/0.png", + "hash": "4758c555242fb3b8f3f7426e575dde95" + }, + { + "path": "confuse/images/1.png", + "hash": "36fad69b2f66b88282254a9faf57d1ac" + }, + { + "path": "confuse/images/10.png", + "hash": "6ce9115d8292edabd74a1b7fbdced670" + }, + { + "path": "confuse/images/11.png", + "hash": "9b1a1434e083320b58390a4f86aa8ac8" + }, + { + "path": "confuse/images/12.png", + "hash": "f53fca003ac5b66f64cf7546a56356bc" + }, + { + "path": "confuse/images/13.png", + "hash": "14e4c8f746f319f15cad7d02daa7dad0" + }, + { + "path": "confuse/images/14.png", + "hash": "ba413c63991f17e2bd0a28bc4c105bae" + }, + { + "path": "confuse/images/15.png", + "hash": "f61159552fa9ab7f9e452ff927fc8acb" + }, + { + "path": "confuse/images/16.png", + "hash": "f938d95975d5f1045c2823ad396b4979" + }, + { + "path": "confuse/images/17.png", + "hash": "6832dea7f470464c095f65925e3a6ba9" + }, + { + "path": "confuse/images/18.png", + "hash": "8a49ebf2ab4564f650e690bef420695d" + }, + { + "path": "confuse/images/19.png", + "hash": "9a6d5fe6538b795bba572e0c4ab55ecc" + }, + { + "path": "confuse/images/2.png", + "hash": "82364e42fbc865097412338837b2d328" + }, + { + "path": "confuse/images/20.png", + "hash": "c29dfedd885067b036f2fa6e6d353ff3" + }, + { + "path": "confuse/images/21.png", + "hash": "349a96211a03552d040d044eb3f6bdd8" + }, + { + "path": "confuse/images/22.png", + "hash": "02de93e56d54e4c0d5628f5261643203" + }, + { + "path": "confuse/images/23.png", + "hash": "5ef1795da2f86c4a64e67263e3edfd1a" + }, + { + "path": "confuse/images/24.png", + "hash": "cc16e54ef506cdb23d9e0cfad2294603" + }, + { + "path": "confuse/images/25.png", + "hash": "b031bdd75398a2c14913d3a9b0f434c2" + }, + { + "path": "confuse/images/26.png", + "hash": "b410f262a549e23bf0c7cade90ebb412" + }, + { + "path": "confuse/images/27.png", + "hash": "11e766fac91cef3a207fab7c6131790f" + }, + { + "path": "confuse/images/28.png", + "hash": "0e5c0d09536eeefd8648c820e3d6d802" + }, + { + "path": "confuse/images/29.png", + "hash": "e22c184a0b8fae0c34e135949bb8b988" + }, + { + "path": "confuse/images/3.png", + "hash": "349dd7216c0a55db11127d985d592b12" + }, + { + "path": "confuse/images/30.png", + "hash": "5cebf2a9d4fa49500885df77b759cf1b" + }, + { + "path": "confuse/images/31.png", + "hash": "1632f329095a83130e72f89ff37c8454" + }, + { + "path": "confuse/images/32.png", + "hash": "362d6ce1326c08436566b7944a8bd2f4" + }, + { + "path": "confuse/images/33.png", + "hash": "ab3516b03fb98be9785005958d2577a3" + }, + { + "path": "confuse/images/34.png", + "hash": "da4af5bbd2f6b8747965b300e421c902" + }, + { + "path": "confuse/images/35.png", + "hash": "ac38b4b0bd681bf5b13b37888d05992b" + }, + { + "path": "confuse/images/36.png", + "hash": "95a43fc43eac8a5bde512ddacf037c3d" + }, + { + "path": "confuse/images/37.png", + "hash": "0c8526d12f5764a860d01017f3fbebbc" + }, + { + "path": "confuse/images/38.png", + "hash": "43a4bf013712dd73830d728e024d03f3" + }, + { + "path": "confuse/images/39.png", + "hash": "df7f3cb11a4e3a964e6886fd91559bf0" + }, + { + "path": "confuse/images/4.png", + "hash": "935ae3c540c5fe3dbad3cc5900564272" + }, + { + "path": "confuse/images/40.png", + "hash": "86d07b123ce2d919aa0d1a11bc8c8af0" + }, + { + "path": "confuse/images/41.png", + "hash": "14c5dcda50d097e7c418d678314f762a" + }, + { + "path": "confuse/images/42.png", + "hash": "670d20b0d7bd9327d4314871972c8dc6" + }, + { + "path": "confuse/images/43.png", + "hash": "cd247bbbe553704b93a22d6a68d68db9" + }, + { + "path": "confuse/images/44.png", + "hash": "e1c0b8a37f6a14d4512e68a5aa9c8c7e" + }, + { + "path": "confuse/images/45.png", + "hash": "f5efa7ffa7d28a8d9734ac6da16fd1f8" + }, + { + "path": "confuse/images/46.png", + "hash": "3e8c31adbb8263e787417efd862c35bd" + }, + { + "path": "confuse/images/47.png", + "hash": "8813439d0c101eee39c62cea5ac7cc63" + }, + { + "path": "confuse/images/48.png", + "hash": "65b6c396ed0e069314d411a760c732d1" + }, + { + "path": "confuse/images/49.png", + "hash": "900a70773d6b7e2bed31a5dbe6e6e0eb" + }, + { + "path": "confuse/images/5.png", + "hash": "93251d796fc5ddaac7b7f11ad606cd93" + }, + { + "path": "confuse/images/50.png", + "hash": "0495631cee1906334eae549aa6cbdbbb" + }, + { + "path": "confuse/images/51.png", + "hash": "d41fbbcd92414fda29dd135c7e52dfd5" + }, + { + "path": "confuse/images/52.png", + "hash": "b28b841b05143a99b493a80a75285fe1" + }, + { + "path": "confuse/images/53.png", + "hash": "b1581543d5840dd8e0614a38eccc6480" + }, + { + "path": "confuse/images/54.png", + "hash": "dbcba662c117d08322de64ddd4fca0de" + }, + { + "path": "confuse/images/55.png", + "hash": "14e32fd9a2f9969e1c487dd7463300fc" + }, + { + "path": "confuse/images/56.png", + "hash": "9ba295cb92d8db271293d5a3ed4efbf9" + }, + { + "path": "confuse/images/57.png", + "hash": "bc7e5cbc5bbc0d2e819e26f86a4c0d15" + }, + { + "path": "confuse/images/58.png", + "hash": "55fe9b8439cf8db774e8edf1ba925220" + }, + { + "path": "confuse/images/59.png", + "hash": "cd2cbac3e51185bd1ab89f0e0f9663d4" + }, + { + "path": "confuse/images/6.png", + "hash": "912dba6537f249ab96329551eb79590e" + }, + { + "path": "confuse/images/60.png", + "hash": "565b13924f766b6d544dad432534e7f4" + }, + { + "path": "confuse/images/61.png", + "hash": "9456d0cf43bca9715dde029eb196852b" + }, + { + "path": "confuse/images/62.png", + "hash": "2c162ab10a9d01e0e8b0019f597cbcdf" + }, + { + "path": "confuse/images/63.png", + "hash": "81940aed3fda844dabe97f05a6636311" + }, + { + "path": "confuse/images/64.png", + "hash": "dbaa72fcd6f6677c569f73449f2a8790" + }, + { + "path": "confuse/images/65.png", + "hash": "b10625ffdd4f4bab57c5ed27b0a84cc9" + }, + { + "path": "confuse/images/66.png", + "hash": "d550962452fe6c383c62b973dc00aefc" + }, + { + "path": "confuse/images/67.png", + "hash": "2187819577b8acd5c170a8137c6eb2ce" + }, + { + "path": "confuse/images/68.png", + "hash": "68b37e77e5678b1382c755e683f600f1" + }, + { + "path": "confuse/images/69.png", + "hash": "7ee64ef710190844ae138beb7080c414" + }, + { + "path": "confuse/images/7.png", + "hash": "b529943d2a97ee24dc2af526d6afbf20" + }, + { + "path": "confuse/images/70.png", + "hash": "31ac37fdcd36c2cc8b444e9245843316" + }, + { + "path": "confuse/images/71.png", + "hash": "c009b05844a6f673baebb85d4ca810df" + }, + { + "path": "confuse/images/72.png", + "hash": "82a8077f34e7052d16ba32386a018534" + }, + { + "path": "confuse/images/73.png", + "hash": "9a8a39e0fb705f9b259e8323a533ff20" + }, + { + "path": "confuse/images/74.png", + "hash": "880a60d5d3e656116b0be4a37a1e56bb" + }, + { + "path": "confuse/images/75.png", + "hash": "589ba816911705a0b7d8f1f07833cfa4" + }, + { + "path": "confuse/images/76.png", + "hash": "b9bb3ce47bb157a8b1b9383df59c0a30" + }, + { + "path": "confuse/images/77.png", + "hash": "f40cca19853e6b29d10dbf6bc410c39e" + }, + { + "path": "confuse/images/78.png", + "hash": "e110e2ce944356056714fe26894ee509" + }, + { + "path": "confuse/images/79.png", + "hash": "6365cbb7ff7106e1c6334942ebebfa6e" + }, + { + "path": "confuse/images/8.png", + "hash": "8faa3341b8a1d86f60313a11215b5ace" + }, + { + "path": "confuse/images/80.png", + "hash": "9a47bd56b13059e349f6236c6dce605a" + }, + { + "path": "confuse/images/81.png", + "hash": "fd5b11c40f60da53da999cce18572552" + }, + { + "path": "confuse/images/82.png", + "hash": "7f6a76528a8a531d8d0fb88b42b1855c" + }, + { + "path": "confuse/images/83.png", + "hash": "d0c1125dbb10d75406c1d2cc88368766" + }, + { + "path": "confuse/images/84.png", + "hash": "cefb8ed958cb38f74db21ed4b390b175" + }, + { + "path": "confuse/images/85.png", + "hash": "508467fcf63c81e8202f6c24b0007032" + }, + { + "path": "confuse/images/86.png", + "hash": "831d56520f8378c8c8a525d8bf786c99" + }, + { + "path": "confuse/images/87.png", + "hash": "807e7b96ac60ffb65b9f28643d87c7ea" + }, + { + "path": "confuse/images/88.png", + "hash": "d1f27772cc238b348e82b5126bf6cbc9" + }, + { + "path": "confuse/images/89.png", + "hash": "fca1191b7a7dbc199f7cb9541c0cffc1" + }, + { + "path": "confuse/images/9.png", + "hash": "562ed906ec350b5f948f2dc5793cdb3f" + }, + { + "path": "confuse/images/90.png", + "hash": "647b0aca88439a8bc0c4fd5f56bb08db" + }, + { + "path": "confuse/images/91.png", + "hash": "6a9f58a786851e8d227255007716c9c3" + }, + { + "path": "confuse/images/92.png", + "hash": "d5bc05be5757a38b5c8ede86b3bbecd0" + }, + { + "path": "confuse/images/93.png", + "hash": "0aec630f9affb58fc244ebf867b6756f" + }, + { + "path": "confuse/images/94.png", + "hash": "7c8afaaacd83da90ef8ba815ae9c6adb" + }, + { + "path": "confuse/images/95.png", + "hash": "98ae73a5d37372b009d70abdcd80c016" + }, + { + "path": "confuse/images/96.png", + "hash": "4197a036a87ae517034548801297b6a4" + }, + { + "path": "confuse/images/97.png", + "hash": "cdc67752ea7f8d22eccf9070068ff91e" + }, + { + "path": "confuse/images/98.png", + "hash": "55dc9c90d7b95a6f6a8f45a10c0b5900" + }, + { + "path": "confuse/images/99.png", + "hash": "8342ce8812c3104f57871d590a2ca408" + }, + { + "path": "coupon/images/0.png", + "hash": "e7faf1dcc5a4ff24536b7e41a4faf733" + }, + { + "path": "cover_face/images/0.png", + "hash": "8e26cd9059e61526a04d87918f9744a7" + }, + { + "path": "crawl/images/01.jpg", + "hash": "cce6b6c568d5fab48dcae9278c46a989" + }, + { + "path": "crawl/images/02.jpg", + "hash": "7c3ee42c3ff9426fc8f4de5ab143d85c" + }, + { + "path": "crawl/images/03.jpg", + "hash": "2e76c43a6d3fdf6f545b03e78e9f9fbd" + }, + { + "path": "crawl/images/04.jpg", + "hash": "97fa4883794bf2897ed5c3bb77469efb" + }, + { + "path": "crawl/images/05.jpg", + "hash": "e43b84a3615f376de456dd3fc44ed7b9" + }, + { + "path": "crawl/images/06.jpg", + "hash": "325794814502c47c91342a01e99ae848" + }, + { + "path": "crawl/images/07.jpg", + "hash": "c4e44e1b0da434105ac7ec3e00a719ef" + }, + { + "path": "crawl/images/08.jpg", + "hash": "854f91dd72caf3570a270de49c6c3a3f" + }, + { + "path": "crawl/images/09.jpg", + "hash": "f2426f40201f0188986fbc581be93f7f" + }, + { + "path": "crawl/images/10.jpg", + "hash": "5e2ed1f0c9b2e1f22ca2996ca0c2c583" + }, + { + "path": "crawl/images/11.jpg", + "hash": "a319d42a631243b12ea5e23e395db053" + }, + { + "path": "crawl/images/12.jpg", + "hash": "b2673e18b7fb6dbb7a8a9b548a9fac67" + }, + { + "path": "crawl/images/13.jpg", + "hash": "c641beff5799a5448c8d599fd382a712" + }, + { + "path": "crawl/images/14.jpg", + "hash": "e8987c5f87145247abf7b71f68315c35" + }, + { + "path": "crawl/images/15.jpg", + "hash": "44333dd2980c8f0634ecb4b51df68883" + }, + { + "path": "crawl/images/16.jpg", + "hash": "c05fc5e54e081909959da84a79602c14" + }, + { + "path": "crawl/images/17.jpg", + "hash": "69072cff8bbf687c5933536df8d14fa2" + }, + { + "path": "crawl/images/18.jpg", + "hash": "5e29e097d7145108d22141c980d37549" + }, + { + "path": "crawl/images/19.jpg", + "hash": "2bf84016a2fc51beb1a3b51ca566129f" + }, + { + "path": "crawl/images/20.jpg", + "hash": "dc8f3e84d315c6e8fb0e81cf01fe9858" + }, + { + "path": "crawl/images/21.jpg", + "hash": "2088ed79ddaf4ba4e90c388151001ba0" + }, + { + "path": "crawl/images/22.jpg", + "hash": "8356a010b3739a423971aaed1c04b189" + }, + { + "path": "crawl/images/23.jpg", + "hash": "c31d7dab81d42449a67f64849c9b92e3" + }, + { + "path": "crawl/images/24.jpg", + "hash": "5c6e144005eee87ef930551887be1231" + }, + { + "path": "crawl/images/25.jpg", + "hash": "d31ab3575368eee91640a61690f96455" + }, + { + "path": "crawl/images/26.jpg", + "hash": "08d586461e3fcf195d33b3dedf4c975c" + }, + { + "path": "crawl/images/27.jpg", + "hash": "231134ba99805ff5df605b152df96b50" + }, + { + "path": "crawl/images/28.jpg", + "hash": "7b224d314e8c0f24909fb81cf4dd46ad" + }, + { + "path": "crawl/images/29.jpg", + "hash": "0438049dbe406a3f226601ee43ccbbc5" + }, + { + "path": "crawl/images/30.jpg", + "hash": "c53078e5fc9c8325e77b3cc8a90742e4" + }, + { + "path": "crawl/images/31.jpg", + "hash": "1180e67b8783a97bc9836f5452d8bc97" + }, + { + "path": "crawl/images/32.jpg", + "hash": "6beb7396a405776b059f2f2570c7ab88" + }, + { + "path": "crawl/images/33.jpg", + "hash": "13a8694e3309db4974941dbbfaa9efeb" + }, + { + "path": "crawl/images/34.jpg", + "hash": "718f66b3fb852638c6e558ac6439134d" + }, + { + "path": "crawl/images/35.jpg", + "hash": "2d1fd0826efdcf432fa4d31123cc29d4" + }, + { + "path": "crawl/images/36.jpg", + "hash": "5f919e2888ae358ebb0d276764677eb9" + }, + { + "path": "crawl/images/37.jpg", + "hash": "3d05ce7d26c64d31d42a1d41b42679b6" + }, + { + "path": "crawl/images/38.jpg", + "hash": "779884b7ea0ba7ea74678dd2b72ed724" + }, + { + "path": "crawl/images/39.jpg", + "hash": "6ca6621f9d24185849e2b04a3d2e48cc" + }, + { + "path": "crawl/images/40.jpg", + "hash": "79b7ade1d3b87e5d12bee2194b575a64" + }, + { + "path": "crawl/images/41.jpg", + "hash": "370414538e997f6ca8c71ceb765a334f" + }, + { + "path": "crawl/images/42.jpg", + "hash": "f8e0e017de609c1761bb480f2c556cfa" + }, + { + "path": "crawl/images/43.jpg", + "hash": "6a0ad1624348135b563d080ab957f194" + }, + { + "path": "crawl/images/44.jpg", + "hash": "cad8a91f4ad34e97c9df229134423d7e" + }, + { + "path": "crawl/images/45.jpg", + "hash": "20a16ca7ecc0c2515c3995658619324e" + }, + { + "path": "crawl/images/46.jpg", + "hash": "9db7359991714a5be0cd8a452d9ad10e" + }, + { + "path": "crawl/images/47.jpg", + "hash": "a759b94eca07f1a544a303aef20da203" + }, + { + "path": "crawl/images/48.jpg", + "hash": "3a3191d21f27ffb9671bcedf1538abcc" + }, + { + "path": "crawl/images/49.jpg", + "hash": "fc04ef2afa46d592658172dd8dc1cc94" + }, + { + "path": "crawl/images/50.jpg", + "hash": "3bdd6b0cd1571511ab68d3a7d65503c6" + }, + { + "path": "crawl/images/51.jpg", + "hash": "73dd26dcebfd8bdd3f8fd11c1bdecb93" + }, + { + "path": "crawl/images/52.jpg", + "hash": "6fecf8ebd4aecbd7edf001a7ad5bd6a8" + }, + { + "path": "crawl/images/53.jpg", + "hash": "6c728cb71eb019ef5dcf79ce276d7304" + }, + { + "path": "crawl/images/54.jpg", + "hash": "249ab6abbadd5775b4db345d4111a483" + }, + { + "path": "crawl/images/55.jpg", + "hash": "43a8723bab213740211b1704a45b3e7a" + }, + { + "path": "crawl/images/56.jpg", + "hash": "27d00835d39b95543421a1070fff1db9" + }, + { + "path": "crawl/images/57.jpg", + "hash": "e5276244ddf08c6b1ec4cd516b72b679" + }, + { + "path": "crawl/images/58.jpg", + "hash": "8bd116c9d35fd75d2dafd93f8fc41ba7" + }, + { + "path": "crawl/images/59.jpg", + "hash": "af9dba7059394fb29d6cde220b4a81bd" + }, + { + "path": "crawl/images/60.jpg", + "hash": "dc6f1aed35469d818a5baa2376b0d079" + }, + { + "path": "crawl/images/61.jpg", + "hash": "f31def3cd5162c00d30972897298cd46" + }, + { + "path": "crawl/images/62.jpg", + "hash": "515ba871787b905c32c2b9d6bfa80e45" + }, + { + "path": "crawl/images/63.jpg", + "hash": "39e88bc661516e766e9c8f3e67a121a6" + }, + { + "path": "crawl/images/64.jpg", + "hash": "f923d5b7efc1ae8779f1255aae1e2e6d" + }, + { + "path": "crawl/images/65.jpg", + "hash": "a93c9bf77017252a84d15f8eab26d4b2" + }, + { + "path": "crawl/images/66.jpg", + "hash": "acea84f25f75d60627c0ede058eed4b8" + }, + { + "path": "crawl/images/67.jpg", + "hash": "18d416a63e4963b4363ebd9289dd15fc" + }, + { + "path": "crawl/images/68.jpg", + "hash": "cb661d9cd0df77b2a8c5535287224f7f" + }, + { + "path": "crawl/images/69.jpg", + "hash": "8137e5b1174969fd131ea04789fca972" + }, + { + "path": "crawl/images/70.jpg", + "hash": "380c0b89c5baf7dbd0ffef38446a87e8" + }, + { + "path": "crawl/images/71.jpg", + "hash": "60428aa26541efa2250d37d74a874ba6" + }, + { + "path": "crawl/images/72.jpg", + "hash": "bee413357320f096a9853804edd5aeb2" + }, + { + "path": "crawl/images/73.jpg", + "hash": "0fc974698f0be98e9c9920e521f73ce6" + }, + { + "path": "crawl/images/74.jpg", + "hash": "5185af743a9e233a7bee56aa0f7d6a1b" + }, + { + "path": "crawl/images/75.jpg", + "hash": "53ad0b0dce7ff8d90b66336108123d75" + }, + { + "path": "crawl/images/76.jpg", + "hash": "cee00e8f49851ef8a002180100aff3b7" + }, + { + "path": "crawl/images/77.jpg", + "hash": "3b1821d3e01777cda96d1a750f0a5570" + }, + { + "path": "crawl/images/78.jpg", + "hash": "be506707b4730cb4e94480231b46a617" + }, + { + "path": "crawl/images/79.jpg", + "hash": "9ae5ed97053b61541afe79b1ccb11fb5" + }, + { + "path": "crawl/images/80.jpg", + "hash": "f890db4dab16bd14f844d20c9a506245" + }, + { + "path": "crawl/images/81.jpg", + "hash": "f768dcefda04786bbc1be87f1da99d26" + }, + { + "path": "crawl/images/82.jpg", + "hash": "1060692fef8cd0865ec65b71ab4b3598" + }, + { + "path": "crawl/images/83.jpg", + "hash": "6c0e706db231044a2ee659a9003453e2" + }, + { + "path": "crawl/images/84.jpg", + "hash": "b30571c5ec880a98048004a859cf90cf" + }, + { + "path": "crawl/images/85.jpg", + "hash": "9245e2da88ebc134b7e2292068a80cfb" + }, + { + "path": "crawl/images/86.jpg", + "hash": "ca01df832eae89b1501c05373cd67b3d" + }, + { + "path": "crawl/images/87.jpg", + "hash": "40c6e2bcdb0292a52df7215844acfed0" + }, + { + "path": "crawl/images/88.jpg", + "hash": "e1ae322f432c9aa97e3735eb917558d1" + }, + { + "path": "crawl/images/89.jpg", + "hash": "5954888d330068ae2cb700b191223e93" + }, + { + "path": "crawl/images/90.jpg", + "hash": "0cdbaec24ba2753083cb57c4ea94fed6" + }, + { + "path": "crawl/images/91.jpg", + "hash": "f15ef87ab6a4e79e0b9fca132a27d060" + }, + { + "path": "crawl/images/92.jpg", + "hash": "b50d7c226a506e9e91bce322219aa131" + }, + { + "path": "decent_kiss/images/0.png", + "hash": "c7bf01f3d8177d267d9dfd0a61b67007" + }, + { + "path": "dinosaur/images/0.png", + "hash": "dc7945042837325b39b70119ae140d66" + }, + { + "path": "distracted/images/0.png", + "hash": "e43d4c7cd948c5784509e6b90f1588d2" + }, + { + "path": "distracted/images/1.png", + "hash": "795ebc988f75b5cda340dce94cb3f6d7" + }, + { + "path": "distracted/images/2.png", + "hash": "cc15e121923c705a0610099217ee3006" + }, + { + "path": "distracted/images/3.png", + "hash": "fd7036c95a7c1270bcf2d7130fdfc0d7" + }, + { + "path": "divorce/images/0.png", + "hash": "2b142fc47a2cc1cc723a4edb33f7efcf" + }, + { + "path": "dog_of_vtb/images/0.png", + "hash": "19a16294341eb540a5f48f6972937fc3" + }, + { + "path": "dont_go_near/images/0.png", + "hash": "eb240a7e56142a9e5233f675430b7481" + }, + { + "path": "dont_touch/images/0.png", + "hash": "3ff591ee749dfc208e2c5bf617c47dbe" + }, + { + "path": "dont_touch/images/mask.png", + "hash": "5b3ffca1b7a588a02d2dc4939cd6d31f" + }, + { + "path": "eat/images/0.png", + "hash": "114a3680c42d8818ff645aad63fa5af6" + }, + { + "path": "eat/images/1.png", + "hash": "1d46ae1a4d0080b62554783bfcdbb177" + }, + { + "path": "eat/images/2.png", + "hash": "3248f754830433778ae06df6ba2735d8" + }, + { + "path": "fanatic/images/0.jpg", + "hash": "65803c47845cd55e16cc3b5155d14430" + }, + { + "path": "fencing/images/0.png", + "hash": "494d6744b2fe70a2a24fa5680eab62e7" + }, + { + "path": "fencing/images/1.png", + "hash": "c2352fea73085e5e30465bdbd14e967f" + }, + { + "path": "fencing/images/10.png", + "hash": "bb11fc489bf9c9a5d6d7b6216cf2add7" + }, + { + "path": "fencing/images/11.png", + "hash": "bd2f472a815021ffe09a038256fc3ed6" + }, + { + "path": "fencing/images/12.png", + "hash": "c38fc68dbffa8f811dde76d05e158000" + }, + { + "path": "fencing/images/13.png", + "hash": "05c025367c38dcb56cbddc25dcee5c2c" + }, + { + "path": "fencing/images/14.png", + "hash": "e630633af5f3554a665c7acc700d8062" + }, + { + "path": "fencing/images/15.png", + "hash": "1a992964b9dc0730e3b8e5f6fa8e33c6" + }, + { + "path": "fencing/images/16.png", + "hash": "b2f3d5aae539fb2be6bc93d70361d93c" + }, + { + "path": "fencing/images/17.png", + "hash": "b6305e03a78495edba1787a2fe33cad0" + }, + { + "path": "fencing/images/18.png", + "hash": "0cd2f141d092fe8c2bc703b222275ac0" + }, + { + "path": "fencing/images/2.png", + "hash": "8ef9f2964d620faf379ac3f9556d1ab1" + }, + { + "path": "fencing/images/3.png", + "hash": "b0353f65f1f5c6f0dff890e314fbf866" + }, + { + "path": "fencing/images/4.png", + "hash": "f7f92325df415688edd85295e8ffbf1b" + }, + { + "path": "fencing/images/5.png", + "hash": "d13f10788d9b40353a3e0ea69fe646a5" + }, + { + "path": "fencing/images/6.png", + "hash": "99691ce248fdb3f8ec6365185c88b944" + }, + { + "path": "fencing/images/7.png", + "hash": "8eefb0b92e25340030721db422658c0c" + }, + { + "path": "fencing/images/8.png", + "hash": "8d4c631bc83659ffa3ede9dbf604f65c" + }, + { + "path": "fencing/images/9.png", + "hash": "81f17c9e3396f1d236efdb4d7a45c92f" + }, + { + "path": "fill_head/images/0.jpg", + "hash": "343d9e2f99061c99b8a2cca8a8074c93" + }, + { + "path": "find_chips/images/0.jpg", + "hash": "a8b86967b35a34129dd70a6c81a565e4" + }, + { + "path": "garbage/images/0.png", + "hash": "15a74fc27fc5ad1b2c4f3145aea81be4" + }, + { + "path": "garbage/images/1.png", + "hash": "13ed796e6e5a0f098d385ff5aa9360e2" + }, + { + "path": "garbage/images/10.png", + "hash": "018e992e8409366305d368b0b60d77e8" + }, + { + "path": "garbage/images/11.png", + "hash": "f6ae17fafdfc956645fb3f04deb5dc75" + }, + { + "path": "garbage/images/12.png", + "hash": "db2999727594e3186602b674993955b7" + }, + { + "path": "garbage/images/13.png", + "hash": "8186573633635047b38a8251494e207a" + }, + { + "path": "garbage/images/14.png", + "hash": "0374fac3fc52836b22226b33e10e4651" + }, + { + "path": "garbage/images/15.png", + "hash": "33ca5a42d75a9fc836082f31f059cb07" + }, + { + "path": "garbage/images/16.png", + "hash": "e044d9ad65657d0fadddefa91fe9d9f6" + }, + { + "path": "garbage/images/17.png", + "hash": "a4bcdd0bd939287881e3f6ab0ec5ee9e" + }, + { + "path": "garbage/images/18.png", + "hash": "41a300f7ed6ac9c5258c79ae3b655c45" + }, + { + "path": "garbage/images/19.png", + "hash": "a9adcc4884bf0c54f6acc1c268e89259" + }, + { + "path": "garbage/images/2.png", + "hash": "76adaddd8f25c4ff87af41652fae8426" + }, + { + "path": "garbage/images/20.png", + "hash": "f702cab4e4fb5a12e5c927e081357b01" + }, + { + "path": "garbage/images/21.png", + "hash": "4a8d8ff86119d8765855cf7a4d6ddfb0" + }, + { + "path": "garbage/images/22.png", + "hash": "54a5c78b51335d5cca607eb61114c684" + }, + { + "path": "garbage/images/23.png", + "hash": "712228f18c3fd891779cc53637e51ecb" + }, + { + "path": "garbage/images/24.png", + "hash": "3146fd2df0be2e38b8971c58c51b6cdd" + }, + { + "path": "garbage/images/3.png", + "hash": "9b9b4f0dfc7de061341aa423bacc91f4" + }, + { + "path": "garbage/images/4.png", + "hash": "ed4545859014cbd5f88ff3d8d464406c" + }, + { + "path": "garbage/images/5.png", + "hash": "da3d51b91a7e166c4e6b1a71396dbad3" + }, + { + "path": "garbage/images/6.png", + "hash": "f0ef28426cfd2a62633da02c3e3867c3" + }, + { + "path": "garbage/images/7.png", + "hash": "0a36874457b495812f8a708dc2af5eba" + }, + { + "path": "garbage/images/8.png", + "hash": "6f7f69ba43029a2053bd12fabdcd7a43" + }, + { + "path": "garbage/images/9.png", + "hash": "6749dad6bd2ade275a157a205ef6b860" + }, + { + "path": "genshin_start/images/0.png", + "hash": "7b5b9156e3582217ac185d1b96059c80" + }, + { + "path": "gif_subtitle/images/chanshenzi.gif", + "hash": "1d691ef44c9015b90118d462d8254bcf" + }, + { + "path": "gif_subtitle/images/maikease.gif", + "hash": "6eb2aa718212b23329a43e4bfd70998c" + }, + { + "path": "gif_subtitle/images/nihaosaoa.gif", + "hash": "97d8904891a6c890778b7ff6c467f438" + }, + { + "path": "gif_subtitle/images/qiegewala.gif", + "hash": "286153b55f83fc13f4eb6c7d48252541" + }, + { + "path": "gif_subtitle/images/shishilani.gif", + "hash": "64a1c8167bfd0cb46898bba32225a49b" + }, + { + "path": "gif_subtitle/images/shuifandui.gif", + "hash": "3125a5b21d77fbe5176068f82a82f344" + }, + { + "path": "gif_subtitle/images/wangjingze.gif", + "hash": "96bc6eefc1ec523746ba3ddccfe79833" + }, + { + "path": "gif_subtitle/images/weisuoyuwei.gif", + "hash": "c835e2828b6c050e594592c4f44923e9" + }, + { + "path": "gif_subtitle/images/wunian.gif", + "hash": "ef27895e6f030f6a308d60189aba662d" + }, + { + "path": "gif_subtitle/images/yalidaye.gif", + "hash": "ce1a77bc761384d08324bc94a495d74f" + }, + { + "path": "gif_subtitle/images/zengxiaoxian.gif", + "hash": "aa3dde683bea08ee6fe5fee1d34ae8b1" + }, + { + "path": "good_news/images/0.jpg", + "hash": "bb1631780bd36ea5e2338611d2887800" + }, + { + "path": "gun/images/0.png", + "hash": "23e223aba69b4c42cd04dcae58aec254" + }, + { + "path": "hammer/images/0.png", + "hash": "b8e94050256fc70c57f7167a8bbe3842" + }, + { + "path": "hammer/images/1.png", + "hash": "e87bdd2e1ab714965594486ec7e59273" + }, + { + "path": "hammer/images/2.png", + "hash": "3db04d878be5d2bcc6385419464f8cbe" + }, + { + "path": "hammer/images/3.png", + "hash": "5926fc010853908f2fde98149b1cde26" + }, + { + "path": "hammer/images/4.png", + "hash": "9b10b4ee1c6980260d48489d2edb4b0d" + }, + { + "path": "hammer/images/5.png", + "hash": "4f183ced8935e58b63e65330bce6479a" + }, + { + "path": "hammer/images/6.png", + "hash": "fa99a2c0fd32fc12b53c29b34ecfdc5b" + }, + { + "path": "high_EQ/images/0.jpg", + "hash": "e82b03fd3c5e4b8671c76047c9bcc1ad" + }, + { + "path": "hit_screen/images/0.png", + "hash": "1b77c941f4e0be97f9209b1697af78a3" + }, + { + "path": "hit_screen/images/1.png", + "hash": "2f72e6b3a7ef135bab0d1791740e50ad" + }, + { + "path": "hit_screen/images/10.png", + "hash": "06ee8c86ee5a4fcf1c64f05178d7a9f1" + }, + { + "path": "hit_screen/images/11.png", + "hash": "d9a620a32c4d83ef5124acc4c8f44c0c" + }, + { + "path": "hit_screen/images/12.png", + "hash": "906e33a3efcd9939c8d101d7907ee8d6" + }, + { + "path": "hit_screen/images/13.png", + "hash": "1ab5a1f4460db339b769265eb611a984" + }, + { + "path": "hit_screen/images/14.png", + "hash": "2ab2826e31abfa85e9bed6bd9a29a0fb" + }, + { + "path": "hit_screen/images/15.png", + "hash": "fcf7244d01c15e81f1a10509a57f7421" + }, + { + "path": "hit_screen/images/16.png", + "hash": "f31617015d582e6fb0dc705f3ce9dda1" + }, + { + "path": "hit_screen/images/17.png", + "hash": "595266cf81b8d831defc869588f13866" + }, + { + "path": "hit_screen/images/18.png", + "hash": "edf5600790fb42cde74fc0e08f018d0b" + }, + { + "path": "hit_screen/images/19.png", + "hash": "e69e9768da626a01dda6d5a3d86160c6" + }, + { + "path": "hit_screen/images/2.png", + "hash": "52d6b3e7409df8b840f2b1b1ec096be7" + }, + { + "path": "hit_screen/images/20.png", + "hash": "8079c0064342788af30cd274a52dfa89" + }, + { + "path": "hit_screen/images/21.png", + "hash": "e7fb41be10bef52e718aa4022995732b" + }, + { + "path": "hit_screen/images/22.png", + "hash": "61fd0c02dbc2fee0fb6e47fee48b47bc" + }, + { + "path": "hit_screen/images/23.png", + "hash": "3bcec874887b5f1b730622cb22834cc9" + }, + { + "path": "hit_screen/images/24.png", + "hash": "4660c21bb748574a9cc1d4b25536814c" + }, + { + "path": "hit_screen/images/25.png", + "hash": "f8e28991d35af936974315a1035bcccf" + }, + { + "path": "hit_screen/images/26.png", + "hash": "2561fbdf6d86e0296b2954c28015bf24" + }, + { + "path": "hit_screen/images/27.png", + "hash": "746c182316953d7ab216f0aa960224ef" + }, + { + "path": "hit_screen/images/28.png", + "hash": "0c6dce8b6f078612bfcac807bec83779" + }, + { + "path": "hit_screen/images/3.png", + "hash": "15e94688ebc0649564df9344e900e0ad" + }, + { + "path": "hit_screen/images/4.png", + "hash": "f1dbc36604462cf90604f3143938ab0b" + }, + { + "path": "hit_screen/images/5.png", + "hash": "44dd57fa5d23ff5614a6d98656b31095" + }, + { + "path": "hit_screen/images/6.png", + "hash": "8c66d2df6ce945ef239d3b18a4774b3b" + }, + { + "path": "hit_screen/images/7.png", + "hash": "6e42ce15f6a148273e926e085307a3cc" + }, + { + "path": "hit_screen/images/8.png", + "hash": "e313e4047ff2973817e1a47064a32988" + }, + { + "path": "hit_screen/images/9.png", + "hash": "a6dd680de2644d2fba6dfeeff2c90d1b" + }, + { + "path": "hold_grudge/images/0.png", + "hash": "985187c5c54b553b0af9781bfa7a5514" + }, + { + "path": "hold_tight/images/0.png", + "hash": "6c52f7de5f1a4aca8f1a0e7c5f6dd89b" + }, + { + "path": "hug_leg/images/0.png", + "hash": "bc08d4fd4fa124712006354df0eb092d" + }, + { + "path": "hug_leg/images/1.png", + "hash": "31bd381c73cccd175f1c71497a752870" + }, + { + "path": "hug_leg/images/2.png", + "hash": "49214e16f3150aea1cbc4849576516cd" + }, + { + "path": "hug_leg/images/3.png", + "hash": "83f6f33ead609c270873287ee65b89ca" + }, + { + "path": "hug_leg/images/4.png", + "hash": "74bf0373ac3b8057326722a0cf8fc51c" + }, + { + "path": "hug_leg/images/5.png", + "hash": "3d12cc8ad857617351d2961790e031af" + }, + { + "path": "hutao_bite/images/0.png", + "hash": "bbd32543d177575053100e7b23ac1b27" + }, + { + "path": "hutao_bite/images/1.png", + "hash": "9b108ddc8654bda9c3a3922308be2e07" + }, + { + "path": "imprison/images/0.jpg", + "hash": "7afdc93efbe34a2a9c7e7fc60b8b57a4" + }, + { + "path": "incivilization/images/0.png", + "hash": "352c9534c4ff182174b0b29e7ff7df42" + }, + { + "path": "interview/images/huaji.png", + "hash": "3bb5ff24a83544995edab765c837bec3" + }, + { + "path": "interview/images/microphone.png", + "hash": "34775a82e4b8b7df44d2cf37a6e16ea5" + }, + { + "path": "jiji_king/images/0.png", + "hash": "346ae46e7697540a3bec08c30ad78969" + }, + { + "path": "jiujiu/images/0.png", + "hash": "781d5ebfc9f2309c4cf60ef7b93ecfb8" + }, + { + "path": "jiujiu/images/1.png", + "hash": "c01805a80c726fbc6f9bab364b82e867" + }, + { + "path": "jiujiu/images/2.png", + "hash": "a1bdbc3c5d4830ca6baf459d686e38e3" + }, + { + "path": "jiujiu/images/3.png", + "hash": "b972b1ea12948e76735bed4528dcab46" + }, + { + "path": "jiujiu/images/4.png", + "hash": "30caae291977d2a3121c0a15a3832683" + }, + { + "path": "jiujiu/images/5.png", + "hash": "281b8b7796f579ac907c5923bf8a368b" + }, + { + "path": "jiujiu/images/6.png", + "hash": "eddf5fe3cc1e7470c17e04c8db35168f" + }, + { + "path": "jiujiu/images/7.png", + "hash": "82f7556d03c3e847ec3dd3f1d0915d9f" + }, + { + "path": "karyl_point/images/0.png", + "hash": "9ab82387b8f3cce1acdb57499c49ebe7" + }, + { + "path": "kick_ball/images/0.png", + "hash": "d2b2916d37d2cccd5c9f12ea99da87a7" + }, + { + "path": "kick_ball/images/1.png", + "hash": "509da70e46d631c3353ad525431b7674" + }, + { + "path": "kick_ball/images/10.png", + "hash": "9535ff5344dbf99f1afbf27f0282b2d1" + }, + { + "path": "kick_ball/images/11.png", + "hash": "4af18499194c6436eca8ff0a24944643" + }, + { + "path": "kick_ball/images/12.png", + "hash": "95897fc9e72b3f9ada4cf3ae673cbcdc" + }, + { + "path": "kick_ball/images/13.png", + "hash": "0dd603137fc1de3b18e4c0fd65eda6bb" + }, + { + "path": "kick_ball/images/14.png", + "hash": "fd8164a80326bdd5eee1291767b05ed3" + }, + { + "path": "kick_ball/images/2.png", + "hash": "bd6d20fd03612ba751059157b1966fab" + }, + { + "path": "kick_ball/images/3.png", + "hash": "94990605389da96c2f8168b4c2561562" + }, + { + "path": "kick_ball/images/4.png", + "hash": "6b0071cb715b8bc853ba32cde58a0e73" + }, + { + "path": "kick_ball/images/5.png", + "hash": "80e2a119658a86b1d97a26c2b41d8481" + }, + { + "path": "kick_ball/images/6.png", + "hash": "f573f27a1d7ff5117f2855d34bd225a1" + }, + { + "path": "kick_ball/images/7.png", + "hash": "76d027ec89c7de2b0abeabe84497cd00" + }, + { + "path": "kick_ball/images/8.png", + "hash": "3865dacbe636cb2c56967b1d873752c3" + }, + { + "path": "kick_ball/images/9.png", + "hash": "ca06142edeeafb64a59cd83985d4afd5" + }, + { + "path": "kirby_hammer/images/0.png", + "hash": "4b3d751215727547c9d596370c0cadc6" + }, + { + "path": "kirby_hammer/images/1.png", + "hash": "3e9b3db1325109cd857498cf764ed8b3" + }, + { + "path": "kirby_hammer/images/10.png", + "hash": "252c83ec6328ca49a46bf2cf88481123" + }, + { + "path": "kirby_hammer/images/11.png", + "hash": "410b76d943885f08adbf10f7f054e75f" + }, + { + "path": "kirby_hammer/images/12.png", + "hash": "9ae6ca270a810714a8d7c9dd4099593a" + }, + { + "path": "kirby_hammer/images/13.png", + "hash": "ec99ad3043c7fd3ff77530ee38172eaa" + }, + { + "path": "kirby_hammer/images/14.png", + "hash": "096fe608b0490ed4fcb3f35bd0f74baa" + }, + { + "path": "kirby_hammer/images/15.png", + "hash": "d117358a362f2ddff2d4c22ed88c981a" + }, + { + "path": "kirby_hammer/images/16.png", + "hash": "d80a8e7ab1c123c5ece132d9770de968" + }, + { + "path": "kirby_hammer/images/17.png", + "hash": "9e4d604e25a2da6b99d1840fe7c75602" + }, + { + "path": "kirby_hammer/images/18.png", + "hash": "067baa4f017f15f90d36d15e89f9527f" + }, + { + "path": "kirby_hammer/images/19.png", + "hash": "3907359c33ea79883f36a5aaf2cffc81" + }, + { + "path": "kirby_hammer/images/2.png", + "hash": "2b9c78da812ace9ab756b3ac31bcc2c3" + }, + { + "path": "kirby_hammer/images/20.png", + "hash": "f72fb17d8f0187ab153021b44dd214d9" + }, + { + "path": "kirby_hammer/images/21.png", + "hash": "f2c1b689c31792c862208e0027f5eb3d" + }, + { + "path": "kirby_hammer/images/22.png", + "hash": "d2a986955f38ae165ab7ed5e9eff2c22" + }, + { + "path": "kirby_hammer/images/23.png", + "hash": "9e90dba73e5a018ca0fd222a004b1f5e" + }, + { + "path": "kirby_hammer/images/24.png", + "hash": "94a54a4bb46aef8764dc90b4a914c68e" + }, + { + "path": "kirby_hammer/images/25.png", + "hash": "54bd5065691abd93f8f67e6d4d82654b" + }, + { + "path": "kirby_hammer/images/26.png", + "hash": "984150c2897e2989bd89a4739ee17628" + }, + { + "path": "kirby_hammer/images/27.png", + "hash": "acf235c91c7ae0877531dd5eb1d05e9c" + }, + { + "path": "kirby_hammer/images/28.png", + "hash": "bb8829238a3bbdc892999a9bc3cc0035" + }, + { + "path": "kirby_hammer/images/29.png", + "hash": "de595b27464eac39b387f139daf0240c" + }, + { + "path": "kirby_hammer/images/3.png", + "hash": "f2298d7403dc43180713d7d31de47455" + }, + { + "path": "kirby_hammer/images/30.png", + "hash": "ed48b5f532f17d4419d2eac91787a6e2" + }, + { + "path": "kirby_hammer/images/31.png", + "hash": "e322d5ae9489235c164239ea0c80ea68" + }, + { + "path": "kirby_hammer/images/32.png", + "hash": "cca6150a0e930ac96985d7a33daa47ac" + }, + { + "path": "kirby_hammer/images/33.png", + "hash": "efb182d89025cb064a6d5ab7682975b5" + }, + { + "path": "kirby_hammer/images/34.png", + "hash": "99ca9df71e8a370f192374a057406d37" + }, + { + "path": "kirby_hammer/images/35.png", + "hash": "fdde0008b84eabbcff46a0e59c057105" + }, + { + "path": "kirby_hammer/images/36.png", + "hash": "45b4f1f245216b8670067b9df8df1ea6" + }, + { + "path": "kirby_hammer/images/37.png", + "hash": "206a72ca3b28cc1a50d4414364190bdc" + }, + { + "path": "kirby_hammer/images/38.png", + "hash": "9c5f7a67a182d73e0bc4a3a51b676938" + }, + { + "path": "kirby_hammer/images/39.png", + "hash": "c8eedcaacd133ac50373d45b56f007a7" + }, + { + "path": "kirby_hammer/images/4.png", + "hash": "9b7ab8b7137898c3a3ef680f27c5dac4" + }, + { + "path": "kirby_hammer/images/40.png", + "hash": "080e309ebc305fcd4ee2aabfaf40e6e0" + }, + { + "path": "kirby_hammer/images/41.png", + "hash": "fc6622b31b57c66e5b008d63e6409a94" + }, + { + "path": "kirby_hammer/images/42.png", + "hash": "1d003a0c7b75832ec11849d0192cabd3" + }, + { + "path": "kirby_hammer/images/43.png", + "hash": "fc56793262bf080fe9b8cef629a3acc2" + }, + { + "path": "kirby_hammer/images/44.png", + "hash": "cd3cc7029bffd42fe7119c687be44f2e" + }, + { + "path": "kirby_hammer/images/45.png", + "hash": "96419814c91e3082d1850793b5978f3f" + }, + { + "path": "kirby_hammer/images/46.png", + "hash": "f06d71ebed8e4410fe9139de30612e3e" + }, + { + "path": "kirby_hammer/images/47.png", + "hash": "ac9b4ae0523f7cc80f6ec8545436888c" + }, + { + "path": "kirby_hammer/images/48.png", + "hash": "3592a8851467f316e4b4186a85bbce27" + }, + { + "path": "kirby_hammer/images/49.png", + "hash": "9e5bc3797377a5f4f9385e8c9d55b8d6" + }, + { + "path": "kirby_hammer/images/5.png", + "hash": "3a5ec940f94e19c03879b3b2c21d9959" + }, + { + "path": "kirby_hammer/images/50.png", + "hash": "b0d440755b85428fc2d00537ab79caec" + }, + { + "path": "kirby_hammer/images/51.png", + "hash": "65757c46c224edd93ade8aadf0bfc3c0" + }, + { + "path": "kirby_hammer/images/52.png", + "hash": "c12fe7a7d87912155c9ded3b999752fb" + }, + { + "path": "kirby_hammer/images/53.png", + "hash": "5046fbe65e726be58b1ef243b7111a7d" + }, + { + "path": "kirby_hammer/images/54.png", + "hash": "3500fda8e30e5818cf16576e11be38d8" + }, + { + "path": "kirby_hammer/images/55.png", + "hash": "dec898961403796b4dff81f2af93767f" + }, + { + "path": "kirby_hammer/images/56.png", + "hash": "da4a03072d3f3ac6b2ef6bd0c129ff4c" + }, + { + "path": "kirby_hammer/images/57.png", + "hash": "054c7b45c34819d7c6f086da4b27cf3a" + }, + { + "path": "kirby_hammer/images/58.png", + "hash": "7f2f3d88c96d8f21f47087db40a3156a" + }, + { + "path": "kirby_hammer/images/59.png", + "hash": "4db1b3f61044fc795e6e859da2030321" + }, + { + "path": "kirby_hammer/images/6.png", + "hash": "98602b15bcd486f5d334910791a11ff5" + }, + { + "path": "kirby_hammer/images/60.png", + "hash": "e3e2b230e05b168aeb0c278ce6dea4ed" + }, + { + "path": "kirby_hammer/images/61.png", + "hash": "89e1a31ef24f68d5d8d7cdf5c0e245c5" + }, + { + "path": "kirby_hammer/images/7.png", + "hash": "85f414a42d7c8a991e55ef227ce44c22" + }, + { + "path": "kirby_hammer/images/8.png", + "hash": "3e5886543cdee36c21830fb681568ac1" + }, + { + "path": "kirby_hammer/images/9.png", + "hash": "83a12e09e4c29d23c634c0f11f1823b4" + }, + { + "path": "kiss/images/0.png", + "hash": "49c396692947a52148bcef0ea47f0491" + }, + { + "path": "kiss/images/1.png", + "hash": "f3587ef479ea9243d6370f7a6b30b68b" + }, + { + "path": "kiss/images/10.png", + "hash": "d396dfc1857882f82a5294a3af1d638b" + }, + { + "path": "kiss/images/11.png", + "hash": "84c8d3845fca2efe97cb5f6beb113376" + }, + { + "path": "kiss/images/12.png", + "hash": "9d50e3777af1b60b58fe30009ce29c24" + }, + { + "path": "kiss/images/2.png", + "hash": "97fd783dbe88877952a092199671d514" + }, + { + "path": "kiss/images/3.png", + "hash": "93c7d6c3ca56be70f10eabe92a828120" + }, + { + "path": "kiss/images/4.png", + "hash": "a6079d128ba4c0fb0f332823a36b780b" + }, + { + "path": "kiss/images/5.png", + "hash": "686562b06f7834f6328a2ece1931d85d" + }, + { + "path": "kiss/images/6.png", + "hash": "5d53fa7523b11b2c61450c0b57c5826f" + }, + { + "path": "kiss/images/7.png", + "hash": "e690eac3a3ba1c32f235a64de9f10263" + }, + { + "path": "kiss/images/8.png", + "hash": "2226d16df1e6f42535bb8814919b6e7c" + }, + { + "path": "kiss/images/9.png", + "hash": "2ddacd69e518cdc3b63dc682bffe4d1e" + }, + { + "path": "klee_eat/images/0.png", + "hash": "bd284652faeb1d17a9fe9a96a7eed219" + }, + { + "path": "klee_eat/images/1.png", + "hash": "434dd145519e680ea5a7efd6c46fc181" + }, + { + "path": "klee_eat/images/10.png", + "hash": "57f83d884ef3f5da1b7036e97a77ca31" + }, + { + "path": "klee_eat/images/11.png", + "hash": "8a5807ac9c701f1fe6d8f4acac72db36" + }, + { + "path": "klee_eat/images/12.png", + "hash": "2474538aae3536aa75f00bf49eba6587" + }, + { + "path": "klee_eat/images/13.png", + "hash": "c24d9702f74c06b1e14473542ca0ecea" + }, + { + "path": "klee_eat/images/14.png", + "hash": "ce03d5f6d09e6aba982272187701756e" + }, + { + "path": "klee_eat/images/15.png", + "hash": "30f24c72d721677136805ca01cfdc50e" + }, + { + "path": "klee_eat/images/16.png", + "hash": "36f15c4c9460e7d6b2d41a03ebfa7718" + }, + { + "path": "klee_eat/images/17.png", + "hash": "70023d2f2d0a6018b839a29c70f8710f" + }, + { + "path": "klee_eat/images/18.png", + "hash": "5206043658761fa5ec7479c518a8e369" + }, + { + "path": "klee_eat/images/19.png", + "hash": "c1a2d1db2ffafc850d2d859884437dae" + }, + { + "path": "klee_eat/images/2.png", + "hash": "a187d0bfec4b78787077b8780d91e7b0" + }, + { + "path": "klee_eat/images/20.png", + "hash": "3ba814841c09721cd37e30b8ef9683a5" + }, + { + "path": "klee_eat/images/21.png", + "hash": "578e23bba8cb885c377833aa14562f9d" + }, + { + "path": "klee_eat/images/22.png", + "hash": "8495aa8a2792ac60f6359553c29c1567" + }, + { + "path": "klee_eat/images/23.png", + "hash": "1746365398e99a42860a0a8154a63541" + }, + { + "path": "klee_eat/images/24.png", + "hash": "a594a3457c8c562e5891e721a692c324" + }, + { + "path": "klee_eat/images/25.png", + "hash": "92bbc0ca794e36e10b5e1049884df9ce" + }, + { + "path": "klee_eat/images/26.png", + "hash": "85a9bc64d2a974cb718b9c08c1f84a34" + }, + { + "path": "klee_eat/images/27.png", + "hash": "bd29cf8dd5ba31d77e69904d6456d1c9" + }, + { + "path": "klee_eat/images/28.png", + "hash": "1ff5c07022df2f08355b337209d5464e" + }, + { + "path": "klee_eat/images/29.png", + "hash": "97f45cae4dd2daa2a4d3f00ca561eb58" + }, + { + "path": "klee_eat/images/3.png", + "hash": "dcc1eca20924fcd31012dc38bc2b6429" + }, + { + "path": "klee_eat/images/30.png", + "hash": "8ac8286949b0c765fb8312fee62363a6" + }, + { + "path": "klee_eat/images/4.png", + "hash": "fe507dbb5c909e28c2919d26210225a4" + }, + { + "path": "klee_eat/images/5.png", + "hash": "4b65a362e8465945c7d439c64386abc5" + }, + { + "path": "klee_eat/images/6.png", + "hash": "76244ceeded4c6ea3ffb226cf717be3f" + }, + { + "path": "klee_eat/images/7.png", + "hash": "0e7ce4c9b664d253ec57c1eb579158ef" + }, + { + "path": "klee_eat/images/8.png", + "hash": "ead69b36f949b45cc4c7da801405ae38" + }, + { + "path": "klee_eat/images/9.png", + "hash": "6bc9f15771d52cf1c1bb09e09b9b76a7" + }, + { + "path": "knock/images/0.png", + "hash": "c4e73350a09ac862d8109995436734f4" + }, + { + "path": "knock/images/1.png", + "hash": "d241d1aee02c46b4b9947eb6685822f0" + }, + { + "path": "knock/images/2.png", + "hash": "cc7023dd828227ca6af658639b10ce7e" + }, + { + "path": "knock/images/3.png", + "hash": "06b3459243c79f4c499d3c52929a4b2c" + }, + { + "path": "knock/images/4.png", + "hash": "992619f101424f96cc972b537532d7ce" + }, + { + "path": "knock/images/5.png", + "hash": "9dde5fd9fe478412f51b26344114f33d" + }, + { + "path": "knock/images/6.png", + "hash": "14823b403ee6ade595c4b9c326bf72be" + }, + { + "path": "knock/images/7.png", + "hash": "fa158f632f04f81c4eeeb4766222df07" + }, + { + "path": "learn/images/0.png", + "hash": "548f528b25cfab4e79c36f901227cd59" + }, + { + "path": "lim_x_0/images/0.png", + "hash": "c2f71b246240a101195ab7bc653fc46f" + }, + { + "path": "listen_music/images/0.png", + "hash": "63252e976b3d821f982d8886bd2900c8" + }, + { + "path": "loading/images/icon.png", + "hash": "e85c326ab5c4ca55589761be7b7e990d" + }, + { + "path": "look_this_icon/images/nmsl.png", + "hash": "4f37b38d2a14cbe368b4e427c23aa28d" + }, + { + "path": "love_you/images/0.png", + "hash": "66ddcca5601fd9224a1e0d17909ade56" + }, + { + "path": "love_you/images/1.png", + "hash": "3e44083dd543e1380bdc03931b09984d" + }, + { + "path": "luoyonghao_say/images/0.jpg", + "hash": "dce3cd1bc790a4475744b525e728bdd2" + }, + { + "path": "luxun_say/images/0.jpg", + "hash": "fdf0252e7edea0a3186b908af557d2a7" + }, + { + "path": "maimai_awaken/images/0.png", + "hash": "e624223202cfeefff8c6be39f395c9d6" + }, + { + "path": "maimai_join/images/0.png", + "hash": "0b76b5df240115d862fb08425857363b" + }, + { + "path": "make_friend/images/0.png", + "hash": "9b20187a1eb50164c5963fc13146f4ea" + }, + { + "path": "marriage/images/0.png", + "hash": "700e73f2bfc52f205caa46c81c55a1fe" + }, + { + "path": "marriage/images/1.png", + "hash": "b118fe27aec8b9b05fe73cb8c942726e" + }, + { + "path": "meteor/images/0.png", + "hash": "16a32e5a514c4355a164afb05d510fb6" + }, + { + "path": "mihoyo/images/logo.png", + "hash": "681bf981d9181df226ca42354b7b7a85" + }, + { + "path": "mourning/images/0.png", + "hash": "931a7b0cda1cc6aa9dc2fcc6853dea35" + }, + { + "path": "murmur/images/0.jpg", + "hash": "2f37f330f2f6c83ef74505a405f25b87" + }, + { + "path": "my_friend/images/corner1.png", + "hash": "53a18e9da0e4723e876976b06aebe1a2" + }, + { + "path": "my_friend/images/corner2.png", + "hash": "5789f0bfbbfc745f5d5e37b77d911940" + }, + { + "path": "my_friend/images/corner3.png", + "hash": "e6a10dded6652680d878434ca1a19830" + }, + { + "path": "my_friend/images/corner4.png", + "hash": "40a04aa8c2ffb9243119c865ece04cc4" + }, + { + "path": "my_friend/images/label.png", + "hash": "9538d1dd1e7e3befadacc26bbb511754" + }, + { + "path": "my_wife/images/0.png", + "hash": "c6fd08ac07edee816f25a4713758f428" + }, + { + "path": "my_wife/images/1.png", + "hash": "d4f0dca879deb9a02a732c921c6a8abb" + }, + { + "path": "name_generator/images/title.png", + "hash": "770526d5d986cceb04c0516ec5e31bd1" + }, + { + "path": "need/images/0.png", + "hash": "1b1b6fb05731056fc52d9afd3b4877b9" + }, + { + "path": "nekoha_holdsign/images/0.jpg", + "hash": "2f218b1028744b120d6ee6b19664194b" + }, + { + "path": "nijika_holdsign/images/0.jpg", + "hash": "267732c79de6f2b26a7e98146237d67e" + }, + { + "path": "no_response/images/0.png", + "hash": "2d83def33dfe559dcad883f39bfee4cb" + }, + { + "path": "nokia/images/0.jpg", + "hash": "b15f3d10d3b28f9988deb9940b2084cb" + }, + { + "path": "not_call_me/images/0.png", + "hash": "27500542a606e46aa4c218686fd43077" + }, + { + "path": "oshi_no_ko/images/background.png", + "hash": "758bbb5a92d74f0869e64039be575a76" + }, + { + "path": "oshi_no_ko/images/foreground.png", + "hash": "7e0c9250c5294a062ab4e948dbe6261c" + }, + { + "path": "oshi_no_ko/images/text1.png", + "hash": "3819b528aa7d71a3243d95de6152183e" + }, + { + "path": "oshi_no_ko/images/text2.png", + "hash": "f6f7d087081b960c553acd1645d3d6a3" + }, + { + "path": "osu/images/osu.png", + "hash": "b4b0bc153542220204b590cf48736bdd" + }, + { + "path": "overtime/images/0.png", + "hash": "ec2b3f45754ffef5dd334f1e2d21f652" + }, + { + "path": "paint/images/0.png", + "hash": "c819d7bdbada27cc825db68927157b98" + }, + { + "path": "painter/images/0.png", + "hash": "a9b282341ef7222e44ad66cd9a39819c" + }, + { + "path": "pass_the_buck/images/0.png", + "hash": "5f749373afdf58cde70ea2af2d30a714" + }, + { + "path": "pass_the_buck/images/1.png", + "hash": "c966daa812d79ec51ac14ea845b8b910" + }, + { + "path": "pass_the_buck/images/2.png", + "hash": "a85eeded320e6d73ca8681f1a09cb2ec" + }, + { + "path": "pass_the_buck/images/3.png", + "hash": "18e0ee5e536c311bbc89814e73c09e62" + }, + { + "path": "pass_the_buck/images/4.png", + "hash": "b9917d59c00d7c72cd15b44e6f5432cd" + }, + { + "path": "pass_the_buck/images/5.png", + "hash": "d2f837911a2a085538a2d4800dd4650e" + }, + { + "path": "pass_the_buck/images/6.png", + "hash": "b096dc7de13827c73013216d34358be7" + }, + { + "path": "pass_the_buck/images/7.png", + "hash": "30d2811477862856697b2b277390a022" + }, + { + "path": "pat/images/0.png", + "hash": "089f648471c168ed0eadcb5ad1a8a5b0" + }, + { + "path": "pat/images/1.png", + "hash": "7e2aa352d96eee79d7ead97f9f867c58" + }, + { + "path": "pat/images/2.png", + "hash": "8268e18385c30834fad22a44d26bfffd" + }, + { + "path": "pat/images/3.png", + "hash": "afbfb52f564d8e21275d5150ebd7bc19" + }, + { + "path": "pat/images/4.png", + "hash": "184b8669333285cbca11ff02bd5e7938" + }, + { + "path": "pat/images/5.png", + "hash": "f57fb66c9dc205f3a2d60039ed3e81f3" + }, + { + "path": "pat/images/6.png", + "hash": "0bca6a9528efd4c75e069aade9c5d70f" + }, + { + "path": "pat/images/7.png", + "hash": "981681b3d681ca561254e2cbb1b2908b" + }, + { + "path": "pat/images/8.png", + "hash": "92b0a66f1fa6818635772f03a6379788" + }, + { + "path": "pat/images/9.png", + "hash": "2ee0fda7d77e0b5d6c6d65e5974c70dc" + }, + { + "path": "perfect/images/0.png", + "hash": "52548c41f2774afb87bf7aad314e9938" + }, + { + "path": "petpet/images/0.png", + "hash": "aa8401e6dee45a90dd17efac4797355d" + }, + { + "path": "petpet/images/1.png", + "hash": "97cb8b8449b8000edaa5c4380594a611" + }, + { + "path": "petpet/images/2.png", + "hash": "5e2896e5971f1f18410353c63c35bfc5" + }, + { + "path": "petpet/images/3.png", + "hash": "73812f2ec03138254ffed2199d172c9b" + }, + { + "path": "petpet/images/4.png", + "hash": "4cc824d2d50fadc3e7aa83b8f8c01585" + }, + { + "path": "play/images/0.png", + "hash": "4964c0cecc7d5536ac070356531a0f2a" + }, + { + "path": "play/images/1.png", + "hash": "44242083d7d62c88ce1c4df5fa7e2c18" + }, + { + "path": "play/images/10.png", + "hash": "0622967a345a44eb787cb0729430ed1f" + }, + { + "path": "play/images/11.png", + "hash": "d07943ca2dc901e87dd1de1fb57f1522" + }, + { + "path": "play/images/12.png", + "hash": "611d761e98d873f2e0a295273901cb9d" + }, + { + "path": "play/images/13.png", + "hash": "91807f3f15dbd8385022a3967029520d" + }, + { + "path": "play/images/14.png", + "hash": "873c8b4a9d05d1f6869f80ba0efaf4fa" + }, + { + "path": "play/images/15.png", + "hash": "b553976c6c0539afeb354315428211f1" + }, + { + "path": "play/images/16.png", + "hash": "b9eef848ee25631a033023c18f5fb711" + }, + { + "path": "play/images/17.png", + "hash": "b0319ca25761c7b036e81e555d0d350d" + }, + { + "path": "play/images/18.png", + "hash": "2c2d1c73ef7586f3bbaed7083f503171" + }, + { + "path": "play/images/19.png", + "hash": "2678692ef119d151594d6e6b72a21100" + }, + { + "path": "play/images/2.png", + "hash": "70194e8b049eaf52bf64137d402af5b7" + }, + { + "path": "play/images/20.png", + "hash": "e5d12ed9217f3df2d5e8d3f50f692ade" + }, + { + "path": "play/images/21.png", + "hash": "93e8b3a0197f707a0272013c296c45ad" + }, + { + "path": "play/images/22.png", + "hash": "d1e23a1e2f3f67a68f13d514222e8288" + }, + { + "path": "play/images/23.png", + "hash": "027cfd72da7425411f4fbfb45af76e75" + }, + { + "path": "play/images/24.png", + "hash": "ca73fe22c80e4d12f07854456e67500d" + }, + { + "path": "play/images/25.png", + "hash": "79a133301b5aadd29c00ae351eb70258" + }, + { + "path": "play/images/26.png", + "hash": "be4252f16c83933f0f843dbe65c41df1" + }, + { + "path": "play/images/27.png", + "hash": "ca73fe22c80e4d12f07854456e67500d" + }, + { + "path": "play/images/28.png", + "hash": "668b9f971b27685a1fcc06bc6d27d661" + }, + { + "path": "play/images/29.png", + "hash": "027cfd72da7425411f4fbfb45af76e75" + }, + { + "path": "play/images/3.png", + "hash": "1f5064e766aa07cbd0bcc6014e04a363" + }, + { + "path": "play/images/30.png", + "hash": "79a133301b5aadd29c00ae351eb70258" + }, + { + "path": "play/images/31.png", + "hash": "be4252f16c83933f0f843dbe65c41df1" + }, + { + "path": "play/images/32.png", + "hash": "668b9f971b27685a1fcc06bc6d27d661" + }, + { + "path": "play/images/33.png", + "hash": "027cfd72da7425411f4fbfb45af76e75" + }, + { + "path": "play/images/34.png", + "hash": "79a133301b5aadd29c00ae351eb70258" + }, + { + "path": "play/images/35.png", + "hash": "be4252f16c83933f0f843dbe65c41df1" + }, + { + "path": "play/images/36.png", + "hash": "f15c1afc1766b284662e2972caff927e" + }, + { + "path": "play/images/37.png", + "hash": "ca73fe22c80e4d12f07854456e67500d" + }, + { + "path": "play/images/4.png", + "hash": "931b04efcc1786137c24e532f42ed847" + }, + { + "path": "play/images/5.png", + "hash": "e1e185b4450a41b2f0b3a20e7822a87f" + }, + { + "path": "play/images/6.png", + "hash": "53c0d86f536a10a6ad551845e9cb477a" + }, + { + "path": "play/images/7.png", + "hash": "84855b20768e4a8baf8a904898759fa8" + }, + { + "path": "play/images/8.png", + "hash": "12febab0de29bfc49af8f11941eef7a3" + }, + { + "path": "play/images/9.png", + "hash": "8aaa0621bce8a04b4932d997c47bae03" + }, + { + "path": "play_game/images/0.png", + "hash": "aa9ba6ed9b53feb9b655a2db774fa0cf" + }, + { + "path": "police/images/0.png", + "hash": "31a60a25b44407b6a01a07b3cdd3c2de" + }, + { + "path": "police/images/1.png", + "hash": "57959e7d0b0ee6a36f7d6eb39fd37714" + }, + { + "path": "potato/images/0.png", + "hash": "46cc49cf59d8c234489c4493d4d2c6f2" + }, + { + "path": "pound/images/0.png", + "hash": "bc329e320843da1652fa8cbaebc547f6" + }, + { + "path": "pound/images/1.png", + "hash": "1287970496b73b9b8cc581efca214167" + }, + { + "path": "pound/images/2.png", + "hash": "5196cd4b0b7add30d6ee445ef3302bdc" + }, + { + "path": "pound/images/3.png", + "hash": "6de53f69220f12bd575962f98cfcbf89" + }, + { + "path": "pound/images/4.png", + "hash": "363bc7090b8a57a5eca03ce0903cd5be" + }, + { + "path": "pound/images/5.png", + "hash": "e82712a091010daf9805def5a3a87790" + }, + { + "path": "pound/images/6.png", + "hash": "2f577a122a2b94f6a9b13a2746203906" + }, + { + "path": "pound/images/7.png", + "hash": "bbb206934bdec99cc500ccc2cd2d801d" + }, + { + "path": "printing/images/0.png", + "hash": "ac3cff745bbaf446918b84aee688a750" + }, + { + "path": "printing/images/1.png", + "hash": "72da190ca388c9ae96eb884830b4481f" + }, + { + "path": "printing/images/10.png", + "hash": "129583017b8b02ace1340066433cf986" + }, + { + "path": "printing/images/100.png", + "hash": "b6e14377c005356ef57a2e4b07cf7292" + }, + { + "path": "printing/images/101.png", + "hash": "6b1cd000248a907c1291acb96361441e" + }, + { + "path": "printing/images/102.png", + "hash": "8ec22ccdbd0c9dbc858da3dfda4967ff" + }, + { + "path": "printing/images/103.png", + "hash": "b65ecfdbdae3bbc81148bf1228180c2f" + }, + { + "path": "printing/images/104.png", + "hash": "d1fa8d37a18f9f581e9f6b14622ca1a6" + }, + { + "path": "printing/images/105.png", + "hash": "941d0130b5581674813e8404f9509eff" + }, + { + "path": "printing/images/106.png", + "hash": "63adb8500c990b71f3cb77b50375e52f" + }, + { + "path": "printing/images/107.png", + "hash": "c1fe55f6eea0e8c4971f5038318fcdc1" + }, + { + "path": "printing/images/108.png", + "hash": "3620c7b0de8754f8de8708ceedba6b23" + }, + { + "path": "printing/images/109.png", + "hash": "65e3ab016ec0924e1a2739ecbf7d35d1" + }, + { + "path": "printing/images/11.png", + "hash": "2ca1c8654ca6de78f3c4bdcd7d663234" + }, + { + "path": "printing/images/110.png", + "hash": "7f0840cfc51722d11b1843144227efdf" + }, + { + "path": "printing/images/111.png", + "hash": "77031111c9e53996042ef6e908443c15" + }, + { + "path": "printing/images/112.png", + "hash": "1264df4ba6d1325af96ba15477533af7" + }, + { + "path": "printing/images/113.png", + "hash": "8af7d6b59d30692d4e260140603abd93" + }, + { + "path": "printing/images/114.png", + "hash": "2f27ca03f5bf1f601036e5b4de8d9950" + }, + { + "path": "printing/images/12.png", + "hash": "b3c65ab3c51c559928198e17df9badc8" + }, + { + "path": "printing/images/13.png", + "hash": "740fdc5053855d7f1278cba3593ea729" + }, + { + "path": "printing/images/14.png", + "hash": "f6adfc704b7839c3e391396995073af5" + }, + { + "path": "printing/images/15.png", + "hash": "40829d369a13a6b1bfaa38406e492cbe" + }, + { + "path": "printing/images/16.png", + "hash": "6a3c780a61dbfa06f737c597e2abcaf7" + }, + { + "path": "printing/images/17.png", + "hash": "7876c0a5e91d5fecadc61ef526fdab86" + }, + { + "path": "printing/images/18.png", + "hash": "b10a8f8602cf048399db9020bfdd1e75" + }, + { + "path": "printing/images/19.png", + "hash": "ffc2ae5e811fded5c8aefe2d60635ba8" + }, + { + "path": "printing/images/2.png", + "hash": "65c5675d45f77b42afd06f5d28cecdc5" + }, + { + "path": "printing/images/20.png", + "hash": "b5609cde715a76f2857a2f810db1a8df" + }, + { + "path": "printing/images/21.png", + "hash": "7f9e9f595867665d6e793dae66957b37" + }, + { + "path": "printing/images/22.png", + "hash": "94d1cfd2cc404470f4216b3b9d5c09c9" + }, + { + "path": "printing/images/23.png", + "hash": "555fbe5b32b8fabb4dec5b67de806416" + }, + { + "path": "printing/images/24.png", + "hash": "815f6cfa6c8b8bd67e22d25f592f9efb" + }, + { + "path": "printing/images/25.png", + "hash": "fbdb72c2c34bda528c658a64ea87929b" + }, + { + "path": "printing/images/26.png", + "hash": "2711343b873784e8339b1bdfc4fcfb17" + }, + { + "path": "printing/images/27.png", + "hash": "a0e1b20a162997030cca39394f753d83" + }, + { + "path": "printing/images/28.png", + "hash": "33bfd6b64103e85f8f7593df3083d3c6" + }, + { + "path": "printing/images/29.png", + "hash": "822ddd7bb4f08a790cc2dd12def9fd57" + }, + { + "path": "printing/images/3.png", + "hash": "dd7c0fd91b364ec6ab67424f9b290252" + }, + { + "path": "printing/images/30.png", + "hash": "26135ed3ba6a1ea2689d5d456bae8091" + }, + { + "path": "printing/images/31.png", + "hash": "a4618e6f0ba690e19f8eb46fa40c24d8" + }, + { + "path": "printing/images/32.png", + "hash": "0c6b1311b2b87ec4217965aaf0142a67" + }, + { + "path": "printing/images/33.png", + "hash": "9a5328b1a5a9a1661a6d4383485f38a0" + }, + { + "path": "printing/images/34.png", + "hash": "863b57994604481030eeb9fa07917025" + }, + { + "path": "printing/images/35.png", + "hash": "b89e61f0d769c422927e2870ee2a52f7" + }, + { + "path": "printing/images/36.png", + "hash": "b2a12f4258eb4263725b3e9626c1ce0f" + }, + { + "path": "printing/images/37.png", + "hash": "fe7e7592705cfb38f3554af6ce40e676" + }, + { + "path": "printing/images/38.png", + "hash": "b8e553c9741860aa57bb88f7555bf027" + }, + { + "path": "printing/images/39.png", + "hash": "bf27d396f0e0d04a4c93370089a42e27" + }, + { + "path": "printing/images/4.png", + "hash": "91731f63760d20e0f051d57976897bf7" + }, + { + "path": "printing/images/40.png", + "hash": "5eb837747fa0858effde2e64651a14c9" + }, + { + "path": "printing/images/41.png", + "hash": "605b3313f6d3e38949db0e45e5b1a985" + }, + { + "path": "printing/images/42.png", + "hash": "36807f6d13f4c28d35c22eb8f835e4c8" + }, + { + "path": "printing/images/43.png", + "hash": "687f41f2072c26536b70bf35cbc5009b" + }, + { + "path": "printing/images/44.png", + "hash": "2d64170cbc237d3334766ffcbd1e2c54" + }, + { + "path": "printing/images/45.png", + "hash": "d5234d8605636ee02a77780372c7987f" + }, + { + "path": "printing/images/46.png", + "hash": "53d08603e7fa7e02a3741b4635ec7cd3" + }, + { + "path": "printing/images/47.png", + "hash": "bc71ddd856dfd8b24208ab35b7eedc0c" + }, + { + "path": "printing/images/48.png", + "hash": "44ce15c87fea669d5cc48f41429abf12" + }, + { + "path": "printing/images/49.png", + "hash": "ed2ba817c893cd33f465bf7d2e60ecb2" + }, + { + "path": "printing/images/5.png", + "hash": "8a7f6f66b93e58e9feb6e2207bc7e97f" + }, + { + "path": "printing/images/50.png", + "hash": "a0aab0eb666a90665c9cad5951950143" + }, + { + "path": "printing/images/51.png", + "hash": "8ac53fb8acad20be4ac2a89f7fac5f5a" + }, + { + "path": "printing/images/52.png", + "hash": "4eb11b4cc5bed7f74144884253df1fd2" + }, + { + "path": "printing/images/53.png", + "hash": "c7eaa6de72c86e828dd2f128264e6687" + }, + { + "path": "printing/images/54.png", + "hash": "f7131c14f1a5cee663b8980a21a5bffe" + }, + { + "path": "printing/images/55.png", + "hash": "5d8559a47e8580724039777930942e6d" + }, + { + "path": "printing/images/56.png", + "hash": "be35cd4ddd47f5601ce37afbf14a84de" + }, + { + "path": "printing/images/57.png", + "hash": "c8d986b658968045fea08d94c52b15c4" + }, + { + "path": "printing/images/58.png", + "hash": "2c988b171455d149b0ee55bb37c10c80" + }, + { + "path": "printing/images/59.png", + "hash": "f9a40a01d0dc22f6afed309a42357d56" + }, + { + "path": "printing/images/6.png", + "hash": "a0a79ec55b3c75901bd92d6532fe077d" + }, + { + "path": "printing/images/60.png", + "hash": "61a95457564ceb9c76acd4a778ab6cb0" + }, + { + "path": "printing/images/61.png", + "hash": "6bf483eb9155431f7b8a0592818375c2" + }, + { + "path": "printing/images/62.png", + "hash": "2a7c1edb268391bdf1b3a1ec064103e1" + }, + { + "path": "printing/images/63.png", + "hash": "725821184a8defc249382043632f4848" + }, + { + "path": "printing/images/64.png", + "hash": "4f8839993c49489c7eccd0e7b93c3f50" + }, + { + "path": "printing/images/65.png", + "hash": "7de3f303bd6415f46fd9ca4c99c1c2c6" + }, + { + "path": "printing/images/66.png", + "hash": "be3d33ec8caa9ebe286e52480bab794c" + }, + { + "path": "printing/images/67.png", + "hash": "75e5257d44f16697a230dc959554f6c9" + }, + { + "path": "printing/images/68.png", + "hash": "e52e73c209f028c8b9bdd3baf7d025bf" + }, + { + "path": "printing/images/69.png", + "hash": "fa9f2b49b619559effea9e40f5d7ecd4" + }, + { + "path": "printing/images/7.png", + "hash": "001b10e11da922e3ae037074454be1d0" + }, + { + "path": "printing/images/70.png", + "hash": "a753a853c71e1c2f9d872556a14fabff" + }, + { + "path": "printing/images/71.png", + "hash": "8adf4cf372980a71cebd5a195f955e30" + }, + { + "path": "printing/images/72.png", + "hash": "5c45b2906aaee6a42e01fd1ba6799c32" + }, + { + "path": "printing/images/73.png", + "hash": "674d3c828ea55aef6f5403c5962bde88" + }, + { + "path": "printing/images/74.png", + "hash": "8697148fdb62eaf4fcb3ba5d6d6b0d45" + }, + { + "path": "printing/images/75.png", + "hash": "1f574ff3f9fa2fcaaed49348eceff336" + }, + { + "path": "printing/images/76.png", + "hash": "048b2aae09f83ad559ee9c4f733327b5" + }, + { + "path": "printing/images/77.png", + "hash": "adf05d6f80151eef426075490d542689" + }, + { + "path": "printing/images/78.png", + "hash": "b76784a51340993c26222076ae1c7e19" + }, + { + "path": "printing/images/79.png", + "hash": "a896795729718db13a6dddb410c9ce27" + }, + { + "path": "printing/images/8.png", + "hash": "ab3e1334d642043899b90a78c4fd61f9" + }, + { + "path": "printing/images/80.png", + "hash": "a7dc03466b6bd804c503610906c6f853" + }, + { + "path": "printing/images/81.png", + "hash": "bfbc2cc1c6dd88e45c3842fbd1b90873" + }, + { + "path": "printing/images/82.png", + "hash": "5ca48057a33ee011ce9da553da7f926f" + }, + { + "path": "printing/images/83.png", + "hash": "1c658cc34d088660d29ec36df3c39f0d" + }, + { + "path": "printing/images/84.png", + "hash": "ec9221b4772f36dec3f3511fef90bb89" + }, + { + "path": "printing/images/85.png", + "hash": "5717a74c5271a239b39b525d1f7c6601" + }, + { + "path": "printing/images/86.png", + "hash": "ae6aadc1a4ee31717601b6ae9a33b755" + }, + { + "path": "printing/images/87.png", + "hash": "588ab7fd8d58c05a7874b68f67af4363" + }, + { + "path": "printing/images/88.png", + "hash": "3ffc2d672a8e8feb3db6b184c6b8588e" + }, + { + "path": "printing/images/89.png", + "hash": "c8f92188f211f3d13b760ea63bd26f22" + }, + { + "path": "printing/images/9.png", + "hash": "f99c09832d7c0381625174e9a7e0d392" + }, + { + "path": "printing/images/90.png", + "hash": "6690eec00bce15cda128cf846ea212f0" + }, + { + "path": "printing/images/91.png", + "hash": "070ee6cd8529b919b716f07074e0d167" + }, + { + "path": "printing/images/92.png", + "hash": "14bd48c391ae0baad5d3ef3d242f2fb9" + }, + { + "path": "printing/images/93.png", + "hash": "2ea865e0b43b0eba043b6b1b72f6f5c5" + }, + { + "path": "printing/images/94.png", + "hash": "3475397d42b855ebf9fb6500d194761e" + }, + { + "path": "printing/images/95.png", + "hash": "7fa50ae1f73e19ffa7ab5acb84cf9947" + }, + { + "path": "printing/images/96.png", + "hash": "9111106b9cb4a836b81f250446b3142b" + }, + { + "path": "printing/images/97.png", + "hash": "76ba9e5d54ecdb4bb7913563d9d6b7aa" + }, + { + "path": "printing/images/98.png", + "hash": "61aff8c626615c3fcae399c4e9874030" + }, + { + "path": "printing/images/99.png", + "hash": "0cf63257d2a31dc023d15e718e433300" + }, + { + "path": "prpr/images/0.png", + "hash": "0375726f9e7c54b6993129b790e7adcc" + }, + { + "path": "psyduck/images/0.jpg", + "hash": "6202ec5410070dc4c555da8f96bed178" + }, + { + "path": "psyduck/images/1.jpg", + "hash": "4bbd69e7d760ceb020ea183703ee3c4f" + }, + { + "path": "psyduck/images/10.jpg", + "hash": "2b88c97afddc019a361948db1d294c09" + }, + { + "path": "psyduck/images/11.jpg", + "hash": "443509125b7c6bf2f8d7bd2de1850e8f" + }, + { + "path": "psyduck/images/12.jpg", + "hash": "450c7e24cd5782923d5fc43ed6059274" + }, + { + "path": "psyduck/images/13.jpg", + "hash": "4d3c4db8e6d5603a670c1fda698d2a65" + }, + { + "path": "psyduck/images/14.jpg", + "hash": "fb05987bc8e71c10ea14945f5773998f" + }, + { + "path": "psyduck/images/15.jpg", + "hash": "d41d59768debb11cce3b8ddd459d9f73" + }, + { + "path": "psyduck/images/16.jpg", + "hash": "cb748afc1be5c6a24631e834e65dc788" + }, + { + "path": "psyduck/images/17.jpg", + "hash": "c907167d37716a05f9b98f67c738b5f9" + }, + { + "path": "psyduck/images/2.jpg", + "hash": "bcdada499bee10b55bcf89f3c4203c41" + }, + { + "path": "psyduck/images/3.jpg", + "hash": "2c56d83e9c28ecff169f2bc2934b7e51" + }, + { + "path": "psyduck/images/4.jpg", + "hash": "7d2d686127e5ecbdca634859f044b245" + }, + { + "path": "psyduck/images/5.jpg", + "hash": "44ef8e53403baed0104ec7be98a255f1" + }, + { + "path": "psyduck/images/6.jpg", + "hash": "1e5a3c85df09b32b7ba5e8b3e62aaa67" + }, + { + "path": "psyduck/images/7.jpg", + "hash": "4447c7004f6a34fe1dfa973899b74593" + }, + { + "path": "psyduck/images/8.jpg", + "hash": "567f517d1674346c98bd223b11549c93" + }, + { + "path": "psyduck/images/9.jpg", + "hash": "5d9e85e3e70a8f2488070a19b703452d" + }, + { + "path": "punch/images/0.png", + "hash": "5c50071615149514b42f692aea9d9290" + }, + { + "path": "punch/images/1.png", + "hash": "84e6a624d4546bfcc85c5544ee252c2b" + }, + { + "path": "punch/images/10.png", + "hash": "8c6f3c2838078b5c7241fdd5316f4363" + }, + { + "path": "punch/images/11.png", + "hash": "f25db4a8ee3a9ab72d57f434aa0ca865" + }, + { + "path": "punch/images/12.png", + "hash": "85aee422feae35c368fed239a2b1b0e0" + }, + { + "path": "punch/images/2.png", + "hash": "4c8c9b99f640c78cc634ae4c34191070" + }, + { + "path": "punch/images/3.png", + "hash": "412ac6464392446a77af45c6980e5228" + }, + { + "path": "punch/images/4.png", + "hash": "58c5395c3275558187d0c07c8e59b8b1" + }, + { + "path": "punch/images/5.png", + "hash": "f6a16aaf1d0c0f7239fb12d8f10400a7" + }, + { + "path": "punch/images/6.png", + "hash": "e827982690eaa7703178649a9f7d035b" + }, + { + "path": "punch/images/7.png", + "hash": "0086c46b42a8910c475615ce6c5a98a1" + }, + { + "path": "punch/images/8.png", + "hash": "ee9f371aff7718d1ed2dce523d2f89a0" + }, + { + "path": "punch/images/9.png", + "hash": "f17f4a89f7fed4ff5360b7a566303cd1" + }, + { + "path": "raise_image/images/raise_image.png", + "hash": "53034eae1c09898b82c778c1ea420139" + }, + { + "path": "raise_sign/images/0.jpg", + "hash": "e8151cf81d9c6944e38de9652f5ac409" + }, + { + "path": "read_book/images/0.png", + "hash": "c0bfd676ec62f8ef1a2e2c98244681ba" + }, + { + "path": "repeat/images/0.jpg", + "hash": "b3dabf0c895201454e66011eb88459ff" + }, + { + "path": "rip/images/0.png", + "hash": "ce5cab4dddc51f7c1627e5d135dc6336" + }, + { + "path": "rip/images/1.png", + "hash": "91cfac98b3e2857988c727c03ce76418" + }, + { + "path": "rip_angrily/images/0.png", + "hash": "0c58878dfbe96bdc8a4f8cb3a5d9ae79" + }, + { + "path": "rise_dead/images/0.png", + "hash": "bb4da42954caf82707ae808e07101365" + }, + { + "path": "rise_dead/images/1.png", + "hash": "b10c173d27820716bfa62c906d2fdfe0" + }, + { + "path": "rise_dead/images/10.png", + "hash": "ebc7017ada9ba2d340988a41def17a31" + }, + { + "path": "rise_dead/images/11.png", + "hash": "064625a135258a8525e23cdc44040157" + }, + { + "path": "rise_dead/images/12.png", + "hash": "78c77f0495aa78756ed7e1909009b2f6" + }, + { + "path": "rise_dead/images/13.png", + "hash": "86b71d80dea3789312eba925ea7e3b56" + }, + { + "path": "rise_dead/images/14.png", + "hash": "05993f523cdcceb0ec8bc9e6656c1f49" + }, + { + "path": "rise_dead/images/15.png", + "hash": "ac00807a618031c7f76928526503451f" + }, + { + "path": "rise_dead/images/16.png", + "hash": "8983a7c0ffd8b6ebd12fc2339c2edfeb" + }, + { + "path": "rise_dead/images/17.png", + "hash": "f3f90643dacf3f515bb6524fc87c7535" + }, + { + "path": "rise_dead/images/18.png", + "hash": "5f0b03fe62967453b1b9020ac056440d" + }, + { + "path": "rise_dead/images/19.png", + "hash": "347769d325378397d07e3532969eb868" + }, + { + "path": "rise_dead/images/2.png", + "hash": "0bc0586166d67e6a316228514d4fcb7d" + }, + { + "path": "rise_dead/images/20.png", + "hash": "0b7b35f43230dd8978969173b394d5ee" + }, + { + "path": "rise_dead/images/21.png", + "hash": "0f2e2388b681dbbd1d31fda06684e0b5" + }, + { + "path": "rise_dead/images/22.png", + "hash": "db7f48285b432b21535e8c96cb9d184c" + }, + { + "path": "rise_dead/images/23.png", + "hash": "f5d5737d78644c9f67029da754ca0ebf" + }, + { + "path": "rise_dead/images/24.png", + "hash": "6145e4ab2f5b0f92586b68b7f85ddc5e" + }, + { + "path": "rise_dead/images/25.png", + "hash": "5585c88626e8412b78bf4c51d045c2c1" + }, + { + "path": "rise_dead/images/26.png", + "hash": "be925adadeb85d0a3dce380f522440ef" + }, + { + "path": "rise_dead/images/27.png", + "hash": "339666a1146653e04cb89991310afc60" + }, + { + "path": "rise_dead/images/28.png", + "hash": "b3136958aa2f24b4c74b21a0e79bcd64" + }, + { + "path": "rise_dead/images/29.png", + "hash": "89c73842fa2b0390acbcac79014b849b" + }, + { + "path": "rise_dead/images/3.png", + "hash": "425bb05e042774c00b77a697733f9902" + }, + { + "path": "rise_dead/images/30.png", + "hash": "b36b2c85161ac3d73ece81485a725b25" + }, + { + "path": "rise_dead/images/31.png", + "hash": "dd172233ca3a26d5729ca48f8e212362" + }, + { + "path": "rise_dead/images/32.png", + "hash": "b4a080295ec55dc4e790d7f2eeaa5c88" + }, + { + "path": "rise_dead/images/33.png", + "hash": "b65493e53df17f3a9637b3e73996c111" + }, + { + "path": "rise_dead/images/4.png", + "hash": "8496a9a5c3d2a82ddaabdb477096d213" + }, + { + "path": "rise_dead/images/5.png", + "hash": "325cc724fe6e4a38dc89c08bb95aee06" + }, + { + "path": "rise_dead/images/6.png", + "hash": "23490018d2317c7feae489de998ac222" + }, + { + "path": "rise_dead/images/7.png", + "hash": "08994770e161777e630fd5436eb1e418" + }, + { + "path": "rise_dead/images/8.png", + "hash": "03c5ca3f5c0a26ea9e7c95892ff4c391" + }, + { + "path": "rise_dead/images/9.png", + "hash": "401693c6a4e254609bd2bff9864ea0ce" + }, + { + "path": "roll/images/0.png", + "hash": "4b7bd23b3f9d4c2ab7d36885a6edf21a" + }, + { + "path": "roll/images/1.png", + "hash": "19aa54e138172a2ddf7f031da195ebf1" + }, + { + "path": "roll/images/2.png", + "hash": "fa009d3c3dc2645600b3b8ba2200c240" + }, + { + "path": "roll/images/3.png", + "hash": "72ddb67a4589290b6cc7c2591c163401" + }, + { + "path": "roll/images/4.png", + "hash": "3d0292a3e7bfc90e32aa320f85d906e0" + }, + { + "path": "roll/images/5.png", + "hash": "5767ba5025f85dc60b65c1e448f4c243" + }, + { + "path": "roll/images/6.png", + "hash": "b1babecdb8a977ba477c033b9d929b73" + }, + { + "path": "roll/images/7.png", + "hash": "c0475533dc1e6282dfa50e7ee5f6d150" + }, + { + "path": "rub/images/0.png", + "hash": "a9b12cb6b809ae7bce00f90776a0366b" + }, + { + "path": "rub/images/1.png", + "hash": "dcaa808bd4d89084ab341ee5589dab75" + }, + { + "path": "rub/images/2.png", + "hash": "8e8eb0d22991b02c96b4098210a861ee" + }, + { + "path": "rub/images/3.png", + "hash": "4039152492625de12a188e0d80a2fc9c" + }, + { + "path": "rub/images/4.png", + "hash": "b80153e63c7918ae5b59aa2287803a94" + }, + { + "path": "rub/images/5.png", + "hash": "910edf679e3b3bf292a7f3a86fe878c0" + }, + { + "path": "run/images/0.png", + "hash": "8504969b4b3d302c15e37ab7033591a7" + }, + { + "path": "safe_sense/images/0.png", + "hash": "83653a8efa58498ae0ff8ea806e523ad" + }, + { + "path": "scratch_head/images/0.png", + "hash": "a72596f3beff01eaa56d20458175cf7b" + }, + { + "path": "scratch_head/images/1.png", + "hash": "7e2ffe8187564dd18cdbd7826c4cc264" + }, + { + "path": "scratch_head/images/2.png", + "hash": "53293a097661db4412b2cd4fd4d50b49" + }, + { + "path": "scratch_head/images/3.png", + "hash": "473644e2d0a98e9e4c6149485c7f57c1" + }, + { + "path": "scratch_head/images/4.png", + "hash": "4dc269cf571fe1ed75082ccb90acb5d5" + }, + { + "path": "scratch_head/images/5.png", + "hash": "477528b65f0672b6c98a285455c727c3" + }, + { + "path": "scratchcard/images/0.png", + "hash": "0a9f099c2ff528543c6c8a7cfb2566aa" + }, + { + "path": "scratchcard/images/1.png", + "hash": "8b4dd8c62fe06b2accb2ca8d79102e3a" + }, + { + "path": "scroll/images/corner1.png", + "hash": "53a18e9da0e4723e876976b06aebe1a2" + }, + { + "path": "scroll/images/corner2.png", + "hash": "5789f0bfbbfc745f5d5e37b77d911940" + }, + { + "path": "scroll/images/corner3.png", + "hash": "e6a10dded6652680d878434ca1a19830" + }, + { + "path": "scroll/images/corner4.png", + "hash": "40a04aa8c2ffb9243119c865ece04cc4" + }, + { + "path": "shutup/images/0.jpg", + "hash": "79462d2472da56f64f056b7c16b43b04" + }, + { + "path": "sit_still/images/0.png", + "hash": "7c80de7e9f83c87b8f099fda676cafde" + }, + { + "path": "slap/images/0.jpg", + "hash": "bca036d3b7a5de25c57e92525891c3e7" + }, + { + "path": "slogan/images/0.jpg", + "hash": "93e06a80b22c3b796f3bb90c590f8cd6" + }, + { + "path": "smash/images/0.png", + "hash": "f14a1ff64015a1cd5cf752e80e5cf367" + }, + { + "path": "step_on/images/0.png", + "hash": "560c088afdbeff7f0321455b0640b938" + }, + { + "path": "step_on/images/1.png", + "hash": "9ce4e4cee75d6d30385d7bd1b50b3882" + }, + { + "path": "step_on/images/2.png", + "hash": "2c3531495de3bfb2c0a631db0e2a6bf1" + }, + { + "path": "step_on/images/3.png", + "hash": "a6a77178a80d9cffdb4292901dc6cb14" + }, + { + "path": "step_on/images/4.png", + "hash": "f052852fa07b20fa4d9d48a7d8249d87" + }, + { + "path": "suck/images/0.png", + "hash": "848d6fe194940dc2fef9db9a354fb126" + }, + { + "path": "suck/images/1.png", + "hash": "74739c77b17df646533133ab49174379" + }, + { + "path": "suck/images/10.png", + "hash": "76688f982ae2e1c7e878ba185fcddd7d" + }, + { + "path": "suck/images/11.png", + "hash": "d1723c022b984588853956ad454c0eba" + }, + { + "path": "suck/images/2.png", + "hash": "a13a413108cc0e319449d549df0533f2" + }, + { + "path": "suck/images/3.png", + "hash": "5663af4b29aed543cbab978154d46a6c" + }, + { + "path": "suck/images/4.png", + "hash": "e51457c880f8bac176d7adcce7c2374f" + }, + { + "path": "suck/images/5.png", + "hash": "1d7a90731644d866ca75975da0b9b074" + }, + { + "path": "suck/images/6.png", + "hash": "95f8124f3135e813c1273b6261a49855" + }, + { + "path": "suck/images/7.png", + "hash": "138eb577148a6e2ffa7e9b21de644395" + }, + { + "path": "suck/images/8.png", + "hash": "e58d096d494249b94565a122b6bfc1e6" + }, + { + "path": "suck/images/9.png", + "hash": "35d5c2a770ca62666d054f7de229c01b" + }, + { + "path": "support/images/0.png", + "hash": "f82b0d023e4aab41df6159db726f4ae3" + }, + { + "path": "tankuku_raisesign/images/0.png", + "hash": "e4eac3dbf019343f3dd02597e982707d" + }, + { + "path": "tankuku_raisesign/images/1.png", + "hash": "896acaf1cc2cab04833244b89cb8dca7" + }, + { + "path": "tankuku_raisesign/images/10.png", + "hash": "e775c695c9875a67106d5f7f421def85" + }, + { + "path": "tankuku_raisesign/images/11.png", + "hash": "067068e71e648e0e79ef280e1bc49fc2" + }, + { + "path": "tankuku_raisesign/images/12.png", + "hash": "0dcb44d94c9422d1c2f5b37b7d0409c7" + }, + { + "path": "tankuku_raisesign/images/13.png", + "hash": "41fd25672049e69721d9f626247d4ac8" + }, + { + "path": "tankuku_raisesign/images/14.png", + "hash": "f798a270ffe2a4992e8ac23c4e749dc4" + }, + { + "path": "tankuku_raisesign/images/2.png", + "hash": "d60603b9dce7924f237c7610c636b6d0" + }, + { + "path": "tankuku_raisesign/images/3.png", + "hash": "c9eaf5c44495cd5b2e3a53e92dca9037" + }, + { + "path": "tankuku_raisesign/images/4.png", + "hash": "eadb1e2419fe3b138b83ac72d26c175f" + }, + { + "path": "tankuku_raisesign/images/5.png", + "hash": "31511f3f3bd656f0481cc43579806f69" + }, + { + "path": "tankuku_raisesign/images/6.png", + "hash": "140f1de7f05e51ca0024b7147978e5a6" + }, + { + "path": "tankuku_raisesign/images/7.png", + "hash": "7c595f3e0855121586e0e1d132a570f6" + }, + { + "path": "tankuku_raisesign/images/8.png", + "hash": "cbfc69c8c36e6b40b1f6d2b5de48cdd7" + }, + { + "path": "tankuku_raisesign/images/9.png", + "hash": "7b43a1b2f5c738b821224919141318dc" + }, + { + "path": "taunt/images/0.png", + "hash": "5c7c1026ecca7ec754ecdf622bdbcec9" + }, + { + "path": "teach/images/0.png", + "hash": "d035c0cb0ffcc2487cc563d9222fe37e" + }, + { + "path": "tease/images/0.png", + "hash": "8251e76997777ce41acf17ff15af23fd" + }, + { + "path": "tease/images/1.png", + "hash": "9d647800cdeedc8f3c5b1b776a7cccb0" + }, + { + "path": "tease/images/10.png", + "hash": "057cfbfad82bb0b0908007ebfed287a5" + }, + { + "path": "tease/images/11.png", + "hash": "da37ae44aed77e31d728b67661617ad5" + }, + { + "path": "tease/images/12.png", + "hash": "7b5aed610a21aa724d51b42b72a65c38" + }, + { + "path": "tease/images/13.png", + "hash": "78cb78fd9a69b688f32035c04b823799" + }, + { + "path": "tease/images/14.png", + "hash": "f35d24e56efebef538d35710f00e6705" + }, + { + "path": "tease/images/15.png", + "hash": "208655c7486c4a84e431001034d34f36" + }, + { + "path": "tease/images/16.png", + "hash": "2885f985790cb8faa62245a6eae275d5" + }, + { + "path": "tease/images/17.png", + "hash": "0ea6cec2b18fdf9c1114e478327735ce" + }, + { + "path": "tease/images/18.png", + "hash": "8d5d40c3632d608229f4654598504c2c" + }, + { + "path": "tease/images/19.png", + "hash": "2af25b9cd739f125ed554da6d42a57e4" + }, + { + "path": "tease/images/2.png", + "hash": "7082394ff52b33cda2cb8331d13abdb0" + }, + { + "path": "tease/images/20.png", + "hash": "73555cf10c26253029e27d6357ff7a68" + }, + { + "path": "tease/images/21.png", + "hash": "1ed333486a248a18a84348c7f5629639" + }, + { + "path": "tease/images/22.png", + "hash": "180f319575c668818299fbb7354665e6" + }, + { + "path": "tease/images/23.png", + "hash": "41a81c881a2a9b2232f36d7fe7f640c3" + }, + { + "path": "tease/images/3.png", + "hash": "9df8b76fba5c7efad4d2d41182ed23ec" + }, + { + "path": "tease/images/4.png", + "hash": "ecca01e1ea93aacf2b993eeac4c8a9a3" + }, + { + "path": "tease/images/5.png", + "hash": "f8b89e4d9227c84aab6327595e262341" + }, + { + "path": "tease/images/6.png", + "hash": "3581909eee488e81c478575b83366856" + }, + { + "path": "tease/images/7.png", + "hash": "efade3fa3148c398d3f7dbf7cb0b7ce2" + }, + { + "path": "tease/images/8.png", + "hash": "b731fabc810ff894d8204bb3d5070068" + }, + { + "path": "tease/images/9.png", + "hash": "841db44a3ea19ca016ba21a0f4d71564" + }, + { + "path": "think_what/images/0.png", + "hash": "550c3e7829033511873a12d0a8e25447" + }, + { + "path": "throw/images/0.png", + "hash": "b3e1c12b34acc44e1dd63ce05330025e" + }, + { + "path": "throw_gif/images/0.png", + "hash": "1f0eab0cb4b06609712c14e788209b58" + }, + { + "path": "throw_gif/images/1.png", + "hash": "8d7168433eaa08d827d4165a1f47f76a" + }, + { + "path": "throw_gif/images/2.png", + "hash": "aae0c868f80bdeb5a3345326757c129f" + }, + { + "path": "throw_gif/images/3.png", + "hash": "770abef39d642b721896fc92237beb7a" + }, + { + "path": "throw_gif/images/4.png", + "hash": "29ed013453ef3495b16f231a3c423dd4" + }, + { + "path": "throw_gif/images/5.png", + "hash": "144ba24b867fb739dd2c497e325fb508" + }, + { + "path": "throw_gif/images/6.png", + "hash": "ce41dd6517baf2a389093a2b9a9d1b4b" + }, + { + "path": "throw_gif/images/7.png", + "hash": "08d7b581c394979ab9d63e004b23d67a" + }, + { + "path": "thump/images/0.png", + "hash": "a3db460ecbc8ed4d14bb900d535bf6a5" + }, + { + "path": "thump/images/1.png", + "hash": "4fb3e391510555e146d506705ea0f242" + }, + { + "path": "thump/images/2.png", + "hash": "f004a475c1cd77b1c3852b3df5027f0f" + }, + { + "path": "thump/images/3.png", + "hash": "4e9ef84cdbd3833171f74727ae007aa3" + }, + { + "path": "thump_wildly/images/0.png", + "hash": "1f6e57bccf7f58f6b140abeb1c3b941c" + }, + { + "path": "thump_wildly/images/1.png", + "hash": "22aec171174bb5957d74390e87b6ef83" + }, + { + "path": "thump_wildly/images/10.png", + "hash": "6fcc6eb6320db0a166bee663d999eae5" + }, + { + "path": "thump_wildly/images/11.png", + "hash": "4c3db37b7014adaff0c836f39d4c964f" + }, + { + "path": "thump_wildly/images/12.png", + "hash": "827281875b7952487f338771a4209983" + }, + { + "path": "thump_wildly/images/13.png", + "hash": "a325da842b19d4fe31ab95e0656315f8" + }, + { + "path": "thump_wildly/images/14.png", + "hash": "4d6016470d4d7e461529663cec234cf0" + }, + { + "path": "thump_wildly/images/15.png", + "hash": "4b39fd5edfa9d053c00d2990cd20ff41" + }, + { + "path": "thump_wildly/images/16.png", + "hash": "c48b6254004e13e205a06b70f5ea300d" + }, + { + "path": "thump_wildly/images/17.png", + "hash": "920f35e60237244358c8f9a47d04b7c6" + }, + { + "path": "thump_wildly/images/18.png", + "hash": "09677cbd43ec5d7dc791855ba0e2842e" + }, + { + "path": "thump_wildly/images/19.png", + "hash": "66f6a4dc418a6955ba7b9a5df64dd2d1" + }, + { + "path": "thump_wildly/images/2.png", + "hash": "7efa3aea81c37808f950335be0922424" + }, + { + "path": "thump_wildly/images/20.png", + "hash": "2c2b482fb1154a3cf885ba38223aa2da" + }, + { + "path": "thump_wildly/images/21.png", + "hash": "673ec97403e5d92fc44788942b152642" + }, + { + "path": "thump_wildly/images/22.png", + "hash": "641d5f6862d18b43c2c8c4670f40eb74" + }, + { + "path": "thump_wildly/images/23.png", + "hash": "6af4359dea571c1f14f9f7fba43838d1" + }, + { + "path": "thump_wildly/images/24.png", + "hash": "c055a22db19722fb2f4ea5c4026d8fdd" + }, + { + "path": "thump_wildly/images/25.png", + "hash": "5ef2c5f1a390eb74147291d51ecbc5af" + }, + { + "path": "thump_wildly/images/26.png", + "hash": "b7005e93b8971a5d0e410b3a2fad6722" + }, + { + "path": "thump_wildly/images/27.png", + "hash": "c3a792b3c53f0c20e5d7e11b20ed4545" + }, + { + "path": "thump_wildly/images/28.png", + "hash": "a53192f9a057b5fac112a722a6df83b6" + }, + { + "path": "thump_wildly/images/29.png", + "hash": "12b2d0924e1d697f43a95024e2c5cc47" + }, + { + "path": "thump_wildly/images/3.png", + "hash": "eccebf3e49a818599407970f1835f951" + }, + { + "path": "thump_wildly/images/30.png", + "hash": "f2df7fed5364434fc87f2c200ee8230d" + }, + { + "path": "thump_wildly/images/4.png", + "hash": "0831aee0b3a2def7bef42ac7eae538f4" + }, + { + "path": "thump_wildly/images/5.png", + "hash": "af37e14eba37d272867bc16a650d6cc7" + }, + { + "path": "thump_wildly/images/6.png", + "hash": "af0eebbf35bdf5a9b0726c86322af301" + }, + { + "path": "thump_wildly/images/7.png", + "hash": "cf85f256a80234fbc2cb8dd5fb10a536" + }, + { + "path": "thump_wildly/images/8.png", + "hash": "01a470586d3ca7b4f5ce1701e4a2ea31" + }, + { + "path": "thump_wildly/images/9.png", + "hash": "d191535c58006ac23687e7910d84970e" + }, + { + "path": "tightly/images/0.png", + "hash": "a75986b79eb610e85c68d27a68e121ce" + }, + { + "path": "tightly/images/1.png", + "hash": "ffc5cd12eff13d205b5873810939d071" + }, + { + "path": "tightly/images/10.png", + "hash": "c3be7c205aca24c400e83a98a007a4b1" + }, + { + "path": "tightly/images/11.png", + "hash": "cd36590de435081542abc4f8b5e03ed7" + }, + { + "path": "tightly/images/12.png", + "hash": "dd3dc95f856c43fa3dcad347d7942506" + }, + { + "path": "tightly/images/13.png", + "hash": "5a57c97e80873ac64a2db11e71d0331c" + }, + { + "path": "tightly/images/14.png", + "hash": "d5f7e6eafabe6c1a17b1f985b154ff08" + }, + { + "path": "tightly/images/15.png", + "hash": "85075732dbd183a02b93b7b4eb8c2592" + }, + { + "path": "tightly/images/16.png", + "hash": "8ad15d7c72142b9b3f81146ced8196c8" + }, + { + "path": "tightly/images/17.png", + "hash": "43ec85f7f7fc18485acdb329a7b9c173" + }, + { + "path": "tightly/images/18.png", + "hash": "ba7ac479234cfada00bebe34d96ae8b4" + }, + { + "path": "tightly/images/19.png", + "hash": "7e562d467024416aea69a05e3214cc0e" + }, + { + "path": "tightly/images/2.png", + "hash": "91ab512732c81e4f1ee8a1ed85834523" + }, + { + "path": "tightly/images/3.png", + "hash": "991e06589dd243c45560f4259627c673" + }, + { + "path": "tightly/images/4.png", + "hash": "170d0a57327cb7dc19a44d6dd801a292" + }, + { + "path": "tightly/images/5.png", + "hash": "021d95cebbf539d14d258253d10e9370" + }, + { + "path": "tightly/images/6.png", + "hash": "7f7cb2d5f554dbc3df70128e53653c1f" + }, + { + "path": "tightly/images/7.png", + "hash": "68683eaeb8e1a2d4aa81f6b627738d09" + }, + { + "path": "tightly/images/8.png", + "hash": "dc63be99d968fe0007527d8fc4dcfcf1" + }, + { + "path": "tightly/images/9.png", + "hash": "23e00f03f6a6a883f2550b91646e9fd7" + }, + { + "path": "together/images/0.png", + "hash": "ed562d9d1bf0d345ce83b1b7a83524ac" + }, + { + "path": "twist/images/0.png", + "hash": "f0b59e60d234e2b145e453f1f3ccac5b" + }, + { + "path": "twist/images/1.png", + "hash": "380855eda516a3be0401a5dee662378e" + }, + { + "path": "twist/images/2.png", + "hash": "07b9cf8a73ed851f4965522143c74a8b" + }, + { + "path": "twist/images/3.png", + "hash": "f15b4a115675e62d213d25f6bc7237e8" + }, + { + "path": "twist/images/4.png", + "hash": "946914bdada2b87e06172331e288db83" + }, + { + "path": "wakeup/images/0.jpg", + "hash": "2737c1fd3d312729ee917f5bee3f9016" + }, + { + "path": "wallpaper/images/0.png", + "hash": "5d91e3669643cd2d45fe64c6c9501696" + }, + { + "path": "wallpaper/images/1.png", + "hash": "12eab235d0dc3272884509f6e1ca2bf2" + }, + { + "path": "wallpaper/images/10.png", + "hash": "bd5589b1c64998359a7e1d7bae363aba" + }, + { + "path": "wallpaper/images/11.png", + "hash": "474c2eb9906581e94ce89fcc1636d961" + }, + { + "path": "wallpaper/images/12.png", + "hash": "eb823e78e4088b6abc8273f4466f31c4" + }, + { + "path": "wallpaper/images/13.png", + "hash": "fc6c90ca74d9a8de92bcda226749809b" + }, + { + "path": "wallpaper/images/14.png", + "hash": "22e92ab92d43a4b83d572e2f3d58d6be" + }, + { + "path": "wallpaper/images/15.png", + "hash": "d1a5f088684f0b400a792efbb34c00cb" + }, + { + "path": "wallpaper/images/16.png", + "hash": "7db94297b51c8766705325cc7aa51a09" + }, + { + "path": "wallpaper/images/17.png", + "hash": "edabb58ee911b3d7970afab119ec0b38" + }, + { + "path": "wallpaper/images/18.png", + "hash": "3d8ffcd34928a1bbd75134f1ccee7f04" + }, + { + "path": "wallpaper/images/19.png", + "hash": "41879caa381e4c7107c6f1810a2c308a" + }, + { + "path": "wallpaper/images/2.png", + "hash": "c13be05187841b4b03263e545fe24b76" + }, + { + "path": "wallpaper/images/3.png", + "hash": "7b711459f62b49211078c1473f938062" + }, + { + "path": "wallpaper/images/4.png", + "hash": "d16b19293ef50f06c5ccd192697eec71" + }, + { + "path": "wallpaper/images/5.png", + "hash": "18eb7e80f997eadb512c2151162788dc" + }, + { + "path": "wallpaper/images/6.png", + "hash": "4a3a35a985229434c62e82278a788630" + }, + { + "path": "wallpaper/images/7.png", + "hash": "94dc43c774b32afaa43b42e2fac96ad4" + }, + { + "path": "wallpaper/images/8.png", + "hash": "f123ff9994c6b73a00503a0f71bb61c4" + }, + { + "path": "wallpaper/images/9.png", + "hash": "c622e4dcaca983ed072022692373abc8" + }, + { + "path": "walnut_pad/images/0.png", + "hash": "1868d25967cc950d811b0218d313b42e" + }, + { + "path": "walnut_zoom/images/0.png", + "hash": "54f145f1d66d15726196e6ae690158dc" + }, + { + "path": "walnut_zoom/images/1.png", + "hash": "f61d350a4d95a56083924cd88dbbcd14" + }, + { + "path": "walnut_zoom/images/10.png", + "hash": "0114a2c7010d751674794e9d61febcb5" + }, + { + "path": "walnut_zoom/images/11.png", + "hash": "7ece5bcc469a441e7d3b3f5166ab450f" + }, + { + "path": "walnut_zoom/images/12.png", + "hash": "3ce53d39a6fdb411f88cfbc71a02d553" + }, + { + "path": "walnut_zoom/images/13.png", + "hash": "80f449d1aa5b37f19be05952e9ea4690" + }, + { + "path": "walnut_zoom/images/14.png", + "hash": "3484665c49f4f8e4843be3e016703c01" + }, + { + "path": "walnut_zoom/images/15.png", + "hash": "52017060b380dd4dcab6c330defea997" + }, + { + "path": "walnut_zoom/images/16.png", + "hash": "bd0575ed22a9c9e84da546e15f3eaa36" + }, + { + "path": "walnut_zoom/images/17.png", + "hash": "3354b0a41977265d7768344ed4a01798" + }, + { + "path": "walnut_zoom/images/18.png", + "hash": "097143f19e124348e8823ab11feb2356" + }, + { + "path": "walnut_zoom/images/19.png", + "hash": "98b50bc4b8b8de10fb552821faff3947" + }, + { + "path": "walnut_zoom/images/2.png", + "hash": "90849c6c92dd907cebd0caab396aa36d" + }, + { + "path": "walnut_zoom/images/20.png", + "hash": "4c2d68c5b9299513c8da7a99b320042a" + }, + { + "path": "walnut_zoom/images/21.png", + "hash": "02758244cb76dbbdb087931c0b3aaae2" + }, + { + "path": "walnut_zoom/images/22.png", + "hash": "b8681ce06bda9297986edfa87ba8b9e5" + }, + { + "path": "walnut_zoom/images/23.png", + "hash": "cadf3d90bfc3d9c9989013223604cbb5" + }, + { + "path": "walnut_zoom/images/3.png", + "hash": "7075b474c816617ee6ae77e7a31ded3a" + }, + { + "path": "walnut_zoom/images/4.png", + "hash": "f9c430517346247063fcf9a751c9b497" + }, + { + "path": "walnut_zoom/images/5.png", + "hash": "b41589e48ffe5d2f7ebccb0ccbeb15ae" + }, + { + "path": "walnut_zoom/images/6.png", + "hash": "c247af529838e071ee28bbf89c6884f0" + }, + { + "path": "walnut_zoom/images/7.png", + "hash": "304e586f1c14a677283bcba85d2846ef" + }, + { + "path": "walnut_zoom/images/8.png", + "hash": "d61f4ba636a50858c013b3d271805867" + }, + { + "path": "walnut_zoom/images/9.png", + "hash": "2b3a75dc7874fc47612a0ebe82ead577" + }, + { + "path": "what_I_want_to_do/images/0.png", + "hash": "6f6f0b5452555aa8c162745db59587f0" + }, + { + "path": "what_he_wants/images/0.png", + "hash": "3e9ca1ac6dacf6cb3dceb30c4d2ca74d" + }, + { + "path": "why_at_me/images/0.png", + "hash": "46a89eb5567c049e12722511653722f7" + }, + { + "path": "why_have_hands/images/0.png", + "hash": "96487155465f64e3b79ef5c7d2a1b882" + }, + { + "path": "wish_fail/images/0.png", + "hash": "ecf3e01a1091ef0d035dd2114e7c9a29" + }, + { + "path": "wooden_fish/images/0.png", + "hash": "6d9a3a2283fa7804c7d0053d7dfd2564" + }, + { + "path": "wooden_fish/images/1.png", + "hash": "e0209aed76388832884846769156dceb" + }, + { + "path": "wooden_fish/images/10.png", + "hash": "cec873d2011eb7939946b7b4b3bff9a3" + }, + { + "path": "wooden_fish/images/11.png", + "hash": "712b2bfd606eb5efbddf673862436008" + }, + { + "path": "wooden_fish/images/12.png", + "hash": "5839329d4d13278ec1dad21e601bf006" + }, + { + "path": "wooden_fish/images/13.png", + "hash": "7aace48b94ec939fccbe0123482193c7" + }, + { + "path": "wooden_fish/images/14.png", + "hash": "ecfca6644fbc21c8a0d1b65ddf10c5f7" + }, + { + "path": "wooden_fish/images/15.png", + "hash": "28ac570cd87020781c754eae7062c91e" + }, + { + "path": "wooden_fish/images/16.png", + "hash": "2361ffd34ff8bfcc4a70da139c92af4d" + }, + { + "path": "wooden_fish/images/17.png", + "hash": "884dee4b95f5e58348f0dbeb38e6120a" + }, + { + "path": "wooden_fish/images/18.png", + "hash": "c5fc69e1b72e4576fae6928b09d77942" + }, + { + "path": "wooden_fish/images/19.png", + "hash": "01bf0df0405a5f3bec8a88f1955c487f" + }, + { + "path": "wooden_fish/images/2.png", + "hash": "2d3f89df34bbbf50314030e19582ac82" + }, + { + "path": "wooden_fish/images/20.png", + "hash": "28c1e84abd5af68d67933cba7cb39293" + }, + { + "path": "wooden_fish/images/21.png", + "hash": "7433c06114ff44dcfdfa586d05b9e12a" + }, + { + "path": "wooden_fish/images/22.png", + "hash": "401386db0da8d0f451f037a83055fb97" + }, + { + "path": "wooden_fish/images/23.png", + "hash": "d88ca4f0cbe0baba69cc5124a26ac271" + }, + { + "path": "wooden_fish/images/24.png", + "hash": "6a0b876ccd4fc868da1b1482130724b2" + }, + { + "path": "wooden_fish/images/25.png", + "hash": "facabd578b33beecebaaad619a931cfc" + }, + { + "path": "wooden_fish/images/26.png", + "hash": "c9b3fbd6530cec0851ab8ad4f4f3e2dd" + }, + { + "path": "wooden_fish/images/27.png", + "hash": "e8e7f3d0cd8fcbc949162cfb513db643" + }, + { + "path": "wooden_fish/images/28.png", + "hash": "28345563148569bfc1c87bdaf15512cd" + }, + { + "path": "wooden_fish/images/29.png", + "hash": "6e509f0cf10d8d0eed6694ad4d67d417" + }, + { + "path": "wooden_fish/images/3.png", + "hash": "408ee9928d337a3e1093b360ce1f9c42" + }, + { + "path": "wooden_fish/images/30.png", + "hash": "e17e47ec541b137347ee5092f50b7af4" + }, + { + "path": "wooden_fish/images/31.png", + "hash": "c46afd5cfe31fbb0dff54a21b5275652" + }, + { + "path": "wooden_fish/images/32.png", + "hash": "c733975ec503cbab59083075fdac7df1" + }, + { + "path": "wooden_fish/images/33.png", + "hash": "6fd18280a7b3daeba31211ec357432ce" + }, + { + "path": "wooden_fish/images/34.png", + "hash": "3469e52d0176a91ab0bde381ac609b3e" + }, + { + "path": "wooden_fish/images/35.png", + "hash": "55a55c562ec107000c6a37cd0b6dc93a" + }, + { + "path": "wooden_fish/images/36.png", + "hash": "cfbd99fac76a8f4730aa2abd0d1f9fc7" + }, + { + "path": "wooden_fish/images/37.png", + "hash": "e0c36d441526f5f0ae1d4f7ebf33f783" + }, + { + "path": "wooden_fish/images/38.png", + "hash": "9b344ef9f6f5d0cef32f7fae1aeb867d" + }, + { + "path": "wooden_fish/images/39.png", + "hash": "0eaf38b4b65ba9d8acb18ddedb482c26" + }, + { + "path": "wooden_fish/images/4.png", + "hash": "7a2c835125447d2c2c91022e9da5bba0" + }, + { + "path": "wooden_fish/images/40.png", + "hash": "605ffccc8dd614b3776bcb1b68f8392d" + }, + { + "path": "wooden_fish/images/41.png", + "hash": "6c3942c1f933048da5842556d45b41fd" + }, + { + "path": "wooden_fish/images/42.png", + "hash": "de6cdb5e5dc569b93bd110e328ccee48" + }, + { + "path": "wooden_fish/images/43.png", + "hash": "a2590f5e678b4b49951d457bff4d7416" + }, + { + "path": "wooden_fish/images/44.png", + "hash": "e25bbc2b3d6b004107a78a07472a26bc" + }, + { + "path": "wooden_fish/images/45.png", + "hash": "f0b6b348a1e95793535a963cdec0c526" + }, + { + "path": "wooden_fish/images/46.png", + "hash": "feaf6200470ca2670250a5ecf1d82638" + }, + { + "path": "wooden_fish/images/47.png", + "hash": "f20eea970a4f715bed311fd3292b6ffe" + }, + { + "path": "wooden_fish/images/48.png", + "hash": "c131eac6cb07efb11fc0db34344375cd" + }, + { + "path": "wooden_fish/images/49.png", + "hash": "4d02f896bd3add73f0607efbd399405a" + }, + { + "path": "wooden_fish/images/5.png", + "hash": "9808b4dfc0739cc92801c07218c437a1" + }, + { + "path": "wooden_fish/images/50.png", + "hash": "79390633b41372c3697d0f5cb875c920" + }, + { + "path": "wooden_fish/images/51.png", + "hash": "094b6e5646bfe1ba46a5a37075316aa4" + }, + { + "path": "wooden_fish/images/52.png", + "hash": "110f85d76529735911de6f2aed883c8d" + }, + { + "path": "wooden_fish/images/53.png", + "hash": "a06dd450c09157782cb1dca8cad9ea88" + }, + { + "path": "wooden_fish/images/54.png", + "hash": "f8417b9eeb8dff82385cd20a5acdb5be" + }, + { + "path": "wooden_fish/images/55.png", + "hash": "db3b4b5518a00cc83db2d6cb1190b4b3" + }, + { + "path": "wooden_fish/images/56.png", + "hash": "0d3848e808467b632a273a51e4b94b94" + }, + { + "path": "wooden_fish/images/57.png", + "hash": "67c3ec213de1f043b3123262c2dcba24" + }, + { + "path": "wooden_fish/images/58.png", + "hash": "9c58d568c71c2724985853f7843ae11a" + }, + { + "path": "wooden_fish/images/59.png", + "hash": "d6e191f08943eeda4afd3d038447810a" + }, + { + "path": "wooden_fish/images/6.png", + "hash": "25d5ae7ef4a3cd3f4443f632fdd968e7" + }, + { + "path": "wooden_fish/images/60.png", + "hash": "e834e553cc5f24fbbe99c8dbd92d440e" + }, + { + "path": "wooden_fish/images/61.png", + "hash": "45491e4be378af118a0d55628837ae64" + }, + { + "path": "wooden_fish/images/62.png", + "hash": "d71f36fca51f8713105c1d2a855b89bb" + }, + { + "path": "wooden_fish/images/63.png", + "hash": "aeef95bbadaa5fb5f0e5845a96d82dbf" + }, + { + "path": "wooden_fish/images/64.png", + "hash": "8c2465ab4495a49e3be4fdc8a0e1c773" + }, + { + "path": "wooden_fish/images/65.png", + "hash": "c09e998c676b8d1982a1db064823bf6e" + }, + { + "path": "wooden_fish/images/7.png", + "hash": "01a98a53bda386901f09ed2e146c0c3c" + }, + { + "path": "wooden_fish/images/8.png", + "hash": "e16848c937770c31d310aa71661558aa" + }, + { + "path": "wooden_fish/images/9.png", + "hash": "64d52829d84b8f4a1aa98c0248be1380" + }, + { + "path": "worship/images/0.png", + "hash": "bd2f7121444b63365141cb292da532d5" + }, + { + "path": "worship/images/1.png", + "hash": "6145ed9f4c81b253d53fdedab1a63886" + }, + { + "path": "worship/images/2.png", + "hash": "c5e679727b7a5d5ca5a5f32bfba08f87" + }, + { + "path": "worship/images/3.png", + "hash": "56174982455efd6b7e7d41711cb72d0c" + }, + { + "path": "worship/images/4.png", + "hash": "661d4a2397fdf2f07d9ef4a48c825ed1" + }, + { + "path": "worship/images/5.png", + "hash": "6eeff86edfbe977e92ee1027ec68b011" + }, + { + "path": "worship/images/6.png", + "hash": "91ccfb9ae60b673e765a5e616c032a77" + }, + { + "path": "worship/images/7.png", + "hash": "e298d69f5c3eade7b741d6ec260f7a15" + }, + { + "path": "worship/images/8.png", + "hash": "9053063bc46e88f0a33c642037dafc39" + }, + { + "path": "worship/images/9.png", + "hash": "3861aae5494bd34ce6937d6224c48915" + }, + { + "path": "wujing/images/0.jpg", + "hash": "c187cfc302ce2c8583e7d18bfdde245a" + }, + { + "path": "youtube/images/corner.png", + "hash": "2f6f9a380e8c3045022d28fed799ff1c" + } +] \ No newline at end of file diff --git a/resources/update_list.py b/resources/update_list.py new file mode 100644 index 0000000000000000000000000000000000000000..d17db930d1276c97bdf5de3bf75c99b1fed9e0a4 --- /dev/null +++ b/resources/update_list.py @@ -0,0 +1,26 @@ +import hashlib +import json +from pathlib import Path + +dir_path = Path(__file__).parent +memes_path = dir_path.parent / "meme_generator" / "memes" + + +def update(): + resource_list = [] + for file in memes_path.rglob("*"): + if not file.is_file() or not file.suffix in [".jpg", ".png", ".gif"]: + continue + resource_list.append( + { + "path": str(file.relative_to(memes_path).as_posix()), + "hash": hashlib.md5(file.read_bytes()).hexdigest(), + } + ) + resource_list.sort(key=lambda i: i["path"]) + with open(dir_path / "resource_list.json", "w", encoding="utf-8") as f: + json.dump(resource_list, f, ensure_ascii=False, indent=4) + + +if __name__ == "__main__": + update()