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
- icc and gpu-shader cache are now saved by default (use --no-icc-shader-cache and
--no-gpu-shader-cache to disable)
- add `--directory-mode=recursive|lazy|ignore`
--- mpv 0.35.0 ---
- add the `--vo=gpu-next` video output driver, as well as the options
`--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
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
-----

View File

@ -24,6 +24,7 @@
#include "common/common.h"
#include "options/options.h"
#include "options/m_config.h"
#include "common/msg.h"
#include "common/playlist.h"
#include "misc/thread_tools.h"
@ -35,6 +36,31 @@
#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)
{
if (s->mime_type) {
@ -59,6 +85,7 @@ struct pl_parser {
enum demux_check check_level;
struct stream *real_stream;
char *format;
struct demux_playlist_opts *opts;
};
@ -323,6 +350,7 @@ static bool scan_dir(struct pl_parser *p, char *path,
return false;
}
int dir_mode = p->opts->dir_mode;
struct dirent *ep;
while ((ep = readdir(dp))) {
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);
struct stat st;
if (stat(file, &st) == 0 && S_ISDIR(st.st_mode)) {
for (int n = 0; n < num_dir_stack; n++) {
if (same_st(&dir_stack[n], &st)) {
MP_VERBOSE(p, "Skip recursive entry: %s\n", file);
goto skip;
if (dir_mode != DIR_LAZY && stat(file, &st) == 0 && S_ISDIR(st.st_mode)) {
if (dir_mode != DIR_IGNORE) {
for (int n = 0; n < num_dir_stack; n++) {
if (same_st(&dir_stack[n], &st)) {
MP_VERBOSE(p, "Skip recursive entry: %s\n", file);
goto skip;
}
}
}
dir_stack[num_dir_stack] = st;
scan_dir(p, file, dir_stack, num_dir_stack + 1, files, num_files);
dir_stack[num_dir_stack] = st;
scan_dir(p, file, dir_stack, num_dir_stack + 1, files, num_files);
}
} else {
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->s = demuxer->stream;
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;
if (p->add_base)
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;
}
const struct demuxer_desc demuxer_desc_playlist = {
const demuxer_desc_t demuxer_desc_playlist = {
.name = "playlist",
.desc = "Playlist 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 demux_rawaudio_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_mkv_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)},
{"demuxer-rawaudio", OPT_SUBSTRUCT(demux_rawaudio, demux_rawaudio_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-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_rawvideo_opts *demux_rawvideo;
struct demux_playlist_opts *demux_playlist;
struct demux_lavf_opts *demux_lavf;
struct demux_mkv_opts *demux_mkv;
struct demux_cue_opts *demux_cue;