11
11
from imageio .v3 import immeta
12
12
from imageio_ffmpeg import get_ffmpeg_exe
13
13
14
- FFMPEG_BINARY = get_ffmpeg_exe ()
14
+ from . thumbnails import _ThumbnailMixin
15
15
16
+ ffmpeg_bin = get_ffmpeg_exe ()
16
17
17
- class FFMpeg :
18
+
19
+ class FFMpeg (_ThumbnailMixin ):
18
20
def __init__ (self , filename ):
19
21
self .__compress = 1
20
22
self .__interval = 1
23
+ self .__basepath = ""
24
+ self .thumbnails = []
21
25
duration , size = self ._parse_metadata (filename )
26
+ _ThumbnailMixin .__init__ (self , size )
22
27
self .tempdir = TemporaryDirectory ()
28
+ self .duration = int (duration + 1 )
23
29
self .filename = filename
24
- self .duration = duration
25
- self .size = size
30
+ self ._vtt_name = filename + ".vtt"
31
+ self ._image_name = filename + ".png"
26
32
27
33
def get_compress (self ):
28
34
return self .__compress
@@ -40,8 +46,14 @@ def set_interval(self, interval):
40
46
raise TypeError ("Interval must be an integer." )
41
47
self .__interval = interval
42
48
49
+ def get_basepath (self ):
50
+ return self .__basepath
51
+
52
+ def set_basepath (self , path ):
53
+ self .__basepath = str (path )
54
+
43
55
@staticmethod
44
- def calc_columns (frames_count , width , height ):
56
+ def _calc_columns (frames_count , width , height ):
45
57
ratio = 16 / 9
46
58
for col in range (1 , frames_count ):
47
59
if (col * width ) / (frames_count // col * height ) > ratio :
@@ -77,7 +89,7 @@ def _parse_metadata(self, filename):
77
89
duration , size = meta .get ("duration" ), meta .get ("size" )
78
90
79
91
if not all ((duration , size )):
80
- cmd = (FFMPEG_BINARY , "-hide_banner" , "-i" , filename )
92
+ cmd = (ffmpeg_bin , "-hide_banner" , "-i" , filename )
81
93
82
94
popen_params = self ._cross_platform_popen_params ()
83
95
process = subprocess .Popen (cmd , ** popen_params )
@@ -95,7 +107,7 @@ def _extract_frame(self, start_time):
95
107
_timestamp = str (timedelta (seconds = start_time ))
96
108
97
109
cmd = (
98
- FFMPEG_BINARY ,
110
+ ffmpeg_bin ,
99
111
"-ss" , _timestamp ,
100
112
"-i" , _input_file ,
101
113
"-loglevel" , "error" ,
@@ -107,30 +119,27 @@ def _extract_frame(self, start_time):
107
119
subprocess .Popen (cmd ).wait ()
108
120
109
121
def extract_frames (self ):
110
- _intervals = range (0 , int ( self .duration ) , self .get_interval ())
122
+ _intervals = range (0 , self .duration , self .get_interval ())
111
123
with concurrent .futures .ThreadPoolExecutor () as executor :
112
124
executor .map (self ._extract_frame , _intervals )
113
125
114
126
def join_frames (self ):
115
- width , height = self .size
116
-
117
- _min_width = 300
118
- _min_height = math .ceil (_min_width * height / width )
119
-
120
- width = max (_min_width , width * self .__compress / 10 )
121
- height = max (_min_height , height * self .__compress / 10 )
122
-
123
127
line , column = 0 , 0
124
128
frames = sorted (glob .glob (self .tempdir .name + os .sep + "*.png" ))
125
- frames_count = len (range (0 , int ( self .duration ) , self .get_interval ()))
126
- columns = self .calc_columns (frames_count , width , height )
127
- master_height = height * int (math .ceil (float (frames_count ) / columns ))
128
- master = Image .new (mode = "RGBA" , size = (width * columns , master_height ))
129
+ frames_count = len (range (0 , self .duration , self .get_interval ()))
130
+ columns = self ._calc_columns (frames_count , self . width , self . height )
131
+ master_height = self . height * int (math .ceil (float (frames_count ) / columns ))
132
+ master = Image .new (mode = "RGBA" , size = (self . width * columns , master_height ))
129
133
130
- for frame in frames :
134
+ for n , frame in enumerate ( frames ) :
131
135
with Image .open (frame ) as image :
132
- x , y = width * column , height * line
133
- image = image .resize ((width , height ), Image .ANTIALIAS )
136
+ x , y = self .width * column , self .height * line
137
+
138
+ start = n * self .get_interval ()
139
+ end = (n + 1 ) * self .get_interval ()
140
+ self .thumbnails .append ((start , end , x , y ))
141
+
142
+ image = image .resize ((self .width , self .height ), Image .ANTIALIAS )
134
143
master .paste (image , (x , y ))
135
144
136
145
column += 1
@@ -139,5 +148,22 @@ def join_frames(self):
139
148
line += 1
140
149
column = 0
141
150
142
- master .save (self .filename + ".png" )
151
+ master .save (self ._image_name )
143
152
self .tempdir .cleanup ()
153
+
154
+ def to_vtt (self ):
155
+ def _format_time (seconds ):
156
+ return "0%s.000" % str (timedelta (seconds = seconds ))
157
+
158
+ _lines = ["WEBVTT\n \n " ]
159
+ _img_src = self .get_basepath () + self ._image_name
160
+
161
+ for start , end , x , y in self .thumbnails :
162
+ _thumbnail = "%s --> %s\n %s#xywh=%d,%d,%d,%d\n \n " % (
163
+ _format_time (start ), _format_time (end ),
164
+ _img_src , x , y , self .width , self .height
165
+ )
166
+ _lines .append (_thumbnail )
167
+
168
+ with open (self ._vtt_name , "w" ) as vtt :
169
+ vtt .writelines (_lines )
0 commit comments