|
|
|
@ -27,11 +27,13 @@ |
|
|
|
#include <assert.h> |
|
|
|
#include <time.h> |
|
|
|
#include <limits.h> |
|
|
|
#include <ctype.h> |
|
|
|
|
|
|
|
#include <vlc_common.h> |
|
|
|
#include <vlc_url.h> |
|
|
|
#include <vlc_interface.h> |
|
|
|
#include <vlc_charset.h> |
|
|
|
#include <vlc_strings.h> |
|
|
|
|
|
|
|
#include "item.h" |
|
|
|
#include "info.h" |
|
|
|
@ -1319,3 +1321,363 @@ void input_item_UpdateTracksInfo(input_item_t *item, const es_format_t *fmt) |
|
|
|
TAB_APPEND(item->i_es, item->es, fmt_copy); |
|
|
|
vlc_mutex_unlock( &item->lock ); |
|
|
|
} |
|
|
|
|
|
|
|
static int compar_type(input_item_t *p1, input_item_t *p2) |
|
|
|
{ |
|
|
|
if (p1->i_type != p2->i_type) |
|
|
|
{ |
|
|
|
if (p1->i_type == ITEM_TYPE_DIRECTORY) |
|
|
|
return -1; |
|
|
|
if (p2->i_type == ITEM_TYPE_DIRECTORY) |
|
|
|
return 1; |
|
|
|
} |
|
|
|
return 0; |
|
|
|
} |
|
|
|
|
|
|
|
static int compar_filename(const void *a, const void *b) |
|
|
|
{ |
|
|
|
input_item_node_t *const *na = a, *const *nb = b; |
|
|
|
input_item_t *ia = (*na)->p_item, *ib = (*nb)->p_item; |
|
|
|
|
|
|
|
int i_ret = compar_type(ia, ib); |
|
|
|
if (i_ret != 0) |
|
|
|
return i_ret; |
|
|
|
|
|
|
|
return vlc_filenamecmp(ia->psz_name, ib->psz_name); |
|
|
|
} |
|
|
|
|
|
|
|
static void fsdir_sort(input_item_node_t *p_node) |
|
|
|
{ |
|
|
|
if (p_node->i_children <= 0) |
|
|
|
return; |
|
|
|
|
|
|
|
/* Lock first all children. This avoids to lock/unlock them from each
|
|
|
|
* compar callback call */ |
|
|
|
for (int i = 0; i < p_node->i_children; i++) |
|
|
|
vlc_mutex_lock(&p_node->pp_children[i]->p_item->lock); |
|
|
|
|
|
|
|
/* Sort current node */ |
|
|
|
qsort(p_node->pp_children, p_node->i_children, |
|
|
|
sizeof(input_item_node_t *), compar_filename); |
|
|
|
|
|
|
|
/* Unlock all children */ |
|
|
|
for (int i = 0; i < p_node->i_children; i++) |
|
|
|
vlc_mutex_unlock(&p_node->pp_children[i]->p_item->lock); |
|
|
|
|
|
|
|
/* Sort all children */ |
|
|
|
for (int i = 0; i < p_node->i_children; i++) |
|
|
|
fsdir_sort(p_node->pp_children[i]); |
|
|
|
} |
|
|
|
|
|
|
|
/**
|
|
|
|
* Does the provided file name has one of the extension provided ? |
|
|
|
*/ |
|
|
|
static bool fsdir_has_ext(const char *psz_filename, |
|
|
|
const char *psz_ignored_exts) |
|
|
|
{ |
|
|
|
if (psz_ignored_exts == NULL) |
|
|
|
return false; |
|
|
|
|
|
|
|
const char *ext = strrchr(psz_filename, '.'); |
|
|
|
if (ext == NULL) |
|
|
|
return false; |
|
|
|
|
|
|
|
size_t extlen = strlen(++ext); |
|
|
|
|
|
|
|
for (const char *type = psz_ignored_exts, *end; type[0]; type = end + 1) |
|
|
|
{ |
|
|
|
end = strchr(type, ','); |
|
|
|
if (end == NULL) |
|
|
|
end = type + strlen(type); |
|
|
|
|
|
|
|
if (type + extlen == end && !strncasecmp(ext, type, extlen)) |
|
|
|
return true; |
|
|
|
|
|
|
|
if (*end == '\0') |
|
|
|
break; |
|
|
|
} |
|
|
|
|
|
|
|
return false; |
|
|
|
} |
|
|
|
|
|
|
|
static bool fsdir_is_ignored(struct access_fsdir *p_fsdir, |
|
|
|
const char *psz_filename) |
|
|
|
{ |
|
|
|
return (psz_filename[0] == '\0' |
|
|
|
|| strcmp(psz_filename, ".") == 0 |
|
|
|
|| strcmp(psz_filename, "..") == 0 |
|
|
|
|| (!p_fsdir->b_show_hiddenfiles && psz_filename[0] == '.') |
|
|
|
|| fsdir_has_ext(psz_filename, p_fsdir->psz_ignored_exts)); |
|
|
|
} |
|
|
|
|
|
|
|
struct fsdir_slave |
|
|
|
{ |
|
|
|
input_item_slave_t *p_slave; |
|
|
|
char *psz_filename; |
|
|
|
input_item_node_t *p_node; |
|
|
|
}; |
|
|
|
|
|
|
|
static char *fsdir_name_from_filename(const char *psz_filename) |
|
|
|
{ |
|
|
|
/* remove leading white spaces */ |
|
|
|
while (*psz_filename != '\0' && *psz_filename == ' ') |
|
|
|
psz_filename++; |
|
|
|
|
|
|
|
char *psz_name = strdup(psz_filename); |
|
|
|
if (!psz_name) |
|
|
|
return NULL; |
|
|
|
|
|
|
|
/* remove extension */ |
|
|
|
char *psz_ptr = strrchr(psz_name, '.'); |
|
|
|
if (psz_ptr && psz_ptr != psz_name) |
|
|
|
*psz_ptr = '\0'; |
|
|
|
|
|
|
|
/* remove trailing white spaces */ |
|
|
|
int i = strlen(psz_name) - 1; |
|
|
|
while (psz_name[i] == ' ' && i >= 0) |
|
|
|
psz_name[i--] = '\0'; |
|
|
|
|
|
|
|
/* convert to lower case */ |
|
|
|
psz_ptr = psz_name; |
|
|
|
while (*psz_ptr != '\0') |
|
|
|
{ |
|
|
|
*psz_ptr = tolower(*psz_ptr); |
|
|
|
psz_ptr++; |
|
|
|
} |
|
|
|
|
|
|
|
return psz_name; |
|
|
|
} |
|
|
|
|
|
|
|
static uint8_t fsdir_get_slave_priority(input_item_t *p_item, |
|
|
|
input_item_slave_t *p_slave, |
|
|
|
const char *psz_slave_filename) |
|
|
|
{ |
|
|
|
uint8_t i_priority = SLAVE_PRIORITY_MATCH_NONE; |
|
|
|
char *psz_item_name = fsdir_name_from_filename(p_item->psz_name); |
|
|
|
char *psz_slave_name = fsdir_name_from_filename(psz_slave_filename); |
|
|
|
|
|
|
|
if (!psz_item_name || !psz_slave_name) |
|
|
|
goto done; |
|
|
|
|
|
|
|
/* check if the names match exactly */ |
|
|
|
if (!strcmp(psz_item_name, psz_slave_name)) |
|
|
|
{ |
|
|
|
i_priority = SLAVE_PRIORITY_MATCH_ALL; |
|
|
|
goto done; |
|
|
|
} |
|
|
|
|
|
|
|
/* "cdg" slaves have to be a full match */ |
|
|
|
if (p_slave->i_type == SLAVE_TYPE_SPU) |
|
|
|
{ |
|
|
|
char *psz_ext = strrchr(psz_slave_name, '.'); |
|
|
|
if (psz_ext != NULL && strcasecmp(++psz_ext, "cdg") == 0) |
|
|
|
goto done; |
|
|
|
} |
|
|
|
|
|
|
|
/* check if the item name is a substring of the slave name */ |
|
|
|
const char *psz_sub = strstr(psz_slave_name, psz_item_name); |
|
|
|
|
|
|
|
if (psz_sub) |
|
|
|
{ |
|
|
|
/* check if the item name was found at the end of the slave name */ |
|
|
|
if (strlen(psz_sub + strlen(psz_item_name)) == 0) |
|
|
|
{ |
|
|
|
i_priority = SLAVE_PRIORITY_MATCH_RIGHT; |
|
|
|
goto done; |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
i_priority = SLAVE_PRIORITY_MATCH_LEFT; |
|
|
|
goto done; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
done: |
|
|
|
free(psz_item_name); |
|
|
|
free(psz_slave_name); |
|
|
|
return i_priority; |
|
|
|
} |
|
|
|
|
|
|
|
static int fsdir_should_match_idx(struct access_fsdir *p_fsdir, |
|
|
|
struct fsdir_slave *p_fsdir_sub) |
|
|
|
{ |
|
|
|
char *psz_ext = strrchr(p_fsdir_sub->psz_filename, '.'); |
|
|
|
if (!psz_ext) |
|
|
|
return false; |
|
|
|
psz_ext++; |
|
|
|
|
|
|
|
if (strcasecmp(psz_ext, "sub") != 0) |
|
|
|
return false; |
|
|
|
|
|
|
|
for (unsigned int i = 0; i < p_fsdir->i_slaves; i++) |
|
|
|
{ |
|
|
|
struct fsdir_slave *p_fsdir_slave = p_fsdir->pp_slaves[i]; |
|
|
|
|
|
|
|
if (p_fsdir_slave == NULL || p_fsdir_slave == p_fsdir_sub) |
|
|
|
continue; |
|
|
|
|
|
|
|
/* check that priorities match */ |
|
|
|
if (p_fsdir_slave->p_slave->i_priority != |
|
|
|
p_fsdir_sub->p_slave->i_priority) |
|
|
|
continue; |
|
|
|
|
|
|
|
/* check that the filenames without extension match */ |
|
|
|
if (strncasecmp(p_fsdir_sub->psz_filename, p_fsdir_slave->psz_filename, |
|
|
|
strlen(p_fsdir_sub->psz_filename) - 3 ) != 0) |
|
|
|
continue; |
|
|
|
|
|
|
|
/* check that we have an idx file */ |
|
|
|
char *psz_ext_idx = strrchr(p_fsdir_slave->psz_filename, '.'); |
|
|
|
if (psz_ext_idx == NULL) |
|
|
|
continue; |
|
|
|
psz_ext_idx++; |
|
|
|
if (strcasecmp(psz_ext_idx, "idx" ) == 0) |
|
|
|
return true; |
|
|
|
} |
|
|
|
return false; |
|
|
|
} |
|
|
|
|
|
|
|
static void fsdir_attach_slaves(struct access_fsdir *p_fsdir) |
|
|
|
{ |
|
|
|
if (p_fsdir->i_sub_autodetect_fuzzy == 0) |
|
|
|
return; |
|
|
|
|
|
|
|
/* Try to match slaves for each items of the node */ |
|
|
|
for (int i = 0; i < p_fsdir->p_node->i_children; i++) |
|
|
|
{ |
|
|
|
input_item_node_t *p_node = p_fsdir->p_node->pp_children[i]; |
|
|
|
input_item_t *p_item = p_node->p_item; |
|
|
|
|
|
|
|
for (unsigned int j = 0; j < p_fsdir->i_slaves; j++) |
|
|
|
{ |
|
|
|
struct fsdir_slave *p_fsdir_slave = p_fsdir->pp_slaves[j]; |
|
|
|
|
|
|
|
/* Don't try to match slaves with themselves or slaves already
|
|
|
|
* attached with the higher priority */ |
|
|
|
if (p_fsdir_slave->p_node == p_node |
|
|
|
|| p_fsdir_slave->p_slave->i_priority == SLAVE_PRIORITY_MATCH_ALL) |
|
|
|
continue; |
|
|
|
|
|
|
|
uint8_t i_priority = |
|
|
|
fsdir_get_slave_priority(p_item, p_fsdir_slave->p_slave, |
|
|
|
p_fsdir_slave->psz_filename); |
|
|
|
|
|
|
|
if (i_priority < p_fsdir->i_sub_autodetect_fuzzy) |
|
|
|
continue; |
|
|
|
|
|
|
|
/* Drop the ".sub" slave if a ".idx" slave matches */ |
|
|
|
if (p_fsdir_slave->p_slave->i_type == SLAVE_TYPE_SPU |
|
|
|
&& fsdir_should_match_idx(p_fsdir, p_fsdir_slave)) |
|
|
|
continue; |
|
|
|
|
|
|
|
input_item_slave_t *p_slave = |
|
|
|
input_item_slave_New(p_fsdir_slave->p_slave->psz_uri, |
|
|
|
p_fsdir_slave->p_slave->i_type, |
|
|
|
i_priority); |
|
|
|
if (p_slave == NULL) |
|
|
|
break; |
|
|
|
|
|
|
|
if (input_item_AddSlave(p_item, p_slave) != VLC_SUCCESS) |
|
|
|
{ |
|
|
|
input_item_slave_Delete(p_slave); |
|
|
|
break; |
|
|
|
} |
|
|
|
|
|
|
|
/* Remove the corresponding node if any: This slave won't be
|
|
|
|
* added in the parent node */ |
|
|
|
if (p_fsdir_slave->p_node != NULL) |
|
|
|
{ |
|
|
|
input_item_node_RemoveNode(p_fsdir->p_node, |
|
|
|
p_fsdir_slave->p_node); |
|
|
|
input_item_node_Delete(p_fsdir_slave->p_node); |
|
|
|
p_fsdir_slave->p_node = NULL; |
|
|
|
} |
|
|
|
|
|
|
|
p_fsdir_slave->p_slave->i_priority = i_priority; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
void access_fsdir_init(struct access_fsdir *p_fsdir, |
|
|
|
stream_t *p_access, input_item_node_t *p_node) |
|
|
|
{ |
|
|
|
p_fsdir->p_node = p_node; |
|
|
|
p_fsdir->b_show_hiddenfiles = var_InheritBool(p_access, "show-hiddenfiles"); |
|
|
|
p_fsdir->psz_ignored_exts = var_InheritString(p_access, "ignore-filetypes"); |
|
|
|
bool b_autodetect = var_InheritBool(p_access, "sub-autodetect-file"); |
|
|
|
p_fsdir->i_sub_autodetect_fuzzy = !b_autodetect ? 0 : |
|
|
|
var_InheritInteger(p_access, "sub-autodetect-fuzzy"); |
|
|
|
TAB_INIT(p_fsdir->i_slaves, p_fsdir->pp_slaves); |
|
|
|
} |
|
|
|
|
|
|
|
void access_fsdir_finish(struct access_fsdir *p_fsdir, bool b_success) |
|
|
|
{ |
|
|
|
if (b_success) |
|
|
|
{ |
|
|
|
fsdir_attach_slaves(p_fsdir); |
|
|
|
fsdir_sort(p_fsdir->p_node); |
|
|
|
} |
|
|
|
free(p_fsdir->psz_ignored_exts); |
|
|
|
|
|
|
|
/* Remove unmatched slaves */ |
|
|
|
for (unsigned int i = 0; i < p_fsdir->i_slaves; i++) |
|
|
|
{ |
|
|
|
struct fsdir_slave *p_fsdir_slave = p_fsdir->pp_slaves[i]; |
|
|
|
if (p_fsdir_slave != NULL) |
|
|
|
{ |
|
|
|
input_item_slave_Delete(p_fsdir_slave->p_slave); |
|
|
|
free(p_fsdir_slave->psz_filename); |
|
|
|
free(p_fsdir_slave); |
|
|
|
} |
|
|
|
} |
|
|
|
TAB_CLEAN(p_fsdir->i_slaves, p_fsdir->pp_slaves); |
|
|
|
} |
|
|
|
|
|
|
|
int access_fsdir_additem(struct access_fsdir *p_fsdir, |
|
|
|
const char *psz_uri, const char *psz_filename, |
|
|
|
int i_type, int i_net) |
|
|
|
{ |
|
|
|
enum slave_type i_slave_type; |
|
|
|
struct fsdir_slave *p_fsdir_slave = NULL; |
|
|
|
input_item_node_t *p_node; |
|
|
|
|
|
|
|
if (p_fsdir->i_sub_autodetect_fuzzy != 0 |
|
|
|
&& input_item_slave_GetType(psz_filename, &i_slave_type)) |
|
|
|
{ |
|
|
|
p_fsdir_slave = malloc(sizeof(*p_fsdir_slave)); |
|
|
|
if (!p_fsdir_slave) |
|
|
|
return VLC_ENOMEM; |
|
|
|
|
|
|
|
p_fsdir_slave->p_node = NULL; |
|
|
|
p_fsdir_slave->psz_filename = strdup(psz_filename); |
|
|
|
p_fsdir_slave->p_slave = input_item_slave_New(psz_uri, i_slave_type, |
|
|
|
SLAVE_PRIORITY_MATCH_NONE); |
|
|
|
if (!p_fsdir_slave->p_slave || !p_fsdir_slave->psz_filename) |
|
|
|
{ |
|
|
|
free(p_fsdir_slave->psz_filename); |
|
|
|
free(p_fsdir_slave); |
|
|
|
return VLC_ENOMEM; |
|
|
|
} |
|
|
|
|
|
|
|
TAB_APPEND(p_fsdir->i_slaves, p_fsdir->pp_slaves, p_fsdir_slave); |
|
|
|
} |
|
|
|
|
|
|
|
if (fsdir_is_ignored(p_fsdir, psz_filename)) |
|
|
|
return VLC_SUCCESS; |
|
|
|
|
|
|
|
input_item_t *p_item = input_item_NewExt(psz_uri, psz_filename, -1, |
|
|
|
i_type, i_net); |
|
|
|
if (p_item == NULL) |
|
|
|
return VLC_ENOMEM; |
|
|
|
|
|
|
|
input_item_CopyOptions(p_item, p_fsdir->p_node->p_item); |
|
|
|
p_node = input_item_node_AppendItem(p_fsdir->p_node, p_item); |
|
|
|
input_item_Release(p_item); |
|
|
|
|
|
|
|
/* A slave can also be an item. If there is a match, this item will be
|
|
|
|
* removed from the parent node. This is not a common case, since most |
|
|
|
* slaves will be ignored by fsdir_is_ignored() */ |
|
|
|
if (p_fsdir_slave != NULL) |
|
|
|
p_fsdir_slave->p_node = p_node; |
|
|
|
return VLC_SUCCESS; |
|
|
|
} |
|
|
|
|