Commit c458178a authored by Thomas Guillem's avatar Thomas Guillem

input: move access_fsdir to input/item.c

parent 6c643e6f
...@@ -74,51 +74,6 @@ VLC_API int access_vaDirectoryControlHelper( stream_t *p_access, int i_query, va ...@@ -74,51 +74,6 @@ VLC_API int access_vaDirectoryControlHelper( stream_t *p_access, int i_query, va
p_access->pf_seek = (seek); \ p_access->pf_seek = (seek); \
} while(0) } while(0)
/**
* Access pf_readdir helper struct
* \see access_fsdir_init()
* \see access_fsdir_additem()
* \see access_fsdir_finish()
*/
struct access_fsdir
{
input_item_node_t *p_node;
void **pp_slaves;
unsigned int i_slaves;
int i_sub_autodetect_fuzzy;
bool b_show_hiddenfiles;
char *psz_ignored_exts;
};
/**
* Init a access_fsdir struct
*
* \param p_fsdir need to be cleaned with access_fsdir_finish()
* \param p_node node that will be used to add items
*/
VLC_API void access_fsdir_init(struct access_fsdir *p_fsdir,
stream_t *p_access, input_item_node_t *p_node);
/**
* Finish adding items to the node
*
* \param b_success if true, items of the node will be sorted.
*/
VLC_API void access_fsdir_finish(struct access_fsdir *p_fsdir, bool b_success);
/**
* Add a new input_item_t entry to the node of the access_fsdir struct.
*
* \param p_fsdir previously inited access_fsdir struct
* \param psz_uri uri of the new item
* \param psz_filename file name of the new item
* \param i_type see \ref input_item_type_e
* \param i_net see \ref input_item_net_type
*/
VLC_API int access_fsdir_additem(struct access_fsdir *p_fsdir,
const char *psz_uri, const char *psz_filename,
int i_type, int i_net);
/** /**
* @} @} * @} @}
*/ */
......
...@@ -410,4 +410,49 @@ struct input_stats_t ...@@ -410,4 +410,49 @@ struct input_stats_t
int64_t i_lost_abuffers; int64_t i_lost_abuffers;
}; };
/**
* Access pf_readdir helper struct
* \see access_fsdir_init()
* \see access_fsdir_additem()
* \see access_fsdir_finish()
*/
struct access_fsdir
{
input_item_node_t *p_node;
void **pp_slaves;
unsigned int i_slaves;
int i_sub_autodetect_fuzzy;
bool b_show_hiddenfiles;
char *psz_ignored_exts;
};
/**
* Init a access_fsdir struct
*
* \param p_fsdir need to be cleaned with access_fsdir_finish()
* \param p_node node that will be used to add items
*/
VLC_API void access_fsdir_init(struct access_fsdir *p_fsdir,
stream_t *p_access, input_item_node_t *p_node);
/**
* Finish adding items to the node
*
* \param b_success if true, items of the node will be sorted.
*/
VLC_API void access_fsdir_finish(struct access_fsdir *p_fsdir, bool b_success);
/**
* Add a new input_item_t entry to the node of the access_fsdir struct.
*
* \param p_fsdir previously inited access_fsdir struct
* \param psz_uri uri of the new item
* \param psz_filename file name of the new item
* \param i_type see \ref input_item_type_e
* \param i_net see \ref input_item_net_type
*/
VLC_API int access_fsdir_additem(struct access_fsdir *p_fsdir,
const char *psz_uri, const char *psz_filename,
int i_type, int i_net);
#endif #endif
...@@ -28,13 +28,11 @@ ...@@ -28,13 +28,11 @@
#include <assert.h> #include <assert.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <ctype.h>
#include <vlc_common.h> #include <vlc_common.h>
#include <vlc_url.h> #include <vlc_url.h>
#include <vlc_modules.h> #include <vlc_modules.h>
#include <vlc_interrupt.h> #include <vlc_interrupt.h>
#include <vlc_strings.h>
#include <libvlc.h> #include <libvlc.h>
#include "stream.h" #include "stream.h"
...@@ -319,363 +317,3 @@ stream_t *stream_AccessNew(vlc_object_t *parent, input_thread_t *input, ...@@ -319,363 +317,3 @@ stream_t *stream_AccessNew(vlc_object_t *parent, input_thread_t *input,
s = stream_FilterChainNew(s, cachename); s = stream_FilterChainNew(s, cachename);
return stream_FilterAutoNew(s); return stream_FilterAutoNew(s);
} }
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;
}
...@@ -27,11 +27,13 @@ ...@@ -27,11 +27,13 @@
#include <assert.h> #include <assert.h>
#include <time.h> #include <time.h>
#include <limits.h> #include <limits.h>
#include <ctype.h>
#include <vlc_common.h> #include <vlc_common.h>
#include <vlc_url.h> #include <vlc_url.h>
#include <vlc_interface.h> #include <vlc_interface.h>
#include <vlc_charset.h> #include <vlc_charset.h>
#include <vlc_strings.h>
#include "item.h" #include "item.h"
#include "info.h" #include "info.h"
...@@ -1319,3 +1321,363 @@ void input_item_UpdateTracksInfo(input_item_t *item, const es_format_t *fmt) ...@@ -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); TAB_APPEND(item->i_es, item->es, fmt_copy);
vlc_mutex_unlock( &item->lock ); 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;