add conversion for stickers
This commit is contained in:
parent
706852e425
commit
700f8f8ec4
2 changed files with 75 additions and 13 deletions
|
|
@ -257,7 +257,6 @@ async def migrate_messages(
|
||||||
if hasattr(format_val, 'name'): # discord.py StickerFormat enum
|
if hasattr(format_val, 'name'): # discord.py StickerFormat enum
|
||||||
ext = format_val.name.lower()
|
ext = format_val.name.lower()
|
||||||
elif isinstance(format_val, int):
|
elif isinstance(format_val, int):
|
||||||
# Map common StickerFormat values if it's an int
|
|
||||||
format_map = {1: 'png', 2: 'apng', 3: 'lottie', 4: 'gif'}
|
format_map = {1: 'png', 2: 'apng', 3: 'lottie', 4: 'gif'}
|
||||||
ext = format_map.get(format_val, 'png')
|
ext = format_map.get(format_val, 'png')
|
||||||
else:
|
else:
|
||||||
|
|
@ -265,25 +264,53 @@ async def migrate_messages(
|
||||||
|
|
||||||
logger.debug(f"Determined sticker extension: {ext}")
|
logger.debug(f"Determined sticker extension: {ext}")
|
||||||
|
|
||||||
# Handle Lottie (json) -> Convert to GIF
|
# Fluxer: Convert animated stickers to WebP
|
||||||
|
# Lottie (json) → GIF (via lottie lib) → WebP (via Pillow)
|
||||||
if ext == 'lottie':
|
if ext == 'lottie':
|
||||||
if HAS_LOTTIE:
|
if HAS_LOTTIE:
|
||||||
try:
|
try:
|
||||||
logger.debug(f"Converting Lottie sticker {s.name} to GIF...")
|
logger.debug(f"Converting Lottie sticker {s.name} to WebP...")
|
||||||
lottie_data = json.loads(sticker_data)
|
lottie_data = json.loads(sticker_data)
|
||||||
animation = Animation.load(lottie_data)
|
animation = Animation.load(lottie_data)
|
||||||
output = io.BytesIO()
|
gif_buf = io.BytesIO()
|
||||||
export_gif(animation, output)
|
export_gif(animation, gif_buf)
|
||||||
sticker_data = output.getvalue()
|
gif_buf.seek(0)
|
||||||
ext = 'gif'
|
# GIF → WebP via Pillow
|
||||||
logger.debug(f"Successfully converted Lottie sticker {s.name} to GIF")
|
from PIL import Image
|
||||||
|
img = Image.open(gif_buf)
|
||||||
|
webp_buf = io.BytesIO()
|
||||||
|
if getattr(img, 'n_frames', 1) > 1:
|
||||||
|
img.save(webp_buf, format='WEBP', save_all=True, loop=0)
|
||||||
|
else:
|
||||||
|
img.save(webp_buf, format='WEBP')
|
||||||
|
sticker_data = webp_buf.getvalue()
|
||||||
|
ext = 'webp'
|
||||||
|
logger.debug(f"Successfully converted Lottie sticker {s.name} to WebP")
|
||||||
except Exception as conv_err:
|
except Exception as conv_err:
|
||||||
logger.error(f"Failed to convert Lottie sticker {s.name} to GIF: {conv_err}")
|
logger.error(f"Failed to convert Lottie sticker {s.name} to WebP: {conv_err}")
|
||||||
ext = 'json' # Fallback to json if conversion fails
|
ext = 'json'
|
||||||
else:
|
else:
|
||||||
logger.warning(f"Lottie library not available, sending sticker {s.name} as raw JSON")
|
logger.warning(f"Lottie library not available, sending sticker {s.name} as raw JSON")
|
||||||
ext = 'json'
|
ext = 'json'
|
||||||
|
|
||||||
|
# APNG / GIF → WebP (via Pillow)
|
||||||
|
elif ext in ('apng', 'gif'):
|
||||||
|
try:
|
||||||
|
logger.debug(f"Converting {ext.upper()} sticker {s.name} to WebP...")
|
||||||
|
from PIL import Image
|
||||||
|
img = Image.open(io.BytesIO(sticker_data))
|
||||||
|
webp_buf = io.BytesIO()
|
||||||
|
if getattr(img, 'n_frames', 1) > 1:
|
||||||
|
img.save(webp_buf, format='WEBP', save_all=True, loop=0)
|
||||||
|
else:
|
||||||
|
img.save(webp_buf, format='WEBP')
|
||||||
|
sticker_data = webp_buf.getvalue()
|
||||||
|
ext = 'webp'
|
||||||
|
logger.debug(f"Successfully converted sticker {s.name} to WebP")
|
||||||
|
except Exception as conv_err:
|
||||||
|
logger.error(f"Failed to convert {ext.upper()} sticker {s.name} to WebP: {conv_err}")
|
||||||
|
# Keep original format as fallback
|
||||||
|
|
||||||
filename = f"sticker_{s.name}_{s.id}.{ext}"
|
filename = f"sticker_{s.name}_{s.id}.{ext}"
|
||||||
files.append({"filename": filename, "data": sticker_data})
|
files.append({"filename": filename, "data": sticker_data})
|
||||||
stats["attachments"] += 1
|
stats["attachments"] += 1
|
||||||
|
|
|
||||||
|
|
@ -260,7 +260,6 @@ async def migrate_messages(
|
||||||
if hasattr(format_val, 'name'): # discord.py StickerFormat enum
|
if hasattr(format_val, 'name'): # discord.py StickerFormat enum
|
||||||
ext = format_val.name.lower()
|
ext = format_val.name.lower()
|
||||||
elif isinstance(format_val, int):
|
elif isinstance(format_val, int):
|
||||||
# Map common StickerFormat values if it's an int
|
|
||||||
format_map = {1: 'png', 2: 'apng', 3: 'lottie', 4: 'gif'}
|
format_map = {1: 'png', 2: 'apng', 3: 'lottie', 4: 'gif'}
|
||||||
ext = format_map.get(format_val, 'png')
|
ext = format_map.get(format_val, 'png')
|
||||||
else:
|
else:
|
||||||
|
|
@ -268,7 +267,8 @@ async def migrate_messages(
|
||||||
|
|
||||||
logger.debug(f"Determined sticker extension: {ext}")
|
logger.debug(f"Determined sticker extension: {ext}")
|
||||||
|
|
||||||
# Handle Lottie (json) -> Convert to GIF
|
# Stoat: Convert animated stickers to GIF
|
||||||
|
# Lottie (json) → GIF (via lottie lib)
|
||||||
if ext == 'lottie':
|
if ext == 'lottie':
|
||||||
if HAS_LOTTIE:
|
if HAS_LOTTIE:
|
||||||
try:
|
try:
|
||||||
|
|
@ -282,11 +282,46 @@ async def migrate_messages(
|
||||||
logger.debug(f"Successfully converted Lottie sticker {s.name} to GIF")
|
logger.debug(f"Successfully converted Lottie sticker {s.name} to GIF")
|
||||||
except Exception as conv_err:
|
except Exception as conv_err:
|
||||||
logger.error(f"Failed to convert Lottie sticker {s.name} to GIF: {conv_err}")
|
logger.error(f"Failed to convert Lottie sticker {s.name} to GIF: {conv_err}")
|
||||||
ext = 'json' # Fallback to json if conversion fails
|
ext = 'json'
|
||||||
else:
|
else:
|
||||||
logger.warning(f"Lottie library not available, sending sticker {s.name} as raw JSON")
|
logger.warning(f"Lottie library not available, sending sticker {s.name} as raw JSON")
|
||||||
ext = 'json'
|
ext = 'json'
|
||||||
|
|
||||||
|
# APNG → GIF (via Pillow, with proper frame disposal)
|
||||||
|
elif ext == 'apng':
|
||||||
|
try:
|
||||||
|
logger.debug(f"Converting APNG sticker {s.name} to GIF...")
|
||||||
|
from PIL import Image
|
||||||
|
img = Image.open(io.BytesIO(sticker_data))
|
||||||
|
gif_buf = io.BytesIO()
|
||||||
|
if getattr(img, 'n_frames', 1) > 1:
|
||||||
|
# Extract each frame onto a clean background to avoid overlap
|
||||||
|
frames = []
|
||||||
|
durations = []
|
||||||
|
for frame_idx in range(img.n_frames):
|
||||||
|
img.seek(frame_idx)
|
||||||
|
# Create fresh background and composite the frame
|
||||||
|
canvas = Image.new('RGBA', img.size, (0, 0, 0, 0))
|
||||||
|
frame = img.convert('RGBA')
|
||||||
|
canvas.paste(frame, (0, 0), frame)
|
||||||
|
# Convert to palette mode for GIF
|
||||||
|
frames.append(canvas.convert('RGBA'))
|
||||||
|
durations.append(img.info.get('duration', 100))
|
||||||
|
# Save with disposal=2 (clear frame before next)
|
||||||
|
frames[0].save(
|
||||||
|
gif_buf, format='GIF', save_all=True,
|
||||||
|
append_images=frames[1:], loop=0,
|
||||||
|
duration=durations, disposal=2, transparency=0
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
img.save(gif_buf, format='GIF')
|
||||||
|
sticker_data = gif_buf.getvalue()
|
||||||
|
ext = 'gif'
|
||||||
|
logger.debug(f"Successfully converted APNG sticker {s.name} to GIF")
|
||||||
|
except Exception as conv_err:
|
||||||
|
logger.error(f"Failed to convert APNG sticker {s.name} to GIF: {conv_err}")
|
||||||
|
# Keep original apng as fallback
|
||||||
|
|
||||||
filename = f"sticker_{s.name}_{s.id}.{ext}"
|
filename = f"sticker_{s.name}_{s.id}.{ext}"
|
||||||
files.append({"filename": filename, "data": sticker_data})
|
files.append({"filename": filename, "data": sticker_data})
|
||||||
stats["attachments"] += 1
|
stats["attachments"] += 1
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue