From 98848eccf41a57ef2e7a04c86a4592aa6dfa9953 Mon Sep 17 00:00:00 2001 From: waker Date: Sat, 4 Dec 2010 11:44:37 +0100 Subject: added missing m3u plugin --- plugins/m3u/Makefile.am | 11 ++ plugins/m3u/m3u.c | 339 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 350 insertions(+) create mode 100644 plugins/m3u/Makefile.am create mode 100644 plugins/m3u/m3u.c (limited to 'plugins/m3u') diff --git a/plugins/m3u/Makefile.am b/plugins/m3u/Makefile.am new file mode 100644 index 00000000..e77663c6 --- /dev/null +++ b/plugins/m3u/Makefile.am @@ -0,0 +1,11 @@ +if HAVE_M3U +pkglib_LTLIBRARIES = m3u.la + +m3u_la_SOURCES = m3u.c + +m3u_la_LDFLAGS = -module + +m3u_la_LIBADD = $(LIBADD) + +m3u_la_CFLAGS = $(CFLAGS) -std=c99 +endif diff --git a/plugins/m3u/m3u.c b/plugins/m3u/m3u.c new file mode 100644 index 00000000..454d7dd2 --- /dev/null +++ b/plugins/m3u/m3u.c @@ -0,0 +1,339 @@ +/* + DeaDBeeF - ultimate music player for GNU/Linux systems with X11 + Copyright (C) 2009-2010 Alexey Yakovenko + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#include +#include +#include "../../deadbeef.h" + +#define trace(...) { fprintf(stderr, __VA_ARGS__); } +//#define trace(fmt,...) + +#define min(x,y) ((x)<(y)?(x):(y)) +#define max(x,y) ((x)>(y)?(x):(y)) + +static DB_functions_t *deadbeef; + +static const uint8_t * +skipspaces (const uint8_t *p, const uint8_t *end) { + while (p < end && *p <= ' ') { + p++; + } + return p; +} + +static DB_playItem_t * +load_m3u (DB_playItem_t *after, const char *fname, int *pabort, int (*cb)(DB_playItem_t *it, void *data), void *user_data) { + const char *slash = strrchr (fname, '/'); + trace ("enter pl_insert_m3u\n"); + // skip all empty lines and comments + DB_FILE *fp = deadbeef->fopen (fname); + if (!fp) { + trace ("failed to open file %s\n", fname); + return NULL; + } + int sz = deadbeef->fgetlength (fp); + if (sz > 1024*1024) { + deadbeef->fclose (fp); + trace ("file %s is too large to be a playlist\n", fname); + return NULL; + } + if (sz < 30) { + deadbeef->fclose (fp); + trace ("file %s is too small to be a playlist (%d)\n", fname, sz); + return NULL; + } + trace ("loading m3u...\n"); + uint8_t buffer[sz]; + deadbeef->fread (buffer, 1, sz, fp); + deadbeef->fclose (fp); + const uint8_t *p = buffer; + const uint8_t *end = buffer+sz; + deadbeef->pl_lock (); + while (p < end) { + p = skipspaces (p, end); + if (p >= end) { + break; + } + if (*p == '#') { + while (p < end && *p >= 0x20) { + p++; + } + if (p >= end) { + break; + } + continue; + } + const uint8_t *e = p; + while (e < end && *e >= 0x20) { + e++; + } + int n = e-p; + uint8_t nm[n+1]; + memcpy (nm, p, n); + nm[n] = 0; + + DB_playItem_t *it = NULL; + if (strrchr (nm, '/')) { + trace ("pl_insert_m3u: adding file %s\n", nm); + it = deadbeef->pl_insert_file (after, nm, pabort, cb, user_data); + } + else { + int l = strlen (nm); + char fullpath[slash - fname + l + 2]; + memcpy (fullpath, fname, slash - fname + 1); + strcpy (fullpath + (slash - fname + 1), nm); + trace ("pl_insert_m3u: adding file %s\n", fullpath); + it = deadbeef->pl_insert_file (after, fullpath, pabort, cb, user_data); + } + if (it) { + after = it; + } + if (pabort && *pabort) { + deadbeef->pl_unlock (); + return after; + } + p = e; + if (p >= end) { + break; + } + } + deadbeef->pl_unlock (); + trace ("leave pl_insert_m3u\n"); + return after; +} + +static DB_playItem_t * +pls_insert_file (DB_playItem_t *after, const char *fname, const char *uri, int *pabort, int (*cb)(DB_playItem_t *it, void *data), void *user_data) { + DB_playItem_t *it = NULL; + const char *slash = NULL; + + if (strrchr (uri, '/')) { + trace ("pls: adding file %s\n", uri); + it = deadbeef->pl_insert_file (after, uri, pabort, cb, user_data); + } + else if (slash = strrchr (fname, '/')) { + int l = strlen (uri); + char fullpath[slash - fname + l + 2]; + memcpy (fullpath, fname, slash - fname + 1); + strcpy (fullpath + (slash - fname + 1), uri); + trace ("pl_insert_m3u: adding file %s\n", fullpath); + it = deadbeef->pl_insert_file (after, fullpath, pabort, cb, user_data); + } + return it; +} + +static DB_playItem_t * +load_pls (DB_playItem_t *after, const char *fname, int *pabort, int (*cb)(DB_playItem_t *it, void *data), void *user_data) { + const char *slash = strrchr (fname, '/'); + DB_FILE *fp = deadbeef->fopen (fname); + if (!fp) { + trace ("failed to open file %s\n", fname); + return NULL; + } + int sz = deadbeef->fgetlength (fp); + if (sz > 1024*1024) { + deadbeef->fclose (fp); + trace ("file %s is too large to be a playlist\n", fname); + return NULL; + } + if (sz < 30) { + deadbeef->fclose (fp); + trace ("file %s is too small to be a playlist (%d)\n", fname, sz); + return NULL; + } + deadbeef->rewind (fp); + uint8_t buffer[sz]; + deadbeef->fread (buffer, 1, sz, fp); + deadbeef->fclose (fp); + // 1st line must be "[playlist]" + const uint8_t *p = buffer; + const uint8_t *end = buffer+sz; + if (strncasecmp (p, "[playlist]", 10)) { + trace ("file %s doesn't begin with [playlist]\n", fname); + return NULL; + } + p += 10; + p = skipspaces (p, end); + if (p >= end) { + trace ("file %s finished before numberofentries had been read\n", fname); + return NULL; + } + if (strncasecmp (p, "numberofentries=", 16)) { + trace ("can't get number of entries from %s\n", fname); + return NULL; + } + p += 15; + // ignore numentries - no real need for it here + while (p < end && *p > 0x20) { + p++; + } + p = skipspaces (p, end); + // fetch all tracks + char uri[1024] = ""; + char title[1024] = ""; + char length[20] = ""; + deadbeef->pl_lock (); + while (p < end) { + p = skipspaces (p, end); + if (p >= end) { + break; + } + if (end-p < 6) { + break; + } + const uint8_t *e; + int n; + if (!strncasecmp (p, "file", 4)) { + if (uri[0]) { + DB_playItem_t *it = pls_insert_file (after, fname, uri, pabort, cb, user_data); + if (it) { + after = it; + deadbeef->pl_set_item_duration (it, atoi (length)); + if (title[0]) { + deadbeef->pl_add_meta (it, "title", title); + } + } + if (pabort && *pabort) { + deadbeef->pl_unlock (); + return after; + } + uri[0] = 0; + title[0] = 0; + length[0] = 0; + } + p += 4; + while (p < end && *p != '=') { + p++; + } + p++; + if (p >= end) { + break; + } + e = p; + while (e < end && *e >= 0x20) { + e++; + } + n = e-p; + n = min (n, sizeof (uri)-1); + memcpy (uri, p, n); + uri[n] = 0; + trace ("uri: %s\n", uri); + p = ++e; + } + else if (!strncasecmp (p, "title", 5)) { + p += 5; + while (p < end && *p != '=') { + p++; + } + p++; + if (p >= end) { + break; + } + e = p; + while (e < end && *e >= 0x20) { + e++; + } + n = e-p; + n = min (n, sizeof (title)-1); + memcpy (title, p, n); + title[n] = 0; + trace ("title: %s\n", title); + p = ++e; + } + else if (!strncasecmp (p, "length", 6)) { + p += 6; + // skip = + while (p < end && *p != '=') { + p++; + } + p++; + if (p >= end) { + break; + } + e = p; + while (e < end && *e >= 0x20) { + e++; + } + n = e-p; + n = min (n, sizeof (length)-1); + memcpy (length, p, n); + break; + } + else { + trace ("invalid entry in pls file: %s\n", p); + break; + } + while (e < end && *e < 0x20) { + e++; + } + p = e; + } + if (uri[0]) { + DB_playItem_t *it = pls_insert_file (after, fname, uri, pabort, cb, user_data); + if (it) { + after = it; + deadbeef->pl_set_item_duration (it, atoi (length)); + if (title[0]) { + deadbeef->pl_add_meta (it, "title", title); + } + } + } + deadbeef->pl_unlock (); + return after; +} + +static DB_playItem_t * +m3uplug_load (DB_playItem_t *after, const char *fname, int *pabort, int (*cb)(DB_playItem_t *it, void *data), void *user_data) { + const char *ext = strrchr (fname, '.'); + if (!ext) { + return NULL; + } + ext++; + + if (!strcasecmp (ext, "m3u")) { + return load_m3u (after, fname, pabort, cb, user_data); + } + else if (!strcasecmp (ext, "pls")) { + return load_pls (after, fname, pabort, cb, user_data); + } + + return NULL; +} + +static const char * exts[] = { "m3u", "pls", NULL }; +DB_playlist_t plugin = { + DB_PLUGIN_SET_API_VERSION + .plugin.version_major = 1, + .plugin.version_minor = 0, + .plugin.type = DB_PLUGIN_PLAYLIST, + .plugin.id = "m3u", + .plugin.name = "M3U and PLS playlist loader", + .plugin.descr = "Imports playlists from M3U and PLS formats", + .plugin.author = "Alexey Yakovenko", + .plugin.email = "waker@users.sourceforge.net", + .plugin.website = "http://deadbeef.sf.net", + .load = m3uplug_load, + .extensions = exts, +}; + +DB_plugin_t * +m3u_load (DB_functions_t *api) { + deadbeef = api; + return &plugin.plugin; +} -- cgit v1.2.3