From 3cdb0b6e7cd0e09f2b03580923d69e9b57efd00c 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: Thu, 21 Nov 2024 20:55:50 +0800 Subject: [PATCH] feat!: video spotter & using `ffmpeg` instead of `ffmpegthumbnailer` as previewer backend (#1928) --- Cargo.lock | 1 + cspell.json | 2 +- yazi-boot/src/actions/debug.rs | 29 ++++----- yazi-plugin/Cargo.toml | 1 + yazi-plugin/preset/plugins/file.lua | 77 +++++++++-------------- yazi-plugin/preset/plugins/image.lua | 29 ++++----- yazi-plugin/preset/plugins/video.lua | 94 +++++++++++++++++++++++----- yazi-plugin/preset/setup.lua | 1 - yazi-plugin/preset/ya.lua | 1 + yazi-plugin/src/error.rs | 25 ++++++++ yazi-plugin/src/isolate/isolate.rs | 1 + yazi-plugin/src/isolate/peek.rs | 2 +- yazi-plugin/src/isolate/spot.rs | 2 +- yazi-plugin/src/lib.rs | 2 +- yazi-plugin/src/lua.rs | 1 + yazi-plugin/src/utils/json.rs | 24 +++++++ yazi-plugin/src/utils/mod.rs | 2 +- yazi-plugin/src/utils/utils.rs | 4 ++ 18 files changed, 203 insertions(+), 95 deletions(-) create mode 100644 yazi-plugin/src/error.rs create mode 100644 yazi-plugin/src/utils/json.rs diff --git a/Cargo.lock b/Cargo.lock index 8e237baed..6836954dd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3550,6 +3550,7 @@ dependencies = [ "mlua", "parking_lot", "ratatui", + "serde_json", "shell-words", "syntect", "tokio", diff --git a/cspell.json b/cspell.json index fd7580787..c1f5d8e5d 100644 --- a/cspell.json +++ b/cspell.json @@ -1 +1 @@ -{"flagWords":[],"words":["Punct","KEYMAP","splitn","crossterm","YAZI","unar","peekable","ratatui","syntect","pbpaste","pbcopy","ffmpegthumbnailer","oneshot","Posix","Lsar","XADDOS","zoxide","cands","Deque","precache","imageops","IFBLK","IFCHR","IFDIR","IFIFO","IFLNK","IFMT","IFSOCK","IRGRP","IROTH","IRUSR","ISGID","ISUID","ISVTX","IWGRP","IWOTH","IWUSR","IXGRP","IXOTH","IXUSR","libc","winsize","TIOCGWINSZ","xpixel","ypixel","ioerr","appender","Catppuccin","macchiato","gitmodules","Dotfiles","bashprofile","vimrc","flac","webp","exiftool","mediainfo","ripgrep","nvim","indexmap","indexmap","unwatch","canonicalize","serde","fsevent","Ueberzug","iterm","wezterm","sixel","chafa","ueberzugpp","️ Überzug","️ Überzug","Konsole","Alacritty","Überzug","pkgs","paru","unarchiver","pdftoppm","poppler","prebuild","singlefile","jpegopt","EXIF","rustfmt","mktemp","nanos","xclip","xsel","natord","Mintty","nixos","nixpkgs","SIGTSTP","SIGCONT","SIGCONT","mlua","nonstatic","userdata","metatable","natsort","backstack","luajit","Succ","Succ","cand","fileencoding","foldmethod","lightgreen","darkgray","lightred","lightyellow","lightcyan","nushell","msvc","aarch","linemode","sxyazi","rsplit","ZELLIJ","bitflags","bitflags","USERPROFILE","Neovim","vergen","gitcl","Renderable","preloaders","prec","Upserting","prio","Ghostty","Catmull","Lanczos","cmds","unyank","scrolloff","headsup","unsub","uzers","scopeguard","SPDLOG","globset","filetime","magick","magick","prefetcher","Prework","prefetchers","PREWORKERS","conds","translit","rxvt","Urxvt","realpath","realname","REPARSE","hardlink","hardlinking","nlink","nlink","linemodes","SIGSTOP","sevenzip","rsplitn","replacen","DECSET","DECRQM","repeek","cwds","tcsi","Hyprland","Wayfire","SWAYSOCK","btime","nsec","codegen","gethostname","fchmod","fdfind","Rustc","rustc","Sysinfo"],"language":"en","version":"0.2"} +{"language":"en","flagWords":[],"words":["Punct","KEYMAP","splitn","crossterm","YAZI","unar","peekable","ratatui","syntect","pbpaste","pbcopy","oneshot","Posix","Lsar","XADDOS","zoxide","cands","Deque","precache","imageops","IFBLK","IFCHR","IFDIR","IFIFO","IFLNK","IFMT","IFSOCK","IRGRP","IROTH","IRUSR","ISGID","ISUID","ISVTX","IWGRP","IWOTH","IWUSR","IXGRP","IXOTH","IXUSR","libc","winsize","TIOCGWINSZ","xpixel","ypixel","ioerr","appender","Catppuccin","macchiato","gitmodules","Dotfiles","bashprofile","vimrc","flac","webp","exiftool","mediainfo","ripgrep","nvim","indexmap","indexmap","unwatch","canonicalize","serde","fsevent","Ueberzug","iterm","wezterm","sixel","chafa","ueberzugpp","️ Überzug","️ Überzug","Konsole","Alacritty","Überzug","pkgs","paru","unarchiver","pdftoppm","poppler","prebuild","singlefile","jpegopt","EXIF","rustfmt","mktemp","nanos","xclip","xsel","natord","Mintty","nixos","nixpkgs","SIGTSTP","SIGCONT","SIGCONT","mlua","nonstatic","userdata","metatable","natsort","backstack","luajit","Succ","Succ","cand","fileencoding","foldmethod","lightgreen","darkgray","lightred","lightyellow","lightcyan","nushell","msvc","aarch","linemode","sxyazi","rsplit","ZELLIJ","bitflags","bitflags","USERPROFILE","Neovim","vergen","gitcl","Renderable","preloaders","prec","Upserting","prio","Ghostty","Catmull","Lanczos","cmds","unyank","scrolloff","headsup","unsub","uzers","scopeguard","SPDLOG","globset","filetime","magick","magick","prefetcher","Prework","prefetchers","PREWORKERS","conds","translit","rxvt","Urxvt","realpath","realname","REPARSE","hardlink","hardlinking","nlink","nlink","linemodes","SIGSTOP","sevenzip","rsplitn","replacen","DECSET","DECRQM","repeek","cwds","tcsi","Hyprland","Wayfire","SWAYSOCK","btime","nsec","codegen","gethostname","fchmod","fdfind","Rustc","rustc","Sysinfo","ffprobe","vframes"],"version":"0.2"} \ No newline at end of file diff --git a/yazi-boot/src/actions/debug.rs b/yazi-boot/src/actions/debug.rs index e5859d6e1..30dd6a24a 100644 --- a/yazi-boot/src/actions/debug.rs +++ b/yazi-boot/src/actions/debug.rs @@ -76,20 +76,21 @@ impl Actions { writeln!(s, "\nDependencies")?; #[rustfmt::skip] - writeln!(s, " file : {}", Self::process_output(env::var_os("YAZI_FILE_ONE").unwrap_or("file".into()), "--version"))?; - writeln!(s, " ueberzugpp : {}", Self::process_output("ueberzugpp", "--version"))?; - writeln!(s, " ffmpegthumbnailer: {}", Self::process_output("ffmpegthumbnailer", "-v"))?; - writeln!(s, " pdftoppm : {}", Self::process_output("pdftoppm", "--help"))?; - writeln!(s, " magick : {}", Self::process_output("magick", "--version"))?; - writeln!(s, " fzf : {}", Self::process_output("fzf", "--version"))?; - writeln!(s, " fd : {}", Self::process_output("fd", "--version"))?; - writeln!(s, " fdfind : {}", Self::process_output("fdfind", "--version"))?; - writeln!(s, " rg : {}", Self::process_output("rg", "--version"))?; - writeln!(s, " chafa : {}", Self::process_output("chafa", "--version"))?; - writeln!(s, " zoxide : {}", Self::process_output("zoxide", "--version"))?; - writeln!(s, " 7z : {}", Self::process_output("7z", "i"))?; - writeln!(s, " 7zz : {}", Self::process_output("7zz", "i"))?; - writeln!(s, " jq : {}", Self::process_output("jq", "--version"))?; + writeln!(s, " file : {}", Self::process_output(env::var_os("YAZI_FILE_ONE").unwrap_or("file".into()), "--version"))?; + writeln!(s, " ueberzugpp : {}", Self::process_output("ueberzugpp", "--version"))?; + #[rustfmt::skip] + writeln!(s, " ffmpeg/ffprobe: {} / {}", Self::process_output("ffmpeg", "-version"), Self::process_output("ffprobe", "-version"))?; + writeln!(s, " pdftoppm : {}", Self::process_output("pdftoppm", "--help"))?; + writeln!(s, " magick : {}", Self::process_output("magick", "--version"))?; + writeln!(s, " fzf : {}", Self::process_output("fzf", "--version"))?; + #[rustfmt::skip] + writeln!(s, " fd/fdfind : {} / {}", Self::process_output("fd", "--version"), Self::process_output("fdfind", "--version"))?; + writeln!(s, " rg : {}", Self::process_output("rg", "--version"))?; + writeln!(s, " chafa : {}", Self::process_output("chafa", "--version"))?; + writeln!(s, " zoxide : {}", Self::process_output("zoxide", "--version"))?; + #[rustfmt::skip] + writeln!(s, " 7z/7zz : {} / {}", Self::process_output("7z", "i"), Self::process_output("7zz", "i"))?; + writeln!(s, " jq : {}", Self::process_output("jq", "--version"))?; writeln!(s, "\nClipboard")?; #[rustfmt::skip] diff --git a/yazi-plugin/Cargo.toml b/yazi-plugin/Cargo.toml index d49f97068..741cdcad6 100644 --- a/yazi-plugin/Cargo.toml +++ b/yazi-plugin/Cargo.toml @@ -32,6 +32,7 @@ md-5 = { workspace = true } mlua = { workspace = true } parking_lot = { workspace = true } ratatui = { workspace = true } +serde_json = { workspace = true } shell-words = { workspace = true } syntect = { version = "5.2.0", default-features = false, features = [ "parsing", "plist-load", "regex-onig" ] } tokio = { workspace = true } diff --git a/yazi-plugin/preset/plugins/file.lua b/yazi-plugin/preset/plugins/file.lua index 40ae48c28..001ae1dc3 100644 --- a/yazi-plugin/preset/plugins/file.lua +++ b/yazi-plugin/preset/plugins/file.lua @@ -16,56 +16,10 @@ end function M:seek() end --- TODO: remove this -local hovered_mime = ya.sync(function() - local h = cx.active.current.hovered - if not h then - return nil - elseif h.cha.is_dir then - return "inode/directory" - else - return h:mime() - end -end) - function M:spot(job) - local mime = hovered_mime() - if not mime then - return - end - - local file = job.file - local spotter = PLUGIN.spotter(file.url, mime) - local previewer = PLUGIN.previewer(file.url, mime) - local fetchers = PLUGIN.fetchers(file.url, mime) - local preloaders = PLUGIN.preloaders(file.url, mime) - - for i, v in ipairs(fetchers) do - fetchers[i] = v.cmd - end - for i, v in ipairs(preloaders) do - preloaders[i] = v.cmd - end - - local rows = {} - local row = function(key, value) - local h = type(value) == "table" and #value or 1 - rows[#rows + 1] = ui.Row({ key, value }):height(h) - end - - rows[#rows + 1] = ui.Row({ "Metadata", "" }):style(ui.Style():fg("red")) - row(" Created:", file.cha.btime and os.date("%y/%m/%d %H:%M", math.floor(file.cha.btime)) or "-") - row(" Modified:", file.cha.mtime and os.date("%y/%m/%d %H:%M", math.floor(file.cha.mtime)) or "-") - row(" Mimetype:", mime) - rows[#rows + 1] = ui.Row({ { "", "Plugins" }, "" }):height(2):style(ui.Style():fg("red")) - row(" Spotter:", spotter and spotter.cmd or "-") - row(" Previewer:", previewer and previewer.cmd or "-") - row(" Fetchers:", #fetchers ~= 0 and fetchers or "-") - row(" Preloaders:", #preloaders ~= 0 and preloaders or "-") - ya.spot_table( job, - ui.Table(rows) + ui.Table(self:spot_base(job)) :area(ui.Pos { "center", w = 60, h = 20 }) :row(1) :col(1) @@ -75,4 +29,33 @@ function M:spot(job) ) end +function M:spot_base(job) + local url, cha = job.file.url, job.file.cha + local spotter = PLUGIN.spotter(url, job.mime) + local previewer = PLUGIN.previewer(url, job.mime) + local fetchers = PLUGIN.fetchers(url, job.mime) + local preloaders = PLUGIN.preloaders(url, job.mime) + + for i, v in ipairs(fetchers) do + fetchers[i] = v.cmd + end + for i, v in ipairs(preloaders) do + preloaders[i] = v.cmd + end + + return { + ui.Row({ "Base" }):style(ui.Style():fg("green")), + ui.Row { " Created:", cha.btime and os.date("%y/%m/%d %H:%M", math.floor(cha.btime)) or "-" }, + ui.Row { " Modified:", cha.mtime and os.date("%y/%m/%d %H:%M", math.floor(cha.mtime)) or "-" }, + ui.Row { " Mimetype:", job.mime }, + ui.Row {}, + + ui.Row({ "Plugins" }):style(ui.Style():fg("green")), + ui.Row { " Spotter:", spotter and spotter.cmd or "-" }, + ui.Row { " Previewer:", previewer and previewer.cmd or "-" }, + ui.Row { " Fetchers:", #fetchers ~= 0 and fetchers or "-" }, + ui.Row { " Preloaders:", #preloaders ~= 0 and preloaders or "-" }, + } +end + return M diff --git a/yazi-plugin/preset/plugins/image.lua b/yazi-plugin/preset/plugins/image.lua index ca913417c..05a17041a 100644 --- a/yazi-plugin/preset/plugins/image.lua +++ b/yazi-plugin/preset/plugins/image.lua @@ -23,29 +23,30 @@ function M:preload() end function M:spot(job) - local info = ya.image_info(job.file.url) - - local rows = {} - local row = function(key, value) - local h = type(value) == "table" and #value or 1 - rows[#rows + 1] = ui.Row({ key, value }):height(h) - end - - row("Format:", tostring(info.format)) - row("Width:", string.format("%dpx", info.w)) - row("Height:", string.format("%dpx", info.h)) - row("Color:", tostring(info.color)) + local rows = self:spot_base(job) + rows[#rows + 1] = ui.Row {} ya.spot_table( job, - ui.Table(rows) + ui.Table(ya.list_merge(rows, require("file"):spot_base(job))) :area(ui.Pos { "center", w = 60, h = 20 }) :row(job.skip) + :row(1) :col(1) :col_style(ui.Style():fg("blue")) :cell_style(ui.Style():fg("yellow"):reverse()) - :widths { ui.Constraint.Length(12), ui.Constraint.Fill(1) } + :widths { ui.Constraint.Length(14), ui.Constraint.Fill(1) } ) end +function M:spot_base(job) + local info = ya.image_info(job.file.url) + return { + ui.Row({ "Image" }):style(ui.Style():fg("green")), + ui.Row { " Format:", tostring(info.format) }, + ui.Row { " Size:", string.format("%dx%d", info.w, info.h) }, + ui.Row { " Color:", tostring(info.color) }, + } +end + return M diff --git a/yazi-plugin/preset/plugins/video.lua b/yazi-plugin/preset/plugins/video.lua index be953c5b0..f6dceb01b 100644 --- a/yazi-plugin/preset/plugins/video.lua +++ b/yazi-plugin/preset/plugins/video.lua @@ -38,23 +38,29 @@ function M:preload() return 1 end - local child, code = Command("ffmpegthumbnailer"):args({ - "-q", - "6", - "-c", - "jpeg", - "-i", - tostring(self.file.url), - "-o", + local meta, err = self.list_meta(self.file.url, "format=duration") + if not meta then + ya.err(tostring(err)) + return 0 + end + + local ss = tonumber(meta.format.duration) * percent / 100 + local qv = 31 - math.floor(PREVIEW.image_quality * 0.3) + -- stylua: ignore + local child, code = Command("ffmpeg"):args({ + "-v", "quiet", + "-ss", ss, + "-an", "-bn", "-sn", + "-i", tostring(self.file.url), + "-vframes", 1, + "-q:v", qv, + "-vf", string.format("scale=%d:-2:flags=fast_bilinear", PREVIEW.max_width), + "-f", "image2", tostring(cache), - "-t", - tostring(percent), - "-s", - tostring(PREVIEW.max_width), }):spawn() if not child then - ya.err("spawn `ffmpegthumbnailer` command returns " .. tostring(code)) + ya.err("Spawn `ffmpeg` process returns " .. tostring(code)) return 0 end @@ -62,6 +68,66 @@ function M:preload() return status and status.success and 1 or 2 end -function M:spot(job) require("file"):spot(job) end +function M:spot(job) + local rows = self:spot_base(job) + rows[#rows + 1] = ui.Row {} + + ya.spot_table( + job, + ui.Table(ya.list_merge(rows, require("file"):spot_base(job))) + :area(ui.Pos { "center", w = 60, h = 20 }) + :row(1) + :col(1) + :col_style(ui.Style():fg("blue")) + :cell_style(ui.Style():fg("yellow"):reverse()) + :widths { ui.Constraint.Length(14), ui.Constraint.Fill(1) } + ) +end + +function M:spot_base(job) + local meta, err = self.list_meta(job.file.url, "format=duration:stream=codec_name,codec_type,width,height") + if not meta then + ya.err(tostring(err)) + return {} + end + + local dur = meta.format.duration + local rows = { + ui.Row({ "Video" }):style(ui.Style():fg("green")), + ui.Row { " Codec:", meta.streams[1].codec_name }, + ui.Row { " Duration:", string.format("%d:%02d", math.floor(dur / 60), math.floor(dur % 60)) }, + } + + for i, s in ipairs(meta.streams) do + if s.codec_type == "video" then + rows[#rows + 1] = ui.Row { string.format(" Stream %d:", i), "video" } + rows[#rows + 1] = ui.Row { " Codec:", s.codec_name } + rows[#rows + 1] = ui.Row { " Size:", string.format("%dx%d", s.width, s.height) } + elseif s.codec_type == "audio" then + rows[#rows + 1] = ui.Row { string.format(" Stream %d:", i), "audio" } + rows[#rows + 1] = ui.Row { " Codec:", s.codec_name } + end + end + return rows +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() + if not output then + return nil, Err("Spawn `ffprobe` process returns %s", err) + end + + local t = ya.json_decode(output.stdout) + if not t then + return nil, Err("Failed to decode `ffprobe` output: %s", output.stdout) + elseif type(t) ~= "table" then + return nil, Err("Invalid `ffprobe` output: %s", output.stdout) + end + + t.format = t.format or {} + t.streams = t.streams or {} + return t +end return M diff --git a/yazi-plugin/preset/setup.lua b/yazi-plugin/preset/setup.lua index 0c00ec166..10a6a343f 100644 --- a/yazi-plugin/preset/setup.lua +++ b/yazi-plugin/preset/setup.lua @@ -1,5 +1,4 @@ os.setlocale("") -package.path = BOOT.plugin_dir .. "/?.yazi/init.lua;" .. package.path require("dds"):setup() require("extract"):setup() diff --git a/yazi-plugin/preset/ya.lua b/yazi-plugin/preset/ya.lua index 03d6d492a..a8a935a1d 100644 --- a/yazi-plugin/preset/ya.lua +++ b/yazi-plugin/preset/ya.lua @@ -1,4 +1,5 @@ table.unpack = table.unpack or unpack +function Err(s, ...) return Error.custom(string.format(s, ...)) end function ya.clamp(min, x, max) if x < min then diff --git a/yazi-plugin/src/error.rs b/yazi-plugin/src/error.rs new file mode 100644 index 000000000..1315e3adf --- /dev/null +++ b/yazi-plugin/src/error.rs @@ -0,0 +1,25 @@ +use mlua::{Lua, MetaMethod, UserData, UserDataMethods}; + +pub enum Error { + Serde(serde_json::Error), + Custom(String), +} + +impl Error { + pub fn install(lua: &Lua) -> mlua::Result<()> { + let new = lua.create_function(|_, msg: String| Ok(Error::Custom(msg)))?; + + lua.globals().raw_set("Error", lua.create_table_from([("custom", new)])?) + } +} + +impl UserData for Error { + fn add_methods>(methods: &mut M) { + methods.add_meta_method(MetaMethod::ToString, |_, me, ()| { + Ok(match me { + Error::Serde(e) => e.to_string(), + Error::Custom(s) => s.clone(), + }) + }); + } +} diff --git a/yazi-plugin/src/isolate/isolate.rs b/yazi-plugin/src/isolate/isolate.rs index c4a902d68..42f433163 100644 --- a/yazi-plugin/src/isolate/isolate.rs +++ b/yazi-plugin/src/isolate/isolate.rs @@ -18,6 +18,7 @@ pub fn slim_lua(name: &str) -> mlua::Result { crate::file::pour(&lua)?; crate::url::pour(&lua)?; + crate::Error::install(&lua)?; crate::loader::install_isolate(&lua)?; crate::process::install(&lua)?; diff --git a/yazi-plugin/src/isolate/peek.rs b/yazi-plugin/src/isolate/peek.rs index 6fd878d43..533982e80 100644 --- a/yazi-plugin/src/isolate/peek.rs +++ b/yazi-plugin/src/isolate/peek.rs @@ -62,7 +62,7 @@ pub fn peek( if let Err(e) = result { if !e.to_string().contains("Peek task cancelled") { - error!("{e:?}"); + error!("{e}"); } } }); diff --git a/yazi-plugin/src/isolate/spot.rs b/yazi-plugin/src/isolate/spot.rs index c1690c176..ccb80f3df 100644 --- a/yazi-plugin/src/isolate/spot.rs +++ b/yazi-plugin/src/isolate/spot.rs @@ -58,7 +58,7 @@ pub fn spot( if let Err(e) = result { if !e.to_string().contains("Spot task cancelled") { - error!("{e:?}"); + error!("{e}"); } } }); diff --git a/yazi-plugin/src/lib.rs b/yazi-plugin/src/lib.rs index d610c2e97..354e0c300 100644 --- a/yazi-plugin/src/lib.rs +++ b/yazi-plugin/src/lib.rs @@ -6,7 +6,7 @@ yazi_macro::mod_pub!( bindings, elements, external, file, fs, isolate, loader, process, pubsub, url, utils ); -yazi_macro::mod_flat!(clipboard config lua runtime); +yazi_macro::mod_flat!(clipboard config error lua runtime); pub fn init() -> anyhow::Result<()> { CLIPBOARD.with(<_>::default); diff --git a/yazi-plugin/src/lua.rs b/yazi-plugin/src/lua.rs index 2083cf96e..d80ef526e 100644 --- a/yazi-plugin/src/lua.rs +++ b/yazi-plugin/src/lua.rs @@ -26,6 +26,7 @@ fn stage_1(lua: &'static Lua) -> Result<()> { globals.raw_set("ya", crate::utils::compose(lua, false)?)?; globals.raw_set("ps", crate::pubsub::compose(lua)?)?; + crate::Error::install(lua)?; crate::bindings::Cha::install(lua)?; crate::loader::install(lua)?; crate::file::pour(lua)?; diff --git a/yazi-plugin/src/utils/json.rs b/yazi-plugin/src/utils/json.rs new file mode 100644 index 000000000..5913f9920 --- /dev/null +++ b/yazi-plugin/src/utils/json.rs @@ -0,0 +1,24 @@ +use mlua::{Function, IntoLuaMulti, Lua, LuaSerdeExt, Value}; + +use super::Utils; +use crate::Error; + +impl Utils { + pub(super) fn json_encode(lua: &Lua) -> mlua::Result { + lua.create_async_function(|lua, value: Value| async move { + match serde_json::to_string(&value) { + Ok(s) => (s, Value::Nil).into_lua_multi(&lua), + Err(e) => (Value::Nil, Error::Serde(e)).into_lua_multi(&lua), + } + }) + } + + pub(super) fn json_decode(lua: &Lua) -> mlua::Result { + lua.create_async_function(|lua, s: mlua::String| async move { + match serde_json::from_slice::(&s.as_bytes()) { + Ok(v) => (lua.to_value(&v)?, Value::Nil).into_lua_multi(&lua), + Err(e) => (Value::Nil, Error::Serde(e)).into_lua_multi(&lua), + } + }) + } +} diff --git a/yazi-plugin/src/utils/mod.rs b/yazi-plugin/src/utils/mod.rs index e2afc13af..5a320167f 100644 --- a/yazi-plugin/src/utils/mod.rs +++ b/yazi-plugin/src/utils/mod.rs @@ -1,5 +1,5 @@ #![allow(clippy::module_inception)] yazi_macro::mod_flat!( - app cache call image layer log preview spot sync target text time user utils + app cache call image json layer log preview spot sync target text time user utils ); diff --git a/yazi-plugin/src/utils/utils.rs b/yazi-plugin/src/utils/utils.rs index c41ded1a6..3e483cab3 100644 --- a/yazi-plugin/src/utils/utils.rs +++ b/yazi-plugin/src/utils/utils.rs @@ -23,6 +23,10 @@ pub fn compose(lua: &Lua, isolate: bool) -> mlua::Result { b"image_show" => Utils::image_show(lua)?, b"image_precache" => Utils::image_precache(lua)?, + // JSON + b"json_encode" => Utils::json_encode(lua)?, + b"json_decode" => Utils::json_decode(lua)?, + // Layout b"which" => Utils::which(lua)?, b"input" => Utils::input(lua)?,