mirror of
				https://github.com/owntone/owntone-server.git
				synced 2025-10-29 15:55:02 -04:00 
			
		
		
		
	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_i32,  	"daap.songcompilation",	OFFSET_OF(MP3FILE, compilation) }, | ||||
|     { qft_string,	"daap.songcomposer",	OFFSET_OF(MP3FILE, composer) }, | ||||
|     //    { qft_i32_const,	"daap.songdatakind", 0 },
 | ||||
|     //    { qft_string,	"daap.songdataurl",	OFFSET_OF(MP3FILE, url) },
 | ||||
|     { qft_i32,  	"daap.songdatakind",    OFFSET_OF(MP3FILE, data_kind) }, | ||||
|     { qft_string,	"daap.songdataurl",	OFFSET_OF(MP3FILE, url) }, | ||||
|     { qft_i32,		"daap.songdateadded",	OFFSET_OF(MP3FILE, time_added) }, | ||||
|     { qft_i32,		"daap.songdatemodified",OFFSET_OF(MP3FILE, time_modified) }, | ||||
|     { 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; | ||||
|     int			songs = 0; | ||||
| 
 | ||||
|     DPRINTF(ERR_DEBUG,"enter daap_response_songlist\n"); | ||||
| 
 | ||||
|     // if the meta tag is specified, encode it, if it's not specified
 | ||||
|     // we're given the latitude to select our own subset, for
 | ||||
|     // simplicity we just include everything.
 | ||||
| @ -429,7 +431,7 @@ DAAP_BLOCK *daap_response_songlist(char* metaStr, char* query) { | ||||
| 
 | ||||
|     henum=db_enum_begin(); | ||||
|     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; | ||||
|     } | ||||
| 
 | ||||
| @ -462,7 +464,7 @@ DAAP_BLOCK *daap_response_songlist(char* metaStr, char* query) { | ||||
| 	query_free(filter); | ||||
| 
 | ||||
|     if(!g) { | ||||
| 	DPRINTF(ERR_DEBUG,"Error enumerating database\n"); | ||||
| 	DPRINTF(ERR_DEBUG,"Error enumerating database - exiting daap_response_songlist\n"); | ||||
| 	daap_free(root); | ||||
| 	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, "mrco", songs); | ||||
| 
 | ||||
|     DPRINTF(ERR_DEBUG,"Exiting daap_response_songlist\n"); | ||||
|     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 */ | ||||
| 
 | ||||
| 	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))) | ||||
| 	    g = g && daap_add_string(mlit,"asal",song->album); | ||||
|  | ||||
| @ -46,7 +46,7 @@ | ||||
| /*
 | ||||
|  * Defines | ||||
|  */ | ||||
| #define DB_VERSION 4 | ||||
| #define DB_VERSION 5 | ||||
| #define STRLEN(a) (a) ? strlen((a)) + 1 : 1  | ||||
| #define MAYBEFREE(a) { if((a)) free((a)); }; | ||||
| 
 | ||||
| @ -121,7 +121,7 @@ typedef struct tag_mp3packed { | ||||
|     int conductor_len; | ||||
|     int grouping_len; | ||||
| 
 | ||||
|     /* DB VERSION 4 AND ABOVE GO HERE! */ | ||||
|     int url_len;  /* DB Version 4 */ | ||||
|     char data[1]; | ||||
| } MP3PACKED; | ||||
| 
 | ||||
