Support for streaming audio via .url files -- particularly for the Roku SoundBridge
This commit is contained in:
parent
baf7ba8bda
commit
493f0bffbf
16
src/daap.c
16
src/daap.c
|
@ -147,8 +147,8 @@ static query_field_t song_fields[] = {
|
||||||
{ qft_string, "daap.songcomment", OFFSET_OF(MP3FILE, comment) },
|
{ qft_string, "daap.songcomment", OFFSET_OF(MP3FILE, comment) },
|
||||||
{ qft_i32, "daap.songcompilation", OFFSET_OF(MP3FILE, compilation) },
|
{ qft_i32, "daap.songcompilation", OFFSET_OF(MP3FILE, compilation) },
|
||||||
{ qft_string, "daap.songcomposer", OFFSET_OF(MP3FILE, composer) },
|
{ qft_string, "daap.songcomposer", OFFSET_OF(MP3FILE, composer) },
|
||||||
// { qft_i32_const, "daap.songdatakind", 0 },
|
{ qft_i32, "daap.songdatakind", OFFSET_OF(MP3FILE, data_kind) },
|
||||||
// { qft_string, "daap.songdataurl", OFFSET_OF(MP3FILE, url) },
|
{ qft_string, "daap.songdataurl", OFFSET_OF(MP3FILE, url) },
|
||||||
{ qft_i32, "daap.songdateadded", OFFSET_OF(MP3FILE, time_added) },
|
{ qft_i32, "daap.songdateadded", OFFSET_OF(MP3FILE, time_added) },
|
||||||
{ qft_i32, "daap.songdatemodified",OFFSET_OF(MP3FILE, time_modified) },
|
{ qft_i32, "daap.songdatemodified",OFFSET_OF(MP3FILE, time_modified) },
|
||||||
{ qft_string, "daap.songdescription", OFFSET_OF(MP3FILE, description) },
|
{ qft_string, "daap.songdescription", OFFSET_OF(MP3FILE, description) },
|
||||||
|
@ -410,6 +410,8 @@ DAAP_BLOCK *daap_response_songlist(char* metaStr, char* query) {
|
||||||
query_node_t* filter = 0;
|
query_node_t* filter = 0;
|
||||||
int songs = 0;
|
int songs = 0;
|
||||||
|
|
||||||
|
DPRINTF(ERR_DEBUG,"enter daap_response_songlist\n");
|
||||||
|
|
||||||
// if the meta tag is specified, encode it, if it's not specified
|
// if the meta tag is specified, encode it, if it's not specified
|
||||||
// we're given the latitude to select our own subset, for
|
// we're given the latitude to select our own subset, for
|
||||||
// simplicity we just include everything.
|
// simplicity we just include everything.
|
||||||
|
@ -429,7 +431,7 @@ DAAP_BLOCK *daap_response_songlist(char* metaStr, char* query) {
|
||||||
|
|
||||||
henum=db_enum_begin();
|
henum=db_enum_begin();
|
||||||
if((!henum) && (db_get_song_count())) {
|
if((!henum) && (db_get_song_count())) {
|
||||||
DPRINTF(ERR_DEBUG,"Can't get enum handle\n");
|
DPRINTF(ERR_DEBUG,"Can't get enum handle - exiting daap_response_songlist\n");
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -462,7 +464,7 @@ DAAP_BLOCK *daap_response_songlist(char* metaStr, char* query) {
|
||||||
query_free(filter);
|
query_free(filter);
|
||||||
|
|
||||||
if(!g) {
|
if(!g) {
|
||||||
DPRINTF(ERR_DEBUG,"Error enumerating database\n");
|
DPRINTF(ERR_DEBUG,"Error enumerating database - exiting daap_response_songlist\n");
|
||||||
daap_free(root);
|
daap_free(root);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
@ -472,6 +474,7 @@ DAAP_BLOCK *daap_response_songlist(char* metaStr, char* query) {
|
||||||
daap_set_int(root, "mtco", songs);
|
daap_set_int(root, "mtco", songs);
|
||||||
daap_set_int(root, "mrco", songs);
|
daap_set_int(root, "mrco", songs);
|
||||||
|
|
||||||
|
DPRINTF(ERR_DEBUG,"Exiting daap_response_songlist\n");
|
||||||
return root;
|
return root;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -491,7 +494,10 @@ DAAP_BLOCK* daap_add_song_entry(DAAP_BLOCK* mlcl, MP3FILE* song, MetaField_t met
|
||||||
g = g && daap_add_char(mlit,"mikd",song->item_kind); /* audio */
|
g = g && daap_add_char(mlit,"mikd",song->item_kind); /* audio */
|
||||||
|
|
||||||
if(wantsMeta(meta, metaSongDataKind))
|
if(wantsMeta(meta, metaSongDataKind))
|
||||||
g = g && daap_add_char(mlit,"asdk",0); /* local file */
|
g = g && daap_add_char(mlit,"asdk",song->data_kind); /* local file */
|
||||||
|
|
||||||
|
if(wantsMeta(meta, metaSongDataURL))
|
||||||
|
g = g && daap_add_string(mlit,"asul",song->url);
|
||||||
|
|
||||||
if(song->album && (wantsMeta(meta, metaSongAlbum)))
|
if(song->album && (wantsMeta(meta, metaSongAlbum)))
|
||||||
g = g && daap_add_string(mlit,"asal",song->album);
|
g = g && daap_add_string(mlit,"asal",song->album);
|
||||||
|
|
|
@ -46,7 +46,7 @@
|
||||||
/*
|
/*
|
||||||
* Defines
|
* Defines
|
||||||
*/
|
*/
|
||||||
#define DB_VERSION 4
|
#define DB_VERSION 5
|
||||||
#define STRLEN(a) (a) ? strlen((a)) + 1 : 1
|
#define STRLEN(a) (a) ? strlen((a)) + 1 : 1
|
||||||
#define MAYBEFREE(a) { if((a)) free((a)); };
|
#define MAYBEFREE(a) { if((a)) free((a)); };
|
||||||
|
|
||||||
|
@ -121,7 +121,7 @@ typedef struct tag_mp3packed {
|
||||||
int conductor_len;
|
int conductor_len;
|
||||||
int grouping_len;
|
int grouping_len;
|
||||||
|
|
||||||
/* DB VERSION 4 AND ABOVE GO HERE! */
|
int url_len; /* DB Version 4 */
|
||||||
char data[1];
|
char data[1];
|
||||||
} MP3PACKED;
|
} MP3PACKED;
|
||||||
|
|
||||||
|
@ -549,6 +549,7 @@ datum *db_packrecord(MP3FILE *pmp3) {
|
||||||
len += STRLEN(pmp3->orchestra);
|
len += STRLEN(pmp3->orchestra);
|
||||||
len += STRLEN(pmp3->conductor);
|
len += STRLEN(pmp3->conductor);
|
||||||
len += STRLEN(pmp3->grouping);
|
len += STRLEN(pmp3->grouping);
|
||||||
|
len += STRLEN(pmp3->url);
|
||||||
|
|
||||||
result = (datum*) malloc(sizeof(datum));
|
result = (datum*) malloc(sizeof(datum));
|
||||||
if(!result)
|
if(!result)
|
||||||
|
@ -598,6 +599,7 @@ datum *db_packrecord(MP3FILE *pmp3) {
|
||||||
ppacked->orchestra_len=STRLEN(pmp3->orchestra);
|
ppacked->orchestra_len=STRLEN(pmp3->orchestra);
|
||||||
ppacked->conductor_len=STRLEN(pmp3->conductor);
|
ppacked->conductor_len=STRLEN(pmp3->conductor);
|
||||||
ppacked->grouping_len=STRLEN(pmp3->grouping);
|
ppacked->grouping_len=STRLEN(pmp3->grouping);
|
||||||
|
ppacked->url_len=STRLEN(pmp3->url);
|
||||||
|
|
||||||
offset=0;
|
offset=0;
|
||||||
if(pmp3->path)
|
if(pmp3->path)
|
||||||
|
@ -648,6 +650,11 @@ datum *db_packrecord(MP3FILE *pmp3) {
|
||||||
memcpy(&ppacked->data[offset],pmp3->grouping,ppacked->grouping_len);
|
memcpy(&ppacked->data[offset],pmp3->grouping,ppacked->grouping_len);
|
||||||
offset+=ppacked->grouping_len;
|
offset+=ppacked->grouping_len;
|
||||||
|
|
||||||
|
if(pmp3->url)
|
||||||
|
memcpy(&ppacked->data[offset],pmp3->url,ppacked->url_len);
|
||||||
|
offset+=ppacked->url_len;
|
||||||
|
|
||||||
|
|
||||||
/* whew */
|
/* whew */
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -738,6 +745,10 @@ int db_unpackrecord(datum *pdatum, MP3FILE *pmp3) {
|
||||||
pmp3->grouping=strdup(&ppacked->data[offset]);
|
pmp3->grouping=strdup(&ppacked->data[offset]);
|
||||||
offset += ppacked->grouping_len;
|
offset += ppacked->grouping_len;
|
||||||
|
|
||||||
|
if(ppacked->url_len > 1)
|
||||||
|
pmp3->url=strdup(&ppacked->data[offset]);
|
||||||
|
offset += ppacked->url_len;
|
||||||
|
|
||||||
/* shouldn't this have been done when scanning? */
|
/* shouldn't this have been done when scanning? */
|
||||||
make_composite_tags(pmp3);
|
make_composite_tags(pmp3);
|
||||||
|
|
||||||
|
@ -822,6 +833,7 @@ void db_freefile(MP3FILE *pmp3) {
|
||||||
MAYBEFREE(pmp3->conductor);
|
MAYBEFREE(pmp3->conductor);
|
||||||
MAYBEFREE(pmp3->grouping);
|
MAYBEFREE(pmp3->grouping);
|
||||||
MAYBEFREE(pmp3->description);
|
MAYBEFREE(pmp3->description);
|
||||||
|
MAYBEFREE(pmp3->url);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int nullstrcmp(const char* a, const char* b)
|
static int nullstrcmp(const char* a, const char* b)
|
||||||
|
|
|
@ -252,9 +252,12 @@ int scan_path(char *path);
|
||||||
int scan_gettags(char *file, MP3FILE *pmp3);
|
int scan_gettags(char *file, MP3FILE *pmp3);
|
||||||
int scan_get_mp3tags(char *file, MP3FILE *pmp3);
|
int scan_get_mp3tags(char *file, MP3FILE *pmp3);
|
||||||
int scan_get_aactags(char *file, MP3FILE *pmp3);
|
int scan_get_aactags(char *file, MP3FILE *pmp3);
|
||||||
|
int scan_get_nultags(char *file, MP3FILE *pmp3) { return 0; };
|
||||||
int scan_get_fileinfo(char *file, MP3FILE *pmp3);
|
int scan_get_fileinfo(char *file, MP3FILE *pmp3);
|
||||||
int scan_get_mp3fileinfo(char *file, MP3FILE *pmp3);
|
int scan_get_mp3fileinfo(char *file, MP3FILE *pmp3);
|
||||||
int scan_get_aacfileinfo(char *file, MP3FILE *pmp3);
|
int scan_get_aacfileinfo(char *file, MP3FILE *pmp3);
|
||||||
|
int scan_get_nulfileinfo(char *file, MP3FILE *pmp3) { return 0; };
|
||||||
|
int scan_get_urlfileinfo(char *file, MP3FILE *pmp3);
|
||||||
|
|
||||||
int scan_freetags(MP3FILE *pmp3);
|
int scan_freetags(MP3FILE *pmp3);
|
||||||
void scan_static_playlist(char *path, struct dirent *pde, struct stat *psb);
|
void scan_static_playlist(char *path, struct dirent *pde, struct stat *psb);
|
||||||
|
@ -267,12 +270,14 @@ typedef struct {
|
||||||
int (*tags)(char* file, MP3FILE* pmp3);
|
int (*tags)(char* file, MP3FILE* pmp3);
|
||||||
int (*files)(char* file, MP3FILE* pmp3);
|
int (*files)(char* file, MP3FILE* pmp3);
|
||||||
} taghandler;
|
} taghandler;
|
||||||
|
|
||||||
static taghandler taghandlers[] = {
|
static taghandler taghandlers[] = {
|
||||||
{ "aac", scan_get_aactags, scan_get_aacfileinfo },
|
{ "aac", scan_get_aactags, scan_get_aacfileinfo },
|
||||||
{ "mp4", scan_get_aactags, scan_get_aacfileinfo },
|
{ "mp4", scan_get_aactags, scan_get_aacfileinfo },
|
||||||
{ "m4a", scan_get_aactags, scan_get_aacfileinfo },
|
{ "m4a", scan_get_aactags, scan_get_aacfileinfo },
|
||||||
{ "m4p", scan_get_aactags, scan_get_aacfileinfo },
|
{ "m4p", scan_get_aactags, scan_get_aacfileinfo },
|
||||||
{ "mp3", scan_get_mp3tags, scan_get_mp3fileinfo },
|
{ "mp3", scan_get_mp3tags, scan_get_mp3fileinfo },
|
||||||
|
{ "url", scan_get_nultags, scan_get_urlfileinfo },
|
||||||
{ NULL, 0 }
|
{ NULL, 0 }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -472,7 +477,7 @@ void scan_music_file(char *path, struct dirent *pde, struct stat *psb) {
|
||||||
if(strlen(pde->d_name) > 4)
|
if(strlen(pde->d_name) > 4)
|
||||||
mp3file.type=strdup(strrchr(pde->d_name, '.') + 1);
|
mp3file.type=strdup(strrchr(pde->d_name, '.') + 1);
|
||||||
|
|
||||||
/* FIXME; assumes that st_ino is a u_int_32
|
/* FIXME: assumes that st_ino is a u_int_32
|
||||||
DWB: also assumes that the library is contained entirely within
|
DWB: also assumes that the library is contained entirely within
|
||||||
one file system
|
one file system
|
||||||
*/
|
*/
|
||||||
|
@ -953,6 +958,59 @@ off_t aac_drilltoatom(FILE *aac_fp, char *atom_path, unsigned int *atom_length)
|
||||||
return ftell(aac_fp) - 8;
|
return ftell(aac_fp) - 8;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* scan_get_urlfileinfo
|
||||||
|
*
|
||||||
|
* Get info from a "url" file -- a media stream file
|
||||||
|
*/
|
||||||
|
int scan_get_urlfileinfo(char *file, MP3FILE *pmp3) {
|
||||||
|
FILE *infile;
|
||||||
|
char *head, *tail;
|
||||||
|
char linebuffer[256];
|
||||||
|
|
||||||
|
DPRINTF(ERR_DEBUG,"Getting URL file info\n");
|
||||||
|
|
||||||
|
if(!(infile=fopen(file,"rb"))) {
|
||||||
|
DPRINTF(ERR_WARN,"Could not open %s for reading\n",file);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
fgets(linebuffer,sizeof(linebuffer),infile);
|
||||||
|
while((linebuffer[strlen(linebuffer)-1] == '\n') ||
|
||||||
|
(linebuffer[strlen(linebuffer)-1] == '\r')) {
|
||||||
|
linebuffer[strlen(linebuffer)-1] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
head=linebuffer;
|
||||||
|
tail=strchr(head,',');
|
||||||
|
if(!tail) {
|
||||||
|
DPRINTF(ERR_LOG,"Badly formatted .url file - must be bitrate,descr,url\n");
|
||||||
|
fclose(infile);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
pmp3->bitrate=atoi(head);
|
||||||
|
head=++tail;
|
||||||
|
tail=strchr(head,',');
|
||||||
|
if(!tail) {
|
||||||
|
DPRINTF(ERR_LOG,"Badly formatted .url file - must be bitrate,descr,url\n");
|
||||||
|
fclose(infile);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
*tail++='\0';
|
||||||
|
|
||||||
|
pmp3->title=strdup(head);
|
||||||
|
pmp3->url=strdup(tail);
|
||||||
|
fclose(infile);
|
||||||
|
|
||||||
|
DPRINTF(ERR_DEBUG," Title: %s\n",pmp3->title);
|
||||||
|
DPRINTF(ERR_DEBUG," Bitrate: %d\n",pmp3->bitrate);
|
||||||
|
DPRINTF(ERR_DEBUG," URL: %s\n",pmp3->url);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* scan_get_aacfileinfo
|
* scan_get_aacfileinfo
|
||||||
*
|
*
|
||||||
|
@ -1250,6 +1308,13 @@ void make_composite_tags(MP3FILE *song)
|
||||||
song->description = strdup(fdescr);
|
song->description = strdup(fdescr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(song->url) {
|
||||||
|
song->description = strdup("Playlist URL");
|
||||||
|
song->data_kind=1;
|
||||||
|
} else {
|
||||||
|
song->data_kind=0;
|
||||||
|
}
|
||||||
|
|
||||||
if(!song->title)
|
if(!song->title)
|
||||||
song->title = strdup(song->fname);
|
song->title = strdup(song->fname);
|
||||||
|
|
||||||
|
|
|
@ -37,7 +37,7 @@ typedef struct tag_mp3file {
|
||||||
char *orchestra; /* TPE2 */
|
char *orchestra; /* TPE2 */
|
||||||
char *conductor; /* TPE3 */
|
char *conductor; /* TPE3 */
|
||||||
char *grouping; /* TIT1 */
|
char *grouping; /* TIT1 */
|
||||||
|
char *url; /* daap.songdataurl (asul) */
|
||||||
|
|
||||||
int bitrate;
|
int bitrate;
|
||||||
int samplerate;
|
int samplerate;
|
||||||
|
@ -63,6 +63,7 @@ typedef struct tag_mp3file {
|
||||||
/* generated fields */
|
/* generated fields */
|
||||||
char* description; /* long file type */
|
char* description; /* long file type */
|
||||||
int item_kind; /* song or movie */
|
int item_kind; /* song or movie */
|
||||||
|
int data_kind; /* dmap.datakind (asdk) */
|
||||||
|
|
||||||
char compilation;
|
char compilation;
|
||||||
} MP3FILE;
|
} MP3FILE;
|
||||||
|
|
Loading…
Reference in New Issue