添加新的表情包

This commit is contained in:
2026-04-11 21:52:18 +08:00
parent eff9cea804
commit e1bfa7a712
900 changed files with 2425 additions and 1073 deletions

1
.gitignore vendored
View File

@@ -1,3 +1,4 @@
dist/
node_modules/ node_modules/
.wrangler/ .wrangler/
.dev.vars .dev.vars

View File

@@ -1,212 +0,0 @@
#!/usr/bin/env python3
"""
Recursively find *.webp under 原图/ (by default). Animated WebP -> same-stem .gif
(then remove the .webp). Static WebP unchanged. Default 10 worker threads.
"""
from __future__ import annotations
import argparse
import io
import re
import sys
import tempfile
import threading
from concurrent.futures import ThreadPoolExecutor
from pathlib import Path
try:
from PIL import Image, ImageSequence
except ImportError as exc: # pragma: no cover
raise SystemExit(
"This script requires Pillow. Install it with: python3 -m pip install pillow"
) from exc
# 只处理已编号的成品动图(避免误处理未编号的素材)
OUTPUT_NAME_RE = re.compile(r"^(\d+)\.webp$", re.IGNORECASE)
DEFAULT_JOBS = 10
_log_lock = threading.Lock()
def _log(*args, file=sys.stdout, **kwargs) -> None:
with _log_lock:
print(*args, file=file, **kwargs)
def convert_one(path: Path, root: Path, dry_run: bool) -> str:
try:
if not is_animated_webp(path):
return "skip_static"
except OSError as e:
_log(f"[skip] {path}: {e}", file=sys.stderr)
return "error"
gif_path = path.with_suffix(".gif")
rel = path.relative_to(root)
if dry_run:
_log(f"would convert: {rel} -> {gif_path.name}")
return "ok"
try:
webp_to_gif(path, gif_path)
path.unlink()
_log(f"{rel} -> {gif_path.name} (animated)")
return "ok"
except Exception as e:
_log(f"[error] {path}: {e}", file=sys.stderr)
if gif_path.exists():
try:
gif_path.unlink()
except OSError:
pass
return "error"
def is_animated_webp_image(im: Image.Image) -> bool:
if im.format != "WEBP":
return False
return bool(
getattr(im, "is_animated", False) or getattr(im, "n_frames", 1) > 1
)
def is_animated_webp(path: Path) -> bool:
with Image.open(path) as im:
return is_animated_webp_image(im)
def save_animated_webp_image_to_gif(im: Image.Image, dst: Path) -> None:
if im.format != "WEBP":
raise ValueError("expected WebP image")
loop = im.info.get("loop", 0)
frames: list[Image.Image] = []
durations: list[int] = []
for frame in ImageSequence.Iterator(im):
rgba = frame.convert("RGBA")
frames.append(rgba)
d = frame.info.get(
"duration", im.info.get("duration", im.info.get("gif_duration", 100))
)
try:
durations.append(int(d) if d is not None else 100)
except (TypeError, ValueError):
durations.append(100)
if len(frames) == 0:
raise ValueError("no frames")
if len(durations) < len(frames):
durations.extend([durations[-1]] * (len(frames) - len(durations)))
dst.parent.mkdir(parents=True, exist_ok=True)
with tempfile.NamedTemporaryFile(
suffix=".gif", delete=False, dir=dst.parent
) as tmp:
tmp_path = Path(tmp.name)
try:
if len(frames) == 1:
frames[0].save(
tmp_path,
format="GIF",
duration=durations[0],
loop=loop,
)
else:
frames[0].save(
tmp_path,
format="GIF",
save_all=True,
append_images=frames[1:],
duration=durations,
loop=loop,
disposal=2,
)
tmp_path.replace(dst)
finally:
if tmp_path.exists():
try:
tmp_path.unlink()
except OSError:
pass
def webp_bytes_to_gif(data: bytes, dst: Path) -> None:
with Image.open(io.BytesIO(data)) as im:
save_animated_webp_image_to_gif(im, dst)
def webp_to_gif(src: Path, dst: Path) -> None:
with Image.open(src) as im:
save_animated_webp_image_to_gif(im, dst)
def main(argv: list[str]) -> int:
parser = argparse.ArgumentParser(
description=(
"Convert animated WebP under 原图/ to GIF (in place); "
"static WebP unchanged. Only numbered N.webp files are considered."
)
)
parser.add_argument(
"root",
nargs="?",
default="原图",
type=Path,
help="Root directory to scan (default: 原图).",
)
parser.add_argument(
"--dry-run",
action="store_true",
help="Only print what would be converted.",
)
parser.add_argument(
"--all-webp",
action="store_true",
help="Process any *.webp, not only numbered N.webp.",
)
parser.add_argument(
"-j",
"--jobs",
type=int,
default=DEFAULT_JOBS,
metavar="N",
help=f"Parallel worker threads (default: {DEFAULT_JOBS}).",
)
args = parser.parse_args(argv)
root = args.root.expanduser().resolve()
if not root.is_dir():
print(f"Not a directory: {root}", file=sys.stderr)
return 1
webp_files = sorted(root.rglob("*.webp"))
converted = 0
skipped_static = 0
skipped_name = 0
errors = 0
candidates: list[Path] = []
for path in webp_files:
if not args.all_webp and not OUTPUT_NAME_RE.match(path.name):
skipped_name += 1
continue
candidates.append(path)
workers = max(1, args.jobs)
with ThreadPoolExecutor(max_workers=workers) as pool:
for outcome in pool.map(
lambda p: convert_one(p, root, args.dry_run), candidates
):
if outcome == "ok":
converted += 1
elif outcome == "skip_static":
skipped_static += 1
else:
errors += 1
print(
f"Done. converted={converted}, skipped_static_webp={skipped_static}, "
f"skipped_unnumbered={skipped_name}, errors={errors} ({workers} workers)"
)
return 0 if errors == 0 else 1
if __name__ == "__main__":
raise SystemExit(main(sys.argv[1:]))

View File

@@ -1,348 +0,0 @@
#!/usr/bin/env python3
from __future__ import annotations
import argparse
import io
import re
import sys
import zipfile
from concurrent.futures import ThreadPoolExecutor
from dataclasses import dataclass
from pathlib import Path, PurePosixPath
from typing import Callable
try:
from PIL import Image, ImageSequence
except ImportError as exc: # pragma: no cover
raise SystemExit(
"This script requires Pillow. Install it with: python3 -m pip install pillow"
) from exc
from animated_webp_to_gif import webp_bytes_to_gif
IMAGE_EXTENSIONS = {
".avif",
".bmp",
".gif",
".jfif",
".jpeg",
".jpg",
".jpe",
".png",
".tif",
".tiff",
".webp",
}
# 已编号的成品1.webp、42.gif不参与转换用于各子文件夹内续号
OUTPUT_NAME_RE = re.compile(r"^(\d+)\.(webp|gif)$", re.IGNORECASE)
DEFAULT_SOURCE_DIR = Path("原图")
@dataclass(frozen=True)
class Job:
source_label: str
target_dir: Path
index: int
loader: Callable[[], bytes]
# 磁盘上的源文件转换成功后删除zip 成员为 None。与输出路径相同时不会删。
source_file: Path | None = None
def is_image_path(path: Path) -> bool:
return path.is_file() and path.suffix.lower() in IMAGE_EXTENSIONS
def is_numbered_asset_file(path: Path) -> bool:
return path.is_file() and bool(OUTPUT_NAME_RE.match(path.name))
def max_index_in_directory(directory: Path) -> int:
highest = 0
if not directory.is_dir():
return 0
for p in directory.iterdir():
if not p.is_file():
continue
m = OUTPUT_NAME_RE.match(p.name)
if m:
highest = max(highest, int(m.group(1)))
return highest
def convert_to_webp(data: bytes) -> tuple[bytes, bool]:
with Image.open(io.BytesIO(data)) as im:
is_animated = bool(
getattr(im, "is_animated", False) or getattr(im, "n_frames", 1) > 1
)
if is_animated:
frames = []
durations = []
loop = im.info.get("loop", 0)
for frame in ImageSequence.Iterator(im):
frames.append(frame.convert("RGBA"))
durations.append(frame.info.get("duration", im.info.get("duration", 100)))
output = io.BytesIO()
frames[0].save(
output,
format="WEBP",
save_all=True,
append_images=frames[1:],
duration=durations,
loop=loop,
lossless=True,
method=4,
)
return output.getvalue(), True
output = io.BytesIO()
im.convert("RGBA").save(output, format="WEBP", lossless=True, method=4)
return output.getvalue(), False
def is_animated_gif(data: bytes) -> bool:
with Image.open(io.BytesIO(data)) as im:
if im.format != "GIF":
return False
n_frames = getattr(im, "n_frames", 1)
return bool(getattr(im, "is_animated", False) or n_frames > 1)
def is_animated_webp_bytes(data: bytes) -> bool:
with Image.open(io.BytesIO(data)) as im:
if im.format != "WEBP":
return False
return bool(
getattr(im, "is_animated", False) or getattr(im, "n_frames", 1) > 1
)
def _read_zip_member(zip_path: Path, member_name: str) -> bytes:
with zipfile.ZipFile(zip_path) as zf:
return zf.read(member_name)
def collect_directory_jobs(
source_root: Path, output_root: Path, *, delete_sources: bool
) -> list[Job]:
grouped: dict[Path, list[Path]] = {}
for path in sorted(source_root.rglob("*")):
if not is_image_path(path):
continue
if is_numbered_asset_file(path):
continue
grouped.setdefault(path.parent, []).append(path)
jobs: list[Job] = []
for directory in sorted(grouped, key=lambda p: str(p)):
files = sorted(grouped[directory], key=lambda p: p.name)
target_dir = output_root / directory.relative_to(source_root)
next_i = max_index_in_directory(target_dir) + 1
for path in files:
jobs.append(
Job(
source_label=path.relative_to(source_root).as_posix(),
target_dir=target_dir,
index=next_i,
loader=lambda p=path: p.read_bytes(),
source_file=path if delete_sources else None,
)
)
next_i += 1
return jobs
def collect_zip_jobs(zip_path: Path, output_root: Path) -> list[Job]:
grouped: dict[PurePosixPath, list[PurePosixPath]] = {}
with zipfile.ZipFile(zip_path) as zf:
for info in zf.infolist():
if info.is_dir():
continue
member_path = PurePosixPath(info.filename)
suf = member_path.suffix.lower()
if suf not in IMAGE_EXTENSIONS:
continue
if OUTPUT_NAME_RE.match(member_path.name):
continue
grouped.setdefault(member_path.parent, []).append(member_path)
jobs: list[Job] = []
for directory in sorted(grouped, key=lambda p: str(p)):
files = sorted(grouped[directory], key=lambda p: str(p.name))
target_dir = output_root / Path(str(directory))
next_i = max_index_in_directory(target_dir) + 1
for member_path in files:
jobs.append(
Job(
source_label=member_path.as_posix(),
target_dir=target_dir,
index=next_i,
loader=lambda name=member_path.as_posix(), zp=zip_path: _read_zip_member(
zp, name
),
)
)
next_i += 1
return jobs
def _remove_source_if_any(job: Job, out: Path, kind: str) -> str:
if job.source_file is None:
return kind
try:
src = job.source_file.resolve()
if src == out.resolve():
return kind
src.unlink()
return f"{kind}; removed-source"
except OSError as exc:
print(
f"[warn] ok: {out} ({kind}) but could not delete {job.source_file}: {exc}",
file=sys.stderr,
)
return kind
def run_job(job: Job) -> tuple[bool, str, Path, str | None, str | None]:
try:
data = job.loader()
job.target_dir.mkdir(parents=True, exist_ok=True)
if is_animated_gif(data):
out = job.target_dir / f"{job.index}.gif"
out.write_bytes(data)
kind = _remove_source_if_any(job, out, "gif-animated")
return True, job.source_label, out, kind, None
if is_animated_webp_bytes(data):
out = job.target_dir / f"{job.index}.gif"
webp_bytes_to_gif(data, out)
kind = _remove_source_if_any(job, out, "animated-webp->gif")
return True, job.source_label, out, kind, None
webp_data, animated = convert_to_webp(data)
out = job.target_dir / f"{job.index}.webp"
out.write_bytes(webp_data)
base = "animated-webp" if animated else "static-webp"
kind = _remove_source_if_any(job, out, base)
return True, job.source_label, out, kind, None
except Exception as exc:
return False, job.source_label, job.target_dir / f"{job.index}.?", None, str(exc)
def main(argv: list[str]) -> int:
parser = argparse.ArgumentParser(
description=(
"Read unnumbered images from 原图/ (by default), write numbered *.webp / *.gif "
"into the same folder tree (in-place). Subfolders mirror 原图. "
"Animated GIFs and animated WebPs become .gif; static images become lossless .webp. "
"Next index per folder = max existing N in that folder + 1. "
"By default unnumbered source files are deleted after a successful conversion."
)
)
parser.add_argument(
"source",
nargs="?",
default=None,
type=Path,
help=f"Source root folder, single image, or .zip (default: {DEFAULT_SOURCE_DIR.as_posix()}/).",
)
parser.add_argument(
"-o",
"--output",
default=None,
dest="output_root",
type=Path,
help="Output root (default: same as source for folders/single file; for .zip default: <zip-stem>/).",
)
parser.add_argument(
"-j",
"--jobs",
type=int,
default=10,
help="Number of worker threads (default: 10).",
)
parser.add_argument(
"--keep-sources",
action="store_true",
help="Keep unnumbered source files after conversion (default: delete them).",
)
args = parser.parse_args(argv)
cwd = Path.cwd()
source = (
args.source.expanduser().resolve()
if args.source is not None
else (cwd / DEFAULT_SOURCE_DIR).resolve()
)
jobs: list[Job] = []
if source.is_dir():
out_root = (
args.output_root.expanduser().resolve()
if args.output_root is not None
else source
)
out_root.mkdir(parents=True, exist_ok=True)
jobs = collect_directory_jobs(
source, out_root, delete_sources=not args.keep_sources
)
elif source.is_file() and source.suffix.lower() == ".zip":
out_root = (
args.output_root.expanduser().resolve()
if args.output_root is not None
else (source.parent / source.stem).resolve()
)
out_root.mkdir(parents=True, exist_ok=True)
jobs = collect_zip_jobs(source, out_root)
elif is_image_path(source):
if is_numbered_asset_file(source):
print(f"Skip already-numbered file: {source}", file=sys.stderr)
return 0
target_dir = (
args.output_root.expanduser().resolve()
if args.output_root is not None
else source.parent.resolve()
)
target_dir.mkdir(parents=True, exist_ok=True)
idx = max_index_in_directory(target_dir) + 1
jobs = [
Job(
source_label=source.name,
target_dir=target_dir,
index=idx,
loader=lambda p=source: p.read_bytes(),
source_file=source if not args.keep_sources else None,
)
]
else:
print(f"Not a folder, image, or zip: {source}", file=sys.stderr)
return 1
jobs.sort(key=lambda job: (str(job.target_dir), job.index))
if not jobs:
print(f"No new images to convert under: {source}")
return 0
converted = 0
skipped = 0
with ThreadPoolExecutor(max_workers=max(1, args.jobs)) as pool:
for ok, label, target, kind, error in pool.map(run_job, jobs):
if ok:
print(f"{label} -> {target} ({kind})")
converted += 1
else:
print(f"[skip] {label}: {error}", file=sys.stderr)
skipped += 1
print(f"Done. converted={converted}, skipped={skipped}")
return 0 if converted else 1
if __name__ == "__main__":
raise SystemExit(main(sys.argv[1:]))

