diff --git a/DOCS/man/lua.rst b/DOCS/man/lua.rst index dab185a284..ba09164d3b 100644 --- a/DOCS/man/lua.rst +++ b/DOCS/man/lua.rst @@ -653,45 +653,23 @@ strictly part of the guaranteed API. ``utils.subprocess(t)`` Runs an external process and waits until it exits. Returns process status - and the captured output. + and the captured output. This is a legacy wrapper around calling the + ``subprocess`` command with ``mp.command_native``. It does the following + things: - The parameter ``t`` is a table. The function reads the following entries: + - copy the table ``t`` + - rename ``cancellable`` field to ``playback_only`` + - rename ``max_size`` to ``capture_size`` + - set ``capture_stdout`` field to ``true`` if unset + - set ``name`` field to ``subprocess`` + - call ``mp.command_native(copied_t)`` + - if the command failed, create a dummy result table + - copy ``error_string`` to ``error`` field if the string is non-empty + - return the result table - ``args`` - Array of strings. The first array entry is the executable. This - can be either an absolute path, or a filename with no path - components, in which case the ``PATH`` environment variable is - used to resolve the executable. The other array elements are - passed as command line arguments. - - ``cancellable`` - Optional. If set to ``true`` (default), then if the user stops - playback or goes to the next file while the process is running, - the process will be killed. - - ``max_size`` - Optional. The maximum size in bytes of the data that can be captured - from stdout. (Default: 16 MB.) - - The function returns a table as result with the following entries: - - ``status`` - The raw exit status of the process. It will be negative on error. - - ``stdout`` - Captured output stream as string, limited to ``max_size``. - - ``error`` - ``nil`` on success. The string ``killed`` if the process was - terminated in an unusual way. The string ``init`` if the process - could not be started. - - On Windows, ``killed`` is only returned when the process has been - killed by mpv as a result of ``cancellable`` being set to ``true``. - - ``killed_by_us`` - Set to ``true`` if the process has been killed by mpv as a result - of ``cancellable`` being set to ``true``. + It is recommended to use ``mp.command_native`` or ``mp.command_native_async`` + directly, instead of calling this legacy wrapper. It is for compatibility + only. ``utils.subprocess_detached(t)`` Runs an external process and detaches it from mpv's control. diff --git a/player/command.c b/player/command.c index fe6c3fd2ca..f2d7eedd5f 100644 --- a/player/command.c +++ b/player/command.c @@ -5680,7 +5680,7 @@ static void cmd_subprocess(void *p) void *tmp = talloc_new(NULL); struct subprocess_cb_ctx ctx = { - .log = mpctx->log, + .log = mp_log_new(tmp, mpctx->log, cmd->cmd->sender), .talloc_ctx = tmp, .max_size = cmd->args[2].v.i, .capture = {0, cmd->args[3].v.i, cmd->args[4].v.i}, diff --git a/player/lua.c b/player/lua.c index 8d4e794a52..f3bc3e699b 100644 --- a/player/lua.c +++ b/player/lua.c @@ -1187,83 +1187,6 @@ static int script_join_path(lua_State *L) return 1; } -struct subprocess_cb_ctx { - struct mp_log *log; - void* talloc_ctx; - int64_t max_size; - bstr output; -}; - -static void subprocess_stdout(void *p, char *data, size_t size) -{ - struct subprocess_cb_ctx *ctx = p; - if (ctx->output.len < ctx->max_size) - bstr_xappend(ctx->talloc_ctx, &ctx->output, (bstr){data, size}); -} - -static void subprocess_stderr(void *p, char *data, size_t size) -{ - struct subprocess_cb_ctx *ctx = p; - MP_INFO(ctx, "%.*s", (int)size, data); -} - -static int script_subprocess(lua_State *L) -{ - struct script_ctx *ctx = get_ctx(L); - luaL_checktype(L, 1, LUA_TTABLE); - void *tmp = mp_lua_PITA(L); - - lua_getfield(L, 1, "args"); // args - int num_args = mp_lua_len(L, -1); - char *args[256]; - if (num_args > MP_ARRAY_SIZE(args) - 1) // last needs to be NULL - luaL_error(L, "too many arguments"); - if (num_args < 1) - luaL_error(L, "program name missing"); - for (int n = 0; n < num_args; n++) { - lua_pushinteger(L, n + 1); // args n - lua_gettable(L, -2); // args arg - args[n] = talloc_strdup(tmp, lua_tostring(L, -1)); - if (!args[n]) - luaL_error(L, "program arguments must be strings"); - lua_pop(L, 1); // args - } - args[num_args] = NULL; - lua_pop(L, 1); // - - - lua_getfield(L, 1, "cancellable"); // c - struct mp_cancel *cancel = NULL; - if (lua_isnil(L, -1) ? true : lua_toboolean(L, -1)) - cancel = ctx->mpctx->playback_abort; - lua_pop(L, 1); // - - - lua_getfield(L, 1, "max_size"); // m - int64_t max_size = lua_isnil(L, -1) ? 64 * 1024 * 1024 : lua_tointeger(L, -1); - - struct subprocess_cb_ctx cb_ctx = { - .log = ctx->log, - .talloc_ctx = tmp, - .max_size = max_size, - }; - - char *error = NULL; - int status = mp_subprocess(args, cancel, &cb_ctx, subprocess_stdout, - subprocess_stderr, &error); - - lua_newtable(L); // res - if (error) { - lua_pushstring(L, error); // res e - lua_setfield(L, -2, "error"); // res - } - lua_pushinteger(L, status); // res s - lua_setfield(L, -2, "status"); // res - lua_pushlstring(L, cb_ctx.output.start, cb_ctx.output.len); // res d - lua_setfield(L, -2, "stdout"); // res - lua_pushboolean(L, status == MP_SUBPROCESS_EKILLED_BY_US); // res b - lua_setfield(L, -2, "killed_by_us"); // res - return 1; -} - static int script_subprocess_detached(lua_State *L) { struct script_ctx *ctx = get_ctx(L); @@ -1386,7 +1309,6 @@ static const struct fn_entry utils_fns[] = { FN_ENTRY(file_info), FN_ENTRY(split_path), FN_ENTRY(join_path), - FN_ENTRY(subprocess), FN_ENTRY(subprocess_detached), FN_ENTRY(getpid), FN_ENTRY(parse_json), diff --git a/player/lua/defaults.lua b/player/lua/defaults.lua index 6f5a9c4b6c..feb400111a 100644 --- a/player/lua/defaults.lua +++ b/player/lua/defaults.lua @@ -617,4 +617,27 @@ function mp_utils.format_bytes_humanized(b) return string.format("%0.2f %s", b, d[i] and d[i] or "*1024^" .. (i-1)) end +function mp_utils.subprocess(t) + local cmd = {} + cmd.name = "subprocess" + cmd.capture_stdout = true + for k, v in pairs(t) do + if k == "cancellable" then + k = "playback_only" + elseif k == "max_size" then + k = "capture_size" + end + cmd[k] = v + end + local res, err = mp.command_native(cmd) + if res == nil then + -- an error usually happens only if parsing failed (or no args passed) + res = {error_string = err, status = -1} + end + if res.error_string ~= "" then + res.error = res.error_string + end + return res +end + return {}