Coverage Report

Created: 2026-11-11 06:43

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/mpv/demux/demux_playlist.c
Line
Count
Source
1
/*
2
 * This file is part of mpv.
3
 *
4
 * mpv is free software; you can redistribute it and/or
5
 * modify it under the terms of the GNU Lesser General Public
6
 * License as published by the Free Software Foundation; either
7
 * version 2.1 of the License, or (at your option) any later version.
8
 *
9
 * mpv is distributed in the hope that it will be useful,
10
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
 * GNU Lesser General Public License for more details.
13
 *
14
 * You should have received a copy of the GNU Lesser General Public
15
 * License along with mpv.  If not, see <http://www.gnu.org/licenses/>.
16
 */
17
18
#include <stdlib.h>
19
#include <string.h>
20
21
#include <libavutil/common.h>
22
23
#include "common/common.h"
24
#include "options/options.h"
25
#include "options/m_config.h"
26
#include "common/msg.h"
27
#include "common/playlist.h"
28
#include "misc/charset_conv.h"
29
#include "misc/thread_tools.h"
30
#include "options/path.h"
31
#include "player/core.h"
32
#include "stream/stream.h"
33
#include "osdep/io.h"
34
#include "misc/natural_sort.h"
35
#include "demux.h"
36
37
#define PROBE_SIZE (8 * 1024)
38
39
enum dir_mode {
40
    DIR_AUTO,
41
    DIR_LAZY,
42
    DIR_RECURSIVE,
43
    DIR_IGNORE,
44
};
45
46
enum autocreate_mode {
47
    AUTO_NONE     = 0,
48
    AUTO_VIDEO    = 1 << 0,
49
    AUTO_AUDIO    = 1 << 1,
50
    AUTO_IMAGE    = 1 << 2,
51
    AUTO_ARCHIVE  = 1 << 3,
52
    AUTO_PLAYLIST = 1 << 4,
53
    AUTO_ANY      = 1 << 5,
54
};
55
56
#define OPT_BASE_STRUCT struct demux_playlist_opts
57
struct demux_playlist_opts {
58
    int dir_mode;
59
};
60
61
struct m_sub_options demux_playlist_conf = {
62
    .opts = (const struct m_option[]) {
63
        {"directory-mode", OPT_CHOICE(dir_mode,
64
            {"auto", DIR_AUTO},
65
            {"lazy", DIR_LAZY},
66
            {"recursive", DIR_RECURSIVE},
67
            {"ignore", DIR_IGNORE})},
68
        {0}
69
    },
70
    .size = sizeof(struct demux_playlist_opts),
71
    .defaults = &(const struct demux_playlist_opts){
72
        .dir_mode = DIR_AUTO,
73
    },
74
    .change_flags = UPDATE_DEMUXER,
75
};
76
77
static bool check_mimetype(struct stream *s, const char *const *list)
78
621k
{
79
621k
    if (s->mime_type) {
80
0
        for (int n = 0; list && list[n]; n++) {
81
0
            if (strcasecmp(s->mime_type, list[n]) == 0)
82
0
                return true;
83
0
        }
84
0
    }
85
621k
    return false;
86
621k
}
87
88
struct pl_parser {
89
    struct mpv_global *global;
90
    struct mp_log *log;
91
    struct stream *s;
92
    char buffer[2 * 1024 * 1024];
93
    int utf16;
94
    struct playlist *pl;
95
    bool error;
96
    bool probing;
97
    bool force;
98
    bool line_allocated;
99
    int autocreate_playlist;
100
    enum demux_check check_level;
101
    struct stream *real_stream;
102
    const char *format;
103
    char *codepage;
104
    char **directory_filter;
105
    struct demux_playlist_opts *opts;
106
    struct MPOpts *mp_opts;
107
};
108
109
110
static uint16_t stream_read_word_endian(stream_t *s, bool big_endian)
111
1.66M
{
112
1.66M
    unsigned int y = stream_read_char(s);
113
1.66M
    y = (y << 8) | stream_read_char(s);
114
1.66M
    if (!big_endian)
115
302k
        y = ((y >> 8) & 0xFF) | (y << 8);
116
1.66M
    return y;
117
1.66M
}
118
119
// Read characters until the next '\n' (including), or until the buffer in s is
120
// exhausted.
121
static int read_characters(stream_t *s, uint8_t *dst, int dstsize, int utf16)
122
1.80M
{
123
1.80M
    if (utf16 == 1 || utf16 == 2) {
124
10.0k
        uint8_t *cur = dst;
125
1.65M
        while (1) {
126
1.65M
            if ((cur - dst) + 8 >= dstsize) // PUT_UTF8 writes max. 8 bytes
127
0
                return -1; // line too long
128
1.65M
            uint32_t c;
129
1.65M
            uint8_t tmp;
130
1.65M
            GET_UTF16(c, stream_read_word_endian(s, utf16 == 2), return -1;)
131
1.65M
            if (s->eof)
132
7.57k
                break; // legitimate EOF; ignore the case of partial reads
133
1.65M
            PUT_UTF8(c, tmp, *cur++ = tmp;)
134
1.64M
            if (c == '\n')
135
44
                break;
136
1.64M
        }
137
7.61k
        return cur - dst;
138
1.79M
    } else {
139
1.79M
        uint8_t buf[1024];
140
1.79M
        int buf_len = stream_read_peek(s, buf, sizeof(buf));
141
1.79M
        uint8_t *end = memchr(buf, '\n', buf_len);
142
1.79M
        int len = end ? end - buf + 1 : buf_len;
143
1.79M
        if (len > dstsize)
144
0
            return -1; // line too long
145
1.79M
        memcpy(dst, buf, len);
146
1.79M
        stream_seek_skip(s, stream_tell(s) + len);
147
1.79M
        return len;
148
1.79M
    }
149
1.80M
}
150
151
// On error, or if the line is larger than max-1, return NULL and unset s->eof.
152
// On EOF, return NULL, and s->eof will be set.
153
// Otherwise, return the line (including \n or \r\n at the end of the line).
154
// If the return value is non-NULL, it's always the same as mem.
155
// utf16: 0: UTF8 or 8 bit legacy, 1: UTF16-LE, 2: UTF16-BE
156
static char *read_line(stream_t *s, char *mem, int max, int utf16)
157
1.43M
{
158
1.43M
    if (max < 1)
159
0
        return NULL;
160
1.43M
    int read = 0;
161
1.80M
    while (1) {
162
        // Reserve 1 byte of ptr for terminating \0.
163
1.80M
        int l = read_characters(s, &mem[read], max - read - 1, utf16);
164
1.80M
        if (l < 0 || memchr(&mem[read], '\0', l)) {
165
29.9k
            MP_WARN(s, "error reading line\n");
166
29.9k
            return NULL;
167
29.9k
        }
168
1.77M
        read += l;
169
1.77M
        if (l == 0 || (read > 0 && mem[read - 1] == '\n'))
170
1.40M
            break;
171
1.77M
    }
172
1.40M
    mem[read] = '\0';
173
1.40M
    if (!stream_read_peek(s, &(char){0}, 1) && read == 0) // legitimate EOF
174
5.79k
        return NULL;
175
1.40M
    return mem;
176
1.40M
}
177
178
static char *pl_get_line0(struct pl_parser *p)
179
1.43M
{
180
1.43M
    char *res = read_line(p->s, p->buffer, sizeof(p->buffer), p->utf16);
181
1.43M
    if (res) {
182
1.40M
        int len = strlen(res);
183
1.40M
        if (len > 0 && res[len - 1] == '\n')
184
1.21M
            res[len - 1] = '\0';
185
1.40M
    } else {
186
35.7k
        p->error |= !p->s->eof;
187
35.7k
    }
188
1.43M
    return res;
189
1.43M
}
190
191
static bstr pl_get_line(struct pl_parser *p)
192
1.43M
{
193
1.43M
    bstr line = bstr_strip(bstr0(pl_get_line0(p)));
194
1.43M
    const char *charset = mp_charset_guess(p, p->log, line, p->codepage, 0);
195
1.43M
    if (charset && !mp_charset_is_utf8(charset)) {
196
510k
        bstr utf8 = mp_iconv_to_utf8(p->log, line, charset, 0);
197
510k
        if (utf8.start && utf8.start != line.start) {
198
510k
            line = utf8;
199
510k
            p->line_allocated = true;
200
510k
        }
201
510k
    }
202
1.43M
    return line;
203
1.43M
}
204
205
// Helper in case mp_iconv_to_utf8 allocates memory
206
static void pl_free_line(struct pl_parser *p, bstr line)
207
1.38M
{
208
1.38M
    if (p->line_allocated) {
209
510k
        talloc_free(line.start);
210
510k
        p->line_allocated = false;
211
510k
    }
212
1.38M
}
213
214
static void pl_add(struct pl_parser *p, bstr entry)
215
385k
{
216
385k
    void *ctx = talloc_new(NULL);
217
385k
    char *path = mp_get_user_path(ctx, p->global, bstrto0(ctx, entry));
218
219
385k
    bstr proto = mp_split_proto(bstr0(p->s->url), NULL);
220
    // Don't add base path to self-expanding protocols
221
385k
    if (!mp_is_url(entry) && bstrcasecmp0(proto, "memory") &&
222
134k
        bstrcasecmp0(proto, "lavf") && bstrcasecmp0(proto, "hex") &&
223
28.8k
        bstrcasecmp0(proto, "data") && bstrcasecmp0(proto, "fd")) {
224
1.18k
        const char *base = p->s->effective_url ? p->s->effective_url : p->s->url;
225
1.18k
        path = mp_path_join_bstr(ctx, mp_dirname(base), bstr0(path));
226
1.18k
    }
227
228
385k
    playlist_append_file(p->pl, path);
229
385k
    talloc_free(ctx);
230
385k
}
231
232
static bool pl_eof(struct pl_parser *p)
233
842k
{
234
842k
    return p->error || p->s->eof;
235
842k
}
236
237
static bool maybe_text(bstr d)
238
9.76k
{
239
5.56M
    for (int n = 0; n < d.len; n++) {
240
5.55M
        unsigned char c = d.start[n];
241
5.55M
        if (c < 32 && c != '\n' && c != '\r' && c != '\t')
242
6.24k
            return false;
243
5.55M
    }
244
3.52k
    return true;
245
9.76k
}
246
247
static int parse_m3u(struct pl_parser *p)
248
154k
{
249
154k
    bstr line = pl_get_line(p);
250
154k
    if (p->probing && !bstr_equals0(line, "#EXTM3U")) {
251
        // Last resort: if the file extension is m3u, it might be headerless.
252
147k
        if (p->check_level == DEMUX_CHECK_UNSAFE) {
253
62.5k
            bstr ext = mp_get_ext(bstr0(p->real_stream->url));
254
62.5k
            char probe[PROBE_SIZE];
255
62.5k
            int len = stream_read_peek(p->real_stream, probe, sizeof(probe));
256
62.5k
            bstr data = {probe, len};
257
62.5k
            if (ext.len && data.len >= 2 && maybe_text(data)) {
258
3.52k
                const char *exts[] = {"m3u", "m3u8", "strm", NULL};
259
8.60k
                for (int n = 0; exts[n]; n++) {
260
7.36k
                    if (bstrcasecmp(ext, bstr0(exts[n])) == 0)
261
2.29k
                        goto ok;
262
7.36k
                }
263
3.52k
            }
264
62.5k
        }
265
144k
        pl_free_line(p, line);
266
144k
        return -1;
267
147k
    }
268
269
9.11k
ok:
270
9.11k
    if (p->probing) {
271
4.55k
        pl_free_line(p, line);
272
4.55k
        return 0;
273
4.55k
    }
274
275
4.55k
    char *title = NULL;
276
669k
    while (line.len || !pl_eof(p)) {
277
664k
        bstr line_dup = line;
278
664k
        if (bstr_eatstart0(&line_dup, "#EXTINF:")) {
279
1.90k
            bstr duration, btitle;
280
1.90k
            if (bstr_split_tok(line_dup, ",", &duration, &btitle) && btitle.len) {
281
313
                talloc_free(title);
282
313
                title = bstrto0(NULL, btitle);
283
313
            }
284
663k
        } else if (bstr_startswith0(line_dup, "#EXT-X-")) {
285
            // Note that this is only used for user advice. Actual HLS demuxing
286
            // must happen through demux_lavf.
287
1.19k
            p->format = "hls";
288
661k
        } else if (line_dup.len > 0 && !bstr_startswith0(line_dup, "#")) {
289
382k
            pl_add(p, line);
290
382k
            struct playlist_entry *e = playlist_get_last(p->pl);
291
382k
            e->title = talloc_steal(e, title);
292
382k
            title = NULL;
293
382k
        }
294
664k
        pl_free_line(p, line);
295
664k
        line = pl_get_line(p);
296
664k
    }
297
4.55k
    pl_free_line(p, line);
298
4.55k
    talloc_free(title);
299
4.55k
    return 0;
300
9.11k
}
301
302
static int parse_ref_init(struct pl_parser *p)
303
72.6k
{
304
72.6k
    bstr line = pl_get_line(p);
305
72.6k
    if (!bstr_equals0(line, "[Reference]")) {
306
72.3k
        pl_free_line(p, line);
307
72.3k
        return -1;
308
72.3k
    }
309
284
    pl_free_line(p, line);
310
311
    // ASF http streaming redirection - this is needed because ffmpeg http://
312
    // and mmsh:// can not automatically switch automatically between each
313
    // others. Both protocols use http - MMSH requires special http headers
314
    // to "activate" it, and will in other cases return this playlist.
315
284
    static const char *const mmsh_types[] = {"audio/x-ms-wax",
316
284
        "audio/x-ms-wma", "video/x-ms-asf", "video/x-ms-afs", "video/x-ms-wmv",
317
284
        "video/x-ms-wma", "application/x-mms-framed",
318
284
        "application/vnd.ms.wms-hdr.asfv1", NULL};
319
284
    bstr burl = bstr0(p->s->url);
320
284
    if (bstr_eatstart0(&burl, "http://") && check_mimetype(p->s, mmsh_types)) {
321
0
        MP_INFO(p, "Redirecting to mmsh://\n");
322
0
        playlist_append_file(p->pl, talloc_asprintf(p, "mmsh://%.*s", BSTR_P(burl)));
323
0
        return 0;
324
0
    }
325
326
72.8k
    while (!pl_eof(p)) {
327
72.5k
        line = pl_get_line(p);
328
72.5k
        bstr value;
329
72.5k
        if (bstr_case_startswith(line, bstr0("Ref"))) {
330
1.21k
            bstr_split_tok(line, "=", &(bstr){0}, &value);
331
1.21k
            if (value.len)
332
638
                pl_add(p, value);
333
1.21k
        }
334
72.5k
        pl_free_line(p, line);
335
72.5k
    }
336
284
    return 0;
337
284
}
338
339
static int parse_ini_thing(struct pl_parser *p, const char *header,
340
                           const char *entry)
