| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136 |
- #!/usr/bin/env python3
- """
- Validators - Check if GIFs meet Slack's requirements.
- These validators help ensure your GIFs meet Slack's size and dimension constraints.
- """
- from pathlib import Path
- def validate_gif(
- gif_path: str | Path, is_emoji: bool = True, verbose: bool = True
- ) -> tuple[bool, dict]:
- """
- Validate GIF for Slack (dimensions, size, frame count).
- Args:
- gif_path: Path to GIF file
- is_emoji: True for emoji (128x128 recommended), False for message GIF
- verbose: Print validation details
- Returns:
- Tuple of (passes: bool, results: dict with all details)
- """
- from PIL import Image
- gif_path = Path(gif_path)
- if not gif_path.exists():
- return False, {"error": f"File not found: {gif_path}"}
- # Get file size
- size_bytes = gif_path.stat().st_size
- size_kb = size_bytes / 1024
- size_mb = size_kb / 1024
- # Get dimensions and frame info
- try:
- with Image.open(gif_path) as img:
- width, height = img.size
- # Count frames
- frame_count = 0
- try:
- while True:
- img.seek(frame_count)
- frame_count += 1
- except EOFError:
- pass
- # Get duration
- try:
- duration_ms = img.info.get("duration", 100)
- total_duration = (duration_ms * frame_count) / 1000
- fps = frame_count / total_duration if total_duration > 0 else 0
- except:
- total_duration = None
- fps = None
- except Exception as e:
- return False, {"error": f"Failed to read GIF: {e}"}
- # Validate dimensions
- if is_emoji:
- optimal = width == height == 128
- acceptable = width == height and 64 <= width <= 128
- dim_pass = acceptable
- else:
- aspect_ratio = (
- max(width, height) / min(width, height)
- if min(width, height) > 0
- else float("inf")
- )
- dim_pass = aspect_ratio <= 2.0 and 320 <= min(width, height) <= 640
- results = {
- "file": str(gif_path),
- "passes": dim_pass,
- "width": width,
- "height": height,
- "size_kb": size_kb,
- "size_mb": size_mb,
- "frame_count": frame_count,
- "duration_seconds": total_duration,
- "fps": fps,
- "is_emoji": is_emoji,
- "optimal": optimal if is_emoji else None,
- }
- # Print if verbose
- if verbose:
- print(f"\nValidating {gif_path.name}:")
- print(
- f" Dimensions: {width}x{height}"
- + (
- f" ({'optimal' if optimal else 'acceptable'})"
- if is_emoji and acceptable
- else ""
- )
- )
- print(
- f" Size: {size_kb:.1f} KB"
- + (f" ({size_mb:.2f} MB)" if size_mb >= 1.0 else "")
- )
- print(
- f" Frames: {frame_count}"
- + (f" @ {fps:.1f} fps ({total_duration:.1f}s)" if fps else "")
- )
- if not dim_pass:
- print(
- f" Note: {'Emoji should be 128x128' if is_emoji else 'Unusual dimensions for Slack'}"
- )
- if size_mb > 5.0:
- print(f" Note: Large file size - consider fewer frames/colors")
- return dim_pass, results
- def is_slack_ready(
- gif_path: str | Path, is_emoji: bool = True, verbose: bool = True
- ) -> bool:
- """
- Quick check if GIF is ready for Slack.
- Args:
- gif_path: Path to GIF file
- is_emoji: True for emoji GIF, False for message GIF
- verbose: Print feedback
- Returns:
- True if dimensions are acceptable
- """
- passes, _ = validate_gif(gif_path, is_emoji, verbose)
- return passes
|