Spaces:
Running on Zero
Running on Zero
Download app.py from donotthink/meme: direct link, hf CLI and curl.
- Browser
- Download file 5.11 kB
-
https://huggingface.co/spaces/donotthink/meme/resolve/main/app.py
- Command line
-
hf download hf://spaces/donotthink/meme/app.py
-
curl -L -o app.py https://huggingface.co/spaces/donotthink/meme/resolve/main/app.py
5.11 kB
| import io | |
| import os | |
| import shutil | |
| import subprocess | |
| import urllib.request | |
| from concurrent.futures import ThreadPoolExecutor, as_completed | |
| from pathlib import Path | |
| # ---- 自动拉取 Xet 指针文件 ---- | |
| def _is_valid_image(fpath): | |
| try: | |
| with open(fpath, "rb") as f: | |
| head = f.read(16) | |
| if len(head) < 4: | |
| return False | |
| if head[:8] == b"\x89PNG\r\n\x1a\n": | |
| return True | |
| if head[:3] == b"\xff\xd8\xff": | |
| return True | |
| if head[:6] in (b"GIF87a", b"GIF89a"): | |
| return True | |
| if head[:4] == b"RIFF" and head[8:12] == b"WEBP": | |
| return True | |
| if head[:2] == b"BM": | |
| return True | |
| return False | |
| except Exception: | |
| return False | |
| def _is_valid_image_bytes(data): | |
| if len(data) < 4: | |
| return False | |
| if data[:8] == b"\x89PNG\r\n\x1a\n": | |
| return True | |
| if data[:3] == b"\xff\xd8\xff": | |
| return True | |
| if data[:6] in (b"GIF87a", b"GIF89a"): | |
| return True | |
| if data[:4] == b"RIFF" and data[8:12] == b"WEBP": | |
| return True | |
| if data[:2] == b"BM": | |
| return True | |
| return False | |
| def _download_one(urls, fpath): | |
| """尝试从多个源下载,成功一个即返回。""" | |
| if isinstance(urls, str): | |
| urls = [urls] | |
| for url in urls: | |
| for attempt in range(3): | |
| try: | |
| req = urllib.request.Request(url, headers={"User-Agent": "Mozilla/5.0"}) | |
| with urllib.request.urlopen(req, timeout=120) as resp: | |
| data = resp.read() | |
| if data and _is_valid_image_bytes(data): | |
| with open(fpath, "wb") as f: | |
| f.write(data) | |
| return True | |
| except Exception: | |
| if attempt < 2: | |
| import time | |
| time.sleep(1) | |
| return False | |
| def _materialize_xet(): | |
| repo_id = "donotthink/meme" | |
| hf_base = f"https://huggingface.co/spaces/{repo_id}/resolve/main" | |
| gh_base = "https://raw.githubusercontent.com/Memes02/meme-generator/main" | |
| exts = (".png", ".jpg", ".jpeg", ".gif", ".webp", ".bmp") | |
| need_download = [] | |
| total = 0 | |
| for root, _dirs, files in os.walk("meme_generator"): | |
| for fname in files: | |
| if not fname.lower().endswith(exts): | |
| continue | |
| total += 1 | |
| fpath = os.path.join(root, fname) | |
| if not _is_valid_image(fpath): | |
| need_download.append(fpath) | |
| print(f"[xet] scanned {total} images, {len(need_download)} need download", flush=True) | |
| if not need_download: | |
| return | |
| count = 0 | |
| failed_files = [] | |
| with ThreadPoolExecutor(max_workers=6) as executor: | |
| futures = {} | |
| for fpath in need_download: | |
| rel = os.path.relpath(fpath, ".").replace(os.sep, "/") | |
| # 先试 HF,失败再试 GitHub | |
| urls = [f"{hf_base}/{rel}", f"{gh_base}/{rel}"] | |
| futures[executor.submit(_download_one, urls, fpath)] = fpath | |
| for fut in as_completed(futures): | |
| fpath = futures[fut] | |
| if fut.result(): | |
| count += 1 | |
| else: | |
| failed_files.append(fpath) | |
| if (count + len(failed_files)) % 100 == 0: | |
| print(f"[xet] progress {count + len(failed_files)}/{len(need_download)}", flush=True) | |
| print(f"[xet] materialized {count} files, {len(failed_files)} failures", flush=True) | |
| if failed_files: | |
| print(f"[xet] failed files ({len(failed_files)}):", flush=True) | |
| for f in failed_files[:30]: | |
| print(f" - {f}", flush=True) | |
| if len(failed_files) > 30: | |
| print(f" ... and {len(failed_files) - 30} more", flush=True) | |
| _materialize_xet() | |
| # ---- 注册字体 ---- | |
| _font_src = Path("resources/fonts") | |
| _font_dst = Path.home() / ".fonts" | |
| if _font_src.exists(): | |
| _font_dst.mkdir(parents=True, exist_ok=True) | |
| for _f in _font_src.iterdir(): | |
| if _f.is_file(): | |
| shutil.copy2(_f, _font_dst / _f.name) | |
| subprocess.run(["fc-cache", "-fv"], capture_output=True) | |
| # ---- ZeroGPU 兼容层:必须有 @spaces.GPU 装饰的函数并实际调用,否则 ZeroGPU 会停止应用 ---- | |
| import spaces | |
| import asyncio | |
| def _zero_gpu_warmup(): | |
| """占位函数,让 ZeroGPU 检测到 GPU 函数调用。实际不使用 GPU。""" | |
| return "warmup-ok" | |
| # 启动前实际调用一次,触发 spaces 库的 GPU 初始化 | |
| try: | |
| _r = _zero_gpu_warmup() | |
| if asyncio.iscoroutine(_r): | |
| asyncio.run(_r) | |
| print("[zero-gpu] warmup done", flush=True) | |
| except Exception as _e: | |
| print(f"[zero-gpu] warmup skipped: {_e}", flush=True) | |
| # ---- 启动 meme_generator 原生 FastAPI(无 Gradio,无 CSRF)---- | |
| import uvicorn | |
| from meme_generator.app import app, register_routers | |
| register_routers() | |
| print("[meme] API server starting on 0.0.0.0:7860", flush=True) | |
| print("[meme] endpoints: /memes/keys, /memes/{key}/, /memes/{key}/info, /docs", flush=True) | |
| if __name__ == "__main__": | |
| uvicorn.run(app, host="0.0.0.0", port=7860) | |