added feature for multiple selection of videos, showing their duration sum on top of the info of the shown file

This commit is contained in:
salah0eldin 2026-01-06 21:31:09 +02:00
parent ff440914ad
commit 46ddb10a7a
4 changed files with 65 additions and 8 deletions

View file

@ -20,13 +20,16 @@ impl Actor for Spot {
// self.active_mut().spot.reset();
// }
// Collect selected files
let selected: Vec<_> = cx.tab().selected.values().cloned().collect();
if let Some(skip) = opt.skip {
cx.tab_mut().spot.skip = skip;
} else if !cx.tab().spot.same_url(&hovered.url) {
cx.tab_mut().spot.skip = 0;
}
cx.tab_mut().spot.go(hovered, mime);
cx.tab_mut().spot.go(hovered, mime, selected);
succ!();
}
}

View file

@ -15,7 +15,7 @@ pub struct Spot {
}
impl Spot {
pub fn go(&mut self, file: File, mime: Symbol<str>) {
pub fn go(&mut self, file: File, mime: Symbol<str>, selected: Vec<UrlBuf>) {
if mime.is_empty() {
return; // Wait till mimetype is resolved to avoid flickering
} else if self.same_lock(&file, &mime) {
@ -27,7 +27,7 @@ impl Spot {
};
self.abort();
self.ct = Some(isolate::spot(&spotter.run, file, mime, self.skip));
self.ct = Some(isolate::spot(&spotter.run, file, mime, self.skip, selected));
}
pub fn visible(&self) -> bool { self.lock.is_some() }

View file

@ -101,10 +101,57 @@ function M:spot(job)
end
function M:spot_base(job)
local rows = {}
-- Check if there are multiple selected files
if job.selected and #job.selected > 1 then
-- Multi-file selection: show sum at the top
local total_dur = 0
local video_count = 0
-- Start all ffprobe commands concurrently
local commands = {}
for i, file in ipairs(job.selected) do
commands[i] = Command("ffprobe")
:arg { "-v", "quiet", "-select_streams", "v", "-show_entries", "format=duration", "-of", "json=c=1", tostring(file.path) }
:stdout(Command.PIPED)
:spawn()
end
-- Collect results
for i, child in ipairs(commands) do
local output, err = child:wait_with_output()
if output and output.status.success then
local t = ya.json_decode(output.stdout)
if t and type(t) == "table" and t.format and t.format.duration then
total_dur = total_dur + t.format.duration
video_count = video_count + 1
end
end
end
-- Format total duration
local hours = math.floor(total_dur / 3600)
local mins = math.floor((total_dur % 3600) / 60)
local secs = math.floor(total_dur % 60)
local duration_str
if hours > 0 then
duration_str = string.format("%d:%02d:%02d", hours, mins, secs)
else
duration_str = string.format("%d:%02d", mins, secs)
end
rows[#rows + 1] = ui.Row({ string.format("# Videos (%d)", video_count) }):style(ui.Style():fg("green"))
rows[#rows + 1] = ui.Row { " Duration:", duration_str }
rows[#rows + 1] = ui.Row {} -- Empty row separator
end
-- Show detailed info for the current hovered file
local meta, err = self.list_meta(job.file.path, "format=duration:stream=codec_name,codec_type,width,height")
if not meta then
ya.err(tostring(err))
return {}
return rows -- Return at least the selection info if available
end
local dur = meta.format.duration or 0
@ -119,10 +166,8 @@ function M:spot_base(job)
duration_str = string.format("%d:%02d", mins, secs)
end
local rows = {
ui.Row({ "Video" }):style(ui.Style():fg("green")),
ui.Row { " Duration:", duration_str },
}
rows[#rows + 1] = ui.Row({ "Video" }):style(ui.Style():fg("green"))
rows[#rows + 1] = ui.Row { " Duration:", duration_str }
for i, s in ipairs(meta.streams) do
if s.codec_type == "video" then

View file

@ -16,6 +16,7 @@ pub fn spot(
file: yazi_fs::File,
mime: Symbol<str>,
skip: usize,
selected: Vec<yazi_shared::url::UrlBuf>,
) -> CancellationToken {
let ct = CancellationToken::new();
let (ct1, ct2) = (ct.clone(), ct.clone());
@ -37,12 +38,20 @@ pub fn spot(
)?;
let plugin = LOADER.load_once(&lua, &cmd.name)?;
// Convert selected URLs to Lua table
let selected_table = lua.create_table()?;
for (i, url) in selected.iter().enumerate() {
selected_table.set(i + 1, yazi_binding::Url::new(url.clone()))?;
}
let job = lua.create_table_from([
("id", Id(IDS.next()).into_lua(&lua)?),
("args", Sendable::args_to_table_ref(&lua, &cmd.args)?.into_lua(&lua)?),
("file", File::new(file).into_lua(&lua)?),
("mime", mime.into_lua(&lua)?),
("skip", skip.into_lua(&lua)?),
("selected", selected_table.into_lua(&lua)?),
])?;
if ct2.is_cancelled() { Ok(()) } else { plugin.call_async_method("spot", job).await }