Now serving...
This commit is contained in:
parent
20e564ed18
commit
c38085474e
122
src/daap.c
122
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;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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_ */
|
||||
|
||||
|
|
117
src/main.c
117
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,28 +97,81 @@ 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(!root) {
|
||||
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)&&(!streaming)) {
|
||||
ws_returnerror(pwsc,400,"Invalid Request");
|
||||
return;
|
||||
}
|
||||
|
||||
pwsc->close=close;
|
||||
|
||||
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);
|
||||
|
||||
|
@ -120,6 +184,31 @@ void daap_handler(WS_CONNINFO *pwsc) {
|
|||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -1,40 +0,0 @@
|
|||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
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;
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
web_root ../admin-root
|
||||
port 3689
|
||||
admin_password secret
|
||||
mp3_dir mp3
|
||||
mp3_dir mp3/Type O Negative
|
||||
|
|
Loading…
Reference in New Issue