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) +