diff --git a/DOCS/man/input.rst b/DOCS/man/input.rst index e0b8210df19f3..f277b52f021de 100644 --- a/DOCS/man/input.rst +++ b/DOCS/man/input.rst @@ -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 [ []]`` +``loadfile [ [ []]]`` 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.) - - The third argument is a list of options and values which should be set + + Insert the file into the playlist, at the index given in the third + argument. + + 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 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 position in the playlist, or appended to the end if + 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 []`` +``loadlist [ []]`` 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 the new playlist at the index given in the third argument. + + 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 position in the internal playlist, or appended + to the end if 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. diff --git a/common/playlist.c b/common/playlist.c index 5ff34496c809b..39c49a52309a9 100644 --- a/common/playlist.c +++ b/common/playlist.c @@ -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); diff --git a/common/playlist.h b/common/playlist.h index 1e01b1a5e3bc1..853cd311da74c 100644 --- a/common/playlist.h +++ b/common/playlist.h @@ -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); diff --git a/player/command.c b/player/command.c index 2c5c67e78ac98..d5a315290e683 100644 --- a/player/command.c +++ b/player/command.c @@ -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,