demux_playlist: add option to control recursive directory loading

Directories were always loaded recursively, which can be slow
(e.g. one of the subdirectories is a mounting point to a slow device)
and can unexpectedly expand into a massive playlist.

Due to the problems described in 503dada42f,
this defaults to recursive loading.

ref. https://github.com/mpv-player/mpv/issues/9652
This commit is contained in:
Christoph Heinrich 2023-02-11 21:08:38 +01:00 committed by Dudemanguy
parent 9ad14e0886
commit f266eadf1e
5 changed files with 51 additions and 9 deletions

View File

@ -84,6 +84,7 @@ Interface changes
passthrough the mpv window passthrough the mpv window
- icc and gpu-shader cache are now saved by default (use --no-icc-shader-cache and - icc and gpu-shader cache are now saved by default (use --no-icc-shader-cache and
--no-gpu-shader-cache to disable) --no-gpu-shader-cache to disable)
- add `--directory-mode=recursive|lazy|ignore`
--- mpv 0.35.0 --- --- mpv 0.35.0 ---
- add the `--vo=gpu-next` video output driver, as well as the options - add the `--vo=gpu-next` video output driver, as well as the options
`--allow-delayed-peak-detect`, `--builtin-scalers`, `--allow-delayed-peak-detect`, `--builtin-scalers`,

View File

@ -3945,6 +3945,13 @@ Demuxer
libarchive opens all volumes anyway when playing the main file, even though libarchive opens all volumes anyway when playing the main file, even though
mpv iterated no archive entries yet. mpv iterated no archive entries yet.
``--directory-mode=<recursive|lazy|ignore>``
When opening a directory, open subdirectories recursively, lazily or not at
all (default: recursive).
Values other then ``recursive`` can lead to problems with resuming playlists
(`RESUMING PLAYBACK`_) and possibly other things.
Input Input
----- -----

View File

