diff --git a/src/Makefile.am b/src/Makefile.am index 343ba2ab..244798c0 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -4,7 +4,8 @@ sbin_PROGRAMS = mt-daapd mt_daapd_SOURCES = main.c daapd.h rend.c rend.h uici.c uici.h webserver.c \ webserver.h configfile.c configfile.h err.c err.h restart.c restart.h \ - daap-proto.c daap-proto.h daap.c daap.h \ + daap-proto.c daap-proto.h daap.c daap.h db-memory.c db-memory.h \ + mp3-scanner.h mp3-scanner.c \ mdns/mDNS.c mdns/mDNSClientAPI.h mdns/mDNSDebug.h mdns/mDNSPosix.c \ mdns/mDNSUNP.c diff --git a/src/daap.c b/src/daap.c index 7d6edcc6..3d8df2b8 100644 --- a/src/daap.c +++ b/src/daap.c @@ -23,6 +23,7 @@ #include #include +#include "db-memory.h" #include "daap-proto.h" #include "daap.h" #include "err.h" @@ -210,17 +211,67 @@ DAAP_BLOCK *daap_response_login(void) { DAAP_BLOCK *daap_response_songlist(void) { DAAP_BLOCK *root; int g=1; - + DAAP_BLOCK *mlcl; + DAAP_BLOCK *mlit; + ENUMHANDLE henum; + MP3FILE *current; + + henum=db_enum_begin(); + if(!henum) + return NULL; root=daap_add_empty(NULL,"adbs"); if(root) { g = (int)daap_add_int(root,"mstt",200); g = g && daap_add_char(root,"muty",0); - g = g && daap_add_int(root,"mtco",0); - g = g && daap_add_int(root,"mrco",0); - g = g && daap_add_empty(root,"mlcl"); + g = g && daap_add_int(root,"mtco",db_get_song_count()); + g = g && daap_add_int(root,"mrco",db_get_song_count()); + + mlcl=daap_add_empty(root,"mlcl"); + + if(mlcl) { + while(current=db_enum(&henum)) { + DPRINTF(ERR_DEBUG,"Got entry for %s\n",current->fname); + mlit=daap_add_empty(mlcl,"mlit"); + if(mlit) { + g = g && daap_add_char(mlit,"mikd",2); /* audio */ + g = g && daap_add_string(mlit,"asal",current->album); + g = g && daap_add_string(mlit,"asar",current->artist); + g = g && daap_add_short(mlit,"asbt",0); /* bpm */ + g = g && daap_add_short(mlit,"asbr",128); /* bitrate!! */ + g = g && daap_add_string(mlit,"ascm","ron was here"); /* comment */ + g = g && daap_add_char(mlit,"asco",0x0); /* compilation */ + g = g && daap_add_string(mlit,"ascp",""); /* composer */ + g = g && daap_add_int(mlit,"asda",0); /* date added */ + g = g && daap_add_int(mlit,"asdm",0); /* date modified */ + g = g && daap_add_short(mlit,"asdc",0); /* # of discs */ + g = g && daap_add_short(mlit,"asdn",0); /* disc number */ + g = g && daap_add_char(mlit,"asdk",0); /* song datakind? */ + g = g && daap_add_string(mlit,"asfm","mp3"); /* song format */ + // aseq - null string! + g = g && daap_add_string(mlit,"asgn",current->genre); /* genre */ + g = g && daap_add_int(mlit,"miid",current->id); /* id */ + g = g && daap_add_string(mlit,"asdt","MPEG audio file"); /* descr */ + g = g && daap_add_string(mlit,"minm",current->fname); /* descr */ + // mper (long) + g = g && daap_add_char(mlit,"asdb",0); /* disabled */ + g = g && daap_add_char(mlit,"asrv",0); /* rel vol */ + g = g && daap_add_int(mlit,"assr",44100); /* sample rate */ + g = g && daap_add_int(mlit,"assz",1024); /* FIXME: Song size! */ + g = g && daap_add_int(mlit,"asst",0); /* song start time? */ + g = g && daap_add_int(mlit,"assp",0); /* songstoptime */ + g = g && daap_add_int(mlit,"astm",3600); /* song time */ + g = g && daap_add_short(mlit,"astc",0); /* track count */ + g = g && daap_add_short(mlit,"astn",0); /* track number */ + g = g && daap_add_char(mlit,"asur",3); /* rating */ + g = g && daap_add_short(mlit,"asyr",0); + } else g=0; + } + } else g=0; } + db_enum_end(); + if(!g) { daap_free(root); return NULL; @@ -236,15 +287,19 @@ DAAP_BLOCK *daap_response_songlist(void) { * handle the daap block for the /update URI */ -DAAP_BLOCK *daap_response_update(void) { +DAAP_BLOCK *daap_response_update(int clientver) { DAAP_BLOCK *root; int g=1; - + + while(clientver == db_version()) { + sleep(30); + } + root=daap_add_empty(NULL,"mupd"); if(root) { g = (int)daap_add_int(root,"mstt",200); /* theoretically, this would go up if the db changes? */ - g = g && daap_add_int(root,"musr",3); + g = g && daap_add_int(root,"musr",db_version()); } if(!g) { @@ -373,7 +428,7 @@ DAAP_BLOCK *daap_response_playlists(void) { g = g && daap_add_int(mlit,"miid",0x1); g = g && daap_add_long(mlit,"mper",0,2); g = g && daap_add_string(mlit,"minm","daapd music"); - g = g && daap_add_int(mlit,"mimc",0x0); + g = g && daap_add_int(mlit,"mimc",db_get_song_count()); } } } @@ -414,7 +469,7 @@ DAAP_BLOCK *daap_response_dbinfo(void) { g = g && daap_add_int(mlit,"miid",0x20); g = g && daap_add_long(mlit,"mper",0,1); g = g && daap_add_string(mlit,"minm","daapd music"); - g = g && daap_add_int(mlit,"mimc",0x0); /* songs */ + g = g && daap_add_int(mlit,"mimc",db_get_song_count()); /* songs */ g = g && daap_add_int(mlit,"mctc",0x1); /* playlists */ } } @@ -476,8 +531,15 @@ DAAP_BLOCK *daap_response_server_info(void) { */ DAAP_BLOCK *daap_response_playlist_items(int playlist) { DAAP_BLOCK *root; + DAAP_BLOCK *mlcl; + DAAP_BLOCK *mlit; + ENUMHANDLE henum; + MP3FILE *current; int g=1; + henum=db_enum_begin(); + if(!henum) + return NULL; root=daap_add_empty(NULL,"apso"); if(root) { @@ -485,9 +547,23 @@ DAAP_BLOCK *daap_response_playlist_items(int playlist) { g = g && daap_add_char(root,"muty",0); g = g && daap_add_int(root,"mtco",0); g = g && daap_add_int(root,"mrco",0); - g = g && daap_add_empty(root,"mlcl"); + + mlcl=daap_add_empty(root,"mlcl"); + + if(mlcl) { + while(current=db_enum(&henum)) { + mlit=daap_add_empty(mlcl,"mlit"); + if(mlit) { + g = g && daap_add_char(mlit,"mikd",2); + g = g && daap_add_int(mlit,"miid",current->id); + g = g && daap_add_int(mlit,"mcti",0x1); /* built-in container */ + } else g=0; + } + } else g=0; } + db_enum_end(); + if(!g) { daap_free(root); return NULL; diff --git a/src/daap.h b/src/daap.h index c43f9d81..e18402bd 100644 --- a/src/daap.h +++ b/src/daap.h @@ -26,7 +26,7 @@ DAAP_BLOCK *daap_response_server_info(void); DAAP_BLOCK *daap_response_content_codes(void); DAAP_BLOCK *daap_response_login(void); -DAAP_BLOCK *daap_response_update(void); +DAAP_BLOCK *daap_response_update(int clientver); DAAP_BLOCK *daap_response_databases(char *path); #endif /* _DAAP_H_ */ diff --git a/src/db-memory.c b/src/db-memory.c index c1b960be..309d1c22 100644 --- a/src/db-memory.c +++ b/src/db-memory.c @@ -24,6 +24,7 @@ #include #include +#include "err.h" #include "mp3-scanner.h" /* @@ -40,10 +41,11 @@ typedef struct tag_mp3record { * Globals */ MP3RECORD db_root; -int db_version; +int db_version_no; int db_update_mode=0; -pthread_rwlock_t db_rwlock=PTHREAD_RWLOCK_INITIALIZER; - +int db_song_count; +pthread_rwlock_t db_rwlock; /* OSX doesn't have PTHREAD_RWLOCK_INITIALIZER */ +pthread_once_t db_initlock=PTHREAD_ONCE_INIT; /* * Forwards */ @@ -56,8 +58,24 @@ int db_deinit(void); int db_version(void); int db_add(MP3FILE *mp3file); +MP3RECORD *db_enum_begin(void); +MP3FILE *db_enum(MP3RECORD **current); +int db_enum_end(void); +int db_get_song_count(void); +MP3FILE *db_find(int id); + void db_freerecord(MP3RECORD *mp3record); +/* + * db_init_once + * + * Must dynamically initialize the rwlock, as Mac OSX 10.3 (at least) + * doesn't have a static initializer for rwlocks + */ +void db_init_once(void) { + pthread_rwlock_init(&db_rwlock,NULL); +} + /* * db_init * @@ -66,7 +84,10 @@ void db_freerecord(MP3RECORD *mp3record); */ int db_init(char *parameters) { db_root.next=NULL; - return 0; + db_version_no=1; + db_song_count=0; + + return pthread_once(&db_initlock,db_init_once); } /* @@ -85,7 +106,7 @@ int db_deinit(void) { * return the db version */ int db_version(void) { - return db_version; + return db_version_no; } /* @@ -116,7 +137,7 @@ int db_end_initial_update(void) { * background update mode */ int db_is_empty(void) { - return db_root.next; + return !db_root.next; } /* @@ -127,9 +148,12 @@ int db_is_empty(void) { int db_add(MP3FILE *mp3file) { int err; + int g; MP3RECORD *pnew; - if(!pnew=(MP3RECORD*)malloc(sizeof(MP3RECORD))) { + DPRINTF(ERR_DEBUG,"Adding %s\n",mp3file->path); + + if((pnew=(MP3RECORD*)malloc(sizeof(MP3RECORD))) == NULL) { free(pnew); errno=ENOMEM; return -1; @@ -137,29 +161,40 @@ int db_add(MP3FILE *mp3file) { memset(pnew,0,sizeof(MP3RECORD)); - memcpy(pnew->mp3file,mp3file,sizeof(MP3FILE)); - err=(int) pnew->mp3file.path=strdup(mp3file->path); - err = err | pnew->mp3file.fname=strdup(mp3file->fname); - err = err | pnew->mp3file.artist=strdup(mp3file->artist); - err = err | pnew->mp3file.album=strdup(mp3file->album); - err = err | pnew->mp3file.genre=strdup(mp3file->genre); + memcpy(&pnew->mp3file,mp3file,sizeof(MP3FILE)); - if(err) { + g=(int) pnew->mp3file.path=strdup(mp3file->path); + g = g && (pnew->mp3file.fname=strdup(mp3file->fname)); + g = g && (pnew->mp3file.artist=strdup(mp3file->artist)); + g = g && (pnew->mp3file.album=strdup(mp3file->album)); + g = g && (pnew->mp3file.genre=strdup(mp3file->genre)); + + if(!g) { + DPRINTF(ERR_WARN,"Malloc error in db_add\n"); db_freerecord(pnew); errno=ENOMEM; return -1; } - if(err=pthread_rwlock_wrlock(&db_wrlock)) { + if(err=pthread_rwlock_wrlock(&db_rwlock)) { + DPRINTF(ERR_WARN,"cannot lock wrlock in db_add\n"); db_freerecord(pnew); errno=err; return -1; } - pnew->next=root.next; - root.next=pnew->next; + pnew->next=db_root.next; + db_root.next=pnew; - pthread_rwlock_unlock(&db_wrlock); + if(!db_update_mode) { + db_version_no++; + } + + db_song_count++; + + pthread_rwlock_unlock(&db_rwlock); + DPRINTF(ERR_DEBUG,"Added file\n"); + return 0; } /* @@ -176,3 +211,76 @@ void db_freerecord(MP3RECORD *mp3record) { free(mp3record); } +/* + * db_enum_begin + * + * Begin to walk through an enum of + * the database. + * + * this should be done quickly, as we'll be holding + * a reader lock on the db + */ +MP3RECORD *db_enum_begin(void) { + int err; + + if(err=pthread_rwlock_wrlock(&db_rwlock)) { + log_err(0,"Cannot lock rwlock\n"); + errno=err; + return NULL; + } + + return db_root.next; +} + + +/* + * db_enum + * + * Walk to the next entry + */ +MP3FILE *db_enum(MP3RECORD **current) { + MP3FILE *retval; + + if(*current) { + retval=&((*current)->mp3file); + *current=(*current)->next; + return retval; + } + return NULL; +} + +/* + * db_enum_end + * + * quit walking the database (and give up reader lock) + */ +int db_enum_end(void) { + return pthread_rwlock_unlock(&db_rwlock); +} + +/* + * db_find + * + * Find a MP3FILE entry based on file id + */ +MP3FILE *db_find(int id) { + MP3RECORD *current=db_root.next; + while((current) && (current->mp3file.id != id)) { + current=current->next; + } + + if(!current) + return NULL; + + return ¤t->mp3file; +} + +/* + * db_get_song_count + * + * return the number of songs in the database. Used for the /database + * request + */ +int db_get_song_count(void) { + return db_song_count; +} diff --git a/src/db-memory.h b/src/db-memory.h index 534b0c9c..71472503 100644 --- a/src/db-memory.h +++ b/src/db-memory.h @@ -35,7 +35,9 @@ extern int db_version(void); extern int db_add(MP3FILE *mp3file); extern ENUMHANDLE db_enum_begin(void); extern MP3FILE *db_enum(ENUMHANDLE *current); -extern int db_enum_end(ENUMHANDLE current); +extern int db_enum_end(void); extern MP3FILE *db_find(int id); +extern int db_get_song_count(void); + #endif /* _DB_MEMORY_H_ */ diff --git a/src/main.c b/src/main.c index 3d3ffa04..d92654ec 100644 --- a/src/main.c +++ b/src/main.c @@ -36,9 +36,11 @@ #include #include "configfile.h" +#include "db-memory.h" #include "daap.h" #include "daap-proto.h" #include "err.h" +#include "mp3-scanner.h" #include "rend.h" #include "webserver.h" @@ -61,6 +63,7 @@ void daap_handler(WS_CONNINFO *pwsc) { int close; DAAP_BLOCK *root,*error; int compress=0; + int clientrev; close=pwsc->close; pwsc->close=1; @@ -77,7 +80,12 @@ void daap_handler(WS_CONNINFO *pwsc) { } else if (!strcasecmp(pwsc->uri,"/login")) { root=daap_response_login(); } else if (!strcasecmp(pwsc->uri,"/update")) { - root=daap_response_update(); + if(!ws_getvar(pwsc,"revision-number")) { /* first check */ + clientrev=db_version() - 1; + } else { + clientrev=atoi(ws_getvar(pwsc,"revision-number")); + } + root=daap_response_update(clientrev); } else if (!strcasecmp(pwsc->uri,"/databases")) { root=daap_response_databases(pwsc->uri); } else if (!strcasecmp(pwsc->uri,"/logout")) { @@ -250,6 +258,8 @@ int main(int argc, char *argv[]) { WSHANDLE server; pid_t rendezvous_pid; int use_mdns=0; + ENUMHANDLE handle; + MP3FILE *pmp3; #ifdef DEBUG char *optval="d:c:m"; @@ -299,8 +309,27 @@ int main(int argc, char *argv[]) { } } - /* should verify the configfile has enough info - * to start */ + /* Initialize the database before starting */ + if(db_init("none")) { + perror("db_init"); + exit(EXIT_FAILURE); + } + + if(scan_init(config.mp3dir)) { + perror("scan_init"); + exit(EXIT_FAILURE); + } + +#ifdef DEBUG + printf("Dump of database:\n"); + handle=db_enum_begin(); + while(pmp3=db_enum(&handle)) { + printf("File: %s\n",pmp3->fname); + } + db_enum_end(); + + +#endif /* start up the web server */ ws_config.web_root=config.web_root; diff --git a/src/mp3-scanner.h b/src/mp3-scanner.h index 2b652355..967d3f09 100644 --- a/src/mp3-scanner.h +++ b/src/mp3-scanner.h @@ -31,4 +31,6 @@ typedef struct tag_mp3file { int id; } MP3FILE; +extern int scan_init(char *path); + #endif /* _MP3_SCANNER_H_ */