mirror of
https://github.com/owntone/owntone-server.git
synced 2024-12-24 22:25:56 -05:00
Move daap to an external output module
This commit is contained in:
parent
3051c4ef46
commit
8e23eb9e1d
@ -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 \
|
||||
|
@ -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
|
||||
*
|
||||
|
@ -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 {
|
||||
|
426
src/db-generic.c
426
src/db-generic.c
@ -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
|
||||
|
@ -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?
|
||||
|
405
src/db-sql.c
405
src/db-sql.c
@ -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));
|
||||
|
||||
|
@ -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
|
||||
|
1605
src/dispatch.c
1605
src/dispatch.c
File diff suppressed because it is too large
Load Diff
@ -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
61
src/ff-dbstruct.h
Normal 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_ */
|
@ -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;
|
||||
|
||||
|
||||
|
10
src/main.c
10
src/main.c
@ -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);
|
||||
|
@ -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
|
||||
|
228
src/plugin.c
228
src/plugin.c
@ -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);
|
||||
}
|
||||
|
@ -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..
|
||||
|
||||
|
7
src/plugins/out-daap-db.c
Normal file
7
src/plugins/out-daap-db.c
Normal file
@ -0,0 +1,7 @@
|
||||
/*
|
||||
* $Id: $
|
||||
*/
|
||||
|
||||
#include "out-daap.h"
|
||||
#include "out-daap-proto.h"
|
||||
#include "out-daap-db.h"
|
8
src/plugins/out-daap-db.h
Normal file
8
src/plugins/out-daap-db.h
Normal file
@ -0,0 +1,8 @@
|
||||
/*
|
||||
* $Id: $
|
||||
*/
|
||||
|
||||
#ifndef _OUT_DAAP_DB_H_
|
||||
#define _OUT_DAAP_DB_H_
|
||||
|
||||
#endif /* _OUT_DAAP_DB_H_ */
|
894
src/plugins/out-daap-proto.c
Normal file
894
src/plugins/out-daap-proto.c
Normal 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;
|
||||
}
|
||||
|
36
src/plugins/out-daap-proto.h
Normal file
36
src/plugins/out-daap-proto.h
Normal 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
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
98
src/plugins/out-daap.h
Normal 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_ */
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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 */
|
||||
|
@ -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 */
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user