mirror of
https://github.com/mpv-player/mpv
synced 2025-04-09 11:11:52 +00:00
command: add a mechanism to allow scripts to intercept file loads
A vague idea to get something similar what libquvi did. Undocumented because it might change a lot, or even be removed. To give an idea what it does, a Lua script could do the following: -- type ID priority mp.commandv("hook_add", "on_load", 0, 0) mp.register_script_message("hook_run", function(param, param2) -- param is "0", the user-chosen ID from the hook_add command -- param2 is the magic value that has to be passed to finish -- the hook mp.resume_all() -- do something, maybe set options that are reset on end: mp.set_property("file-local-options/name", "value") -- or change the URL that's being opened: local url = mp.get_property("stream-open-filename") mp.set_property("stream-open-filename", url .. ".png") -- let the player (or the next script) continue mp.commandv("hook_ack", param2) end)
This commit is contained in:
parent
bc0ed90481
commit
8e4fa5fcd1
@ -170,6 +170,9 @@ const struct mp_cmd_def mp_cmds[] = {
|
||||
|
||||
{ MP_CMD_WRITE_WATCH_LATER_CONFIG, "write_watch_later_config", },
|
||||
|
||||
{ MP_CMD_HOOK_ADD, "hook_add", { ARG_STRING, ARG_INT, ARG_INT } },
|
||||
{ MP_CMD_HOOK_ACK, "hook_ack", { ARG_STRING } },
|
||||
|
||||
{0}
|
||||
};
|
||||
|
||||
|
@ -97,6 +97,9 @@ enum mp_command_type {
|
||||
|
||||
MP_CMD_WRITE_WATCH_LATER_CONFIG,
|
||||
|
||||
MP_CMD_HOOK_ADD,
|
||||
MP_CMD_HOOK_ACK,
|
||||
|
||||
// Internal
|
||||
MP_CMD_COMMAND_LIST, // list of sub-commands in args[0].v.p
|
||||
};
|
||||
|
@ -82,6 +82,7 @@ typedef struct mp_cmd {
|
||||
struct mp_cmd *queue_next;
|
||||
double scale; // for scaling numeric arguments
|
||||
const struct mp_cmd_def *def;
|
||||
char *sender; // name of the client API user which sent this
|
||||
} mp_cmd_t;
|
||||
|
||||
struct mp_input_src {
|
||||
|
@ -871,6 +871,8 @@ static int run_client_command(mpv_handle *ctx, struct mp_cmd *cmd)
|
||||
if (mp_input_is_abort_cmd(cmd))
|
||||
mp_cancel_trigger(ctx->mpctx->playback_abort);
|
||||
|
||||
cmd->sender = ctx->name;
|
||||
|
||||
struct cmd_request req = {
|
||||
.mpctx = ctx->mpctx,
|
||||
.cmd = cmd,
|
||||
@ -905,6 +907,8 @@ static int run_cmd_async(mpv_handle *ctx, uint64_t ud, struct mp_cmd *cmd)
|
||||
if (!cmd)
|
||||
return MPV_ERROR_INVALID_PARAMETER;
|
||||
|
||||
cmd->sender = ctx->name;
|
||||
|
||||
struct cmd_request *req = talloc_ptrtype(NULL, req);
|
||||
*req = (struct cmd_request){
|
||||
.mpctx = ctx->mpctx,
|
||||
|
138
player/command.c
138
player/command.c
@ -85,6 +85,10 @@ struct command_ctx {
|
||||
struct sub_bitmaps overlay_osd[2];
|
||||
struct sub_bitmaps *overlay_osd_current;
|
||||
|
||||
struct hook_handler **hooks;
|
||||
int num_hooks;
|
||||
int64_t hook_seq; // for hook_handler.seq
|
||||
|
||||
struct ao_device_list *cached_ao_devices;
|
||||
};
|
||||
|
||||
@ -94,11 +98,107 @@ struct overlay {
|
||||
struct sub_bitmap osd;
|
||||
};
|
||||
|
||||
struct hook_handler {
|
||||
char *client; // client API user name
|
||||
char *type; // kind of hook, e.g. "on_load"
|
||||
char *user_id; // numeric user-chosen ID, printed as string
|
||||
int priority; // priority for global hook order
|
||||
int64_t seq; // unique ID (also age -> fixed order for equal priorities)
|
||||
bool active; // hook is currently in progress (only 1 at a time for now)
|
||||
};
|
||||
|
||||
static int edit_filters(struct MPContext *mpctx, enum stream_type mediatype,
|
||||
const char *cmd, const char *arg);
|
||||
static int set_filters(struct MPContext *mpctx, enum stream_type mediatype,
|
||||
struct m_obj_settings *new_chain);
|
||||
|
||||
bool mp_hook_test_completion(struct MPContext *mpctx, char *type)
|
||||
{
|
||||
struct command_ctx *cmd = mpctx->command_ctx;
|
||||
for (int n = 0; n < cmd->num_hooks; n++) {
|
||||
struct hook_handler *h = cmd->hooks[n];
|
||||
if (h->active && strcmp(h->type, type) == 0)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool send_hook_msg(struct MPContext *mpctx, struct hook_handler *h,
|
||||
char *cmd)
|
||||
{
|
||||
mpv_event_client_message *m = talloc_ptrtype(NULL, m);
|
||||
*m = (mpv_event_client_message){0};
|
||||
MP_TARRAY_APPEND(m, m->args, m->num_args, "hook_run");
|
||||
MP_TARRAY_APPEND(m, m->args, m->num_args, talloc_strdup(m, h->user_id));
|
||||
MP_TARRAY_APPEND(m, m->args, m->num_args, talloc_strdup(m, h->type));
|
||||
bool r =
|
||||
mp_client_send_event(mpctx, h->client, MPV_EVENT_CLIENT_MESSAGE, m) >= 0;
|
||||
if (!r)
|
||||
MP_WARN(mpctx, "Sending hook command failed.\n");
|
||||
return r;
|
||||
}
|
||||
|
||||
// client==NULL means start the hook chain
|
||||
void mp_hook_run(struct MPContext *mpctx, char *client, char *type)
|
||||
{
|
||||
struct command_ctx *cmd = mpctx->command_ctx;
|
||||
struct hook_handler *next = NULL;
|
||||
bool found_current = !client;
|
||||
for (int n = 0; n < cmd->num_hooks; n++) {
|
||||
struct hook_handler *h = cmd->hooks[n];
|
||||
if (!found_current) {
|
||||
if (h->active && strcmp(h->type, type) == 0) {
|
||||
h->active = false;
|
||||
found_current = true;
|
||||
}
|
||||
} else if (strcmp(h->type, type) == 0) {
|
||||
next = h;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!next)
|
||||
return;
|
||||
MP_VERBOSE(mpctx, "Running hook: %s/%s\n", next->client, type);
|
||||
next->active = true;
|
||||
send_hook_msg(mpctx, next, "hook_run");
|
||||
}
|
||||
|
||||
void mp_hook_abort(struct MPContext *mpctx, char *type)
|
||||
{
|
||||
struct command_ctx *cmd = mpctx->command_ctx;
|
||||
for (int n = 0; n < cmd->num_hooks; n++) {
|
||||
struct hook_handler *h = cmd->hooks[n];
|
||||
if (h->active && strcmp(h->type, type) == 0)
|
||||
send_hook_msg(mpctx, h, "hook_abort");
|
||||
}
|
||||
}
|
||||
|
||||
static int compare_hook(const void *pa, const void *pb)
|
||||
{
|
||||
struct hook_handler **h1 = (void *)pa;
|
||||
struct hook_handler **h2 = (void *)pb;
|
||||
if ((*h1)->priority != (*h2)->priority)
|
||||
return (*h1)->priority - (*h2)->priority;
|
||||
return (*h1)->seq - (*h2)->seq;
|
||||
}
|
||||
|
||||
static void mp_hook_add(struct MPContext *mpctx, char *client, char *name,
|
||||
int id, int pri)
|
||||
{
|
||||
struct command_ctx *cmd = mpctx->command_ctx;
|
||||
struct hook_handler *h = talloc_ptrtype(cmd, h);
|
||||
int64_t seq = cmd->hook_seq++;
|
||||
*h = (struct hook_handler){
|
||||
.client = talloc_strdup(h, client),
|
||||
.type = talloc_strdup(h, name),
|
||||
.user_id = talloc_asprintf(h, "%d", id),
|
||||
.priority = pri,
|
||||
.seq = seq,
|
||||
};
|
||||
MP_TARRAY_APPEND(cmd, cmd->hooks, cmd->num_hooks, h);
|
||||
qsort(cmd->hooks, cmd->num_hooks, sizeof(cmd->hooks[0]), compare_hook);
|
||||
}
|
||||
|
||||
// Call before a seek, in order to allow revert_seek to undo the seek.
|
||||
static void mark_seek(struct MPContext *mpctx)
|
||||
{
|
||||
@ -213,6 +313,27 @@ static int mp_property_filename(void *ctx, struct m_property *prop,
|
||||
return r;
|
||||
}
|
||||
|
||||
static int mp_property_stream_open_filename(void *ctx, struct m_property *prop,
|
||||
int action, void *arg)
|
||||
{
|
||||
MPContext *mpctx = ctx;
|
||||
if (!mpctx->stream_open_filename || !mpctx->playing)
|
||||
return M_PROPERTY_UNAVAILABLE;
|
||||
switch (action) {
|
||||
case M_PROPERTY_SET: {
|
||||
if (mpctx->stream)
|
||||
return M_PROPERTY_ERROR;
|
||||
mpctx->stream_open_filename =
|
||||
talloc_strdup(mpctx->stream_open_filename, *(char **)arg);
|
||||
return M_PROPERTY_OK;
|
||||
}
|
||||
case M_PROPERTY_GET_TYPE:
|
||||
case M_PROPERTY_GET:
|
||||
return m_property_strdup_ro(action, arg, mpctx->stream_open_filename);
|
||||
}
|
||||
return M_PROPERTY_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
static int mp_property_file_size(void *ctx, struct m_property *prop,
|
||||
int action, void *arg)
|
||||
{
|
||||
@ -2771,6 +2892,7 @@ static const struct m_property mp_properties[] = {
|
||||
{"loop-file", mp_property_generic_option},
|
||||
{"speed", mp_property_playback_speed},
|
||||
{"filename", mp_property_filename},
|
||||
{"stream-open-filename", mp_property_stream_open_filename},
|
||||
{"file-size", mp_property_file_size},
|
||||
{"path", mp_property_path},
|
||||
{"media-title", mp_property_media_title},
|
||||
@ -4066,6 +4188,22 @@ int run_command(MPContext *mpctx, mp_cmd_t *cmd)
|
||||
break;
|
||||
}
|
||||
|
||||
case MP_CMD_HOOK_ADD:
|
||||
if (!cmd->sender) {
|
||||
MP_ERR(mpctx, "Can be used from client API only.\n");
|
||||
return -1;
|
||||
}
|
||||
mp_hook_add(mpctx, cmd->sender, cmd->args[0].v.s, cmd->args[1].v.i,
|
||||
cmd->args[2].v.i);
|
||||
break;
|
||||
case MP_CMD_HOOK_ACK:
|
||||
if (!cmd->sender) {
|
||||
MP_ERR(mpctx, "Can be used from client API only.\n");
|
||||
return -1;
|
||||
}
|
||||
mp_hook_run(mpctx, cmd->sender, cmd->args[0].v.s);
|
||||
break;
|
||||
|
||||
default:
|
||||
MP_VERBOSE(mpctx, "Received unknown cmd %s\n", cmd->name);
|
||||
return -1;
|
||||
|
@ -43,4 +43,8 @@ uint64_t mp_get_property_event_mask(const char *name);
|
||||
#define INTERNAL_EVENT_BASE 24
|
||||
#define MP_EVENT_CACHE_UPDATE (INTERNAL_EVENT_BASE + 0)
|
||||
|
||||
bool mp_hook_test_completion(struct MPContext *mpctx, char *type);
|
||||
void mp_hook_run(struct MPContext *mpctx, char *client, char *type);
|
||||
void mp_hook_abort(struct MPContext *mpctx, char *type);
|
||||
|
||||
#endif /* MPLAYER_COMMAND_H */
|
||||
|
@ -179,6 +179,7 @@ typedef struct MPContext {
|
||||
struct playlist *playlist;
|
||||
struct playlist_entry *playing; // currently playing file
|
||||
char *filename; // immutable copy of playing->filename (or NULL)
|
||||
char *stream_open_filename;
|
||||
struct mp_resolve_result *resolve_result;
|
||||
enum stop_play_reason stop_play;
|
||||
bool playback_initialized; // playloop can be run/is running
|
||||
|
@ -794,6 +794,36 @@ static void transfer_playlist(struct MPContext *mpctx, struct playlist *pl)
|
||||
}
|
||||
}
|
||||
|
||||
static int process_open_hooks(struct MPContext *mpctx)
|
||||
{
|
||||
|
||||
mp_hook_run(mpctx, NULL, "on_load");
|
||||
|
||||
while (!mp_hook_test_completion(mpctx, "on_load")) {
|
||||
mp_idle(mpctx);
|
||||
if (mpctx->stop_play) {
|
||||
if (mpctx->stop_play == PT_QUIT)
|
||||
return -1;
|
||||
// Can't exit immediately, the script would interfere with the
|
||||
// next file being loaded.
|
||||
mp_hook_abort(mpctx, "on_load");
|
||||
}
|
||||
}
|
||||
|
||||
// quvi stuff
|
||||
char *filename = mpctx->stream_open_filename;
|
||||
mpctx->resolve_result = resolve_url(filename, mpctx->global);
|
||||
if (mpctx->resolve_result) {
|
||||
print_resolve_contents(mpctx->log, mpctx->resolve_result);
|
||||
if (mpctx->resolve_result->playlist) {
|
||||
transfer_playlist(mpctx, mpctx->resolve_result->playlist);
|
||||
return 1;
|
||||
}
|
||||
mpctx->stream_open_filename = mpctx->resolve_result->url;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void print_timeline(struct MPContext *mpctx)
|
||||
{
|
||||
if (mpctx->timeline) {
|
||||
@ -936,6 +966,7 @@ static void play_current_file(struct MPContext *mpctx)
|
||||
mpctx->playing->reserved += 1;
|
||||
|
||||
mpctx->filename = talloc_strdup(tmp, mpctx->playing->filename);
|
||||
mpctx->stream_open_filename = mpctx->filename;
|
||||
|
||||
mpctx->add_osd_seek_info &= OSD_SEEK_INFO_EDITION;
|
||||
|
||||
@ -972,21 +1003,16 @@ static void play_current_file(struct MPContext *mpctx)
|
||||
assert(mpctx->d_sub[0] == NULL);
|
||||
assert(mpctx->d_sub[1] == NULL);
|
||||
|
||||
char *stream_filename = mpctx->filename;
|
||||
mpctx->resolve_result = resolve_url(stream_filename, mpctx->global);
|
||||
if (mpctx->resolve_result) {
|
||||
talloc_steal(tmp, mpctx->resolve_result);
|
||||
print_resolve_contents(mpctx->log, mpctx->resolve_result);
|
||||
if (mpctx->resolve_result->playlist) {
|
||||
transfer_playlist(mpctx, mpctx->resolve_result->playlist);
|
||||
goto terminate_playback;
|
||||
}
|
||||
stream_filename = mpctx->resolve_result->url;
|
||||
}
|
||||
int hooks_res = process_open_hooks(mpctx);
|
||||
talloc_steal(tmp, mpctx->resolve_result);
|
||||
if (hooks_res)
|
||||
goto terminate_playback; // quit or preloaded playlist special-case
|
||||
|
||||
int stream_flags = STREAM_READ;
|
||||
if (!opts->load_unsafe_playlists)
|
||||
stream_flags |= mpctx->playing->stream_flags;
|
||||
mpctx->stream = open_stream_async(mpctx, stream_filename, stream_flags);
|
||||
mpctx->stream = open_stream_async(mpctx, mpctx->stream_open_filename,
|
||||
stream_flags);
|
||||
if (!mpctx->stream)
|
||||
goto terminate_playback;
|
||||
|
||||
@ -1245,6 +1271,7 @@ terminate_playback:
|
||||
playlist_entry_unref(mpctx->playing);
|
||||
mpctx->playing = NULL;
|
||||
mpctx->filename = NULL;
|
||||
mpctx->stream_open_filename = NULL;
|
||||
|
||||
talloc_free(tmp);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user