Skip to content

Commit

Permalink
Merge pull request #700 from jacebrowning/fix-slow-gifs
Browse files Browse the repository at this point in the history
Set a maxium duration on GIFs
  • Loading branch information
jacebrowning authored Feb 28, 2022
2 parents 7ea2a50 + 878dd8d commit 4c72417
Show file tree
Hide file tree
Showing 9 changed files with 36 additions and 12 deletions.
11 changes: 7 additions & 4 deletions app/models/template.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,9 +77,10 @@ def image(self) -> Path:

def get_image(self, style: str = "", *, animated: bool = False) -> Path:
style = style or settings.DEFAULT_STYLE
if style == settings.DEFAULT_STYLE and animated:
if (style == settings.DEFAULT_STYLE or ".gif" in style) and animated:
style = "animated"

url = ""
if utils.urls.schema(style):
url = style
style = utils.text.fingerprint(url)
Expand All @@ -90,15 +91,17 @@ def get_image(self, style: str = "", *, animated: bool = False) -> Path:
return path

if style == settings.DEFAULT_STYLE:
logger.debug(f"No default background image for template: {self.id}")
logger.info(f"No default background image for template: {self.id}")
return self.directory / (
settings.DEFAULT_STYLE + settings.PLACEHOLDER_SUFFIX
)

if style == "animated":
logger.info(f"Using default image to animate static template: {self.id}")
else:
logger.warning(f"Style {style!r} not available for template: {self.id}")
logger.warning(
f"Style {url or style!r} not available for template: {self.id}"
)

return self.get_image()

Expand Down Expand Up @@ -222,7 +225,7 @@ async def create(cls, url: str, *, force=False) -> "Template":
logger.warning(f"Unable to determine image extension: {url}")
suffix = settings.PLACEHOLDER_SUFFIX

filename = "default" + suffix
filename = ("animated" if suffix == ".gif" else "default") + suffix
path = aiopath.AsyncPath(template.directory) / filename

if await path.exists() and not settings.DEBUG and not force:
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
11 changes: 11 additions & 0 deletions app/tests/test_utils_images.py
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,17 @@ def test_debug_images(images, monkeypatch, extension):
)


@pytest.mark.slow
@pytest.mark.asyncio
async def test_debug_images_with_slow_background(images, monkeypatch):
monkeypatch.setattr(settings, "DEBUG", True)

url = "https://media.giphy.com/media/4560Nv2656Gv0Lvp9F/giphy.gif"
template = await models.Template.create(url)
lines = ["this isn't the GIF", "you're looking for"]
utils.images.save(template, lines, style=url, extension="gif", directory=images)


def test_deployed_images(images, monkeypatch):
monkeypatch.setattr(settings, "DEPLOYED", True)

Expand Down
26 changes: 18 additions & 8 deletions app/utils/images.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,12 @@ def save(

count = len(frames)
if (count <= settings.MINIMUM_FRAMES) or (count > settings.MAXIMUM_FRAMES):
logger.info(f"Saving {count} frame(s) as animated PNG")
logger.info(
f"Saving {count} frame(s) as animated PNG at {duration} duration"
)
extension = "png"
else:
logger.info(f"Saving {count} frame(s) as GIF at {duration} duration")

frames[0].save(
path,
Expand Down Expand Up @@ -243,12 +247,12 @@ def render_animation(
sources = ImageSequence.Iterator(source)
elif template.animated:
sources = [source] * settings.MAXIMUM_FRAMES # type: ignore
duration = 300
duration = 250
total = settings.MAXIMUM_FRAMES
elif sum(1 for line in lines if line.strip()) >= 2:
template.animate()
sources = [source] * settings.MINIMUM_FRAMES # type: ignore
duration = 300
duration = 1000
total = settings.MINIMUM_FRAMES
else:
sources = [source] # type: ignore
Expand Down Expand Up @@ -337,12 +341,16 @@ def render_animation(
image = add_watermark(image, ".", is_preview, index, total)

if settings.DEBUG:
image = add_counter(image, index, total, modulus)
image = add_counter(image, index, total, modulus, duration)

frames.append(image)

ratio = len(frames) / max(total, settings.MAXIMUM_FRAMES)
duration = duration / ratio
if len(frames) > settings.MINIMUM_FRAMES:
logger.info(
f"Adjusting initial duration of {duration} based on {total} initial frame(s)"
)
ratio = len(frames) / max(total, settings.MAXIMUM_FRAMES)
duration = min(250, duration / ratio)

return frames, duration

Expand Down Expand Up @@ -444,9 +452,11 @@ def add_watermark(
return Image.alpha_composite(image, box)


def add_counter(image: ImageType, index: int, total: int, modulus: float) -> ImageType:
def add_counter(
image: ImageType, index: int, total: int, modulus: float, duration: int
) -> ImageType:
size = (image.size[0], settings.WATERMARK_HEIGHT)
text = f"{index+1:02} of {total:02} / {modulus}"
text = f"{index+1:02} of {total:02} / {modulus} @ {duration}"
font = get_font("tiny", text, size, 99)

box = Image.new("RGBA", image.size)
Expand Down

0 comments on commit 4c72417

Please sign in to comment.