137 lines
3.7 KiB
Python
Executable File
137 lines
3.7 KiB
Python
Executable File
#!/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
|