@@ -14,6 +14,11 @@ local Thumbnailer = {
1414 thumbnail_size = nil ,
1515
1616 finished_thumbnails = 0 ,
17+
18+ -- List of thumbnail states (from 1 to thumbnail_count)
19+ -- ready: 1
20+ -- in progress: 0
21+ -- not ready: -1
1722 thumbnails = {}
1823 },
1924 workers = {}
@@ -33,17 +38,19 @@ function Thumbnailer:on_file_loaded()
3338end
3439
3540function Thumbnailer :on_thumb_ready (index )
36- self .state .thumbnails [index ] = true
41+ self .state .thumbnails [index ] = 1
3742
38- -- Recount (just in case )
43+ -- Full recount instead of a naive increment (let's be safe! )
3944 self .state .finished_thumbnails = 0
40- for i in pairs (self .state .thumbnails ) do
41- self .state .finished_thumbnails = self .state .finished_thumbnails + 1
45+ for i , v in pairs (self .state .thumbnails ) do
46+ if v > 0 then
47+ self .state .finished_thumbnails = self .state .finished_thumbnails + 1
48+ end
4249 end
4350end
4451
4552function Thumbnailer :on_thumb_progress (index )
46- self .state .thumbnails [index ] = self .state .thumbnails [index ] or false
53+ self .state .thumbnails [index ] = math.max ( self .state .thumbnails [index ], 0 )
4754end
4855
4956function Thumbnailer :on_video_change (params )
5865
5966function Thumbnailer :update_state ()
6067 self .state .thumbnail_delta = self :get_delta ()
61- self .state .thumbnail_count = self :get_thumbnail_count ()
68+ self .state .thumbnail_count = self :get_thumbnail_count (self .state .thumbnail_delta )
69+
70+ -- Prefill individual thumbnail states
71+ for i = 1 , self .state .thumbnail_count do
72+ self .state .thumbnails [i ] = - 1
73+ end
6274
6375 self .state .thumbnail_template = self :get_thumbnail_template ()
6476 self .state .thumbnail_size = self :get_thumbnail_size ()
@@ -165,8 +177,7 @@ function Thumbnailer:get_delta()
165177end
166178
167179
168- function Thumbnailer :get_thumbnail_count ()
169- local delta = self :get_delta ()
180+ function Thumbnailer :get_thumbnail_count (delta )
170181 if delta == nil then
171182 return 0
172183 end
@@ -176,26 +187,50 @@ function Thumbnailer:get_thumbnail_count()
176187end
177188
178189function Thumbnailer :get_closest (thumbnail_index )
179- local min_distance = self .state .thumbnail_count + 1
190+ -- Given a 1-based index, find the closest available thumbnail and return it's 1-based index
191+
192+ -- Check the direct thumbnail index first
193+ if self .state .thumbnails [thumbnail_index ] > 0 then
194+ return thumbnail_index
195+ end
196+
197+ local min_distance = self .state .thumbnail_count + 1
180198 local closest = nil
181199
200+ -- Naive, inefficient, lazy. But functional.
182201 for index , value in pairs (self .state .thumbnails ) do
183202 local distance = math.abs (index - thumbnail_index )
184- if distance < min_distance and value then
203+ if distance < min_distance and value > 0 then
185204 min_distance = distance
186205 closest = index
187206 end
188207 end
189- return closest , min_distance
208+ return closest
209+ end
210+
211+ function Thumbnailer :get_thumbnail_index (time_position )
212+ -- Returns a 1-based thumbnail index for the given timestamp (between 1 and thumbnail_count, inclusive)
213+ if self .state .thumbnail_delta and (self .state .thumbnail_count and self .state .thumbnail_count > 0 ) then
214+ return math.min (math.floor (time_position / self .state .thumbnail_delta ) + 1 , self .state .thumbnail_count )
215+ else
216+ return nil
217+ end
190218end
191219
192220function Thumbnailer :get_thumbnail_path (time_position )
193- local thumbnail_index = math.min (math.floor (time_position / self .state .thumbnail_delta ), self .state .thumbnail_count - 1 )
221+ -- Given a timestamp, return:
222+ -- the closest available thumbnail path (if any)
223+ -- the 1-based thumbnail index calculated from the timestamp
224+ -- the 1-based thumbnail index of the closest available (and used) thumbnail
225+ -- OR nil if thumbnails are not available.
226+
227+ local thumbnail_index = self :get_thumbnail_index (time_position )
228+ if not thumbnail_index then return nil end
194229
195- local closest , distance = self :get_closest (thumbnail_index )
230+ local closest = self :get_closest (thumbnail_index )
196231
197232 if closest ~= nil then
198- return self .state .thumbnail_template :format (closest ), thumbnail_index , closest
233+ return self .state .thumbnail_template :format (closest - 1 ), thumbnail_index , closest
199234 else
200235 return nil , thumbnail_index , nil
201236 end
@@ -243,13 +278,16 @@ function Thumbnailer:register_client()
243278end
244279
245280function Thumbnailer :_create_thumbnail_job_order ()
281+ -- Returns a list of 1-based thumbnail indices in a job order
246282 local used_frames = {}
247283 local work_frames = {}
248284
285+ -- Pick frames in increasing frequency.
286+ -- This way we can do a quick few passes over the video and then fill in the gaps.
249287 for x = 6 , 0 , - 1 do
250288 local nth = (2 ^ x )
251289
252- for thi = 0 , self .state .thumbnail_count - 1 , nth do
290+ for thi = 1 , self .state .thumbnail_count , nth do
253291 if not used_frames [thi ] then
254292 table.insert (work_frames , thi )
255293 used_frames [thi ] = true
0 commit comments