lua: reimplement mp.subprocess() by invoking the new subprocess command

We keep mp.subprocess() with roughly the same semantics for
compatibility with scripts (including the internal ytdl script).

Seems to work with rhe ytdl wrapper. Not tested further.
This commit is contained in:
wm4 2018-05-12 15:36:43 +02:00
parent d9bc97bda6
commit 548ef07864
4 changed files with 39 additions and 116 deletions

View File

@ -653,45 +653,23 @@ strictly part of the guaranteed API.
``utils.subprocess(t)`` ``utils.subprocess(t)``
Runs an external process and waits until it exits. Returns process status 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`` It is recommended to use ``mp.command_native`` or ``mp.command_native_async``
Array of strings. The first array entry is the executable. This directly, instead of calling this legacy wrapper. It is for compatibility
can be either an absolute path, or a filename with no path only.
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``.
``utils.subprocess_detached(t)`` ``utils.subprocess_detached(t)``
Runs an external process and detaches it from mpv's control. Runs an external process and detaches it from mpv's control.

View File

@ -5680,7 +5680,7 @@ static void cmd_subprocess(void *p)
void *tmp = talloc_new(NULL); void *tmp = talloc_new(NULL);
struct subprocess_cb_ctx ctx = { struct subprocess_cb_ctx ctx = {
.log = mpctx->log, .log = mp_log_new(tmp, mpctx->log, cmd->cmd->sender),
.talloc_ctx = tmp, .talloc_ctx = tmp,
.max_size = cmd->args[2].v.i, .max_size = cmd->args[2].v.i,
.capture = {0, cmd->args[3].v.i, cmd->args[4].v.i}, .capture = {0, cmd->args[3].v.i, cmd->args[4].v.i},

View File

@ -1187,83 +1187,6 @@ static int script_join_path(lua_State *L)
return 1; 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) static int script_subprocess_detached(lua_State *L)
{ {
struct script_ctx *ctx = get_ctx(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(file_info),
FN_ENTRY(split_path), FN_ENTRY(split_path),
FN_ENTRY(join_path), FN_ENTRY(join_path),
FN_ENTRY(subprocess),
FN_ENTRY(subprocess_detached), FN_ENTRY(subprocess_detached),
FN_ENTRY(getpid), FN_ENTRY(getpid),
FN_ENTRY(parse_json), FN_ENTRY(parse_json),

View File

@ -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)) return string.format("%0.2f %s", b, d[i] and d[i] or "*1024^" .. (i-1))
end 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 {} return {}