341
144k
{
342
144k
    bstr line = {0};
343
343k
    while (!line.len && !pl_eof(p))
344
199k
        line = pl_get_line(p);
345
144k
    if (bstrcasecmp0(line, header) != 0) {
346
144k
        pl_free_line(p, line);
347
144k
        return -1;
348
144k
    }
349
610
    if (p->probing) {
350
305
        pl_free_line(p, line);
351
305
        return 0;
352
305
    }
353
305
    pl_free_line(p, line);
354
273k
    while (!pl_eof(p)) {
355
272k
        line = pl_get_line(p);
356
272k
        bstr key, value;
357
272k
        if (bstr_split_tok(line, "=", &key, &value) &&
358
6.06k
            bstr_case_startswith(key, bstr0(entry)))
359
2.52k
        {
360
2.52k
            value = bstr_strip(value);
361
2.52k
            if (bstr_startswith0(value, "\"") && bstr_endswith0(value, "\""))
362
143
                value = bstr_splice(value, 1, -1);
363
2.52k
            pl_add(p, value);
364
2.52k
        }
365
272k
        pl_free_line(p, line);
366
272k
    }
367
305
    return 0;
368
610
}
369
370
static int parse_pls(struct pl_parser *p)
371
72.5k
{
372
72.5k
    return parse_ini_thing(p, "[playlist]", "File");
373
72.5k
}
374
375
static int parse_url(struct pl_parser *p)
376
72.2k
{
377
72.2k
    return parse_ini_thing(p, "[InternetShortcut]", "URL");
378
72.2k
}
379
380
static int parse_txt(struct pl_parser *p)
381
72.0k
{
382
72.0k
    if (!p->force)
383
72.0k
        return -1;
384
0
    if (p->probing)
385
0
        return 0;
386
0
    MP_WARN(p, "Reading plaintext playlist.\n");
387
0
    while (!pl_eof(p)) {
388
0
        bstr line = pl_get_line(p);
389
0
        if (line.len == 0)
390
0
            continue;
391
0
        pl_add(p, line);
392
0
        pl_free_line(p, line);
393
0
    }
394
0
    return 0;
395
0
}
396
397
0
#define MAX_DIR_STACK 20
398
399
static bool same_st(struct stat *st1, struct stat *st2)
400
0
{
401
0
    return st1->st_dev == st2->st_dev && st1->st_ino == st2->st_ino;
402
0
}
403
404
struct pl_dir_entry {
405
    char *path;
406
    char *name;
407
    struct stat st;
408
    bool is_dir;
409
};
410
411
static int cmp_dir_entry(const void *a, const void *b)
412
0
{
413
0
    struct pl_dir_entry *a_entry = (struct pl_dir_entry*) a;
414
0
    struct pl_dir_entry *b_entry = (struct pl_dir_entry*) b;
415
0
    if (a_entry->is_dir == b_entry->is_dir) {
416
0
        return mp_natural_sort_cmp(a_entry->name, b_entry->name);
417
0
    } else {
418
0
        return a_entry->is_dir ? 1 : -1;
419
0
    }
420
0
}
421
422
static bool test_path(struct pl_parser *p, char *path, int autocreate)
423
0
{
424
0
    if (autocreate & AUTO_ANY)
425
0
        return true;
426
427
0
    if (!strcmp(path, p->real_stream->path))
428
0
        return true;
429
430
0
    bstr ext = mp_get_ext(bstr0(path));
431
0
    if (autocreate & AUTO_VIDEO && bstr_in_list0(ext, p->mp_opts->video_exts))
432
0
        return true;
433
0
    if (autocreate & AUTO_AUDIO && bstr_in_list0(ext, p->mp_opts->audio_exts))
434
0
        return true;
435
0
    if (autocreate & AUTO_IMAGE && bstr_in_list0(ext, p->mp_opts->image_exts))
436
0
        return true;
437
0
    if (autocreate & AUTO_ARCHIVE && bstr_in_list0(ext, p->mp_opts->archive_exts))
438
0
        return true;
439
0
    if (autocreate & AUTO_PLAYLIST && bstr_in_list0(ext, p->mp_opts->playlist_exts))
440
0
        return true;
441
442
0
    return false;
443
0
}
444
445
// Return true if this was a readable directory.
446
static bool scan_dir(struct pl_parser *p, char *path,
447
                     struct stat *dir_stack, int num_dir_stack, int autocreate)
