[filescanner] Copy playlist scan optimisations to itunes scanner
This commit is contained in:
parent
3520ab030e
commit
322abb8274
|
@ -191,6 +191,27 @@ strip_extension(const char *path)
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
parent_dir(const char **current, const char *path)
|
||||||
|
{
|
||||||
|
const char *ptr;
|
||||||
|
|
||||||
|
if (*current)
|
||||||
|
ptr = *current;
|
||||||
|
else
|
||||||
|
ptr = strrchr(path, '/');
|
||||||
|
|
||||||
|
if (!ptr || (ptr == path))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
for (ptr--; (ptr > path) && (*ptr != '/'); ptr--)
|
||||||
|
;
|
||||||
|
|
||||||
|
*current = ptr;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
push_dir(struct stacked_dir **s, char *path, int parent_id)
|
push_dir(struct stacked_dir **s, char *path, int parent_id)
|
||||||
{
|
{
|
||||||
|
|
|
@ -5,7 +5,8 @@
|
||||||
#include "db.h"
|
#include "db.h"
|
||||||
|
|
||||||
|
|
||||||
/* Actual scanners */
|
/* --------------------------- Actual scanners ---------------------------- */
|
||||||
|
|
||||||
int
|
int
|
||||||
scan_metadata_ffmpeg(const char *file, struct media_file_info *mfi);
|
scan_metadata_ffmpeg(const char *file, struct media_file_info *mfi);
|
||||||
|
|
||||||
|
@ -20,10 +21,40 @@ void
|
||||||
scan_itunes_itml(const char *file);
|
scan_itunes_itml(const char *file);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/* ------------ Common utility functions used by the scanners ------------ */
|
||||||
|
|
||||||
|
/* Returns a pointer to the filename part of path.
|
||||||
|
*
|
||||||
|
* @in path the complete path
|
||||||
|
* @return pointer to filename
|
||||||
|
*/
|
||||||
const char *
|
const char *
|
||||||
filename_from_path(const char *path);
|
filename_from_path(const char *path);
|
||||||
|
|
||||||
|
/* Returns path without file extension. Caller must free result.
|
||||||
|
*
|
||||||
|
* @in path the complete path
|
||||||
|
* @return modified path
|
||||||
|
*/
|
||||||
char *
|
char *
|
||||||
strip_extension(const char *path);
|
strip_extension(const char *path);
|
||||||
|
|
||||||
|
/* Iterate up a file path.
|
||||||
|
*
|
||||||
|
* Example of three calls where path is '/foo/bar/file.mp3', and starting with
|
||||||
|
* current = NULL:
|
||||||
|
* ret = parent_dir(¤t, path) -> ret = 0, current = /bar/file.mp3
|
||||||
|
* ret = parent_dir(¤t, path) -> ret = 0, current = /foo/bar/file.mp3
|
||||||
|
* ret = parent_dir(¤t, path) -> ret = -1, current = /foo/bar/file.mp3
|
||||||
|
*
|
||||||
|
* @in/out current if the input pointer is not a null pointer, it will be moved
|
||||||
|
* one level up, otherwise it will be set to the point at the
|
||||||
|
* file's directory
|
||||||
|
* @in path the complete path
|
||||||
|
* @return 0 if parent dir found, otherwise -1
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
parent_dir(const char **current, const char *path);
|
||||||
|
|
||||||
#endif /* !__FILESCANNER_H__ */
|
#endif /* !__FILESCANNER_H__ */
|
||||||
|
|
|
@ -41,6 +41,7 @@
|
||||||
|
|
||||||
#include "logger.h"
|
#include "logger.h"
|
||||||
#include "db.h"
|
#include "db.h"
|
||||||
|
#include "library/filescanner.h"
|
||||||
#include "conffile.h"
|
#include "conffile.h"
|
||||||
#include "misc.h"
|
#include "misc.h"
|
||||||
|
|
||||||
|
@ -307,73 +308,88 @@ check_meta(plist_t dict)
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
find_track_file(char *location)
|
mfi_id_find(const char *path)
|
||||||
{
|
{
|
||||||
|
struct query_params qp;
|
||||||
|
char filter[PATH_MAX];
|
||||||
|
const char *a;
|
||||||
|
const char *b;
|
||||||
|
char *dbpath;
|
||||||
|
char *winner;
|
||||||
|
int score;
|
||||||
|
int i;
|
||||||
int ret;
|
int ret;
|
||||||
int plen;
|
|
||||||
int mfi_id;
|
|
||||||
char *entry;
|
|
||||||
char *ptr;
|
|
||||||
|
|
||||||
location = evhttp_decode_uri(location);
|
ret = db_snprintf(filter, sizeof(filter), "f.fname = '%q' COLLATE NOCASE", filename_from_path(path));
|
||||||
if (!location)
|
if (ret < 0)
|
||||||
{
|
{
|
||||||
DPRINTF(E_LOG, L_SCAN, "Could not decode iTunes XML playlist url.\n");
|
DPRINTF(E_LOG, L_SCAN, "Location in iTunes XML is too long: '%s'\n", path);
|
||||||
|
return -1;
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
plen = strlen("file://");
|
memset(&qp, 0, sizeof(struct query_params));
|
||||||
|
|
||||||
/* Not a local file ... */
|
qp.type = Q_BROWSE_PATH;
|
||||||
if (strncmp(location, "file://", plen) != 0)
|
qp.sort = S_NONE;
|
||||||
return 0;
|
qp.filter = filter;
|
||||||
|
|
||||||
/* Now search for the library item where the path has closest match to playlist item */
|
ret = db_query_start(&qp);
|
||||||
/* Succes is when we find an unambiguous match, or when we no longer can expand the */
|
if (ret < 0)
|
||||||
/* the path to refine our search. */
|
|
||||||
entry = NULL;
|
|
||||||
do
|
|
||||||
{
|
{
|
||||||
ptr = strrchr(location, '/');
|
db_query_end(&qp);
|
||||||
if (entry)
|
return -1;
|
||||||
*(entry - 1) = '/';
|
|
||||||
if (ptr)
|
|
||||||
{
|
|
||||||
*ptr = '\0';
|
|
||||||
entry = ptr + 1;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
entry = location;
|
|
||||||
|
|
||||||
DPRINTF(E_SPAM, L_SCAN, "iTunes XML playlist entry is now %s\n", entry);
|
|
||||||
ret = db_files_get_count_bymatch(entry);
|
|
||||||
|
|
||||||
} while (ptr && (ret > 1));
|
|
||||||
|
|
||||||
if (ret > 0)
|
|
||||||
{
|
|
||||||
mfi_id = db_file_id_bymatch(entry);
|
|
||||||
DPRINTF(E_DBG, L_SCAN, "Found iTunes XML playlist entry match, id is %d, entry is %s\n", mfi_id, entry);
|
|
||||||
|
|
||||||
free(location);
|
|
||||||
return mfi_id;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
DPRINTF(E_DBG, L_SCAN, "No match for iTunes XML playlist entry %s\n", entry);
|
|
||||||
|
|
||||||
free(location);
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
winner = NULL;
|
||||||
|
score = 0;
|
||||||
|
while ((db_query_fetch_string(&qp, &dbpath) == 0) && dbpath)
|
||||||
|
{
|
||||||
|
if (qp.results == 1)
|
||||||
|
{
|
||||||
|
winner = strdup(dbpath);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0, a = NULL, b = NULL; (parent_dir(&a, path) == 0) && (parent_dir(&b, dbpath) == 0) && (strcasecmp(a, b) == 0); i++)
|
||||||
|
;
|
||||||
|
|
||||||
|
DPRINTF(E_SPAM, L_SCAN, "Comparison of '%s' and '%s' gave score %d\n", dbpath, path, i);
|
||||||
|
|
||||||
|
if (i > score)
|
||||||
|
{
|
||||||
|
free(winner);
|
||||||
|
winner = strdup(dbpath);
|
||||||
|
score = i;
|
||||||
|
}
|
||||||
|
else if (i == score)
|
||||||
|
{
|
||||||
|
free(winner);
|
||||||
|
winner = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
db_query_end(&qp);
|
||||||
|
|
||||||
|
if (!winner)
|
||||||
|
{
|
||||||
|
DPRINTF(E_LOG, L_SCAN, "No file matches iTunes XML entry '%s'\n", path);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
DPRINTF(E_DBG, L_SCAN, "Found '%s' from iTunes XML (results %d)\n", path, qp.results);
|
||||||
|
|
||||||
|
ret = db_file_id_bypath(winner);
|
||||||
|
free(winner);
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
process_track_file(plist_t trk)
|
process_track_file(plist_t trk)
|
||||||
{
|
{
|
||||||
char *location;
|
|
||||||
struct media_file_info *mfi;
|
struct media_file_info *mfi;
|
||||||
|
char *location;
|
||||||
|
char *path;
|
||||||
char *string;
|
char *string;
|
||||||
uint64_t integer;
|
uint64_t integer;
|
||||||
char **strval;
|
char **strval;
|
||||||
|
@ -385,24 +401,30 @@ process_track_file(plist_t trk)
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
ret = get_dictval_string_from_key(trk, "Location", &location);
|
ret = get_dictval_string_from_key(trk, "Location", &location);
|
||||||
if (ret < 0)
|
if ((ret < 0) || !location)
|
||||||
{
|
{
|
||||||
DPRINTF(E_WARN, L_SCAN, "Track type File with no Location\n");
|
DPRINTF(E_LOG, L_SCAN, "Track type File with no Location\n");
|
||||||
|
return -1;
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mfi_id = find_track_file(location);
|
if (strncmp(location, "file://", strlen("file://")) != 0)
|
||||||
|
{
|
||||||
|
DPRINTF(E_LOG, L_SCAN, "Track type File, but Location does not start with file://: '%s'\n", location);
|
||||||
|
free(location);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
path = evhttp_decode_uri(location + strlen("file://"));
|
||||||
|
free(location);
|
||||||
|
|
||||||
|
mfi_id = mfi_id_find(path);
|
||||||
if (mfi_id <= 0)
|
if (mfi_id <= 0)
|
||||||
{
|
{
|
||||||
DPRINTF(E_INFO, L_SCAN, "Could not match location '%s' to any known file\n", location);
|
free(path);
|
||||||
|
return -1;
|
||||||
free(location);
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
free(location);
|
free(path);
|
||||||
|
|
||||||
if (!cfg_getbool(cfg_getsec(cfg, "library"), "itunes_overrides"))
|
if (!cfg_getbool(cfg_getsec(cfg, "library"), "itunes_overrides"))
|
||||||
return mfi_id;
|
return mfi_id;
|
||||||
|
|
|
@ -72,27 +72,6 @@ extinf_get(char *string, struct media_file_info *mfi, int *extinf)
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
|
||||||
parent_dir(const char **current, const char *path)
|
|
||||||
{
|
|
||||||
const char *ptr;
|
|
||||||
|
|
||||||
if (*current)
|
|
||||||
ptr = *current;
|
|
||||||
else
|
|
||||||
ptr = strrchr(path, '/');
|
|
||||||
|
|
||||||
if (!ptr || (ptr == path))
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
for (ptr--; (ptr > path) && (*ptr != '/'); ptr--)
|
|
||||||
;
|
|
||||||
|
|
||||||
*current = ptr;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
static int
|
||||||
process_url(int pl_id, const char *path, time_t mtime, int extinf, struct media_file_info *mfi)
|
process_url(int pl_id, const char *path, time_t mtime, int extinf, struct media_file_info *mfi)
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in New Issue