Move daap to an external output module

This commit is contained in:
Ron Pedde 2006-09-25 03:20:22 +00:00
parent 3051c4ef46
commit 8e23eb9e1d
26 changed files with 2930 additions and 2673 deletions

View File

@ -52,7 +52,7 @@ mt_daapd_SOURCES = main.c daapd.h rend.h uici.c uici.h webserver.c \
webserver.h configfile.c configfile.h err.c err.h restart.c restart.h \
mp3-scanner.h mp3-scanner.c rend-unix.h \
dynamic-art.c dynamic-art.h \
db-generic.c db-generic.h dispatch.c dispatch.h \
db-generic.c db-generic.h \
rxml.c rxml.h redblack.c redblack.h scan-mp3.c scan-mp4.c scan-aif.c \
scan-xml.c scan-wma.c scan-aac.c scan-aac.h scan-wav.c scan-url.c \
smart-parser.c smart-parser.h xml-rpc.c xml-rpc.h \

View File

@ -913,21 +913,6 @@ void config_set_status(WS_CONNINFO *pwsc, int session, char *fmt, ...) {
DPRINTF(E_DBG,L_CONF,"Exiting config_set_status\n");
}
/**
* Get the next available session id.
* This is vulnerable to races, but we don't track sessions,
* so there really isn't a point anyway.
*
* @returns duh... the next available session id
*/
int config_get_next_session(void) {
int session;
session=++config_session;
return session;
}
/**
* implement the VERSION command
*

View File

@ -29,7 +29,6 @@ extern int config_auth(WS_CONNINFO *pwsc, char *user, char *password);
extern void config_handler(WS_CONNINFO *pwsc);
extern void config_set_status(WS_CONNINFO *pwsc, int session, char *fmt, ...);
extern int config_get_session_count(void);
extern int config_get_next_session(void);
/** thread local storage */
typedef struct tag_scan_status {

View File

@ -59,8 +59,6 @@ typedef struct tag_db_functions {
int(*dbs_edit_playlist)(char **, int, char*, char*);
int(*dbs_playcount_increment)(char **, int);
int(*dbs_enum_start)(char **, DBQUERYINFO *);
int(*dbs_enum_size)(char **, DBQUERYINFO *, int *, int *);
int(*dbs_enum_fetch)(char **, DBQUERYINFO *, int *, unsigned char **);
int(*dbs_enum_fetch_row)(char **, PACKED_MP3FILE *, DBQUERYINFO *);
int(*dbs_enum_reset)(char **, DBQUERYINFO *);
int(*dbs_enum_end)(char **);
@ -92,8 +90,6 @@ DB_FUNCTIONS db_functions[] = {
db_sql_edit_playlist,
db_sql_playcount_increment,
db_sql_enum_start,
db_sql_enum_size,
db_sql_enum_fetch,
db_sql_enum_fetch_row,
db_sql_enum_reset,
db_sql_enum_end,
@ -123,8 +119,6 @@ DB_FUNCTIONS db_functions[] = {
db_sql_edit_playlist,
db_sql_playcount_increment,
db_sql_enum_start,
db_sql_enum_size,
db_sql_enum_fetch,
db_sql_enum_fetch_row,
db_sql_enum_reset,
db_sql_enum_end,
@ -142,207 +136,6 @@ DB_FUNCTIONS db_functions[] = {
#endif
{ NULL,NULL }
};
DAAP_ITEMS taglist[] = {
{ 0x05, "miid", "dmap.itemid" },
{ 0x09, "minm", "dmap.itemname" },
{ 0x01, "mikd", "dmap.itemkind" },
{ 0x07, "mper", "dmap.persistentid" },
{ 0x0C, "mcon", "dmap.container" },
{ 0x05, "mcti", "dmap.containeritemid" },
{ 0x05, "mpco", "dmap.parentcontainerid" },
{ 0x05, "mstt", "dmap.status" },
{ 0x09, "msts", "dmap.statusstring" },
{ 0x05, "mimc", "dmap.itemcount" },
{ 0x05, "mctc", "dmap.containercount" },
{ 0x05, "mrco", "dmap.returnedcount" },
{ 0x05, "mtco", "dmap.specifiedtotalcount" },
{ 0x0C, "mlcl", "dmap.listing" },
{ 0x0C, "mlit", "dmap.listingitem" },
{ 0x0C, "mbcl", "dmap.bag" },
{ 0x0C, "mdcl", "dmap.dictionary" },
{ 0x0C, "msrv", "dmap.serverinforesponse" },
{ 0x01, "msau", "dmap.authenticationmethod" },
{ 0x01, "mslr", "dmap.loginrequired" },
{ 0x0B, "mpro", "dmap.protocolversion" },
{ 0x01, "msal", "dmap.supportsautologout" },
{ 0x01, "msup", "dmap.supportsupdate" },
{ 0x01, "mspi", "dmap.supportspersistentids" },
{ 0x01, "msex", "dmap.supportsextensions" },
{ 0x01, "msbr", "dmap.supportsbrowse" },
{ 0x01, "msqy", "dmap.supportsquery" },
{ 0x01, "msix", "dmap.supportsindex" },
{ 0x01, "msrs", "dmap.supportsresolve" },
{ 0x05, "mstm", "dmap.timeoutinterval" },
{ 0x05, "msdc", "dmap.databasescount" },
{ 0x0C, "mlog", "dmap.loginresponse" },
{ 0x05, "mlid", "dmap.sessionid" },
{ 0x0C, "mupd", "dmap.updateresponse" },
{ 0x05, "musr", "dmap.serverrevision" },
{ 0x01, "muty", "dmap.updatetype" },
{ 0x0C, "mudl", "dmap.deletedidlisting" },
{ 0x0C, "mccr", "dmap.contentcodesresponse" },
{ 0x05, "mcnm", "dmap.contentcodesnumber" },
{ 0x09, "mcna", "dmap.contentcodesname" },
{ 0x03, "mcty", "dmap.contentcodestype" },
{ 0x0B, "apro", "daap.protocolversion" },
{ 0x0C, "avdb", "daap.serverdatabases" },
{ 0x0C, "abro", "daap.databasebrowse" },
{ 0x0C, "abal", "daap.browsealbumlisting" },
{ 0x0C, "abar", "daap.browseartistlisting" },
{ 0x0C, "abcp", "daap.browsecomposerlisting" },
{ 0x0C, "abgn", "daap.browsegenrelisting" },
{ 0x0C, "adbs", "daap.databasesongs" },
{ 0x09, "asal", "daap.songalbum" },
{ 0x09, "asar", "daap.songartist" },
{ 0x03, "asbt", "daap.songbeatsperminute" },
{ 0x03, "asbr", "daap.songbitrate" },
{ 0x09, "ascm", "daap.songcomment" },
{ 0x01, "asco", "daap.songcompilation" },
{ 0x09, "ascp", "daap.songcomposer" },
{ 0x0A, "asda", "daap.songdateadded" },
{ 0x0A, "asdm", "daap.songdatemodified" },
{ 0x03, "asdc", "daap.songdisccount" },
{ 0x03, "asdn", "daap.songdiscnumber" },
{ 0x01, "asdb", "daap.songdisabled" },
{ 0x09, "aseq", "daap.songeqpreset" },
{ 0x09, "asfm", "daap.songformat" },
{ 0x09, "asgn", "daap.songgenre" },
{ 0x09, "asdt", "daap.songdescription" },
{ 0x02, "asrv", "daap.songrelativevolume" },
{ 0x05, "assr", "daap.songsamplerate" },
{ 0x05, "assz", "daap.songsize" },
{ 0x05, "asst", "daap.songstarttime" },
{ 0x05, "assp", "daap.songstoptime" },
{ 0x05, "astm", "daap.songtime" },
{ 0x03, "astc", "daap.songtrackcount" },
{ 0x03, "astn", "daap.songtracknumber" },
{ 0x01, "asur", "daap.songuserrating" },
{ 0x03, "asyr", "daap.songyear" },
{ 0x01, "asdk", "daap.songdatakind" },
{ 0x09, "asul", "daap.songdataurl" },
{ 0x0C, "aply", "daap.databaseplaylists" },
{ 0x01, "abpl", "daap.baseplaylist" },
{ 0x0C, "apso", "daap.playlistsongs" },
{ 0x0C, "arsv", "daap.resolve" },
{ 0x0C, "arif", "daap.resolveinfo" },
{ 0x05, "aeNV", "com.apple.itunes.norm-volume" },
{ 0x01, "aeSP", "com.apple.itunes.smart-playlist" },
/* iTunes 4.5+ */
{ 0x01, "msas", "dmap.authenticationschemes" },
{ 0x05, "ascd", "daap.songcodectype" },
{ 0x05, "ascs", "daap.songcodecsubtype" },
{ 0x09, "agrp", "daap.songgrouping" },
{ 0x05, "aeSV", "com.apple.itunes.music-sharing-version" },
{ 0x05, "aePI", "com.apple.itunes.itms-playlistid" },
{ 0x05, "aeCI", "com.apple.iTunes.itms-composerid" },
{ 0x05, "aeGI", "com.apple.iTunes.itms-genreid" },
{ 0x05, "aeAI", "com.apple.iTunes.itms-artistid" },
{ 0x05, "aeSI", "com.apple.iTunes.itms-songid" },
{ 0x05, "aeSF", "com.apple.iTunes.itms-storefrontid" },
/* iTunes 5.0+ */
{ 0x01, "ascr", "daap.songcontentrating" },
{ 0x01, "f" "\x8d" "ch", "dmap.haschildcontainers" },
/* iTunes 6.0.2+ */
{ 0x01, "aeHV", "com.apple.itunes.has-video" },
/* iTunes 6.0.4+ */
{ 0x05, "msas", "dmap.authenticationschemes" },
{ 0x09, "asct", "daap.songcategory" },
{ 0x09, "ascn", "daap.songcontentdescription" },
{ 0x09, "aslc", "daap.songlongcontentdescription" },
{ 0x09, "asky", "daap.songkeywords" },
{ 0x01, "apsm", "daap.playlistshufflemode" },
{ 0x01, "aprm", "daap.playlistrepeatmode" },
{ 0x01, "aePC", "com.apple.itunes.is-podcast" },
{ 0x01, "aePP", "com.apple.itunes.is-podcast-playlist" },
{ 0x01, "aeMK", "com.apple.itunes.mediakind" },
{ 0x09, "aeSN", "com.apple.itunes.series-name" },
{ 0x09, "aeNN", "com.apple.itunes.network-name" },
{ 0x09, "aeEN", "com.apple.itunes.episode-num-str" },
{ 0x05, "aeES", "com.apple.itunes.episode-sort" },
{ 0x05, "aeSU", "com.apple.itunes.season-num" },
/* mt-daapd specific */
{ 0x09, "MSPS", "org.mt-daapd.smart-playlist-spec" },
{ 0x01, "MPTY", "org.mt-daapd.playlist-type" },
{ 0x0C, "MAPR", "org.mt-daapd.addplaylist" },
{ 0x0C, "MAPI", "org.mt-daapd.addplaylistitem" },
{ 0x0C, "MDPR", "org.mt-daapd.delplaylist" },
{ 0x0C, "MDPI", "org.mt-daapd.delplaylistitem" },
{ 0x0C, "MEPR", "org.mt-daapd.editplaylist" },
{ 0x00, NULL, NULL }
};
/** map the string names specified in the meta= tag to bit numbers */
static METAMAP db_metamap[] = {
{ "dmap.itemid", metaItemId },
{ "dmap.itemname", metaItemName },
{ "dmap.itemkind", metaItemKind },
{ "dmap.persistentid", metaPersistentId },
{ "dmap.containeritemid", metaContainerItemId },
{ "dmap.parentcontainerid", metaParentContainerId },
/* end generics */
{ "daap.songalbum", metaSongAlbum },
{ "daap.songartist", metaSongArtist },
{ "daap.songbitrate", metaSongBitRate },
{ "daap.songbeatsperminute", metaSongBPM },
{ "daap.songcomment", metaSongComment },
{ "daap.songcompilation", metaSongCompilation },
{ "daap.songcomposer", metaSongComposer },
{ "daap.songdatakind", metaSongDataKind },
{ "daap.songdataurl", metaSongDataURL },
{ "daap.songdateadded", metaSongDateAdded },
{ "daap.songdatemodified", metaSongDateModified },
{ "daap.songdescription", metaSongDescription },
{ "daap.songdisabled", metaSongDisabled },
{ "daap.songdisccount", metaSongDiscCount },
{ "daap.songdiscnumber", metaSongDiscNumber },
{ "daap.songeqpreset", metaSongEqPreset },
{ "daap.songformat", metaSongFormat },
{ "daap.songgenre", metaSongGenre },
{ "daap.songgrouping", metaSongGrouping },
{ "daap.songrelativevolume", metaSongRelativeVolume },
{ "daap.songsamplerate", metaSongSampleRate },
{ "daap.songsize", metaSongSize },
{ "daap.songstarttime", metaSongStartTime },
{ "daap.songstoptime", metaSongStopTime },
{ "daap.songtime", metaSongTime },
{ "daap.songtrackcount", metaSongTrackCount },
{ "daap.songtracknumber", metaSongTrackNumber },
{ "daap.songuserrating", metaSongUserRating },
{ "daap.songyear", metaSongYear },
/* iTunes 4.5+ (forgot exactly when) */
{ "daap.songcodectype", metaSongCodecType },
{ "daap.songcodecsubtype", metaSongCodecSubType },
{ "com.apple.itunes.norm-volume", metaItunesNormVolume },
{ "com.apple.itunes.itms-songid", metaItmsSongId },
{ "com.apple.itunes.itms-artistid", metaItmsArtistId },
{ "com.apple.itunes.itms-playlistid", metaItmsPlaylistId },
{ "com.apple.itunes.itms-composerid", metaItmsComposerId },
{ "com.apple.itunes.itms-genreid", metaItmsGenreId },
{ "com.apple.itunes.itms-storefrontid",metaItmsStorefrontId },
{ "com.apple.itunes.smart-playlist", metaItunesSmartPlaylist },
/* iTunes 5.0+ */
{ "daap.songcontentrating", metaSongContentRating },
{ "dmap.haschildcontainers", metaHasChildContainers },
/* iTunes 6.0.2+ */
{ "com.apple.itunes.has-video", metaItunesHasVideo },
/* mt-daapd specific */
{ "org.mt-daapd.smart-playlist-spec", metaMPlaylistSpec },
{ "org.mt-daapd.playlist-type", metaMPlaylistType },
{ 0, 0 }
};
char *db_error_list[] = {
"Success",
"Misc SQL Error: %s",
@ -378,53 +171,6 @@ static void db_utf8_validate(MP3FILE *pmp3);
static int db_utf8_validate_string(char *string);
static void db_trim_strings(MP3FILE *pmp3);
static void db_trim_string(char *string);
/**
* encode a string meta request into a MetaField_t
*
* \param meta meta string variable from GET request
*/
MetaField_t db_encode_meta(char *meta) {
MetaField_t bits = 0;
char *start;
char *end;
METAMAP *m;
for(start = meta ; *start ; start = end) {
int len;
if(0 == (end = strchr(start, ',')))
end = start + strlen(start);
len = (int)(end - start);
if(*end != 0)
end++;
for(m = db_metamap ; m->tag ; ++m)
if(!strncmp(m->tag, start, len))
break;
if(m->tag)
bits |= (((MetaField_t) 1) << m->bit);
else
DPRINTF(E_WARN,L_DAAP,"Unknown meta code: %.*s\n", len, start);
}
DPRINTF(E_DBG, L_DAAP, "meta codes: %llu\n", bits);
return bits;
}
/**
* see if a specific metafield was requested
*
* \param meta encoded list of requested metafields
* \param fieldNo field to test for
*/
int db_wantsmeta(MetaField_t meta, MetaFieldName_t fieldNo) {
return 0 != (meta & (((MetaField_t) 1) << fieldNo));
}
/*
* db_readlock
@ -715,31 +461,6 @@ int db_enum_start(char **pe, DBQUERYINFO *pinfo) {
return 0;
}
/**
* get size info about the returned query. This implicitly calls
* db_<dbase>_enum_reset, so it should be positioned at the head
* of the list of returned items.
*/
int db_enum_size(char **pe, DBQUERYINFO *pinfo, int *size, int *count) {
return db_current->dbs_enum_size(pe,pinfo,size,count);
}
/**
* fetch the next item in the result set started by the db enum. this item
* will the the appropriate dmap item. It is the application's duty to free
* the dmap item.
*
* \param plen length of the dmap item returned
* \returns dmap item
*/
int db_enum_fetch(char **pe, DBQUERYINFO *pinfo, int *size,
unsigned char **pdmap)
{
return db_current->dbs_enum_fetch(pe,pinfo,size,pdmap);
}
/**
* fetch the next item int he result set started by the db enum. this
* will be in native packed row format
@ -876,153 +597,6 @@ int db_get_playlist_count(char **pe, int *count) {
}
/* These dmap functions arguably don't belong here, but with
* the database delivering dmap objects by preference over MP3FILE
* objects, it does make some amount of sense to be here
*/
/**
* add a character type to a dmap block (type 0x01)
*
* \param where where to serialize the dmap info
* \tag what four byte tag
* \value what character value
*/
int db_dmap_add_char(unsigned char *where, char *tag, char value) {
/* tag */
memcpy(where,tag,4);
/* len */
where[4]=where[5]=where[6]=0;
where[7]=1;
/* value */
where[8] = value;
return 9;
}
/**
* add a short type to a dmap block (type 0x03)
*
* \param where where to serialize the dmap info
* \tag what four byte tag
* \value what character value
*/
int db_dmap_add_short(unsigned char *where, char *tag, short value) {
/* tag */
memcpy(where,tag,4);
/* len */
where[4]=where[5]=where[6]=0;
where[7]=2;
/* value */
where[8] = (value >> 8) & 0xFF;
where[9] = value & 0xFF;
return 10;
}
/**
* add an int type to a dmap block (type 0x05)
*
* \param where where to serialize the dmap info
* \tag what four byte tag
* \value what character value
*/
int db_dmap_add_int(unsigned char *where, char *tag, int value) {
/* tag */
memcpy(where,tag,4);
/* len */
where[4]=where[5]=where[6]=0;
where[7]=4;
/* value */
where[8] = (value >> 24) & 0xFF;
where[9] = (value >> 16) & 0xFF;
where[10] = (value >> 8) & 0xFF;
where[11] = value & 0xFF;
return 12;
}
/**
* add a string type to a dmap block (type 0x09)
*
* \param where where to serialize the dmap info
* \tag what four byte tag
* \value what character value
*/
int db_dmap_add_string(unsigned char *where, char *tag, char *value) {
int len=0;
if(value)
len = (int)strlen(value);
/* tag */
memcpy(where,tag,4);
/* length */
where[4]=(len >> 24) & 0xFF;
where[5]=(len >> 16) & 0xFF;
where[6]=(len >> 8) & 0xFF;
where[7]=len & 0xFF;
if(len)
strncpy((char*)where+8,value,len);
return 8 + len;
}
/**
* 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(unsigned 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)
*
* \param where where to serialize the dmap info
* \tag what four byte tag
* \value what character value
*/
int db_dmap_add_container(unsigned char *where, char *tag, int size) {
int len=size;
/* tag */
memcpy(where,tag,4);
/* length */
where[4]=(len >> 24) & 0xFF;
where[5]=(len >> 16) & 0xFF;
where[6]=(len >> 8) & 0xFF;
where[7]=len & 0xFF;
return 8;
}
/**
* check the strings in a MP3FILE to ensure they are
* valid utf-8. If they are not, the string will be corrected

View File

@ -26,72 +26,6 @@
#include "smart-parser.h" /** for PARSETREE */
#include "webserver.h" /** for WS_CONNINFO */
typedef enum {
// generic meta data
metaItemId,
metaItemName,
metaItemKind,
metaPersistentId,
metaContainerItemId,
metaParentContainerId,
firstTypeSpecificMetaId,
// song meta data
metaSongAlbum = firstTypeSpecificMetaId,
metaSongArtist,
metaSongBPM,
metaSongBitRate,
metaSongComment,
metaSongCompilation,
metaSongComposer,
metaSongDataKind,
metaSongDataURL,
metaSongDateAdded,
metaSongDateModified,
metaSongDescription,
metaSongDisabled,
metaSongDiscCount,
metaSongDiscNumber,
metaSongEqPreset,
metaSongFormat,
metaSongGenre,
metaSongGrouping,
metaSongRelativeVolume,
metaSongSampleRate,
metaSongSize,
metaSongStartTime,
metaSongStopTime,
metaSongTime,
metaSongTrackCount,
metaSongTrackNumber,
metaSongUserRating,
metaSongYear,
/* iTunes 4.5 + */
metaSongCodecType,
metaSongCodecSubType,
metaItunesNormVolume,
metaItmsSongId,
metaItmsArtistId,
metaItmsPlaylistId,
metaItmsComposerId,
metaItmsGenreId,
metaItmsStorefrontId,
metaItunesSmartPlaylist,
/* iTunes 5.0 + */
metaSongContentRating,
metaHasChildContainers,
/* iTunes 6.0.2+ */
metaItunesHasVideo,
/* mt-daapd specific */
metaMPlaylistSpec,
metaMPlaylistType
} MetaFieldName_t;
typedef enum {
queryTypeItems,
queryTypePlaylists,
@ -114,12 +48,9 @@ typedef enum {
countPlaylists
} CountType_t;
typedef unsigned long long MetaField_t;
typedef struct tag_dbqueryinfo {
QueryType_t query_type;
IndexType_t index_type;
MetaField_t meta;
int zero_length; /* emit zero-length strings? */
int index_low;
int index_high;
@ -136,19 +67,6 @@ typedef struct tag_dbqueryinfo {
WS_CONNINFO *pwsc;
} DBQUERYINFO;
typedef struct {
const char* tag;
MetaFieldName_t bit;
} METAMAP;
typedef struct tag_daap_items {
int type;
char *tag;
char *description;
} DAAP_ITEMS;
extern DAAP_ITEMS taglist[];
extern int db_open(char **pe, char *type, char *parameters);
extern int db_init(int *reload);
extern int db_deinit(void);
@ -158,8 +76,6 @@ extern int db_revision(void);
extern int db_add(char **pe, MP3FILE *pmp3, int *id);
extern int db_enum_start(char **pe, DBQUERYINFO *pinfo);
extern int db_enum_size(char **pe, DBQUERYINFO *pinfo, int *count, int *total_size);
extern int db_enum_fetch(char **pe, DBQUERYINFO *pinfo, int *size, unsigned char **pdmap);
extern int db_enum_fetch_row(char **pe, PACKED_MP3FILE *row, DBQUERYINFO *pinfo);
extern int db_enum_reset(char **pe, DBQUERYINFO *pinfo);
extern int db_enum_end(char **pe);
@ -181,17 +97,6 @@ extern MP3FILE *db_fetch_item(char **pe, int id);
extern MP3FILE *db_fetch_path(char **pe, char *path, int index);
extern M3UFILE *db_fetch_playlist(char **pe, char *path, int index);
/* metatag parsing */
extern MetaField_t db_encode_meta(char *meta);
extern int db_wantsmeta(MetaField_t meta, MetaFieldName_t fieldNo);
/* dmap helper functions */
extern int db_dmap_add_char(unsigned char *where, char *tag, char value);
extern int db_dmap_add_short(unsigned char *where, char *tag, short value);
extern int db_dmap_add_int(unsigned char *where, char *tag, int value);
extern int db_dmap_add_string(unsigned char *where, char *tag, char *value);
extern int db_dmap_add_literal(unsigned char *where, char *tag, char *value, int size);
extern int db_dmap_add_container(unsigned char *where, char *tag, int size);
/* Holdover functions from old db interface...
* should these be removed? Refactored?

View File

@ -60,8 +60,8 @@ static int db_sql_need_dispose=0;
extern char *db_sqlite_updates[];
/* Forwards */
int db_sql_get_size(DBQUERYINFO *pinfo, char **valarray);
int db_sql_build_dmap(DBQUERYINFO *pinfo, char **valarray, unsigned char *presult, int len);
void db_sql_build_mp3file(char **valarray, MP3FILE *pmp3);
int db_sql_update(char **pe, MP3FILE *pmp3, int *id);
int db_sql_update_playlists(char **pe);
@ -71,10 +71,11 @@ char *_db_proper_path(char *path);
#define STR(a) (a) ? (a) : ""
#define ISSTR(a) ((a) && strlen((a)))
#define MAYBEFREE(a) { if((a)) free((a)); };
/*
#define DMAPLEN(a) (((a) && strlen(a)) ? (8+(int)strlen((a))) : \
((pinfo->zero_length) ? 8 : 0))
#define EMIT(a) (pinfo->zero_length ? 1 : ((a) && strlen((a))) ? 1 : 0)
*/
/**
* functions for the specific db backend
*/
@ -149,6 +150,13 @@ char *_db_proper_path(char *path) {
}
int db_sql_atoi(const char *what) {
return what ? atoi(what) : 0;
}
char *db_sql_strdup(const char *what) {
return what ? (strlen(what) ? strdup(what) : NULL) : NULL;
}
/**
* escape a sql string, returning it the supplied buffer.
* note that this uses the sqlite escape format -- use %q for quoted
@ -1344,40 +1352,6 @@ int db_sql_enum_start(char **pe, DBQUERYINFO *pinfo) {
return err;
}
/**
* find the size of the response by walking through the query and
* sizing it
*
* @returns DB_E_SUCCESS on success, error code otherwise
*/
int db_sql_enum_size(char **pe, DBQUERYINFO *pinfo, int *count, int *total_size) {
int err;
int record_size;
SQL_ROW row;
DPRINTF(E_DBG,L_DB,"Enumerating size\n");
*count=0;
*total_size = 0;
while(((err=db_sql_enum_fetch_fn(pe,&row)) == DB_E_SUCCESS) && (row)) {
if((record_size = db_sql_get_size(pinfo,row))) {
*total_size += record_size;
*count = *count + 1;
}
}
if(err != DB_E_SUCCESS) {
db_sql_enum_end_fn(NULL);
return err;
}
err=db_sql_enum_restart_fn(pe);
DPRINTF(E_DBG,L_DB,"Got size: %d\n",*total_size);
return err;
}
/**
* fetch the next row in raw row format
@ -1394,40 +1368,6 @@ int db_sql_enum_fetch_row(char **pe, PACKED_MP3FILE *row, DBQUERYINFO *pinfo) {
return DB_E_SUCCESS;
}
/**
* fetch the next record from the enum
*/
int db_sql_enum_fetch(char **pe, DBQUERYINFO *pinfo, int *size, unsigned char **pdmap) {
int err;
int result_size=0;
unsigned char *presult;
SQL_ROW row;
err=db_sql_enum_fetch_fn(pe, &row);
if(err != DB_E_SUCCESS) {
db_sql_enum_end_fn(NULL);
return err;
}
if(row) {
result_size = db_sql_get_size(pinfo,row);
if(result_size) {
presult = (unsigned char*)malloc(result_size);
if(!presult) {
DPRINTF(E_FATAL,L_DB,"Malloc error\n");
}
db_sql_build_dmap(pinfo,row,presult,result_size);
*pdmap=presult;
*size = result_size;
}
} else {
*size = 0;
}
return DB_E_SUCCESS;
}
/**
* start the enum again
*/
@ -1443,329 +1383,6 @@ int db_sql_enum_end(char **pe) {
return db_sql_enum_end_fn(pe);
}
/**
* get the size of the generated dmap, given a specific meta
*/
int db_sql_get_size(DBQUERYINFO *pinfo, SQL_ROW valarray) {
int size;
int transcode;
switch(pinfo->query_type) {
case queryTypeBrowseArtists: /* simple 'mlit' entry */
case queryTypeBrowseAlbums:
case queryTypeBrowseGenres:
case queryTypeBrowseComposers:
return valarray[0] ? (8 + (int) strlen(valarray[0])) : 0;
case queryTypePlaylists:
size = 8; /* mlit */
size += 12; /* mimc - you get it whether you want it or not */
if(db_wantsmeta(pinfo->meta, metaItemId))
size += 12; /* miid */
if(db_wantsmeta(pinfo->meta, metaItunesSmartPlaylist)) {
if(valarray[plType] && (atoi(valarray[plType])==1))
size += 9; /* aeSP */
}
if(db_wantsmeta(pinfo->meta, metaItemName))
size += (8 + (int) strlen(valarray[plTitle])); /* minm */
if(valarray[plType] && (atoi(valarray[plType])==1) &&
db_wantsmeta(pinfo->meta, metaMPlaylistSpec))
size += (8 + (int) strlen(valarray[plQuery])); /* MSPS */
if(db_wantsmeta(pinfo->meta, metaMPlaylistType))
size += 9; /* MPTY */
return size;
break;
case queryTypeItems:
case queryTypePlaylistItems: /* essentially the same query */
/* see if this is going to be transcoded */
transcode = plugin_ssc_should_transcode(pinfo->pwsc,valarray[37]);
/* Items that get changed by transcode:
*
* type: item 8: changes to 'wav'
* description: item 29: changes to 'wav audio file'
* bitrate: item 15: guestimated, based on item 15, samplerate
*
* probably file size should change as well, but currently doesn't
*/
size = 8; /* mlit */
if(db_wantsmeta(pinfo->meta, metaItemKind))
/* mikd */
size += 9;
if(db_wantsmeta(pinfo->meta, metaSongDataKind))
/* asdk */
size += 9;
if(db_wantsmeta(pinfo->meta, metaSongDataURL))
/* asul */
size += DMAPLEN(valarray[13]);
if(db_wantsmeta(pinfo->meta, metaSongAlbum))
/* asal */
size += DMAPLEN(valarray[5]);
if(db_wantsmeta(pinfo->meta, metaSongArtist))
/* asar */
size += DMAPLEN(valarray[4]);
if(valarray[23] && atoi(valarray[23]) && db_wantsmeta(pinfo->meta, metaSongBPM))
/* asbt */
size += 10;
if(db_wantsmeta(pinfo->meta, metaSongBitRate)) {
/* asbr */
if(transcode) {
if(valarray[15] && atoi(valarray[15]))
size += 10;
} else {
if(valarray[14] && atoi(valarray[14]))
size += 10;
}
}
if(db_wantsmeta(pinfo->meta, metaSongComment))
/* ascm */
size += DMAPLEN(valarray[7]);
if(valarray[24] && atoi(valarray[24]) && db_wantsmeta(pinfo->meta,metaSongCompilation))
/* asco */
size += 9;
if(db_wantsmeta(pinfo->meta, metaSongComposer))
/* ascp */
size += DMAPLEN(valarray[9]);
if(db_wantsmeta(pinfo->meta, metaSongGrouping))
/* agrp */
size += DMAPLEN(valarray[12]);
if(valarray[30] && atoi(valarray[30]) && db_wantsmeta(pinfo->meta, metaSongDateAdded))
/* asda */
size += 12;
if(valarray[31] && atoi(valarray[31]) && db_wantsmeta(pinfo->meta,metaSongDateModified))
/* asdm */
size += 12;
if(valarray[22] && atoi(valarray[22]) && db_wantsmeta(pinfo->meta, metaSongDiscCount))
/* asdc */
size += 10;
if(valarray[21] && atoi(valarray[21]) && db_wantsmeta(pinfo->meta, metaSongDiscNumber))
/* asdn */
size += 10;
if(db_wantsmeta(pinfo->meta, metaSongGenre))
/* asgn */
size += DMAPLEN(valarray[6]);
if(db_wantsmeta(pinfo->meta,metaItemId))
/* miid */
size += 12;
if(db_wantsmeta(pinfo->meta,metaSongFormat)) {
/* asfm */
if(transcode) {
size += 11; /* 'wav' */
} else {
size += DMAPLEN(valarray[8]);
}
}
if(db_wantsmeta(pinfo->meta,metaSongDescription)) {
/* asdt */
if(transcode) {
size += 22; /* 'wav audio file' */
} else {
size += DMAPLEN(valarray[29]);
}
}
if(db_wantsmeta(pinfo->meta,metaItemName))
/* minm */
size += DMAPLEN(valarray[3]);
if(valarray[34] && atoi(valarray[34]) && db_wantsmeta(pinfo->meta,metaSongDisabled))
/* asdb */
size += 9;
if(valarray[15] && atoi(valarray[15]) && db_wantsmeta(pinfo->meta,metaSongSampleRate))
/* assr */
size += 12;
if(valarray[17] && atoi(valarray[17]) && db_wantsmeta(pinfo->meta,metaSongSize))
/* assz */
size += 12;
/* In the old daap code, we always returned 0 for asst and assp
* (song start time, song stop time). I don't know if this
* is required, so I'm going to disabled it
*/
if(valarray[16] && atoi(valarray[16]) && db_wantsmeta(pinfo->meta, metaSongTime))
/* astm */
size += 12;
if(valarray[20] && atoi(valarray[20]) && db_wantsmeta(pinfo->meta, metaSongTrackCount))
/* astc */
size += 10;
if(valarray[19] && atoi(valarray[19]) && db_wantsmeta(pinfo->meta, metaSongTrackNumber))
/* astn */
size += 10;
if(valarray[25] && atoi(valarray[25]) && db_wantsmeta(pinfo->meta, metaSongUserRating))
/* asur */
size += 9;
if(valarray[18] && atoi(valarray[18]) && db_wantsmeta(pinfo->meta, metaSongYear))
/* asyr */
size += 10;
if(db_wantsmeta(pinfo->meta, metaContainerItemId))
/* mcti */
size += 12;
if((valarray[37]) && (strlen(valarray[37]) == 4) &&
db_wantsmeta(pinfo->meta,metaSongCodecType))
/* ascd */
size += 12;
if(db_wantsmeta(pinfo->meta,metaSongContentRating))
/* ascr */
size += 9;
if(db_wantsmeta(pinfo->meta,metaItunesHasVideo))
/* aeHV */
size += 9;
return size;
break;
default:
DPRINTF(E_LOG,L_DB|L_DAAP,"Unknown query type: %d\n",(int)pinfo->query_type);
return 0;
}
return 0;
}
int db_sql_build_dmap(DBQUERYINFO *pinfo, char **valarray, unsigned char *presult, int len) {
unsigned char *current = presult;
int transcode;
int samplerate=0;
switch(pinfo->query_type) {
case queryTypeBrowseArtists: /* simple 'mlit' entry */
case queryTypeBrowseAlbums:
case queryTypeBrowseGenres:
case queryTypeBrowseComposers:
return db_dmap_add_string(current,"mlit",valarray[0]);
case queryTypePlaylists:
/* do I want to include the mlit? */
current += db_dmap_add_container(current,"mlit",len - 8);
if(db_wantsmeta(pinfo->meta,metaItemId))
current += db_dmap_add_int(current,"miid",atoi(valarray[plID]));
current += db_dmap_add_int(current,"mimc",atoi(valarray[plItems]));
if(db_wantsmeta(pinfo->meta,metaItunesSmartPlaylist)) {
if(valarray[plType] && (atoi(valarray[plType]) == 1))
current += db_dmap_add_char(current,"aeSP",1);
}
if(db_wantsmeta(pinfo->meta,metaItemName))
current += db_dmap_add_string(current,"minm",valarray[plTitle]);
if((valarray[plType]) && (atoi(valarray[plType])==1) &&
db_wantsmeta(pinfo->meta, metaMPlaylistSpec))
current += db_dmap_add_string(current,"MSPS",valarray[plQuery]);
if(db_wantsmeta(pinfo->meta, metaMPlaylistType))
current += db_dmap_add_char(current,"MPTY",atoi(valarray[plType]));
break;
case queryTypeItems:
case queryTypePlaylistItems: /* essentially the same query */
/* see if this is going to be transcoded */
transcode = plugin_ssc_should_transcode(pinfo->pwsc,valarray[37]);
/* Items that get changed by transcode:
*
* type: item 8: changes to 'wav'
* description: item 29: changes to 'wav audio file'
* bitrate: item 15: guestimated, but doesn't change file size
*
* probably file size should change as well, but currently doesn't
*/
current += db_dmap_add_container(current,"mlit",len-8);
if(db_wantsmeta(pinfo->meta, metaItemKind))
current += db_dmap_add_char(current,"mikd",(char)atoi(valarray[28]));
if(db_wantsmeta(pinfo->meta, metaSongDataKind))
current += db_dmap_add_char(current,"asdk",(char)atoi(valarray[27]));
if(EMIT(valarray[13]) && db_wantsmeta(pinfo->meta, metaSongDataURL))
current += db_dmap_add_string(current,"asul",valarray[13]);
if(EMIT(valarray[5]) && db_wantsmeta(pinfo->meta, metaSongAlbum))
current += db_dmap_add_string(current,"asal",valarray[5]);
if(EMIT(valarray[4]) && db_wantsmeta(pinfo->meta, metaSongArtist))
current += db_dmap_add_string(current,"asar",valarray[4]);
if(valarray[23] && atoi(valarray[23]) && db_wantsmeta(pinfo->meta, metaSongBPM))
current += db_dmap_add_short(current,"asbt",(short)atoi(valarray[23]));
if(valarray[14] && atoi(valarray[14]) && db_wantsmeta(pinfo->meta, metaSongBitRate)) {
if(transcode) {
if(valarray[15]) samplerate=atoi(valarray[15]);
if(samplerate) {
current += db_dmap_add_short(current,"asbr",
(short)(samplerate / 250 * 8));
}
} else {
current += db_dmap_add_short(current,"asbr",(short)atoi(valarray[14]));
}
}
if(EMIT(valarray[7]) && db_wantsmeta(pinfo->meta, metaSongComment))
current += db_dmap_add_string(current,"ascm",valarray[7]);
if(valarray[24] && atoi(valarray[24]) && db_wantsmeta(pinfo->meta,metaSongCompilation))
current += db_dmap_add_char(current,"asco",(char)atoi(valarray[24]));
if(EMIT(valarray[9]) && db_wantsmeta(pinfo->meta, metaSongComposer))
current += db_dmap_add_string(current,"ascp",valarray[9]);
if(EMIT(valarray[12]) && db_wantsmeta(pinfo->meta, metaSongGrouping))
current += db_dmap_add_string(current,"agrp",valarray[12]);
if(valarray[30] && atoi(valarray[30]) && db_wantsmeta(pinfo->meta, metaSongDateAdded))
current += db_dmap_add_int(current,"asda",(int)atoi(valarray[30]));
if(valarray[31] && atoi(valarray[31]) && db_wantsmeta(pinfo->meta,metaSongDateModified))
current += db_dmap_add_int(current,"asdm",(int)atoi(valarray[31]));
if(valarray[22] && atoi(valarray[22]) && db_wantsmeta(pinfo->meta, metaSongDiscCount))
current += db_dmap_add_short(current,"asdc",(short)atoi(valarray[22]));
if(valarray[21] && atoi(valarray[21]) && db_wantsmeta(pinfo->meta, metaSongDiscNumber))
current += db_dmap_add_short(current,"asdn",(short)atoi(valarray[21]));
if(EMIT(valarray[6]) && db_wantsmeta(pinfo->meta, metaSongGenre))
current += db_dmap_add_string(current,"asgn",valarray[6]);
if(db_wantsmeta(pinfo->meta,metaItemId))
current += db_dmap_add_int(current,"miid",(int)atoi(valarray[0]));
if(EMIT(valarray[8]) && db_wantsmeta(pinfo->meta,metaSongFormat)) {
if(transcode) {
current += db_dmap_add_string(current,"asfm","wav");
} else {
current += db_dmap_add_string(current,"asfm",valarray[8]);
}
}
if(EMIT(valarray[29]) && db_wantsmeta(pinfo->meta,metaSongDescription)) {
if(transcode) {
current += db_dmap_add_string(current,"asdt","wav audio file");
} else {
current += db_dmap_add_string(current,"asdt",valarray[29]);
}
}
if(EMIT(valarray[3]) && db_wantsmeta(pinfo->meta,metaItemName))
current += db_dmap_add_string(current,"minm",valarray[3]);
if(valarray[34] && atoi(valarray[34]) && db_wantsmeta(pinfo->meta,metaSongDisabled))
current += db_dmap_add_char(current,"asdb",(char)atoi(valarray[34]));
if(valarray[15] && atoi(valarray[15]) && db_wantsmeta(pinfo->meta,metaSongSampleRate))
current += db_dmap_add_int(current,"assr",atoi(valarray[15]));
if(valarray[17] && atoi(valarray[17]) && db_wantsmeta(pinfo->meta,metaSongSize))
current += db_dmap_add_int(current,"assz",atoi(valarray[17]));
if(valarray[16] && atoi(valarray[16]) && db_wantsmeta(pinfo->meta, metaSongTime))
current += db_dmap_add_int(current,"astm",atoi(valarray[16]));
if(valarray[20] && atoi(valarray[20]) && db_wantsmeta(pinfo->meta, metaSongTrackCount))
current += db_dmap_add_short(current,"astc",(short)atoi(valarray[20]));
if(valarray[19] && atoi(valarray[19]) && db_wantsmeta(pinfo->meta, metaSongTrackNumber))
current += db_dmap_add_short(current,"astn",(short)atoi(valarray[19]));
if(valarray[25] && atoi(valarray[25]) && db_wantsmeta(pinfo->meta, metaSongUserRating))
current += db_dmap_add_char(current,"asur",(char)atoi(valarray[25]));
if(valarray[18] && atoi(valarray[18]) && db_wantsmeta(pinfo->meta, metaSongYear))
current += db_dmap_add_short(current,"asyr",(short)atoi(valarray[18]));
if((valarray[37]) && (strlen(valarray[37]) == 4) &&
db_wantsmeta(pinfo->meta,metaSongCodecType))
current += db_dmap_add_literal(current,"ascd",valarray[37],4);
if(db_wantsmeta(pinfo->meta, metaContainerItemId))
current += db_dmap_add_int(current,"mcti",atoi(valarray[0]));
if(db_wantsmeta(pinfo->meta, metaItunesHasVideo))
current += db_dmap_add_char(current,"aeHV",atoi(valarray[39]));
if(db_wantsmeta(pinfo->meta, metaSongContentRating))
current += db_dmap_add_char(current,"ascr",atoi(valarray[40]));
return 0;
break;
default:
DPRINTF(E_LOG,L_DB|L_DAAP,"Unknown query type: %d\n",(int)pinfo->query_type);
return 0;
}
return 0;
}
int db_sql_atoi(const char *what) {
return what ? atoi(what) : 0;
}
char *db_sql_strdup(const char *what) {
return what ? (strlen(what) ? strdup(what) : NULL) : NULL;
}
void db_sql_build_m3ufile(SQL_ROW valarray, M3UFILE *pm3u) {
memset(pm3u,0x00,sizeof(M3UFILE));

View File

@ -37,8 +37,8 @@ extern int db_sql_deinit(void);
extern int db_sql_escape(char *buffer, int *size, char *fmt, ...);
extern int db_sql_add(char **pe, MP3FILE *pmp3, int *id);
extern int db_sql_enum_start(char **pe, DBQUERYINFO *pinfo);
extern int db_sql_enum_size(char **pe, DBQUERYINFO *pinfo, int *count, int *total_size);
extern int db_sql_enum_fetch(char **pe, DBQUERYINFO *pinfo, int *size, unsigned char **pdmap);
extern int db_sql_enum_fetch_row(char **pe, PACKED_MP3FILE *row, DBQUERYINFO *pinfo);
extern int db_sql_enum_reset(char **pe, DBQUERYINFO *pinfo);
extern int db_sql_enum_end(char **pe);
@ -64,6 +64,7 @@ extern int db_sql_fetch_int(char **pe, int *result, char *fmt, ...);
extern int db_sql_fetch_char(char **pe, char **result, char *fmt, ...);
extern int db_sql_dispose_row(void);
/*
typedef enum {
songID,
songPath,
@ -103,7 +104,9 @@ typedef enum {
songForceUpdate,
songCodecType
} SongField_t;
*/
/*
typedef enum {
plID,
plTitle,
@ -113,6 +116,7 @@ typedef enum {
plDBTimestamp,
plPath
} PlaylistField_t;
*/
#define DB_SQL_EVENT_STARTUP 0
#define DB_SQL_EVENT_SONGSCANSTART 1

File diff suppressed because it is too large Load Diff

View File

@ -1,14 +0,0 @@
/*
* $Id$
*/
#ifndef _DISPATCH_H_
#define _DISPATCH_H_
#include "db-generic.h"
extern void daap_handler(WS_CONNINFO *pwsc);
extern int daap_auth(WS_CONNINFO *pwsc, char *username, char *password);
extern void dispatch_stream_id(WS_CONNINFO *pwsc, int session, char *id);
#endif

61
src/ff-dbstruct.h Normal file
View File

@ -0,0 +1,61 @@
/*
* $Id: $
*
* Database structure and fields
*/
#ifndef _FF_DBSTRUCT_H_
#define _FF_DBSTRUCT_H_
#define PL_ID 0
#define PL_TITLE 1
#define PL_TYPE 2
#define PL_ITEMS 3
#define PL_QUERY 4
#define PL_DB_TIMESTAMP 5
#define PL_PATH 6
#define PL_IDX 7
#define SG_ID 0
#define SG_PATH 1
#define SG_FNAME 2
#define SG_TITLE 3
#define SG_ARTIST 4
#define SG_ALBUM 5
#define SG_GENRE 6
#define SG_COMMENT 7
#define SG_TYPE 8
#define SG_COMPOSER 9
#define SG_ORCHESTRA 10
#define SG_CONDUCTOR 11
#define SG_GROUPING 12
#define SG_URL 13
#define SG_BITRATE 14
#define SG_SAMPLERATE 15
#define SG_SONG_LENGTH 16
#define SG_FILE_SIZE 17
#define SG_YEAR 18
#define SG_TRACK 19
#define SG_TOTAL_TRACKS 20
#define SG_DISC 21
#define SG_TOTAL_DISCS 22
#define SG_BPM 23
#define SG_COMPILATION 24
#define SG_RATING 25
#define SG_PLAY_COUNT 26
#define SG_DATA_KIND 27
#define SG_ITEM_KIND 28
#define SG_DESCRIPTION 29
#define SG_TIME_ADDED 30
#define SG_TIME_MODIFIED 31
#define SG_TIME_PLAYED 32
#define SG_DB_TIMESTAMP 33
#define SG_DISABLED 34
#define SG_SAMPLE_COUNT 35
#define SG_FORCE_UPDATE 36
#define SG_CODECTYPE 37
#define SG_IDX 38
#define SG_HAS_VIDEO 39
#define SG_CONTENTRATING 40
#endif /* _FF_DBSTRUCT_H_ */

View File

@ -44,15 +44,20 @@
#ifndef E_FATAL
# define E_FATAL 0
# define E_LOG 1
# define E_WARN 2
# define E_INF 5
# define E_DBG 9
# define E_SPAM 10
#endif
#define COUNT_SONGS 0
#define COUNT_PLAYLISTS 1
struct tag_ws_conninfo;
/* Functions that must be exported by different plugin types */
typedef struct tag_plugin_output_fn {
int(*can_handle)(struct tag_ws_conninfo *pwsc);
void(*handler)(struct tag_ws_conninfo *pwsc);
int(*auth)(struct tag_ws_conninfo *pwsc, char *username, char *pw);
} PLUGIN_OUTPUT_FN;
@ -81,7 +86,6 @@ typedef struct tag_plugin_info {
int version; /* PLUGIN_VERSION */
int type; /* PLUGIN_OUTPUT, etc */
char *server; /* Server/version format */
char *url; /* regex match of web urls */
PLUGIN_OUTPUT_FN *output_fns; /* functions for different plugin types */
PLUGIN_EVENT_FN *event_fns;
PLUGIN_TRANSCODE_FN *transcode_fns;
@ -116,7 +120,7 @@ typedef struct tag_db_query {
typedef struct tag_plugin_input_fn {
/* webserver helpers */
char* (*ws_uri)(struct tag_ws_conninfo *);
void (*ws_close)(struct tag_ws_conninfo *);
void (*ws_will_close)(struct tag_ws_conninfo *);
int (*ws_returnerror)(struct tag_ws_conninfo *, int, char *);
char* (*ws_getvar)(struct tag_ws_conninfo *, char *);
int (*ws_writefd)(struct tag_ws_conninfo *, char *, ...);
@ -136,11 +140,23 @@ typedef struct tag_plugin_input_fn {
int (*db_enum_start)(char **, DB_QUERY *);
int (*db_enum_fetch_row)(char **, char ***, DB_QUERY *);
int (*db_enum_end)(char **);
int (*db_enum_restart)(char **, DB_QUERY *);
void (*db_enum_dispose)(char **, DB_QUERY*);
void (*stream)(struct tag_ws_conninfo *, char *);
int (*db_add_playlist)(char **pe, char *name, int type, char *clause, char *path, int index, int *playlistid);
int (*db_add_playlist_item)(char **pe, int playlistid, int songid);
int (*db_edit_playlist)(char **pe, int id, char *name, char *clause);
int (*db_delete_playlist)(char **pe, int playlistid);
int (*db_delete_playlist_item)(char **pe, int playlistid, int songid);
int (*db_revision)(void);
int (*db_count_items)(int what);
char *(*conf_alloc_string)(char *section, char *key, char *dflt);
void (*conf_dispose_string)(char *str);
int (*conf_get_int)(char *section, char *key, int dflt);
int (*config_set_status)(struct tag_ws_conninfo *pwsc, int session, char *fmt, ...);
} PLUGIN_INPUT_FN;

View File

@ -72,7 +72,6 @@
#include "conf.h"
#include "configfile.h"
#include "dispatch.h"
#include "err.h"
#include "mp3-scanner.h"
#include "webserver.h"
@ -451,13 +450,6 @@ int main(int argc, char *argv[]) {
}
ws_registerhandler(config.server, "^.*$",main_handler,main_auth,1);
ws_registerhandler(config.server, "^/server-info$",daap_handler,NULL,0);
ws_registerhandler(config.server, "^/content-codes$",daap_handler,daap_auth,0); /* iTunes 6.0.4+? */
ws_registerhandler(config.server,"^/login$",daap_handler,daap_auth,0);
ws_registerhandler(config.server,"^/update$",daap_handler,daap_auth,0);
ws_registerhandler(config.server,"^/databases$",daap_handler,daap_auth,0);
ws_registerhandler(config.server,"^/logout$",daap_handler,NULL,0);
ws_registerhandler(config.server,"^/databases/.*",daap_handler,NULL,0);
#ifndef WITHOUT_MDNS
if(config.use_mdns) { /* register services */
@ -487,7 +479,7 @@ int main(int argc, char *argv[]) {
DPRINTF(E_LOG,L_MAIN|L_REND,"Registering rendezvous names\n");
iface = conf_alloc_string("general","interface","");
rend_register(servername,"_daap._tcp",ws_config.port,iface,txtrecord);
rend_register(servername,"_http._tcp",ws_config.port,iface,txtrecord);
plugin_rend_register(servername,ws_config.port,iface,txtrecord);

View File

@ -142,6 +142,7 @@ static TAGHANDLER taghandlers[] = {
{ "url", scan_get_urlinfo, "pls", NULL, "Playlist URL" },
{ "pls", scan_get_urlinfo, "pls", NULL, "Playlist URL" },
{ "m4v", scan_get_mp4info, "m4v", "mp4v", "MPEG-4 video file" },
{ "mp4", scan_get_mp4info, "m4v", "mp4v", "MPEG-4 video file" },
#ifdef OGGVORBIS
{ "ogg", scan_get_ogginfo, "ogg", "ogg", "Ogg Vorbis audio file" },
#endif

View File

@ -26,8 +26,8 @@
#define _XOPEN_SOURCE 500 /** unix98? pthread_once_t, etc */
#include <errno.h>
#include <fcntl.h>
#include <pthread.h>
#include <regex.h>
#include <stdarg.h>
#include <stdlib.h>
#include <stdio.h>
@ -37,12 +37,14 @@
#include <sys/types.h>
#include "conf.h"
#include "configfile.h"
#include "db-generic.h"
#include "dispatch.h"
#include "dynamic-art.h"
#include "err.h"
#include "os.h"
#include "plugin.h"
#include "rend.h"
#include "restart.h"
#include "smart-parser.h"
#include "xml-rpc.h"
#include "webserver.h"
@ -50,7 +52,6 @@
typedef struct tag_pluginentry {
void *phandle;
regex_t regex;
PLUGIN_INFO *pinfo;
struct tag_pluginentry *next;
} PLUGIN_ENTRY;
@ -76,7 +77,7 @@ void _plugin_recalc_codecs(void);
/* webserver helpers */
char *pi_ws_uri(WS_CONNINFO *pwsc);
void pi_ws_close(WS_CONNINFO *pwsc);
void pi_ws_will_close(WS_CONNINFO *pwsc);
int pi_ws_fd(WS_CONNINFO *pwsc);
/* misc helpers */
@ -89,14 +90,15 @@ int pi_db_count(void);
int pi_db_enum_start(char **pe, DB_QUERY *pinfo);
int pi_db_enum_fetch_row(char **pe, char ***row, DB_QUERY *pinfo);
int pi_db_enum_end(char **pe);
int pi_db_enum_restart(char **pe, DB_QUERY *pinfo);
void pi_db_enum_dispose(char **pe, DB_QUERY *pinfo);
void pi_stream(WS_CONNINFO *pwsc, char *id);
int pi_db_count_items(int what);
void pi_conf_dispose_string(char *str);
PLUGIN_INPUT_FN pi = {
pi_ws_uri,
pi_ws_close,
pi_ws_will_close,
ws_returnerror,
ws_getvar,
ws_writefd,
@ -115,11 +117,23 @@ PLUGIN_INPUT_FN pi = {
pi_db_enum_start,
pi_db_enum_fetch_row,
pi_db_enum_end,
pi_db_enum_restart,
pi_db_enum_dispose,
pi_stream,
db_add_playlist,
db_add_playlist_item,
db_edit_playlist,
db_delete_playlist,
db_delete_playlist_item,
db_revision,
pi_db_count_items,
conf_alloc_string,
pi_conf_dispose_string
pi_conf_dispose_string,
conf_get_int,
config_set_status
};
@ -262,13 +276,6 @@ int plugin_load(char **pe, char *path) {
return PLUGIN_E_NOLOAD;
}
if(pinfo->type & PLUGIN_OUTPUT) {
/* build the regex */
if(regcomp(&ppi->regex,pinfo->url,REG_EXTENDED | REG_NOSUB)) {
DPRINTF(E_LOG,L_PLUG,"Bad regex in %s: %s\n",path,pinfo->url);
}
}
DPRINTF(E_INF,L_PLUG,"Loaded plugin %s (%s)\n",path,pinfo->server);
if(!_plugin_initialized) {
@ -291,23 +298,20 @@ int plugin_load(char **pe, char *path) {
*/
int plugin_url_candispatch(WS_CONNINFO *pwsc) {
PLUGIN_ENTRY *ppi;
DPRINTF(E_DBG,L_PLUG,"Entering candispatch\n");
ppi = _plugin_list.next;
while(ppi) {
if(ppi->pinfo->type & PLUGIN_OUTPUT) {
if(!regexec(&ppi->regex,pwsc->uri,0,NULL,0)) {
/* we have a winner */
if((ppi->pinfo->output_fns)->can_handle(pwsc)) {
return TRUE;
}
}
ppi = ppi->next;
}
return FALSE;
}
/**
* actually DISPATCH the hander we said we wanted
*
@ -321,7 +325,7 @@ void plugin_url_handle(WS_CONNINFO *pwsc) {
ppi = _plugin_list.next;
while(ppi) {
if(ppi->pinfo->type & PLUGIN_OUTPUT) {
if(!regexec(&ppi->regex,pwsc->uri,0,NULL,0)) {
if((ppi->pinfo->output_fns)->can_handle(pwsc)) {
/* we have a winner */
DPRINTF(E_DBG,L_PLUG,"Dispatching %s to %s\n", pwsc->uri,
ppi->pinfo->server);
@ -387,7 +391,7 @@ int plugin_auth_handle(WS_CONNINFO *pwsc, char *username, char *pw) {
ppi = _plugin_list.next;
while(ppi) {
if(ppi->pinfo->type & PLUGIN_OUTPUT) {
if(!regexec(&ppi->regex,pwsc->uri,0,NULL,0)) {
if((ppi->pinfo->output_fns)->can_handle(pwsc)) {
/* we have a winner */
DPRINTF(E_DBG,L_PLUG,"Dispatching %s to %s\n", pwsc->uri,
ppi->pinfo->server);
@ -602,7 +606,7 @@ char *pi_ws_uri(WS_CONNINFO *pwsc) {
return pwsc->uri;
}
void pi_ws_close(WS_CONNINFO *pwsc) {
void pi_ws_will_close(WS_CONNINFO *pwsc) {
pwsc->close=1;
}
@ -726,9 +730,28 @@ int pi_db_enum_end(char **pe) {
return db_enum_end(pe);
}
void pi_stream(WS_CONNINFO *pwsc, char *id) {
dispatch_stream_id(pwsc, 0, id);
return;
/* FIXME: error checking */
int pi_db_count_items(int what) {
int count=0;
switch(what) {
case COUNT_SONGS:
db_get_song_count(NULL,&count);
break;
case COUNT_PLAYLISTS:
db_get_playlist_count(NULL,&count);
break;
}
return count;
}
int pi_db_enum_restart(char **pe, DB_QUERY *pinfo) {
DBQUERYINFO *pqi;
pqi = (DBQUERYINFO*)pinfo->priv;
return db_enum_reset(pe,pqi);
}
void pi_db_enum_dispose(char **pe, DB_QUERY *pinfo) {
@ -746,6 +769,161 @@ void pi_db_enum_dispose(char **pe, DB_QUERY *pinfo) {
}
}
void pi_stream(WS_CONNINFO *pwsc, char *id) {
int session = 0;
MP3FILE *pmp3;
int file_fd;
int bytes_copied;
off_t real_len;
off_t file_len;
off_t offset=0;
long img_size;
struct stat sb;
int img_fd;
int item;
/* stream out the song */
pwsc->close=1;
item = atoi(id);
if(ws_getrequestheader(pwsc,"range")) {
offset=(off_t)atol(ws_getrequestheader(pwsc,"range") + 6);
}
/* FIXME: error handling */
pmp3=db_fetch_item(NULL,item);
if(!pmp3) {
DPRINTF(E_LOG,L_DAAP|L_WS|L_DB,"Could not find requested item %lu\n",item);
config_set_status(pwsc,session,NULL);
ws_returnerror(pwsc,404,"File Not Found");
} else if (plugin_ssc_should_transcode(pwsc,pmp3->codectype)) {
/************************
* Server side conversion
************************/
config_set_status(pwsc,session,
"Transcoding '%s' (id %d)",
pmp3->title,pmp3->id);
DPRINTF(E_WARN,L_WS,
"Session %d: Streaming file '%s' to %s (offset %ld)\n",
session,pmp3->fname, pwsc->hostname,(long)offset);
bytes_copied = plugin_ssc_transcode(pwsc,pmp3->path,pmp3->codectype,
pmp3->song_length,offset,1);
config_set_status(pwsc,session,NULL);
db_dispose_item(pmp3);
} else {
/**********************
* stream file normally
**********************/
if(pmp3->data_kind != 0) {
ws_returnerror(pwsc,500,"Can't stream radio station");
return;
}
file_fd=r_open2(pmp3->path,O_RDONLY);
if(file_fd == -1) {
pwsc->error=errno;
DPRINTF(E_WARN,L_WS,"Thread %d: Error opening %s: %s\n",
pwsc->threadno,pmp3->path,strerror(errno));
ws_returnerror(pwsc,404,"Not found");
config_set_status(pwsc,session,NULL);
db_dispose_item(pmp3);
} else {
real_len=lseek(file_fd,0,SEEK_END);
lseek(file_fd,0,SEEK_SET);
/* Re-adjust content length for cover art */
if((conf_isset("general","art_filename")) &&
((img_fd=da_get_image_fd(pmp3->path)) != -1)) {
fstat(img_fd, &sb);
img_size = sb.st_size;
r_close(img_fd);
if (strncasecmp(pmp3->type,"mp3",4) ==0) {
/*PENDING*/
} else if (strncasecmp(pmp3->type, "m4a", 4) == 0) {
real_len += img_size + 24;
if (offset > img_size + 24) {
offset -= img_size + 24;
}
}
}
file_len = real_len - offset;
DPRINTF(E_DBG,L_WS,"Thread %d: Length of file (remaining) is %ld\n",
pwsc->threadno,(long)file_len);
// DWB: fix content-type to correctly reflect data
// content type (dmap tagged) should only be used on
// dmap protocol requests, not the actually song data
if(pmp3->type)
ws_addresponseheader(pwsc,"Content-Type","audio/%s",pmp3->type);
ws_addresponseheader(pwsc,"Content-Length","%ld",(long)file_len);
ws_addresponseheader(pwsc,"Connection","Close");
if(!offset)
ws_writefd(pwsc,"HTTP/1.1 200 OK\r\n");
else {
ws_addresponseheader(pwsc,"Content-Range","bytes %ld-%ld/%ld",
(long)offset,(long)real_len,
(long)real_len+1);
ws_writefd(pwsc,"HTTP/1.1 206 Partial Content\r\n");
}
ws_emitheaders(pwsc);
config_set_status(pwsc,session,"Streaming '%s' (id %d)",
pmp3->title, pmp3->id);
DPRINTF(E_WARN,L_WS,"Session %d: Streaming file '%s' to %s (offset %d)\n",
session,pmp3->fname, pwsc->hostname,(long)offset);
if(!offset)
config.stats.songs_served++; /* FIXME: remove stat races */
if((conf_isset("general","art_filename")) &&
(!offset) &&
((img_fd=da_get_image_fd(pmp3->path)) != -1)) {
if (strncasecmp(pmp3->type,"mp3",4) ==0) {
DPRINTF(E_INF,L_WS|L_ART,"Dynamic add artwork to %s (fd %d)\n",
pmp3->fname, img_fd);
da_attach_image(img_fd, pwsc->fd, file_fd, offset);
} else if (strncasecmp(pmp3->type, "m4a", 4) == 0) {
DPRINTF(E_INF,L_WS|L_ART,"Dynamic add artwork to %s (fd %d)\n",
pmp3->fname, img_fd);
da_aac_attach_image(img_fd, pwsc->fd, file_fd, offset);
}
} else if(offset) {
DPRINTF(E_INF,L_WS,"Seeking to offset %ld\n",(long)offset);
lseek(file_fd,offset,SEEK_SET);
}
if((bytes_copied=copyfile(file_fd,pwsc->fd)) == -1) {
DPRINTF(E_INF,L_WS,"Error copying file to remote... %s\n",
strerror(errno));
} else {
DPRINTF(E_INF,L_WS,"Finished streaming file to remote: %d bytes\n",
bytes_copied);
/* update play counts */
if(bytes_copied + 20 >= real_len) {
db_playcount_increment(NULL,pmp3->id);
}
}
config_set_status(pwsc,session,NULL);
r_close(file_fd);
db_dispose_item(pmp3);
}
}
// free(pqi);
}
void pi_conf_dispose_string(char *str) {
free(str);
}

View File

@ -1,6 +1,7 @@
# $Id: $
#
rspdir = ${pkgdatadir}/plugins
out_daapdir = ${pkgdatadir}/plugins
ssc_ffmpegdir = ${pkgdatadir}/plugins
ssc_scriptdir = ${pkgdatadir}/plugins
@ -18,7 +19,12 @@ ssc_script_LTLIBRARIES=ssc-script.la
ssc_script_la_LDFLAGS=-module -avoid-version
ssc_script_la_SOURCES=ssc-script.c
EXTRA_DIST = compat.h rsp.h xml-rpc.h ssc-ffmpeg.c ssc-script.c
out_daap_LTLIBRARIES=out-daap.la
out_daap_la_LDFLAGS=-module -avoid-version
out_daap_la_SOURCES=out-daap.c out-daap-proto.c out-daap-db.c
EXTRA_DIST = compat.h rsp.h xml-rpc.h ssc-ffmpeg.c ssc-script.c out-daap.h \
out-daap-proto.h out-daap-db.h
AM_CFLAGS = -I..

View File

@ -0,0 +1,7 @@
/*
* $Id: $
*/
#include "out-daap.h"
#include "out-daap-proto.h"
#include "out-daap-db.h"

View File

@ -0,0 +1,8 @@
/*
* $Id: $
*/
#ifndef _OUT_DAAP_DB_H_
#define _OUT_DAAP_DB_H_
#endif /* _OUT_DAAP_DB_H_ */

View File

@ -0,0 +1,894 @@
/*
* $Id: $
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "ff-dbstruct.h"
#include "ff-plugins.h"
#include "out-daap.h"
#include "out-daap-proto.h"
DAAP_ITEMS taglist[] = {
{ 0x05, "miid", "dmap.itemid" },
{ 0x09, "minm", "dmap.itemname" },
{ 0x01, "mikd", "dmap.itemkind" },
{ 0x07, "mper", "dmap.persistentid" },
{ 0x0C, "mcon", "dmap.container" },
{ 0x05, "mcti", "dmap.containeritemid" },
{ 0x05, "mpco", "dmap.parentcontainerid" },
{ 0x05, "mstt", "dmap.status" },
{ 0x09, "msts", "dmap.statusstring" },
{ 0x05, "mimc", "dmap.itemcount" },
{ 0x05, "mctc", "dmap.containercount" },
{ 0x05, "mrco", "dmap.returnedcount" },
{ 0x05, "mtco", "dmap.specifiedtotalcount" },
{ 0x0C, "mlcl", "dmap.listing" },
{ 0x0C, "mlit", "dmap.listingitem" },
{ 0x0C, "mbcl", "dmap.bag" },
{ 0x0C, "mdcl", "dmap.dictionary" },
{ 0x0C, "msrv", "dmap.serverinforesponse" },
{ 0x01, "msau", "dmap.authenticationmethod" },
{ 0x01, "mslr", "dmap.loginrequired" },
{ 0x0B, "mpro", "dmap.protocolversion" },
{ 0x01, "msal", "dmap.supportsautologout" },
{ 0x01, "msup", "dmap.supportsupdate" },
{ 0x01, "mspi", "dmap.supportspersistentids" },
{ 0x01, "msex", "dmap.supportsextensions" },
{ 0x01, "msbr", "dmap.supportsbrowse" },
{ 0x01, "msqy", "dmap.supportsquery" },
{ 0x01, "msix", "dmap.supportsindex" },
{ 0x01, "msrs", "dmap.supportsresolve" },
{ 0x05, "mstm", "dmap.timeoutinterval" },
{ 0x05, "msdc", "dmap.databasescount" },
{ 0x0C, "mlog", "dmap.loginresponse" },
{ 0x05, "mlid", "dmap.sessionid" },
{ 0x0C, "mupd", "dmap.updateresponse" },
{ 0x05, "musr", "dmap.serverrevision" },
{ 0x01, "muty", "dmap.updatetype" },
{ 0x0C, "mudl", "dmap.deletedidlisting" },
{ 0x0C, "mccr", "dmap.contentcodesresponse" },
{ 0x05, "mcnm", "dmap.contentcodesnumber" },
{ 0x09, "mcna", "dmap.contentcodesname" },
{ 0x03, "mcty", "dmap.contentcodestype" },
{ 0x0B, "apro", "daap.protocolversion" },
{ 0x0C, "avdb", "daap.serverdatabases" },
{ 0x0C, "abro", "daap.databasebrowse" },
{ 0x0C, "abal", "daap.browsealbumlisting" },
{ 0x0C, "abar", "daap.browseartistlisting" },
{ 0x0C, "abcp", "daap.browsecomposerlisting" },
{ 0x0C, "abgn", "daap.browsegenrelisting" },
{ 0x0C, "adbs", "daap.databasesongs" },
{ 0x09, "asal", "daap.songalbum" },
{ 0x09, "asar", "daap.songartist" },
{ 0x03, "asbt", "daap.songbeatsperminute" },
{ 0x03, "asbr", "daap.songbitrate" },
{ 0x09, "ascm", "daap.songcomment" },
{ 0x01, "asco", "daap.songcompilation" },
{ 0x09, "ascp", "daap.songcomposer" },
{ 0x0A, "asda", "daap.songdateadded" },
{ 0x0A, "asdm", "daap.songdatemodified" },
{ 0x03, "asdc", "daap.songdisccount" },
{ 0x03, "asdn", "daap.songdiscnumber" },
{ 0x01, "asdb", "daap.songdisabled" },
{ 0x09, "aseq", "daap.songeqpreset" },
{ 0x09, "asfm", "daap.songformat" },
{ 0x09, "asgn", "daap.songgenre" },
{ 0x09, "asdt", "daap.songdescription" },
{ 0x02, "asrv", "daap.songrelativevolume" },
{ 0x05, "assr", "daap.songsamplerate" },
{ 0x05, "assz", "daap.songsize" },
{ 0x05, "asst", "daap.songstarttime" },
{ 0x05, "assp", "daap.songstoptime" },
{ 0x05, "astm", "daap.songtime" },
{ 0x03, "astc", "daap.songtrackcount" },
{ 0x03, "astn", "daap.songtracknumber" },
{ 0x01, "asur", "daap.songuserrating" },
{ 0x03, "asyr", "daap.songyear" },
{ 0x01, "asdk", "daap.songdatakind" },
{ 0x09, "asul", "daap.songdataurl" },
{ 0x0C, "aply", "daap.databaseplaylists" },
{ 0x01, "abpl", "daap.baseplaylist" },
{ 0x0C, "apso", "daap.playlistsongs" },
{ 0x0C, "arsv", "daap.resolve" },
{ 0x0C, "arif", "daap.resolveinfo" },
{ 0x05, "aeNV", "com.apple.itunes.norm-volume" },
{ 0x01, "aeSP", "com.apple.itunes.smart-playlist" },
/* iTunes 4.5+ */
{ 0x01, "msas", "dmap.authenticationschemes" },
{ 0x05, "ascd", "daap.songcodectype" },
{ 0x05, "ascs", "daap.songcodecsubtype" },
{ 0x09, "agrp", "daap.songgrouping" },
{ 0x05, "aeSV", "com.apple.itunes.music-sharing-version" },
{ 0x05, "aePI", "com.apple.itunes.itms-playlistid" },
{ 0x05, "aeCI", "com.apple.iTunes.itms-composerid" },
{ 0x05, "aeGI", "com.apple.iTunes.itms-genreid" },
{ 0x05, "aeAI", "com.apple.iTunes.itms-artistid" },
{ 0x05, "aeSI", "com.apple.iTunes.itms-songid" },
{ 0x05, "aeSF", "com.apple.iTunes.itms-storefrontid" },
/* iTunes 5.0+ */
{ 0x01, "ascr", "daap.songcontentrating" },
{ 0x01, "f" "\x8d" "ch", "dmap.haschildcontainers" },
/* iTunes 6.0.2+ */
{ 0x01, "aeHV", "com.apple.itunes.has-video" },
/* iTunes 6.0.4+ */
{ 0x05, "msas", "dmap.authenticationschemes" },
{ 0x09, "asct", "daap.songcategory" },
{ 0x09, "ascn", "daap.songcontentdescription" },
{ 0x09, "aslc", "daap.songlongcontentdescription" },
{ 0x09, "asky", "daap.songkeywords" },
{ 0x01, "apsm", "daap.playlistshufflemode" },
{ 0x01, "aprm", "daap.playlistrepeatmode" },
{ 0x01, "aePC", "com.apple.itunes.is-podcast" },
{ 0x01, "aePP", "com.apple.itunes.is-podcast-playlist" },
{ 0x01, "aeMK", "com.apple.itunes.mediakind" },
{ 0x09, "aeSN", "com.apple.itunes.series-name" },
{ 0x09, "aeNN", "com.apple.itunes.network-name" },
{ 0x09, "aeEN", "com.apple.itunes.episode-num-str" },
{ 0x05, "aeES", "com.apple.itunes.episode-sort" },
{ 0x05, "aeSU", "com.apple.itunes.season-num" },
/* mt-daapd specific */
{ 0x09, "MSPS", "org.mt-daapd.smart-playlist-spec" },
{ 0x01, "MPTY", "org.mt-daapd.playlist-type" },
{ 0x0C, "MAPR", "org.mt-daapd.addplaylist" },
{ 0x0C, "MAPI", "org.mt-daapd.addplaylistitem" },
{ 0x0C, "MDPR", "org.mt-daapd.delplaylist" },
{ 0x0C, "MDPI", "org.mt-daapd.delplaylistitem" },
{ 0x0C, "MEPR", "org.mt-daapd.editplaylist" },
{ 0x00, NULL, NULL }
};
typedef struct {
const char* tag;
MetaFieldName_t bit;
} METAMAP;
/** map the string names specified in the meta= tag to bit numbers */
static METAMAP db_metamap[] = {
{ "dmap.itemid", metaItemId },
{ "dmap.itemname", metaItemName },
{ "dmap.itemkind", metaItemKind },
{ "dmap.persistentid", metaPersistentId },
{ "dmap.containeritemid", metaContainerItemId },
{ "dmap.parentcontainerid", metaParentContainerId },
/* end generics */
{ "daap.songalbum", metaSongAlbum },
{ "daap.songartist", metaSongArtist },
{ "daap.songbitrate", metaSongBitRate },
{ "daap.songbeatsperminute", metaSongBPM },
{ "daap.songcomment", metaSongComment },
{ "daap.songcompilation", metaSongCompilation },
{ "daap.songcomposer", metaSongComposer },
{ "daap.songdatakind", metaSongDataKind },
{ "daap.songdataurl", metaSongDataURL },
{ "daap.songdateadded", metaSongDateAdded },
{ "daap.songdatemodified", metaSongDateModified },
{ "daap.songdescription", metaSongDescription },
{ "daap.songdisabled", metaSongDisabled },
{ "daap.songdisccount", metaSongDiscCount },
{ "daap.songdiscnumber", metaSongDiscNumber },
{ "daap.songeqpreset", metaSongEqPreset },
{ "daap.songformat", metaSongFormat },
{ "daap.songgenre", metaSongGenre },
{ "daap.songgrouping", metaSongGrouping },
{ "daap.songrelativevolume", metaSongRelativeVolume },
{ "daap.songsamplerate", metaSongSampleRate },
{ "daap.songsize", metaSongSize },
{ "daap.songstarttime", metaSongStartTime },
{ "daap.songstoptime", metaSongStopTime },
{ "daap.songtime", metaSongTime },
{ "daap.songtrackcount", metaSongTrackCount },
{ "daap.songtracknumber", metaSongTrackNumber },
{ "daap.songuserrating", metaSongUserRating },
{ "daap.songyear", metaSongYear },
/* iTunes 4.5+ (forgot exactly when) */
{ "daap.songcodectype", metaSongCodecType },
{ "daap.songcodecsubtype", metaSongCodecSubType },
{ "com.apple.itunes.norm-volume", metaItunesNormVolume },
{ "com.apple.itunes.itms-songid", metaItmsSongId },
{ "com.apple.itunes.itms-artistid", metaItmsArtistId },
{ "com.apple.itunes.itms-playlistid", metaItmsPlaylistId },
{ "com.apple.itunes.itms-composerid", metaItmsComposerId },
{ "com.apple.itunes.itms-genreid", metaItmsGenreId },
{ "com.apple.itunes.itms-storefrontid",metaItmsStorefrontId },
{ "com.apple.itunes.smart-playlist", metaItunesSmartPlaylist },
/* iTunes 5.0+ */
{ "daap.songcontentrating", metaSongContentRating },
{ "dmap.haschildcontainers", metaHasChildContainers },
/* iTunes 6.0.2+ */
{ "com.apple.itunes.has-video", metaItunesHasVideo },
/* mt-daapd specific */
{ "org.mt-daapd.smart-playlist-spec", metaMPlaylistSpec },
{ "org.mt-daapd.playlist-type", metaMPlaylistType },
{ 0, 0 }
};
int out_daap_session=0;
#define DMAPLEN(a) (((a) && strlen(a)) ? (8+(int)strlen((a))) : \
((pinfo->empty_strings) ? 8 : 0))
#define EMIT(a) (pinfo->empty_strings ? 1 : ((a) && strlen((a))) ? 1 : 0)
/* Forwards */
int daap_get_size(PRIVINFO *pinfo, char **valarray);
int daap_build_dmap(PRIVINFO *pinfo, char **valarray, unsigned char *presult, int len);
/**
* encode a string meta request into a MetaField_t
*
* \param meta meta string variable from GET request
*/
MetaField_t daap_encode_meta(char *meta) {
MetaField_t bits = 0;
char *start;
char *end;
METAMAP *m;
for(start = meta ; *start ; start = end) {
int len;
if(0 == (end = strchr(start, ',')))
end = start + strlen(start);
len = (int)(end - start);
if(*end != 0)
end++;
for(m = db_metamap ; m->tag ; ++m)
if(!strncmp(m->tag, start, len))
break;
if(m->tag)
bits |= (((MetaField_t) 1) << m->bit);
else
_ppi->log(E_WARN,"Unknown meta code: %.*s\n", len, start);
}
_ppi->log(E_DBG, "meta codes: %llu\n", bits);
return bits;
}
/**
* see if a specific metafield was requested
*
* \param meta encoded list of requested metafields
* \param fieldNo field to test for
*/
int daap_wantsmeta(MetaField_t meta, MetaFieldName_t fieldNo) {
return 0 != (meta & (((MetaField_t) 1) << fieldNo));
}
/**
* add a character type to a dmap block (type 0x01)
*
* \param where where to serialize the dmap info
* \tag what four byte tag
* \value what character value
*/
int dmap_add_char(unsigned char *where, char *tag, char value) {
/* tag */
memcpy(where,tag,4);
/* len */
where[4]=where[5]=where[6]=0;
where[7]=1;
/* value */
where[8] = value;
return 9;
}
/**
* add a short type to a dmap block (type 0x03)
*
* \param where where to serialize the dmap info
* \tag what four byte tag
* \value what character value
*/
int dmap_add_short(unsigned char *where, char *tag, short value) {
/* tag */
memcpy(where,tag,4);
/* len */
where[4]=where[5]=where[6]=0;
where[7]=2;
/* value */
where[8] = (value >> 8) & 0xFF;
where[9] = value & 0xFF;
return 10;
}
/**
* add an int type to a dmap block (type 0x05)
*
* \param where where to serialize the dmap info
* \tag what four byte tag
* \value what character value
*/
int dmap_add_int(unsigned char *where, char *tag, int value) {
/* tag */
memcpy(where,tag,4);
/* len */
where[4]=where[5]=where[6]=0;
where[7]=4;
/* value */
where[8] = (value >> 24) & 0xFF;
where[9] = (value >> 16) & 0xFF;
where[10] = (value >> 8) & 0xFF;
where[11] = value & 0xFF;
return 12;
}
/**
* add a string type to a dmap block (type 0x09)
*
* \param where where to serialize the dmap info
* \tag what four byte tag
* \value what character value
*/
int dmap_add_string(unsigned char *where, char *tag, char *value) {
int len=0;
if(value)
len = (int)strlen(value);
/* tag */
memcpy(where,tag,4);
/* length */
where[4]=(len >> 24) & 0xFF;
where[5]=(len >> 16) & 0xFF;
where[6]=(len >> 8) & 0xFF;
where[7]=len & 0xFF;
if(len)
strncpy((char*)where+8,value,len);
return 8 + len;
}
/**
* 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 dmap_add_literal(unsigned 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)
*
* \param where where to serialize the dmap info
* \tag what four byte tag
* \value what character value
*/
int dmap_add_container(unsigned char *where, char *tag, int size) {
int len=size;
/* tag */
memcpy(where,tag,4);
/* length */
where[4]=(len >> 24) & 0xFF;
where[5]=(len >> 16) & 0xFF;
where[6]=(len >> 8) & 0xFF;
where[7]=len & 0xFF;
return 8;
}
/**
* Get the next available session id.
* This is vulnerable to races, but we don't track sessions,
* so there really isn't a point anyway.
*
* @returns duh... the next available session id
*/
int daap_get_next_session(void) {
int session;
session=++out_daap_session;
return session;
}
/**
* find the size of the response by walking through the query and
* sizing it
*
* @returns DB_E_SUCCESS on success, error code otherwise
*/
int daap_enum_size(char **pe, PRIVINFO *pinfo, int *count, int *total_size) {
int err;
int record_size;
char **row;
_ppi->log(E_DBG,"Enumerating size\n");
*count=0;
*total_size = 0;
while((!(err=_ppi->db_enum_fetch_row(pe,&row,&pinfo->dq))) && (row)) {
if((record_size = daap_get_size(pinfo,row))) {
*total_size += record_size;
*count = *count + 1;
}
}
if(err) {
_ppi->db_enum_end(NULL);
_ppi->db_enum_dispose(NULL,&pinfo->dq);
return err;
}
err=_ppi->db_enum_restart(pe, &pinfo->dq);
_ppi->log(E_DBG,"Got size: %d\n",*total_size);
return err;
}
/**
* fetch the next record from the enum
*/
int daap_enum_fetch(char **pe, PRIVINFO *pinfo, int *size, unsigned char **pdmap) {
int err;
int result_size=0;
unsigned char *presult;
char **row;
err=_ppi->db_enum_fetch_row(pe, &row, &pinfo->dq);
if(err) {
_ppi->db_enum_end(NULL);
_ppi->db_enum_dispose(NULL,&pinfo->dq);
return err;
}
if(row) {
result_size = daap_get_size(pinfo,row);
if(result_size) {
presult = (unsigned char*)malloc(result_size);
if(!presult) {
_ppi->log(E_FATAL,"Malloc error\n");
}
daap_build_dmap(pinfo,row,presult,result_size);
*pdmap=presult;
*size = result_size;
}
} else {
*size = 0;
}
return 0;
}
/**
* get the size of the generated dmap, given a specific meta
*/
int daap_get_size(PRIVINFO *pinfo, char **valarray) {
int size;
int transcode;
switch(pinfo->dq.query_type) {
case QUERY_TYPE_DISTINCT: /* simple 'mlit' entry */
return valarray[0] ? (8 + (int) strlen(valarray[0])) : 0;
case QUERY_TYPE_PLAYLISTS:
size = 8; /* mlit */
size += 12; /* mimc - you get it whether you want it or not */
if(daap_wantsmeta(pinfo->meta, metaItemId))
size += 12; /* miid */
if(daap_wantsmeta(pinfo->meta, metaItunesSmartPlaylist)) {
if(valarray[PL_TYPE] && (atoi(valarray[PL_TYPE])==1))
size += 9; /* aeSP */
}
if(daap_wantsmeta(pinfo->meta, metaItemName))
size += (8 + (int) strlen(valarray[PL_TITLE])); /* minm */
if(valarray[PL_TYPE] && (atoi(valarray[PL_TYPE])==1) &&
daap_wantsmeta(pinfo->meta, metaMPlaylistSpec))
size += (8 + (int) strlen(valarray[PL_QUERY])); /* MSPS */
if(daap_wantsmeta(pinfo->meta, metaMPlaylistType))
size += 9; /* MPTY */
return size;
break;
case QUERY_TYPE_ITEMS:
/* see if this is going to be transcoded */
transcode = _ppi->should_transcode(pinfo->pwsc,valarray[SG_CODECTYPE]);
/* Items that get changed by transcode:
*
* type: item 8: changes to 'wav'
* description: item 29: changes to 'wav audio file'
* bitrate: item 15: guestimated, based on item 15, samplerate
*
* probably file size should change as well, but currently doesn't
*/
size = 8; /* mlit */
if(daap_wantsmeta(pinfo->meta, metaItemKind))
/* mikd */
size += 9;
if(daap_wantsmeta(pinfo->meta, metaSongDataKind))
/* asdk */
size += 9;
if(daap_wantsmeta(pinfo->meta, metaSongDataURL))
/* asul */
size += DMAPLEN(valarray[SG_URL]);
if(daap_wantsmeta(pinfo->meta, metaSongAlbum))
/* asal */
size += DMAPLEN(valarray[SG_ALBUM]);
if(daap_wantsmeta(pinfo->meta, metaSongArtist))
/* asar */
size += DMAPLEN(valarray[SG_ARTIST]);
if(valarray[SG_BPM] && atoi(valarray[SG_BPM]) &&
daap_wantsmeta(pinfo->meta, metaSongBPM))
/* asbt */
size += 10;
if(daap_wantsmeta(pinfo->meta, metaSongBitRate)) {
/* asbr */
if(transcode) {
if(valarray[SG_SAMPLERATE] && atoi(valarray[SG_SAMPLERATE])) {
size += 10;
}
} else {
if(valarray[SG_BITRATE] && atoi(valarray[SG_BITRATE])) {
size += 10;
}
}
}
if(daap_wantsmeta(pinfo->meta, metaSongComment))
/* ascm */
size += DMAPLEN(valarray[SG_COMMENT]);
if(valarray[SG_COMPILATION] && atoi(valarray[SG_COMPILATION]) &&
daap_wantsmeta(pinfo->meta,metaSongCompilation))
/* asco */
size += 9;
if(daap_wantsmeta(pinfo->meta, metaSongComposer))
/* ascp */
size += DMAPLEN(valarray[SG_COMPOSER]);
if(daap_wantsmeta(pinfo->meta, metaSongGrouping))
/* agrp */
size += DMAPLEN(valarray[SG_GROUPING]);
if(valarray[SG_TIME_ADDED] && atoi(valarray[SG_TIME_ADDED]) &&
daap_wantsmeta(pinfo->meta, metaSongDateAdded))
/* asda */
size += 12;
if(valarray[SG_TIME_MODIFIED] && atoi(valarray[SG_TIME_MODIFIED]) &&
daap_wantsmeta(pinfo->meta,metaSongDateModified))
/* asdm */
size += 12;
if(valarray[SG_TOTAL_DISCS] && atoi(valarray[SG_TOTAL_DISCS]) &&
daap_wantsmeta(pinfo->meta, metaSongDiscCount))
/* asdc */
size += 10;
if(valarray[SG_DISC] && atoi(valarray[SG_DISC]) &&
daap_wantsmeta(pinfo->meta, metaSongDiscNumber))
/* asdn */
size += 10;
if(daap_wantsmeta(pinfo->meta, metaSongGenre))
/* asgn */
size += DMAPLEN(valarray[SG_GENRE]);
if(daap_wantsmeta(pinfo->meta,metaItemId))
/* miid */
size += 12;
if(daap_wantsmeta(pinfo->meta,metaSongFormat)) {
/* asfm */
if(transcode) {
size += 11; /* 'wav' */
} else {
size += DMAPLEN(valarray[SG_TYPE]);
}
}
if(daap_wantsmeta(pinfo->meta,metaSongDescription)) {
/* asdt */
if(transcode) {
size += 22; /* 'wav audio file' */
} else {
size += DMAPLEN(valarray[SG_DESCRIPTION]);
}
}
if(daap_wantsmeta(pinfo->meta,metaItemName))
/* minm */
size += DMAPLEN(valarray[SG_TITLE]);
if(valarray[SG_DISABLED] && atoi(valarray[SG_DISABLED]) &&
daap_wantsmeta(pinfo->meta,metaSongDisabled))
/* asdb */
size += 9;
if(valarray[SG_SAMPLERATE] && atoi(valarray[SG_SAMPLERATE]) &&
daap_wantsmeta(pinfo->meta,metaSongSampleRate))
/* assr */
size += 12;
if(valarray[SG_FILE_SIZE] && atoi(valarray[SG_FILE_SIZE]) &&
daap_wantsmeta(pinfo->meta,metaSongSize))
/* assz */
size += 12;
/* In the old daap code, we always returned 0 for asst and assp
* (song start time, song stop time). I don't know if this
* is required, so I'm going to disabled it
*/
if(valarray[SG_SONG_LENGTH] && atoi(valarray[SG_SONG_LENGTH]) &&
daap_wantsmeta(pinfo->meta, metaSongTime))
/* astm */
size += 12;
if(valarray[SG_TOTAL_TRACKS] && atoi(valarray[SG_TOTAL_TRACKS]) &&
daap_wantsmeta(pinfo->meta, metaSongTrackCount))
/* astc */
size += 10;
if(valarray[SG_TRACK] && atoi(valarray[SG_TRACK]) &&
daap_wantsmeta(pinfo->meta, metaSongTrackNumber))
/* astn */
size += 10;
if(valarray[SG_RATING] && atoi(valarray[SG_RATING]) &&
daap_wantsmeta(pinfo->meta, metaSongUserRating))
/* asur */
size += 9;
if(valarray[SG_YEAR] && atoi(valarray[SG_YEAR]) &&
daap_wantsmeta(pinfo->meta, metaSongYear))
/* asyr */
size += 10;
if(daap_wantsmeta(pinfo->meta, metaContainerItemId))
/* mcti */
size += 12;
/* FIXME: This is not right - doesn't have to be 4 */
if((valarray[SG_CODECTYPE]) && (strlen(valarray[SG_CODECTYPE])==4) &&
daap_wantsmeta(pinfo->meta,metaSongCodecType))
/* ascd */
size += 12;
if(daap_wantsmeta(pinfo->meta,metaSongContentRating))
/* ascr */
size += 9;
if(daap_wantsmeta(pinfo->meta,metaItunesHasVideo))
/* aeHV */
size += 9;
return size;
break;
default:
_ppi->log(E_LOG,"Unknown query type: %d\n",(int)pinfo->dq.query_type);
return 0;
}
return 0;
}
int daap_build_dmap(PRIVINFO *pinfo, char **valarray, unsigned char *presult, int len) {
unsigned char *current = presult;
int transcode;
int samplerate=0;
switch(pinfo->dq.query_type) {
case QUERY_TYPE_DISTINCT:
return dmap_add_string(current,"mlit",valarray[0]);
case QUERY_TYPE_PLAYLISTS:
/* do I want to include the mlit? */
current += dmap_add_container(current,"mlit",len - 8);
if(daap_wantsmeta(pinfo->meta,metaItemId))
current += dmap_add_int(current,"miid",atoi(valarray[PL_ID]));
current += dmap_add_int(current,"mimc",atoi(valarray[PL_ITEMS]));
if(daap_wantsmeta(pinfo->meta,metaItunesSmartPlaylist)) {
if(valarray[PL_TYPE] && (atoi(valarray[PL_TYPE]) == 1))
current += dmap_add_char(current,"aeSP",1);
}
if(daap_wantsmeta(pinfo->meta,metaItemName))
current += dmap_add_string(current,"minm",valarray[PL_TITLE]);
if((valarray[PL_TYPE]) && (atoi(valarray[PL_TYPE])==1) &&
daap_wantsmeta(pinfo->meta, metaMPlaylistSpec))
current += dmap_add_string(current,"MSPS",valarray[PL_QUERY]);
if(daap_wantsmeta(pinfo->meta, metaMPlaylistType))
current += dmap_add_char(current,"MPTY",atoi(valarray[PL_TYPE]));
break;
case QUERY_TYPE_ITEMS:
/* see if this is going to be transcoded */
transcode = _ppi->should_transcode(pinfo->pwsc,valarray[SG_CODECTYPE]);
/* Items that get changed by transcode:
*
* type: item 8: changes to 'wav'
* description: item 29: changes to 'wav audio file'
* bitrate: item 15: guestimated, but doesn't change file size
*
* probably file size should change as well, but currently doesn't
*/
current += dmap_add_container(current,"mlit",len-8);
if(daap_wantsmeta(pinfo->meta, metaItemKind))
current += dmap_add_char(current,"mikd",
(char)atoi(valarray[SG_ITEM_KIND]));
if(daap_wantsmeta(pinfo->meta, metaSongDataKind))
current += dmap_add_char(current,"asdk",
(char)atoi(valarray[SG_DATA_KIND]));
if(EMIT(valarray[13]) && daap_wantsmeta(pinfo->meta, metaSongDataURL))
current += dmap_add_string(current,"asul",valarray[SG_URL]);
if(EMIT(valarray[5]) && daap_wantsmeta(pinfo->meta, metaSongAlbum))
current += dmap_add_string(current,"asal",valarray[SG_ALBUM]);
if(EMIT(valarray[4]) && daap_wantsmeta(pinfo->meta, metaSongArtist))
current += dmap_add_string(current,"asar",valarray[SG_ARTIST]);
if(valarray[23] && atoi(valarray[23]) && daap_wantsmeta(pinfo->meta, metaSongBPM))
current += dmap_add_short(current,"asbt",
(short)atoi(valarray[SG_BPM]));
if(valarray[SG_BITRATE] && atoi(valarray[SG_BITRATE]) &&
daap_wantsmeta(pinfo->meta, metaSongBitRate)) {
if(transcode) {
if(valarray[SG_SAMPLERATE])
samplerate=atoi(valarray[SG_SAMPLERATE]);
if(samplerate) {
current += dmap_add_short(current,"asbr",
(short)(samplerate / 250 * 8));
}
} else {
current += dmap_add_short(current,"asbr",
(short)atoi(valarray[SG_BITRATE]));
}
}
if(EMIT(valarray[SG_COMMENT]) &&
daap_wantsmeta(pinfo->meta, metaSongComment))
current += dmap_add_string(current,"ascm",valarray[SG_COMMENT]);
if(valarray[SG_COMPILATION] && atoi(valarray[SG_COMPILATION]) &&
daap_wantsmeta(pinfo->meta,metaSongCompilation))
current += dmap_add_char(current,"asco",
(char)atoi(valarray[SG_COMPILATION]));
if(EMIT(valarray[SG_COMPOSER]) &&
daap_wantsmeta(pinfo->meta, metaSongComposer))
current += dmap_add_string(current,"ascp",
valarray[SG_COMPOSER]);
if(EMIT(valarray[SG_GROUPING]) &&
daap_wantsmeta(pinfo->meta, metaSongGrouping))
current += dmap_add_string(current,"agrp",
valarray[SG_GROUPING]);
if(valarray[SG_TIME_ADDED] && atoi(valarray[SG_TIME_ADDED]) &&
daap_wantsmeta(pinfo->meta, metaSongDateAdded))
current += dmap_add_int(current,"asda",
(int)atoi(valarray[SG_TIME_ADDED]));
if(valarray[SG_TIME_MODIFIED] && atoi(valarray[SG_TIME_MODIFIED]) &&
daap_wantsmeta(pinfo->meta,metaSongDateModified))
current += dmap_add_int(current,"asdm",
(int)atoi(valarray[SG_TIME_MODIFIED]));
if(valarray[SG_TOTAL_DISCS] && atoi(valarray[SG_TOTAL_DISCS]) &&
daap_wantsmeta(pinfo->meta, metaSongDiscCount))
current += dmap_add_short(current,"asdc",
(short)atoi(valarray[SG_TOTAL_DISCS]));
if(valarray[SG_DISC] && atoi(valarray[SG_DISC]) &&
daap_wantsmeta(pinfo->meta, metaSongDiscNumber))
current += dmap_add_short(current,"asdn",
(short)atoi(valarray[SG_DISC]));
if(EMIT(valarray[SG_GENRE]) &&
daap_wantsmeta(pinfo->meta, metaSongGenre))
current += dmap_add_string(current,"asgn",valarray[SG_GENRE]);
if(daap_wantsmeta(pinfo->meta,metaItemId))
current += dmap_add_int(current,"miid",
(int)atoi(valarray[SG_ID]));
if(EMIT(valarray[SG_TYPE]) &&
daap_wantsmeta(pinfo->meta,metaSongFormat)) {
if(transcode) {
current += dmap_add_string(current,"asfm","wav");
} else {
current += dmap_add_string(current,"asfm",
valarray[SG_TYPE]);
}
}
if(EMIT(valarray[SG_DESCRIPTION]) &&
daap_wantsmeta(pinfo->meta,metaSongDescription)) {
if(transcode) {
current += dmap_add_string(current,"asdt","wav audio file");
} else {
current += dmap_add_string(current,"asdt",
valarray[SG_DESCRIPTION]);
}
}
if(EMIT(valarray[SG_TITLE]) &&
daap_wantsmeta(pinfo->meta,metaItemName))
current += dmap_add_string(current,"minm",valarray[SG_TITLE]);
if(valarray[SG_DISABLED] && atoi(valarray[SG_DISABLED]) &&
daap_wantsmeta(pinfo->meta,metaSongDisabled))
current += dmap_add_char(current,"asdb",
(char)atoi(valarray[SG_DISABLED]));
if(valarray[SG_SAMPLERATE] && atoi(valarray[SG_SAMPLERATE]) &&
daap_wantsmeta(pinfo->meta,metaSongSampleRate))
current += dmap_add_int(current,"assr",
atoi(valarray[SG_SAMPLERATE]));
if(valarray[SG_FILE_SIZE] && atoi(valarray[SG_FILE_SIZE]) &&
daap_wantsmeta(pinfo->meta,metaSongSize))
current += dmap_add_int(current,"assz",
atoi(valarray[SG_FILE_SIZE]));
if(valarray[SG_SONG_LENGTH] && atoi(valarray[SG_SONG_LENGTH]) &&
daap_wantsmeta(pinfo->meta, metaSongTime))
current += dmap_add_int(current,"astm",
atoi(valarray[SG_SONG_LENGTH]));
if(valarray[SG_TOTAL_TRACKS] && atoi(valarray[SG_TOTAL_TRACKS]) &&
daap_wantsmeta(pinfo->meta, metaSongTrackCount))
current += dmap_add_short(current,"astc",
(short)atoi(valarray[SG_TOTAL_TRACKS]));
if(valarray[SG_TRACK] && atoi(valarray[SG_TRACK]) &&
daap_wantsmeta(pinfo->meta, metaSongTrackNumber))
current += dmap_add_short(current,"astn",
(short)atoi(valarray[SG_TRACK]));
if(valarray[SG_RATING] && atoi(valarray[SG_RATING]) &&
daap_wantsmeta(pinfo->meta, metaSongUserRating))
current += dmap_add_char(current,"asur",
(char)atoi(valarray[SG_RATING]));
if(valarray[SG_YEAR] && atoi(valarray[SG_YEAR]) &&
daap_wantsmeta(pinfo->meta, metaSongYear))
current += dmap_add_short(current,"asyr",
(short)atoi(valarray[SG_YEAR]));
if((valarray[SG_CODECTYPE]) && (strlen(valarray[SG_CODECTYPE]) == 4) &&
daap_wantsmeta(pinfo->meta,metaSongCodecType))
current += dmap_add_literal(current,"ascd",
valarray[SG_CODECTYPE],4);
if(daap_wantsmeta(pinfo->meta, metaContainerItemId))
current += dmap_add_int(current,"mcti",atoi(valarray[SG_ID]));
if(daap_wantsmeta(pinfo->meta, metaItunesHasVideo))
current += dmap_add_char(current,"aeHV",
atoi(valarray[SG_HAS_VIDEO]));
if(daap_wantsmeta(pinfo->meta, metaSongContentRating))
current += dmap_add_char(current,"ascr",
atoi(valarray[SG_CONTENTRATING]));
return 0;
break;
default:
_ppi->log(E_LOG,"Unknown query type: %d\n",(int)pinfo->dq.query_type);
return 0;
}
return 0;
}

View File

@ -0,0 +1,36 @@
/*
* $Id: $
*/
#ifndef _OUT_DAAP_PROTO_H_
#define _OUT_DAAP_PROTO_H_
#include "out-daap.h"
typedef struct tag_daap_items {
int type;
char *tag;
char *description;
} DAAP_ITEMS;
extern DAAP_ITEMS taglist[];
/* metatag parsing */
extern MetaField_t daap_encode_meta(char *meta);
extern int daap_wantsmeta(MetaField_t meta, MetaFieldName_t fieldNo);
/* dmap helper functions */
extern int dmap_add_char(unsigned char *where, char *tag, char value);
extern int dmap_add_short(unsigned char *where, char *tag, short value);
extern int dmap_add_int(unsigned char *where, char *tag, int value);
extern int dmap_add_string(unsigned char *where, char *tag, char *value);
extern int dmap_add_literal(unsigned char *where, char *tag, char *value, int size);
extern int dmap_add_container(unsigned char *where, char *tag, int size);
extern int daap_get_next_session(void);
extern int daap_enum_size(char **pe, PRIVINFO *pinfo, int *count, int *total_size);
extern int daap_enum_fetch(char **pe, PRIVINFO *pinfo, int *size, unsigned char **pdmap);
#endif /* _OUT_DAAP_PROTO_H_ */

1485
src/plugins/out-daap.c Normal file

File diff suppressed because it is too large Load Diff

98
src/plugins/out-daap.h Normal file
View File

@ -0,0 +1,98 @@
/*
* $Id$
*/
#ifndef _OUT_DAAP_H_
#define _OUT_DAAP_H_
#ifndef TRUE
# define TRUE 1
# define FALSE 0
#endif
#include "ff-plugins.h"
typedef enum {
// generic meta data
metaItemId,
metaItemName,
metaItemKind,
metaPersistentId,
metaContainerItemId,
metaParentContainerId,
firstTypeSpecificMetaId,
// song meta data
metaSongAlbum = firstTypeSpecificMetaId,
metaSongArtist,
metaSongBPM,
metaSongBitRate,
metaSongComment,
metaSongCompilation,
metaSongComposer,
metaSongDataKind,
metaSongDataURL,
metaSongDateAdded,
metaSongDateModified,
metaSongDescription,
metaSongDisabled,
metaSongDiscCount,
metaSongDiscNumber,
metaSongEqPreset,
metaSongFormat,
metaSongGenre,
metaSongGrouping,
metaSongRelativeVolume,
metaSongSampleRate,
metaSongSize,
metaSongStartTime,
metaSongStopTime,
metaSongTime,
metaSongTrackCount,
metaSongTrackNumber,
metaSongUserRating,
metaSongYear,
/* iTunes 4.5 + */
metaSongCodecType,
metaSongCodecSubType,
metaItunesNormVolume,
metaItmsSongId,
metaItmsArtistId,
metaItmsPlaylistId,
metaItmsComposerId,
metaItmsGenreId,
metaItmsStorefrontId,
metaItunesSmartPlaylist,
/* iTunes 5.0 + */
metaSongContentRating,
metaHasChildContainers,
/* iTunes 6.0.2+ */
metaItunesHasVideo,
/* mt-daapd specific */
metaMPlaylistSpec,
metaMPlaylistType
} MetaFieldName_t;
typedef unsigned long long MetaField_t;
typedef struct tag_ws_conninfo WS_CONNINFO;
typedef struct tag_daap_privinfo {
DB_QUERY dq;
int uri_count;
MetaField_t meta;
int empty_strings;
struct tag_output_info *output_info;
int session_id;
char *uri_sections[10];
WS_CONNINFO *pwsc;
} PRIVINFO;
extern PLUGIN_INPUT_FN *_ppi;
#endif /* _OUT_DAAP_H_ */

View File

@ -24,6 +24,7 @@ typedef struct tag_rsp_privinfo {
/* Forwards */
PLUGIN_INFO *plugin_info(PLUGIN_INPUT_FN *);
void plugin_handler(WS_CONNINFO *pwsc);
int plugin_can_handle(WS_CONNINFO *pwsc);
int plugin_auth(WS_CONNINFO *pwsc, char *username, char *password);
void rsp_info(WS_CONNINFO *pwsc, PRIVINFO *ppi);
void rsp_db(WS_CONNINFO *pwsc, PRIVINFO *ppi);
@ -33,19 +34,17 @@ void rsp_stream(WS_CONNINFO *pwsc, PRIVINFO *ppi);
void rsp_error(WS_CONNINFO *pwsc, PRIVINFO *ppi, int eno, char *estr);
/* Globals */
PLUGIN_OUTPUT_FN _pofn = { plugin_handler, plugin_auth };
PLUGIN_OUTPUT_FN _pofn = { plugin_can_handle, plugin_handler, plugin_auth };
PLUGIN_REND_INFO _pri[] = {
{ "_rsp._tcp", NULL },
{ NULL, NULL }
};
PLUGIN_INPUT_FN *_ppi;
PLUGIN_INFO _pi = {
PLUGIN_VERSION, /* version */
PLUGIN_OUTPUT, /* type */
"rsp/" VERSION, /* server */
"/rsp/.*", /* url */
&_pofn, /* output fns */
NULL, /* event fns */
NULL, /* transcode fns */
@ -100,46 +99,46 @@ FIELDSPEC rsp_playlist_fields[] = {
FIELDSPEC rsp_fields[] = {
{ "id" , 15, T_INT },
{ "path" , 8, T_STRING },
{ "fname" , 8, T_STRING },
{ "path" , 8, T_STRING },
{ "fname" , 8, T_STRING },
{ "title" , 15, T_STRING },
{ "artist" , 11, T_STRING },
{ "album" , 11, T_STRING },
{ "genre" , 9, T_STRING },
{ "comment" , 9, T_STRING },
{ "genre" , 9, T_STRING },
{ "comment" , 9, T_STRING },
{ "type" , 15, T_STRING },
{ "composer" , 9, T_STRING },
{ "orchestra" , 9, T_STRING },
{ "conductor" , 9, T_STRING },
{ "grouping" , 0, T_STRING },
{ "url" , 9, T_STRING },
{ "bitrate" , 9, T_INT },
{ "samplerate" , 9, T_INT },
{ "song_length" , 9, T_INT },
{ "file_size" , 9, T_INT },
{ "year" , 9, T_INT },
{ "composer" , 9, T_STRING },
{ "orchestra" , 9, T_STRING },
{ "conductor" , 9, T_STRING },
{ "grouping" , 0, T_STRING },
{ "url" , 9, T_STRING },
{ "bitrate" , 9, T_INT },
{ "samplerate" , 9, T_INT },
{ "song_length" , 9, T_INT },
{ "file_size" , 9, T_INT },
{ "year" , 9, T_INT },
{ "track" , 11, T_INT },
{ "total_tracks" , 9, T_INT },
{ "total_tracks" , 9, T_INT },
{ "disc" , 11, T_INT },
{ "total_discs" , 9, T_INT },
{ "bpm" , 9, T_INT },
{ "compilation" , 9, T_INT },
{ "rating" , 9, T_INT },
{ "play_count" , 9, T_INT },
{ "data_kind" , 8, T_INT },
{ "item_kind" , 8, T_INT },
{ "description" , 9, T_STRING },
{ "time_added" , 9, T_DATE },
{ "time_modified", 9, T_DATE },
{ "time_played" , 9, T_DATE },
{ "db_timestamp" , 8, T_DATE },
{ "total_discs" , 9, T_INT },
{ "bpm" , 9, T_INT },
{ "compilation" , 9, T_INT },
{ "rating" , 9, T_INT },
{ "play_count" , 9, T_INT },
{ "data_kind" , 8, T_INT },
{ "item_kind" , 8, T_INT },
{ "description" , 9, T_STRING },
{ "time_added" , 9, T_DATE },
{ "time_modified", 9, T_DATE },
{ "time_played" , 9, T_DATE },
{ "db_timestamp" , 8, T_DATE },
{ "disabled" , 15, T_INT },
{ "sample_count" , 8, T_INT },
{ "force_update" , 8, T_INT },
{ "sample_count" , 8, T_INT },
{ "force_update" , 8, T_INT },
{ "codectype" , 15, T_INT },
{ "idx" , 8, T_INT },
{ "has_video" , 8, T_INT },
{ "contentrating", 8, T_INT },
{ "idx" , 8, T_INT },
{ "has_video" , 8, T_INT },
{ "contentrating", 8, T_INT },
{ NULL , 0 }
};
@ -151,6 +150,15 @@ PLUGIN_INFO *plugin_info(PLUGIN_INPUT_FN *ppi) {
return &_pi;
}
/**
* see if the plugin should handle this request
*/
int plugin_can_handle(WS_CONNINFO *pwsc) {
_ppi->log(E_DBG,"Checking url %s\n",_ppi->ws_uri(pwsc));
if(strncasecmp(_ppi->ws_uri(pwsc),"/rsp/",5) == 0)
return TRUE;
return FALSE;
}
/**
* check for auth. Kind of a ham-handed implementation, but
@ -249,13 +257,13 @@ void plugin_handler(WS_CONNINFO *pwsc) {
if(found) {
rsp_uri_map[index].dispatch(pwsc, ppi);
_ppi->ws_close(pwsc);
_ppi->ws_will_close(pwsc);
free(ppi);
return;
}
rsp_error(pwsc, ppi, 1, "Bad path");
_ppi->ws_close(pwsc);
_ppi->ws_will_close(pwsc);
free(ppi);
return;
}
@ -559,6 +567,6 @@ void rsp_error(WS_CONNINFO *pwsc, PRIVINFO *ppi, int eno, char *estr) {
xml_pop(pxml); /* status */
xml_pop(pxml); /* response */
xml_deinit(pxml);
_ppi->ws_close(pwsc);
_ppi->ws_will_close(pwsc);
}

View File

@ -18,20 +18,20 @@
#include <avformat.h>
#include "ff-plugins.h"
#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ > 4)
# define _PACKED __attribute((packed))
#else
# define _PACKED
#endif
typedef struct tag_scan_id3header {
unsigned char id[3];
unsigned char version[2];
unsigned char flags;
unsigned char size[4];
} _PACKED SCAN_ID3HEADER;
#pragma pack()
#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ > 4)
# define _PACKED __attribute((packed))
#else
# define _PACKED
#endif
typedef struct tag_scan_id3header {
unsigned char id[3];
unsigned char version[2];
unsigned char flags;
unsigned char size[4];
} _PACKED SCAN_ID3HEADER;
#pragma pack()
#ifndef TRUE
# define TRUE 1
@ -122,7 +122,6 @@ PLUGIN_INFO _pi = {
PLUGIN_VERSION, /* version */
PLUGIN_TRANSCODE, /* type */
"ssc-ffmpeg/" VERSION, /* server */
NULL, /* url */
NULL, /* output fns */
NULL, /* event fns */
&_ptfn, /* fns */
@ -168,12 +167,12 @@ int ssc_ffmpeg_open(void *vp, char *file, char *codec, int duration) {
int i;
enum CodecID id=CODEC_ID_FLAC;
SSCHANDLE *handle = (SSCHANDLE*)vp;
#ifdef WIN32
WCHAR utf16_path[_MAX_PATH+1];
WCHAR utf16_mode[3];
#endif
SCAN_ID3HEADER id3;
unsigned int size = 0;
#ifdef WIN32
WCHAR utf16_path[_MAX_PATH+1];
WCHAR utf16_mode[3];
#endif
SCAN_ID3HEADER id3;
unsigned int size = 0;
if(!handle)
return FALSE;
@ -207,8 +206,8 @@ int ssc_ffmpeg_open(void *vp, char *file, char *codec, int duration) {
/* this is a mess. the fopen should really be pushed back
* up to the server, so it can handle streams or something.
*/
MultiByteToWideChar(CP_UTF8,0,file,-1,utf16_path,sizeof(utf16_path)/sizeof(utf16_path[0]));
MultiByteToWideChar(CP_ACP,0,"rb",-1,utf16_mode,sizeof(utf16_mode)/sizeof(utf16_mode[0]));
MultiByteToWideChar(CP_UTF8,0,file,-1,utf16_path,sizeof(utf16_path)/sizeof(utf16_path[0]));
MultiByteToWideChar(CP_ACP,0,"rb",-1,utf16_mode,sizeof(utf16_mode)/sizeof(utf16_mode[0]));
handle->fin = _wfopen(utf16_path, utf16_mode);
#else
handle->fin = fopen(file,"rb");
@ -220,28 +219,28 @@ int ssc_ffmpeg_open(void *vp, char *file, char *codec, int duration) {
}
/* check to see if there is an id3 tag there... if so, skip it. */
if(fread((unsigned char *)&id3,1,sizeof(id3),handle->fin) != sizeof(id3)) {
if(ferror(handle->fin)) {
_ppi->log(E_LOG,"Error reading file: %s\n",file);
} else {
_ppi->log(E_LOG,"Short file: %s\n",file);
}
handle->errnum = SSC_FFMPEG_E_FILEOPEN;
fclose(handle->fin);
return FALSE;
}
if(fread((unsigned char *)&id3,1,sizeof(id3),handle->fin) != sizeof(id3)) {
if(ferror(handle->fin)) {
_ppi->log(E_LOG,"Error reading file: %s\n",file);
} else {
_ppi->log(E_LOG,"Short file: %s\n",file);
}
handle->errnum = SSC_FFMPEG_E_FILEOPEN;
fclose(handle->fin);
return FALSE;
if(strncmp(id3.id,"ID3",3)==0) {
/* found an ID3 header... */
_ppi->log(E_DBG,"Found ID3 header\n");
size = (id3.size[0] << 21 | id3.size[1] << 14 |
id3.size[2] << 7 | id3.size[3]);
fseek(handle->fin,size + sizeof(SCAN_ID3HEADER),SEEK_SET);
_ppi->log(E_DBG,"Header length: %d\n",size);
} else {
fseek(handle->fin,0,SEEK_SET);
}
}
if(strncmp(id3.id,"ID3",3)==0) {
/* found an ID3 header... */
_ppi->log(E_DBG,"Found ID3 header\n");
size = (id3.size[0] << 21 | id3.size[1] << 14 |
id3.size[2] << 7 | id3.size[3]);
fseek(handle->fin,size + sizeof(SCAN_ID3HEADER),SEEK_SET);
_ppi->log(E_DBG,"Header length: %d\n",size);
} else {
fseek(handle->fin,0,SEEK_SET);
}
return TRUE;
}

View File

@ -45,7 +45,6 @@ PLUGIN_INFO _pi = {
PLUGIN_VERSION, /* version */
PLUGIN_TRANSCODE, /* type */
"ssc-script/" VERSION, /* server */
NULL, /* url */
NULL, /* output fns */
NULL, /* event fns */
&_ptfn, /* fns */

View File

@ -19,7 +19,6 @@ PLUGIN_INFO _pi = {
PLUGIN_VERSION, /* version */
PLUGIN_EVENT, /* type */
"w32-event/" VERSION, /* server */
NULL, /* url */
NULL, /* output fns */
&_pefn, /* event fns */
NULL, /* transocde fns */

View File

@ -82,6 +82,7 @@ static char *scan_xml_track_tags[] = {
"Location",
"Date Added",
"Comments",
"Composer",
NULL
};
@ -106,6 +107,7 @@ static char *scan_xml_track_tags[] = {
#define SCAN_XML_T_LOCATION 16
#define SCAN_XML_T_DATE_ADDED 17
#define SCAN_XML_T_COMMENTS 18
#define SCAN_XML_T_COMPOSER 19
#ifndef TRUE
# define TRUE 1
@ -700,6 +702,7 @@ int scan_xml_tracks_section(int action, char *info) {
MAYBECOPYSTRING(album);
MAYBECOPYSTRING(genre);
MAYBECOPYSTRING(comment);
MAYBECOPYSTRING(composer);
MAYBECOPY(song_length);
MAYBECOPY(track);
MAYBECOPY(total_tracks);
@ -818,6 +821,8 @@ int scan_xml_tracks_section(int action, char *info) {
mp3.time_added = scan_xml_datedecode(info);
} else if(current_field == SCAN_XML_T_COMMENTS) {
mp3.comment = strdup(info);
} else if(current_field == SCAN_XML_T_COMPOSER) {
mp3.composer = strdup(info);
}
} else if(action == RXML_EVT_END) {
state = XML_TRACK_ST_TRACK_INFO;