From 81ccdd8b645ebfa9471c2d8bb13a7c23abc0fe74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E5=92=B2=E9=9B=85=20misaki=20masa?= Date: Sat, 29 Nov 2025 10:06:05 +0800 Subject: [PATCH] feat: include stderr along with existing error code when video preview fails (#3383) --- yazi-plugin/preset/plugins/video.lua | 25 ++++++++++++------------ yazi-plugin/src/process/command.rs | 29 ++++++++++++++-------------- 2 files changed, 26 insertions(+), 28 deletions(-) diff --git a/yazi-plugin/preset/plugins/video.lua b/yazi-plugin/preset/plugins/video.lua index 3edefcc6..890c48cd 100644 --- a/yazi-plugin/preset/plugins/video.lua +++ b/yazi-plugin/preset/plugins/video.lua @@ -52,36 +52,35 @@ function M:preload(job) return false end - -- stylua: ignore - local cmd = Command("ffmpeg"):arg({ - "-v", "quiet", "-threads", 1, "-hwaccel", "auto", - "-skip_frame", "nokey", - "-an", "-sn", "-dn", - }) + local cmd = Command("ffmpeg") + :stderr(Command.PIPED) + :arg { "-v", "warning", "-hwaccel", "auto", "-threads", 1, "-an", "-sn", "-dn" } if percent ~= 0 then cmd:arg { "-ss", math.floor(meta.format.duration * percent / 100) } end - cmd:arg { "-i", tostring(job.file.url) } + cmd:arg { "-skip_frame", "nokey", "-i", tostring(job.file.url) } if percent == 0 then cmd:arg { "-map", "disp:attached_pic" } end -- stylua: ignore - local status, err = cmd:arg({ + local output, err = cmd:arg({ "-vframes", 1, "-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), - }):status() + }):output() - if not status then + if not output then return true, Err("Failed to start `ffmpeg`, error: %s", err) - elseif not status.success then - return false, Err("`ffmpeg` exited with error code: %s", status.code) - else + elseif output.status.success then return true + elseif output.stderr:find("No filtered frames for output stream", 1, true) then + return true, Err("No more keyframes available") + else + return false, Err("`ffmpeg` exited with error code %s: %s", output.status.code, output.stderr) end end diff --git a/yazi-plugin/src/process/command.rs b/yazi-plugin/src/process/command.rs index 93fb9a48..67d4b3d4 100644 --- a/yazi-plugin/src/process/command.rs +++ b/yazi-plugin/src/process/command.rs @@ -1,6 +1,6 @@ use std::{any::TypeId, ffi::OsStr, io, process::Stdio}; -use mlua::{AnyUserData, ExternalError, IntoLuaMulti, Lua, MetaMethod, Table, UserData, Value}; +use mlua::{AnyUserData, ExternalError, IntoLua, IntoLuaMulti, Lua, MetaMethod, Table, UserData, Value}; use tokio::process::{ChildStderr, ChildStdin, ChildStdout}; use yazi_binding::Error; use yazi_shared::wtf8::FromWtf8; @@ -139,22 +139,21 @@ impl UserData for Command { ) } - methods.add_function_mut("arg", |_, (ud, arg): (AnyUserData, Value)| { - { - let mut me = ud.borrow_mut::()?; - match arg { - Value::String(s) => { - me.inner.arg(OsStr::from_wtf8(&s.as_bytes())?); - } - Value::Table(t) => { - for s in t.sequence_values::() { - me.inner.arg(OsStr::from_wtf8(&s?.as_bytes())?); - } - } - _ => return Err("arg must be a string or table of strings".into_lua_err()), + methods.add_function_mut("arg", |lua, (ud, arg): (AnyUserData, Value)| { + let mut me = ud.borrow_mut::()?; + match arg { + Value::Nil => return lua.create_sequence_from(me.inner.as_std().get_args())?.into_lua(lua), + Value::String(s) => { + me.inner.arg(OsStr::from_wtf8(&s.as_bytes())?); } + Value::Table(t) => { + for s in t.sequence_values::() { + me.inner.arg(OsStr::from_wtf8(&s?.as_bytes())?); + } + } + _ => Err("arg must be a string or table of strings".into_lua_err())?, } - Ok(ud) + ud.into_lua(lua) }); methods.add_function_mut("cwd", |_, (ud, dir): (AnyUserData, mlua::String)| { ud.borrow_mut::()?.inner.current_dir(dir.to_str()?.as_ref());