Skip to content

Commit 43594bb

Browse files
GH-29: Fix the image compression option (GH-57)
* Change the frame size calculation * Optimize the calculation of columns' count * Limit the final image size be at most 8K * Allow `0` as a value for the `--compress` option * Update snapshots for related test cases
1 parent 69c3fed commit 43594bb

File tree

7 files changed

+98
-64
lines changed

7 files changed

+98
-64
lines changed

src/thumbnails/__init__.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
from .thumbnail import ThumbnailVTT
2323
from .thumbnail import register_thumbnail
2424

25-
__version__ = "0.1.4"
25+
__version__ = "0.1.5"
2626
__all__ = (
2727
"Generator",
2828
"Thumbnail",

src/thumbnails/frame.py

+27-10
Original file line numberDiff line numberDiff line change
@@ -5,33 +5,50 @@ class _Frame:
55
"""This class is used to calculate the optimal size of a thumbnail frame."""
66

77
def __init__(self, size):
8-
width, height = size
9-
_min_width = 300
10-
_min_height = math.ceil(_min_width * height / width)
8+
# Original size of the frame
9+
self._width, self._height = size
1110

11+
# Minimum size of the frame
12+
self._min_width = 30
13+
self._min_height = math.ceil(self._min_width * self._height / self._width)
14+
15+
# Maximum size of the frame
16+
self._max_width = 7680 // self.columns_count # 7680 is the width of 8K
17+
self._max_height = math.ceil(self._max_width * self._height / self._width)
18+
19+
# Final size of the frame
1220
self.__width = None
1321
self.__height = None
1422

15-
self._width = width / 10
16-
self._height = height / 10
17-
self._min_width = _min_width
18-
self._min_height = _min_height
19-
2023
@property
2124
def compress(self):
2225
"""Defines an interface for the compress property."""
2326
raise NotImplementedError
2427

28+
@property
29+
def frames_count(self):
30+
"""Defines an interface for the frames_count property."""
31+
raise NotImplementedError
32+
33+
@property
34+
def columns_count(self):
35+
"""Defines an interface for the columns_count property."""
36+
raise NotImplementedError
37+
2538
@property
2639
def width(self):
2740
"""Calculates and caches the frame width."""
2841
if not self.__width:
29-
self.__width = max(self._min_width, self._width * self.compress)
42+
self.__width = round(self._width * self.compress)
43+
self.__width = max(self.__width, self._min_width)
44+
self.__width = min(self.__width, self._max_width)
3045
return self.__width
3146

3247
@property
3348
def height(self):
3449
"""Calculates and caches the frame height."""
3550
if not self.__height:
36-
self.__height = max(self._min_height, self._height * self.compress)
51+
self.__height = round(self._height * self.compress)
52+
self.__height = max(self.__height, self._min_height)
53+
self.__height = min(self.__height, self._max_height)
3754
return self.__height

src/thumbnails/video.py

+26-9
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,12 @@ def __init__(self, filepath, compress, interval):
3434
self.__compress = float(compress)
3535
self.__interval = float(interval)
3636

37-
if self.__compress <= 0 or self.__compress > 1:
37+
if self.__compress < 0 or self.__compress > 1:
3838
raise ValueError("Compress must be between 0 and 1.")
3939

4040
self.tempdir = TemporaryDirectory()
41+
self.__frames_count = None
42+
self.__columns = None
4143

4244
_FFMpeg.__init__(self, filepath)
4345
_Frame.__init__(self, self.size)
@@ -54,12 +56,26 @@ def compress(self):
5456
def interval(self):
5557
return self.__interval
5658

57-
@staticmethod
58-
def calc_columns(frames_count, width, height):
59+
@property
60+
def frames_count(self):
61+
"""Calculates and caches the count of frames."""
62+
if not self.__frames_count:
63+
self.__frames_count = len(arange(0, self.duration, self.interval))
64+
return self.__frames_count
65+
66+
@property
67+
def columns_count(self):
68+
"""Calculates and caches the count of columns."""
69+
if not self.__columns:
70+
self.__columns = self.calc_columns()
71+
return self.__columns
72+
73+
def calc_columns(self):
5974
"""Calculates an optimal number of columns for 16:9 aspect ratio."""
6075
ratio = 16 / 9
61-
for col in range(1, frames_count):
62-
if (col * width) / (frames_count // col * height) > ratio:
76+
width, height = self.size
77+
for col in range(1, self.frames_count):
78+
if (col * width) / (self.frames_count // col * height) > ratio:
6379
return col
6480

6581
def _extract_frame(self, start_time):
@@ -102,11 +118,12 @@ def thumbnails(self, master_size=False):
102118
"""
103119
line, column = 0, 0
104120
frames = sorted(glob.glob(self.tempdir.name + os.sep + "*.png"))
105-
frames_count = len(arange(0, self.duration, self.interval))
106-
columns = self.calc_columns(frames_count, self.width, self.height)
107121

108122
if master_size:
109-
yield self.width * columns, self.height * math.ceil(frames_count / columns)
123+
yield (
124+
self.width * self.columns_count,
125+
self.height * math.ceil(self.frames_count / self.columns_count)
126+
)
110127

111128
for n, frame in enumerate(frames):
112129
x, y = self.width * column, self.height * line
@@ -117,6 +134,6 @@ def thumbnails(self, master_size=False):
117134

118135
column += 1
119136

120-
if column == columns:
137+
if column == self.columns_count:
121138
line += 1
122139
column = 0
+11-11
Original file line numberDiff line numberDiff line change
@@ -1,46 +1,46 @@
11
{
22
"0": {
33
"src": "%(tmp_media)s/video/0-00-00.png",
4-
"width": "300px"
4+
"width": "2560px"
55
},
66
"10": {
77
"src": "%(tmp_media)s/video/0-00-10.png",
8-
"width": "300px"
8+
"width": "2560px"
99
},
1010
"20": {
1111
"src": "%(tmp_media)s/video/0-00-20.png",
12-
"width": "300px"
12+
"width": "2560px"
1313
},
1414
"30": {
1515
"src": "%(tmp_media)s/video/0-00-30.png",
16-
"width": "300px"
16+
"width": "2560px"
1717
},
1818
"40": {
1919
"src": "%(tmp_media)s/video/0-00-40.png",
20-
"width": "300px"
20+
"width": "2560px"
2121
},
2222
"50": {
2323
"src": "%(tmp_media)s/video/0-00-50.png",
24-
"width": "300px"
24+
"width": "2560px"
2525
},
2626
"60": {
2727
"src": "%(tmp_media)s/video/0-01-00.png",
28-
"width": "300px"
28+
"width": "2560px"
2929
},
3030
"70": {
3131
"src": "%(tmp_media)s/video/0-01-10.png",
32-
"width": "300px"
32+
"width": "2560px"
3333
},
3434
"80": {
3535
"src": "%(tmp_media)s/video/0-01-20.png",
36-
"width": "300px"
36+
"width": "2560px"
3737
},
3838
"90": {
3939
"src": "%(tmp_media)s/video/0-01-30.png",
40-
"width": "300px"
40+
"width": "2560px"
4141
},
4242
"100": {
4343
"src": "%(tmp_media)s/video/0-01-40.png",
44-
"width": "300px"
44+
"width": "2560px"
4545
}
4646
}
+11-11
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,35 @@
11
WEBVTT
22

33
00:00:00.000 --> 00:00:10.000
4-
%(tmp_media)s/video.png#xywh=0,0,300,126
4+
%(tmp_media)s/video.png#xywh=0,0,2560,1072
55

66
00:00:10.000 --> 00:00:20.000
7-
%(tmp_media)s/video.png#xywh=300,0,300,126
7+
%(tmp_media)s/video.png#xywh=2560,0,2560,1072
88

99
00:00:20.000 --> 00:00:30.000
10-
%(tmp_media)s/video.png#xywh=600,0,300,126
10+
%(tmp_media)s/video.png#xywh=5120,0,2560,1072
1111

1212
00:00:30.000 --> 00:00:40.000
13-
%(tmp_media)s/video.png#xywh=0,126,300,126
13+
%(tmp_media)s/video.png#xywh=0,1072,2560,1072
1414

1515
00:00:40.000 --> 00:00:50.000
16-
%(tmp_media)s/video.png#xywh=300,126,300,126
16+
%(tmp_media)s/video.png#xywh=2560,1072,2560,1072
1717

1818
00:00:50.000 --> 00:01:00.000
19-
%(tmp_media)s/video.png#xywh=600,126,300,126
19+
%(tmp_media)s/video.png#xywh=5120,1072,2560,1072
2020

2121
00:01:00.000 --> 00:01:10.000
22-
%(tmp_media)s/video.png#xywh=0,252,300,126
22+
%(tmp_media)s/video.png#xywh=0,2144,2560,1072
2323

2424
00:01:10.000 --> 00:01:20.000
25-
%(tmp_media)s/video.png#xywh=300,252,300,126
25+
%(tmp_media)s/video.png#xywh=2560,2144,2560,1072
2626

2727
00:01:20.000 --> 00:01:30.000
28-
%(tmp_media)s/video.png#xywh=600,252,300,126
28+
%(tmp_media)s/video.png#xywh=5120,2144,2560,1072
2929

3030
00:01:30.000 --> 00:01:40.000
31-
%(tmp_media)s/video.png#xywh=0,378,300,126
31+
%(tmp_media)s/video.png#xywh=0,3216,2560,1072
3232

3333
00:01:40.000 --> 00:01:50.000
34-
%(tmp_media)s/video.png#xywh=300,378,300,126
34+
%(tmp_media)s/video.png#xywh=2560,3216,2560,1072
3535

+11-11
Original file line numberDiff line numberDiff line change
@@ -1,46 +1,46 @@
11
{
22
"0": {
33
"src": "/media/thumbnails/video/0-00-00.png",
4-
"width": "300px"
4+
"width": "1280px"
55
},
66
"10": {
77
"src": "/media/thumbnails/video/0-00-10.png",
8-
"width": "300px"
8+
"width": "1280px"
99
},
1010
"20": {
1111
"src": "/media/thumbnails/video/0-00-20.png",
12-
"width": "300px"
12+
"width": "1280px"
1313
},
1414
"30": {
1515
"src": "/media/thumbnails/video/0-00-30.png",
16-
"width": "300px"
16+
"width": "1280px"
1717
},
1818
"40": {
1919
"src": "/media/thumbnails/video/0-00-40.png",
20-
"width": "300px"
20+
"width": "1280px"
2121
},
2222
"50": {
2323
"src": "/media/thumbnails/video/0-00-50.png",
24-
"width": "300px"
24+
"width": "1280px"
2525
},
2626
"60": {
2727
"src": "/media/thumbnails/video/0-01-00.png",
28-
"width": "300px"
28+
"width": "1280px"
2929
},
3030
"70": {
3131
"src": "/media/thumbnails/video/0-01-10.png",
32-
"width": "300px"
32+
"width": "1280px"
3333
},
3434
"80": {
3535
"src": "/media/thumbnails/video/0-01-20.png",
36-
"width": "300px"
36+
"width": "1280px"
3737
},
3838
"90": {
3939
"src": "/media/thumbnails/video/0-01-30.png",
40-
"width": "300px"
40+
"width": "1280px"
4141
},
4242
"100": {
4343
"src": "/media/thumbnails/video/0-01-40.png",
44-
"width": "300px"
44+
"width": "1280px"
4545
}
4646
}
+11-11
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,35 @@
11
WEBVTT
22

33
00:00:00.000 --> 00:00:10.000
4-
/media/thumbnails/video.png#xywh=0,0,300,126
4+
/media/thumbnails/video.png#xywh=0,0,1280,536
55

66
00:00:10.000 --> 00:00:20.000
7-
/media/thumbnails/video.png#xywh=300,0,300,126
7+
/media/thumbnails/video.png#xywh=1280,0,1280,536
88

99
00:00:20.000 --> 00:00:30.000
10-
/media/thumbnails/video.png#xywh=600,0,300,126
10+
/media/thumbnails/video.png#xywh=2560,0,1280,536
1111

1212
00:00:30.000 --> 00:00:40.000
13-
/media/thumbnails/video.png#xywh=0,126,300,126
13+
/media/thumbnails/video.png#xywh=0,536,1280,536
1414

1515
00:00:40.000 --> 00:00:50.000
16-
/media/thumbnails/video.png#xywh=300,126,300,126
16+
/media/thumbnails/video.png#xywh=1280,536,1280,536
1717

1818
00:00:50.000 --> 00:01:00.000
19-
/media/thumbnails/video.png#xywh=600,126,300,126
19+
/media/thumbnails/video.png#xywh=2560,536,1280,536
2020

2121
00:01:00.000 --> 00:01:10.000
22-
/media/thumbnails/video.png#xywh=0,252,300,126
22+
/media/thumbnails/video.png#xywh=0,1072,1280,536
2323

2424
00:01:10.000 --> 00:01:20.000
25-
/media/thumbnails/video.png#xywh=300,252,300,126
25+
/media/thumbnails/video.png#xywh=1280,1072,1280,536
2626

2727
00:01:20.000 --> 00:01:30.000
28-
/media/thumbnails/video.png#xywh=600,252,300,126
28+
/media/thumbnails/video.png#xywh=2560,1072,1280,536
2929

3030
00:01:30.000 --> 00:01:40.000
31-
/media/thumbnails/video.png#xywh=0,378,300,126
31+
/media/thumbnails/video.png#xywh=0,1608,1280,536
3232

3333
00:01:40.000 --> 00:01:50.000
34-
/media/thumbnails/video.png#xywh=300,378,300,126
34+
/media/thumbnails/video.png#xywh=1280,1608,1280,536
3535

0 commit comments

Comments
 (0)