diff --git a/src/Makefile.am b/src/Makefile.am
index 7cefbf6e..e2385f07 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -36,7 +36,7 @@ mt_daapd_SOURCES = main.c daapd.h rend.h uici.c uici.h webserver.c \
mp3-scanner.h mp3-scanner.c rend-unix.h strcasestr.c strcasestr.h \
strsep.c dynamic-art.c dynamic-art.h query.c query.h ssc.c ssc.h \
db-generic.c db-generic.h dispatch.c dispatch.h wma.c \
- scan-xml.c rxml.c rxml.h \
+ scan-xml.c rxml.c rxml.h redblack.c redblack.h \
$(PRENDSRC) $(ORENDSRC) $(HRENDSRC) $(OGGVORBISSRC) $(FLACSRC) \
$(SQLITEDB)
diff --git a/src/db-generic.c b/src/db-generic.c
index a2bdab61..0e11b747 100644
--- a/src/db-generic.c
+++ b/src/db-generic.c
@@ -46,7 +46,7 @@ typedef struct tag_db_functions {
int(*dbs_init)(int);
int(*dbs_deinit)(void);
int(*dbs_add)(MP3FILE*);
- int(*dbs_add_playlist)(char *, int, char *,char *, int *);
+ int(*dbs_add_playlist)(char *, int, char *,char *, int, int *);
int(*dbs_add_playlist_item)(int, int);
int(*dbs_delete_playlist)(int);
int(*dbs_delete_playlist_item)(int, int);
@@ -480,11 +480,11 @@ int db_add(MP3FILE *pmp3) {
* \param playlistid returns the id of the playlist created
* \returns 0 on success, error code otherwise
*/
-int db_add_playlist(char *name, int type, char *clause, char *path, int *playlistid) {
+int db_add_playlist(char *name, int type, char *clause, char *path, int index, int *playlistid) {
int retval;
db_writelock();
- retval=db_current->dbs_add_playlist(name,type,clause,path,playlistid);
+ retval=db_current->dbs_add_playlist(name,type,clause,path,index,playlistid);
if(retval == DB_E_SUCCESS)
db_revision_no++;
db_unlock();
diff --git a/src/db-generic.h b/src/db-generic.h
index db047c28..a675d876 100644
--- a/src/db-generic.h
+++ b/src/db-generic.h
@@ -156,7 +156,7 @@ extern int db_end_scan(void);
extern int db_exists(char *path);
extern int db_scanning(void);
-extern int db_add_playlist(char *name, int type, char *clause, char *path,int *playlistid);
+extern int db_add_playlist(char *name, int type, char *clause, char *path, int index, int *playlistid);
extern int db_add_playlist_item(int playlistid, int songid);
extern int db_delete_playlist(int playlistid);
extern int db_delete_playlist_item(int playlistid, int songid);
diff --git a/src/dbs-sqlite.c b/src/dbs-sqlite.c
index 82a6cef9..a7080d9b 100644
--- a/src/dbs-sqlite.c
+++ b/src/dbs-sqlite.c
@@ -405,7 +405,7 @@ extern int db_sqlite_delete_playlist_item(int playlistid, int songid) {
* \param type playlist type: 0 - static, 1 - smart, 2 - m3u
* \param clause: "where" clause for smart playlist
*/
-int db_sqlite_add_playlist(char *name, int type, char *clause, char *path, int *playlistid) {
+int db_sqlite_add_playlist(char *name, int type, char *clause, char *path, int index, int *playlistid) {
int cnt=0;
int result=DB_E_SUCCESS;
@@ -419,16 +419,17 @@ int db_sqlite_add_playlist(char *name, int type, char *clause, char *path, int *
switch(type) {
case PL_STATICWEB: /* static, maintained in web interface */
case PL_STATICFILE: /* static, from file */
+ case PL_STATICXML: /* from iTunes XML file */
result = db_sqlite_exec(E_LOG,"insert into playlists "
- "(title,type,items,query,db_timestamp,path) "
- "values ('%q',%d,0,NULL,%d,'%q')",name,type,time(NULL),path);
+ "(title,type,items,query,db_timestamp,path,idx) "
+ "values ('%q',%d,0,NULL,%d,'%q',%d)",name,type,time(NULL),path,index);
break;
case PL_SMART: /* smart */
result=db_sqlite_get_int(E_DBG,&cnt,"select count (*) from songs where %s",clause);
if(result != DB_E_SUCCESS) return result;
result = db_sqlite_exec(E_LOG,"insert into playlists "
- "(title,type,items,query,db_timestamp) "
- "values ('%q',%d,%d,'%q',%d)",name,PL_SMART,cnt,clause,time(NULL));
+ "(title,type,items,query,db_timestamp,idx) "
+ "values ('%q',%d,%d,'%q',%d,0)",name,PL_SMART,cnt,clause,time(NULL));
break;
}
@@ -439,7 +440,7 @@ int db_sqlite_add_playlist(char *name, int type, char *clause, char *path, int *
"select id from playlists where title='%q'", name);
if(((type==PL_STATICFILE)||(type==PL_STATICXML))
- && (db_sqlite_in_scan) && (!db_sqlite_reload)) {
+ && (db_sqlite_in_playlist_scan) && (!db_sqlite_reload)) {
db_sqlite_exec(E_FATAL,"insert into plupdated values (%d)",*playlistid);
}
@@ -1307,7 +1308,7 @@ void db_sqlite_build_m3ufile(char **valarray, M3UFILE *pm3u) {
pm3u->query=db_sqlite_strdup(valarray[4]);
pm3u->db_timestamp=db_sqlite_atoi(valarray[5]);
pm3u->path=db_sqlite_strdup(valarray[6]);
- pm3u->index=0; //db_sqlite_atoi(valarray[7]);
+ pm3u->index=db_sqlite_atoi(valarray[7]);
return;
}
void db_sqlite_build_mp3file(char **valarray, MP3FILE *pmp3) {
@@ -1364,7 +1365,8 @@ M3UFILE *db_sqlite_fetch_playlist(char *path, int index) {
M3UFILE *pm3u=NULL;
result = db_sqlite_get_table(E_DBG,&resarray,&rows,&cols,
- "select * from playlists where path='%q'",path);
+ "select * from playlists where path='%q' and idx=%d",
+ path,index);
if(result != DB_E_SUCCESS)
return NULL;
@@ -1372,7 +1374,7 @@ M3UFILE *db_sqlite_fetch_playlist(char *path, int index) {
if(rows != 0) {
pm3u=(M3UFILE*)malloc(sizeof(M3UFILE));
if(!pm3u)
- DPRINTF(E_FATAL,L_MISC,"malloc error in db_sqlite_fetch_playlist\n");
+ DPRINTF(E_FATAL,L_MISC,"malloc error: db_sqlite_fetch_playlist\n");
db_sqlite_build_m3ufile((char**)&resarray[cols],pm3u);
}
@@ -1396,7 +1398,8 @@ MP3FILE *db_sqlite_fetch_item(int id) {
char **resarray;
MP3FILE *pmp3=NULL;
- db_sqlite_get_table(E_DBG,&resarray,&rows,&cols,"SELECT * FROM songs WHERE id=%d",id);
+ db_sqlite_get_table(E_DBG,&resarray,&rows,&cols,
+ "SELECT * FROM songs WHERE id=%d",id);
if(rows != 0) {
pmp3=(MP3FILE*)malloc(sizeof(MP3FILE));
@@ -1425,7 +1428,8 @@ MP3FILE *db_sqlite_fetch_path(char *path) {
char **resarray;
MP3FILE *pmp3=NULL;
- db_sqlite_get_table(E_DBG,&resarray,&rows,&cols,"SELECT * FROM songs WHERE path='%q'",path);
+ db_sqlite_get_table(E_DBG,&resarray,&rows,&cols,
+ "SELECT * FROM songs WHERE path='%q'",path);
if(rows != 0) {
pmp3=(MP3FILE*)malloc(sizeof(MP3FILE));
@@ -1505,7 +1509,8 @@ int db_sqlite_get_count(CountType_t type) {
break;
}
- db_sqlite_get_table(E_DBG,&resarray, &rows, &cols,"SELECT COUNT(*) FROM %q", table);
+ db_sqlite_get_table(E_DBG,&resarray, &rows, &cols,
+ "SELECT COUNT(*) FROM %q", table);
if(rows != 0) {
retval=atoi(resarray[cols]);
@@ -1683,6 +1688,25 @@ char *db_sqlite_upgrade_scripts[] = {
"insert into playlists select *,0,NULL from tempplaylists;\n"
"drop table tempplaylists;\n"
"update config set value=4 where term='version';\n",
+
+ /* version 4 -> version 5 */
+ /* add index to playlist table */
+ "create temp table tempplaylists as select * from playlists;\n"
+ "drop table playlists;\n"
+ "CREATE TABLE playlists (\n"
+ " id INTEGER PRIMARY KEY NOT NULL,\n"
+ " title VARCHAR(255) NOT NULL,\n"
+ " type INTEGER NOT NULL,\n"
+ " items INTEGER NOT NULL,\n"
+ " query VARCHAR(1024),\n"
+ " db_timestamp INTEGER NOT NULL,\n"
+ " path VARCHAR(4096),\n"
+ " idx INTEGER NOT NULL\n"
+ ");\n"
+ "insert into playlists select *,0 from tempplaylists;\n"
+ "drop table tempplaylists;\n"
+ "update config set value=5 where term='version';\n",
+
NULL /* No more versions! */
};
@@ -1712,13 +1736,15 @@ int db_sqlite_update_version(int from_version) {
to_fd=r_open3(db_new_path,O_RDWR | O_CREAT,0666);
if((from_fd == -1) || (to_fd == -1)) {
- DPRINTF(E_FATAL,L_DB,"Could not make backup copy of database (%s). Check write permissions for runas user.\n",db_new_path);
+ DPRINTF(E_FATAL,L_DB,"Could not make backup copy of database "
+ "(%s). Check write permissions for runas user.\n",
+ db_new_path);
}
while((result=readwrite(from_fd, to_fd) > 0));
if(result == -1) {
- DPRINTF(E_FATAL,L_DB,"Could not make backup copy of database (%s)\n",
+ DPRINTF(E_FATAL,L_DB,"Could not make db backup (%s)\n",
strerror(errno));
}
@@ -1728,10 +1754,11 @@ int db_sqlite_update_version(int from_version) {
copied=1;
}
- if(db_sqlite_exec(E_LOG,db_sqlite_upgrade_scripts[from_version])!=DB_E_SUCCESS) {
- DPRINTF(E_FATAL,L_DB,"Error upgrading database. A backup copy of your "
- "original database is located at %s. Please save it somewhere "
- "and report to the forums at www.mt-daapd.org. Thanks.\n",
+ if(db_sqlite_exec(E_LOG,db_sqlite_upgrade_scripts[from_version]) != DB_E_SUCCESS) {
+ DPRINTF(E_FATAL,L_DB,"Error upgrading database. A backup copy of "
+ "you original database is located at %s. Please save it "
+ " somewhere and report to the forums at www.mt-daapd.org. "
+ " Thanks.\n",
db_new_path);
}
from_version++;
diff --git a/src/dbs-sqlite.h b/src/dbs-sqlite.h
index b8f7e634..e1802072 100644
--- a/src/dbs-sqlite.h
+++ b/src/dbs-sqlite.h
@@ -40,7 +40,7 @@ extern MP3FILE *db_sqlite_fetch_path(char *path);
extern M3UFILE *db_sqlite_fetch_playlist(char *path, int index);
extern void db_sqlite_dispose_item(MP3FILE *pmp3);
extern void db_sqlite_dispose_playlist(M3UFILE *pm3u);
-extern int db_sqlite_add_playlist(char *name, int type, char *clause, char *path, int *playlistid);
+extern int db_sqlite_add_playlist(char *name, int type, char *clause, char *path, int index, int *playlistid);
extern int db_sqlite_add_playlist_item(int playlistid, int songid);
extern int db_sqlite_delete_playlist(int playlistid);
extern int db_sqlite_delete_playlist_item(int playlistid, int songid);
diff --git a/src/dispatch.c b/src/dispatch.c
index 8db3abed..1edb2b55 100644
--- a/src/dispatch.c
+++ b/src/dispatch.c
@@ -924,7 +924,7 @@ void dispatch_addplaylist(WS_CONNINFO *pwsc, DBQUERYINFO *pqi) {
name=ws_getvar(pwsc,"dmap.itemname");
query=ws_getvar(pwsc,"org.mt-daapd.smart-playlist-spec");
- retval=db_add_playlist(name,type,query,NULL,&playlistid);
+ retval=db_add_playlist(name,type,query,NULL,0,&playlistid);
if(retval != DB_E_SUCCESS) {
DPRINTF(E_LOG,L_DAAP,"error adding playlist. aborting\n");
ws_returnerror(pwsc,500,"error adding playlist");
diff --git a/src/main.c b/src/main.c
index 01cee9ae..6f08ca90 100644
--- a/src/main.c
+++ b/src/main.c
@@ -421,6 +421,8 @@ int main(int argc, char *argv[]) {
exit(EXIT_FAILURE);
}
+ DPRINTF(E_LOG,"Starting with debuglevel %d\n",err_debuglevel);
+
if(!foreground) {
if(config.logfile) {
err_setdest(config.logfile,LOGDEST_LOGFILE);
diff --git a/src/mp3-scanner.c b/src/mp3-scanner.c
index ed3ced66..a82111d5 100644
--- a/src/mp3-scanner.c
+++ b/src/mp3-scanner.c
@@ -326,17 +326,18 @@ int scan_xml_playlist(char *filename);
* way we currently are: using extension or whatver.
*
* This means that you can test to see if something is, say, an un-drmed
- * aac file by just testing for ->type "m4a", rather than checking every different
- * flavor of file extension.
+ * aac file by just testing for ->type "m4a", rather than checking every
+ * different flavor of file extension.
*
- * NOTE: Although they are represented here as strings, the codectype is *really*
- * an unsigned short. So when it gets serialized, it gets serialized as a short int.
- * If you put something other than 3 or 4 characters as your codectype, you'll see
- * strange results.
+ * NOTE: Although they are represented here as strings, the codectype is
+ * *really* an unsigned short. So when it gets serialized, it gets
+ * serialized as a short int. If you put something other than 3 or 4
+ * characters as your codectype, you'll see strange results.
*
* FIXME: url != pls -- this method of dispatching handlers based on file type
- * is completely wrong. There needs to be a separate type that gets carried around
- * with it, at least outside the database that says where the info CAME FROM.
+ * is completely wrong. There needs to be a separate type that gets carried
+ * around with it, at least outside the database that says where the info
+ * CAME FROM.
*
* This system is broken, and won't work with something like a .cue file
*/
@@ -481,7 +482,7 @@ int scan_init(char *path) {
*/
int scan_path(char *path) {
DIR *current_dir;
- char de[sizeof(struct dirent) + MAXNAMLEN + 1]; /* overcommit for solaris */
+ char de[sizeof(struct dirent) + MAXNAMLEN + 1]; /* extra for solaris */
struct dirent *pde;
int err;
char relative_path[PATH_MAX];
@@ -498,7 +499,7 @@ int scan_path(char *path) {
while(1) {
if(config.stop) {
- DPRINTF(E_WARN,L_SCAN,"Stop detected. Aborting scan of %s.\n",path);
+ DPRINTF(E_WARN,L_SCAN,"Stop req. Aborting scan of %s.\n",path);
closedir(current_dir);
return 0;
}
@@ -610,7 +611,7 @@ void scan_static_playlist(char *path) {
fd=open(path,O_RDONLY);
if(fd != -1) {
- if(db_add_playlist(base_path,PL_STATICFILE,NULL,path,&playlistid) != DB_E_SUCCESS) {
+ if(db_add_playlist(base_path,PL_STATICFILE,NULL,path,0,&playlistid) != DB_E_SUCCESS) {
DPRINTF(E_LOG,L_SCAN,"Error adding m3u playlist %s\n",path);
db_dispose_playlist(pm3u);
return;
diff --git a/src/scan-xml.c b/src/scan-xml.c
index f6d42608..b2130d9b 100644
--- a/src/scan-xml.c
+++ b/src/scan-xml.c
@@ -33,6 +33,7 @@
#include "err.h"
#include "mp3-scanner.h"
#include "rxml.h"
+#include "redblack.h"
/* Forwards */
int scan_xml_playlist(char *filename);
@@ -40,18 +41,21 @@ void scan_xml_handler(int action,void* puser,char* info);
int scan_xml_preamble_section(int action,char *info);
int scan_xml_tracks_section(int action,char *info);
int scan_xml_playlists_section(int action,char *info);
+void scan_xml_add_lookup(int itunes_index, int mtd_index);
/* Globals */
static char *scan_xml_itunes_version = NULL;
static char *scan_xml_itunes_base_path = NULL;
static char *scan_xml_itunes_decoded_base_path = NULL;
static char *scan_xml_real_base_path = NULL;
+static char *scan_xml_file; /** < The actual file we are scanning */
+static struct rbtree *scan_xml_db;
#define MAYBECOPY(a) if(mp3.a) pmp3->a = mp3.a
#define MAYBECOPYSTRING(a) if(mp3.a) { free(pmp3->a); pmp3->a = mp3.a; }
-
#define MAYBEFREE(a) if((a)) { free((a)); (a)=NULL; }
+/** iTunes xml values we are interested in */
static char *scan_xml_track_tags[] = {
"Name",
"Artist",
@@ -73,6 +77,7 @@ static char *scan_xml_track_tags[] = {
NULL
};
+/** Indexes to the iTunes xml fields we are interested in */
#define SCAN_XML_T_UNKNOWN -1
#define SCAN_XML_T_NAME 0
#define SCAN_XML_T_ARTIST 1
@@ -92,6 +97,82 @@ static char *scan_xml_track_tags[] = {
#define SCAN_XML_T_COMPILATION 15
#define SCAN_XML_T_LOCATION 16
+
+#ifndef TRUE
+# define TRUE 1
+# define FALSE 0
+#endif
+
+typedef struct scan_xml_rb_t {
+ int itunes_index;
+ int mtd_index;
+} SCAN_XML_RB;
+
+/**
+ * comparison for the red-black tree. @see redblack.c
+ *
+ * @param pa one node to compare
+ * @param pb other node to compare
+ * @param cfg I have no idea
+ */
+int scan_xml_rb_compare(const void *pa, const void *pb, const void *cfg) {
+ if(((SCAN_XML_RB*)pa)->itunes_index < ((SCAN_XML_RB*)pb)->itunes_index)
+ return -1;
+ if(((SCAN_XML_RB*)pb)->itunes_index < ((SCAN_XML_RB*)pa)->itunes_index)
+ return 1;
+ return 0;
+}
+
+/**
+ * add a mapping from iTunes song index to the mt-daapd song
+ * index. This is so we can add resolve mt-daapd song indexes
+ * when it comes time to build the iTunes playlist
+ *
+ * @param itunes_index the index from the itunes xml file
+ * @param mtd_index the index from db_fetch_path
+ */
+void scan_xml_add_lookup(int itunes_index, int mtd_index) {
+ SCAN_XML_RB *pnew;
+ const void *val;
+
+ pnew=(SCAN_XML_RB*)malloc(sizeof(SCAN_XML_RB));
+ if(!pnew)
+ DPRINTF(E_FATAL,L_SCAN,"malloc error in scan_xml_add_lookup\n");
+
+ pnew->itunes_index = itunes_index;
+ pnew->mtd_index = mtd_index;
+
+ val = rbsearch((const void*)pnew,scan_xml_db);
+ if(!val) {
+ /* couldn't alloc the rb tree structure -- if we don't
+ * die now, we are going to soon enough*/
+ DPRINTF(E_FATAL,L_SCAN,"redblack tree insert error\n");
+ }
+}
+
+/**
+ * Find the mt-daapd index that corresponds with a particular
+ * itunes song id
+ *
+ * @param itunes_index index from the iTunes xml file
+ * @returns the mt-daapd index
+ */
+int scan_xml_get_index(int itunes_index, int *mtd_index) {
+ SCAN_XML_RB rb;
+ SCAN_XML_RB *prb;
+
+ rb.itunes_index = itunes_index;
+ prb = (SCAN_XML_RB*) rbfind((void*)&rb,scan_xml_db);
+ if(prb) {
+ *mtd_index = prb->mtd_index;
+ DPRINTF(E_SPAM,L_SCAN,"Matching %d to %d\n",itunes_index,*mtd_index);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
/**
* get the tag index of a particular tag
*
@@ -177,6 +258,10 @@ char *scan_xml_urldecode(char *string, int space_as_plus) {
*/
int scan_xml_playlist(char *filename) {
char *working_base;
+ const void *val;
+ SCAN_XML_RB *lookup_ptr;
+ SCAN_XML_RB lookup_val;
+
RXMLHANDLE xml_handle;
MAYBEFREE(scan_xml_itunes_version);
@@ -184,6 +269,13 @@ int scan_xml_playlist(char *filename) {
MAYBEFREE(scan_xml_itunes_decoded_base_path);
MAYBEFREE(scan_xml_real_base_path);
+ scan_xml_file = filename;
+
+ /* initialize the redblack tree */
+ if((scan_xml_db = rbinit(scan_xml_rb_compare,NULL)) == NULL) {
+ DPRINTF(E_LOG,L_SCAN,"Could not initialize red/black tree\n");
+ return 0;
+ }
/* find the base dir of the itunes playlist itself */
working_base = strdup(filename);
@@ -208,6 +300,18 @@ int scan_xml_playlist(char *filename) {
}
rxml_close(xml_handle);
+
+ /* destroy the redblack tree */
+ val = rblookup(RB_LUFIRST,NULL,scan_xml_db);
+ while(val) {
+ lookup_val.itunes_index = ((SCAN_XML_RB*)val)->itunes_index;
+ lookup_ptr = (SCAN_XML_RB *)rbdelete((void*)&lookup_val,scan_xml_db);
+ if(lookup_ptr)
+ free(lookup_ptr);
+ val = rblookup(RB_LUFIRST,NULL,scan_xml_db);
+ }
+
+ rbdestroy(scan_xml_db);
return 0;
}
@@ -261,10 +365,11 @@ void scan_xml_handler(int action,void* puser,char* info) {
}
}
-#define SCAN_XML_PRE_NOTHING 0
-#define SCAN_XML_PRE_VERSION 1
-#define SCAN_XML_PRE_PATH 2
-#define SCAN_XML_PRE_DONE 3
+#define SCAN_XML_PRE_NOTHING 0
+#define SCAN_XML_PRE_VERSION 1
+#define SCAN_XML_PRE_PATH 2
+#define SCAN_XML_PRE_TRACKS 3
+#define SCAN_XML_PRE_PLAYLISTS 4
/**
* collect preamble data... version, library id, etc.
@@ -283,8 +388,16 @@ int scan_xml_preamble_section(int action, char *info) {
break;
case RXML_EVT_END:
- if(expecting_next == SCAN_XML_PRE_DONE) /* end of tracks tag */
+ if(expecting_next == SCAN_XML_PRE_TRACKS) { /* end of tracks tag */
+ expecting_next=0;
+ DPRINTF(E_DBG,L_SCAN,"Scanning tracks\n");
return XML_STATE_TRACKS;
+ }
+ if(expecting_next == SCAN_XML_PRE_PLAYLISTS) {
+ expecting_next=0;
+ DPRINTF(E_DBG,L_SCAN,"Scanning playlists\n");
+ return XML_STATE_PLAYLISTS;
+ }
break;
case RXML_EVT_TEXT: /* scan for the tags we expect */
@@ -294,7 +407,9 @@ int scan_xml_preamble_section(int action, char *info) {
} else if (strcmp(info,"Music Folder") == 0) {
expecting_next = SCAN_XML_PRE_PATH;
} else if (strcmp(info,"Tracks") == 0) {
- expecting_next = SCAN_XML_PRE_DONE;
+ expecting_next = SCAN_XML_PRE_TRACKS;
+ } else if (strcmp(info,"Playlists") == 0) {
+ expecting_next = SCAN_XML_PRE_PLAYLISTS;
}
} else {
/* we were expecting someting! */
@@ -333,7 +448,6 @@ int scan_xml_preamble_section(int action, char *info) {
#define XML_TRACK_ST_EXPECTING_TRACK_DICT 3
#define XML_TRACK_ST_TRACK_INFO 4
#define XML_TRACK_ST_TRACK_DATA 5
-#define XML_TRACK_ST_EXPECTING_PLAYLISTS 6
/**
* collect track data for each track in the itunes playlist
@@ -341,10 +455,9 @@ int scan_xml_preamble_section(int action, char *info) {
* @param action xml action (RXML_EVT_TEXT, etc)
* @param info text data associated with event
*/
-#define MAYBESETSTATE(a,b,c) { if((action==(a)) && \
+#define MAYBESETSTATE_TR(a,b,c) { if((action==(a)) && \
(strcmp(info,(b)) == 0)) { \
state = (c); \
- DPRINTF(E_SPAM,L_SCAN,"New state: %d\n",state); \
return XML_STATE_TRACKS; \
}}
@@ -353,7 +466,7 @@ int scan_xml_tracks_section(int action, char *info) {
static int current_track_id;
static int current_field;
static MP3FILE mp3;
- static char *song_path;
+ static char *song_path=NULL;
char physical_path[PATH_MAX];
char real_path[PATH_MAX];
MP3FILE *pmp3;
@@ -369,21 +482,23 @@ int scan_xml_tracks_section(int action, char *info) {
switch(state) {
case XML_TRACK_ST_INITIAL:
/* expection only a */
- MAYBESETSTATE(RXML_EVT_BEGIN,"dict",XML_TRACK_ST_MAIN_DICT);
+ MAYBESETSTATE_TR(RXML_EVT_BEGIN,"dict",XML_TRACK_ST_MAIN_DICT);
return XML_STATE_ERROR;
break;
case XML_TRACK_ST_MAIN_DICT:
/* either get a , or a */
- MAYBESETSTATE(RXML_EVT_BEGIN,"key",XML_TRACK_ST_EXPECTING_TRACK_ID);
- MAYBESETSTATE(RXML_EVT_END,"dict",XML_TRACK_ST_EXPECTING_PLAYLISTS);
+ MAYBESETSTATE_TR(RXML_EVT_BEGIN,"key",XML_TRACK_ST_EXPECTING_TRACK_ID);
+ if ((action == RXML_EVT_END) && (strcasecmp(info,"dict") == 0)) {
+ return XML_STATE_PREAMBLE;
+ }
return XML_STATE_ERROR;
break;
case XML_TRACK_ST_EXPECTING_TRACK_ID:
/* this is somewhat loose - id */
- MAYBESETSTATE(RXML_EVT_BEGIN,"key",XML_TRACK_ST_EXPECTING_TRACK_ID);
- MAYBESETSTATE(RXML_EVT_END,"key",XML_TRACK_ST_EXPECTING_TRACK_DICT);
+ MAYBESETSTATE_TR(RXML_EVT_BEGIN,"key",XML_TRACK_ST_EXPECTING_TRACK_ID);
+ MAYBESETSTATE_TR(RXML_EVT_END,"key",XML_TRACK_ST_EXPECTING_TRACK_DICT);
if (action == RXML_EVT_TEXT) {
current_track_id = atoi(info);
DPRINTF(E_DBG,L_SCAN,"Scanning iTunes id #%d\n",current_track_id);
@@ -394,14 +509,14 @@ int scan_xml_tracks_section(int action, char *info) {
case XML_TRACK_ST_EXPECTING_TRACK_DICT:
/* waiting for a dict */
- MAYBESETSTATE(RXML_EVT_BEGIN,"dict",XML_TRACK_ST_TRACK_INFO);
+ MAYBESETSTATE_TR(RXML_EVT_BEGIN,"dict",XML_TRACK_ST_TRACK_INFO);
return XML_STATE_ERROR;
break;
case XML_TRACK_ST_TRACK_INFO:
/* again, kind of loose */
- MAYBESETSTATE(RXML_EVT_BEGIN,"key",XML_TRACK_ST_TRACK_INFO);
- MAYBESETSTATE(RXML_EVT_END,"key",XML_TRACK_ST_TRACK_DATA);
+ MAYBESETSTATE_TR(RXML_EVT_BEGIN,"key",XML_TRACK_ST_TRACK_INFO);
+ MAYBESETSTATE_TR(RXML_EVT_END,"key",XML_TRACK_ST_TRACK_DATA);
if(action == RXML_EVT_TEXT) {
current_field=scan_xml_get_tagindex(info);
if(current_field == SCAN_XML_T_DISABLED) {
@@ -438,6 +553,9 @@ int scan_xml_tracks_section(int action, char *info) {
MAYBECOPY(disc);
MAYBECOPY(total_discs);
+ /* must add to the red-black tree */
+ scan_xml_add_lookup(current_track_id,pmp3->id);
+
db_add(pmp3);
db_dispose_item(pmp3);
@@ -498,6 +616,23 @@ int scan_xml_tracks_section(int action, char *info) {
return XML_STATE_TRACKS;
}
+
+#define XML_PL_ST_INITIAL 0
+#define XML_PL_ST_EXPECTING_PL 1
+#define XML_PL_ST_EXPECTING_PL_DATA 2
+#define XML_PL_ST_EXPECTING_PL_VALUE 3
+#define XML_PL_ST_EXPECTING_PL_TRACKLIST 4
+
+#define XML_PL_NEXT_VALUE_NONE 0
+#define XML_PL_NEXT_VALUE_NAME 1
+#define XML_PL_NEXT_VALUE_ID 2
+
+#define MAYBESETSTATE_PL(a,b,c) { if((action==(a)) && \
+ (strcmp(info,(b)) == 0)) { \
+ state = (c); \
+ return XML_STATE_PLAYLISTS; \
+ }}
+
/**
* collect playlist data for each playlist in the itunes xml file
*
@@ -505,6 +640,111 @@ int scan_xml_tracks_section(int action, char *info) {
* @param info text data associated with event
*/
int scan_xml_playlists_section(int action, char *info) {
+ static int state = XML_PL_ST_INITIAL;
+ static int next_value=0; /** < what's next song info id or name */
+ static int native_plid=0; /** < the iTunes playlist id */
+ static int current_id=0; /** < the mt-daapd playlist id */
+ static char *current_name=NULL; /** < the iTunes playlist name */
+ int native_track_id; /** < the iTunes id of the track */
+ int track_id; /** < the mt-daapd track id */
+ M3UFILE *pm3u;
+
+ /* do initialization */
+ if(action == RXML_EVT_OPEN) {
+ state = XML_PL_ST_INITIAL;
+ if(current_name)
+ free(current_name);
+ current_name = NULL;
+ return 0;
+ }
+
+ switch(state) {
+ case XML_PL_ST_INITIAL:
+ /* expecting or error */
+ MAYBESETSTATE_PL(RXML_EVT_BEGIN,"array",XML_PL_ST_EXPECTING_PL);
+ return XML_STATE_ERROR;
+ case XML_PL_ST_EXPECTING_PL:
+ /* either a new playlist, or end of playlist list */
+ MAYBESETSTATE_PL(RXML_EVT_BEGIN,"dict",XML_PL_ST_EXPECTING_PL_DATA);
+ if((action == RXML_EVT_END) && (strcasecmp(info,"array") == 0))
+ return XML_STATE_PREAMBLE;
+ return XML_STATE_ERROR;
+ case XML_PL_ST_EXPECTING_PL_DATA:
+ /* either a key/data pair, or an array, signaling start of playlist
+ * or the end of the dict (end of playlist data) */
+ MAYBESETSTATE_PL(RXML_EVT_BEGIN,"key",XML_PL_ST_EXPECTING_PL_DATA);
+ MAYBESETSTATE_PL(RXML_EVT_END,"key",XML_PL_ST_EXPECTING_PL_VALUE);
+ MAYBESETSTATE_PL(RXML_EVT_END,"dict",XML_PL_ST_EXPECTING_PL);
+ if(action == RXML_EVT_TEXT) {
+ next_value=XML_PL_NEXT_VALUE_NONE;
+ if(strcasecmp(info,"Name") == 0) {
+ next_value = XML_PL_NEXT_VALUE_NAME;
+ } else if(strcasecmp(info,"Playlist ID") == 0) {
+ next_value = XML_PL_NEXT_VALUE_ID;
+ }
+ return XML_STATE_PLAYLISTS;
+ }
+ return XML_STATE_ERROR;
+ case XML_PL_ST_EXPECTING_PL_VALUE:
+ /* any tag, value we are looking for, any close tag */
+ if((action == RXML_EVT_BEGIN) && (strcasecmp(info,"array") == 0)) {
+ /* we are about to get track list... must register the playlist */
+ DPRINTF(E_DBG,L_SCAN,"Creating playlist for %s\n",current_name);
+ /* delete the old one first */
+ pm3u = db_fetch_playlist(scan_xml_file,native_plid);
+ if(pm3u) {
+ db_delete_playlist(pm3u->id);
+ db_dispose_playlist(pm3u);
+ }
+ if(db_add_playlist(current_name,PL_STATICXML,NULL,scan_xml_file,
+ native_plid,¤t_id) != DB_E_SUCCESS) {
+ DPRINTF(E_LOG,L_SCAN,"err adding playlist %s\n",current_name);
+ current_id=0;
+ }
+ state=XML_PL_ST_EXPECTING_PL_TRACKLIST;
+ return XML_STATE_PLAYLISTS;
+ }
+ if(action == RXML_EVT_BEGIN)
+ return XML_STATE_PLAYLISTS;
+ if(action == RXML_EVT_END) {
+ state = XML_PL_ST_EXPECTING_PL_DATA;
+ return XML_STATE_PLAYLISTS;
+ }
+ if(action == RXML_EVT_TEXT) {
+ /* got the value we were hoping for */
+ if(next_value == XML_PL_NEXT_VALUE_NAME) {
+ if(current_name)
+ free(current_name);
+ current_name = strdup(info);
+ } else if(next_value == XML_PL_NEXT_VALUE_ID) {
+ native_plid = atoi(info);
+ }
+ return XML_STATE_PLAYLISTS;
+ }
+ return XML_STATE_ERROR;
+
+ case XML_PL_ST_EXPECTING_PL_TRACKLIST:
+ if((strcasecmp(info,"dict") == 0) || (strcasecmp(info,"key") == 0))
+ return XML_STATE_PLAYLISTS;
+ MAYBESETSTATE_PL(RXML_EVT_END,"array",XML_PL_ST_EXPECTING_PL_DATA);
+ if(action == RXML_EVT_TEXT) {
+ if(strcasecmp(info,"Track ID") != 0) {
+ native_track_id = atoi(info);
+ DPRINTF(E_DBG,L_SCAN,"Adding itunes track #%s\n",info);
+ /* add it to the current playlist (current_id) */
+ if(current_id && scan_xml_get_index(native_track_id, &track_id)) {
+ db_add_playlist_item(current_id,track_id);
+ }
+ }
+
+ return XML_STATE_PLAYLISTS;
+ }
+ return XML_STATE_PLAYLISTS;
+
+ default:
+ return XML_STATE_ERROR;
+ }
+
return XML_STATE_PLAYLISTS;
}
diff --git a/src/ssc.c b/src/ssc.c
index e5762c40..be6f341a 100644
--- a/src/ssc.c
+++ b/src/ssc.c
@@ -113,7 +113,7 @@ int server_side_convert(char *fname) {
int server_side_convert_set(MP3FILE *pmp3)
{
char *fname, *path, *description, *ext;
- DPRINTF(E_DBG,L_SCAN,"Checking for ssc: %s\n",pmp3->fname);
+ DPRINTF(E_SPAM,L_SCAN,"Checking for ssc: %s\n",pmp3->fname);
if ((!config.ssc_extensions) ||
(!config.ssc_extensions[0]) ||
(!config.ssc_prog) ||
@@ -126,11 +126,11 @@ int server_side_convert_set(MP3FILE *pmp3)
strlen(pmp3->fname) -
strlen(SERVER_SIDE_CONVERT_SUFFIX),
SERVER_SIDE_CONVERT_SUFFIX) == 0))) {
- DPRINTF(E_DBG,L_SCAN,"Nope\n");
+ DPRINTF(E_SPAM,L_SCAN,"Nope\n");
return 0;
}
- DPRINTF(E_DBG,L_SCAN,"Yup\n");
+ DPRINTF(E_SPAM,L_SCAN,"Yup\n");
if (((ext = strrchr(pmp3->path, '.')) != NULL) &&
(strcasestr(config.ssc_extensions, ext))) {
fname = (char *)malloc(strlen(pmp3->fname) +