Add daap.songcodectype, normalize daap.songformat and daap.songdescription.

This commit is contained in:
Ron Pedde 2005-04-04 02:21:26 +00:00
parent 33e6284639
commit 622171c1b1
6 changed files with 326 additions and 95 deletions

View File

@ -27,12 +27,10 @@
# #
# #
# This is quite rudimentary. Obvious TODO list is as follows: # This is quite rudimentary but handles most cases quite well.
# #
# - Speedup the streaming start. # TODO-list:
# - Make the guessing of the wav length reliable. # - Make the guessing of the wav length reliable.
# - Implement Apple Lossless, somehow, someone, PLEASE! It could be done
# by integrating QuickTime 6.5 codecs to MPlayer (currently 6.3)
# - Possibly implement some kind of caching. # - Possibly implement some kind of caching.
# #
# I'll probably never have time for above things, but now it should # I'll probably never have time for above things, but now it should
@ -71,23 +69,33 @@ $SIG{PIPE} = 'IGNORE';
if ($off < 0) { if ($off < 0) {
usage(); usage();
} }
if ($fn =~ m/^..*\.wav/i) { if ($fn =~ m/^..*\.wav$/i) {
passthru_proc($fn, $off, $forgelen); passthru_proc($fn, $off, $forgelen);
} elsif ($fn =~ m/^..*\.wave/i) { } elsif ($fn =~ m/^..*\.wave$/i) {
passthru_proc($fn, $off, $forgelen); passthru_proc($fn, $off, $forgelen);
} elsif ($fn =~ m/^..*\.flac/i) { } elsif ($fn =~ m/^..*\.flac$/i) {
flac_proc($fn, $off, $forgelen); flac_proc($fn, $off, $forgelen);
} elsif ($fn =~ m/^..*\.fla/i) { } elsif ($fn =~ m/^..*\.fla$/i) {
flac_proc($fn, $off, $forgelen); flac_proc($fn, $off, $forgelen);
} elsif ($fn =~ m/^..*\.mp3/i) { } elsif ($fn =~ m/^..*\.mp4$/i) {
mpeg4_proc($fn, $off, $forgelen);
} elsif ($fn =~ m/^..*\.m4a$/i) {
mpeg4_proc($fn, $off, $forgelen);
} elsif ($fn =~ m/^..*\.m4p$/i) {
mpeg4_proc($fn, $off, $forgelen);
} elsif ($fn =~ m/^..*\.aac$/i) {
mpeg4_proc($fn, $off, $forgelen);
} elsif ($fn =~ m/^..*\.shn$/i) {
ffmpeg_proc($fn, $off, $forgelen); ffmpeg_proc($fn, $off, $forgelen);
} elsif ($fn =~ m/^..*\.mpg/i) { } elsif ($fn =~ m/^..*\.mp3$/i) {
ffmpeg_proc($fn, $off, $forgelen); ffmpeg_proc($fn, $off, $forgelen);
} elsif ($fn =~ m/^..*\.ogg/i) { } elsif ($fn =~ m/^..*\.mpg$/i) {
ffmpeg_proc($fn, $off, $forgelen); ffmpeg_proc($fn, $off, $forgelen);
} elsif ($fn =~ m/^..*\.avi/i) { } elsif ($fn =~ m/^..*\.ogg$/i) {
ffmpeg_proc($fn, $off, $forgelen); ffmpeg_proc($fn, $off, $forgelen);
} elsif ($fn =~ m/^..*\.au/i) { } elsif ($fn =~ m/^..*\.avi$/i) {
ffmpeg_proc($fn, $off, $forgelen);
} elsif ($fn =~ m/^..*\.au$/i) {
ffmpeg_proc($fn, $off, $forgelen); ffmpeg_proc($fn, $off, $forgelen);
} else { } else {
mplayer_proc($fn, $off, $forgelen); mplayer_proc($fn, $off, $forgelen);
@ -513,3 +521,54 @@ sub mplayer_proc
close($w); close($w);
unlink($tf); unlink($tf);
} }
sub mpeg4_proc
{
my ($fn) = shift;
my ($off) = shift;
my ($forgelen) = shift;
my ($f) = undef;
my ($hdr) = undef;
my ($r) = undef;
if (open($f, "< $fn")) {
my ($rl) = sysread($f, $hdr, 512);
close($f);
if ($rl != 512) {
return undef;
}
} else {
return undef;
}
#
# This detection is really rudimentary, but seems to
# do the job for now.
#
if (index($hdr, "Halac") >= 0) {
$r = alac_proc($fn, $off, $forgelen);
} else {
$r = ffmpeg_proc($fn, $off, $forgelen);
}
return $r;
}
sub alac_proc
{
my ($fn) = shift;
my ($off) = shift;
my ($forgelen) = shift;
my ($r) = undef;
my ($w) = undef;
my ($pid);
$pid = open2($r, $w, 'alac', "$fn");
wav_loop($r, $off, undef); # Ignore $forgelen
close($r);
close($w);
if (waitpid($pid, WNOHANG) <= 0) {
kill(SIGKILL, $pid);
waitpid($pid, 0);
}
}