@ -24,6 +24,7 @@
#include "common/common.h" #include "common/common.h"
#include "options/options.h" #include "options/options.h"
#include "options/m_config.h"
#include "common/msg.h" #include "common/msg.h"
#include "common/playlist.h" #include "common/playlist.h"
#include "misc/thread_tools.h" #include "misc/thread_tools.h"
@ -35,6 +36,31 @@
#define PROBE_SIZE (8 * 1024) #define PROBE_SIZE (8 * 1024)
enum dir_mode {
DIR_RECURSIVE,
DIR_LAZY,
DIR_IGNORE,
};
#define OPT_BASE_STRUCT struct demux_playlist_opts
struct demux_playlist_opts {
int dir_mode;
};
struct m_sub_options demux_playlist_conf = {
.opts = (const struct m_option[]) {
{"directory-mode", OPT_CHOICE(dir_mode,
{"recursive", DIR_RECURSIVE},
{"lazy", DIR_LAZY},
{"ignore", DIR_IGNORE})},
{0}
},
.size = sizeof(struct demux_playlist_opts),
.defaults = &(const struct demux_playlist_opts){
.dir_mode = DIR_RECURSIVE,
},
};
static bool check_mimetype(struct stream *s, const char *const *list) static bool check_mimetype(struct stream *s, const char *const *list)
{ {
if (s->mime_type) { if (s->mime_type) {
@ -59,6 +85,7 @@ struct pl_parser {
enum demux_check check_level; enum demux_check check_level;
struct stream *real_stream; struct stream *real_stream;
char *format; char *format;
struct demux_playlist_opts *opts;
}; };
@ -323,6 +350,7 @@ static bool scan_dir(struct pl_parser *p, char *path,
return false; return false;
} }
int dir_mode = p->opts->dir_mode;
struct dirent *ep; struct dirent *ep;
while ((ep = readdir(dp))) { while ((ep = readdir(dp))) {
if (ep->d_name[0] == '.') if (ep->d_name[0] == '.')
@ -334,16 +362,18 @@ static bool scan_dir(struct pl_parser *p, char *path,
char *file = mp_path_join(p, path, ep->d_name); char *file = mp_path_join(p, path, ep->d_name);
struct stat st; struct stat st;
if (stat(file, &st) == 0 && S_ISDIR(st.st_mode)) { if (dir_mode != DIR_LAZY && stat(file, &st) == 0 && S_ISDIR(st.st_mode)) {
for (int n = 0; n < num_dir_stack; n++) { if (dir_mode != DIR_IGNORE) {
if (same_st(&dir_stack[n], &st)) { for (int n = 0; n < num_dir_stack; n++) {
MP_VERBOSE(p, "Skip recursive entry: %s\n", file); if (same_st(&dir_stack[n], &st)) {
goto skip; MP_VERBOSE(p, "Skip recursive entry: %s\n", file);
goto skip;
}
} }
}
dir_stack[num_dir_stack] = st; dir_stack[num_dir_stack] = st;
scan_dir(p, file, dir_stack, num_dir_stack + 1, files, num_files); scan_dir(p, file, dir_stack, num_dir_stack + 1, files, num_files);
}
} else { } else {
MP_TARRAY_APPEND(p, *files, *num_files, file); MP_TARRAY_APPEND(p, *files, *num_files, file);
} }
@ -458,6 +488,7 @@ static int open_file(struct demuxer *demuxer, enum demux_check check)
p->error = false; p->error = false;
p->s = demuxer->stream; p->s = demuxer->stream;
p->utf16 = stream_skip_bom(p->s); p->utf16 = stream_skip_bom(p->s);
p->opts = mp_get_config_group(demuxer, demuxer->global, &demux_playlist_conf);
bool ok = fmt->parse(p) >= 0 && !p->error; bool ok = fmt->parse(p) >= 0 && !p->error;
if (p->add_base) if (p->add_base)
playlist_add_base_path(p->pl, mp_dirname(demuxer->filename)); playlist_add_base_path(p->pl, mp_dirname(demuxer->filename));
@ -471,7 +502,7 @@ static int open_file(struct demuxer *demuxer, enum demux_check check)
return ok ? 0 : -1; return ok ? 0 : -1;
} }
const struct demuxer_desc demuxer_desc_playlist = { const demuxer_desc_t demuxer_desc_playlist = {
.name = "playlist", .name = "playlist",
.desc = "Playlist file", .desc = "Playlist file",
.open = open_file, .open = open_file,

View File

@ -63,6 +63,7 @@ extern const struct m_sub_options zimg_conf;
extern const struct m_sub_options drm_conf; extern const struct m_sub_options drm_conf;
extern const struct m_sub_options demux_rawaudio_conf; extern const struct m_sub_options demux_rawaudio_conf;
extern const struct m_sub_options demux_rawvideo_conf; extern const struct m_sub_options demux_rawvideo_conf;
extern const struct m_sub_options demux_playlist_conf;
extern const struct m_sub_options demux_lavf_conf; extern const struct m_sub_options demux_lavf_conf;
extern const struct m_sub_options demux_mkv_conf; extern const struct m_sub_options demux_mkv_conf;
extern const struct m_sub_options demux_cue_conf; extern const struct m_sub_options demux_cue_conf;
@ -591,6 +592,7 @@ static const m_option_t mp_opts[] = {
{"", OPT_SUBSTRUCT(demux_lavf, demux_lavf_conf)}, {"", OPT_SUBSTRUCT(demux_lavf, demux_lavf_conf)},
{"demuxer-rawaudio", OPT_SUBSTRUCT(demux_rawaudio, demux_rawaudio_conf)}, {"demuxer-rawaudio", OPT_SUBSTRUCT(demux_rawaudio, demux_rawaudio_conf)},
{"demuxer-rawvideo", OPT_SUBSTRUCT(demux_rawvideo, demux_rawvideo_conf)}, {"demuxer-rawvideo", OPT_SUBSTRUCT(demux_rawvideo, demux_rawvideo_conf)},
{"", OPT_SUBSTRUCT(demux_playlist, demux_playlist_conf)},
{"demuxer-mkv", OPT_SUBSTRUCT(demux_mkv, demux_mkv_conf)}, {"demuxer-mkv", OPT_SUBSTRUCT(demux_mkv, demux_mkv_conf)},
{"demuxer-cue", OPT_SUBSTRUCT(demux_cue, demux_cue_conf)}, {"demuxer-cue", OPT_SUBSTRUCT(demux_cue, demux_cue_conf)},

View File

@ -326,6 +326,7 @@ typedef struct MPOpts {
struct demux_rawaudio_opts *demux_rawaudio; struct demux_rawaudio_opts *demux_rawaudio;
struct demux_rawvideo_opts *demux_rawvideo; struct demux_rawvideo_opts *demux_rawvideo;
struct demux_playlist_opts *demux_playlist;
struct demux_lavf_opts *demux_lavf; struct demux_lavf_opts *demux_lavf;
struct demux_mkv_opts *demux_mkv; struct demux_mkv_opts *demux_mkv;
struct demux_cue_opts *demux_cue; struct demux_cue_opts *demux_cue;