From d710e6ee954bcceac922dd6dfb9fd212660e78f0 Mon Sep 17 00:00:00 2001 From: ejurgensen Date: Tue, 15 Oct 2013 13:36:11 +0200 Subject: [PATCH] Add support for M3U metadata (extinf) --- src/filescanner.c | 101 +++++++++++++++++-------------------- src/filescanner.h | 9 +++- src/filescanner_m3u.c | 115 ++++++++++++++++++++++++++++++++++++------ 3 files changed, 155 insertions(+), 70 deletions(-) diff --git a/src/filescanner.c b/src/filescanner.c index c74f2828..ff41ca11 100644 --- a/src/filescanner.c +++ b/src/filescanner.c @@ -296,7 +296,7 @@ fixup_tags(struct media_file_info *mfi) void -process_media_file(char *file, time_t mtime, off_t size, int compilation, int url) +process_media_file(char *file, time_t mtime, off_t size, int compilation, int url, struct extinf_ctx *extinf) { struct media_file_info mfi; char *filename; @@ -305,6 +305,41 @@ process_media_file(char *file, time_t mtime, off_t size, int compilation, int ur int id; int ret; + filename = strrchr(file, '/'); + if (!filename) + { + DPRINTF(E_LOG, L_SCAN, "Could not determine filename for %s\n", file); + + return; + } + + /* File types which should never be processed */ + ext = strrchr(file, '.'); + if (ext) + { + if ((strcmp(ext, ".pls") == 0) || (strcmp(ext, ".url") == 0)) + { + DPRINTF(E_INFO, L_SCAN, "No support for .url and .pls in this version, use .m3u\n"); + + return; + } + else if ((strcmp(ext, ".png") == 0) || (strcmp(ext, ".jpg") == 0)) + { + /* Artwork files - don't scan */ + return; + } + else if ((strcmp(ext, ".db") == 0) || (strcmp(ext, ".ini") == 0)) + { + /* System files - don't scan */ + return; + } + else if ((strlen(filename) > 1) && ((filename[1] == '_') || (filename[1] == '.'))) + { + /* Hidden files - don't scan */ + return; + } + } + db_file_stamp_bypath(file, &stamp, &id); if (stamp >= mtime) @@ -318,14 +353,6 @@ process_media_file(char *file, time_t mtime, off_t size, int compilation, int ur if (stamp) mfi.id = db_file_id_bypath(file); - filename = strrchr(file, '/'); - if (!filename) - { - DPRINTF(E_LOG, L_SCAN, "Could not determine filename for %s\n", file); - - return; - } - mfi.fname = strdup(filename + 1); if (!mfi.fname) { @@ -346,54 +373,20 @@ process_media_file(char *file, time_t mtime, off_t size, int compilation, int ur mfi.time_modified = mtime; mfi.file_size = size; - ret = -1; - - /* Special cases */ - ext = strrchr(file, '.'); - if (ext) + if (!url) { - if ((strcmp(ext, ".pls") == 0) - || (strcmp(ext, ".url") == 0)) - { - DPRINTF(E_INFO, L_SCAN, "No support for .url and .pls in this version, use .m3u\n"); - - goto out; - } - else if ((strcmp(ext, ".png") == 0) - || (strcmp(ext, ".jpg") == 0)) - { - /* Artwork - don't scan */ - goto out; - } - else if ((strcmp(ext, ".db") == 0) - || (strcmp(ext, ".ini") == 0)) - { - /* System files - don't scan */ - goto out; - } - else if ((filename[1] == '_') - || (filename[1] == '.')) - { - /* Hidden files - don't scan */ - goto out; - } + mfi.data_kind = 0; /* real file */ + ret = scan_metadata_ffmpeg(file, &mfi); } - - /* General case */ - if (ret < 0) + else { - if (url == 0) - { - mfi.data_kind = 0; /* real file */ - ret = scan_metadata_ffmpeg(file, &mfi); + mfi.data_kind = 1; /* url/stream */ + if (extinf && extinf->found) + { + mfi.artist = strdup(extinf->artist); + mfi.title = strdup(extinf->title); } - else if (url == 1) - { - mfi.data_kind = 1; /* url/stream */ - ret = scan_metadata_icy(file, &mfi); - } - else - ret = -1; + ret = scan_metadata_icy(file, &mfi); } if (ret < 0) @@ -526,7 +519,7 @@ process_file(char *file, time_t mtime, off_t size, int compilation, int flags) } /* Not any kind of special file, so let's see if it's a media file */ - process_media_file(file, mtime, size, compilation, 0); + process_media_file(file, mtime, size, compilation, 0, NULL); } diff --git a/src/filescanner.h b/src/filescanner.h index 3335fd50..da4d78e7 100644 --- a/src/filescanner.h +++ b/src/filescanner.h @@ -10,8 +10,15 @@ filescanner_init(void); void filescanner_deinit(void); +struct extinf_ctx +{ + char *artist; + char *title; + int found; +}; + void -process_media_file(char *file, time_t mtime, off_t size, int compilation, int url); +process_media_file(char *file, time_t mtime, off_t size, int compilation, int url, struct extinf_ctx *extinf); /* Actual scanners */ int diff --git a/src/filescanner_m3u.c b/src/filescanner_m3u.c index 85d25150..50553a63 100644 --- a/src/filescanner_m3u.c +++ b/src/filescanner_m3u.c @@ -34,18 +34,92 @@ #include #include +#include + #include "logger.h" #include "db.h" #include "filescanner.h" #include "misc.h" +static int +get_extinf(char *string, struct extinf_ctx *extinf) +{ + regex_t *regex; + regmatch_t *regmatch; + size_t matchlen; + + if (strcmp(string, "#EXTINF:") <= 0) + { + DPRINTF(E_DBG, L_SCAN, "Playlist line has no EXTINF data\n"); + + return 0; + } + + regex = (regex_t*)malloc(sizeof(regex_t)); + if (!regex) + { + DPRINTF(E_LOG, L_SCAN, "Out of memory for playlist regex.\n"); + + return 0; + } + + regcomp(regex, "^#EXTINF:.*,(.*?)-(.*)",REG_EXTENDED|REG_NEWLINE); + regmatch = (regmatch_t*)malloc(sizeof(regmatch_t) * (1 + regex->re_nsub)); + if (!regmatch) + { + DPRINTF(E_LOG, L_SCAN, "Out of memory for playlist regmatch.\n"); + + goto regexfail; + } + + if (regexec(regex, string, 1 + regex->re_nsub, regmatch, 0)) + { + DPRINTF(E_DBG, L_SCAN, "Playlist line has no EXTINF data\n"); + + goto regmatchfail; + } + + /* Found extinf */ + if (regmatch[0].rm_so != -1) + { + DPRINTF(E_DBG, L_SCAN, "Playlist line has EXTINF data\n"); + + /* Artist */ + if (extinf->artist) + free(extinf->artist); + matchlen = regmatch[1].rm_eo - regmatch[1].rm_so; + extinf->artist = (char*)malloc(matchlen + 1); + strncpy(extinf->artist, string + regmatch[1].rm_so, matchlen + 1); + extinf->artist[matchlen]='\0'; + + /* Title */ + if (extinf->title) + free(extinf->title); + matchlen = regmatch[1].rm_eo - regmatch[1].rm_so; + extinf->title = (char*)malloc(matchlen + 1); + strncpy(extinf->title, string + regmatch[1].rm_so, matchlen + 1); + extinf->title[matchlen]='\0'; + + free(regmatch); + regfree(regex); + return 1; + } + + regmatchfail: + free(regmatch); + regexfail: + regfree(regex); + return 0; +} + void scan_m3u_playlist(char *file, time_t mtime) { FILE *fp; struct playlist_info *pli; struct stat sb; + struct extinf_ctx extinf; char buf[PATH_MAX]; char *entry; char *filename; @@ -121,30 +195,36 @@ scan_m3u_playlist(char *file, time_t mtime) DPRINTF(E_INFO, L_SCAN, "Added playlist as id %d\n", pl_id); } + extinf.found = 0; while (fgets(buf, sizeof(buf), fp) != NULL) { len = strlen(buf); - if (buf[len - 1] != '\n') + if (len >= (sizeof(buf) - 1)) { - DPRINTF(E_WARN, L_SCAN, "Entry exceeds PATH_MAX, discarding\n"); - - while (fgets(buf, sizeof(buf), fp) != NULL) - { - if (buf[strlen(buf) - 1] == '\n') - break; - } + DPRINTF(E_LOG, L_SCAN, "Playlist entry exceeds PATH_MAX, discarding\n"); continue; } - if ((!isalnum(buf[0])) && (buf[0] != '/')) - continue; - - while (isspace(buf[len - 1])) + /* rtrim and check that length is sane (ignore blank lines) */ + while ((len > 0) && isspace(buf[len - 1])) { len--; buf[len] = '\0'; } + if (len < 1) + continue; + + /* Saves metadata in extinf if EXTINF metadata line */ + if (get_extinf(buf, &extinf)) + { + extinf.found = 1; + continue; + } + + /* Check that first char is sane for a path */ + if ((!isalnum(buf[0])) && (buf[0] != '/') && (buf[0] != '.')) + continue; /* Check if line is an URL */ if (strcmp(buf, "http://") > 0) @@ -159,13 +239,13 @@ scan_m3u_playlist(char *file, time_t mtime) continue; } - process_media_file(filename, mtime, 0, 0, 1); + process_media_file(filename, mtime, 0, 0, 1, &extinf); } /* Regular file */ else { - /* m3u might be from Windows so we change backslash to forward slash */ - for (i = 0; i < strlen(buf); i++) + /* m3u might be from Windows so we change backslash to forward slash */ + for (i = 0; i < strlen(buf); i++) { if (buf[i] == '\\') buf[i] = '/'; @@ -218,6 +298,7 @@ scan_m3u_playlist(char *file, time_t mtime) if (ret < 0) DPRINTF(E_WARN, L_SCAN, "Could not add %s to playlist\n", filename); + extinf.found = 0; free(filename); } @@ -230,6 +311,10 @@ scan_m3u_playlist(char *file, time_t mtime) } fclose(fp); + if (extinf.artist) + free(extinf.artist); + if (extinf.title) + free(extinf.title); DPRINTF(E_INFO, L_SCAN, "Done processing playlist\n"); }