From 88bbbac7dd3f7a712bb720cf4ebeea1fc44fe4ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E5=92=B2=E9=9B=85=20=C2=B7=20Misaki=20Masa?= Date: Sat, 19 Apr 2025 23:13:11 +0800 Subject: [PATCH] feat: support embedded cover for video preview (#2640) --- yazi-plugin/preset/plugins/video.lua | 52 ++++++++++++++++++++-------- 1 file changed, 37 insertions(+), 15 deletions(-) diff --git a/yazi-plugin/preset/plugins/video.lua b/yazi-plugin/preset/plugins/video.lua index e9ca2f1d..7c45630e 100644 --- a/yazi-plugin/preset/plugins/video.lua +++ b/yazi-plugin/preset/plugins/video.lua @@ -27,12 +27,6 @@ function M:seek(job) end function M:preload(job) - local percent = 5 + job.skip - if percent > 95 then - ya.mgr_emit("peek", { 90, only_if = job.file.url, upper_bound = true }) - return false - end - local cache = ya.file_cache(job) if not cache then return true @@ -43,23 +37,39 @@ function M:preload(job) return true end - local meta, err = self.list_meta(job.file.url, "format=duration") + local meta, err = self.list_meta(job.file.url, "format=duration:stream_disposition=attached_pic") if not meta then return true, err elseif not meta.format.duration then return true, Err("Failed to get video duration") end - local ss = math.floor(meta.format.duration * percent / 100) - local qv = 31 - math.floor(rt.preview.image_quality * 0.3) + local pic = M.has_pic(meta) + local percent = (pic and 0 or 5) + job.skip + if percent > 95 then + ya.mgr_emit("peek", { pic and 95 or 90, only_if = job.file.url, upper_bound = true }) + return false + end + -- stylua: ignore - local status, err = Command("ffmpeg"):args({ + local cmd = Command("ffmpeg"):args({ "-v", "quiet", "-threads", 1, "-hwaccel", "auto", - "-skip_frame", "nokey", "-ss", ss, + "-skip_frame", "nokey", "-an", "-sn", "-dn", - "-i", tostring(job.file.url), + }) + + if percent ~= 0 then + cmd:args { "-ss", math.floor(meta.format.duration * percent / 100) } + end + cmd:args { "-i", tostring(job.file.url) } + if percent == 0 then + cmd:args { "-map", "disp:attached_pic" } + end + + -- stylua: ignore + local status, err = cmd:args({ "-vframes", 1, - "-q:v", qv, + "-q:v", 31 - math.floor(rt.preview.image_quality * 0.3), "-vf", string.format("scale='min(%d,iw)':'min(%d,ih)':force_original_aspect_ratio=decrease:flags=fast_bilinear", rt.preview.max_width, rt.preview.max_height), "-f", "image2", "-y", tostring(cache), @@ -115,8 +125,12 @@ function M:spot_base(job) end function M.list_meta(url, entries) - local output, err = - Command("ffprobe"):args({ "-v", "quiet", "-show_entries", entries, "-of", "json=c=1", tostring(url) }):output() + local cmd = Command("ffprobe"):args { "-v", "quiet" } + if not entries:find("attached_pic", 1, true) then + cmd = cmd:args { "-select_streams", "v" } + end + + local output, err = cmd:args({ "-show_entries", entries, "-of", "json=c=1", tostring(url) }):output() if not output then return nil, Err("Failed to start `ffprobe`, error: %s", err) end @@ -133,4 +147,12 @@ function M.list_meta(url, entries) return t end +function M.has_pic(meta) + for _, s in ipairs(meta.streams) do + if s.disposition and s.disposition.attached_pic == 1 then + return true + end + end +end + return M