Compare commits
2 Commits
1a9b32ee7a
...
4cbe796774
Author | SHA1 | Date | |
---|---|---|---|
|
4cbe796774 | ||
|
326b491fde |
19
Makefile.am
19
Makefile.am
@ -15,7 +15,7 @@
|
|||||||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
|
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
|
||||||
# USA.
|
# USA.
|
||||||
|
|
||||||
AM_CFLAGS = -Wall -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64
|
AM_CFLAGS = -Wall -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 @libpng_CFLAGS@ @opus_CFLAGS@
|
||||||
|
|
||||||
SUBDIRS=po
|
SUBDIRS=po
|
||||||
|
|
||||||
@ -55,14 +55,15 @@ endif
|
|||||||
endif
|
endif
|
||||||
|
|
||||||
minidlnad_LDADD = \
|
minidlnad_LDADD = \
|
||||||
@LIBJPEG_LIBS@ \
|
@LIBJPEG_LIBS@ \
|
||||||
@LIBID3TAG_LIBS@ \
|
@LIBID3TAG_LIBS@ \
|
||||||
@LIBSQLITE3_LIBS@ \
|
@LIBSQLITE3_LIBS@ \
|
||||||
@LIBAVFORMAT_LIBS@ \
|
@LIBAVFORMAT_LIBS@ \
|
||||||
@LIBEXIF_LIBS@ \
|
@LIBEXIF_LIBS@ \
|
||||||
@LIBINTL@ \
|
@LIBINTL@ \
|
||||||
@LIBICONV@ \
|
@LIBICONV@ \
|
||||||
-lFLAC $(flacogglibs) $(vorbislibs) $(avahilibs)
|
-lFLAC $(flacogglibs) $(vorbislibs) $(avahilibs) \
|
||||||
|
@opus_LIBS@
|
||||||
|
|
||||||
testupnpdescgen_SOURCES = testupnpdescgen.c upnpdescgen.c
|
testupnpdescgen_SOURCES = testupnpdescgen.c upnpdescgen.c
|
||||||
testupnpdescgen_LDADD = \
|
testupnpdescgen_LDADD = \
|
||||||
|
@ -462,6 +462,12 @@ AC_CHECK_LIB(vorbisfile, ov_open_callbacks,
|
|||||||
AM_CONDITIONAL(HAVE_VORBISFILE, false))],
|
AM_CONDITIONAL(HAVE_VORBISFILE, false))],
|
||||||
AM_CONDITIONAL(HAVE_VORBISFILE, false),
|
AM_CONDITIONAL(HAVE_VORBISFILE, false),
|
||||||
-lvorbis -logg)
|
-lvorbis -logg)
|
||||||
|
#test if we have opus/opusfile
|
||||||
|
|
||||||
|
PKG_CHECK_MODULES([opus], [opus opusfile],
|
||||||
|
AM_CONDITIONAL(HAVE_OPUS, true)
|
||||||
|
AC_DEFINE(HAVE_OPUS,1,[Define to 1 if you have opus/opusfile]),
|
||||||
|
AM_CONDITIONAL(HAVE_OPUS, false))
|
||||||
AC_CHECK_LIB(FLAC, FLAC__stream_decoder_init_stream,
|
AC_CHECK_LIB(FLAC, FLAC__stream_decoder_init_stream,
|
||||||
[AC_CHECK_HEADERS([FLAC/all.h],
|
[AC_CHECK_HEADERS([FLAC/all.h],
|
||||||
AM_CONDITIONAL(HAVE_FLAC, true)
|
AM_CONDITIONAL(HAVE_FLAC, true)
|
||||||
|
54
metadata.c
54
metadata.c
@ -343,11 +343,55 @@ GetAudioMetadata(const char *path, const char *name)
|
|||||||
strcpy(type, "wav");
|
strcpy(type, "wav");
|
||||||
m.mime = strdup("audio/x-wav");
|
m.mime = strdup("audio/x-wav");
|
||||||
}
|
}
|
||||||
else if( ends_with(path, ".ogg") || ends_with(path, ".oga") )
|
else if( ends_with(path,".oga") || ends_with(path,".ogg"))
|
||||||
{
|
{
|
||||||
strcpy(type, "ogg");
|
/* The .ogg/.oga file extensions present something of a problem.
|
||||||
m.mime = strdup("audio/ogg");
|
* ".ogg" has been deprecated in favor of ".oga" for some time, but
|
||||||
}
|
* many applications still only recognize ".ogg".
|
||||||
|
*
|
||||||
|
* This examines the file and causes .ogg to be presented for any naked
|
||||||
|
* Vorbis file (MIME type audio/ogg; codecs=vorbis) and .oga
|
||||||
|
* (audio/ogg) to be used for everything else. This is in line with
|
||||||
|
* the official ogg naming conventions and, hopefully, makes for a
|
||||||
|
* resonable compromise.
|
||||||
|
*/
|
||||||
|
uint8_t oggtestbuf[35];
|
||||||
|
FILE *oggfile = fopen (path, "rb");
|
||||||
|
|
||||||
|
if (oggfile == (FILE *)NULL)
|
||||||
|
{
|
||||||
|
DPRINTF(E_ERROR, L_METADATA, "Error opening %s\n", path);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fread (oggtestbuf, 1, 35, oggfile) != 35)
|
||||||
|
{
|
||||||
|
DPRINTF(E_WARN, L_METADATA, "Premature EOF on %s\n", path);
|
||||||
|
fclose (oggfile);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
fclose (oggfile);
|
||||||
|
|
||||||
|
if (memcmp (&oggtestbuf[28], "\x01vorbis", 7))
|
||||||
|
m.mime = strdup ("audio/ogg");
|
||||||
|
else
|
||||||
|
m.mime = strdup ("audio/ogg; codecs=vorbis");
|
||||||
|
|
||||||
|
strcpy(type, "ogg");
|
||||||
|
}
|
||||||
|
else if ( ends_with(path, ".opus") )
|
||||||
|
{
|
||||||
|
strcpy(type,"ops");
|
||||||
|
m.mime = strdup("audio/ogg; codecs=opus");
|
||||||
|
}
|
||||||
|
#if 0
|
||||||
|
/* Not supported yet, and probably won't be. */
|
||||||
|
else if( ends_with(path, ".ogx") )
|
||||||
|
{
|
||||||
|
strcpy(type, "ogx");
|
||||||
|
m.mime = strdup("application/ogg");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
else if( ends_with(path, ".pcm") )
|
else if( ends_with(path, ".pcm") )
|
||||||
{
|
{
|
||||||
strcpy(type, "pcm");
|
strcpy(type, "pcm");
|
||||||
|
59
tagutils/opus.c
Normal file
59
tagutils/opus.c
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
//=========================================================================
|
||||||
|
// FILENAME : tagutils-opus.c
|
||||||
|
// DESCRIPTION : Opus metadata reader
|
||||||
|
//=========================================================================
|
||||||
|
|
||||||
|
static int
|
||||||
|
_get_opusfileinfo(char *filename, struct song_metadata *psong)
|
||||||
|
{
|
||||||
|
OggOpusFile *opusfile;
|
||||||
|
const OpusTags *tags;
|
||||||
|
char **comment;
|
||||||
|
int *commentlen;
|
||||||
|
int j, e;
|
||||||
|
|
||||||
|
|
||||||
|
opusfile = op_open_file (filename, &e);
|
||||||
|
if(!opusfile)
|
||||||
|
{
|
||||||
|
DPRINTF(E_WARN, L_SCANNER,
|
||||||
|
"Error opening input file \"%s\": %s\n", filename, opus_strerror(e));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
DPRINTF(E_MAXDEBUG, L_SCANNER, "Processing file \"%s\"...\n", filename);
|
||||||
|
|
||||||
|
psong->song_length = op_pcm_total (opusfile, -1);
|
||||||
|
if (psong->song_length < 0)
|
||||||
|
{
|
||||||
|
DPRINTF(E_WARN, L_SCANNER,
|
||||||
|
"Unable to obtain length of %s\n", filename);
|
||||||
|
psong->song_length = 0;
|
||||||
|
} else
|
||||||
|
/* Sample rate is always 48k, so length in ms is just samples/48 */
|
||||||
|
psong->song_length /= 48;
|
||||||
|
|
||||||
|
/* Note that this gives only the first link's channel count. */
|
||||||
|
psong->channels = op_channel_count (opusfile, -1);
|
||||||
|
|
||||||
|
psong->samplerate = 48000;
|
||||||
|
psong->bitrate = op_bitrate (opusfile, -1);
|
||||||
|
|
||||||
|
tags = op_tags (opusfile, -1);
|
||||||
|
|
||||||
|
if (!tags)
|
||||||
|
{
|
||||||
|
DPRINTF(E_WARN, L_SCANNER, "Unable to obtain tags from %s\n",
|
||||||
|
filename);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
comment = tags->user_comments;
|
||||||
|
commentlen = tags->comment_lengths;
|
||||||
|
|
||||||
|
for (j = 0; j < tags->comments; j++)
|
||||||
|
vc_scan (psong, *(comment++), *(commentlen++));
|
||||||
|
|
||||||
|
op_free (opusfile);
|
||||||
|
return 0;
|
||||||
|
}
|
6
tagutils/opus.h
Normal file
6
tagutils/opus.h
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
//=========================================================================
|
||||||
|
// FILENAME : tagutils-opus.h
|
||||||
|
// DESCRIPTION : Opus metadata reader
|
||||||
|
//=========================================================================
|
||||||
|
|
||||||
|
static int _get_opusfileinfo(char *filename, struct song_metadata *psong);
|
@ -1,6 +1,6 @@
|
|||||||
//=========================================================================
|
//=========================================================================
|
||||||
// FILENAME : tagutils.c
|
// FILENAME : tagutils.c
|
||||||
// DESCRIPTION : MP3/MP4/Ogg/FLAC metadata reader
|
// DESCRIPTION : MP3/MP4/Ogg/FLAC metadata reader
|
||||||
//=========================================================================
|
//=========================================================================
|
||||||
// Copyright (c) 2008- NETGEAR, Inc. All Rights Reserved.
|
// Copyright (c) 2008- NETGEAR, Inc. All Rights Reserved.
|
||||||
//=========================================================================
|
//=========================================================================
|
||||||
@ -42,6 +42,10 @@
|
|||||||
#ifdef HAVE_ICONV
|
#ifdef HAVE_ICONV
|
||||||
#include <iconv.h>
|
#include <iconv.h>
|
||||||
#endif
|
#endif
|
||||||
|
/* ADD THIS INCLUDE FOR OPUS */
|
||||||
|
#ifdef HAVE_OPUS
|
||||||
|
#include <opusfile.h>
|
||||||
|
#endif
|
||||||
#include <sqlite3.h>
|
#include <sqlite3.h>
|
||||||
#include "tagutils.h"
|
#include "tagutils.h"
|
||||||
#include "../metadata.h"
|
#include "../metadata.h"
|
||||||
@ -49,51 +53,51 @@
|
|||||||
#include "../log.h"
|
#include "../log.h"
|
||||||
|
|
||||||
struct id3header {
|
struct id3header {
|
||||||
unsigned char id[3];
|
unsigned char id[3];
|
||||||
unsigned char version[2];
|
unsigned char version[2];
|
||||||
unsigned char flags;
|
unsigned char flags;
|
||||||
unsigned char size[4];
|
unsigned char size[4];
|
||||||
} __attribute((packed));
|
} __attribute((packed));
|
||||||
|
|
||||||
char *winamp_genre[] = {
|
char *winamp_genre[] = {
|
||||||
/*00*/ "Blues", "Classic Rock", "Country", "Dance",
|
/*00*/ "Blues", "Classic Rock", "Country", "Dance",
|
||||||
"Disco", "Funk", "Grunge", "Hip-Hop",
|
"Disco", "Funk", "Grunge", "Hip-Hop",
|
||||||
/*08*/ "Jazz", "Metal", "New Age", "Oldies",
|
/*08*/ "Jazz", "Metal", "New Age", "Oldies",
|
||||||
"Other", "Pop", "R&B", "Rap",
|
"Other", "Pop", "R&B", "Rap",
|
||||||
/*10*/ "Reggae", "Rock", "Techno", "Industrial",
|
/*10*/ "Reggae", "Rock", "Techno", "Industrial",
|
||||||
"Alternative", "Ska", "Death Metal", "Pranks",
|
"Alternative", "Ska", "Death Metal", "Pranks",
|
||||||
/*18*/ "Soundtrack", "Euro-Techno", "Ambient", "Trip-Hop",
|
/*18*/ "Soundtrack", "Euro-Techno", "Ambient", "Trip-Hop",
|
||||||
"Vocal", "Jazz+Funk", "Fusion", "Trance",
|
"Vocal", "Jazz+Funk", "Fusion", "Trance",
|
||||||
/*20*/ "Classical", "Instrumental", "Acid", "House",
|
/*20*/ "Classical", "Instrumental", "Acid", "House",
|
||||||
"Game", "Sound Clip", "Gospel", "Noise",
|
"Game", "Sound Clip", "Gospel", "Noise",
|
||||||
/*28*/ "AlternRock", "Bass", "Soul", "Punk",
|
/*28*/ "AlternRock", "Bass", "Soul", "Punk",
|
||||||
"Space", "Meditative", "Instrumental Pop", "Instrumental Rock",
|
"Space", "Meditative", "Instrumental Pop", "Instrumental Rock",
|
||||||
/*30*/ "Ethnic", "Gothic", "Darkwave", "Techno-Industrial",
|
/*30*/ "Ethnic", "Gothic", "Darkwave", "Techno-Industrial",
|
||||||
"Electronic", "Pop-Folk", "Eurodance", "Dream",
|
"Electronic", "Pop-Folk", "Eurodance", "Dream",
|
||||||
/*38*/ "Southern Rock", "Comedy", "Cult", "Gangsta",
|
/*38*/ "Southern Rock", "Comedy", "Cult", "Gangsta",
|
||||||
"Top 40", "Christian Rap", "Pop/Funk", "Jungle",
|
"Top 40", "Christian Rap", "Pop/Funk", "Jungle",
|
||||||
/*40*/ "Native American", "Cabaret", "New Wave", "Psychedelic",
|
/*40*/ "Native American", "Cabaret", "New Wave", "Psychedelic",
|
||||||
"Rave", "Showtunes", "Trailer", "Lo-Fi",
|
"Rave", "Showtunes", "Trailer", "Lo-Fi",
|
||||||
/*48*/ "Tribal", "Acid Punk", "Acid Jazz", "Polka",
|
/*48*/ "Tribal", "Acid Punk", "Acid Jazz", "Polka",
|
||||||
"Retro", "Musical", "Rock & Roll", "Hard Rock",
|
"Retro", "Musical", "Rock & Roll", "Hard Rock",
|
||||||
/*50*/ "Folk", "Folk/Rock", "National folk", "Swing",
|
/*50*/ "Folk", "Folk/Rock", "National folk", "Swing",
|
||||||
"Fast-fusion", "Bebob", "Latin", "Revival",
|
"Fast-fusion", "Bebob", "Latin", "Revival",
|
||||||
/*58*/ "Celtic", "Bluegrass", "Avantgarde", "Gothic Rock",
|
/*58*/ "Celtic", "Bluegrass", "Avantgarde", "Gothic Rock",
|
||||||
"Progressive Rock", "Psychedelic Rock", "Symphonic Rock", "Slow Rock",
|
"Progressive Rock", "Psychedelic Rock", "Symphonic Rock", "Slow Rock",
|
||||||
/*60*/ "Big Band", "Chorus", "Easy Listening", "Acoustic",
|
/*60*/ "Big Band", "Chorus", "Easy Listening", "Acoustic",
|
||||||
"Humour", "Speech", "Chanson", "Opera",
|
"Humour", "Speech", "Chanson", "Opera",
|
||||||
/*68*/ "Chamber Music", "Sonata", "Symphony", "Booty Bass",
|
/*68*/ "Chamber Music", "Sonata", "Symphony", "Booty Bass",
|
||||||
"Primus", "Porn Groove", "Satire", "Slow Jam",
|
"Primus", "Porn Groove", "Satire", "Slow Jam",
|
||||||
/*70*/ "Club", "Tango", "Samba", "Folklore",
|
/*70*/ "Club", "Tango", "Samba", "Folklore",
|
||||||
"Ballad", "Powder Ballad", "Rhythmic Soul", "Freestyle",
|
"Ballad", "Powder Ballad", "Rhythmic Soul", "Freestyle",
|
||||||
/*78*/ "Duet", "Punk Rock", "Drum Solo", "A Capella",
|
/*78*/ "Duet", "Punk Rock", "Drum Solo", "A Capella",
|
||||||
"Euro-House", "Dance Hall", "Goa", "Drum & Bass",
|
"Euro-House", "Dance Hall", "Goa", "Drum & Bass",
|
||||||
/*80*/ "Club House", "Hardcore", "Terror", "Indie",
|
/*80*/ "Club House", "Hardcore", "Terror", "Indie",
|
||||||
"BritPop", "NegerPunk", "Polsk Punk", "Beat",
|
"BritPop", "NegerPunk", "Polsk Punk", "Beat",
|
||||||
/*88*/ "Christian Gangsta", "Heavy Metal", "Black Metal", "Crossover",
|
/*88*/ "Christian Gangsta", "Heavy Metal", "Black Metal", "Crossover",
|
||||||
"Contemporary C", "Christian Rock", "Merengue", "Salsa",
|
"Contemporary C", "Christian Rock", "Merengue", "Salsa",
|
||||||
/*90*/ "Thrash Metal", "Anime", "JPop", "SynthPop",
|
/*90*/ "Thrash Metal", "Anime", "JPop", "SynthPop",
|
||||||
"Unknown"
|
"Unknown"
|
||||||
};
|
};
|
||||||
|
|
||||||
#define WINAMP_GENRE_UNKNOWN ((sizeof(winamp_genre) / sizeof(winamp_genre[0])) - 1)
|
#define WINAMP_GENRE_UNKNOWN ((sizeof(winamp_genre) / sizeof(winamp_genre[0])) - 1)
|
||||||
@ -104,8 +108,9 @@ char *winamp_genre[] = {
|
|||||||
*/
|
*/
|
||||||
#include "tagutils-mp3.h"
|
#include "tagutils-mp3.h"
|
||||||
#include "tagutils-aac.h"
|
#include "tagutils-aac.h"
|
||||||
#ifdef HAVE_VORBISFILE
|
|
||||||
#include "tagutils-ogg.h"
|
#include "tagutils-ogg.h"
|
||||||
|
#ifdef HAVE_OPUS
|
||||||
|
#include "tagutils-opus.h"
|
||||||
#endif
|
#endif
|
||||||
#include "tagutils-flc.h"
|
#include "tagutils-flc.h"
|
||||||
#include "tagutils-asf.h"
|
#include "tagutils-asf.h"
|
||||||
@ -123,24 +128,25 @@ static int _get_fileinfo(char *file, struct song_metadata *psong);
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
char* type;
|
char* type;
|
||||||
int (*get_tags)(char* file, struct song_metadata* psong);
|
int (*get_tags)(char* file, struct song_metadata* psong);
|
||||||
int (*get_fileinfo)(char* file, struct song_metadata* psong);
|
int (*get_fileinfo)(char* file, struct song_metadata* psong);
|
||||||
} taghandler;
|
} taghandler;
|
||||||
|
|
||||||
static taghandler taghandlers[] = {
|
static taghandler taghandlers[] = {
|
||||||
{ "aac", _get_aactags, _get_aacfileinfo },
|
{ "aac", _get_aactags, _get_aacfileinfo },
|
||||||
{ "mp3", _get_mp3tags, _get_mp3fileinfo },
|
{ "mp3", _get_mp3tags, _get_mp3fileinfo },
|
||||||
{ "flc", _get_flctags, _get_flcfileinfo },
|
{ "flc", _get_flctags, _get_flcfileinfo },
|
||||||
#ifdef HAVE_VORBISFILE
|
{ "ogg", 0, _get_oggfileinfo },
|
||||||
{ "ogg", NULL, _get_oggfileinfo },
|
#ifdef HAVE_OPUS
|
||||||
|
{ "ops", 0, _get_opusfileinfo },
|
||||||
#endif
|
#endif
|
||||||
{ "asf", NULL, _get_asffileinfo },
|
{ "asf", 0, _get_asffileinfo },
|
||||||
{ "wav", _get_wavtags, _get_wavfileinfo },
|
{ "wav", _get_wavtags, _get_wavfileinfo },
|
||||||
{ "pcm", NULL, _get_pcmfileinfo },
|
{ "pcm", 0, _get_pcmfileinfo },
|
||||||
{ "dsf", _get_dsftags, _get_dsffileinfo },
|
{ "dsf", _get_dsftags, _get_dsffileinfo },
|
||||||
{ "dff", NULL, _get_dfffileinfo },
|
{ "dff", 0, _get_dfffileinfo },
|
||||||
{ NULL, NULL, NULL }
|
{ NULL, NULL, NULL }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -149,8 +155,9 @@ static taghandler taghandlers[] = {
|
|||||||
#include "tagutils-misc.c"
|
#include "tagutils-misc.c"
|
||||||
#include "tagutils-mp3.c"
|
#include "tagutils-mp3.c"
|
||||||
#include "tagutils-aac.c"
|
#include "tagutils-aac.c"
|
||||||
#ifdef HAVE_VORBISFILE
|
|
||||||
#include "tagutils-ogg.c"
|
#include "tagutils-ogg.c"
|
||||||
|
#ifdef HAVE_OPUS
|
||||||
|
#include "tagutils-opus.c"
|
||||||
#endif
|
#endif
|
||||||
#include "tagutils-flc.c"
|
#include "tagutils-flc.c"
|
||||||
#include "tagutils-asf.c"
|
#include "tagutils-asf.c"
|
||||||
@ -166,86 +173,86 @@ static taghandler taghandlers[] = {
|
|||||||
void
|
void
|
||||||
freetags(struct song_metadata *psong)
|
freetags(struct song_metadata *psong)
|
||||||
{
|
{
|
||||||
int role;
|
int role;
|
||||||
|
|
||||||
MAYBEFREE(psong->path);
|
MAYBEFREE(psong->path);
|
||||||
MAYBEFREE(psong->image);
|
MAYBEFREE(psong->image);
|
||||||
MAYBEFREE(psong->title);
|
MAYBEFREE(psong->title);
|
||||||
MAYBEFREE(psong->album);
|
MAYBEFREE(psong->album);
|
||||||
MAYBEFREE(psong->genre);
|
MAYBEFREE(psong->genre);
|
||||||
MAYBEFREE(psong->comment);
|
MAYBEFREE(psong->comment);
|
||||||
for(role = ROLE_START; role <= ROLE_LAST; role++)
|
for(role = ROLE_START; role <= ROLE_LAST; role++)
|
||||||
{
|
{
|
||||||
MAYBEFREE(psong->contributor[role]);
|
MAYBEFREE(psong->contributor[role]);
|
||||||
MAYBEFREE(psong->contributor_sort[role]);
|
MAYBEFREE(psong->contributor_sort[role]);
|
||||||
}
|
}
|
||||||
MAYBEFREE(psong->grouping);
|
MAYBEFREE(psong->grouping);
|
||||||
MAYBEFREE(psong->mime);
|
MAYBEFREE(psong->mime);
|
||||||
MAYBEFREE(psong->dlna_pn);
|
MAYBEFREE(psong->dlna_pn);
|
||||||
MAYBEFREE(psong->tagversion);
|
MAYBEFREE(psong->tagversion);
|
||||||
MAYBEFREE(psong->musicbrainz_albumid);
|
MAYBEFREE(psong->musicbrainz_albumid);
|
||||||
MAYBEFREE(psong->musicbrainz_trackid);
|
MAYBEFREE(psong->musicbrainz_trackid);
|
||||||
MAYBEFREE(psong->musicbrainz_artistid);
|
MAYBEFREE(psong->musicbrainz_artistid);
|
||||||
MAYBEFREE(psong->musicbrainz_albumartistid);
|
MAYBEFREE(psong->musicbrainz_albumartistid);
|
||||||
}
|
}
|
||||||
|
|
||||||
// _get_fileinfo
|
// _get_fileinfo
|
||||||
static int
|
static int
|
||||||
_get_fileinfo(char *file, struct song_metadata *psong)
|
_get_fileinfo(char *file, struct song_metadata *psong)
|
||||||
{
|
{
|
||||||
taghandler *hdl;
|
taghandler *hdl;
|
||||||
|
|
||||||
// dispatch to appropriate tag handler
|
// dispatch to appropriate tag handler
|
||||||
for(hdl = taghandlers; hdl->type; ++hdl)
|
for(hdl = taghandlers; hdl->type; ++hdl)
|
||||||
if(!strcmp(hdl->type, psong->type))
|
if(!strcmp(hdl->type, psong->type))
|
||||||
break;
|
break;
|
||||||
|
|
||||||
if(hdl->get_fileinfo)
|
if(hdl->get_fileinfo)
|
||||||
return hdl->get_fileinfo(file, psong);
|
return hdl->get_fileinfo(file, psong);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
_make_composite_tags(struct song_metadata *psong)
|
_make_composite_tags(struct song_metadata *psong)
|
||||||
{
|
{
|
||||||
int len;
|
int len;
|
||||||
|
|
||||||
len = 1;
|
len = 1;
|
||||||
|
|
||||||
if(!psong->contributor[ROLE_ARTIST] &&
|
if(!psong->contributor[ROLE_ARTIST] &&
|
||||||
(psong->contributor[ROLE_BAND] || psong->contributor[ROLE_CONDUCTOR]))
|
(psong->contributor[ROLE_BAND] || psong->contributor[ROLE_CONDUCTOR]))
|
||||||
{
|
{
|
||||||
if(psong->contributor[ROLE_BAND])
|
if(psong->contributor[ROLE_BAND])
|
||||||
len += strlen(psong->contributor[ROLE_BAND]);
|
len += strlen(psong->contributor[ROLE_BAND]);
|
||||||
if(psong->contributor[ROLE_CONDUCTOR])
|
if(psong->contributor[ROLE_CONDUCTOR])
|
||||||
len += strlen(psong->contributor[ROLE_CONDUCTOR]);
|
len += strlen(psong->contributor[ROLE_CONDUCTOR]);
|
||||||
|
|
||||||
len += 3;
|
len += 3;
|
||||||
|
|
||||||
psong->contributor[ROLE_ARTIST] = (char*)calloc(len, 1);
|
psong->contributor[ROLE_ARTIST] = (char*)calloc(len, 1);
|
||||||
if(psong->contributor[ROLE_ARTIST])
|
if(psong->contributor[ROLE_ARTIST])
|
||||||
{
|
{
|
||||||
if(psong->contributor[ROLE_BAND])
|
if(psong->contributor[ROLE_BAND])
|
||||||
strcat(psong->contributor[ROLE_ARTIST], psong->contributor[ROLE_BAND]);
|
strcat(psong->contributor[ROLE_ARTIST], psong->contributor[ROLE_BAND]);
|
||||||
|
|
||||||
if(psong->contributor[ROLE_BAND] && psong->contributor[ROLE_CONDUCTOR])
|
if(psong->contributor[ROLE_BAND] && psong->contributor[ROLE_CONDUCTOR])
|
||||||
strcat(psong->contributor[ROLE_ARTIST], " - ");
|
strcat(psong->contributor[ROLE_ARTIST], " - ");
|
||||||
|
|
||||||
if(psong->contributor[ROLE_CONDUCTOR])
|
if(psong->contributor[ROLE_CONDUCTOR])
|
||||||
strcat(psong->contributor[ROLE_ARTIST], psong->contributor[ROLE_CONDUCTOR]);
|
strcat(psong->contributor[ROLE_ARTIST], psong->contributor[ROLE_CONDUCTOR]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#if 0 // already taken care of by scanner.c
|
#if 0 // already taken care of by scanner.c
|
||||||
if(!psong->title)
|
if(!psong->title)
|
||||||
{
|
{
|
||||||
char *suffix;
|
char *suffix;
|
||||||
psong->title = strdup(psong->basename);
|
psong->title = strdup(psong->basename);
|
||||||
suffix = strrchr(psong->title, '.');
|
suffix = strrchr(psong->title, '.');
|
||||||
if(suffix) *suffix = '\0';
|
if(suffix) *suffix = '\0';
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -255,19 +262,19 @@ _make_composite_tags(struct song_metadata *psong)
|
|||||||
static int
|
static int
|
||||||
_get_tags(char *file, struct song_metadata *psong)
|
_get_tags(char *file, struct song_metadata *psong)
|
||||||
{
|
{
|
||||||
taghandler *hdl;
|
taghandler *hdl;
|
||||||
|
|
||||||
// dispatch
|
// dispatch
|
||||||
for(hdl = taghandlers ; hdl->type ; ++hdl)
|
for(hdl = taghandlers ; hdl->type ; ++hdl)
|
||||||
if(!strcasecmp(hdl->type, psong->type))
|
if(!strcasecmp(hdl->type, psong->type))
|
||||||
break;
|
break;
|
||||||
|
|
||||||
if(hdl->get_tags)
|
if(hdl->get_tags)
|
||||||
{
|
{
|
||||||
return hdl->get_tags(file, psong);
|
return hdl->get_tags(file, psong);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
@ -275,31 +282,31 @@ _get_tags(char *file, struct song_metadata *psong)
|
|||||||
int
|
int
|
||||||
readtags(char *path, struct song_metadata *psong, struct stat *stat, char *lang, char *type)
|
readtags(char *path, struct song_metadata *psong, struct stat *stat, char *lang, char *type)
|
||||||
{
|
{
|
||||||
char *fname;
|
char *fname;
|
||||||
|
|
||||||
if(lang_index == -1)
|
if(lang_index == -1)
|
||||||
lang_index = _lang2cp(lang);
|
lang_index = _lang2cp(lang);
|
||||||
|
|
||||||
memset((void*)psong, 0, sizeof(struct song_metadata));
|
memset((void*)psong, 0, sizeof(struct song_metadata));
|
||||||
psong->path = strdup(path);
|
psong->path = strdup(path);
|
||||||
psong->type = type;
|
psong->type = type;
|
||||||
|
|
||||||
fname = strrchr(psong->path, '/');
|
fname = strrchr(psong->path, '/');
|
||||||
psong->basename = fname ? fname + 1 : psong->path;
|
psong->basename = fname ? fname + 1 : psong->path;
|
||||||
|
|
||||||
if(stat)
|
if(stat)
|
||||||
{
|
{
|
||||||
if(!psong->time_modified)
|
if(!psong->time_modified)
|
||||||
psong->time_modified = stat->st_mtime;
|
psong->time_modified = stat->st_mtime;
|
||||||
psong->file_size = stat->st_size;
|
psong->file_size = stat->st_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
// get tag
|
// get tag
|
||||||
if( _get_tags(path, psong) == 0 )
|
if( _get_tags(path, psong) == 0 )
|
||||||
{
|
{
|
||||||
_make_composite_tags(psong);
|
_make_composite_tags(psong);
|
||||||
}
|
}
|
||||||
|
|
||||||
// get fileinfo
|
// get fileinfo
|
||||||
return _get_fileinfo(path, psong);
|
return _get_fileinfo(path, psong);
|
||||||
}
|
}
|
||||||
|
738
utils.c
738
utils.c
@ -38,503 +38,517 @@
|
|||||||
int
|
int
|
||||||
xasprintf(char **strp, char *fmt, ...)
|
xasprintf(char **strp, char *fmt, ...)
|
||||||
{
|
{
|
||||||
va_list args;
|
va_list args;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
va_start(args, fmt);
|
va_start(args, fmt);
|
||||||
ret = vasprintf(strp, fmt, args);
|
ret = vasprintf(strp, fmt, args);
|
||||||
va_end(args);
|
va_end(args);
|
||||||
if( ret < 0 )
|
if( ret < 0 )
|
||||||
{
|
{
|
||||||
DPRINTF(E_WARN, L_GENERAL, "xasprintf: allocation failed\n");
|
DPRINTF(E_WARN, L_GENERAL, "xasprintf: allocation failed\n");
|
||||||
*strp = NULL;
|
*strp = NULL;
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
ends_with(const char * haystack, const char * needle)
|
ends_with(const char * haystack, const char * needle)
|
||||||
{
|
{
|
||||||
const char * end;
|
const char * end;
|
||||||
int nlen = strlen(needle);
|
int nlen = strlen(needle);
|
||||||
int hlen = strlen(haystack);
|
int hlen = strlen(haystack);
|
||||||
|
|
||||||
if( nlen > hlen )
|
if( nlen > hlen )
|
||||||
return 0;
|
return 0;
|
||||||
end = haystack + hlen - nlen;
|
end = haystack + hlen - nlen;
|
||||||
|
|
||||||
return (strcasecmp(end, needle) ? 0 : 1);
|
return (strcasecmp(end, needle) ? 0 : 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
char *
|
char *
|
||||||
trim(char *str)
|
trim(char *str)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
int len;
|
int len;
|
||||||
|
|
||||||
if (!str)
|
if (!str)
|
||||||
return(NULL);
|
return(NULL);
|
||||||
|
|
||||||
len = strlen(str);
|
len = strlen(str);
|
||||||
for (i=len-1; i >= 0 && isspace(str[i]); i--)
|
for (i=len-1; i >= 0 && isspace(str[i]); i--)
|
||||||
{
|
{
|
||||||
str[i] = '\0';
|
str[i] = '\0';
|
||||||
len--;
|
len--;
|
||||||
}
|
}
|
||||||
while (isspace(*str))
|
while (isspace(*str))
|
||||||
{
|
{
|
||||||
str++;
|
str++;
|
||||||
len--;
|
len--;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (str[0] == '"' && str[len-1] == '"')
|
if (str[0] == '"' && str[len-1] == '"')
|
||||||
{
|
{
|
||||||
str[0] = '\0';
|
str[0] = '\0';
|
||||||
str[len-1] = '\0';
|
str[len-1] = '\0';
|
||||||
str++;
|
str++;
|
||||||
}
|
}
|
||||||
|
|
||||||
return str;
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Find the first occurrence of p in s, where s is terminated by t */
|
/* Find the first occurrence of p in s, where s is terminated by t */
|
||||||
char *
|
char *
|
||||||
strstrc(const char *s, const char *p, const char t)
|
strstrc(const char *s, const char *p, const char t)
|
||||||
{
|
{
|
||||||
char *endptr;
|
char *endptr;
|
||||||
size_t slen, plen;
|
size_t slen, plen;
|
||||||
|
|
||||||
endptr = strchr(s, t);
|
endptr = strchr(s, t);
|
||||||
if (!endptr)
|
if (!endptr)
|
||||||
return strstr(s, p);
|
return strstr(s, p);
|
||||||
|
|
||||||
plen = strlen(p);
|
plen = strlen(p);
|
||||||
slen = endptr - s;
|
slen = endptr - s;
|
||||||
while (slen >= plen)
|
while (slen >= plen)
|
||||||
{
|
{
|
||||||
if (*s == *p && strncmp(s+1, p+1, plen-1) == 0)
|
if (*s == *p && strncmp(s+1, p+1, plen-1) == 0)
|
||||||
return (char*)s;
|
return (char*)s;
|
||||||
s++;
|
s++;
|
||||||
slen--;
|
slen--;
|
||||||
}
|
}
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
char *
|
char *
|
||||||
strcasestrc(const char *s, const char *p, const char t)
|
strcasestrc(const char *s, const char *p, const char t)
|
||||||
{
|
{
|
||||||
char *endptr;
|
char *endptr;
|
||||||
size_t slen, plen;
|
size_t slen, plen;
|
||||||
|
|
||||||
endptr = strchr(s, t);
|
endptr = strchr(s, t);
|
||||||
if (!endptr)
|
if (!endptr)
|
||||||
return strcasestr(s, p);
|
return strcasestr(s, p);
|
||||||
|
|
||||||
plen = strlen(p);
|
plen = strlen(p);
|
||||||
slen = endptr - s;
|
slen = endptr - s;
|
||||||
while (slen >= plen)
|
while (slen >= plen)
|
||||||
{
|
{
|
||||||
if (*s == *p && strncasecmp(s+1, p+1, plen-1) == 0)
|
if (*s == *p && strncasecmp(s+1, p+1, plen-1) == 0)
|
||||||
return (char*)s;
|
return (char*)s;
|
||||||
s++;
|
s++;
|
||||||
slen--;
|
slen--;
|
||||||
}
|
}
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
char *
|
char *
|
||||||
modifyString(char *string, const char *before, const char *after, int noalloc)
|
modifyString(char *string, const char *before, const char *after, int noalloc)
|
||||||
{
|
{
|
||||||
int oldlen, newlen, chgcnt = 0;
|
int oldlen, newlen, chgcnt = 0;
|
||||||
char *s, *p;
|
char *s, *p;
|
||||||
|
|
||||||
/* If there is no match, just return */
|
/* If there is no match, just return */
|
||||||
s = strstr(string, before);
|
s = strstr(string, before);
|
||||||
if (!s)
|
if (!s)
|
||||||
return string;
|
return string;
|
||||||
|
|
||||||
oldlen = strlen(before);
|
oldlen = strlen(before);
|
||||||
newlen = strlen(after);
|
newlen = strlen(after);
|
||||||
if (newlen > oldlen)
|
if (newlen > oldlen)
|
||||||
{
|
{
|
||||||
if (noalloc)
|
if (noalloc)
|
||||||
return string;
|
return string;
|
||||||
|
|
||||||
while ((p = strstr(s, before)))
|
while ((p = strstr(s, before)))
|
||||||
{
|
{
|
||||||
chgcnt++;
|
chgcnt++;
|
||||||
s = p + oldlen;
|
s = p + oldlen;
|
||||||
}
|
}
|
||||||
s = realloc(string, strlen(string)+((newlen-oldlen)*chgcnt)+1);
|
s = realloc(string, strlen(string)+((newlen-oldlen)*chgcnt)+1);
|
||||||
/* If we failed to realloc, return the original alloc'd string */
|
/* If we failed to realloc, return the original alloc'd string */
|
||||||
if( s )
|
if( s )
|
||||||
string = s;
|
string = s;
|
||||||
else
|
else
|
||||||
return string;
|
return string;
|
||||||
}
|
}
|
||||||
|
|
||||||
s = string;
|
s = string;
|
||||||
while (s)
|
while (s)
|
||||||
{
|
{
|
||||||
p = strstr(s, before);
|
p = strstr(s, before);
|
||||||
if (!p)
|
if (!p)
|
||||||
return string;
|
return string;
|
||||||
memmove(p + newlen, p + oldlen, strlen(p + oldlen) + 1);
|
memmove(p + newlen, p + oldlen, strlen(p + oldlen) + 1);
|
||||||
memcpy(p, after, newlen);
|
memcpy(p, after, newlen);
|
||||||
s = p + newlen;
|
s = p + newlen;
|
||||||
}
|
}
|
||||||
|
|
||||||
return string;
|
return string;
|
||||||
}
|
}
|
||||||
|
|
||||||
char *
|
char *
|
||||||
unescape_tag(const char *tag, int force_alloc)
|
unescape_tag(const char *tag, int force_alloc)
|
||||||
{
|
{
|
||||||
char *esc_tag = NULL;
|
char *esc_tag = NULL;
|
||||||
|
|
||||||
if (strchr(tag, '&') &&
|
if (strchr(tag, '&') &&
|
||||||
(strstr(tag, "&") || strstr(tag, "<") || strstr(tag, ">") ||
|
(strstr(tag, "&") || strstr(tag, "<") || strstr(tag, ">") ||
|
||||||
strstr(tag, """) || strstr(tag, "'")))
|
strstr(tag, """) || strstr(tag, "'")))
|
||||||
{
|
{
|
||||||
esc_tag = strdup(tag);
|
esc_tag = strdup(tag);
|
||||||
esc_tag = modifyString(esc_tag, "&", "&", 1);
|
esc_tag = modifyString(esc_tag, "&", "&", 1);
|
||||||
esc_tag = modifyString(esc_tag, "<", "<", 1);
|
esc_tag = modifyString(esc_tag, "<", "<", 1);
|
||||||
esc_tag = modifyString(esc_tag, ">", ">", 1);
|
esc_tag = modifyString(esc_tag, ">", ">", 1);
|
||||||
esc_tag = modifyString(esc_tag, """, "\"", 1);
|
esc_tag = modifyString(esc_tag, """, "\"", 1);
|
||||||
esc_tag = modifyString(esc_tag, "'", "'", 1);
|
esc_tag = modifyString(esc_tag, "'", "'", 1);
|
||||||
}
|
}
|
||||||
else if( force_alloc )
|
else if( force_alloc )
|
||||||
esc_tag = strdup(tag);
|
esc_tag = strdup(tag);
|
||||||
|
|
||||||
return esc_tag;
|
return esc_tag;
|
||||||
}
|
}
|
||||||
|
|
||||||
char *
|
char *
|
||||||
escape_tag(const char *tag, int force_alloc)
|
escape_tag(const char *tag, int force_alloc)
|
||||||
{
|
{
|
||||||
char *esc_tag = NULL;
|
char *esc_tag = NULL;
|
||||||
|
|
||||||
if( strchr(tag, '&') || strchr(tag, '<') || strchr(tag, '>') || strchr(tag, '"') )
|
if( strchr(tag, '&') || strchr(tag, '<') || strchr(tag, '>') || strchr(tag, '"') )
|
||||||
{
|
{
|
||||||
esc_tag = strdup(tag);
|
esc_tag = strdup(tag);
|
||||||
esc_tag = modifyString(esc_tag, "&", "&amp;", 0);
|
esc_tag = modifyString(esc_tag, "&", "&amp;", 0);
|
||||||
esc_tag = modifyString(esc_tag, "<", "&lt;", 0);
|
esc_tag = modifyString(esc_tag, "<", "&lt;", 0);
|
||||||
esc_tag = modifyString(esc_tag, ">", "&gt;", 0);
|
esc_tag = modifyString(esc_tag, ">", "&gt;", 0);
|
||||||
esc_tag = modifyString(esc_tag, "\"", "&quot;", 0);
|
esc_tag = modifyString(esc_tag, "\"", "&quot;", 0);
|
||||||
}
|
}
|
||||||
else if( force_alloc )
|
else if( force_alloc )
|
||||||
esc_tag = strdup(tag);
|
esc_tag = strdup(tag);
|
||||||
|
|
||||||
return esc_tag;
|
return esc_tag;
|
||||||
}
|
}
|
||||||
|
|
||||||
char *
|
char *
|
||||||
duration_str(int msec)
|
duration_str(int msec)
|
||||||
{
|
{
|
||||||
char *str;
|
char *str;
|
||||||
|
|
||||||
xasprintf(&str, "%d:%02d:%02d.%03d",
|
xasprintf(&str, "%d:%02d:%02d.%03d",
|
||||||
(msec / 3600000),
|
(msec / 3600000),
|
||||||
(msec / 60000 % 60),
|
(msec / 60000 % 60),
|
||||||
(msec / 1000 % 60),
|
(msec / 1000 % 60),
|
||||||
(msec % 1000));
|
(msec % 1000));
|
||||||
|
|
||||||
return str;
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
char *
|
char *
|
||||||
strip_ext(char *name)
|
strip_ext(char *name)
|
||||||
{
|
{
|
||||||
char *period;
|
char *period;
|
||||||
|
|
||||||
if (!name)
|
if (!name)
|
||||||
return NULL;
|
return NULL;
|
||||||
period = strrchr(name, '.');
|
period = strrchr(name, '.');
|
||||||
if (period)
|
if (period)
|
||||||
*period = '\0';
|
*period = '\0';
|
||||||
|
|
||||||
return period;
|
return period;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Code basically stolen from busybox */
|
/* Code basically stolen from busybox */
|
||||||
int
|
int
|
||||||
make_dir(char * path, mode_t mode)
|
make_dir(char * path, mode_t mode)
|
||||||
{
|
{
|
||||||
char * s = path;
|
char * s = path;
|
||||||
char c;
|
char c;
|
||||||
struct stat st;
|
struct stat st;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
c = '\0';
|
c = '\0';
|
||||||
|
|
||||||
/* Before we do anything, skip leading /'s, so we don't bother
|
/* Before we do anything, skip leading /'s, so we don't bother
|
||||||
* trying to create /. */
|
* trying to create /. */
|
||||||
while (*s == '/')
|
while (*s == '/')
|
||||||
++s;
|
++s;
|
||||||
|
|
||||||
/* Bypass leading non-'/'s and then subsequent '/'s. */
|
/* Bypass leading non-'/'s and then subsequent '/'s. */
|
||||||
while (*s) {
|
while (*s) {
|
||||||
if (*s == '/') {
|
if (*s == '/') {
|
||||||
do {
|
do {
|
||||||
++s;
|
++s;
|
||||||
} while (*s == '/');
|
} while (*s == '/');
|
||||||
c = *s; /* Save the current char */
|
c = *s; /* Save the current char */
|
||||||
*s = '\0'; /* and replace it with nul. */
|
*s = '\0'; /* and replace it with nul. */
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
++s;
|
++s;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mkdir(path, mode) < 0) {
|
if (mkdir(path, mode) < 0) {
|
||||||
/* If we failed for any other reason than the directory
|
/* If we failed for any other reason than the directory
|
||||||
* already exists, output a diagnostic and return -1.*/
|
* already exists, output a diagnostic and return -1.*/
|
||||||
if ((errno != EEXIST && errno != EISDIR)
|
if ((errno != EEXIST && errno != EISDIR)
|
||||||
|| (stat(path, &st) < 0 || !S_ISDIR(st.st_mode))) {
|
|| (stat(path, &st) < 0 || !S_ISDIR(st.st_mode))) {
|
||||||
DPRINTF(E_WARN, L_GENERAL, "make_dir: cannot create directory '%s'\n", path);
|
DPRINTF(E_WARN, L_GENERAL, "make_dir: cannot create directory '%s'\n", path);
|
||||||
if (c)
|
if (c)
|
||||||
*s = c;
|
*s = c;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!c)
|
if (!c)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
/* Remove any inserted nul from the path. */
|
/* Remove any inserted nul from the path. */
|
||||||
*s = c;
|
*s = c;
|
||||||
|
|
||||||
} while (1);
|
} while (1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Simple, efficient hash function from Daniel J. Bernstein */
|
/* Simple, efficient hash function from Daniel J. Bernstein */
|
||||||
unsigned int
|
unsigned int
|
||||||
DJBHash(uint8_t *data, int len)
|
DJBHash(uint8_t *data, int len)
|
||||||
{
|
{
|
||||||
unsigned int hash = 5381;
|
unsigned int hash = 5381;
|
||||||
unsigned int i = 0;
|
unsigned int i = 0;
|
||||||
|
|
||||||
for(i = 0; i < len; data++, i++)
|
for(i = 0; i < len; data++, i++)
|
||||||
{
|
{
|
||||||
hash = ((hash << 5) + hash) + (*data);
|
hash = ((hash << 5) + hash) + (*data);
|
||||||
}
|
}
|
||||||
|
|
||||||
return hash;
|
return hash;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *
|
const char *
|
||||||
mime_to_ext(const char * mime)
|
mime_to_ext(const char * mime)
|
||||||
{
|
{
|
||||||
switch( *mime )
|
switch( *mime )
|
||||||
{
|
{
|
||||||
/* Audio extensions */
|
/* Audio extensions */
|
||||||
case 'a':
|
case 'a':
|
||||||
if( strcmp(mime+6, "mpeg") == 0 )
|
if( strcmp(mime+6, "mpeg") == 0 )
|
||||||
return "mp3";
|
return "mp3";
|
||||||
else if( strcmp(mime+6, "mp4") == 0 )
|
else if( strcmp(mime+6, "mp4") == 0 )
|
||||||
return "m4a";
|
return "m4a";
|
||||||
else if( strcmp(mime+6, "x-ms-wma") == 0 )
|
else if( strcmp(mime+6, "x-ms-wma") == 0 )
|
||||||
return "wma";
|
return "wma";
|
||||||
else if( strcmp(mime+6, "x-flac") == 0 )
|
else if( strcmp(mime+6, "x-flac") == 0 )
|
||||||
return "flac";
|
return "flac";
|
||||||
else if( strcmp(mime+6, "flac") == 0 )
|
else if( strcmp(mime+6, "flac") == 0 )
|
||||||
return "flac";
|
return "flac";
|
||||||
else if( strcmp(mime+6, "x-wav") == 0 )
|
else if( strcmp(mime+6, "x-wav") == 0 )
|
||||||
return "wav";
|
return "wav";
|
||||||
else if( strncmp(mime+6, "L16", 3) == 0 )
|
else if( strncmp(mime+6, "L16", 3) == 0 )
|
||||||
return "pcm";
|
return "pcm";
|
||||||
else if( strcmp(mime+6, "3gpp") == 0 )
|
else if( strcmp(mime+6, "3gpp") == 0 )
|
||||||
return "3gp";
|
return "3gp";
|
||||||
else if( strcmp(mime, "application/ogg") == 0 )
|
else if( strncmp(mime+6, "ogg", 3) == 0 )
|
||||||
return "ogg";
|
{
|
||||||
else if( strcmp(mime+6, "x-dsd") == 0 )
|
if( strstr(mime+9, "opus" ) != (char *)NULL )
|
||||||
return "dsd";
|
return "opus";
|
||||||
break;
|
else if( strstr (mime+9, "vorbis" ) != (char *)NULL )
|
||||||
case 'v':
|
return "ogg";
|
||||||
if( strcmp(mime+6, "avi") == 0 )
|
|
||||||
return "avi";
|
return "oga";
|
||||||
else if( strcmp(mime+6, "divx") == 0 )
|
}
|
||||||
return "avi";
|
else if( strcmp(mime+6, "x-dsd") == 0 )
|
||||||
else if( strcmp(mime+6, "x-msvideo") == 0 )
|
return "dsd";
|
||||||
return "avi";
|
break;
|
||||||
else if( strcmp(mime+6, "mpeg") == 0 )
|
case 'v':
|
||||||
return "mpg";
|
if( strcmp(mime+6, "avi") == 0 )
|
||||||
else if( strcmp(mime+6, "mp4") == 0 )
|
return "avi";
|
||||||
return "mp4";
|
else if( strcmp(mime+6, "divx") == 0 )
|
||||||
else if( strcmp(mime+6, "x-ms-wmv") == 0 )
|
return "avi";
|
||||||
return "wmv";
|
else if( strcmp(mime+6, "x-msvideo") == 0 )
|
||||||
else if( strcmp(mime+6, "x-matroska") == 0 )
|
return "avi";
|
||||||
return "mkv";
|
else if( strcmp(mime+6, "mpeg") == 0 )
|
||||||
else if( strcmp(mime+6, "x-mkv") == 0 )
|
return "mpg";
|
||||||
return "mkv";
|
else if( strcmp(mime+6, "mp4") == 0 )
|
||||||
else if( strcmp(mime+6, "x-flv") == 0 )
|
return "mp4";
|
||||||
return "flv";
|
else if( strcmp(mime+6, "x-ms-wmv") == 0 )
|
||||||
else if( strcmp(mime+6, "vnd.dlna.mpeg-tts") == 0 )
|
return "wmv";
|
||||||
return "mpg";
|
else if( strcmp(mime+6, "x-matroska") == 0 )
|
||||||
else if( strcmp(mime+6, "quicktime") == 0 )
|
return "mkv";
|
||||||
return "mov";
|
else if( strcmp(mime+6, "x-mkv") == 0 )
|
||||||
else if( strcmp(mime+6, "3gpp") == 0 )
|
return "mkv";
|
||||||
return "3gp";
|
else if( strcmp(mime+6, "x-flv") == 0 )
|
||||||
else if( strncmp(mime+6, "x-tivo-mpeg", 11) == 0 )
|
return "flv";
|
||||||
return "TiVo";
|
else if( strcmp(mime+6, "vnd.dlna.mpeg-tts") == 0 )
|
||||||
break;
|
return "mpg";
|
||||||
case 'i':
|
else if( strcmp(mime+6, "quicktime") == 0 )
|
||||||
if( strcmp(mime+6, "jpeg") == 0 )
|
return "mov";
|
||||||
return "jpg";
|
else if( strcmp(mime+6, "3gpp") == 0 )
|
||||||
else if( strcmp(mime+6, "png") == 0 )
|
return "3gp";
|
||||||
return "png";
|
else if( strncmp(mime+6, "x-tivo-mpeg", 11) == 0 )
|
||||||
break;
|
return "TiVo";
|
||||||
default:
|
else if ( strcmp(mime+6, "ogg") == 0 )
|
||||||
break;
|
return "ogv";
|
||||||
}
|
break;
|
||||||
return "dat";
|
case 'i':
|
||||||
|
if( strcmp(mime+6, "jpeg") == 0 )
|
||||||
|
return "jpg";
|
||||||
|
else if( strcmp(mime+6, "png") == 0 )
|
||||||
|
return "png";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return "dat";
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
is_video(const char * file)
|
is_video(const char * file)
|
||||||
{
|
{
|
||||||
return (ends_with(file, ".mpg") || ends_with(file, ".mpeg") ||
|
return (ends_with(file, ".mpg") || ends_with(file, ".mpeg") ||
|
||||||
ends_with(file, ".avi") || ends_with(file, ".divx") ||
|
ends_with(file, ".avi") || ends_with(file, ".divx") ||
|
||||||
ends_with(file, ".asf") || ends_with(file, ".wmv") ||
|
ends_with(file, ".asf") || ends_with(file, ".wmv") ||
|
||||||
ends_with(file, ".mp4") || ends_with(file, ".m4v") ||
|
ends_with(file, ".mp4") || ends_with(file, ".m4v") ||
|
||||||
ends_with(file, ".mts") || ends_with(file, ".m2ts") ||
|
ends_with(file, ".mts") || ends_with(file, ".m2ts") ||
|
||||||
ends_with(file, ".m2t") || ends_with(file, ".mkv") ||
|
ends_with(file, ".m2t") || ends_with(file, ".mkv") ||
|
||||||
ends_with(file, ".vob") || ends_with(file, ".ts") ||
|
ends_with(file, ".vob") || ends_with(file, ".ts") ||
|
||||||
ends_with(file, ".flv") || ends_with(file, ".xvid") ||
|
ends_with(file, ".flv") || ends_with(file, ".xvid") ||
|
||||||
|
ends_with(file, ".ogv") ||
|
||||||
#ifdef TIVO_SUPPORT
|
#ifdef TIVO_SUPPORT
|
||||||
ends_with(file, ".TiVo") ||
|
ends_with(file, ".TiVo") ||
|
||||||
#endif
|
#endif
|
||||||
ends_with(file, ".mov") || ends_with(file, ".3gp") ||
|
ends_with(file, ".mov") || ends_with(file, ".3gp") ||
|
||||||
ends_with(file, ".rm") || ends_with(file, ".rmvb") ||
|
ends_with(file, ".rm") || ends_with(file, ".rmvb") ||
|
||||||
ends_with(file, ".webm"));
|
ends_with(file, ".webm"));
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
is_audio(const char * file)
|
is_audio(const char * file)
|
||||||
{
|
{
|
||||||
return (ends_with(file, ".mp3") || ends_with(file, ".flac") ||
|
return (ends_with(file, ".mp3") || ends_with(file, ".flac") ||
|
||||||
ends_with(file, ".wma") || ends_with(file, ".asf") ||
|
ends_with(file, ".wma") || ends_with(file, ".asf") ||
|
||||||
ends_with(file, ".fla") || ends_with(file, ".flc") ||
|
ends_with(file, ".fla") || ends_with(file, ".flc") ||
|
||||||
ends_with(file, ".m4a") || ends_with(file, ".aac") ||
|
ends_with(file, ".m4a") || ends_with(file, ".aac") ||
|
||||||
ends_with(file, ".mp4") || ends_with(file, ".m4p") ||
|
ends_with(file, ".mp4") || ends_with(file, ".m4p") ||
|
||||||
ends_with(file, ".wav") || ends_with(file, ".ogg") ||
|
ends_with(file, ".wav") || ends_with(file, ".ogg") ||
|
||||||
ends_with(file, ".pcm") || ends_with(file, ".3gp") ||
|
ends_with(file, ".oga") ||
|
||||||
ends_with(file, ".dsf") || ends_with(file, ".dff"));
|
#ifdef HAVE_OPUS
|
||||||
|
ends_with(file, ".opus") ||
|
||||||
|
#endif
|
||||||
|
ends_with(file, ".pcm") || ends_with(file, ".3gp") ||
|
||||||
|
ends_with(file, ".dsf") || ends_with(file, ".dff"));
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
is_image(const char * file)
|
is_image(const char * file)
|
||||||
{
|
{
|
||||||
return (ends_with(file, ".jpg") || ends_with(file, ".jpeg"));
|
return (ends_with(file, ".jpg") || ends_with(file, ".jpeg"));
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
is_playlist(const char * file)
|
is_playlist(const char * file)
|
||||||
{
|
{
|
||||||
return (ends_with(file, ".m3u") || ends_with(file, ".pls"));
|
return (ends_with(file, ".m3u") || ends_with(file, ".pls"));
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
is_caption(const char * file)
|
is_caption(const char * file)
|
||||||
{
|
{
|
||||||
return (ends_with(file, ".srt") || ends_with(file, ".smi"));
|
return (ends_with(file, ".srt") || ends_with(file, ".smi"));
|
||||||
}
|
}
|
||||||
|
|
||||||
media_types
|
media_types
|
||||||
get_media_type(const char *file)
|
get_media_type(const char *file)
|
||||||
{
|
{
|
||||||
const char *ext = strrchr(file, '.');
|
const char *ext = strrchr(file, '.');
|
||||||
if (!ext)
|
if (!ext)
|
||||||
return NO_MEDIA;
|
return NO_MEDIA;
|
||||||
if (is_image(ext))
|
if (is_image(ext))
|
||||||
return TYPE_IMAGE;
|
return TYPE_IMAGE;
|
||||||
if (is_video(ext))
|
if (is_video(ext))
|
||||||
return TYPE_VIDEO;
|
return TYPE_VIDEO;
|
||||||
if (is_audio(ext))
|
if (is_audio(ext))
|
||||||
return TYPE_AUDIO;
|
return TYPE_AUDIO;
|
||||||
if (is_playlist(ext))
|
if (is_playlist(ext))
|
||||||
return TYPE_PLAYLIST;
|
return TYPE_PLAYLIST;
|
||||||
if (is_caption(ext))
|
if (is_caption(ext))
|
||||||
return TYPE_CAPTION;
|
return TYPE_CAPTION;
|
||||||
if (is_nfo(ext))
|
if (is_nfo(ext))
|
||||||
return TYPE_NFO;
|
return TYPE_NFO;
|
||||||
return NO_MEDIA;
|
return NO_MEDIA;
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
is_album_art(const char * name)
|
is_album_art(const char * name)
|
||||||
{
|
{
|
||||||
struct album_art_name_s * album_art_name;
|
struct album_art_name_s * album_art_name;
|
||||||
|
|
||||||
/* Check if this file name matches one of the default album art names */
|
/* Check if this file name matches one of the default album art names */
|
||||||
for( album_art_name = album_art_names; album_art_name; album_art_name = album_art_name->next )
|
for( album_art_name = album_art_names; album_art_name; album_art_name = album_art_name->next )
|
||||||
{
|
{
|
||||||
if( album_art_name->wildcard )
|
if( album_art_name->wildcard )
|
||||||
{
|
{
|
||||||
if( strncmp(album_art_name->name, name, strlen(album_art_name->name)) == 0 )
|
if( strncmp(album_art_name->name, name, strlen(album_art_name->name)) == 0 )
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if( strcmp(album_art_name->name, name) == 0 )
|
if( strcmp(album_art_name->name, name) == 0 )
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return (album_art_name ? 1 : 0);
|
return (album_art_name ? 1 : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
resolve_unknown_type(const char * path, media_types dir_type)
|
resolve_unknown_type(const char * path, media_types dir_type)
|
||||||
{
|
{
|
||||||
struct stat entry;
|
struct stat entry;
|
||||||
enum file_types type = TYPE_UNKNOWN;
|
enum file_types type = TYPE_UNKNOWN;
|
||||||
char str_buf[PATH_MAX];
|
char str_buf[PATH_MAX];
|
||||||
ssize_t len;
|
ssize_t len;
|
||||||
|
|
||||||
if( lstat(path, &entry) == 0 )
|
if( lstat(path, &entry) == 0 )
|
||||||
{
|
{
|
||||||
if( S_ISLNK(entry.st_mode) )
|
if( S_ISLNK(entry.st_mode) )
|
||||||
{
|
{
|
||||||
if( (len = readlink(path, str_buf, PATH_MAX-1)) > 0 )
|
if( (len = readlink(path, str_buf, PATH_MAX-1)) > 0 )
|
||||||
{
|
{
|
||||||
str_buf[len] = '\0';
|
str_buf[len] = '\0';
|
||||||
//DEBUG DPRINTF(E_DEBUG, L_GENERAL, "Checking for recursive symbolic link: %s (%s)\n", path, str_buf);
|
//DEBUG DPRINTF(E_DEBUG, L_GENERAL, "Checking for recursive symbolic link: %s (%s)\n", path, str_buf);
|
||||||
if( strncmp(path, str_buf, strlen(str_buf)) == 0 )
|
if( strncmp(path, str_buf, strlen(str_buf)) == 0 )
|
||||||
{
|
{
|
||||||
DPRINTF(E_DEBUG, L_GENERAL, "Ignoring recursive symbolic link: %s (%s)\n", path, str_buf);
|
DPRINTF(E_DEBUG, L_GENERAL, "Ignoring recursive symbolic link: %s (%s)\n", path, str_buf);
|
||||||
return type;
|
return type;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
stat(path, &entry);
|
stat(path, &entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
if( S_ISDIR(entry.st_mode) )
|
if( S_ISDIR(entry.st_mode) )
|
||||||
{
|
{
|
||||||
type = TYPE_DIR;
|
type = TYPE_DIR;
|
||||||
}
|
}
|
||||||
else if( S_ISREG(entry.st_mode) )
|
else if( S_ISREG(entry.st_mode) )
|
||||||
{
|
{
|
||||||
media_types mtype = get_media_type(path);
|
media_types mtype = get_media_type(path);
|
||||||
if (dir_type & mtype)
|
if (dir_type & mtype)
|
||||||
type = TYPE_FILE;
|
type = TYPE_FILE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return type;
|
return type;
|
||||||
}
|
}
|
||||||
|
|
||||||
media_types
|
media_types
|
||||||
valid_media_types(const char *path)
|
valid_media_types(const char *path)
|
||||||
{
|
{
|
||||||
struct media_dir_s *media_dir;
|
struct media_dir_s *media_dir;
|
||||||
|
|
||||||
for (media_dir = media_dirs; media_dir; media_dir = media_dir->next)
|
for (media_dir = media_dirs; media_dir; media_dir = media_dir->next)
|
||||||
{
|
{
|
||||||
if (strncmp(path, media_dir->path, strlen(media_dir->path)) == 0)
|
if (strncmp(path, media_dir->path, strlen(media_dir->path)) == 0)
|
||||||
return media_dir->types;
|
return media_dir->types;
|
||||||
}
|
}
|
||||||
|
|
||||||
return ALL_MEDIA;
|
return ALL_MEDIA;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -544,35 +558,35 @@ valid_media_types(const char *path)
|
|||||||
* it just gets very confused in this case.
|
* it just gets very confused in this case.
|
||||||
* Caveat emptor.
|
* Caveat emptor.
|
||||||
*/
|
*/
|
||||||
static void timevalfix(struct timeval *);
|
static void timevalfix(struct timeval *);
|
||||||
void
|
void
|
||||||
timevaladd(struct timeval *t1, const struct timeval *t2)
|
timevaladd(struct timeval *t1, const struct timeval *t2)
|
||||||
{
|
{
|
||||||
|
|
||||||
t1->tv_sec += t2->tv_sec;
|
t1->tv_sec += t2->tv_sec;
|
||||||
t1->tv_usec += t2->tv_usec;
|
t1->tv_usec += t2->tv_usec;
|
||||||
timevalfix(t1);
|
timevalfix(t1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
timevalsub(struct timeval *t1, const struct timeval *t2)
|
timevalsub(struct timeval *t1, const struct timeval *t2)
|
||||||
{
|
{
|
||||||
|
|
||||||
t1->tv_sec -= t2->tv_sec;
|
t1->tv_sec -= t2->tv_sec;
|
||||||
t1->tv_usec -= t2->tv_usec;
|
t1->tv_usec -= t2->tv_usec;
|
||||||
timevalfix(t1);
|
timevalfix(t1);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
timevalfix(struct timeval *t1)
|
timevalfix(struct timeval *t1)
|
||||||
{
|
{
|
||||||
|
|
||||||
if (t1->tv_usec < 0) {
|
if (t1->tv_usec < 0) {
|
||||||
t1->tv_sec--;
|
t1->tv_sec--;
|
||||||
t1->tv_usec += 1000000;
|
t1->tv_usec += 1000000;
|
||||||
}
|
}
|
||||||
if (t1->tv_usec >= 1000000) {
|
if (t1->tv_usec >= 1000000) {
|
||||||
t1->tv_sec++;
|
t1->tv_sec++;
|
||||||
t1->tv_usec -= 1000000;
|
t1->tv_usec -= 1000000;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user