| @ -549,6 +549,7 @@ datum *db_packrecord(MP3FILE *pmp3) { | ||||
|     len += STRLEN(pmp3->orchestra); | ||||
|     len += STRLEN(pmp3->conductor); | ||||
|     len += STRLEN(pmp3->grouping); | ||||
|     len += STRLEN(pmp3->url); | ||||
| 
 | ||||
|     result = (datum*) malloc(sizeof(datum)); | ||||
|     if(!result) | ||||
| @ -598,6 +599,7 @@ datum *db_packrecord(MP3FILE *pmp3) { | ||||
|     ppacked->orchestra_len=STRLEN(pmp3->orchestra); | ||||
|     ppacked->conductor_len=STRLEN(pmp3->conductor); | ||||
|     ppacked->grouping_len=STRLEN(pmp3->grouping); | ||||
|     ppacked->url_len=STRLEN(pmp3->url); | ||||
| 
 | ||||
|     offset=0; | ||||
|     if(pmp3->path) | ||||
| @ -648,6 +650,11 @@ datum *db_packrecord(MP3FILE *pmp3) { | ||||
| 	memcpy(&ppacked->data[offset],pmp3->grouping,ppacked->grouping_len); | ||||
|     offset+=ppacked->grouping_len; | ||||
| 
 | ||||
|     if(pmp3->url) | ||||
| 	memcpy(&ppacked->data[offset],pmp3->url,ppacked->url_len); | ||||
|     offset+=ppacked->url_len; | ||||
| 
 | ||||
| 
 | ||||
|     /* whew */ | ||||
|     return result; | ||||
| } | ||||
| @ -738,6 +745,10 @@ int db_unpackrecord(datum *pdatum, MP3FILE *pmp3) { | ||||
| 	pmp3->grouping=strdup(&ppacked->data[offset]); | ||||
|     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? */ | ||||
|     make_composite_tags(pmp3); | ||||
| 
 | ||||
| @ -822,6 +833,7 @@ void db_freefile(MP3FILE *pmp3) { | ||||
|     MAYBEFREE(pmp3->conductor); | ||||
|     MAYBEFREE(pmp3->grouping); | ||||
|     MAYBEFREE(pmp3->description); | ||||
|     MAYBEFREE(pmp3->url); | ||||
| } | ||||
| 
 | ||||
| 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_get_mp3tags(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_mp3fileinfo(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); | ||||
| 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		(*files)(char* file, MP3FILE* pmp3); | ||||
| } taghandler; | ||||
| 
 | ||||
| static taghandler taghandlers[] = { | ||||
|     { "aac", scan_get_aactags, scan_get_aacfileinfo }, | ||||
|     { "mp4", scan_get_aactags, scan_get_aacfileinfo }, | ||||
|     { "m4a", scan_get_aactags, scan_get_aacfileinfo }, | ||||
|     { "m4p", scan_get_aactags, scan_get_aacfileinfo }, | ||||
|     { "mp3", scan_get_mp3tags, scan_get_mp3fileinfo }, | ||||
|     { "url", scan_get_nultags, scan_get_urlfileinfo }, | ||||
|     { NULL, 0 } | ||||
| }; | ||||
| 
 | ||||
| @ -472,7 +477,7 @@ void scan_music_file(char *path, struct dirent *pde, struct stat *psb) { | ||||
|     if(strlen(pde->d_name) > 4) | ||||
| 	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 | ||||
|        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; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * 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 | ||||
|  * | ||||
| @ -1250,6 +1308,13 @@ void make_composite_tags(MP3FILE *song) | ||||
| 	song->description = strdup(fdescr); | ||||
|     } | ||||
| 
 | ||||
|     if(song->url) { | ||||
| 	song->description = strdup("Playlist URL"); | ||||
| 	song->data_kind=1; | ||||
|     } else { | ||||
| 	song->data_kind=0; | ||||
|     } | ||||
| 
 | ||||
|     if(!song->title) | ||||
| 	song->title = strdup(song->fname); | ||||
| 
 | ||||
|  | ||||
| @ -37,7 +37,7 @@ typedef struct tag_mp3file { | ||||
|     char *orchestra; /* TPE2 */ | ||||
|     char *conductor; /* TPE3 */ | ||||
|     char *grouping;  /* TIT1 */ | ||||
| 
 | ||||
|     char *url;       /* daap.songdataurl (asul) */ | ||||
| 
 | ||||
|     int bitrate; | ||||
|     int samplerate; | ||||
| @ -63,6 +63,7 @@ typedef struct tag_mp3file { | ||||
|     /* generated fields */ | ||||
|     char* description;		/* long file type */ | ||||
|     int item_kind;		/* song or movie */ | ||||
|     int data_kind;              /* dmap.datakind (asdk) */ | ||||
| 
 | ||||
|   char compilation; | ||||
| } MP3FILE; | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user