Skip to content

Commit

Permalink
updated file-formats in cache for smaller size
Browse files Browse the repository at this point in the history
- also improved video animation by extracting frames as png (lossless) instead of jpeg (lossy)
  • Loading branch information
Barakudum committed Feb 26, 2024
1 parent 9fd817a commit b6c7e38
Show file tree
Hide file tree
Showing 4 changed files with 40 additions and 42 deletions.
12 changes: 6 additions & 6 deletions src/jarklin/cache/__init__.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
# -*- coding=utf-8 -*-
r"""
video.mp4/
├─ preview.jpg
├─ preview.gif
{gallery,video.mp4}/
├─ preview.webp
├─ animated.webp
├─ previews/
│ ├─ 1.jpg
│ ├─ 2.jpg
│ ├─ 1.webp
│ ├─ 2.webp
├─ meta.json
├─ video.type
├─ {gallery,video}.type
"""
from .cache import Cache

Expand Down
4 changes: 3 additions & 1 deletion src/jarklin/cache/cache.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ def shutdown(self) -> None:
self._shutdown_event.set()

def remove(self, ignore_errors: bool = False) -> None:
shutil.rmtree(self.jarklin_path, ignore_errors=ignore_errors)
shutil.rmtree(self.jarklin_cache, ignore_errors=ignore_errors)

def iteration(self) -> None:
self.invalidate()
Expand Down Expand Up @@ -129,6 +129,8 @@ def generate_info_file():
description=str(error),
traceback='\n'.join(format_exception(type(error), error, error.__traceback__))
))
if dest.is_dir():
shutil.rmtree(dest, ignore_errors=True)
continue
generate_info_file()
info.append(InfoEntry(
Expand Down
29 changes: 14 additions & 15 deletions src/jarklin/cache/gallery.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,14 @@
todo: what if image is extremely height (eg. 512x8192)
- crop for animated preview!?
video.mp4/
├─ preview.jpg
├─ preview.gif
gallery/
├─ preview.webp
├─ animated.webp
├─ previews/
│ ├─ 1.jpg
│ ├─ 2.jpg
│ ├─ 1.webp
│ ├─ 2.webp
├─ meta.json
├─ video.type
├─ gallery.type
"""
import re
import shutil
Expand Down Expand Up @@ -60,17 +60,16 @@ def generate_meta(self) -> None:
def generate_previews(self) -> None:
for i, info in enumerate(self.meta['images']):
with Image.open(self.source.joinpath(info['filename'])) as image:
image_rgb = image.convert('RGB')
image_rgb.thumbnail(self.max_dimensions)
image_rgb.save(self.previews_dir.joinpath(f"{i+1}.jpg"), format='JPEG',
progressive=True, optimize=True, quality=85)
image.thumbnail(self.max_dimensions)
image.save(self.previews_dir.joinpath(f"{i + 1}.webp"), format='WEBP',
method=6, quality=80)

def generate_image_preview(self) -> None:
first_preview = self.previews_dir.joinpath("1.jpg")
shutil.copyfile(first_preview, self.dest.joinpath("preview.jpg"))
first_preview = self.previews_dir.joinpath("1.webp")
shutil.copyfile(first_preview, self.dest.joinpath("preview.webp"))

def generate_animated_preview(self) -> None:
images = sorted(self.previews_dir.glob("*.jpg"), key=lambda f: int(f.stem))[:self.max_images]
images = sorted(self.previews_dir.glob("*.webp"), key=lambda f: int(f.stem))[:self.max_images]
if not images:
raise FileExistsError("no previews found")
with ExitStack() as stack:
Expand All @@ -79,8 +78,8 @@ def generate_animated_preview(self) -> None:
frames = [stack.enter_context(frame.resize(first.size)) for frame in frames]
# minimize_size=True => warned as slow
# method=6 => bit slower but better results
first.save(self.dest.joinpath("preview.webp"), format="WEBP", save_all=True, minimize_size=False,
append_images=frames, duration=round(self.frame_time * 1000), loop=0, method=6, quality=100)
first.save(self.dest.joinpath("animated.webp"), format="WEBP", save_all=True, minimize_size=False,
append_images=frames, duration=round(self.frame_time * 1000), loop=0, method=6, quality=80)

def generate_type(self) -> None:
self.dest.joinpath("gallery.type").touch()
Expand Down
37 changes: 17 additions & 20 deletions src/jarklin/cache/video.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
# -*- coding=utf-8 -*-
r"""
video.mp4/
├─ preview.jpg
├─ preview.gif
├─ preview.webp
├─ animated.webp
├─ previews/
│ ├─ 1.jpg
│ ├─ 2.jpg
│ ├─ 1.webp
│ ├─ 2.webp
├─ meta.json
├─ video.type
"""
Expand Down Expand Up @@ -88,34 +88,31 @@ def generate_previews(self) -> None:
.input(str(self.source))
.filter('select', "+".join(f"eq(n,{frame})" for frame in extract_frames))
.filter('scale', *scale)
.output(str(self.previews_cache.joinpath("%d.jpg")), vframes=len(extract_frames), vsync=0)
.output(str(self.previews_cache.joinpath("%d.png")), vframes=len(extract_frames), vsync=0)
# .global_args('-threads', str(self.config.getint('cache', 'video', 'ffmpeg', 'threads', fallback=0)))
.run(quiet=True, overwrite_output=True)
)

logging.debug(f"{self}: copying frames to previews/")
for i in range(len(main_frames)):
shutil.copyfile(
self.previews_cache.joinpath(f"{round(i * self.seconds_per_scene * self.scene_fps)+1}.jpg"),
self.previews_dir.joinpath(f"{i+1}.jpg")
)
logging.debug(f"{self}: copying main-frames to previews/")
for i, j in enumerate(range(0, len(extract_frames), len(scene_offsets))):
source = self.previews_cache.joinpath(f"{j+1}.png")
dest = self.previews_dir.joinpath(f"{i+1}.webp")
with Image.open(source) as image:
image.save(dest, format='WEBP', method=6, quality=80)

def generate_image_preview(self) -> None:
# prefer the second as the first could be producer-logo
preview_source = self.previews_dir.joinpath("2.jpg")
preview_source = self.previews_dir.joinpath("2.webp")
if not preview_source.is_file():
preview_source = self.previews_dir.joinpath("1.jpg")
shutil.copyfile(preview_source, self.dest.joinpath("preview.jpg"))
preview_source = self.previews_dir.joinpath("1.webp")
shutil.copyfile(preview_source, self.dest.joinpath("preview.webp"))

def generate_animated_preview(self) -> None:
images = sorted(self.previews_cache.glob("*.jpg"), key=lambda f: int(f.stem))
images = sorted(self.previews_cache.glob("*.png"), key=lambda f: int(f.stem))
with ExitStack() as stack:
first, *frames = (stack.enter_context(Image.open(fp)) for fp in images)
# minimize_size=True => warned as slow
# method=6 => bit slower but better results
# note: too many images to use quality=100
first.save(self.dest.joinpath("preview.webp"), format="WEBP", save_all=True, minimize_size=False,
append_images=frames, duration=round(1000 / self.scene_fps), loop=0, method=6) # , quality=100
first.save(self.dest.joinpath("animated.webp"), format="WEBP", save_all=True, minimize_size=False,
append_images=frames, duration=round(1000 / self.scene_fps), loop=0, method=6, quality=80)

def generate_type(self):
self.dest.joinpath("video.type").touch()
Expand Down

0 comments on commit b6c7e38

Please sign in to comment.