Add daap.songcodectype, normalize daap.songformat and daap.songdescription.
This commit is contained in:
parent
33e6284639
commit
622171c1b1
|
@ -27,13 +27,11 @@
|
||||||
#
|
#
|
||||||
|
|
||||||
#
|
#
|
||||||
# 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
|
# - Possibly implement some kind of caching.
|
||||||
# by integrating QuickTime 6.5 codecs to MPlayer (currently 6.3)
|
|
||||||
# - 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
|
||||||
# be easy for someone to take this over, since basically all you have
|
# be easy for someone to take this over, since basically all you have
|
||||||
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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)
|
||||||
*
|
*
|
||||||
|
|
|
@ -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...
|
||||||
|
|
|
@ -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! */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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;
|
return hdl->tags(file,pmp3);
|
||||||
|
|
||||||
if(hdl->tags)
|
|
||||||
return hdl->tags(file, pmp3);
|
|
||||||
|
|
||||||
/* maybe this is an extension that we've manually
|
|
||||||
* specified in the config file, but don't know how
|
|
||||||
* to extract tags from. Ogg, maybe.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
/* otherwise, it's a file type we don't understand yet */
|
||||||
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,29 +1025,28 @@ 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;
|
return hdl->files(file,pmp3);
|
||||||
|
|
||||||
if(hdl->files)
|
|
||||||
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 */
|
||||||
if(!(infile=fopen(file,"rb"))) {
|
if(!(infile=fopen(file,"rb"))) {
|
||||||
|
@ -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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -63,7 +63,8 @@ typedef struct tag_mp3file {
|
||||||
int got_id3;
|
int got_id3;
|
||||||
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;
|
||||||
|
|
Loading…
Reference in New Issue