-def video_from_image(img_path, duration, fps=25):
- tmp = NamedTemporaryFile(prefix='image', suffix='.mkv', delete=False)
- tmp.close()
- subprocess.run(
- ['ffmpeg', '-y', '-framerate', f'1/{duration}', '-i', img_path, '-c:v', 'libx264', '-vf', f'fps={fps},format=yuv420p', tmp.name], check=True)
- return tmp.name
+FILE_CACHE = getattr(settings, 'FILE_CACHE', 'file_cache/')
+
+
+def link_or_copy(src, dst):
+ dstdir = os.path.dirname(dst)
+ if not os.path.exists(dstdir):
+ os.makedirs(dstdir)
+ if os.path.exists(dst):
+ os.unlink(dst)
+ # FIXME: tiny window here when the temp path is not taken.
+ try:
+ os.link(src, dst)
+ except OSError:
+ shutil.copyfile(src, dst)
+
+
+def process_to_file(cmdline, prefix='', suffix='', cache_key=None, output_path=None):
+ if not output_path:
+ tmp = NamedTemporaryFile(prefix=prefix, suffix=suffix, delete=False)
+ tmp.close()
+ output_path = tmp.name
+
+ if cache_key:
+ cache_path = FILE_CACHE + cache_key.replace('/', '__')
+
+ if cache_key and os.path.exists(cache_path):
+ link_or_copy(cache_path, output_path)
+ else:
+ # Actually run the processing.
+ subprocess.run(cmdline + [output_path], check=True)
+ if cache_key:
+ link_or_copy(output_path, cache_path)
+
+ return output_path
+
+
+def video_from_image(img_path, duration, fps=25, cache=True):
+ return process_to_file(
+ ['ffmpeg', '-y', '-loop', '1', '-t', str(duration), '-i', img_path, '-c:v', 'libx264', '-vf', f'fps={fps},format=yuv420p'],
+ 'image-',
+ '.mkv',
+ f'video_from_image:{img_path}:{duration}:{fps}.mkv' if cache else None
+ )