2017-04-21 10:34:41 +00:00
|
|
|
/*
|
|
|
|
* This file is part of mpv.
|
|
|
|
*
|
2017-06-20 11:09:28 +00:00
|
|
|
* mpv is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
|
|
* License as published by the Free Software Foundation; either
|
|
|
|
* version 2.1 of the License, or (at your option) any later version.
|
2017-04-21 10:34:41 +00:00
|
|
|
*
|
|
|
|
* mpv is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
2017-06-20 11:09:28 +00:00
|
|
|
* GNU Lesser General Public License for more details.
|
2017-04-21 10:34:41 +00:00
|
|
|
*
|
2017-06-20 11:09:28 +00:00
|
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
|
|
* License along with mpv. If not, see <http://www.gnu.org/licenses/>.
|
2017-04-21 10:34:41 +00:00
|
|
|
*/
|
|
|
|
|
2011-02-26 19:47:49 +00:00
|
|
|
#include <string.h>
|
|
|
|
#include <stdlib.h>
|
2011-04-19 03:53:56 +00:00
|
|
|
#include <assert.h>
|
2011-02-26 19:47:49 +00:00
|
|
|
|
2012-02-03 07:05:11 +00:00
|
|
|
#include "osdep/io.h"
|
|
|
|
|
2014-07-01 21:10:38 +00:00
|
|
|
#include "common/common.h"
|
2013-12-21 18:02:05 +00:00
|
|
|
#include "common/global.h"
|
2013-12-17 01:39:45 +00:00
|
|
|
#include "common/msg.h"
|
2017-01-24 18:48:02 +00:00
|
|
|
#include "misc/charset_conv.h"
|
2024-05-09 19:37:53 +00:00
|
|
|
#include "misc/language.h"
|
2013-12-17 01:02:25 +00:00
|
|
|
#include "options/options.h"
|
|
|
|
#include "options/path.h"
|
2024-07-29 18:54:06 +00:00
|
|
|
#include "player/core.h"
|
2015-09-20 16:05:06 +00:00
|
|
|
#include "external_files.h"
|
2011-02-26 19:47:49 +00:00
|
|
|
|
player: make all autoload extensions configurable
--audio-file-auto, --cover-art-auto, and --sub-auto all work by using an
internally hardcoded list that determine what file extensions get
recognized. This is fine and people periodically update it, but we can
actually expose this as a stringlist option instead. This way users can
add or remove any file extension for any type. For the most part, this
is pretty pretty easy and involves making sub_exts, etc. the defaults
for the new options (--audio-file-auto-exts, --cover-art-auto-exts, and
--sub-auto-exts). There's actually one slight complication however. The
input code uses mp_might_be_subtitle_file which guesses if the file drag
and dropped file is a subtitle. The input ctx has no access to mpctx so
we have to be clever here.
For this, the trick is to recognize that we can leverage the
m_option_change_callback. We add a new flag, UPDATE_SUB_EXTS, which
fires when the player starts up. Then in the callback, we can set the
value of sub_exts in external_files to opts->sub_auto_exts. Whenever the
option updates, the callback is fired again and sub_exts updates. That
way mp_might_be_subtitle_file can just operate off of this global
variable instead of trying to mess with the core mpv state directly.
Fixes #12000.
2023-08-10 22:36:22 +00:00
|
|
|
// Needed for mp_might_be_subtitle_file
|
|
|
|
char **sub_exts;
|
|
|
|
|
|
|
|
static int test_ext(MPOpts *opts, bstr ext)
|
2015-02-02 20:23:12 +00:00
|
|
|
{
|
2024-07-29 18:54:06 +00:00
|
|
|
if (str_in_list(ext, opts->sub_auto_exts))
|
2015-02-02 20:23:12 +00:00
|
|
|
return STREAM_SUB;
|
2024-07-29 18:54:06 +00:00
|
|
|
if (str_in_list(ext, opts->audio_exts))
|
2015-02-02 20:23:12 +00:00
|
|
|
return STREAM_AUDIO;
|
2024-07-29 18:54:06 +00:00
|
|
|
if (str_in_list(ext, opts->image_exts))
|
2021-03-22 09:21:52 +00:00
|
|
|
return STREAM_VIDEO;
|
2015-02-02 20:23:12 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2024-07-13 05:57:06 +00:00
|
|
|
static int test_cover_filename(bstr fname, char **cover_files)
|
2020-09-27 22:12:52 +00:00
|
|
|
{
|
|
|
|
for (int n = 0; cover_files[n]; n++) {
|
2020-10-28 17:15:34 +00:00
|
|
|
if (bstrcasecmp(bstr0(cover_files[n]), fname) == 0) {
|
2024-07-13 05:57:06 +00:00
|
|
|
size_t size = n;
|
|
|
|
while (cover_files[++size]);
|
|
|
|
return size - n;
|
2020-10-28 17:15:34 +00:00
|
|
|
}
|
2020-09-27 22:12:52 +00:00
|
|
|
}
|
2021-03-22 09:21:52 +00:00
|
|
|
return 0;
|
2020-09-27 22:12:52 +00:00
|
|
|
}
|
|
|
|
|
2014-01-04 00:27:06 +00:00
|
|
|
bool mp_might_be_subtitle_file(const char *filename)
|
|
|
|
{
|
2024-07-29 18:54:06 +00:00
|
|
|
return str_in_list(bstr_get_ext(bstr0(filename)), sub_exts);
|
player: make all autoload extensions configurable
--audio-file-auto, --cover-art-auto, and --sub-auto all work by using an
internally hardcoded list that determine what file extensions get
recognized. This is fine and people periodically update it, but we can
actually expose this as a stringlist option instead. This way users can
add or remove any file extension for any type. For the most part, this
is pretty pretty easy and involves making sub_exts, etc. the defaults
for the new options (--audio-file-auto-exts, --cover-art-auto-exts, and
--sub-auto-exts). There's actually one slight complication however. The
input code uses mp_might_be_subtitle_file which guesses if the file drag
and dropped file is a subtitle. The input ctx has no access to mpctx so
we have to be clever here.
For this, the trick is to recognize that we can leverage the
m_option_change_callback. We add a new flag, UPDATE_SUB_EXTS, which
fires when the player starts up. Then in the callback, we can set the
value of sub_exts in external_files to opts->sub_auto_exts. Whenever the
option updates, the callback is fired again and sub_exts updates. That
way mp_might_be_subtitle_file can just operate off of this global
variable instead of trying to mess with the core mpv state directly.
Fixes #12000.
2023-08-10 22:36:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void mp_update_subtitle_exts(struct MPOpts *opts)
|
|
|
|
{
|
|
|
|
sub_exts = opts->sub_auto_exts;
|
2014-01-04 00:27:06 +00:00
|
|
|
}
|
|
|
|
|
2013-09-07 18:31:00 +00:00
|
|
|
static int compare_sub_filename(const void *a, const void *b)
|
|
|
|
{
|
|
|
|
const struct subfn *s1 = a;
|
|
|
|
const struct subfn *s2 = b;
|
|
|
|
return strcoll(s1->fname, s2->fname);
|
|
|
|
}
|
|
|
|
|
2011-02-26 19:47:49 +00:00
|
|
|
static int compare_sub_priority(const void *a, const void *b)
|
|
|
|
{
|
2011-03-03 19:05:17 +00:00
|
|
|
const struct subfn *s1 = a;
|
|
|
|
const struct subfn *s2 = b;
|
|
|
|
if (s1->priority > s2->priority)
|
2011-02-26 19:47:49 +00:00
|
|
|
return -1;
|
2011-03-03 19:05:17 +00:00
|
|
|
if (s1->priority < s2->priority)
|
2011-02-26 19:47:49 +00:00
|
|
|
return 1;
|
2011-03-03 19:05:17 +00:00
|
|
|
return strcoll(s1->fname, s2->fname);
|
2011-02-26 19:47:49 +00:00
|
|
|
}
|
|
|
|
|
2018-05-21 14:25:52 +00:00
|
|
|
static void append_dir_subtitles(struct mpv_global *global, struct MPOpts *opts,
|
2011-03-03 18:59:15 +00:00
|
|
|
struct subfn **slist, int *nsub,
|
2011-03-01 21:42:22 +00:00
|
|
|
struct bstr path, const char *fname,
|
2015-12-25 12:40:06 +00:00
|
|
|
int limit_fuzziness, int limit_type)
|
2011-02-26 19:47:49 +00:00
|
|
|
{
|
2011-04-19 03:53:56 +00:00
|
|
|
void *tmpmem = talloc_new(NULL);
|
2015-02-02 20:23:12 +00:00
|
|
|
struct mp_log *log = mp_log_new(tmpmem, global->log, "find_files");
|
2011-02-26 19:47:49 +00:00
|
|
|
|
2017-01-24 18:48:02 +00:00
|
|
|
struct bstr f_fbname = bstr0(mp_basename(fname));
|
|
|
|
struct bstr f_fname = mp_iconv_to_utf8(log, f_fbname,
|
|
|
|
"UTF-8-MAC", MP_NO_LATIN1_FALLBACK);
|
2015-11-07 07:40:34 +00:00
|
|
|
struct bstr f_fname_noext = bstrdup(tmpmem, bstr_strip_ext(f_fname));
|
2011-04-19 03:53:56 +00:00
|
|
|
struct bstr f_fname_trim = bstr_strip(f_fname_noext);
|
2011-02-26 19:47:49 +00:00
|
|
|
|
2017-01-24 18:48:02 +00:00
|
|
|
if (f_fbname.start != f_fname.start)
|
|
|
|
talloc_steal(tmpmem, f_fname.start);
|
|
|
|
|
2011-04-19 03:53:56 +00:00
|
|
|
char *path0 = bstrdup0(tmpmem, path);
|
2017-03-31 18:48:06 +00:00
|
|
|
|
|
|
|
if (mp_is_url(bstr0(path0)))
|
|
|
|
goto out;
|
|
|
|
|
2011-03-03 19:05:17 +00:00
|
|
|
DIR *d = opendir(path0);
|
2011-04-19 03:53:56 +00:00
|
|
|
if (!d)
|
|
|
|
goto out;
|
2015-02-02 20:23:12 +00:00
|
|
|
mp_verbose(log, "Loading external files in %.*s\n", BSTR_P(path));
|
2011-04-19 03:53:56 +00:00
|
|
|
struct dirent *de;
|
|
|
|
while ((de = readdir(d))) {
|
|
|
|
void *tmpmem2 = talloc_new(tmpmem);
|
2017-01-24 18:48:02 +00:00
|
|
|
struct bstr den = bstr0(de->d_name);
|
|
|
|
struct bstr dename = mp_iconv_to_utf8(log, den,
|
|
|
|
"UTF-8-MAC", MP_NO_LATIN1_FALLBACK);
|
2011-04-19 03:53:56 +00:00
|
|
|
// retrieve various parts of the filename
|
2015-11-07 07:40:34 +00:00
|
|
|
struct bstr tmp_fname_noext = bstrdup(tmpmem2, bstr_strip_ext(dename));
|
|
|
|
struct bstr tmp_fname_ext = bstr_get_ext(dename);
|
2011-04-19 03:53:56 +00:00
|
|
|
struct bstr tmp_fname_trim = bstr_strip(tmp_fname_noext);
|
|
|
|
|
2017-01-24 18:48:02 +00:00
|
|
|
if (den.start != dename.start)
|
|
|
|
talloc_steal(tmpmem2, dename.start);
|
|
|
|
|
2015-02-02 20:23:12 +00:00
|
|
|
// check what it is (most likely)
|
player: make all autoload extensions configurable
--audio-file-auto, --cover-art-auto, and --sub-auto all work by using an
internally hardcoded list that determine what file extensions get
recognized. This is fine and people periodically update it, but we can
actually expose this as a stringlist option instead. This way users can
add or remove any file extension for any type. For the most part, this
is pretty pretty easy and involves making sub_exts, etc. the defaults
for the new options (--audio-file-auto-exts, --cover-art-auto-exts, and
--sub-auto-exts). There's actually one slight complication however. The
input code uses mp_might_be_subtitle_file which guesses if the file drag
and dropped file is a subtitle. The input ctx has no access to mpctx so
we have to be clever here.
For this, the trick is to recognize that we can leverage the
m_option_change_callback. We add a new flag, UPDATE_SUB_EXTS, which
fires when the player starts up. Then in the callback, we can set the
value of sub_exts in external_files to opts->sub_auto_exts. Whenever the
option updates, the callback is fired again and sub_exts updates. That
way mp_might_be_subtitle_file can just operate off of this global
variable instead of trying to mess with the core mpv state directly.
Fixes #12000.
2023-08-10 22:36:22 +00:00
|
|
|
int type = test_ext(opts, tmp_fname_ext);
|
2015-02-02 20:23:12 +00:00
|
|
|
char **langs = NULL;
|
|
|
|
int fuzz = -1;
|
|
|
|
switch (type) {
|
|
|
|
case STREAM_SUB:
|
2015-05-22 19:00:24 +00:00
|
|
|
langs = opts->stream_lang[type];
|
2015-02-02 20:23:12 +00:00
|
|
|
fuzz = opts->sub_auto;
|
|
|
|
break;
|
|
|
|
case STREAM_AUDIO:
|
2015-05-22 19:00:24 +00:00
|
|
|
langs = opts->stream_lang[type];
|
2015-02-02 20:23:12 +00:00
|
|
|
fuzz = opts->audiofile_auto;
|
|
|
|
break;
|
2020-09-27 22:12:52 +00:00
|
|
|
case STREAM_VIDEO:
|
|
|
|
fuzz = opts->coverart_auto;
|
|
|
|
break;
|
2015-02-02 20:23:12 +00:00
|
|
|
}
|
|
|
|
|
2015-12-25 12:40:06 +00:00
|
|
|
if (fuzz < 0 || (limit_type >= 0 && limit_type != type))
|
2013-09-07 18:31:00 +00:00
|
|
|
goto next_sub;
|
2011-03-03 18:59:15 +00:00
|
|
|
|
2011-04-19 03:53:56 +00:00
|
|
|
// we have a (likely) subtitle file
|
2020-05-09 21:31:33 +00:00
|
|
|
// higher prio -> auto-selection may prefer it (0 = not loaded)
|
2011-04-19 03:53:56 +00:00
|
|
|
int prio = 0;
|
2017-11-27 18:45:13 +00:00
|
|
|
|
2024-04-18 16:08:17 +00:00
|
|
|
if (bstrcasecmp(tmp_fname_trim, f_fname_trim) == 0)
|
2020-05-09 21:31:33 +00:00
|
|
|
prio |= 32; // exact movie name match
|
|
|
|
|
2021-06-20 15:10:39 +00:00
|
|
|
bstr lang = {0};
|
2023-05-21 08:56:18 +00:00
|
|
|
int start = 0;
|
2024-05-09 19:37:53 +00:00
|
|
|
lang = mp_guess_lang_from_filename(dename, &start);
|
2024-04-18 16:08:17 +00:00
|
|
|
if (bstr_case_startswith(tmp_fname_trim, f_fname_trim)) {
|
2020-05-09 21:31:33 +00:00
|
|
|
if (lang.len && start == f_fname_trim.len)
|
|
|
|
prio |= 16; // exact movie name + followed by lang
|
|
|
|
|
2023-06-01 22:54:38 +00:00
|
|
|
if (lang.len && fuzz >= 1)
|
|
|
|
prio |= 4; // matches the movie name + a language was matched
|
|
|
|
|
|
|
|
for (int n = 0; langs && langs[n]; n++) {
|
|
|
|
if (lang.len && bstr_case_startswith(lang, bstr0(langs[n]))) {
|
|
|
|
if (fuzz >= 1)
|
|
|
|
prio |= 8; // known language -> boost priority
|
|
|
|
break;
|
|
|
|
}
|
2011-04-19 03:53:56 +00:00
|
|
|
}
|
|
|
|
}
|
2020-05-09 21:31:33 +00:00
|
|
|
|
|
|
|
if (bstr_find(tmp_fname_trim, f_fname_trim) >= 0 && fuzz >= 1)
|
|
|
|
prio |= 2; // contains the movie name
|
|
|
|
|
2024-07-13 05:57:06 +00:00
|
|
|
if (type == STREAM_VIDEO && prio == 0)
|
|
|
|
prio = test_cover_filename(tmp_fname_trim, opts->coverart_whitelist);
|
2021-06-21 16:27:44 +00:00
|
|
|
|
2020-05-09 21:31:33 +00:00
|
|
|
// doesn't contain the movie name
|
|
|
|
// don't try in the mplayer subtitle directory
|
|
|
|
if (!limit_fuzziness && fuzz >= 2)
|
|
|
|
prio |= 1;
|
2011-02-26 19:47:49 +00:00
|
|
|
|
2022-12-21 12:53:51 +00:00
|
|
|
mp_trace(log, "Potential external file: \"%s\" Priority: %d\n",
|
2015-02-02 20:23:12 +00:00
|
|
|
de->d_name, prio);
|
|
|
|
|
2011-04-19 03:53:56 +00:00
|
|
|
if (prio) {
|
2015-05-09 13:26:47 +00:00
|
|
|
char *subpath = mp_path_join_bstr(*slist, path, dename);
|
2013-09-07 18:24:02 +00:00
|
|
|
if (mp_path_exists(subpath)) {
|
2016-05-17 08:53:14 +00:00
|
|
|
MP_TARRAY_GROW(NULL, *slist, *nsub);
|
2011-04-19 03:53:56 +00:00
|
|
|
struct subfn *sub = *slist + (*nsub)++;
|
|
|
|
|
2013-09-07 18:33:47 +00:00
|
|
|
// annoying and redundant
|
|
|
|
if (strncmp(subpath, "./", 2) == 0)
|
|
|
|
subpath += 2;
|
|
|
|
|
2015-02-02 20:23:12 +00:00
|
|
|
sub->type = type;
|
2011-04-19 03:53:56 +00:00
|
|
|
sub->priority = prio;
|
|
|
|
sub->fname = subpath;
|
2017-11-27 18:45:13 +00:00
|
|
|
sub->lang = lang.len ? bstrdup0(*slist, lang) : NULL;
|
2011-04-19 03:53:56 +00:00
|
|
|
} else
|
|
|
|
talloc_free(subpath);
|
2011-02-26 19:47:49 +00:00
|
|
|
}
|
2011-04-19 03:53:56 +00:00
|
|
|
|
|
|
|
next_sub:
|
|
|
|
talloc_free(tmpmem2);
|
2011-02-26 19:47:49 +00:00
|
|
|
}
|
2011-04-19 03:53:56 +00:00
|
|
|
closedir(d);
|
2011-02-26 19:47:49 +00:00
|
|
|
|
2011-04-19 03:53:56 +00:00
|
|
|
out:
|
|
|
|
talloc_free(tmpmem);
|
2011-03-01 21:42:22 +00:00
|
|
|
}
|
2011-02-26 19:47:49 +00:00
|
|
|
|
2013-09-07 18:31:00 +00:00
|
|
|
static bool case_endswith(const char *s, const char *end)
|
|
|
|
{
|
|
|
|
size_t len = strlen(s);
|
|
|
|
size_t elen = strlen(end);
|
|
|
|
return len >= elen && strcasecmp(s + len - elen, end) == 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Drop .sub file if .idx file exists.
|
|
|
|
// Assumes slist is sorted by compare_sub_filename.
|
|
|
|
static void filter_subidx(struct subfn **slist, int *nsub)
|
|
|
|
{
|
|
|
|
const char *prev = NULL;
|
|
|
|
for (int n = 0; n < *nsub; n++) {
|
|
|
|
const char *fname = (*slist)[n].fname;
|
|
|
|
if (case_endswith(fname, ".idx")) {
|
|
|
|
prev = fname;
|
|
|
|
} else if (case_endswith(fname, ".sub")) {
|
2013-09-17 13:33:12 +00:00
|
|
|
if (prev && strncmp(prev, fname, strlen(fname) - 4) == 0)
|
2013-09-07 18:31:00 +00:00
|
|
|
(*slist)[n].priority = -1;
|
|
|
|
}
|
|
|
|
}
|
2013-09-08 05:24:30 +00:00
|
|
|
for (int n = *nsub - 1; n >= 0; n--) {
|
2013-09-07 18:31:00 +00:00
|
|
|
if ((*slist)[n].priority < 0)
|
|
|
|
MP_TARRAY_REMOVE_AT(*slist, *nsub, n);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-05-21 14:25:52 +00:00
|
|
|
static void load_paths(struct mpv_global *global, struct MPOpts *opts,
|
|
|
|
struct subfn **slist, int *nsubs, const char *fname,
|
|
|
|
char **paths, char *cfg_path, int type)
|
2015-12-25 12:17:11 +00:00
|
|
|
{
|
|
|
|
for (int i = 0; paths && paths[i]; i++) {
|
2017-05-30 15:07:59 +00:00
|
|
|
char *expanded_path = mp_get_user_path(NULL, global, paths[i]);
|
|
|
|
char *path = mp_path_join_bstr(
|
|
|
|
*slist, mp_dirname(fname),
|
|
|
|
bstr0(expanded_path ? expanded_path : paths[i]));
|
2018-05-21 14:25:52 +00:00
|
|
|
append_dir_subtitles(global, opts, slist, nsubs, bstr0(path),
|
2017-05-30 15:07:59 +00:00
|
|
|
fname, 0, type);
|
|
|
|
talloc_free(expanded_path);
|
2015-12-25 12:17:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Load subtitles in ~/.mpv/sub (or similar) limiting sub fuzziness
|
|
|
|
char *mp_subdir = mp_find_config_file(NULL, global, cfg_path);
|
2015-12-25 12:40:06 +00:00
|
|
|
if (mp_subdir) {
|
2018-05-21 14:25:52 +00:00
|
|
|
append_dir_subtitles(global, opts, slist, nsubs, bstr0(mp_subdir),
|
|
|
|
fname, 1, type);
|
2015-12-25 12:40:06 +00:00
|
|
|
}
|
2015-12-25 12:17:11 +00:00
|
|
|
talloc_free(mp_subdir);
|
|
|
|
}
|
|
|
|
|
2015-02-02 20:23:12 +00:00
|
|
|
// Return a list of subtitles and audio files found, sorted by priority.
|
2013-11-25 22:38:51 +00:00
|
|
|
// Last element is terminated with a fname==NULL entry.
|
2018-05-21 14:25:52 +00:00
|
|
|
struct subfn *find_external_files(struct mpv_global *global, const char *fname,
|
|
|
|
struct MPOpts *opts)
|
2011-03-01 21:42:22 +00:00
|
|
|
{
|
|
|
|
struct subfn *slist = talloc_array_ptrtype(NULL, slist, 1);
|
|
|
|
int n = 0;
|
2011-02-26 19:47:49 +00:00
|
|
|
|
2011-03-01 21:42:22 +00:00
|
|
|
// Load subtitles from current media directory
|
2018-05-21 14:25:52 +00:00
|
|
|
append_dir_subtitles(global, opts, &slist, &n, mp_dirname(fname), fname, 0, -1);
|
2011-02-26 19:47:49 +00:00
|
|
|
|
2015-12-25 12:17:11 +00:00
|
|
|
// Load subtitles in dirs specified by sub-paths option
|
2015-12-25 12:40:06 +00:00
|
|
|
if (opts->sub_auto >= 0) {
|
2018-05-21 14:25:52 +00:00
|
|
|
load_paths(global, opts, &slist, &n, fname, opts->sub_paths, "sub",
|
2015-12-25 12:40:06 +00:00
|
|
|
STREAM_SUB);
|
|
|
|
}
|
2011-03-03 10:31:12 +00:00
|
|
|
|
2015-12-25 12:40:06 +00:00
|
|
|
if (opts->audiofile_auto >= 0) {
|
2018-05-21 14:25:52 +00:00
|
|
|
load_paths(global, opts, &slist, &n, fname, opts->audiofile_paths,
|
|
|
|
"audio", STREAM_AUDIO);
|
2015-12-25 12:40:06 +00:00
|
|
|
}
|
2011-02-26 19:47:49 +00:00
|
|
|
|
2013-09-07 18:31:00 +00:00
|
|
|
// Sort by name for filter_subidx()
|
|
|
|
qsort(slist, n, sizeof(*slist), compare_sub_filename);
|
|
|
|
|
|
|
|
filter_subidx(&slist, &n);
|
|
|
|
|
2011-03-01 21:42:22 +00:00
|
|
|
// Sort subs by priority and append them
|
|
|
|
qsort(slist, n, sizeof(*slist), compare_sub_priority);
|
2011-02-26 19:47:49 +00:00
|
|
|
|
2013-11-25 22:38:51 +00:00
|
|
|
struct subfn z = {0};
|
|
|
|
MP_TARRAY_APPEND(NULL, slist, n, z);
|
2011-02-26 19:59:16 +00:00
|
|
|
|
2013-11-25 22:38:51 +00:00
|
|
|
return slist;
|
2011-02-26 19:59:16 +00:00
|
|
|
}
|