SHA256
WIP: эмодзи-пикер (Telegram-набор) — чекпоинт незавершённой работы
- Эмодзи-пикер: js/components/emoji-picker.js + каталог telegram-emoji-*.js (smileys/people/animals_nature/food_drink/travel_places/activity/objects/ symbols/flags) + 631 ассет в assets/emoji (7.2 МБ). - Интеграция пикера: chat-view.js, app.js, index.html, styles/components.css. - Дев-скрипты генерации превью + THIRD_PARTY_NOTICES. - Работа НЕ завершена (WIP), сохранено как чекпоинт. С origin (+56) пока НЕ мержится — большой мерж будет после завершения, отдельным шагом. - VERSION: client 1.2.263. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,90 @@
|
||||
"""Generate local first-frame PNG previews for the Telegram emoji catalog."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import re
|
||||
import sys
|
||||
from concurrent.futures import ThreadPoolExecutor, as_completed
|
||||
from io import BytesIO
|
||||
from pathlib import Path
|
||||
from urllib.parse import quote
|
||||
from urllib.request import Request, urlopen
|
||||
|
||||
from PIL import Image
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parent
|
||||
CATALOG_DIR = ROOT / "js" / "components"
|
||||
OUTPUT_DIR = ROOT / "assets" / "emoji" / "telegram-previews"
|
||||
ASSET_BASE_URL = "https://raw.githubusercontent.com/Tarikul-Islam-Anik/Telegram-Animated-Emojis/main/"
|
||||
FILE_PATTERN = re.compile(r'"file"\s*:\s*"([^"]+\.webp)"', re.IGNORECASE)
|
||||
|
||||
|
||||
def catalog_files() -> list[str]:
|
||||
files: set[str] = set()
|
||||
for path in sorted(CATALOG_DIR.glob("telegram-emoji-*.js")):
|
||||
files.update(FILE_PATTERN.findall(path.read_text(encoding="utf-8")))
|
||||
return sorted(files)
|
||||
|
||||
|
||||
def preview_path(source_file: str) -> Path:
|
||||
return OUTPUT_DIR / Path(source_file).with_suffix(".png")
|
||||
|
||||
|
||||
def generate_one(source_file: str, force: bool) -> tuple[str, str]:
|
||||
output = preview_path(source_file)
|
||||
if output.exists() and not force:
|
||||
return source_file, "cached"
|
||||
|
||||
url = ASSET_BASE_URL + quote(source_file, safe="/")
|
||||
request = Request(url, headers={"User-Agent": "SHiNE emoji preview generator"})
|
||||
with urlopen(request, timeout=30) as response:
|
||||
payload = response.read()
|
||||
|
||||
with Image.open(BytesIO(payload)) as source:
|
||||
source.seek(0)
|
||||
frame = source.convert("RGBA")
|
||||
frame.thumbnail((96, 96), Image.Resampling.LANCZOS)
|
||||
canvas = Image.new("RGBA", (96, 96), (0, 0, 0, 0))
|
||||
offset = ((96 - frame.width) // 2, (96 - frame.height) // 2)
|
||||
canvas.alpha_composite(frame, offset)
|
||||
output.parent.mkdir(parents=True, exist_ok=True)
|
||||
canvas.save(output, format="PNG", optimize=True)
|
||||
return source_file, "generated"
|
||||
|
||||
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("--force", action="store_true", help="Regenerate existing previews")
|
||||
parser.add_argument("--workers", type=int, default=8)
|
||||
args = parser.parse_args()
|
||||
|
||||
files = catalog_files()
|
||||
if not files:
|
||||
print("No catalog files found", file=sys.stderr)
|
||||
return 1
|
||||
|
||||
print(f"Generating {len(files)} previews into {OUTPUT_DIR}")
|
||||
failures: list[tuple[str, str]] = []
|
||||
generated = cached = 0
|
||||
with ThreadPoolExecutor(max_workers=max(1, args.workers)) as executor:
|
||||
futures = {executor.submit(generate_one, source_file, args.force): source_file for source_file in files}
|
||||
for future in as_completed(futures):
|
||||
source_file = futures[future]
|
||||
try:
|
||||
_, status = future.result()
|
||||
except Exception as error: # noqa: BLE001 - report all asset failures together
|
||||
failures.append((source_file, str(error)))
|
||||
continue
|
||||
generated += status == "generated"
|
||||
cached += status == "cached"
|
||||
|
||||
print(f"Generated: {generated}; cached: {cached}; failed: {len(failures)}")
|
||||
for source_file, error in failures:
|
||||
print(f"FAILED {source_file}: {error}", file=sys.stderr)
|
||||
return 1 if failures else 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
Reference in New Issue
Block a user