View File

@ -732,6 +732,29 @@ int db_dmap_add_string(char *where, char *tag, char *value) {
return 8 + strlen(value); return 8 + strlen(value);
} }
/**
* add a literal chunk of data to a dmap block
*
* \param where where to serialize the dmap info
* \param tag what four byte tag
* \param value what to put there
* \param size how much data to cram in there
*/
int db_dmap_add_literal(char *where, char *tag, char *value, int size) {
/* tag */
memcpy(where,tag,4);
/* length */
where[4]=(size >> 24) & 0xFF;
where[5]=(size >> 16) & 0xFF;
where[6]=(size >> 8) & 0xFF;
where[7]=size & 0xFF;
memcpy(where+8,value,size);
return 8+size;
}
/** /**
* add a container type to a dmap block (type 0x0C) * add a container type to a dmap block (type 0x0C)
* *

View File

@ -171,6 +171,7 @@ extern int db_dmap_add_char(char *where, char *tag, char value);
extern int db_dmap_add_short(char *where, char *tag, short value); extern int db_dmap_add_short(char *where, char *tag, short value);
extern int db_dmap_add_int(char *where, char *tag, int value); extern int db_dmap_add_int(char *where, char *tag, int value);
extern int db_dmap_add_string(char *where, char *tag, char *value); extern int db_dmap_add_string(char *where, char *tag, char *value);
extern int db_dmap_add_literal(char *where, char *tag, char *value, int size);
extern int db_dmap_add_container(char *where, char *tag, int size); extern int db_dmap_add_container(char *where, char *tag, int size);
/* Holdover functions from old db interface... /* Holdover functions from old db interface...

View File

@ -244,6 +244,8 @@ int db_sqlite_init(int reload) {
db_sqlite_reload=1; db_sqlite_reload=1;
db_sqlite_exec(E_DBG,"DROP INDEX idx_path"); db_sqlite_exec(E_DBG,"DROP INDEX idx_path");
db_sqlite_exec(E_FATAL,"DELETE FROM songs"); db_sqlite_exec(E_FATAL,"DELETE FROM songs");
} else {
db_sqlite_exec(E_FATAL,"VACUUM");
} }
return 0; return 0;
} }
@ -435,7 +437,8 @@ int db_sqlite_add(MP3FILE *pmp3) {
"%d," // db_timestamp "%d," // db_timestamp
"%d," // disabled "%d," // disabled
"%d," // sample_count "%d," // sample_count
"0)", // force_update "0," // force_update
"'%q')", // codectype
STR(pmp3->path), STR(pmp3->path),
STR(pmp3->fname), STR(pmp3->fname),
STR(pmp3->title), STR(pmp3->title),
@ -468,7 +471,9 @@ int db_sqlite_add(MP3FILE *pmp3) {
pmp3->time_modified, pmp3->time_modified,
pmp3->time_played, pmp3->time_played,
pmp3->db_timestamp, pmp3->db_timestamp,
pmp3->disabled); pmp3->disabled,
pmp3->sample_count,
STR(pmp3->codectype));
if(err == SQLITE_CONSTRAINT) { if(err == SQLITE_CONSTRAINT) {
/* probably because the path already exists... */ /* probably because the path already exists... */
@ -529,7 +534,8 @@ int db_sqlite_update(MP3FILE *pmp3) {
"bpm=%d," // bpm "bpm=%d," // bpm
"compilation=%d," // compilation "compilation=%d," // compilation
"rating=%d," // rating "rating=%d," // rating
"sample_count=%d" // sample_count "sample_count=%d," // sample_count
"codectype='%q'" // codec
" WHERE path='%q'", " WHERE path='%q'",
STR(pmp3->title), STR(pmp3->title),
STR(pmp3->artist), STR(pmp3->artist),
@ -557,6 +563,7 @@ int db_sqlite_update(MP3FILE *pmp3) {
pmp3->compilation, pmp3->compilation,
pmp3->rating, pmp3->rating,
pmp3->sample_count, pmp3->sample_count,
STR(pmp3->codectype),
pmp3->path); pmp3->path);
if((db_sqlite_in_scan) && (!db_sqlite_reload)) { if((db_sqlite_in_scan) && (!db_sqlite_reload)) {
@ -1021,7 +1028,9 @@ int db_sqlite_get_size(DBQUERYINFO *pinfo, char **valarray) {
if(db_wantsmeta(pinfo->meta, metaContainerItemId)) if(db_wantsmeta(pinfo->meta, metaContainerItemId))
/* mcti */ /* mcti */
size += 12; size += 12;
if(ISSTR(valarray[37]) && db_wantsmeta(pinfo->meta, metaSongCodecType))
/* ascd */
size += 12;
return size; return size;
break; break;
@ -1146,6 +1155,8 @@ int db_sqlite_build_dmap(DBQUERYINFO *pinfo, char **valarray, char *presult, int
current += db_dmap_add_char(current,"asur",(char)atoi(valarray[25])); current += db_dmap_add_char(current,"asur",(char)atoi(valarray[25]));
if(valarray[18] && atoi(valarray[18]) && db_wantsmeta(pinfo->meta, metaSongYear)) if(valarray[18] && atoi(valarray[18]) && db_wantsmeta(pinfo->meta, metaSongYear))
current += db_dmap_add_short(current,"asyr",(short)atoi(valarray[18])); current += db_dmap_add_short(current,"asyr",(short)atoi(valarray[18]));
if(ISSTR(valarray[37]) && db_wantsmeta(pinfo->meta, metaSongCodecType))
current += db_dmap_add_literal(current,"ascd",valarray[37],4);
if(db_wantsmeta(pinfo->meta, metaContainerItemId)) if(db_wantsmeta(pinfo->meta, metaContainerItemId))
current += db_dmap_add_int(current,"mcti",atoi(valarray[0])); current += db_dmap_add_int(current,"mcti",atoi(valarray[0]));
return 0; return 0;
@ -1205,6 +1216,7 @@ void db_sqlite_build_mp3file(char **valarray, MP3FILE *pmp3) {
pmp3->disabled=db_sqlite_atoi(valarray[34]); pmp3->disabled=db_sqlite_atoi(valarray[34]);
pmp3->sample_count=db_sqlite_atoi(valarray[35]); pmp3->sample_count=db_sqlite_atoi(valarray[35]);
pmp3->force_update=db_sqlite_atoi(valarray[36]); pmp3->force_update=db_sqlite_atoi(valarray[36]);
pmp3->codectype=db_sqlite_strdup(valarray[37]);
} }
/** /**
@ -1289,6 +1301,7 @@ void db_sqlite_dispose_item(MP3FILE *pmp3) {
MAYBEFREE(pmp3->grouping); MAYBEFREE(pmp3->grouping);
MAYBEFREE(pmp3->description); MAYBEFREE(pmp3->description);
MAYBEFREE(pmp3->url); MAYBEFREE(pmp3->url);
MAYBEFREE(pmp3->codectype);
free(pmp3); free(pmp3);
} }
@ -1411,6 +1424,71 @@ char *db_sqlite_upgrade_scripts[] = {
"REPLACE INTO config VALUES('rescan',NULL,1);\n" "REPLACE INTO config VALUES('rescan',NULL,1);\n"
"UPDATE config SET value=2 WHERE term='version';\n", "UPDATE config SET value=2 WHERE term='version';\n",
/* version 2 -> version 3 */
/* add daap.songcodectype, normalize daap.songformat and daap.songdescription */
"drop index idx_path;\n"
"create temp table tempsongs as select * from songs;\n"
"drop table songs;\n"
"CREATE TABLE songs (\n"
" id INTEGER PRIMARY KEY NOT NULL,\n"
" path VARCHAR(4096) UNIQUE NOT NULL,\n"
" fname VARCHAR(255) NOT NULL,\n"
" title VARCHAR(1024) DEFAULT NULL,\n"
" artist VARCHAR(1024) DEFAULT NULL,\n"
" album VARCHAR(1024) DEFAULT NULL,\n"
" genre VARCHAR(255) DEFAULT NULL,\n"
" comment VARCHAR(4096) DEFAULT NULL,\n"
" type VARCHAR(255) DEFAULT NULL,\n"
" composer VARCHAR(1024) DEFAULT NULL,\n"
" orchestra VARCHAR(1024) DEFAULT NULL,\n"
" conductor VARCHAR(1024) DEFAULT NULL,\n"
" grouping VARCHAR(1024) DEFAULT NULL,\n"
" url VARCHAR(1024) DEFAULT NULL,\n"
" bitrate INTEGER DEFAULT 0,\n"
" samplerate INTEGER DEFAULT 0,\n"
" song_length INTEGER DEFAULT 0,\n"
" file_size INTEGER DEFAULT 0,\n"
" year INTEGER DEFAULT 0,\n"
" track INTEGER DEFAULT 0,\n"
" total_tracks INTEGER DEFAULT 0,\n"
" disc INTEGER DEFAULT 0,\n"
" total_discs INTEGER DEFAULT 0,\n"
" bpm INTEGER DEFAULT 0,\n"
" compilation INTEGER DEFAULT 0,\n"
" rating INTEGER DEFAULT 0,\n"
" play_count INTEGER DEFAULT 0,\n"
" data_kind INTEGER DEFAULT 0,\n"
" item_kind INTEGER DEFAULT 0,\n"
" description INTEGER DEFAULT 0,\n"
" time_added INTEGER DEFAULT 0,\n"
" time_modified INTEGER DEFAULT 0,\n"
" time_played INTEGER DEFAULT 0,\n"
" db_timestamp INTEGER DEFAULT 0,\n"
" disabled INTEGER DEFAULT 0,\n"
" sample_count INTEGER DEFAULT 0,\n"
" force_update INTEGER DEFAULT 0,\n"
" codectype VARCHAR(5) DEFAULT NULL\n"
");\n"
"begin transaction;\n"
"insert into songs select *,NULL from tempsongs;\n"
"commit transaction;\n"
"update songs set type=lower(type);\n"
"update songs set type='m4a' where type='aac' or type='mp4';\n"
"update songs set type='flac' where type='fla';\n"
"update songs set description='AAC audio file' where type='m4a';\n"
"update songs set description='MPEG audio file' where type='mp3';\n"
"update songs set description='WAV audio file' where type='wav';\n"
"update songs set description='Playlist URL' where type='pls';\n"
"update songs set description='Ogg Vorbis audio file' where type='ogg';\n"
"update songs set description='FLAC audio file' where type='flac';\n"
"update songs set codectype='mp4a' where type='m4a' or type='m4p';\n"
"update songs set codectype='mpeg' where type='mp3';\n"
"update songs set codectype='ogg' where type='ogg';\n"
"update songs set codectype='flac' where type='flac';\n"
"update songs set force_update=1 where type='m4a';\n" /* look for alac */
"create index idx_path on songs(path);\n"
"drop table tempsongs;\n"
"update config set value=3 where term='version';\n",
NULL /* No more versions! */ NULL /* No more versions! */
}; };

View File

@ -264,6 +264,20 @@ char *scan_winamp_genre[] = {
#define WINAMP_GENRE_UNKNOWN 148 #define WINAMP_GENRE_UNKNOWN 148
/*
* Typedefs
*/
typedef struct {
char *suffix;
int (*tags)(char* file, MP3FILE* pmp3);
int (*files)(char* file, MP3FILE* pmp3);
char *type; /* daap.songformat */
char *codectype; /* song.codectype */
char *description; /* daap.songdescription */
} TAGHANDLER;
/* /*
* Forwards * Forwards
@ -287,6 +301,8 @@ static void scan_music_file(char *path, struct dirent *pde, struct stat *psb);
static int scan_decode_mp3_frame(unsigned char *frame, SCAN_FRAMEINFO *pfi); static int scan_decode_mp3_frame(unsigned char *frame, SCAN_FRAMEINFO *pfi);
static time_t mac_to_unix_time(int t); static time_t mac_to_unix_time(int t);
static TAGHANDLER *scan_gethandler(char *type);
#ifdef OGGVORBIS #ifdef OGGVORBIS
extern int scan_get_oggfileinfo(char *filename, MP3FILE *pmp3); extern int scan_get_oggfileinfo(char *filename, MP3FILE *pmp3);
#endif #endif
@ -296,35 +312,46 @@ extern int scan_get_flacfileinfo(char *filename, MP3FILE *pmp3);
extern int scan_get_flactags(char *filename, MP3FILE *pmp3); extern int scan_get_flactags(char *filename, MP3FILE *pmp3);
#endif #endif
/*
* Typedefs /* For known types, I'm gong to use the "official" apple
* daap.songformat, daap.songdescription, and daap.songcodecsubtype.
* If I we don't have "official" ones, we can make them up the
* way we currently are: using extension or whatver.
*
* This means that you can test to see if something is, say, an un-drmed
* aac file by just testing for ->type "m4a", rather than checking every different
* flavor of file extension.
*
* NOTE: Although they are represented here as strings, the codectype is *really*
* an unsigned short. So when it gets serialized, it gets serialized as a short int.
* If you put something other than 3 or 4 characters as your codectype, you'll see
* strange results.
*
* FIXME: url != pls -- this method of dispatching handlers based on file type
* is completely wrong. There needs to be a separate type that gets carried around
* with it, at least outside the database that says where the info CAME FROM.
*
* This system is broken, and won't work with something like a .cue file
*/ */
static TAGHANDLER taghandlers[] = {
typedef struct { { "aac", scan_get_aactags, scan_get_aacfileinfo, "m4a", "mp4a", "AAC audio file" },
char* suffix; { "mp4", scan_get_aactags, scan_get_aacfileinfo, "m4a", "mp4a", "AAC audio file" },
int (*tags)(char* file, MP3FILE* pmp3); { "m4a", scan_get_aactags, scan_get_aacfileinfo, "m4a", "mp4a", "AAC audio file" },
int (*files)(char* file, MP3FILE* pmp3); { "m4p", scan_get_aactags, scan_get_aacfileinfo, "m4p", "mp4a", "AAC audio file" },
} taghandler; { "mp3", scan_get_mp3tags, scan_get_mp3fileinfo, "mp3", "mpeg", "MPEG audio file" },
{ "wav", scan_get_nultags, scan_get_wavfileinfo, "wav", "wav", "WAV audio file" },
static taghandler taghandlers[] = { { "url", scan_get_nultags, scan_get_urlfileinfo, "pls", NULL, "Playlist URL" },
{ "aac", scan_get_aactags, scan_get_aacfileinfo }, { "pls", scan_get_nultags, scan_get_urlfileinfo, "pls", NULL, "Playlist URL" },
{ "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 },
{ "wav", scan_get_nultags, scan_get_wavfileinfo },
{ "url", scan_get_nultags, scan_get_urlfileinfo },
#ifdef OGGVORBIS #ifdef OGGVORBIS
{ "ogg", scan_get_nultags, scan_get_oggfileinfo }, { "ogg", scan_get_nultags, scan_get_oggfileinfo, "ogg", "ogg", "Ogg Vorbis audio file" },
#endif #endif
#ifdef FLAC #ifdef FLAC
{ "flac", scan_get_flactags, scan_get_flacfileinfo }, { "flac", scan_get_flactags, scan_get_flacfileinfo, "flac","flac", "FLAC audio file" },
{ "fla", scan_get_flactags, scan_get_flacfileinfo }, { "fla", scan_get_flactags, scan_get_flacfileinfo, "flac","flac", "FLAC audio file" },
#endif #endif
{ NULL, 0 } { NULL, NULL, NULL, NULL, NULL, NULL }
}; };
/** /**
* Convert mac time to unix time (different epochs) * Convert mac time to unix time (different epochs)
* *
@ -520,6 +547,7 @@ void scan_static_playlist(char *path, struct dirent *pde, struct stat *psb) {
DPRINTF(E_WARN,L_SCAN|L_PL,"Done processing playlist\n"); DPRINTF(E_WARN,L_SCAN|L_PL,"Done processing playlist\n");
} }
*/ */
/* /*
* scan_music_file * scan_music_file
* *
@ -528,6 +556,10 @@ void scan_static_playlist(char *path, struct dirent *pde, struct stat *psb) {
void scan_music_file(char *path, struct dirent *pde, struct stat *psb) { void scan_music_file(char *path, struct dirent *pde, struct stat *psb) {
MP3FILE mp3file; MP3FILE mp3file;
char mp3_path[PATH_MAX]; char mp3_path[PATH_MAX];
char *current=NULL;
char *type;
TAGHANDLER *ptaghandler;
char fdescr[50];
snprintf(mp3_path,sizeof(mp3_path),"%s/%s",path,pde->d_name); snprintf(mp3_path,sizeof(mp3_path),"%s/%s",path,pde->d_name);
@ -537,14 +569,38 @@ void scan_music_file(char *path, struct dirent *pde, struct stat *psb) {
memset((void*)&mp3file,0,sizeof(mp3file)); memset((void*)&mp3file,0,sizeof(mp3file));
mp3file.path=strdup(mp3_path); mp3file.path=strdup(mp3_path);
mp3file.fname=strdup(pde->d_name); mp3file.fname=strdup(pde->d_name);
if(strlen(pde->d_name) > 4) if(strlen(pde->d_name) > 4) {
mp3file.type=strdup(strrchr(pde->d_name, '.') + 1); type = strrchr(pde->d_name, '.') + 1;
if(type) {
/* see if there is "official" format and info for it */
ptaghandler=scan_gethandler(type);
if(ptaghandler) {
/* yup, use the official format */
mp3file.type=strdup(ptaghandler->type);
if(ptaghandler->description)
mp3file.description=strdup(ptaghandler->description);
/* FIXME: assumes that st_ino is a u_int_32 if(ptaghandler->codectype)
DWB: also assumes that the library is contained entirely within mp3file.codectype=strdup(ptaghandler->codectype);
one file system
*/ DPRINTF(E_DBG,L_SCAN,"Codec type: %s\n",mp3file.codectype);
mp3file.id=psb->st_ino; } else {
/* just dummy up songformat, codectype and description */
mp3file.type=strdup(type);
/* upper-case types cause some problems */
current=mp3file.type;
while(*current) {
*current=tolower(*current);
current++;
}
sprintf(fdescr,"%s audio file",mp3file.type);
mp3file.description = strdup(fdescr);
/* we'll just dodge the codectype */
}
}
}
/* Do the tag lookup here */ /* Do the tag lookup here */
if(!scan_gettags(mp3file.path,&mp3file) && if(!scan_gettags(mp3file.path,&mp3file) &&
@ -559,12 +615,11 @@ void scan_music_file(char *path, struct dirent *pde, struct stat *psb) {
mp3file.time_added=psb->st_ctime; mp3file.time_added=psb->st_ctime;
mp3file.time_modified=psb->st_mtime; mp3file.time_modified=psb->st_mtime;
// server_side_convert_set(&mp3file);
DPRINTF(E_DBG,L_SCAN," Date Added: %d\n",mp3file.time_added); DPRINTF(E_DBG,L_SCAN," Date Added: %d\n",mp3file.time_added);
DPRINTF(E_DBG,L_SCAN," Codec: %s\n",mp3file.codectype);
db_add(&mp3file); db_add(&mp3file);
// pl_eval(&mp3file); /* FIXME: move to db_add? */
} else { } else {
DPRINTF(E_WARN,L_SCAN,"Skipping %s - scan_gettags failed\n",pde->d_name); DPRINTF(E_WARN,L_SCAN,"Skipping %s - scan_gettags failed\n",pde->d_name);
} }
@ -720,27 +775,37 @@ int scan_get_aactags(char *file, MP3FILE *pmp3) {
} }
/* /**
* scan_gettags * fetch the taghandler for this file type
*/
TAGHANDLER *scan_gethandler(char *type) {
TAGHANDLER *phdl = taghandlers;
while((phdl->suffix) && (strcasecmp(phdl->suffix,type)))
phdl++;
if(phdl->suffix)
return phdl;
return NULL;
}
/**
* Dispatch the appropriate handler to get specific tag metainfomation
* *
* Scan an mp3 file for id3 tags using libid3tag * \param file file to get tag info for
* \param pmp3 mp3 file struct to fill info into
*/ */
int scan_gettags(char *file, MP3FILE *pmp3) { int scan_gettags(char *file, MP3FILE *pmp3) {
taghandler *hdl; TAGHANDLER *hdl;
/* dispatch to appropriate tag handler */ /* dispatch to appropriate tag handler */
for(hdl = taghandlers ; hdl->suffix ; ++hdl) hdl = scan_gethandler(pmp3->type);
if(!strcasecmp(hdl->suffix, pmp3->type)) if(hdl && hdl->tags)
break;
if(hdl->tags)
return hdl->tags(file,pmp3); return hdl->tags(file,pmp3);
/* maybe this is an extension that we've manually /* otherwise, it's a file type we don't understand yet */
* specified in the config file, but don't know how
* to extract tags from. Ogg, maybe.
*/
return 0; return 0;
} }
@ -760,9 +825,6 @@ int scan_get_mp3tags(char *file, MP3FILE *pmp3) {
char *tmp; char *tmp;
int got_numeric_genre; int got_numeric_genre;
if(strcasecmp(pmp3->type,"mp3")) /* can't get tags for non-mp3 */
return 0;
pid3file=id3_file_open(file,ID3_FILE_MODE_READONLY); pid3file=id3_file_open(file,ID3_FILE_MODE_READONLY);
if(!pid3file) { if(!pid3file) {
DPRINTF(E_WARN,L_SCAN,"Cannot open %s\n",file); DPRINTF(E_WARN,L_SCAN,"Cannot open %s\n",file);
@ -963,28 +1025,27 @@ int scan_freetags(MP3FILE *pmp3) {
MAYBEFREE(pmp3->conductor); MAYBEFREE(pmp3->conductor);
MAYBEFREE(pmp3->grouping); MAYBEFREE(pmp3->grouping);
MAYBEFREE(pmp3->description); MAYBEFREE(pmp3->description);
MAYBEFREE(pmp3->codectype);
return 0; return 0;
} }
/* /**
* scan_get_fileinfo
*
* Dispatch to actual file info handlers * Dispatch to actual file info handlers
*
* \param file file to read file metainfo for
* \param pmp3 struct to stuff with info gleaned
*/ */
int scan_get_fileinfo(char *file, MP3FILE *pmp3) { int scan_get_fileinfo(char *file, MP3FILE *pmp3) {
FILE *infile; FILE *infile;
off_t file_size; off_t file_size;
taghandler *hdl; TAGHANDLER *hdl;
/* dispatch to appropriate tag handler */ /* dispatch to appropriate tag handler */
for(hdl = taghandlers ; hdl->suffix ; ++hdl) hdl = scan_gethandler(pmp3->type);
if(!strcasecmp(hdl->suffix, pmp3->type)) if(hdl && hdl->files)
break;
if(hdl->files)
return hdl->files(file,pmp3); return hdl->files(file,pmp3);
/* a file we don't know anything about... ogg or aiff maybe */ /* a file we don't know anything about... ogg or aiff maybe */
@ -1148,6 +1209,7 @@ int scan_get_aacfileinfo(char *file, MP3FILE *pmp3) {
pmp3->file_size=file_size; pmp3->file_size=file_size;
/* now, hunt for the mvhd atom */ /* now, hunt for the mvhd atom */
atom_offset = aac_drilltoatom(infile, "moov:mvhd", &atom_length); atom_offset = aac_drilltoatom(infile, "moov:mvhd", &atom_length);
if(atom_offset != -1) { if(atom_offset != -1) {
@ -1180,6 +1242,17 @@ int scan_get_aacfileinfo(char *file, MP3FILE *pmp3) {
pmp3->bitrate = 0; pmp3->bitrate = 0;
/* see if it is aac or alac */
atom_offset = aac_drilltoatom(infile, "moov:trak:mdia:minf:stbl:stsd:alac", &atom_length);
if(atom_offset != -1) {
/* should we still pull samplerate, etc from the this atom? */
if(pmp3->codectype) {
free(pmp3->codectype);
}
pmp3->codectype=strdup("alac");
}
/* Get the sample rate from the 'mp4a' atom (timescale). This is also /* Get the sample rate from the 'mp4a' atom (timescale). This is also
found in the 'mdhd' atom which is a bit closer but we need to found in the 'mdhd' atom which is a bit closer but we need to
navigate to the 'mp4a' atom anyways to get to the 'esds' atom. */ navigate to the 'mp4a' atom anyways to get to the 'esds' atom. */
@ -1207,6 +1280,14 @@ int scan_get_aacfileinfo(char *file, MP3FILE *pmp3) {
fread((void *)&bit_rate, sizeof(unsigned int), 1, infile); fread((void *)&bit_rate, sizeof(unsigned int), 1, infile);
pmp3->bitrate = ntohl(bit_rate) / 1000; pmp3->bitrate = ntohl(bit_rate) / 1000;
DPRINTF(E_DBG,L_SCAN,"esds bitrate: %d\n",pmp3->bitrate);
if(pmp3->bitrate > 320) {
DPRINTF(E_LOG,L_SCAN,"Capping AAC bitrate. Please report this "
"message to the forums on www.mt-daapd.org. Thx.\n");
pmp3->bitrate = 320;
}
} else { } else {
DPRINTF(E_DBG,L_SCAN, "Could not find 'esds' atom to determine bit rate.\n"); DPRINTF(E_DBG,L_SCAN, "Could not find 'esds' atom to determine bit rate.\n");
} }
@ -1225,7 +1306,6 @@ int scan_get_aacfileinfo(char *file, MP3FILE *pmp3) {
if ((atom_offset != -1) && (pmp3->song_length)) { if ((atom_offset != -1) && (pmp3->song_length)) {
pmp3->bitrate = atom_length / ((pmp3->song_length / 1000) * 128); pmp3->bitrate = atom_length / ((pmp3->song_length / 1000) * 128);
} }
} }
fclose(infile); fclose(infile);
@ -1809,10 +1889,8 @@ int scan_get_mp3fileinfo(char *file, MP3FILE *pmp3) {
* *
* @param song MP3FILE of the file to build composite tags for * @param song MP3FILE of the file to build composite tags for
*/ */
void make_composite_tags(MP3FILE *song) void make_composite_tags(MP3FILE *song) {
{
int len; int len;
char fdescr[50];
len=0; len=0;
@ -1837,16 +1915,8 @@ void make_composite_tags(MP3FILE *song)
} }
} }
sprintf(fdescr,"%s audio file",song->type);
song->description = strdup(fdescr);
if(song->url) { if(song->url) {
song->description = strdup("Playlist URL");
song->data_kind=1; song->data_kind=1;
/* bit of a hack for the roku soundbridge - type *has* to be pls */
if(song->type)
free(song->type);
song->type = strdup("pls");
} else { } else {
song->data_kind=0; song->data_kind=0;
} }
@ -1854,7 +1924,6 @@ void make_composite_tags(MP3FILE *song)
if(!song->title) if(!song->title)
song->title = strdup(song->fname); song->title = strdup(song->fname);
/* Ogg used to be set as an item_kind of 4. Dunno why */ song->item_kind = 2; /* music, I think. */
song->item_kind = 2;
} }

View File

@ -64,6 +64,7 @@ typedef struct tag_mp3file {
unsigned int id; unsigned int id;
char *description; /* long file type */ char *description; /* long file type */
char *codectype; /* song.codectype */
int item_kind; /* song or movie */ int item_kind; /* song or movie */
int data_kind; /* dmap.datakind (asdk) */ int data_kind; /* dmap.datakind (asdk) */
int force_update; int force_update;