Skip to content

Commit

Permalink
Preprocessing the frames more faster (GH-62)
Browse files Browse the repository at this point in the history
  • Loading branch information
ArtyomVancyan authored Mar 20, 2023
2 parents b8a91a0 + 36e8b3b commit e555688
Show file tree
Hide file tree
Showing 6 changed files with 40 additions and 40 deletions.
2 changes: 1 addition & 1 deletion src/thumbnails/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
from .thumbnail import ThumbnailVTT
from .thumbnail import register_thumbnail

__version__ = "0.1.7"
__version__ = "0.1.8"
__all__ = (
"Generator",
"Thumbnail",
Expand Down
24 changes: 12 additions & 12 deletions src/thumbnails/frame.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ def __init__(self, size):
self._max_height = math.ceil(self._max_width * self._height / self._width)

# Final size of the frame
self.__width = None
self.__height = None
self._final_width = None
self._final_height = None

@property
def compress(self):
Expand All @@ -38,17 +38,17 @@ def columns_count(self):
@property
def width(self):
"""Calculates and caches the frame width."""
if not self.__width:
self.__width = round(self._width * self.compress)
self.__width = max(self.__width, self._min_width)
self.__width = min(self.__width, self._max_width)
return self.__width
if not self._final_width:
self._final_width = round(self._width * self.compress)
self._final_width = max(self._final_width, self._min_width)
self._final_width = min(self._final_width, self._max_width)
return self._final_width

@property
def height(self):
"""Calculates and caches the frame height."""
if not self.__height:
self.__height = round(self._height * self.compress)
self.__height = max(self.__height, self._min_height)
self.__height = min(self.__height, self._max_height)
return self.__height
if not self._final_height:
self._final_height = round(self._height * self.compress)
self._final_height = max(self._final_height, self._min_height)
self._final_height = min(self._final_height, self._max_height)
return self._final_height
29 changes: 14 additions & 15 deletions src/thumbnails/generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
from .constants import DEFAULT_OUTPUT
from .constants import DEFAULT_SKIP
from .pathtools import listdir
from .pathtools import metadata_path
from .progress import use_progress
from .thumbnail import ThumbnailExistsError
from .thumbnail import ThumbnailFactory
Expand All @@ -36,6 +35,9 @@ def __init__(self, inputs):
self.compress = DEFAULT_COMPRESS
self.interval = DEFAULT_INTERVAL

# Remove non-video files in case of input directory already contains other generated files.
self.inputs = [file for file in self.inputs if re.match(r"^.*\.(?:(?!png|vtt|json).)+$", file)]

@staticmethod
def worker(video, fmt, base, skip, output):
"""Executes the required workflows for generating a thumbnail."""
Expand All @@ -46,21 +48,18 @@ def worker(video, fmt, base, skip, output):
thumbnail.prepare_frames()
thumbnail.generate()

@use_progress
def generate(self):
self.inputs = [file for file in self.inputs if re.match(r"^.*\.(?:(?!png|vtt|json).)+$", file)]
self.inputs = dict(zip(map(lambda i: metadata_path(i, self.output, self.format), self.inputs), self.inputs))
def __iter__(self):
return self

with concurrent.futures.ThreadPoolExecutor() as executor:
videos = executor.map(
functools.partial(
Video,
compress=self.compress,
interval=self.interval,
),
self.inputs.values(),
)
def __next__(self):
"""Returns the next video to be processed."""
try:
return Video(self.inputs.pop(), self.compress, self.interval)
except IndexError:
raise StopIteration

@use_progress
def generate(self):
with concurrent.futures.ThreadPoolExecutor() as executor:
executor.map(
functools.partial(
Expand All @@ -70,5 +69,5 @@ def generate(self):
skip=self.skip,
output=self.output,
),
videos,
self,
)
6 changes: 4 additions & 2 deletions src/thumbnails/progress.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,9 @@ def use_progress(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
Progress.start()
func(*args, **kwargs)
Progress.stop()
try:
func(*args, **kwargs)
finally:
Progress.stop()

return wrapper
18 changes: 8 additions & 10 deletions src/thumbnails/thumbnail.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,6 @@ def prepare_frames(self):
offset = extract_name(frame).replace("-", ":").split(".")[0]
progress.update("Processing [bold]%s[/bold] frame" % offset)
with Image.open(frame) as image:
image = image.resize((self.width, self.height), Image.ANTIALIAS)
master.paste(image, (x, y))

with Progress("Saving the result at '%s'" % master_path):
Expand All @@ -124,9 +123,8 @@ def format_time(secs):
route = os.path.join(prefix, extract_name(self.filepath) + ".png")
route = pathlib.Path(route).as_posix()

with Progress("Saving thumbnail metadata at '%s'" % self.metadata_path) as progress:
with Progress("Saving thumbnail metadata at '%s'" % self.metadata_path):
for _, start, end, x, y in self.thumbnails():
progress.update("Generating metadata for '%s'" % route)
thumbnail_data = "%s --> %s\n%s#xywh=%d,%d,%d,%d\n\n" % (
format_time(start), format_time(end),
route, x, y, self.width, self.height,
Expand All @@ -148,17 +146,17 @@ def calc_thumbnail_dir(self):
return ensure_tree(os.path.join(basedir, extract_name(self.filepath)), True)

def prepare_frames(self):
if os.path.exists(self.thumbnail_dir):
remove_tree(self.thumbnail_dir)
copy_tree(self.tempdir.name, self.thumbnail_dir)
with Progress("Copying the frames to the output directory"):
if os.path.exists(self.thumbnail_dir):
remove_tree(self.thumbnail_dir)
copy_tree(self.tempdir.name, self.thumbnail_dir)

def generate(self):
metadata = {}

for frame, start, *_ in self.thumbnails():
frame = os.path.join(self.thumbnail_dir, os.path.basename(frame))
with Image.open(frame) as image:
image.resize((self.width, self.height), Image.ANTIALIAS).save(frame)
with Progress("Saving thumbnail metadata at '%s'" % self.metadata_path):
for frame, start, *_ in self.thumbnails():
frame = os.path.join(self.thumbnail_dir, os.path.basename(frame))
base = os.path.join(self.base, os.path.basename(self.thumbnail_dir))
prefix = base if self.base else os.path.relpath(self.thumbnail_dir)
route = os.path.join(prefix, os.path.basename(frame))
Expand Down
1 change: 1 addition & 0 deletions src/thumbnails/video.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ def _extract_frame(self, start_time):
"-i", self.filepath,
"-loglevel", "error",
"-vframes", "1",
"-vf", "scale=%d:%d" % (self.width, self.height),
output,
"-y",
)
Expand Down

0 comments on commit e555688

Please sign in to comment.