448
0
{
449
0
    if (strlen(path) >= 8192 || num_dir_stack == MAX_DIR_STACK)
450
0
        return false; // things like mount bind loops
451
452
0
    DIR *dp = opendir(path);
453
0
    if (!dp) {
454
0
        MP_ERR(p, "Could not read directory.\n");
455
0
        return false;
456
0
    }
457
458
0
    struct pl_dir_entry *dir_entries = NULL;
459
0
    int num_dir_entries = 0;
460
0
    int path_len = strlen(path);
461
0
    int dir_mode = p->opts->dir_mode;
462
463
0
    struct dirent *ep;
464
0
    while ((ep = readdir(dp))) {
465
0
        if (ep->d_name[0] == '.')
466
0
            continue;
467
468
0
        if (mp_cancel_test(p->s->cancel))
469
0
            break;
470
471
0
        char *file = mp_path_join(p, path, ep->d_name);
472
473
0
        struct stat st;
474
0
        if (stat(file, &st) == 0 && S_ISDIR(st.st_mode)) {
475
0
            if (dir_mode != DIR_IGNORE) {
476
0
                for (int n = 0; n < num_dir_stack; n++) {
477
0
                    if (same_st(&dir_stack[n], &st)) {
478
0
                        MP_VERBOSE(p, "Skip recursive entry: %s\n", file);
479
0
                        goto skip;
480
0
                    }
481
0
                }
482
483
0
                struct pl_dir_entry d = {file, &file[path_len], st, true};
484
0
                MP_TARRAY_APPEND(p, dir_entries, num_dir_entries, d);
485
0
            }
486
0
        } else {
487
0
            struct pl_dir_entry f = {file, &file[path_len], .is_dir = false};
488
0
            MP_TARRAY_APPEND(p, dir_entries, num_dir_entries, f);
489
0
        }
490
491
0
        skip: ;
492
0
    }
493
0
    closedir(dp);
494
495
0
    if (dir_entries)
496
0
        qsort(dir_entries, num_dir_entries, sizeof(dir_entries[0]), cmp_dir_entry);
497
498
0
    for (int n = 0; n < num_dir_entries; n++) {
499
0
        char *file = dir_entries[n].path;
500
0
        if (dir_mode == DIR_RECURSIVE && dir_entries[n].is_dir) {
501
0
            dir_stack[num_dir_stack] = dir_entries[n].st;
502
0
            scan_dir(p, file, dir_stack, num_dir_stack + 1, autocreate);
503
0
        }
504
0
        else {
505
0
            if (dir_entries[n].is_dir || test_path(p, file, autocreate))
506
0
                playlist_append_file(p->pl, dir_entries[n].path);
507
0
        }
508
0
    }
509
510
0
    return true;
511
0
}
512
513
static enum autocreate_mode get_directory_filter(struct pl_parser *p)
514
183k
{
515
183k
    enum autocreate_mode autocreate = AUTO_NONE;
516
183k
    if (!p->directory_filter || !p->directory_filter[0])
517
0
        autocreate = AUTO_ANY;
518
183k
    if (bstr_in_list0(bstr0("video"), p->directory_filter))
519
183k
        autocreate |= AUTO_VIDEO;
520
183k
    if (bstr_in_list0(bstr0("audio"), p->directory_filter))
521
183k
        autocreate |= AUTO_AUDIO;
522
183k
    if (bstr_in_list0(bstr0("image"), p->directory_filter))
523
183k
        autocreate |= AUTO_IMAGE;
524
183k
    if (bstr_in_list0(bstr0("archive"), p->directory_filter))
525
183k
        autocreate |= AUTO_ARCHIVE;
526
183k
    if (bstr_in_list0(bstr0("playlist"), p->directory_filter))
527
183k
        autocreate |= AUTO_PLAYLIST;
528
183k
    return autocreate;
529
183k
}
530
531
static int parse_dir(struct pl_parser *p)
532
183k
{
533
183k
    int ret = -1;
534
183k
    struct stream *stream = p->real_stream;
535
183k
    enum autocreate_mode autocreate = AUTO_NONE;
536
183k
    p->pl->playlist_dir = NULL;
537
183k
    if (p->autocreate_playlist && p->real_stream->is_local_fs && p->real_stream->is_regular &&
538
0
        !p->real_stream->is_directory) {
539
0
        bstr ext = mp_get_ext(bstr0(p->real_stream->url));
540
0
        switch (p->autocreate_playlist) {
541
0
        case 1: // filter
542
0
            autocreate = get_directory_filter(p);
543
0
            break;
544
0
        case 2: // same
545
0
            if (bstr_in_list0(ext, p->mp_opts->video_exts)) {
546
0
                autocreate = AUTO_VIDEO;
547
0
            } else if (bstr_in_list0(ext, p->mp_opts->audio_exts)) {
548
0
                autocreate = AUTO_AUDIO;
549
0
            } else if (bstr_in_list0(ext, p->mp_opts->image_exts)) {
550
0
                autocreate = AUTO_IMAGE;
551
0
            } else if (bstr_in_list0(ext, p->mp_opts->archive_exts)) {
552
0
                autocreate = AUTO_ARCHIVE;
553
0
            } else if (bstr_in_list0(ext, p->mp_opts->playlist_exts)) {
554
0
                autocreate = AUTO_PLAYLIST;
555
0
            }
556
0
            break;
557
0
        }
558
0
        int flags = STREAM_READ_FILE_FLAGS_DEFAULT;
559
0
        bstr dir = mp_dirname(p->real_stream->url);
560
0
        if (!dir.len)
561
0
            autocreate = AUTO_NONE;
562
0
        if (autocreate != AUTO_NONE) {
563
0
            stream = stream_create(bstrdup0(p, dir), flags, NULL, p->global);
564
0
            p->pl->playlist_dir = bstrdup0(p->pl, dir);
565
0
        }
566
183k
    } else {
567
183k
        autocreate = get_directory_filter(p);
568
183k
    }
569
183k
    if (!stream->is_directory)
570
183k
        goto done;
571
0
    if (p->probing) {
572
0
        ret = 0;
573
0
        goto done;
574
0
    }
575
576
0
    char *path = mp_file_get_path(p, bstr0(stream->url));
577
0
    if (!path)
578
0
        goto done;
579
580
0
    if (autocreate == AUTO_NONE)
581
0
        goto done;
582
583
0
    struct stat dir_stack[MAX_DIR_STACK];
584
585
0
    if (p->opts->dir_mode == DIR_AUTO)
586
0
        p->opts->dir_mode = p->mp_opts->shuffle ? DIR_RECURSIVE : DIR_LAZY;
587
588
0
    scan_dir(p, path, dir_stack, 0, autocreate);
589
590
0
    ret = p->pl->num_entries > 0 ? 0 : -1;
591
592
183k
done:
593
183k
    if (stream != p->real_stream)
594
0
        free_stream(stream);
595
183k
    return ret;
596
0
}
597
598
#define MIME_TYPES(...) \
599
    .mime_types = (const char*const[]){__VA_ARGS__, NULL}
