Skip to content

Instantly share code, notes, and snippets.

@DavideGalilei
Last active November 21, 2022 17:53
Show Gist options
  • Save DavideGalilei/9a51a04e548195da1693168a5e0cfa65 to your computer and use it in GitHub Desktop.
Save DavideGalilei/9a51a04e548195da1693168a5e0cfa65 to your computer and use it in GitHub Desktop.
Python script to convert a LINE sticker pack to Telegram 100x100 px webm emojis (APNG -> GIF -> WEBM)
import json
import shutil
import zipfile
import traceback
import ffmpeg
import requests
from typing import List
from pathlib import Path
from apnggif import apnggif
from bs4 import BeautifulSoup
from concurrent.futures import ThreadPoolExecutor
from werkzeug.utils import secure_filename
root = Path(__file__).parent.resolve(strict=True)
output = root / "output"
STORE_URLS = [
"https://store.line.me/emojishop/product/612c6f89db3e594732668738/en",
"https://store.line.me/emojishop/product/6303552f83603436e7d17900/en",
"https://store.line.me/emojishop/product/62a6fb695658444ccadfba61/en",
"https://store.line.me/emojishop/product/618dd49daf41c21b7b82cb21/en",
]
WORKERS_COUNT = 40
SIZE = (512, 512)
# 512x512: animated sticker
# 100x100: custom emoji
soups = [
BeautifulSoup(requests.get(url).text, features="lxml")
for url in STORE_URLS
]
results: List[Path] = []
for soup in soups:
shutil.rmtree(output, ignore_errors=True)
output.mkdir(exist_ok=True, parents=True)
with ThreadPoolExecutor(WORKERS_COUNT) as pool:
for sticker in soup.find_all("li", {"class": ["FnStickerPreviewItem"]}):
data = json.loads(sticker.get("data-preview"))
anim_id = data["id"]
if data["type"] != "animation":
print(f"Skipping {anim_id!r}: it's not animated")
# print(json.dumps(data, indent=4))
name = f"{anim_id}_animation"
def worker(apng: Path, gif: Path, webm: Path, animation: str):
try:
with apng.open("wb") as apng_file:
r = requests.get(animation)
for chunk in r.iter_content(chunk_size=4096):
apng_file.write(chunk)
apnggif(apng, gif)
# info = ffmpeg.probe(gif)
stream = ffmpeg.input(str(gif)).filter("scale", SIZE[0], SIZE[1]) # emoji size
stream = stream.trim(end=2.95) # max 3 seconds: https://core.telegram.org/stickers#video-requirements
stream = stream.setpts("PTS-STARTPTS")
stream = ffmpeg.output(stream, str(webm), format="webm", threads=16)
# print(stream.compile())
ffmpeg.run(stream, overwrite_output=True, quiet=False)
apng.unlink(missing_ok=True)
gif.unlink(missing_ok=True)
except Exception:
traceback.print_exc()
pool.submit(
worker,
apng=output / f"{name}.apng",
gif=output / f"{name}.gif",
webm=output / f"{name}.webm",
animation=data["animationUrl"],
)
pack_name = soup.find("p", {"data-test": "emoji-name-title"})
zip_path = root / (secure_filename(pack_name.get_text().strip()) + f"_{SIZE[0]}x{SIZE[1]}px.zip")
with zipfile.ZipFile(zip_path, "w") as zipped:
for file in output.glob("*.webm"):
zipped.write(file, arcname=file.relative_to(output))
results.append(zip_path)
print("Done!")
print("\n".join(map(str, results)))
beautifulsoup4
ffmpeg-python
requests
werkzeug
apnggif
lxml
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment