mirror of https://github.com/mpv-player/mpv
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:
parent
d9bc97bda6
commit
548ef07864
|
@ -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.
|
||||||
|
|
|
@ -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},
|
||||||
|
|
78
player/lua.c
78
player/lua.c
|
@ -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),
|
||||||
|
|
|
@ -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 {}
|
||||||
|
|
Loading…
Reference in New Issue