Add support for .pls playlists

This commit is contained in:
ejurgensen 2014-08-27 21:57:16 +02:00
parent 1b51fcf07c
commit ae3b5077ec
4 changed files with 52 additions and 26 deletions

11
README
View File

@ -237,11 +237,14 @@ them. Watch out for that.
Playlists and internet radio Playlists and internet radio
---------------------------- ----------------------------
forked-daapd supports M3U playlists. Just drop your playlist somewhere in forked-daapd supports M3U and PLS playlists. Just drop your playlist somewhere
your library with an .m3u extension and it will pick it up. in your library with an .m3u or .pls extension and it will pick it up.
If the m3u contains an http URL it will be added as an internet radio station, If the playlist contains an http URL it will be added as an internet radio
and the URL will be probed for Shoutcast (ICY) metadata. station, and the URL will be probed for Shoutcast (ICY) metadata.
forked-daapd does not support playlists in playlists (so for instance a .m3u
where one of the items is another .m3u or .pls).
Support for iTunes Music Library XML format is available as a compile-time Support for iTunes Music Library XML format is available as a compile-time
option. By default, metadata from our parsers is preferred over what's in option. By default, metadata from our parsers is preferred over what's in

View File

@ -460,9 +460,9 @@ filescanner_process_media(char *path, time_t mtime, off_t size, int type, struct
ext = strrchr(path, '.'); ext = strrchr(path, '.');
if (ext) if (ext)
{ {
if ((strcasecmp(ext, ".pls") == 0) || (strcasecmp(ext, ".url") == 0)) if (strcasecmp(ext, ".url") == 0)
{ {
DPRINTF(E_INFO, L_SCAN, "No support for .url and .pls in this version, use .m3u\n"); DPRINTF(E_INFO, L_SCAN, "No support for .url in this version, use .m3u or .pls\n");
return; return;
} }
@ -599,7 +599,9 @@ process_playlist(char *file, time_t mtime)
if (ext) if (ext)
{ {
if (strcasecmp(ext, ".m3u") == 0) if (strcasecmp(ext, ".m3u") == 0)
scan_m3u_playlist(file, mtime); scan_playlist(file, mtime);
else if (strcasecmp(ext, ".pls") == 0)
scan_playlist(file, mtime);
#ifdef ITUNES #ifdef ITUNES
else if (strcasecmp(ext, ".xml") == 0) else if (strcasecmp(ext, ".xml") == 0)
scan_itunes_itml(file); scan_itunes_itml(file);
@ -672,6 +674,7 @@ process_file(char *file, time_t mtime, off_t size, int type, int flags)
if (ext) if (ext)
{ {
if ((strcasecmp(ext, ".m3u") == 0) if ((strcasecmp(ext, ".m3u") == 0)
|| (strcasecmp(ext, ".pls") == 0)
#ifdef ITUNES #ifdef ITUNES
|| (strcasecmp(ext, ".xml") == 0) || (strcasecmp(ext, ".xml") == 0)
#endif #endif

View File

@ -32,7 +32,7 @@ int
scan_metadata_icy(char *url, struct media_file_info *mfi); scan_metadata_icy(char *url, struct media_file_info *mfi);
void void
scan_m3u_playlist(char *file, time_t mtime); scan_playlist(char *file, time_t mtime);
#ifdef ITUNES #ifdef ITUNES
void void

View File

@ -39,6 +39,9 @@
#include "filescanner.h" #include "filescanner.h"
#include "misc.h" #include "misc.h"
/* Formats we can read so far */
#define PLAYLIST_PLS 1
#define PLAYLIST_M3U 2
/* Get metadata from the EXTINF tag */ /* Get metadata from the EXTINF tag */
static int static int
@ -71,19 +74,21 @@ extinf_get(char *string, struct media_file_info *mfi, int *extinf)
} }
void void
scan_m3u_playlist(char *file, time_t mtime) scan_playlist(char *file, time_t mtime)
{ {
FILE *fp; FILE *fp;
struct media_file_info mfi; struct media_file_info mfi;
struct playlist_info *pli; struct playlist_info *pli;
struct stat sb; struct stat sb;
char buf[PATH_MAX]; char buf[PATH_MAX];
char *path;
char *entry; char *entry;
char *filename; char *filename;
char *ptr; char *ptr;
size_t len; size_t len;
int extinf; int extinf;
int pl_id; int pl_id;
int pl_format;
int mfi_id; int mfi_id;
int ret; int ret;
int i; int i;
@ -98,6 +103,17 @@ scan_m3u_playlist(char *file, time_t mtime)
return; return;
} }
ptr = strrchr(file, '.');
if (!ptr)
return;
if (strcasecmp(ptr, ".m3u") == 0)
pl_format = PLAYLIST_M3U;
else if (strcasecmp(ptr, ".pls") == 0)
pl_format = PLAYLIST_PLS;
else
return;
filename = strrchr(file, '/'); filename = strrchr(file, '/');
if (!filename) if (!filename)
filename = file; filename = file;
@ -145,7 +161,7 @@ scan_m3u_playlist(char *file, time_t mtime)
ret = db_pl_add(buf, file, &pl_id); ret = db_pl_add(buf, file, &pl_id);
if (ret < 0) if (ret < 0)
{ {
DPRINTF(E_LOG, L_SCAN, "Error adding m3u playlist '%s'\n", file); DPRINTF(E_LOG, L_SCAN, "Error adding playlist '%s'\n", file);
return; return;
} }
@ -159,12 +175,6 @@ scan_m3u_playlist(char *file, time_t mtime)
while (fgets(buf, sizeof(buf), fp) != NULL) while (fgets(buf, sizeof(buf), fp) != NULL)
{ {
len = strlen(buf); len = strlen(buf);
if (len >= (sizeof(buf) - 1))
{
DPRINTF(E_LOG, L_SCAN, "Playlist entry exceeds PATH_MAX, discarding\n");
continue;
}
/* rtrim and check that length is sane (ignore blank lines) */ /* rtrim and check that length is sane (ignore blank lines) */
while ((len > 0) && isspace(buf[len - 1])) while ((len > 0) && isspace(buf[len - 1]))
@ -176,19 +186,29 @@ scan_m3u_playlist(char *file, time_t mtime)
continue; continue;
/* Saves metadata in mfi if EXTINF metadata line */ /* Saves metadata in mfi if EXTINF metadata line */
if (extinf_get(buf, &mfi, &extinf)) if ((pl_format == PLAYLIST_M3U) && extinf_get(buf, &mfi, &extinf))
continue;
/* For pls files we are only interested in the part after the FileX= entry */
path = NULL;
if ((pl_format == PLAYLIST_PLS) && (strncasecmp(buf, "file", strlen("file")) == 0))
path = strchr(buf, '=') + 1;
else if (pl_format == PLAYLIST_M3U)
path = buf;
if (!path)
continue; continue;
/* Check that first char is sane for a path */ /* Check that first char is sane for a path */
if ((!isalnum(buf[0])) && (buf[0] != '/') && (buf[0] != '.')) if ((!isalnum(path[0])) && (path[0] != '/') && (path[0] != '.'))
continue; continue;
/* Check if line is an URL, will be added to library */ /* Check if line is an URL, will be added to library */
if (strcmp(buf, "http://") > 0) if (strncasecmp(path, "http://", strlen("http://")) == 0)
{ {
DPRINTF(E_DBG, L_SCAN, "Playlist contains URL entry\n"); DPRINTF(E_DBG, L_SCAN, "Playlist contains URL entry\n");
filename = strdup(buf); filename = strdup(path);
if (!filename) if (!filename)
{ {
DPRINTF(E_LOG, L_SCAN, "Out of memory for playlist filename\n"); DPRINTF(E_LOG, L_SCAN, "Out of memory for playlist filename\n");
@ -204,11 +224,11 @@ scan_m3u_playlist(char *file, time_t mtime)
/* Regular file, should already be in library */ /* Regular file, should already be in library */
else else
{ {
/* m3u might be from Windows so we change backslash to forward slash */ /* Playlist might be from Windows so we change backslash to forward slash */
for (i = 0; i < strlen(buf); i++) for (i = 0; i < strlen(path); i++)
{ {
if (buf[i] == '\\') if (path[i] == '\\')
buf[i] = '/'; path[i] = '/';
} }
/* Now search for the library item where the path has closest match to playlist item */ /* Now search for the library item where the path has closest match to playlist item */
@ -217,7 +237,7 @@ scan_m3u_playlist(char *file, time_t mtime)
entry = NULL; entry = NULL;
do do
{ {
ptr = strrchr(buf, '/'); ptr = strrchr(path, '/');
if (entry) if (entry)
*(entry - 1) = '/'; *(entry - 1) = '/';
if (ptr) if (ptr)
@ -226,7 +246,7 @@ scan_m3u_playlist(char *file, time_t mtime)
entry = ptr + 1; entry = ptr + 1;
} }
else else
entry = buf; entry = path;
DPRINTF(E_SPAM, L_SCAN, "Playlist entry is now %s\n", entry); DPRINTF(E_SPAM, L_SCAN, "Playlist entry is now %s\n", entry);
ret = db_files_get_count_bymatch(entry); ret = db_files_get_count_bymatch(entry);