600
601
struct pl_format {
602
    const char *name;
603
    int (*parse)(struct pl_parser *p);
604
    const char *const *mime_types;
605
};
606
607
static const struct pl_format dir_formats[] = {
608
    {"directory", parse_dir},
609
    {0},
610
};
611
612
static const struct pl_format playlist_formats[] = {
613
    {"m3u", parse_m3u,
614
     MIME_TYPES("audio/mpegurl", "audio/x-mpegurl", "application/x-mpegurl")},
615
    {"strm", parse_m3u},
616
    {"ini", parse_ref_init},
617
    {"pls", parse_pls,
618
     MIME_TYPES("audio/x-scpls")},
619
    {"url", parse_url},
620
    {"txt", parse_txt},
621
    {0},
622
};
623
624
static const struct pl_format *probe_pl(struct pl_parser *p, const struct pl_format *fmts)
625
260k
{
626
260k
    int64_t start = stream_tell(p->s);
627
260k
    const struct pl_format *fmt = fmts;
628
877k
    while (fmt->name) {
629
621k
        stream_seek(p->s, start);
630
621k
        if (check_mimetype(p->s, fmt->mime_types)) {
631
0
            MP_VERBOSE(p, "forcing format by mime-type.\n");
632
0
            p->force = true;
633
0
            return fmt;
634
0
        }
635
621k
        if (fmt->parse(p) >= 0)
636
5.00k
            return fmt;
637
616k
        fmt++;
638
616k
    }
639
255k
    return NULL;
640
260k
}
641
642
extern const demuxer_desc_t demuxer_desc_playlist;
643
extern const demuxer_desc_t demuxer_desc_directory;
644
645
static int open_file(struct demuxer *demuxer, enum demux_check check)
646
260k
{
647
260k
    if (!demuxer->access_references)
648
0
        return -1;
649
650
260k
    bool force = check < DEMUX_CHECK_UNSAFE || check == DEMUX_CHECK_REQUEST;
651
652
260k
    struct pl_parser *p = talloc_zero(NULL, struct pl_parser);
653
260k
    p->global = demuxer->global;
654
260k
    p->log = demuxer->log;
655
260k
    p->pl = talloc_zero(p, struct playlist);
656
260k
    p->real_stream = demuxer->stream;
657
658
260k
    struct demux_opts *opts = mp_get_config_group(p, p->global, &demux_conf);
659
260k
    p->codepage = opts->meta_cp;
660
661
260k
    char probe[PROBE_SIZE];
662
260k
    int probe_len = stream_read_peek(p->real_stream, probe, sizeof(probe));
663
260k
    p->s = stream_memory_open(demuxer->global, probe, probe_len);
664
260k
    p->s->mime_type = demuxer->stream->mime_type;
665
260k
    p->utf16 = stream_skip_bom(p->s);
666
260k
    p->force = force;
667
260k
    p->check_level = check;
668
260k
    p->probing = true;
669
260k
    p->autocreate_playlist = demuxer->params->allow_playlist_create ? opts->autocreate_playlist : 0;
670
260k
    p->directory_filter = opts->directory_filter;
671
260k
    p->mp_opts = mp_get_config_group(demuxer, demuxer->global, &mp_opt_root);
672
260k
    p->opts = mp_get_config_group(demuxer, demuxer->global, &demux_playlist_conf);
673
674
260k
    const struct pl_format *fmts = playlist_formats;
675
260k
    if (demuxer->desc == &demuxer_desc_directory)
676
183k
        fmts = dir_formats;
677
678
260k
    const struct pl_format *fmt = probe_pl(p, fmts);
679
260k
    free_stream(p->s);
680
260k
    playlist_clear(p->pl);
681
260k
    if (!fmt) {
682
255k
        talloc_free(p);
683
255k
        return -1;
684
255k
    }
685
686
5.00k
    p->probing = false;
687
5.00k
    p->error = false;
688
5.00k
    p->s = demuxer->stream;
689
5.00k
    p->utf16 = stream_skip_bom(p->s);
690
5.00k
    bool ok = fmt->parse(p) >= 0 && !p->error;
691
5.00k
    playlist_set_stream_flags(p->pl, demuxer->stream_origin);
692
5.00k
    demuxer->playlist = talloc_steal(demuxer, p->pl);
693
5.00k
    demuxer->filetype = p->format ? p->format : fmt->name;
694
5.00k
    demuxer->fully_read = true;
695
5.00k
    talloc_free(p);
696
5.00k
    if (ok)
697
5.00k
        demux_close_stream(demuxer);
698
5.00k
    return ok ? 0 : -1;
699
260k
}
700
701
const demuxer_desc_t demuxer_desc_directory = {
702
    .name = "directory",
703
    .desc = "Playlist dir",
704
    .open = open_file,
705
};
706
707
const demuxer_desc_t demuxer_desc_playlist = {
708
    .name = "playlist",
709
    .desc = "Playlist file",
710
    .open = open_file,
711
};