Add iTunes playlist support

This commit is contained in:
Ron Pedde 2005-05-21 05:53:11 +00:00
parent 8b30cbce9a
commit 9ae132a127
10 changed files with 328 additions and 58 deletions

View File

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

View File

@ -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();

View File

@ -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);

View File

@ -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++;

View File

@ -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);

View File

@ -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");

View File

@ -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);

View File

@ -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;

View File

@ -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 <dict> */
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 <key>, or a </dict> */
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 - <key>id</key> */
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 <array> 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,&current_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;
}

View File

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