diff --git a/src/thumbnailer_shared.lua b/src/thumbnailer_shared.lua index 37390e7..714bf2e 100644 --- a/src/thumbnailer_shared.lua +++ b/src/thumbnailer_shared.lua @@ -73,94 +73,103 @@ end function Thumbnailer:on_video_change(params) -- Gather a new state when we get proper video-dec-params and our state is empty - if params then - if not self.state.ready then - self:update_state() - self:check_storyboard_async(function() - local duration = mp.get_property_native("duration") - local max_duration - if self.state.is_remote then - max_duration = thumbnailer_options.autogenerate_max_duration_remote - else - max_duration = thumbnailer_options.autogenerate_max_duration - end - local max_duration = thumbnailer_options.autogenerate_max_duration - - if duration and self.state.available and thumbnailer_options.autogenerate then - -- Notify if autogenerate is on and video is not too long - if duration < max_duration or max_duration == 0 then - self:start_worker_jobs() - end - end - end) + if not params or self.state.ready then + return + end + + self:update_state() + self:check_storyboard() + + local duration = mp.get_property_native("duration") + local max_duration + if self.state.is_remote then + max_duration = thumbnailer_options.autogenerate_max_duration_remote + else + max_duration = thumbnailer_options.autogenerate_max_duration + end + local max_duration = thumbnailer_options.autogenerate_max_duration + + if duration and self.state.available and thumbnailer_options.autogenerate then + -- Notify if autogenerate is on and video is not too long + if duration < max_duration or max_duration == 0 then + self:start_worker_jobs() end end end --- Check for storyboards existance with yt-dlp and call back (may take a long time) -function Thumbnailer:check_storyboard_async(callback) - if thumbnailer_options.storyboard_enable and self.state.is_remote then - msg.info("Trying to get storyboard info...") - local sb_cmd = {"yt-dlp", "--format", "sb0", "--dump-json", "--no-playlist", - "--extractor-args", "youtube:skip=hls,dash,translated_subs", -- yt speedup - "--", mp.get_property_native("path")} - - mp.command_native_async({name="subprocess", args=sb_cmd, capture_stdout=true}, function(success, sb_json) - if success and sb_json.status == 0 then - local sb = utils.parse_json(sb_json.stdout) - if sb and sb.duration and sb.width and sb.height and sb.fragments and #sb.fragments > 0 then - self.state.storyboard = {} - self.state.storyboard.fragments = sb.fragments - self.state.storyboard.fragment_base_url = sb.fragment_base_url - self.state.storyboard.rows = sb.rows or 5 - self.state.storyboard.cols = sb.columns or 5 - - if sb.fps then - self.state.thumbnail_count = math.floor(sb.fps * sb.duration + 0.5) -- round - -- hack: youtube always adds 1 black frame at the end... - if sb.extractor == "youtube" then - self.state.thumbnail_count = self.state.thumbnail_count - 1 - end - else - -- estimate the count of thumbnails - -- assume first atlas is always full - self.state.thumbnail_delta = sb.fragments[1].duration / (self.state.storyboard.rows*self.state.storyboard.cols) - self.state.thumbnail_count = math.floor(sb.duration / self.state.thumbnail_delta) - end - - -- Storyboard upscaling factor - local scale = 1 - if thumbnailer_options.storyboard_upscale then - -- BUG: sometimes mpv crashes when asked for non-integer scaling and BGRA format (something related to zimg?) - -- use integer scaling for now - scale = math.max(1, math.floor(thumbnailer_options.thumbnail_height / sb.height)) - end - self.state.thumbnail_size = {w=sb.width*scale, h=sb.height*scale} - self.state.storyboard.scale = scale - - local divisor = 1 -- only save every n-th thumbnail - if thumbnailer_options.storyboard_max_thumbnail_count then - divisor = math.ceil(self.state.thumbnail_count / thumbnailer_options.storyboard_max_thumbnail_count) - end - self.state.storyboard.divisor = divisor - self.state.thumbnail_count = math.floor(self.state.thumbnail_count / divisor) - self.state.thumbnail_delta = sb.duration / self.state.thumbnail_count - - - -- Prefill individual thumbnail states - self.state.thumbnails = {} - for i = 1, self.state.thumbnail_count do - self.state.thumbnails[i] = -1 - end - msg.info("Storyboard info acquired! " .. self.state.thumbnail_count) - self.state.available = true - end - end - callback() - end) +-- Check for storyboards in user-data +function Thumbnailer:check_storyboard() + if not thumbnailer_options.storyboard_enable or not self.state.is_remote then + return + end + + local json = mp.get_property_native("user-data/mpv/ytdl/json-subprocess-result/stdout") + if not json then + return + end + + json = utils.parse_json(json) + if not json then + return + end + + local sb + for _, format in pairs(json.formats) do + if format.format_id == "sb0" then + sb = format + break + end + end + local duration = json.duration + + if not (sb and duration and sb.width and sb.height and sb.fragments and #sb.fragments > 0) then + return + end + + self.state.storyboard = {} + self.state.storyboard.fragments = sb.fragments + self.state.storyboard.fragment_base_url = sb.fragment_base_url + self.state.storyboard.rows = sb.rows or 5 + self.state.storyboard.cols = sb.columns or 5 + + if sb.fps then + self.state.thumbnail_count = math.floor(sb.fps * duration + 0.5) -- round + -- hack: youtube always adds 1 black frame at the end... + if sb.extractor == "youtube" then + self.state.thumbnail_count = self.state.thumbnail_count - 1 + end else - callback() + -- estimate the count of thumbnails + -- assume first atlas is always full + self.state.thumbnail_delta = sb.fragments[1].duration / (self.state.storyboard.rows*self.state.storyboard.cols) + self.state.thumbnail_count = math.floor(duration / self.state.thumbnail_delta) + end + + -- Storyboard upscaling factor + local scale = 1 + if thumbnailer_options.storyboard_upscale then + -- BUG: sometimes mpv crashes when asked for non-integer scaling and BGRA format (something related to zimg?) + -- use integer scaling for now + scale = math.max(1, math.floor(thumbnailer_options.thumbnail_height / sb.height)) + end + self.state.thumbnail_size = {w=sb.width*scale, h=sb.height*scale} + self.state.storyboard.scale = scale + + local divisor = 1 -- only save every n-th thumbnail + if thumbnailer_options.storyboard_max_thumbnail_count then + divisor = math.ceil(self.state.thumbnail_count / thumbnailer_options.storyboard_max_thumbnail_count) + end + self.state.storyboard.divisor = divisor + self.state.thumbnail_count = math.floor(self.state.thumbnail_count / divisor) + self.state.thumbnail_delta = duration / self.state.thumbnail_count + + -- Prefill individual thumbnail states + self.state.thumbnails = {} + for i = 1, self.state.thumbnail_count do + self.state.thumbnails[i] = -1 end + msg.info("Storyboard info acquired! " .. self.state.thumbnail_count) + self.state.available = true end