View File

@@ -3,8 +3,11 @@
"private": true, "private": true,
"scripts": { "scripts": {
"build": "node scripts/generate-manifest.mjs", "build": "node scripts/generate-manifest.mjs",
"dev": "npm run build && npx --yes serve public -p 8788", "build:manifest-from-source": "node scripts/generate-manifest.mjs --manifest-from-source",
"deploy": "npm run build && wrangler pages deploy public --project-name=meme-api" "dev": "npm run build && npx --yes serve dist -p 8788",
"deploy": "npm run deploy:upload",
"deploy:upload": "npm run build && wrangler pages deploy dist --project-name=meme-api",
"deploy:preview": "npm run build && wrangler pages deploy dist --project-name=meme-api --branch preview"
}, },
"devDependencies": { "devDependencies": {
"wrangler": "^4.6.0" "wrangler": "^4.6.0"

View File

@@ -17,7 +17,7 @@
</head> </head>
<body> <body>
<h1>meme-api</h1> <h1>meme-api</h1>
<p class="muted">静态站点(<a href="https://pages.cloudflare.com/">Cloudflare Pages</a>)。素材在仓库 <code>原图/</code> 内编号生成 <code>*.webp</code> / <code>*.gif</code><code>npm run build</code> <code>原图/</code> 镜像<code>public/meme/</code> 用于部署。直链可外部引用JSON 已配置 CORS。</p> <p class="muted">静态站点(<a href="https://pages.cloudflare.com/">Cloudflare Pages</a>)。 <code>原图/</code> 放未编号素材后运行 <code>python 转化.py</code>,输出到 <code>原图-已处理/</code>。将成品拷入 <code>public/meme/</code> 后运行 <code>npm run build</code>:只生成 <code>memes.json</code> 并把 <code>public/</code> 复制<code>dist/</code><strong>不会</strong>改写 <code>public/meme/</code>。清单默认按 <code>public/meme/</code> 扫描;若尚未拷素材则回退扫描 <code>原图-已处理/</code>(无则 <code>原图/</code>)。需强制按源目录生成清单时用 <code>npm run build:manifest-from-source</code>。部署目录为 <code>dist/</code>。直链可外部引用JSON 已配置 CORS。</p>
<p><a href="/memes/">浏览图库(分页、按文件夹分类、点击复制链接)</a></p> <p><a href="/memes/">浏览图库(分页、按文件夹分类、点击复制链接)</a></p>
<h2>获取表情包列表</h2> <h2>获取表情包列表</h2>
@@ -26,16 +26,17 @@
<pre id="curl-memes">curl -sS ""</pre> <pre id="curl-memes">curl -sS ""</pre>
<h2>直链访问单张表情包</h2> <h2>直链访问单张表情包</h2>
<p>部署后文件在 <code>public/meme/&lt;分类&gt;/</code>(与仓库 <code>原图/&lt;分类&gt;/</code> 内容一致),对应 URL</p> <p>站点根目录对应构建产物 <code>dist/</code>(本地从 <code>public/</code> 同步而来);表情文件在 <code>/meme/&lt;分类&gt;/</code>,对应 URL</p>
<p><code>GET /meme/&lt;分类&gt;/&lt;文件名&gt;.webp</code><code>.gif</code></p> <p><code>GET /meme/&lt;分类&gt;/&lt;文件名&gt;.webp</code><code>.gif</code></p>
<pre id="curl-example">https://example.com/meme/%E7%99%BD%E8%89%B2%E5%B0%8F%E4%BA%BA/1.webp</pre> <pre id="curl-example">https://example.com/meme/%E7%99%BD%E8%89%B2%E5%B0%8F%E4%BA%BA/1.webp</pre>
<p class="muted">中文或多段路径请使用编码后的 URL浏览器会自动处理图库「复制链接」亦为已编码地址</p> <p class="muted">中文或多段路径请使用编码后的 URL浏览器会自动处理图库「复制链接」亦为已编码地址</p>
<h2>维护流程</h2> <h2>维护流程</h2>
<ol> <ol>
<li><code>原图/&lt;分类&gt;/</code> 放入未编号素材;运行 <code>python convert_to_webp.py</code>(就地生成编号文件,成功后会<strong>删除</strong>这些未编号源图;若需保留源文件可加 <code>--keep-sources</code></li> <li><code>原图/&lt;分类&gt;/</code> 放入未编号素材;运行 <code>python 转化.py</code>,在 <code>原图-已处理/</code> 生成编号 <code>.webp</code>/<code>.gif</code> 并限最长边(默认 300px默认<strong>保留</strong> <code>原图/</code> 源文件,清理可加 <code>--delete-sources</code></li>
<li>若仍有「动图 WebP」编号文件,可<code>原图/</code> 下运行 <code>python animated_webp_to_gif.py</code> 转为 <code>.gif</code></li> <li>动图 WebP<code>python 转化.py</code> 转换流程中转为 GIF</li>
<li><code>npm run build</code> <code>原图/</code> 镜像到 <code>public/meme/</code> 并生成 <code>public/memes.json</code>,再部署 <code>public/</code></li> <li><code>npm run build</code>:生成 <code>dist/</code>(含 <code>dist/memes.json</code>)。请先把 <code>原图-已处理/</code> 拷入 <code>public/meme/</code> 再构建;构建脚本不会覆盖 <code>public/meme/</code></li>
<li><strong>上线(生产)</strong>:项目在 Cloudflare 已连接 Git 时,由推送生产分支触发云端构建:<code>npm run build</code><code>git add/commit/push</code>;仪表盘「构建命令」填 <code>npm run build</code>,「构建输出目录」填 <code>dist</code><code>wrangler pages deploy dist</code> 在 Git 项目里常显示为<strong>预览</strong>,属正常。</li>
</ol> </ol>
<script> <script>
(function () { (function () {

View File

BIN
public/meme/ai/19.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

BIN
public/meme/ai/20.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

BIN
public/meme/ai/21.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

BIN
public/meme/cheems/2.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

BIN
public/meme/cheems/3.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

BIN
public/meme/cheems/4.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

BIN
public/meme/cheems/5.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

BIN
public/meme/emoji/85.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

BIN
public/meme/emoji/86.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

BIN
public/meme/emoji/87.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

BIN
public/meme/emoji/88.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

BIN
public/meme/emoji/89.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 476 KiB

BIN
public/meme/emoji/90.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

BIN
public/meme/emoji/91.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

BIN
public/meme/emoji/92.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 529 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 326 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 737 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 618 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 293 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 513 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 191 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 206 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 181 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 153 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 286 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 213 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 183 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 122 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 278 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 132 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 336 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 137 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 99 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 913 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 117 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 344 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 318 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Some files were not shown because too many files have changed in this diff Show More