diff --git a/src/daap.c b/src/daap.c index e44c6340..2f2ff2fa 100644 --- a/src/daap.c +++ b/src/daap.c @@ -123,10 +123,6 @@ DAAP_ITEMS taglist[] = { }; /* Forwards */ -DAAP_BLOCK *daap_response_songlist(void); -DAAP_BLOCK *daap_response_playlists(void); -DAAP_BLOCK *daap_response_dbinfo(void); -DAAP_BLOCK *daap_response_playlist_items(int playlist); int daap_add_mdcl(DAAP_BLOCK *root, char *tag, char *name, short int number) { DAAP_BLOCK *mdcl; @@ -242,7 +238,8 @@ DAAP_BLOCK *daap_response_songlist(void) { 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!! */ + if(current->bitrate) + g = g && daap_add_short(mlit,"asbr",current->bitrate); /* bitrate!! */ if(current->comment) g = g && daap_add_string(mlit,"ascm",current->comment); /* comment */ @@ -254,7 +251,6 @@ DAAP_BLOCK *daap_response_songlist(void) { // 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! if(current->genre) @@ -262,6 +258,8 @@ DAAP_BLOCK *daap_response_songlist(void) { g = g && daap_add_int(mlit,"miid",current->id); /* id */ + /* these quite go hand in hand */ + g = g && daap_add_string(mlit,"asfm","mp3"); /* song format */ g = g && daap_add_string(mlit,"asdt","MPEG audio file"); /* descr */ if(current->title) @@ -272,11 +270,18 @@ DAAP_BLOCK *daap_response_songlist(void) { // 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 */ + if(current->samplerate) + g = g && daap_add_int(mlit,"assr",current->samplerate); /* samp rate */ + + if(current->file_size) + g = g && daap_add_int(mlit,"assz",current->file_size); /* Size! */ + + g = g && daap_add_int(mlit,"asst",0); /* song start time? */ + g = g && daap_add_int(mlit,"assp",0); /* songstoptime */ + + if(current->song_length) + g = g && daap_add_int(mlit,"astm",current->song_length*1000); /* 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 */ @@ -326,99 +331,6 @@ DAAP_BLOCK *daap_response_update(int clientver) { return root; } -/* - * daap_response_databases - * - * handle the daap block for the /databases URI - */ - -DAAP_BLOCK *daap_response_databases(char *path) { - char *uri; - int db_index; - int playlist_index; - char *first, *last; - - if(strcmp(path,"/databases")==0) { - return daap_response_dbinfo(); - } - - - uri = strdup(path); - first=(char*)&uri[11]; - last=first; - while((*last) && (*last != '/')) { - last++; - } - - if(*last != '/') { - return NULL; - } - - *last='\0'; - db_index=atoi(first); - - /* now we have the db id. Next, we have to figure out - * if it's a container request or a - * items request - * - * note we generally don't care about the DB index, since we only - * support 1 db. - */ - - /* the /databases/ uri will either be - * - * /databases, which returns an AVDB, - * /databases/id/items, which returns items in a db - * /databases/id/containers, which returns a container - * /databases/id/containers/id/items, which returns playlist elements - * /databases/id/items/id.mp3, to spool an mp3 - */ - - last++; - - if(strncasecmp(last,"items/",6)==0) { - /* streaming */ - free(uri); - return NULL; - } - - if(strncasecmp(last,"items",5)==0) { - /* songlist */ - free(uri); - return daap_response_songlist(); - } - - if(strncasecmp(last,"containers/",11)==0) { - /* playlist elements */ - first=last + 11; - last=first; - while((*last) && (*last != '/')) { - last++; - } - - if(*last != '/') { - return NULL; - } - - *last='\0'; - playlist_index=atoi(first); - - free(uri); - - return daap_response_playlist_items(playlist_index); - } - - if(strncasecmp(last,"containers",10)==0) { - /* list of playlists */ - free(uri); - return daap_response_playlists(); - return NULL; - } - - free(uri); - return NULL; - -} /* * daap_response_playlists @@ -587,3 +499,5 @@ DAAP_BLOCK *daap_response_playlist_items(int playlist) { return root; } + + diff --git a/src/daap.h b/src/daap.h index e18402bd..867bf8a3 100644 --- a/src/daap.h +++ b/src/daap.h @@ -27,7 +27,10 @@ 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(int clientver); -DAAP_BLOCK *daap_response_databases(char *path); +DAAP_BLOCK *daap_response_songlist(void); +DAAP_BLOCK *daap_response_playlists(void); +DAAP_BLOCK *daap_response_dbinfo(void); +DAAP_BLOCK *daap_response_playlist_items(int playlist); #endif /* _DAAP_H_ */ diff --git a/src/main.c b/src/main.c index 65553a6e..ab333812 100644 --- a/src/main.c +++ b/src/main.c @@ -52,7 +52,6 @@ */ CONFIG config; - /* * daap_handler * @@ -65,8 +64,20 @@ void daap_handler(WS_CONNINFO *pwsc) { int compress=0; int clientrev; + /* for the /databases URI */ + char *uri; + int db_index; + int playlist_index; + int item; + char *first, *last; + int streaming=0; + + MP3FILE *pmp3; + int file_fd; + close=pwsc->close; - pwsc->close=1; + pwsc->close=1; /* in case we have any errors */ + root=NULL; ws_addresponseheader(pwsc,"Accept-Ranges","bytes"); ws_addresponseheader(pwsc,"DAAP-Server","iTunes/4.1 (Mac OS X)"); @@ -86,40 +97,118 @@ void daap_handler(WS_CONNINFO *pwsc) { clientrev=atoi(ws_getvar(pwsc,"delta")); } root=daap_response_update(clientrev); - } else if (!strcasecmp(pwsc->uri,"/databases")) { - root=daap_response_databases(pwsc->uri); } else if (!strcasecmp(pwsc->uri,"/logout")) { ws_returnerror(pwsc,204,"Logout Successful"); return; - } else if (!strncasecmp(pwsc->uri,"/databases/",11)) { - root=daap_response_databases(pwsc->uri); - } else { - DPRINTF(ERR_WARN,"Bad handler! Can't find uri handler for %s\n", - pwsc->uri); - return; + } else if(strcmp(pwsc->uri,"/databases")==0) { + root=daap_response_dbinfo(); + } else if(strncmp(pwsc->uri,"/databases/",11) == 0) { + + /* the /databases/ uri will either be: + * + * /databases/id/items, which returns items in a db + * /databases/id/containers, which returns a container + * /databases/id/containers/id/items, which returns playlist elements + * /databases/id/items/id.mp3, to spool an mp3 + */ + + uri = strdup(pwsc->uri); + first=(char*)&uri[11]; + last=first; + while((*last) && (*last != '/')) { + last++; + } + + if(*last) { + *last='\0'; + db_index=atoi(first); + + last++; + + if(strncasecmp(last,"items/",6)==0) { + /* streaming */ + first=last+6; + while((*last) && (*last != '.')) + last++; + + if(*last == '.') { + *last='\0'; + item=atoi(first); + streaming=1; + } + free(uri); + } else if (strncasecmp(last,"items",5)==0) { + /* songlist */ + free(uri); + root=daap_response_songlist(); + } else if (strncasecmp(last,"containers/",11)==0) { + /* playlist elements */ + first=last + 11; + last=first; + while((*last) && (*last != '/')) { + last++; + } + + if(*last) { + *last='\0'; + playlist_index=atoi(first); + root=daap_response_playlist_items(playlist_index); + } + free(uri); + } else if (strncasecmp(last,"containers",10)==0) { + /* list of playlists */ + free(uri); + root=daap_response_playlists(); + } + } } - if(!root) { + if((!root)&&(!streaming)) { ws_returnerror(pwsc,400,"Invalid Request"); return; } pwsc->close=close; - ws_addresponseheader(pwsc,"Content-Length","%d",root->reported_size + 8); + if(!streaming) { + ws_addresponseheader(pwsc,"Content-Length","%d",root->reported_size + 8); + ws_writefd(pwsc,"HTTP/1.1 200 OK\r\n"); + ws_emitheaders(pwsc); - ws_writefd(pwsc,"HTTP/1.1 200 OK\r\n"); - ws_emitheaders(pwsc); + /* + if(ws_testrequestheader(pwsc,"Accept-Encoding","gzip")) { + ws_addresponseheader(pwsc,"Content-Encoding","gzip"); + compress=1; + } + */ - /* - if(ws_testrequestheader(pwsc,"Accept-Encoding","gzip")) { - ws_addresponseheader(pwsc,"Content-Encoding","gzip"); - compress=1; + daap_serialize(root,pwsc->fd,0); + daap_free(root); + } else { + /* stream out the song */ + pwsc->close=1; + + pmp3=db_find(item); + if(!pmp3) { + ws_returnerror(pwsc,404,"File Not Found"); + } else { + /* got the file, let's open and serve it */ + file_fd=r_open2(pmp3->path,O_RDONLY); + if(file_fd == -1) { + pwsc->error=errno; + DPRINTF(ERR_WARN,"Thread %d: Error opening %s: %s\n", + pwsc->threadno,pmp3->path,strerror(errno)); + ws_returnerror(pwsc,404,"Not found"); + } else { + ws_writefd(pwsc,"HTTP/1.1 200 OK\r\n"); + ws_addresponseheader(pwsc,"Connection","Close"); + ws_emitheaders(pwsc); + + copyfile(file_fd,pwsc->fd); + r_close(file_fd); + } + } } - */ - - daap_serialize(root,pwsc->fd,0); - daap_free(root); return; } @@ -168,7 +257,7 @@ void config_handler(WS_CONNINFO *pwsc) { return; } - file_fd=open(resolved_path,O_RDONLY); + file_fd=r_open2(resolved_path,O_RDONLY); if(file_fd == -1) { pwsc->error=errno; DPRINTF(ERR_WARN,"Thread %d: Error opening %s: %s\n", @@ -230,7 +319,7 @@ void config_handler(WS_CONNINFO *pwsc) { } } - close(file_fd); + r_close(file_fd); DPRINTF(ERR_DEBUG,"Thread %d: Served successfully\n",pwsc->threadno); return; } diff --git a/src/makeargv.c b/src/makeargv.c deleted file mode 100644 index e4f47783..00000000 --- a/src/makeargv.c +++ /dev/null @@ -1,40 +0,0 @@ -#include -#include - -int makeargv(const char *s, const char *delimiters, char ***argvp) { - int i; - int numtokens; - const char *snew; - char *t; - - if ((s == NULL) || (delimiters == NULL) || (argvp == NULL)) - return -1; - *argvp = NULL; - snew = s + strspn(s, delimiters); /* snew is real start of string */ - if ((t = malloc(strlen(snew) + 1)) == NULL) - return -1; - - /* count the number of tokens in s */ - strcpy(t, snew); - numtokens = 0; - if (strtok(t, delimiters) != NULL) - for (numtokens = 1; strtok(NULL, delimiters) != NULL; numtokens++) ; - - /* create argument array for ptrs to the tokens */ - if ((*argvp = malloc((numtokens + 1)*sizeof(char *))) == NULL) { - free(t); - return -1; - } - /* insert pointers to tokens into the argument array */ - if (numtokens == 0) - free(t); - else { - strcpy(t, snew); - **argvp = strtok(t, delimiters); - for (i = 1; i < numtokens; i++) - *((*argvp) + i) = strtok(NULL, delimiters); - } - /* put in the final NULL pointer and return */ - *((*argvp) + numtokens) = NULL; - return numtokens; -} diff --git a/src/mp3-scanner.c b/src/mp3-scanner.c index 250132c8..fb11ec88 100644 --- a/src/mp3-scanner.c +++ b/src/mp3-scanner.c @@ -36,13 +36,30 @@ /* * Typedefs */ + +typedef struct tag_scan_id3header { + unsigned char id[3]; + unsigned char version[2]; + unsigned char flags; + unsigned char size[4]; +} SCAN_ID3HEADER; + #define MAYBEFREE(a) { if((a)) free((a)); }; + +/* + * Globals + */ +int scan_br_table[] = { + 0,32,40,48,56,64,80,96,112,128,160,192,224,256,320,0 +}; + /* * Forwards */ int scan_foreground(char *path); int scan_gettags(char *file, MP3FILE *pmp3); +int scan_getfileinfo(char *file, MP3FILE *pmp3); int scan_freetags(MP3FILE *pmp3); /* @@ -133,6 +150,7 @@ int scan_foreground(char *path) { /* Do the tag lookup here */ scan_gettags(mp3file.path,&mp3file); + scan_getfileinfo(mp3file.path,&mp3file); db_add(&mp3file); @@ -230,3 +248,84 @@ int scan_freetags(MP3FILE *pmp3) { return 0; } + +/* + * scan_getfileinfo + * + * Get information from the file headers itself -- like + * song length, bit rate, etc. + */ +int scan_getfileinfo(char *file, MP3FILE *pmp3) { + FILE *infile; + SCAN_ID3HEADER *pid3; + unsigned int size=0; + fpos_t fp_size=0; + fpos_t file_size; + unsigned char buffer[256]; + int time_seconds; + + int ver=0; + int layer=0; + int bitrate=0; + int samplerate=0; + + if(!(infile=fopen(file,"rb"))) { + DPRINTF(ERR_WARN,"Could not open %s for reading\n",file); + return -1; + } + + fread(buffer,1,sizeof(buffer),infile); + pid3=(SCAN_ID3HEADER*)buffer; + + if(strncmp(pid3->id,"ID3",3)==0) { + /* found an ID3 header... */ + size = (pid3->size[0] << 21 | pid3->size[1] << 14 | + pid3->size[2] << 7 | pid3->size[3]); + fp_size=size + sizeof(SCAN_ID3HEADER); + } + + fseek(infile,0,SEEK_END); + file_size=ftell(infile); + file_size -= fp_size; + + fsetpos(infile,&fp_size); + fread(buffer,1,sizeof(buffer),infile); + + if((buffer[0] == 0xFF)&&(buffer[1] >= 224)) { + printf("Found sync frame\n"); + + ver=(buffer[1] & 0x18) >> 3; + layer=(buffer[1] & 0x6) >> 1; + if((ver==3) && (layer==1)) { /* MPEG1, Layer 3 */ + bitrate=(buffer[2] & 0xF0) >> 4; + bitrate=scan_br_table[bitrate]; + samplerate=(buffer[2] & 0x0C) >> 2; + switch(samplerate) { + case 0: + samplerate=44100; + break; + case 1: + samplerate=48000; + break; + case 2: + samplerate=32000; + break; + } + pmp3->bitrate=bitrate; + pmp3->samplerate=samplerate; + } + + /* guesstimate the file length */ + time_seconds = ((int)(file_size * 8)) / (bitrate * 1024); + pmp3->song_length=time_seconds; + pmp3->file_size=file_size; + } else { + /* should really scan forward to next sync frame */ + fclose(infile); + return -1; + } + + + fclose(infile); + return 0; +} diff --git a/src/mp3-scanner.h b/src/mp3-scanner.h index a457b87f..ee080f17 100644 --- a/src/mp3-scanner.h +++ b/src/mp3-scanner.h @@ -30,6 +30,12 @@ typedef struct tag_mp3file { char *album; char *genre; char *comment; + + int bitrate; + int samplerate; + int song_length; + int file_size; + int got_id3; int id; } MP3FILE; diff --git a/src/mt-daapd.conf b/src/mt-daapd.conf index 3b4e44b7..c3a4f4e9 100644 --- a/src/mt-daapd.conf +++ b/src/mt-daapd.conf @@ -1,4 +1,4 @@ web_root ../admin-root port 3689 admin_password secret -mp3_dir mp3 +mp3_dir mp3/Type O Negative