mirror of
https://github.com/sxyazi/yazi.git
synced 2026-07-25 08:41:05 +00:00
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:
parent
ff440914ad
commit
46ddb10a7a
4 changed files with 65 additions and 8 deletions
|
|
@ -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!();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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() }
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 }
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue