input/player: add loadfile/loadlist insert-at command

This commit is contained in:
David Vaughan 2024-02-13 13:24:58 -08:00 committed by Dudemanguy
parent da753196af
commit c678033c1d
4 changed files with 112 additions and 28 deletions

View File

@ -452,9 +452,9 @@ Remember to quote string arguments in input.conf (see `Flat command syntax`_).
restarted if for example the new playlist entry is the same as the previous
one.
``loadfile <url> [<flags> [<options>]]``
``loadfile <url> [<flags> [<index> [<options>]]]``
Load the given file or URL and play it. Technically, this is just a playlist
manipulation command (which either replaces the playlist or appends an entry
manipulation command (which either replaces the playlist or adds an entry
to it). Actual file loading happens independently. For example, a
``loadfile`` command that replaces the current file with a new one returns
before the current file is stopped, and the new file even begins loading.
@ -475,15 +475,28 @@ Remember to quote string arguments in input.conf (see `Flat command syntax`_).
Insert the file next, and if nothing is currently playing, start playback.
(Always starts with the added file, even if the playlist was not empty
before running this command.)
<insert-at>
Insert the file into the playlist, at the index given in the third
argument.
<insert-at-play>
Insert the file at the index given in the third argument, and if nothing
is currently playing, start playback. (Always starts with the added
file, even if the playlist was not empty before running this command.)
The third argument is a list of options and values which should be set
The third argument is an insertion index, used only by the `insert-at` and
`insert-at-play` actions. When used with those actions, the new item will be
insert at the <index> position in the playlist, or appended to the end if
<index> is less than 0 or greater than the size of the playlist. This
argument will be ignored for all other actions.
The fourth argument is a list of options and values which should be set
while the file is playing. It is of the form ``opt1=value1,opt2=value2,..``.
When using the client API, this can be a ``MPV_FORMAT_NODE_MAP`` (or a Lua
table), however the values themselves must be strings currently. These
options are set during playback, and restored to the previous value at end
of playback (see `Per-File Options`_).
``loadlist <url> [<flags>]``
``loadlist <url> [<flags> [<index>]]``
Load the given playlist file or URL (like ``--playlist``).
Second argument:
@ -503,6 +516,19 @@ Remember to quote string arguments in input.conf (see `Flat command syntax`_).
Insert the new playlist, and if nothing is currently playing, start
playback. (Always starts with the new playlist, even if the internal
playlist was not empty before running this command.)
<insert-at>
Insert the new playlist at the index given in the third argument.
<insert-at-play>
Insert the new playlist at the index given in the third argument, and if
nothing is currently playing, start playback. (Always starts with the
new playlist, even if the internal playlist was not empty before running
this command.)
The third argument is an insertion index, used only by the `insert-at` and
`insert-at-play` actions. When used with those actions, the new playlist
will be insert at the <index> position in the internal playlist, or appended
to the end if <index> is less than 0 or greater than the size of the
internal playlist. This argument will be ignored for all other actions.
``playlist-clear``
Clear the playlist, except the currently played file.

View File

@ -312,8 +312,8 @@ void playlist_set_stream_flags(struct playlist *pl, int flags)
pl->entries[n]->stream_flags = flags;
}
static int64_t playlist_transfer_entries_to(struct playlist *pl, int dst_index,
struct playlist *source_pl)
int64_t playlist_transfer_entries_to(struct playlist *pl, int dst_index,
struct playlist *source_pl)
{
assert(pl != source_pl);
struct playlist_entry *first = playlist_get_first(source_pl);

View File

@ -106,6 +106,8 @@ struct playlist_entry *playlist_get_first_in_same_playlist(struct playlist_entry
char *current_playlist_path);
void playlist_add_base_path(struct playlist *pl, bstr base_path);
void playlist_set_stream_flags(struct playlist *pl, int flags);
int64_t playlist_transfer_entries_to(struct playlist *pl, int dst_index,
struct playlist *source_pl);
int64_t playlist_transfer_entries(struct playlist *pl, struct playlist *source_pl);
int64_t playlist_append_entries(struct playlist *pl, struct playlist *source_pl);

View File

@ -139,6 +139,18 @@ struct hook_handler {
bool active; // hook is currently in progress (only 1 at a time for now)
};
enum load_action_type {
LOAD_TYPE_REPLACE,
LOAD_TYPE_INSERT_AT,
LOAD_TYPE_INSERT_NEXT,
LOAD_TYPE_APPEND,
};
struct load_action {
enum load_action_type type;
bool play;
};
// U+279C HEAVY ROUND-TIPPED RIGHTWARDS ARROW
// U+00A0 NO-BREAK SPACE
#define ARROW_SP "\342\236\234\302\240"
@ -5522,37 +5534,71 @@ static void cmd_expand_path(void *p)
};
}
static struct load_action get_load_action(struct MPContext *mpctx, int action_flag)
{
switch (action_flag) {
case 0: // replace
return (struct load_action){LOAD_TYPE_REPLACE, .play = true};
case 1: // append
return (struct load_action){LOAD_TYPE_APPEND, .play = false};
case 2: // append-play
return (struct load_action){LOAD_TYPE_APPEND, .play = true};
case 3: // insert-next
return (struct load_action){LOAD_TYPE_INSERT_NEXT, .play = false};
case 4: // insert-next-play
return (struct load_action){LOAD_TYPE_INSERT_NEXT, .play = true};
case 5: // insert-at
return (struct load_action){LOAD_TYPE_INSERT_AT, .play = false};
case 6: // insert-at-play
return (struct load_action){LOAD_TYPE_INSERT_AT, .play = true};
default: // default: replace
return (struct load_action){LOAD_TYPE_REPLACE, .play = true};
}
}
static struct playlist_entry *get_insert_entry(struct MPContext *mpctx, struct load_action *action,
int insert_at_idx)
{
switch (action->type) {
case LOAD_TYPE_INSERT_NEXT:
return playlist_get_next(mpctx->playlist, +1);
case LOAD_TYPE_INSERT_AT:
return playlist_entry_from_index(mpctx->playlist, insert_at_idx);
case LOAD_TYPE_REPLACE:
case LOAD_TYPE_APPEND:
default:
return NULL;
}
}
static void cmd_loadfile(void *p)
{
struct mp_cmd_ctx *cmd = p;
struct MPContext *mpctx = cmd->mpctx;
char *filename = cmd->args[0].v.s;
int action = cmd->args[1].v.i;
int action_flag = cmd->args[1].v.i;
int insert_at_idx = cmd->args[2].v.i;
bool replace = (action == 0);
bool insert_next = (action == 3 || action == 4);
bool play = (action == 2 || action == 4);
struct load_action action = get_load_action(mpctx, action_flag);
if (replace)
if (action.type == LOAD_TYPE_REPLACE)
playlist_clear(mpctx->playlist);
struct playlist_entry *entry = playlist_entry_new(filename);
if (cmd->args[2].v.str_list) {
char **pairs = cmd->args[2].v.str_list;
if (cmd->args[3].v.str_list) {
char **pairs = cmd->args[3].v.str_list;
for (int i = 0; pairs[i] && pairs[i + 1]; i += 2)
playlist_entry_add_param(entry, bstr0(pairs[i]), bstr0(pairs[i + 1]));
}
struct playlist_entry *at = insert_next ?
playlist_get_next(mpctx->playlist, +1) : NULL;
struct playlist_entry *at = get_insert_entry(mpctx, &action, insert_at_idx);
playlist_insert_at(mpctx->playlist, entry, at);
struct mpv_node *res = &cmd->result;
node_init(res, MPV_FORMAT_NODE_MAP, NULL);
node_map_add_int64(res, "playlist_entry_id", entry->id);
if (replace || (play && !mpctx->playlist->current)) {
if (action.type == LOAD_TYPE_REPLACE || (action.play && !mpctx->playlist->current)) {
if (mpctx->opts->position_save_on_quit) // requested in issue #1148
mp_write_watch_later_conf(mpctx);
mp_set_playlist_entry(mpctx, entry);
@ -5566,33 +5612,37 @@ static void cmd_loadlist(void *p)
struct mp_cmd_ctx *cmd = p;
struct MPContext *mpctx = cmd->mpctx;
char *filename = cmd->args[0].v.s;
int flag = cmd->args[1].v.i;
int action_flag = cmd->args[1].v.i;
int insert_at_idx = cmd->args[2].v.i;
bool replace = (flag == 0);
bool insert_next = (flag == 3 || flag == 4);
bool play = (flag == 2 || flag == 4);
struct load_action action = get_load_action(mpctx, action_flag);
struct playlist *pl = playlist_parse_file(filename, cmd->abort->cancel,
mpctx->global);
if (pl) {
prepare_playlist(mpctx, pl);
struct playlist_entry *new = pl->current;
if (replace)
if (action.type == LOAD_TYPE_REPLACE)
playlist_clear(mpctx->playlist);
struct playlist_entry *first = playlist_entry_from_index(pl, 0);
int num_entries = pl->num_entries;
if (insert_next) {
playlist_transfer_entries(mpctx->playlist, pl);
} else {
struct playlist_entry *at = get_insert_entry(mpctx, &action, insert_at_idx);
if (at == NULL) {
playlist_append_entries(mpctx->playlist, pl);
} else {
int at_index = playlist_entry_to_index(mpctx->playlist, at);
playlist_transfer_entries_to(mpctx->playlist, at_index, pl);
}
talloc_free(pl);
if (!new)
new = playlist_get_first(mpctx->playlist);
if ((replace || (play && !mpctx->playlist->current)) && new)
if ((action.type == LOAD_TYPE_REPLACE ||
(action.play && !mpctx->playlist->current)) && new) {
mp_set_playlist_entry(mpctx, new);
}
struct mpv_node *res = &cmd->result;
node_init(res, MPV_FORMAT_NODE_MAP, NULL);
@ -6699,8 +6749,11 @@ const struct mp_cmd_def mp_cmds[] = {
{"append", 1},
{"append-play", 2},
{"insert-next", 3},
{"insert-next-play", 4}),
{"insert-next-play", 4},
{"insert-at", 5},
{"insert-at-play", 6}),
.flags = MP_CMD_OPT_ARG},
{"index", OPT_INT(v.i), OPTDEF_INT(-1)},
{"options", OPT_KEYVALUELIST(v.str_list), .flags = MP_CMD_OPT_ARG},
},
},
@ -6712,8 +6765,11 @@ const struct mp_cmd_def mp_cmds[] = {
{"append", 1},
{"append-play", 2},
{"insert-next", 3},
{"insert-next-play", 4}),
{"insert-next-play", 4},
{"insert-at", 5},
{"insert-at-play", 6}),
.flags = MP_CMD_OPT_ARG},
{"index", OPT_INT(v.i), OPTDEF_INT(-1)},
},
.spawn_thread = true,
.can_abort = true,