static bool open_source(struct MPContext *mpctx, struct bstr filename) { void *ctx = talloc_new(NULL); bool res = false; struct bstr dirname = mp_dirname(mpctx->demuxer->filename); struct bstr base_filename = bstr0(mp_basename(bstrdup0(ctx, filename))); if (!base_filename.len) { mp_msg(MSGT_CPLAYER, MSGL_WARN, "CUE: Invalid audio filename in .cue file!\n"); } else { char *fullname = mp_path_join(ctx, dirname, base_filename); if (try_open(mpctx, fullname)) { res = true; goto out; } } // Try an audio file with the same name as the .cue file (but different // extension). // Rationale: this situation happens easily if the audio file or both files // are renamed. struct bstr cuefile = bstr_strip_ext(bstr0(mp_basename(mpctx->demuxer->filename))); DIR *d = opendir(bstrdup0(ctx, dirname)); if (!d) goto out; struct dirent *de; while ((de = readdir(d))) { char *dename0 = de->d_name; struct bstr dename = bstr0(dename0); if (bstr_case_startswith(dename, cuefile)) { mp_msg(MSGT_CPLAYER, MSGL_WARN, "CUE: No useful audio filename " "in .cue file found, trying with '%s' instead!\n", dename0); if (try_open(mpctx, mp_path_join(ctx, dirname, dename))) { res = true; break; } } } closedir(d); out: talloc_free(ctx); if (!res) mp_msg(MSGT_CPLAYER, MSGL_ERR, "CUE: Could not open audio file!\n"); return res; }
int bstr_sscanf(struct bstr str, const char *format, ...) { char *ptr = bstrdup0(NULL, str); va_list va; va_start(va, format); int ret = vsscanf(ptr, format, va); va_end(va); talloc_free(ptr); return ret; }
static int parse_include(struct m_config *config, struct bstr param, bool set) { if (param.len == 0) return M_OPT_MISSING_PARAM; if (!set) return 1; char *filename = bstrdup0(NULL, param); config->includefunc(config, filename); talloc_free(filename); return 1; }
static struct bstr strip_ext(struct bstr str) { int dotpos = bstrrchr(str, '.'); if (dotpos < 0) return str; return (struct bstr){str.start, dotpos}; } static struct bstr get_ext(struct bstr s) { int dotpos = bstrrchr(s, '.'); if (dotpos < 0) return (struct bstr){NULL, 0}; return bstr_splice(s, dotpos + 1, s.len); } bool mp_might_be_subtitle_file(const char *filename) { return test_ext(get_ext(bstr0(filename))) == STREAM_SUB; } 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); } static int compare_sub_priority(const void *a, const void *b) { const struct subfn *s1 = a; const struct subfn *s2 = b; if (s1->priority > s2->priority) return -1; if (s1->priority < s2->priority) return 1; return strcoll(s1->fname, s2->fname); } static struct bstr guess_lang_from_filename(struct bstr name) { if (name.len < 2) return (struct bstr){NULL, 0}; int n = 0; int i = name.len - 1; if (name.start[i] == ')' || name.start[i] == ']') i--; while (i >= 0 && mp_isalpha(name.start[i])) { n++; if (n > 3) return (struct bstr){NULL, 0}; i--; } if (n < 2) return (struct bstr){NULL, 0}; return (struct bstr){name.start + i + 1, n}; } static void append_dir_subtitles(struct mpv_global *global, struct subfn **slist, int *nsub, struct bstr path, const char *fname, int limit_fuzziness) { void *tmpmem = talloc_new(NULL); struct MPOpts *opts = global->opts; struct mp_log *log = mp_log_new(tmpmem, global->log, "find_files"); if (mp_is_url(bstr0(fname))) goto out; struct bstr f_fname = bstr0(mp_basename(fname)); struct bstr f_fname_noext = bstrdup(tmpmem, strip_ext(f_fname)); bstr_lower(f_fname_noext); struct bstr f_fname_trim = bstr_strip(f_fname_noext); // 0 = nothing // 1 = any subtitle file // 2 = any sub file containing movie name // 3 = sub file containing movie name and the lang extension char *path0 = bstrdup0(tmpmem, path); DIR *d = opendir(path0); if (!d) goto out; mp_verbose(log, "Loading external files in %.*s\n", BSTR_P(path)); struct dirent *de; while ((de = readdir(d))) { struct bstr dename = bstr0(de->d_name); void *tmpmem2 = talloc_new(tmpmem); // retrieve various parts of the filename struct bstr tmp_fname_noext = bstrdup(tmpmem2, strip_ext(dename)); bstr_lower(tmp_fname_noext); struct bstr tmp_fname_ext = get_ext(dename); struct bstr tmp_fname_trim = bstr_strip(tmp_fname_noext); // check what it is (most likely) int type = test_ext(tmp_fname_ext); char **langs = NULL; int fuzz = -1; switch (type) { case STREAM_SUB: langs = opts->sub_lang; fuzz = opts->sub_auto; break; case STREAM_AUDIO: langs = opts->audio_lang; fuzz = opts->audiofile_auto; break; } if (fuzz < 0) goto next_sub; // we have a (likely) subtitle file int prio = 0; char *found_lang = NULL; if (langs) { if (bstr_startswith(tmp_fname_trim, f_fname_trim)) { struct bstr lang = guess_lang_from_filename(tmp_fname_trim); if (lang.len) { for (int n = 0; langs[n]; n++) { if (bstr_startswith0(lang, langs[n])) { prio = 4; // matches the movie name + lang extension found_lang = langs[n]; break; } } } } } if (!prio && bstrcmp(tmp_fname_trim, f_fname_trim) == 0) prio = 3; // matches the movie name if (!prio && bstr_find(tmp_fname_trim, f_fname_trim) >= 0 && fuzz >= 1) prio = 2; // contains the movie name if (!prio) { // doesn't contain the movie name // don't try in the mplayer subtitle directory if (!limit_fuzziness && fuzz >= 2) { prio = 1; } } mp_dbg(log, "Potential external file: \"%s\" Priority: %d\n", de->d_name, prio); if (prio) { prio += prio; char *subpath = mp_path_join(*slist, path, dename); if (mp_path_exists(subpath)) { MP_GROW_ARRAY(*slist, *nsub); struct subfn *sub = *slist + (*nsub)++; // annoying and redundant if (strncmp(subpath, "./", 2) == 0) subpath += 2; sub->type = type; sub->priority = prio; sub->fname = subpath; sub->lang = found_lang; } else talloc_free(subpath); } next_sub: talloc_free(tmpmem2); } closedir(d); out: talloc_free(tmpmem); } 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")) { if (prev && strncmp(prev, fname, strlen(fname) - 4) == 0) (*slist)[n].priority = -1; } } for (int n = *nsub - 1; n >= 0; n--) { if ((*slist)[n].priority < 0) MP_TARRAY_REMOVE_AT(*slist, *nsub, n); } } // Return a list of subtitles and audio files found, sorted by priority. // Last element is terminated with a fname==NULL entry. struct subfn *find_external_files(struct mpv_global *global, const char *fname) { struct MPOpts *opts = global->opts; struct subfn *slist = talloc_array_ptrtype(NULL, slist, 1); int n = 0; // Load subtitles from current media directory append_dir_subtitles(global, &slist, &n, mp_dirname(fname), fname, 0); if (opts->sub_auto >= 0) { // Load subtitles in dirs specified by sub-paths option if (opts->sub_paths) { for (int i = 0; opts->sub_paths[i]; i++) { char *path = mp_path_join(slist, mp_dirname(fname), bstr0(opts->sub_paths[i])); append_dir_subtitles(global, &slist, &n, bstr0(path), fname, 0); } } // Load subtitles in ~/.mpv/sub limiting sub fuzziness char *mp_subdir = mp_find_config_file(NULL, global, "sub/"); if (mp_subdir) append_dir_subtitles(global, &slist, &n, bstr0(mp_subdir), fname, 1); talloc_free(mp_subdir); } // Sort by name for filter_subidx() qsort(slist, n, sizeof(*slist), compare_sub_filename); filter_subidx(&slist, &n); // Sort subs by priority and append them qsort(slist, n, sizeof(*slist), compare_sub_priority); struct subfn z = {0}; MP_TARRAY_APPEND(NULL, slist, n, z); return slist; }
// returns M_OPT_... error code int m_config_parse_mp_command_line(m_config_t *config, struct playlist *files, struct mpv_global *global, char **argv) { int ret = M_OPT_UNKNOWN; int mode = 0; struct playlist_entry *local_start = NULL; int local_params_count = 0; struct playlist_param *local_params = 0; assert(config != NULL); mode = GLOBAL; struct parse_state p = {config, argv}; while (split_opt(&p)) { if (p.is_opt) { int flags = M_SETOPT_FROM_CMDLINE; if (mode == LOCAL) flags |= M_SETOPT_BACKUP | M_SETOPT_CHECK_ONLY; int r = m_config_set_option_ext(config, p.arg, p.param, flags); if (r == M_OPT_EXIT) { ret = r; goto err_out; } else if (r < 0) { MP_FATAL(config, "Setting command line option '--%.*s=%.*s' failed.\n", BSTR_P(p.arg), BSTR_P(p.param)); goto err_out; } // Handle some special arguments outside option parser. if (!bstrcmp0(p.arg, "{")) { if (mode != GLOBAL) { MP_ERR(config, "'--{' can not be nested.\n"); goto err_out; } mode = LOCAL; assert(!local_start); local_start = files->last; continue; } if (!bstrcmp0(p.arg, "}")) { if (mode != LOCAL) { MP_ERR(config, "Too many closing '--}'.\n"); goto err_out; } if (local_params_count) { // The files added between '{' and '}' are the entries from // the entry _after_ local_start, until the end of the list. // If local_start is NULL, the list was empty on '{', and we // want all files in the list. struct playlist_entry *cur = local_start ? local_start->next : files->first; if (!cur) MP_WARN(config, "Ignored options!\n"); while (cur) { playlist_entry_add_params(cur, local_params, local_params_count); cur = cur->next; } } local_params_count = 0; mode = GLOBAL; m_config_restore_backups(config); local_start = NULL; continue; } if (bstrcmp0(p.arg, "playlist") == 0) { // append the playlist to the local args char *param0 = bstrdup0(NULL, p.param); struct playlist *pl = playlist_parse_file(param0, global); talloc_free(param0); if (!pl) { MP_FATAL(config, "Error reading playlist '%.*s'\n", BSTR_P(p.param)); goto err_out; } playlist_transfer_entries(files, pl); talloc_free(pl); continue; } if (mode == LOCAL) { MP_TARRAY_APPEND(NULL, local_params, local_params_count, (struct playlist_param) {p.arg, p.param}); }
static struct bstr strip_ext(struct bstr str) { int dotpos = bstrrchr(str, '.'); if (dotpos < 0) return str; return (struct bstr){str.start, dotpos}; } static struct bstr get_ext(struct bstr s) { int dotpos = bstrrchr(s, '.'); if (dotpos < 0) return (struct bstr){NULL, 0}; return bstr_splice(s, dotpos + 1, s.len); } struct subfn { int priority; char *fname; }; static int compare_sub_priority(const void *a, const void *b) { const struct subfn *s1 = a; const struct subfn *s2 = b; if (s1->priority > s2->priority) return -1; if (s1->priority < s2->priority) return 1; return strcoll(s1->fname, s2->fname); } static struct bstr guess_lang_from_filename(struct bstr name) { if (name.len < 2) return (struct bstr){NULL, 0}; int n = 0; int i = name.len - 1; if (name.start[i] == ')' || name.start[i] == ']') i--; while (i >= 0 && isalpha(name.start[i])) { n++; if (n > 3) return (struct bstr){NULL, 0}; i--; } if (n < 2) return (struct bstr){NULL, 0}; return (struct bstr){name.start + i + 1, n}; } struct sub_list { struct subfn subs[MAX_SUBTITLE_FILES]; int sid; void *ctx; }; /** * @brief Append all the subtitles in the given path matching fname * @param opts MPlayer options * @param slist pointer to the subtitles list tallocated * @param nsub pointer to the number of subtitles * @param path Look for subtitles in this directory * @param fname Subtitle filename (pattern) * @param limit_fuzziness Ignore flag when sub_fuziness == 2 */ static void append_dir_subtitles(struct MPOpts *opts, struct subfn **slist, int *nsub, struct bstr path, const char *fname, int limit_fuzziness) { char *sub_exts[] = {"utf", "utf8", "utf-8", "sub", "srt", "smi", "rt", "txt", "ssa", "aqt", "jss", "js", "ass", NULL}; void *tmpmem = talloc_new(NULL); FILE *f; assert(strlen(fname) < 1e6); struct bstr f_fname = bstr0(mp_basename(fname)); struct bstr f_fname_noext = bstrdup(tmpmem, strip_ext(f_fname)); bstr_lower(f_fname_noext); struct bstr f_fname_trim = bstr_strip(f_fname_noext); // 0 = nothing // 1 = any subtitle file // 2 = any sub file containing movie name // 3 = sub file containing movie name and the lang extension char *path0 = bstrdup0(tmpmem, path); DIR *d = opendir(path0); if (!d) goto out; mp_msg(MSGT_SUBREADER, MSGL_V, "Load subtitles in %.*s\n", BSTR_P(path)); struct dirent *de; while ((de = readdir(d))) { struct bstr dename = bstr0(de->d_name); void *tmpmem2 = talloc_new(tmpmem); // retrieve various parts of the filename struct bstr tmp_fname_noext = bstrdup(tmpmem2, strip_ext(dename)); bstr_lower(tmp_fname_noext); struct bstr tmp_fname_ext = get_ext(dename); struct bstr tmp_fname_trim = bstr_strip(tmp_fname_noext); // If it's a .sub, check if there is a .idx with the same name. If // there is one, it's certainly a vobsub so we skip it. if (bstrcasecmp(tmp_fname_ext, bstr0("sub")) == 0) { char *idxname = talloc_asprintf(tmpmem2, "%.*s.idx", (int)tmp_fname_noext.len, de->d_name); char *idx = mp_path_join(tmpmem2, path, bstr0(idxname)); f = fopen(idx, "rt"); if (f) { fclose(f); goto next_sub; } } // does it end with a subtitle extension? #ifdef CONFIG_ICONV #ifdef CONFIG_ENCA int i = (sub_cp && strncasecmp(sub_cp, "enca", 4) != 0) ? 3 : 0; #else int i = sub_cp ? 3 : 0; #endif #else int i = 0; #endif while (1) { if (!sub_exts[i]) goto next_sub; if (bstrcasecmp(bstr0(sub_exts[i]), tmp_fname_ext) == 0) break; i++; } // we have a (likely) subtitle file int prio = 0; if (opts->sub_lang) { if (bstr_startswith(tmp_fname_trim, f_fname_trim)) { struct bstr lang = guess_lang_from_filename(tmp_fname_trim); if (lang.len) { for (int n = 0; opts->sub_lang[n]; n++) { if (bstr_startswith(lang, bstr0(opts->sub_lang[n]))) { prio = 4; // matches the movie name + lang extension break; } } } } } if (!prio && bstrcmp(tmp_fname_trim, f_fname_trim) == 0) prio = 3; // matches the movie name if (!prio && bstr_find(tmp_fname_trim, f_fname_trim) >= 0 && sub_match_fuzziness >= 1) prio = 2; // contains the movie name if (!prio) { // doesn't contain the movie name // don't try in the mplayer subtitle directory if (!limit_fuzziness && sub_match_fuzziness >= 2) { prio = 1; } } mp_msg(MSGT_SUBREADER, MSGL_DBG2, "Potential sub file: " "\"%s\" Priority: %d\n", de->d_name, prio); if (prio) { prio += prio; #ifdef CONFIG_ICONV if (i < 3) // prefer UTF-8 coded prio++; #endif char *subpath = mp_path_join(*slist, path, dename); if ((f = fopen(subpath, "rt"))) { MP_GROW_ARRAY(*slist, *nsub); struct subfn *sub = *slist + (*nsub)++; fclose(f); sub->priority = prio; sub->fname = subpath; } else talloc_free(subpath); } next_sub: talloc_free(tmpmem2); } closedir(d); out: talloc_free(tmpmem); } char **find_text_subtitles(struct MPOpts *opts, const char *fname) { char **subnames = NULL; struct subfn *slist = talloc_array_ptrtype(NULL, slist, 1); int n = 0; // Load subtitles from current media directory append_dir_subtitles(opts, &slist, &n, mp_dirname(fname), fname, 0); // Load subtitles in dirs specified by sub-paths option if (opts->sub_paths) { for (int i = 0; opts->sub_paths[i]; i++) { char *path = mp_path_join(slist, mp_dirname(fname), bstr0(opts->sub_paths[i])); append_dir_subtitles(opts, &slist, &n, bstr0(path), fname, 0); } } // Load subtitles in ~/.mplayer/sub limiting sub fuzziness char *mp_subdir = get_path("sub/"); if (mp_subdir) append_dir_subtitles(opts, &slist, &n, bstr0(mp_subdir), fname, 1); free(mp_subdir); // Sort subs by priority and append them qsort(slist, n, sizeof(*slist), compare_sub_priority); subnames = talloc_array_ptrtype(NULL, subnames, n); for (int i = 0; i < n; i++) subnames[i] = talloc_strdup(subnames, slist[i].fname); talloc_free(slist); return subnames; } char **find_vob_subtitles(struct MPOpts *opts, const char *fname) { char **vobs = talloc_array_ptrtype(NULL, vobs, 1); int n = 0; // Potential vobsub in the media directory struct bstr bname = bstr0(mp_basename(fname)); int pdot = bstrrchr(bname, '.'); if (pdot >= 0) bname.len = pdot; vobs[n++] = mp_path_join(vobs, mp_dirname(fname), bname); // Potential vobsubs in directories specified by sub-paths option if (opts->sub_paths) { for (int i = 0; opts->sub_paths[i]; i++) { char *path = mp_path_join(NULL, mp_dirname(fname), bstr0(opts->sub_paths[i])); MP_GROW_ARRAY(vobs, n); vobs[n++] = mp_path_join(vobs, bstr0(path), bname); talloc_free(path); } } // Potential vobsub in ~/.mplayer/sub char *mp_subdir = get_path("sub/"); if (mp_subdir) { MP_GROW_ARRAY(vobs, n); vobs[n++] = mp_path_join(vobs, bstr0(mp_subdir), bname); } free(mp_subdir); MP_RESIZE_ARRAY(NULL, vobs, n); return vobs; }
bool m_config_parse_mp_command_line(m_config_t *config, struct playlist *files, int argc, char **argv) { int mode = 0; struct playlist_entry *local_start = NULL; bool shuffle = false; int local_params_count = 0; struct playlist_param *local_params = 0; assert(config != NULL); assert(!config->file_local_mode); config->mode = M_COMMAND_LINE; mode = GLOBAL; #ifdef CONFIG_MACOSX_FINDER if (macosx_finder_args(config, files, argc, argv)) return true; #endif struct parse_state p = {config, argc, argv}; while (split_opt(&p)) { if (p.mp_opt) { int r; if (mode == GLOBAL && !(p.mp_opt->flags & M_OPT_PRE_PARSE)) { r = m_config_set_option(config, p.arg, p.param); } else { r = m_config_check_option(config, p.arg, p.param); } if (r <= M_OPT_EXIT) goto err_out; if (r < 0) { char *msg = m_option_strerror(r); if (!msg) goto print_err; mp_tmsg(MSGT_CFGPARSER, MSGL_FATAL, "Error parsing commandline option %.*s: %s\n", BSTR_P(p.arg), msg); goto err_out; } // Handle some special arguments outside option parser. if (!bstrcmp0(p.arg, "{")) { if (mode != GLOBAL) { mp_msg(MSGT_CFGPARSER, MSGL_ERR, "'--{' can not be nested.\n"); goto err_out; } mode = LOCAL; // Needed for option checking. m_config_enter_file_local(config); assert(!local_start); local_start = files->last; continue; } if (!bstrcmp0(p.arg, "}")) { if (mode != LOCAL) { mp_msg(MSGT_CFGPARSER, MSGL_ERR, "Too many closing '--}'.\n"); goto err_out; } if (local_params_count) { // The files added between '{' and '}' are the entries from // the entry _after_ local_start, until the end of the list. // If local_start is NULL, the list was empty on '{', and we // want all files in the list. struct playlist_entry *cur = local_start ? local_start->next : files->first; if (!cur) mp_msg(MSGT_CFGPARSER, MSGL_WARN, "Ignored options!\n"); while (cur) { playlist_entry_add_params(cur, local_params, local_params_count); cur = cur->next; } } local_params_count = 0; mode = GLOBAL; m_config_leave_file_local(config); local_start = NULL; shuffle = false; continue; } if (bstrcmp0(p.arg, "shuffle") == 0) { shuffle = parse_flag(p.arg, p.param); continue; } if (bstrcmp0(p.arg, "playlist") == 0) { // append the playlist to the local args char *param0 = bstrdup0(NULL, p.param); struct playlist *pl = playlist_parse_file(param0); talloc_free(param0); if (!pl) goto print_err; playlist_transfer_entries(files, pl); talloc_free(pl); continue; } if (mode == LOCAL) { MP_TARRAY_APPEND(NULL, local_params, local_params_count, (struct playlist_param) {p.arg, p.param}); }
static struct bstr strip_ext(struct bstr str) { int dotpos = bstrrchr(str, '.'); if (dotpos < 0) return str; return (struct bstr){str.start, dotpos}; } static struct bstr get_ext(struct bstr s) { int dotpos = bstrrchr(s, '.'); if (dotpos < 0) return (struct bstr){NULL, 0}; return bstr_splice(s, dotpos + 1, s.len); } 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); } static int compare_sub_priority(const void *a, const void *b) { const struct subfn *s1 = a; const struct subfn *s2 = b; if (s1->priority > s2->priority) return -1; if (s1->priority < s2->priority) return 1; return strcoll(s1->fname, s2->fname); } static struct bstr guess_lang_from_filename(struct bstr name) { if (name.len < 2) return (struct bstr){NULL, 0}; int n = 0; int i = name.len - 1; if (name.start[i] == ')' || name.start[i] == ']') i--; while (i >= 0 && isalpha(name.start[i])) { n++; if (n > 3) return (struct bstr){NULL, 0}; i--; } if (n < 2) return (struct bstr){NULL, 0}; return (struct bstr){name.start + i + 1, n}; } /** * @brief Append all the subtitles in the given path matching fname * @param opts MPlayer options * @param slist pointer to the subtitles list tallocated * @param nsub pointer to the number of subtitles * @param path Look for subtitles in this directory * @param fname Subtitle filename (pattern) * @param limit_fuzziness Ignore flag when sub_fuziness == 2 */ static void append_dir_subtitles(struct MPOpts *opts, struct subfn **slist, int *nsub, struct bstr path, const char *fname, int limit_fuzziness) { void *tmpmem = talloc_new(NULL); if (mp_is_url(bstr0(fname))) goto out; struct bstr f_fname = bstr0(mp_basename(fname)); struct bstr f_fname_noext = bstrdup(tmpmem, strip_ext(f_fname)); bstr_lower(f_fname_noext); struct bstr f_fname_trim = bstr_strip(f_fname_noext); // 0 = nothing // 1 = any subtitle file // 2 = any sub file containing movie name // 3 = sub file containing movie name and the lang extension char *path0 = bstrdup0(tmpmem, path); DIR *d = opendir(path0); if (!d) goto out; mp_msg(MSGT_SUBREADER, MSGL_V, "Load subtitles in %.*s\n", BSTR_P(path)); struct dirent *de; while ((de = readdir(d))) { struct bstr dename = bstr0(de->d_name); void *tmpmem2 = talloc_new(tmpmem); // retrieve various parts of the filename struct bstr tmp_fname_noext = bstrdup(tmpmem2, strip_ext(dename)); bstr_lower(tmp_fname_noext); struct bstr tmp_fname_ext = get_ext(dename); struct bstr tmp_fname_trim = bstr_strip(tmp_fname_noext); // does it end with a subtitle extension? if (!is_sub_ext(tmp_fname_ext)) goto next_sub; // we have a (likely) subtitle file int prio = 0; char *found_lang = NULL; if (opts->sub_lang) { if (bstr_startswith(tmp_fname_trim, f_fname_trim)) { struct bstr lang = guess_lang_from_filename(tmp_fname_trim); if (lang.len) { for (int n = 0; opts->sub_lang[n]; n++) { if (bstr_startswith0(lang, opts->sub_lang[n])) { prio = 4; // matches the movie name + lang extension found_lang = opts->sub_lang[n]; break; } } } } } if (!prio && bstrcmp(tmp_fname_trim, f_fname_trim) == 0) prio = 3; // matches the movie name if (!prio && bstr_find(tmp_fname_trim, f_fname_trim) >= 0 && opts->sub_match_fuzziness >= 1) prio = 2; // contains the movie name if (!prio) { // doesn't contain the movie name // don't try in the mplayer subtitle directory if (!limit_fuzziness && opts->sub_match_fuzziness >= 2) { prio = 1; } } mp_msg(MSGT_SUBREADER, MSGL_DBG2, "Potential sub file: " "\"%s\" Priority: %d\n", de->d_name, prio); if (prio) { prio += prio; char *subpath = mp_path_join(*slist, path, dename); if (mp_path_exists(subpath)) { MP_GROW_ARRAY(*slist, *nsub); struct subfn *sub = *slist + (*nsub)++; // annoying and redundant if (strncmp(subpath, "./", 2) == 0) subpath += 2; sub->priority = prio; sub->fname = subpath; sub->lang = found_lang; } else talloc_free(subpath); } next_sub: talloc_free(tmpmem2); } closedir(d); out: talloc_free(tmpmem); } 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")) { if (prev && strncmp(prev, fname, strlen(fname) - 4) == 0) (*slist)[n].priority = -1; } } for (int n = *nsub - 1; n >= 0; n--) { if ((*slist)[n].priority < 0) MP_TARRAY_REMOVE_AT(*slist, *nsub, n); } } // Return a list of subtitles found, sorted by priority. // Last element is terminated with a fname==NULL entry. struct subfn *find_text_subtitles(struct MPOpts *opts, const char *fname) { struct subfn *slist = talloc_array_ptrtype(NULL, slist, 1); int n = 0; // Load subtitles from current media directory append_dir_subtitles(opts, &slist, &n, mp_dirname(fname), fname, 0); // Load subtitles in dirs specified by sub-paths option if (opts->sub_paths) { for (int i = 0; opts->sub_paths[i]; i++) { char *path = mp_path_join(slist, mp_dirname(fname), bstr0(opts->sub_paths[i])); append_dir_subtitles(opts, &slist, &n, bstr0(path), fname, 0); } } // Load subtitles in ~/.mpv/sub limiting sub fuzziness char *mp_subdir = mp_find_user_config_file("sub/"); if (mp_subdir) append_dir_subtitles(opts, &slist, &n, bstr0(mp_subdir), fname, 1); talloc_free(mp_subdir); // Sort by name for filter_subidx() qsort(slist, n, sizeof(*slist), compare_sub_filename); filter_subidx(&slist, &n); // Sort subs by priority and append them qsort(slist, n, sizeof(*slist), compare_sub_priority); struct subfn z = {0}; MP_TARRAY_APPEND(NULL, slist, n, z); return slist; }
static mf_t *open_mf_pattern(void *talloc_ctx, struct mp_log *log, char *filename) { int error_count = 0; int count = 0; mf_t *mf = talloc_zero(talloc_ctx, mf_t); mf->log = log; if (filename[0] == '@') { FILE *lst_f = fopen(filename + 1, "r"); if (lst_f) { char *fname = talloc_size(mf, 512); while (fgets(fname, 512, lst_f)) { /* remove spaces from end of fname */ char *t = fname + strlen(fname) - 1; while (t > fname && mp_isspace(*t)) *(t--) = 0; if (!mp_path_exists(fname)) { mp_verbose(log, "file not found: '%s'\n", fname); } else { mf_add(mf, fname); } } fclose(lst_f); mp_info(log, "number of files: %d\n", mf->nr_of_files); goto exit_mf; } mp_info(log, "%s is not indirect filelist\n", filename + 1); } if (strchr(filename, ',')) { mp_info(log, "filelist: %s\n", filename); bstr bfilename = bstr0(filename); while (bfilename.len) { bstr bfname; bstr_split_tok(bfilename, ",", &bfname, &bfilename); char *fname2 = bstrdup0(mf, bfname); if (!mp_path_exists(fname2)) mp_verbose(log, "file not found: '%s'\n", fname2); else { mf_add(mf, fname2); } talloc_free(fname2); } mp_info(log, "number of files: %d\n", mf->nr_of_files); goto exit_mf; } char *fname = talloc_size(mf, strlen(filename) + 32); if (!strchr(filename, '%')) { strcpy(fname, filename); if (!strchr(filename, '*')) strcat(fname, "*"); mp_info(log, "search expr: %s\n", fname); glob_t gg; if (glob(fname, 0, NULL, &gg)) { talloc_free(mf); return NULL; } for (int i = 0; i < gg.gl_pathc; i++) { if (mp_path_isdir(gg.gl_pathv[i])) continue; mf_add(mf, gg.gl_pathv[i]); } mp_info(log, "number of files: %d\n", mf->nr_of_files); globfree(&gg); goto exit_mf; } mp_info(log, "search expr: %s\n", filename); while (error_count < 5) { sprintf(fname, filename, count++); if (!mp_path_exists(fname)) { error_count++; mp_verbose(log, "file not found: '%s'\n", fname); } else { mf_add(mf, fname); } } mp_info(log, "number of files: %d\n", mf->nr_of_files); exit_mf: return mf; }