From e45792a5c444fae7318a5a0d17cf3504e86edf9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mike=20F=C3=A4hrmann?= Date: Sat, 26 Mar 2022 18:04:12 +0100 Subject: [PATCH] [postprocessor:ugoira] insert extra frame into files generated with the 'image2' demuxer to compensate for the last frame not being shown for as long as it should. This only happens for ugoira with non-uniform delays between frames and only when 'repeat-last-frame' is enabled. --- gallery_dl/postprocessor/ugoira.py | 51 ++++++++++++++++++++++++++---- 1 file changed, 45 insertions(+), 6 deletions(-) diff --git a/gallery_dl/postprocessor/ugoira.py b/gallery_dl/postprocessor/ugoira.py index 59e9f36c..a6157e86 100644 --- a/gallery_dl/postprocessor/ugoira.py +++ b/gallery_dl/postprocessor/ugoira.py @@ -10,12 +10,20 @@ from .common import PostProcessor from .. import util -import collections import subprocess import tempfile import zipfile +import shutil import os +try: + from math import gcd +except ImportError: + def gcd(a, b): + while b: + a, b = b, a % b + return a + class UgoiraPP(PostProcessor): @@ -151,10 +159,26 @@ class UgoiraPP(PostProcessor): def _process_image2(self, pathfmt, tempdir): tempdir += "/" + frames = self._frames + + # add extra frame if necessary + if self.repeat and not self._delay_is_uniform(frames): + last = frames[-1] + delay_gcd = self._delay_gcd(frames) + if last["delay"] - delay_gcd > 0: + last["delay"] -= delay_gcd + + self.log.debug("non-uniform delays; inserting extra frame") + last_copy = last.copy() + frames.append(last_copy) + name, _, ext = last_copy["file"].rpartition(".") + last_copy["file"] = "{:>06}.{}".format(int(name)+1, ext) + shutil.copyfile(tempdir + last["file"], + tempdir + last_copy["file"]) # adjust frame mtime values ts = 0 - for frame in self._frames: + for frame in frames: os.utime(tempdir + frame["file"], ns=(ts, ts)) ts += frame["delay"] * 1000000 @@ -228,11 +252,26 @@ class UgoiraPP(PostProcessor): file.write("\n".join(content)) return timecodes + def calculate_framerate(self, frames): + uniform = self._delay_is_uniform(frames) + if uniform: + return ("1000/{}".format(frames[0]["delay"]), None) + return (None, "1000/{}".format(self._delay_gcd(frames))) + @staticmethod - def calculate_framerate(framelist): - counter = collections.Counter(frame["delay"] for frame in framelist) - fps = "1000/{}".format(min(counter)) - return (fps, None) if len(counter) == 1 else (None, fps) + def _delay_gcd(frames): + result = frames[0]["delay"] + for f in frames: + result = gcd(result, f["delay"]) + return result + + @staticmethod + def _delay_is_uniform(frames): + delay = frames[0]["delay"] + for f in frames: + if f["delay"] != delay: + return False + return True __postprocessor__ = UgoiraPP