mirror of
https://github.com/owntone/owntone-server.git
synced 2025-02-03 09:56:00 -05:00
abstract database somewhat, improve error handling
This commit is contained in:
parent
24f40d7956
commit
86b45a028e
@ -30,7 +30,7 @@ MUSEPACKSRC=scan-mpc.c
|
|||||||
endif
|
endif
|
||||||
|
|
||||||
if COND_SQLITE
|
if COND_SQLITE
|
||||||
SQLITEDB=dbs-sqlite.c dbs-sqlite.h
|
SQLITEDB=db-sql.c db-sql.h db-sql-sqlite2.c db-sql-sqlite2.h
|
||||||
endif
|
endif
|
||||||
|
|
||||||
wavstreamer_SOURCES = wavstreamer.c
|
wavstreamer_SOURCES = wavstreamer.c
|
||||||
|
@ -22,7 +22,7 @@
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* \file configfile.c
|
* \file configfile.c
|
||||||
*
|
*
|
||||||
* Configfile and web interface handling.
|
* Configfile and web interface handling.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -178,7 +178,7 @@ void config_content_type(WS_CONNINFO *pwsc, char *path) {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Try and create a directory, including parents.
|
* Try and create a directory, including parents.
|
||||||
*
|
*
|
||||||
* \param path path to make
|
* \param path path to make
|
||||||
* \returns 0 on success, -1 otherwise, with errno set
|
* \returns 0 on success, -1 otherwise, with errno set
|
||||||
*/
|
*/
|
||||||
@ -208,11 +208,11 @@ int config_makedir(char *path) {
|
|||||||
err=errno;
|
err=errno;
|
||||||
free(pathdup);
|
free(pathdup);
|
||||||
errno=err;
|
errno=err;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
errno=ENAMETOOLONG;
|
errno=ENAMETOOLONG;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -281,7 +281,7 @@ int config_read(char *file) {
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef NSLU2
|
#ifdef NSLU2
|
||||||
config.always_scan=0;
|
config.always_scan=0;
|
||||||
#else
|
#else
|
||||||
config.always_scan=1;
|
config.always_scan=1;
|
||||||
@ -334,7 +334,7 @@ int config_read(char *file) {
|
|||||||
if((value) && (term) && (strlen(term))) {
|
if((value) && (term) && (strlen(term))) {
|
||||||
while(strlen(value) && (strchr("\t ",*value)))
|
while(strlen(value) && (strchr("\t ",*value)))
|
||||||
value++;
|
value++;
|
||||||
|
|
||||||
pce=config_elements;
|
pce=config_elements;
|
||||||
handled=0;
|
handled=0;
|
||||||
while((!handled) && (pce->config_element != -1)) {
|
while((!handled) && (pce->config_element != -1)) {
|
||||||
@ -342,9 +342,9 @@ int config_read(char *file) {
|
|||||||
/* valid config directive */
|
/* valid config directive */
|
||||||
handled=1;
|
handled=1;
|
||||||
pce->changed=1;
|
pce->changed=1;
|
||||||
|
|
||||||
DPRINTF(E_DBG,L_CONF,"Read %s: %s\n",pce->name,value);
|
DPRINTF(E_DBG,L_CONF,"Read %s: %s\n",pce->name,value);
|
||||||
|
|
||||||
switch(pce->type) {
|
switch(pce->type) {
|
||||||
case CONFIG_TYPE_STRING:
|
case CONFIG_TYPE_STRING:
|
||||||
/* DWB: free space to prevent small leak */
|
/* DWB: free space to prevent small leak */
|
||||||
@ -359,7 +359,7 @@ int config_read(char *file) {
|
|||||||
}
|
}
|
||||||
pce++;
|
pce++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!handled) {
|
if(!handled) {
|
||||||
fprintf(stderr,"Invalid config directive: %s\n",buffer);
|
fprintf(stderr,"Invalid config directive: %s\n",buffer);
|
||||||
fclose(fin);
|
fclose(fin);
|
||||||
@ -396,7 +396,7 @@ int config_read(char *file) {
|
|||||||
pce->changed=0;
|
pce->changed=0;
|
||||||
pce++;
|
pce++;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Set the directory components to realpaths */
|
/* Set the directory components to realpaths */
|
||||||
realpath(config.web_root,path_buffer);
|
realpath(config.web_root,path_buffer);
|
||||||
free(config.web_root);
|
free(config.web_root);
|
||||||
@ -471,7 +471,7 @@ int config_read(char *file) {
|
|||||||
DPRINTF(E_FATAL,L_MISC,"Alloc error.\n");
|
DPRINTF(E_FATAL,L_MISC,"Alloc error.\n");
|
||||||
|
|
||||||
currentterm=0;
|
currentterm=0;
|
||||||
|
|
||||||
term_begin=config.compdirs;
|
term_begin=config.compdirs;
|
||||||
while(*term_begin && *term_begin ==' ')
|
while(*term_begin && *term_begin ==' ')
|
||||||
term_begin++;
|
term_begin++;
|
||||||
@ -521,8 +521,8 @@ void config_close(void) {
|
|||||||
pce=config_elements;
|
pce=config_elements;
|
||||||
err=0;
|
err=0;
|
||||||
while((pce->config_element != -1)) {
|
while((pce->config_element != -1)) {
|
||||||
if((pce->config_element) &&
|
if((pce->config_element) &&
|
||||||
(pce->type == CONFIG_TYPE_STRING) &&
|
(pce->type == CONFIG_TYPE_STRING) &&
|
||||||
(*((char**)pce->var))) {
|
(*((char**)pce->var))) {
|
||||||
DPRINTF(E_DBG,L_CONF,"Freeing %s\n",pce->name);
|
DPRINTF(E_DBG,L_CONF,"Freeing %s\n",pce->name);
|
||||||
free(*((char**)pce->var));
|
free(*((char**)pce->var));
|
||||||
@ -575,9 +575,9 @@ int config_write(WS_CONNINFO *pwsc) {
|
|||||||
fprintf(configfile,"art_filename\t%s\n",ws_getvar(pwsc,"art_filename"));
|
fprintf(configfile,"art_filename\t%s\n",ws_getvar(pwsc,"art_filename"));
|
||||||
if(ws_getvar(pwsc,"logfile") && strlen(ws_getvar(pwsc,"logfile")))
|
if(ws_getvar(pwsc,"logfile") && strlen(ws_getvar(pwsc,"logfile")))
|
||||||
fprintf(configfile,"logfile\t\t%s\n",ws_getvar(pwsc,"logfile"));
|
fprintf(configfile,"logfile\t\t%s\n",ws_getvar(pwsc,"logfile"));
|
||||||
fprintf(configfile,"process_m3u\t%s\n",ws_getvar(pwsc,"process_m3u"));
|
fprintf(configfile,"process_m3u\t%s\n",ws_getvar(pwsc,"process_m3u"));
|
||||||
fprintf(configfile,"compress\t%s\n",ws_getvar(pwsc,"compress"));
|
fprintf(configfile,"compress\t%s\n",ws_getvar(pwsc,"compress"));
|
||||||
|
|
||||||
fprintf(configfile,"debuglevel\t%s\n",ws_getvar(pwsc,"debuglevel"));
|
fprintf(configfile,"debuglevel\t%s\n",ws_getvar(pwsc,"debuglevel"));
|
||||||
fprintf(configfile,"compdirs\t%s\n",ws_getvar(pwsc,"compdirs"));
|
fprintf(configfile,"compdirs\t%s\n",ws_getvar(pwsc,"compdirs"));
|
||||||
|
|
||||||
@ -668,12 +668,12 @@ void config_handler(WS_CONNINFO *pwsc) {
|
|||||||
DPRINTF(E_DBG,L_CONF|L_WS,"Entering config_handler\n");
|
DPRINTF(E_DBG,L_CONF|L_WS,"Entering config_handler\n");
|
||||||
|
|
||||||
config_set_status(pwsc,0,"Serving admin pages");
|
config_set_status(pwsc,0,"Serving admin pages");
|
||||||
|
|
||||||
pwsc->close=1;
|
pwsc->close=1;
|
||||||
ws_addresponseheader(pwsc,"Connection","close");
|
ws_addresponseheader(pwsc,"Connection","close");
|
||||||
|
|
||||||
if(strcasecmp(pwsc->uri,"/xml-rpc")==0) {
|
if(strcasecmp(pwsc->uri,"/xml-rpc")==0) {
|
||||||
// perhaps this should get a separate handler
|
// perhaps this should get a separate handler
|
||||||
config_set_status(pwsc,0,"Serving xml-rpc method");
|
config_set_status(pwsc,0,"Serving xml-rpc method");
|
||||||
xml_handle(pwsc);
|
xml_handle(pwsc);
|
||||||
DPRINTF(E_DBG,L_CONF|L_XML,"Thread %d: xml-rpc served\n",pwsc->threadno);
|
DPRINTF(E_DBG,L_CONF|L_XML,"Thread %d: xml-rpc served\n",pwsc->threadno);
|
||||||
@ -717,7 +717,7 @@ void config_handler(WS_CONNINFO *pwsc) {
|
|||||||
config_set_status(pwsc,0,NULL);
|
config_set_status(pwsc,0,NULL);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(strcasecmp(pwsc->uri,"/config-update.html")==0) {
|
if(strcasecmp(pwsc->uri,"/config-update.html")==0) {
|
||||||
/* don't update (and turn everything to (null)) the
|
/* don't update (and turn everything to (null)) the
|
||||||
configuration file if what the user's really trying to do is
|
configuration file if what the user's really trying to do is
|
||||||
@ -763,10 +763,10 @@ void config_handler(WS_CONNINFO *pwsc) {
|
|||||||
|
|
||||||
ws_writefd(pwsc,"HTTP/1.1 200 OK\r\n");
|
ws_writefd(pwsc,"HTTP/1.1 200 OK\r\n");
|
||||||
ws_emitheaders(pwsc);
|
ws_emitheaders(pwsc);
|
||||||
|
|
||||||
if(strcasecmp(&resolved_path[strlen(resolved_path) - 5],".html") == 0) {
|
if(strcasecmp(&resolved_path[strlen(resolved_path) - 5],".html") == 0) {
|
||||||
config_subst_stream(pwsc, file_fd);
|
config_subst_stream(pwsc, file_fd);
|
||||||
} else {
|
} else {
|
||||||
copyfile(file_fd,pwsc->fd);
|
copyfile(file_fd,pwsc->fd);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -800,7 +800,7 @@ int config_auth(char *user, char *password) {
|
|||||||
void config_emit_host(WS_CONNINFO *pwsc, void *value, char *arg) {
|
void config_emit_host(WS_CONNINFO *pwsc, void *value, char *arg) {
|
||||||
char *host;
|
char *host;
|
||||||
char *port;
|
char *port;
|
||||||
|
|
||||||
if(ws_getrequestheader(pwsc,"host")) {
|
if(ws_getrequestheader(pwsc,"host")) {
|
||||||
host = strdup(ws_getrequestheader(pwsc,"host"));
|
host = strdup(ws_getrequestheader(pwsc,"host"));
|
||||||
if((port = strrchr(host,':'))) {
|
if((port = strrchr(host,':'))) {
|
||||||
@ -868,6 +868,7 @@ void config_emit_service_status(WS_CONNINFO *pwsc, void *value, char *arg) {
|
|||||||
char buf[256];
|
char buf[256];
|
||||||
int r_days, r_hours, r_mins, r_secs;
|
int r_days, r_hours, r_mins, r_secs;
|
||||||
int scanning;
|
int scanning;
|
||||||
|
int song_count;
|
||||||
|
|
||||||
ws_writefd(pwsc,"<table><tr><th align=\"left\">Service</th>");
|
ws_writefd(pwsc,"<table><tr><th align=\"left\">Service</th>");
|
||||||
ws_writefd(pwsc,"<th align=\"left\">Status</th><th align=\"left\">Control</th></tr>\n");
|
ws_writefd(pwsc,"<th align=\"left\">Status</th><th align=\"left\">Control</th></tr>\n");
|
||||||
@ -925,11 +926,11 @@ void config_emit_service_status(WS_CONNINFO *pwsc, void *value, char *arg) {
|
|||||||
r_secs -= 60 * r_mins;
|
r_secs -= 60 * r_mins;
|
||||||
|
|
||||||
memset(buf,0x0,sizeof(buf));
|
memset(buf,0x0,sizeof(buf));
|
||||||
if(r_days)
|
if(r_days)
|
||||||
sprintf((char*)&buf[strlen(buf)],"%d day%s, ", r_days,
|
sprintf((char*)&buf[strlen(buf)],"%d day%s, ", r_days,
|
||||||
r_days == 1 ? "" : "s");
|
r_days == 1 ? "" : "s");
|
||||||
|
|
||||||
if(r_days || r_hours)
|
if(r_days || r_hours)
|
||||||
sprintf((char*)&buf[strlen(buf)],"%d hour%s, ", r_hours,
|
sprintf((char*)&buf[strlen(buf)],"%d hour%s, ", r_hours,
|
||||||
r_hours == 1 ? "" : "s");
|
r_hours == 1 ? "" : "s");
|
||||||
|
|
||||||
@ -939,13 +940,14 @@ void config_emit_service_status(WS_CONNINFO *pwsc, void *value, char *arg) {
|
|||||||
|
|
||||||
sprintf((char*)&buf[strlen(buf)],"%d second%s ", r_secs,
|
sprintf((char*)&buf[strlen(buf)],"%d second%s ", r_secs,
|
||||||
r_secs == 1 ? "" : "s");
|
r_secs == 1 ? "" : "s");
|
||||||
|
|
||||||
ws_writefd(pwsc," <td>%s</td>\n",buf);
|
ws_writefd(pwsc," <td>%s</td>\n",buf);
|
||||||
ws_writefd(pwsc,"</tr>\n");
|
ws_writefd(pwsc,"</tr>\n");
|
||||||
|
|
||||||
ws_writefd(pwsc,"<tr>\n");
|
ws_writefd(pwsc,"<tr>\n");
|
||||||
ws_writefd(pwsc," <th>Songs</th>\n");
|
ws_writefd(pwsc," <th>Songs</th>\n");
|
||||||
ws_writefd(pwsc," <td>%d</td>\n",db_get_song_count());
|
db_get_song_count(NULL,&song_count);
|
||||||
|
ws_writefd(pwsc," <td>%d</td>\n",song_count);
|
||||||
ws_writefd(pwsc,"</tr>\n");
|
ws_writefd(pwsc,"</tr>\n");
|
||||||
|
|
||||||
ws_writefd(pwsc,"<tr>\n");
|
ws_writefd(pwsc,"<tr>\n");
|
||||||
@ -975,7 +977,7 @@ void config_emit_service_status(WS_CONNINFO *pwsc, void *value, char *arg) {
|
|||||||
* Get the count of connected users. This is actually not totally accurate,
|
* Get the count of connected users. This is actually not totally accurate,
|
||||||
* as there may be a "connected" host that is in between requesting songs.
|
* as there may be a "connected" host that is in between requesting songs.
|
||||||
* It's marginally close though. It is really the number of connections
|
* It's marginally close though. It is really the number of connections
|
||||||
* with non-zero session numbers.
|
* with non-zero session numbers.
|
||||||
*
|
*
|
||||||
* \returns connected user count
|
* \returns connected user count
|
||||||
*/
|
*/
|
||||||
@ -1018,7 +1020,7 @@ void config_emit_threadstatus(WS_CONNINFO *pwsc, void *value, char *arg) {
|
|||||||
WS_CONNINFO *pci;
|
WS_CONNINFO *pci;
|
||||||
SCAN_STATUS *pss;
|
SCAN_STATUS *pss;
|
||||||
WSTHREADENUM wste;
|
WSTHREADENUM wste;
|
||||||
|
|
||||||
ws_writefd(pwsc,"<table><tr><th align=\"left\">Thread</th>");
|
ws_writefd(pwsc,"<table><tr><th align=\"left\">Thread</th>");
|
||||||
ws_writefd(pwsc,"<th align=\"left\">Session</th><th align=\"left\">Host</th>");
|
ws_writefd(pwsc,"<th align=\"left\">Session</th><th align=\"left\">Host</th>");
|
||||||
ws_writefd(pwsc,"<th align=\"left\">Action</th></tr>\n");
|
ws_writefd(pwsc,"<th align=\"left\">Action</th></tr>\n");
|
||||||
@ -1058,7 +1060,7 @@ void config_emit_ispage(WS_CONNINFO *pwsc, void *value, char *arg) {
|
|||||||
|
|
||||||
first=last=arg;
|
first=last=arg;
|
||||||
strsep(&last,":");
|
strsep(&last,":");
|
||||||
|
|
||||||
if(last) {
|
if(last) {
|
||||||
page=strdup(first);
|
page=strdup(first);
|
||||||
if(!page)
|
if(!page)
|
||||||
@ -1152,7 +1154,7 @@ void config_emit_readonly(WS_CONNINFO *pwsc, void *value, char *arg) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* implement the INCLUDE command. This is essentially a server
|
* implement the INCLUDE command. This is essentially a server
|
||||||
* side include.
|
* side include.
|
||||||
*
|
*
|
||||||
* \param pwsc web connection
|
* \param pwsc web connection
|
||||||
@ -1166,7 +1168,7 @@ void config_emit_include(WS_CONNINFO *pwsc, void *value, char *arg) {
|
|||||||
struct stat sb;
|
struct stat sb;
|
||||||
|
|
||||||
DPRINTF(E_DBG,L_CONF|L_WS,"Preparing to include %s\n",arg);
|
DPRINTF(E_DBG,L_CONF|L_WS,"Preparing to include %s\n",arg);
|
||||||
|
|
||||||
snprintf(path,PATH_MAX,"%s/%s",config.web_root,arg);
|
snprintf(path,PATH_MAX,"%s/%s",config.web_root,arg);
|
||||||
if(!realpath(path,resolved_path)) {
|
if(!realpath(path,resolved_path)) {
|
||||||
pwsc->error=errno;
|
pwsc->error=errno;
|
||||||
@ -1203,7 +1205,7 @@ void config_emit_include(WS_CONNINFO *pwsc, void *value, char *arg) {
|
|||||||
ws_writefd(pwsc,"<hr><i>error: cannot open %s: %s</i><hr>",arg,strerror(errno));
|
ws_writefd(pwsc,"<hr><i>error: cannot open %s: %s</i><hr>",arg,strerror(errno));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
config_subst_stream(pwsc, file_fd);
|
config_subst_stream(pwsc, file_fd);
|
||||||
|
|
||||||
r_close(file_fd);
|
r_close(file_fd);
|
||||||
@ -1212,13 +1214,13 @@ void config_emit_include(WS_CONNINFO *pwsc, void *value, char *arg) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* free a SCAN_STATUS block
|
* free a SCAN_STATUS block
|
||||||
*
|
*
|
||||||
* @param vp pointer to SCAN_STATUS block
|
* @param vp pointer to SCAN_STATUS block
|
||||||
*/
|
*/
|
||||||
void config_freescan(void *vp) {
|
void config_freescan(void *vp) {
|
||||||
SCAN_STATUS *pss = (SCAN_STATUS*)vp;
|
SCAN_STATUS *pss = (SCAN_STATUS*)vp;
|
||||||
|
|
||||||
if(pss) {
|
if(pss) {
|
||||||
if(pss->what)
|
if(pss->what)
|
||||||
free(pss->what);
|
free(pss->what);
|
||||||
@ -1243,7 +1245,7 @@ void config_set_status(WS_CONNINFO *pwsc, int session, char *fmt, ...) {
|
|||||||
va_list ap;
|
va_list ap;
|
||||||
SCAN_STATUS *pfirst;
|
SCAN_STATUS *pfirst;
|
||||||
char *newmsg = NULL;
|
char *newmsg = NULL;
|
||||||
|
|
||||||
DPRINTF(E_DBG,L_CONF,"Entering config_set_status\n");
|
DPRINTF(E_DBG,L_CONF,"Entering config_set_status\n");
|
||||||
|
|
||||||
if(fmt) {
|
if(fmt) {
|
||||||
@ -1262,24 +1264,24 @@ void config_set_status(WS_CONNINFO *pwsc, int session, char *fmt, ...) {
|
|||||||
pfirst->what = NULL;
|
pfirst->what = NULL;
|
||||||
pfirst->thread = pwsc->threadno;
|
pfirst->thread = pwsc->threadno;
|
||||||
pfirst->host = strdup(pwsc->hostname);
|
pfirst->host = strdup(pwsc->hostname);
|
||||||
ws_set_local_storage(pwsc,pfirst,config_freescan);
|
ws_set_local_storage(pwsc,pfirst,config_freescan);
|
||||||
} else {
|
} else {
|
||||||
DPRINTF(E_FATAL,L_CONF,"Malloc Error\n");
|
DPRINTF(E_FATAL,L_CONF,"Malloc Error\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(pfirst) {
|
if(pfirst) {
|
||||||
/* just update */
|
/* just update */
|
||||||
if(pfirst->what) {
|
if(pfirst->what) {
|
||||||
free(pfirst->what);
|
free(pfirst->what);
|
||||||
}
|
}
|
||||||
pfirst->what=newmsg;
|
pfirst->what=newmsg;
|
||||||
pfirst->session=session;
|
pfirst->session=session;
|
||||||
} else {
|
} else {
|
||||||
if(newmsg)
|
if(newmsg)
|
||||||
free(newmsg);
|
free(newmsg);
|
||||||
}
|
}
|
||||||
|
|
||||||
ws_unlock_local_storage(pwsc);
|
ws_unlock_local_storage(pwsc);
|
||||||
DPRINTF(E_DBG,L_CONF,"Exiting config_set_status\n");
|
DPRINTF(E_DBG,L_CONF,"Exiting config_set_status\n");
|
||||||
}
|
}
|
||||||
@ -1288,7 +1290,7 @@ void config_set_status(WS_CONNINFO *pwsc, int session, char *fmt, ...) {
|
|||||||
* Get the next available session id.
|
* Get the next available session id.
|
||||||
* This is vulnerable to races, but we don't track sessions,
|
* This is vulnerable to races, but we don't track sessions,
|
||||||
* so there really isn't a point anyway.
|
* so there really isn't a point anyway.
|
||||||
*
|
*
|
||||||
* @returns duh... the next available session id
|
* @returns duh... the next available session id
|
||||||
*/
|
*/
|
||||||
int config_get_next_session(void) {
|
int config_get_next_session(void) {
|
||||||
|
430
src/db-generic.c
430
src/db-generic.c
@ -27,6 +27,7 @@
|
|||||||
|
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <stdarg.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
@ -34,7 +35,7 @@
|
|||||||
#include "err.h"
|
#include "err.h"
|
||||||
#include "mp3-scanner.h"
|
#include "mp3-scanner.h"
|
||||||
|
|
||||||
#include "dbs-sqlite.h"
|
#include "db-sql.h"
|
||||||
|
|
||||||
#define DB_VERSION 1
|
#define DB_VERSION 1
|
||||||
#define MAYBEFREE(a) { if((a)) free((a)); };
|
#define MAYBEFREE(a) { if((a)) free((a)); };
|
||||||
@ -42,61 +43,59 @@
|
|||||||
/** pointers to database-specific functions */
|
/** pointers to database-specific functions */
|
||||||
typedef struct tag_db_functions {
|
typedef struct tag_db_functions {
|
||||||
char *name;
|
char *name;
|
||||||
int(*dbs_open)(char *);
|
int(*dbs_open)(char **, char *);
|
||||||
int(*dbs_init)(int);
|
int(*dbs_init)(int);
|
||||||
int(*dbs_deinit)(void);
|
int(*dbs_deinit)(void);
|
||||||
int(*dbs_add)(MP3FILE*);
|
int(*dbs_add)(char **, MP3FILE*);
|
||||||
int(*dbs_add_playlist)(char *, int, char *,char *, int, int *);
|
int(*dbs_add_playlist)(char **, char *, int, char *,char *, int, int *);
|
||||||
int(*dbs_add_playlist_item)(int, int);
|
int(*dbs_add_playlist_item)(char **, int, int);
|
||||||
int(*dbs_delete_playlist)(int);
|
int(*dbs_delete_playlist)(char **, int);
|
||||||
int(*dbs_delete_playlist_item)(int, int);
|
int(*dbs_delete_playlist_item)(char **, int, int);
|
||||||
int(*dbs_edit_playlist)(int, char*, char*);
|
int(*dbs_edit_playlist)(char **, int, char*, char*);
|
||||||
int(*dbs_enum_start)(DBQUERYINFO *);
|
int(*dbs_enum_start)(char **, DBQUERYINFO *);
|
||||||
int(*dbs_enum_size)(DBQUERYINFO *, int *);
|
int(*dbs_enum_size)(char **, DBQUERYINFO *, int *, int *);
|
||||||
int(*dbs_enum_fetch)(DBQUERYINFO *, unsigned char **);
|
int(*dbs_enum_fetch)(char **, DBQUERYINFO *, int *, unsigned char **);
|
||||||
int(*dbs_enum_reset)(DBQUERYINFO *);
|
int(*dbs_enum_reset)(char **, DBQUERYINFO *);
|
||||||
int(*dbs_enum_end)(void);
|
int(*dbs_enum_end)(char **);
|
||||||
int(*dbs_start_scan)(void);
|
int(*dbs_start_scan)(void);
|
||||||
int(*dbs_end_song_scan)(void);
|
int(*dbs_end_song_scan)(void);
|
||||||
int(*dbs_end_scan)(void);
|
int(*dbs_end_scan)(void);
|
||||||
int(*dbs_get_count)(CountType_t);
|
int(*dbs_get_count)(char **, int *, CountType_t);
|
||||||
MP3FILE*(*dbs_fetch_item)(int);
|
MP3FILE*(*dbs_fetch_item)(char **, int);
|
||||||
MP3FILE*(*dbs_fetch_path)(char *,int);
|
MP3FILE*(*dbs_fetch_path)(char **, char *,int);
|
||||||
M3UFILE*(*dbs_fetch_playlist)(char *, int);
|
M3UFILE*(*dbs_fetch_playlist)(char **, char *, int);
|
||||||
void(*dbs_dispose_item)(MP3FILE*);
|
void(*dbs_dispose_item)(MP3FILE*);
|
||||||
void(*dbs_dispose_playlist)(M3UFILE*);
|
void(*dbs_dispose_playlist)(M3UFILE*);
|
||||||
}DB_FUNCTIONS;
|
}DB_FUNCTIONS;
|
||||||
|
|
||||||
/** All supported backend databases, and pointers to the db specific implementations */
|
/** All supported backend databases, and pointers to the db specific implementations */
|
||||||
DB_FUNCTIONS db_functions[] = {
|
DB_FUNCTIONS db_functions[] = {
|
||||||
#ifdef HAVE_LIBSQLITE
|
|
||||||
{
|
{
|
||||||
"sqlite",
|
"sql",
|
||||||
db_sqlite_open,
|
db_sql_open,
|
||||||
db_sqlite_init,
|
db_sql_init,
|
||||||
db_sqlite_deinit,
|
db_sql_deinit,
|
||||||
db_sqlite_add,
|
db_sql_add,
|
||||||
db_sqlite_add_playlist,
|
db_sql_add_playlist,
|
||||||
db_sqlite_add_playlist_item,
|
db_sql_add_playlist_item,
|
||||||
db_sqlite_delete_playlist,
|
db_sql_delete_playlist,
|
||||||
db_sqlite_delete_playlist_item,
|
db_sql_delete_playlist_item,
|
||||||
db_sqlite_edit_playlist,
|
db_sql_edit_playlist,
|
||||||
db_sqlite_enum_start,
|
db_sql_enum_start,
|
||||||
db_sqlite_enum_size,
|
db_sql_enum_size,
|
||||||
db_sqlite_enum_fetch,
|
db_sql_enum_fetch,
|
||||||
db_sqlite_enum_reset,
|
db_sql_enum_reset,
|
||||||
db_sqlite_enum_end,
|
db_sql_enum_end,
|
||||||
db_sqlite_start_scan,
|
db_sql_start_scan,
|
||||||
db_sqlite_end_song_scan,
|
db_sql_end_song_scan,
|
||||||
db_sqlite_end_scan,
|
db_sql_end_scan,
|
||||||
db_sqlite_get_count,
|
db_sql_get_count,
|
||||||
db_sqlite_fetch_item,
|
db_sql_fetch_item,
|
||||||
db_sqlite_fetch_path,
|
db_sql_fetch_path,
|
||||||
db_sqlite_fetch_playlist,
|
db_sql_fetch_playlist,
|
||||||
db_sqlite_dispose_item,
|
db_sql_dispose_item,
|
||||||
db_sqlite_dispose_playlist
|
db_sql_dispose_playlist
|
||||||
},
|
},
|
||||||
#endif
|
|
||||||
{ NULL,NULL }
|
{ NULL,NULL }
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -152,11 +151,11 @@ DAAP_ITEMS taglist[] = {
|
|||||||
{ 0x0C, "adbs", "daap.databasesongs" },
|
{ 0x0C, "adbs", "daap.databasesongs" },
|
||||||
{ 0x09, "asal", "daap.songalbum" },
|
{ 0x09, "asal", "daap.songalbum" },
|
||||||
{ 0x09, "asar", "daap.songartist" },
|
{ 0x09, "asar", "daap.songartist" },
|
||||||
{ 0x03, "asbt", "daap.songbeatsperminute" },
|
{ 0x03, "asbt", "daap.songbeatsperminute" },
|
||||||
{ 0x03, "asbr", "daap.songbitrate" },
|
{ 0x03, "asbr", "daap.songbitrate" },
|
||||||
{ 0x09, "ascm", "daap.songcomment" },
|
{ 0x09, "ascm", "daap.songcomment" },
|
||||||
{ 0x01, "asco", "daap.songcompilation" },
|
{ 0x01, "asco", "daap.songcompilation" },
|
||||||
{ 0x09, "ascp", "daap.songcomposer" },
|
{ 0x09, "ascp", "daap.songcomposer" },
|
||||||
{ 0x0A, "asda", "daap.songdateadded" },
|
{ 0x0A, "asda", "daap.songdateadded" },
|
||||||
{ 0x0A, "asdm", "daap.songdatemodified" },
|
{ 0x0A, "asdm", "daap.songdatemodified" },
|
||||||
{ 0x03, "asdc", "daap.songdisccount" },
|
{ 0x03, "asdc", "daap.songdisccount" },
|
||||||
@ -210,43 +209,43 @@ DAAP_ITEMS taglist[] = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/** map the string names specified in the meta= tag to bit numbers */
|
/** map the string names specified in the meta= tag to bit numbers */
|
||||||
static METAMAP db_metamap[] = {
|
static METAMAP db_metamap[] = {
|
||||||
{ "dmap.itemid", metaItemId },
|
{ "dmap.itemid", metaItemId },
|
||||||
{ "dmap.itemname", metaItemName },
|
{ "dmap.itemname", metaItemName },
|
||||||
{ "dmap.itemkind", metaItemKind },
|
{ "dmap.itemkind", metaItemKind },
|
||||||
{ "dmap.persistentid", metaPersistentId },
|
{ "dmap.persistentid", metaPersistentId },
|
||||||
{ "dmap.containeritemid", metaContainerItemId },
|
{ "dmap.containeritemid", metaContainerItemId },
|
||||||
{ "dmap.parentcontainerid", metaParentContainerId },
|
{ "dmap.parentcontainerid", metaParentContainerId },
|
||||||
/* end generics */
|
/* end generics */
|
||||||
{ "daap.songalbum", metaSongAlbum },
|
{ "daap.songalbum", metaSongAlbum },
|
||||||
{ "daap.songartist", metaSongArtist },
|
{ "daap.songartist", metaSongArtist },
|
||||||
{ "daap.songbitrate", metaSongBitRate },
|
{ "daap.songbitrate", metaSongBitRate },
|
||||||
{ "daap.songbeatsperminute", metaSongBPM },
|
{ "daap.songbeatsperminute", metaSongBPM },
|
||||||
{ "daap.songcomment", metaSongComment },
|
{ "daap.songcomment", metaSongComment },
|
||||||
{ "daap.songcompilation", metaSongCompilation },
|
{ "daap.songcompilation", metaSongCompilation },
|
||||||
{ "daap.songcomposer", metaSongComposer },
|
{ "daap.songcomposer", metaSongComposer },
|
||||||
{ "daap.songdatakind", metaSongDataKind },
|
{ "daap.songdatakind", metaSongDataKind },
|
||||||
{ "daap.songdataurl", metaSongDataURL },
|
{ "daap.songdataurl", metaSongDataURL },
|
||||||
{ "daap.songdateadded", metaSongDateAdded },
|
{ "daap.songdateadded", metaSongDateAdded },
|
||||||
{ "daap.songdatemodified", metaSongDateModified },
|
{ "daap.songdatemodified", metaSongDateModified },
|
||||||
{ "daap.songdescription", metaSongDescription },
|
{ "daap.songdescription", metaSongDescription },
|
||||||
{ "daap.songdisabled", metaSongDisabled },
|
{ "daap.songdisabled", metaSongDisabled },
|
||||||
{ "daap.songdisccount", metaSongDiscCount },
|
{ "daap.songdisccount", metaSongDiscCount },
|
||||||
{ "daap.songdiscnumber", metaSongDiscNumber },
|
{ "daap.songdiscnumber", metaSongDiscNumber },
|
||||||
{ "daap.songeqpreset", metaSongEqPreset },
|
{ "daap.songeqpreset", metaSongEqPreset },
|
||||||
{ "daap.songformat", metaSongFormat },
|
{ "daap.songformat", metaSongFormat },
|
||||||
{ "daap.songgenre", metaSongGenre },
|
{ "daap.songgenre", metaSongGenre },
|
||||||
{ "daap.songgrouping", metaSongGrouping },
|
{ "daap.songgrouping", metaSongGrouping },
|
||||||
{ "daap.songrelativevolume", metaSongRelativeVolume },
|
{ "daap.songrelativevolume", metaSongRelativeVolume },
|
||||||
{ "daap.songsamplerate", metaSongSampleRate },
|
{ "daap.songsamplerate", metaSongSampleRate },
|
||||||
{ "daap.songsize", metaSongSize },
|
{ "daap.songsize", metaSongSize },
|
||||||
{ "daap.songstarttime", metaSongStartTime },
|
{ "daap.songstarttime", metaSongStartTime },
|
||||||
{ "daap.songstoptime", metaSongStopTime },
|
{ "daap.songstoptime", metaSongStopTime },
|
||||||
{ "daap.songtime", metaSongTime },
|
{ "daap.songtime", metaSongTime },
|
||||||
{ "daap.songtrackcount", metaSongTrackCount },
|
{ "daap.songtrackcount", metaSongTrackCount },
|
||||||
{ "daap.songtracknumber", metaSongTrackNumber },
|
{ "daap.songtracknumber", metaSongTrackNumber },
|
||||||
{ "daap.songuserrating", metaSongUserRating },
|
{ "daap.songuserrating", metaSongUserRating },
|
||||||
{ "daap.songyear", metaSongYear },
|
{ "daap.songyear", metaSongYear },
|
||||||
/* iTunes 4.5+ (forgot exactly when) */
|
/* iTunes 4.5+ (forgot exactly when) */
|
||||||
{ "daap.songcodectype", metaSongCodecType },
|
{ "daap.songcodectype", metaSongCodecType },
|
||||||
{ "daap.songcodecsubtype", metaSongCodecSubType },
|
{ "daap.songcodecsubtype", metaSongCodecSubType },
|
||||||
@ -261,9 +260,20 @@ static METAMAP db_metamap[] = {
|
|||||||
/* mt-daapd specific */
|
/* mt-daapd specific */
|
||||||
{ "org.mt-daapd.smart-playlist-spec", metaMPlaylistSpec },
|
{ "org.mt-daapd.smart-playlist-spec", metaMPlaylistSpec },
|
||||||
{ "org.mt-daapd.playlist-type", metaMPlaylistType },
|
{ "org.mt-daapd.playlist-type", metaMPlaylistType },
|
||||||
{ 0, 0 }
|
{ 0, 0 }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
char *db_error_list[] = {
|
||||||
|
"Success",
|
||||||
|
"Misc SQL Error: %s",
|
||||||
|
"Duplicate Playlist: %s",
|
||||||
|
"Missing playlist spec",
|
||||||
|
"Cannot add playlist items to a playlist of that type",
|
||||||
|
"No rows returned",
|
||||||
|
"Invalid playlist id: %d",
|
||||||
|
"Invalid song id: %d",
|
||||||
|
"Parse error"
|
||||||
|
};
|
||||||
|
|
||||||
/* Globals */
|
/* Globals */
|
||||||
static DB_FUNCTIONS *db_current=&db_functions[0]; /**< current database backend */
|
static DB_FUNCTIONS *db_current=&db_functions[0]; /**< current database backend */
|
||||||
@ -287,30 +297,30 @@ static void db_trim_string(char *string);
|
|||||||
* \param meta meta string variable from GET request
|
* \param meta meta string variable from GET request
|
||||||
*/
|
*/
|
||||||
MetaField_t db_encode_meta(char *meta) {
|
MetaField_t db_encode_meta(char *meta) {
|
||||||
MetaField_t bits = 0;
|
MetaField_t bits = 0;
|
||||||
char *start;
|
char *start;
|
||||||
char *end;
|
char *end;
|
||||||
METAMAP *m;
|
METAMAP *m;
|
||||||
|
|
||||||
for(start = meta ; *start ; start = end) {
|
for(start = meta ; *start ; start = end) {
|
||||||
int len;
|
int len;
|
||||||
|
|
||||||
if(0 == (end = strchr(start, ',')))
|
if(0 == (end = strchr(start, ',')))
|
||||||
end = start + strlen(start);
|
end = start + strlen(start);
|
||||||
|
|
||||||
len = end - start;
|
len = end - start;
|
||||||
|
|
||||||
if(*end != 0)
|
if(*end != 0)
|
||||||
end++;
|
end++;
|
||||||
|
|
||||||
for(m = db_metamap ; m->tag ; ++m)
|
for(m = db_metamap ; m->tag ; ++m)
|
||||||
if(!strncmp(m->tag, start, len))
|
if(!strncmp(m->tag, start, len))
|
||||||
break;
|
break;
|
||||||
|
|
||||||
if(m->tag)
|
if(m->tag)
|
||||||
bits |= (((MetaField_t) 1) << m->bit);
|
bits |= (((MetaField_t) 1) << m->bit);
|
||||||
else
|
else
|
||||||
DPRINTF(E_WARN,L_DAAP,"Unknown meta code: %.*s\n", len, start);
|
DPRINTF(E_WARN,L_DAAP,"Unknown meta code: %.*s\n", len, start);
|
||||||
}
|
}
|
||||||
|
|
||||||
DPRINTF(E_DBG, L_DAAP, "meta codes: %llu\n", bits);
|
DPRINTF(E_DBG, L_DAAP, "meta codes: %llu\n", bits);
|
||||||
@ -329,42 +339,58 @@ int db_wantsmeta(MetaField_t meta, MetaFieldName_t fieldNo) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* db_readlock
|
* db_readlock
|
||||||
*
|
*
|
||||||
* If this fails, something is so amazingly hosed, we might just as well
|
* If this fails, something is so amazingly hosed, we might just as well
|
||||||
* terminate.
|
* terminate.
|
||||||
*/
|
*/
|
||||||
void db_readlock(void) {
|
void db_readlock(void) {
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
if((err=pthread_rwlock_rdlock(&db_rwlock))) {
|
if((err=pthread_rwlock_rdlock(&db_rwlock))) {
|
||||||
DPRINTF(E_FATAL,L_DB,"cannot lock rdlock: %s\n",strerror(err));
|
DPRINTF(E_FATAL,L_DB,"cannot lock rdlock: %s\n",strerror(err));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* db_writelock
|
* db_writelock
|
||||||
*
|
*
|
||||||
* same as above
|
* same as above
|
||||||
*/
|
*/
|
||||||
void db_writelock(void) {
|
void db_writelock(void) {
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
if((err=pthread_rwlock_wrlock(&db_rwlock))) {
|
if((err=pthread_rwlock_wrlock(&db_rwlock))) {
|
||||||
DPRINTF(E_FATAL,L_DB,"cannot lock rwlock: %s\n",strerror(err));
|
DPRINTF(E_FATAL,L_DB,"cannot lock rwlock: %s\n",strerror(err));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* db_unlock
|
* db_unlock
|
||||||
*
|
*
|
||||||
* useless, but symmetrical
|
* useless, but symmetrical
|
||||||
*/
|
*/
|
||||||
int db_unlock(void) {
|
int db_unlock(void) {
|
||||||
return pthread_rwlock_unlock(&db_rwlock);
|
return pthread_rwlock_unlock(&db_rwlock);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build an error string
|
||||||
|
*/
|
||||||
|
void db_get_error(char **pe, int error, ...) {
|
||||||
|
va_list ap;
|
||||||
|
char errbuf[1024];
|
||||||
|
|
||||||
|
if(!pe)
|
||||||
|
return;
|
||||||
|
|
||||||
|
va_start(ap, error);
|
||||||
|
vsnprintf(errbuf, sizeof(errbuf), db_error_list[error], ap);
|
||||||
|
va_end(ap);
|
||||||
|
|
||||||
|
*pe = strdup(errbuf);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Must dynamically initialize the rwlock, as Mac OSX 10.3 (at least)
|
* Must dynamically initialize the rwlock, as Mac OSX 10.3 (at least)
|
||||||
@ -385,17 +411,17 @@ extern int db_set_backend(char *type) {
|
|||||||
DPRINTF(E_DBG,L_DB,"Setting backend database to %s\n",type);
|
DPRINTF(E_DBG,L_DB,"Setting backend database to %s\n",type);
|
||||||
|
|
||||||
if(!db_functions[0].name) {
|
if(!db_functions[0].name) {
|
||||||
DPRINTF(E_FATAL,L_DB,"No database backends are available. Install sqlite!\n");
|
DPRINTF(E_FATAL,L_DB,"No database backends are available. Install sqlite!\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
db_current=&db_functions[0];
|
db_current=&db_functions[0];
|
||||||
while((db_current->name) && (strcasecmp(db_current->name,type))) {
|
while((db_current->name) && (strcasecmp(db_current->name,type))) {
|
||||||
db_current++;
|
db_current++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!db_current->name) {
|
if(!db_current->name) {
|
||||||
DPRINTF(E_WARN,L_DB,"Could not find db backend %s. Aborting.\n",type);
|
DPRINTF(E_WARN,L_DB,"Could not find db backend %s. Aborting.\n",type);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
DPRINTF(E_DBG,L_DB,"Backend database set\n");
|
DPRINTF(E_DBG,L_DB,"Backend database set\n");
|
||||||
@ -409,15 +435,15 @@ extern int db_set_backend(char *type) {
|
|||||||
*
|
*
|
||||||
* \param parameters This is backend-specific (mysql, sqlite, etc)
|
* \param parameters This is backend-specific (mysql, sqlite, etc)
|
||||||
*/
|
*/
|
||||||
int db_open(char *parameters) {
|
int db_open(char **pe, char *parameters) {
|
||||||
int result;
|
int result;
|
||||||
|
|
||||||
DPRINTF(E_DBG,L_DB,"Opening database\n");
|
DPRINTF(E_DBG,L_DB,"Opening database\n");
|
||||||
|
|
||||||
if(pthread_once(&db_initlock,db_init_once))
|
if(pthread_once(&db_initlock,db_init_once))
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
result=db_current->dbs_open(parameters);
|
result=db_current->dbs_open(pe, parameters);
|
||||||
|
|
||||||
DPRINTF(E_DBG,L_DB,"Results: %d\n",result);
|
DPRINTF(E_DBG,L_DB,"Results: %d\n",result);
|
||||||
return result;
|
return result;
|
||||||
@ -463,13 +489,13 @@ int db_scanning(void) {
|
|||||||
/**
|
/**
|
||||||
* add (or update) a file
|
* add (or update) a file
|
||||||
*/
|
*/
|
||||||
int db_add(MP3FILE *pmp3) {
|
int db_add(char **pe, MP3FILE *pmp3) {
|
||||||
int retval;
|
int retval;
|
||||||
|
|
||||||
db_writelock();
|
db_writelock();
|
||||||
db_utf8_validate(pmp3);
|
db_utf8_validate(pmp3);
|
||||||
db_trim_strings(pmp3);
|
db_trim_strings(pmp3);
|
||||||
retval=db_current->dbs_add(pmp3);
|
retval=db_current->dbs_add(pe,pmp3);
|
||||||
db_revision_no++;
|
db_revision_no++;
|
||||||
db_unlock();
|
db_unlock();
|
||||||
|
|
||||||
@ -483,15 +509,15 @@ int db_add(MP3FILE *pmp3) {
|
|||||||
* \param type type of playlist to add: 0 - static, 1 - smart, 2 - m3u
|
* \param type type of playlist to add: 0 - static, 1 - smart, 2 - m3u
|
||||||
* \param clause where clause (if type 1)
|
* \param clause where clause (if type 1)
|
||||||
* \param playlistid returns the id of the playlist created
|
* \param playlistid returns the id of the playlist created
|
||||||
* \returns 0 on success, error code otherwise
|
* \returns 0 on success, error code otherwise
|
||||||
*/
|
*/
|
||||||
int db_add_playlist(char *name, int type, char *clause, char *path, int index, int *playlistid) {
|
int db_add_playlist(char **pe, char *name, int type, char *clause, char *path, int index, int *playlistid) {
|
||||||
int retval;
|
int retval;
|
||||||
|
|
||||||
db_writelock();
|
db_writelock();
|
||||||
retval=db_current->dbs_add_playlist(name,type,clause,path,index,playlistid);
|
retval=db_current->dbs_add_playlist(pe,name,type,clause,path,index,playlistid);
|
||||||
if(retval == DB_E_SUCCESS)
|
if(retval == DB_E_SUCCESS)
|
||||||
db_revision_no++;
|
db_revision_no++;
|
||||||
db_unlock();
|
db_unlock();
|
||||||
|
|
||||||
return retval;
|
return retval;
|
||||||
@ -504,13 +530,13 @@ int db_add_playlist(char *name, int type, char *clause, char *path, int index, i
|
|||||||
* \param songid song to add to playlist
|
* \param songid song to add to playlist
|
||||||
* \returns 0 on success, DB_E_ code otherwise
|
* \returns 0 on success, DB_E_ code otherwise
|
||||||
*/
|
*/
|
||||||
int db_add_playlist_item(int playlistid, int songid) {
|
int db_add_playlist_item(char **pe, int playlistid, int songid) {
|
||||||
int retval;
|
int retval;
|
||||||
|
|
||||||
db_writelock();
|
db_writelock();
|
||||||
retval=db_current->dbs_add_playlist_item(playlistid,songid);
|
retval=db_current->dbs_add_playlist_item(pe,playlistid,songid);
|
||||||
if(retval == DB_E_SUCCESS)
|
if(retval == DB_E_SUCCESS)
|
||||||
db_revision_no++;
|
db_revision_no++;
|
||||||
db_unlock();
|
db_unlock();
|
||||||
|
|
||||||
return retval;
|
return retval;
|
||||||
@ -520,15 +546,15 @@ int db_add_playlist_item(int playlistid, int songid) {
|
|||||||
* delete a playlist
|
* delete a playlist
|
||||||
*
|
*
|
||||||
* \param playlistid id of the playlist to delete
|
* \param playlistid id of the playlist to delete
|
||||||
* \returns 0 on success, error code otherwise
|
* \returns 0 on success, error code otherwise
|
||||||
*/
|
*/
|
||||||
int db_delete_playlist(int playlistid) {
|
int db_delete_playlist(char **pe, int playlistid) {
|
||||||
int retval;
|
int retval;
|
||||||
|
|
||||||
db_writelock();
|
db_writelock();
|
||||||
retval=db_current->dbs_delete_playlist(playlistid);
|
retval=db_current->dbs_delete_playlist(pe,playlistid);
|
||||||
if(retval == DB_E_SUCCESS)
|
if(retval == DB_E_SUCCESS)
|
||||||
db_revision_no++;
|
db_revision_no++;
|
||||||
db_unlock();
|
db_unlock();
|
||||||
|
|
||||||
return retval;
|
return retval;
|
||||||
@ -539,15 +565,15 @@ int db_delete_playlist(int playlistid) {
|
|||||||
*
|
*
|
||||||
* \param playlistid id of the playlist to delete
|
* \param playlistid id of the playlist to delete
|
||||||
* \param songid id of the song to delete
|
* \param songid id of the song to delete
|
||||||
* \returns 0 on success, error code otherwise
|
* \returns 0 on success, error code otherwise
|
||||||
*/
|
*/
|
||||||
int db_delete_playlist_item(int playlistid, int songid) {
|
int db_delete_playlist_item(char **pe, int playlistid, int songid) {
|
||||||
int retval;
|
int retval;
|
||||||
|
|
||||||
db_writelock();
|
db_writelock();
|
||||||
retval=db_current->dbs_delete_playlist_item(playlistid,songid);
|
retval=db_current->dbs_delete_playlist_item(pe,playlistid,songid);
|
||||||
if(retval == DB_E_SUCCESS)
|
if(retval == DB_E_SUCCESS)
|
||||||
db_revision_no++;
|
db_revision_no++;
|
||||||
db_unlock();
|
db_unlock();
|
||||||
|
|
||||||
return retval;
|
return retval;
|
||||||
@ -560,12 +586,12 @@ int db_delete_playlist_item(int playlistid, int songid) {
|
|||||||
* @param name new name of playlist
|
* @param name new name of playlist
|
||||||
* @param clause new where clause
|
* @param clause new where clause
|
||||||
*/
|
*/
|
||||||
int db_edit_playlist(int id, char *name, char *clause) {
|
int db_edit_playlist(char **pe, int id, char *name, char *clause) {
|
||||||
int retval;
|
int retval;
|
||||||
|
|
||||||
db_writelock();
|
db_writelock();
|
||||||
|
|
||||||
retval = db_current->dbs_edit_playlist(id, name, clause);
|
retval = db_current->dbs_edit_playlist(pe, id, name, clause);
|
||||||
db_unlock();
|
db_unlock();
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
@ -577,15 +603,15 @@ int db_edit_playlist(int id, char *name, char *clause) {
|
|||||||
* \param pinfo pointer to DBQUERYINFO struction
|
* \param pinfo pointer to DBQUERYINFO struction
|
||||||
* \returns 0 on success, -1 on failure
|
* \returns 0 on success, -1 on failure
|
||||||
*/
|
*/
|
||||||
int db_enum_start(DBQUERYINFO *pinfo) {
|
int db_enum_start(char **pe, DBQUERYINFO *pinfo) {
|
||||||
int retval;
|
int retval;
|
||||||
|
|
||||||
db_writelock();
|
db_writelock();
|
||||||
retval=db_current->dbs_enum_start(pinfo);
|
retval=db_current->dbs_enum_start(pe, pinfo);
|
||||||
|
|
||||||
if(retval) {
|
if(retval) {
|
||||||
db_unlock();
|
db_unlock();
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@ -596,8 +622,8 @@ int db_enum_start(DBQUERYINFO *pinfo) {
|
|||||||
* db_<dbase>_enum_reset, so it should be positioned at the head
|
* db_<dbase>_enum_reset, so it should be positioned at the head
|
||||||
* of the list of returned items.
|
* of the list of returned items.
|
||||||
*/
|
*/
|
||||||
int db_enum_size(DBQUERYINFO *pinfo, int *count) {
|
int db_enum_size(char **pe, DBQUERYINFO *pinfo, int *size, int *count) {
|
||||||
return db_current->dbs_enum_size(pinfo,count);
|
return db_current->dbs_enum_size(pe,pinfo,size,count);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -607,27 +633,29 @@ int db_enum_size(DBQUERYINFO *pinfo, int *count) {
|
|||||||
* the dmap item.
|
* the dmap item.
|
||||||
*
|
*
|
||||||
* \param plen length of the dmap item returned
|
* \param plen length of the dmap item returned
|
||||||
* \returns dmap item
|
* \returns dmap item
|
||||||
*/
|
*/
|
||||||
int db_enum_fetch(DBQUERYINFO *pinfo, unsigned char **pdmap) {
|
int db_enum_fetch(char **pe, DBQUERYINFO *pinfo, int *size,
|
||||||
return db_current->dbs_enum_fetch(pinfo,pdmap);
|
unsigned char **pdmap)
|
||||||
|
{
|
||||||
|
return db_current->dbs_enum_fetch(pe,pinfo,size,pdmap);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* reset the enum, without coming out the the db_writelock
|
* reset the enum, without coming out the the db_writelock
|
||||||
*/
|
*/
|
||||||
int db_enum_reset(DBQUERYINFO *pinfo) {
|
int db_enum_reset(char **pe, DBQUERYINFO *pinfo) {
|
||||||
return db_current->dbs_enum_reset(pinfo);
|
return db_current->dbs_enum_reset(pe,pinfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* finish the enumeration
|
* finish the enumeration
|
||||||
*/
|
*/
|
||||||
int db_enum_end(void) {
|
int db_enum_end(char **pe) {
|
||||||
int retval;
|
int retval;
|
||||||
|
|
||||||
retval=db_current->dbs_enum_end();
|
retval=db_current->dbs_enum_end(pe);
|
||||||
db_unlock();
|
db_unlock();
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
@ -638,32 +666,32 @@ int db_enum_end(void) {
|
|||||||
* mostly only by the web interface, and when streaming a song
|
* mostly only by the web interface, and when streaming a song
|
||||||
*
|
*
|
||||||
* \param id id of the item to get details for
|
* \param id id of the item to get details for
|
||||||
*/
|
*/
|
||||||
MP3FILE *db_fetch_item(int id) {
|
MP3FILE *db_fetch_item(char **pe, int id) {
|
||||||
MP3FILE *retval;
|
MP3FILE *retval;
|
||||||
|
|
||||||
db_readlock();
|
db_readlock();
|
||||||
retval=db_current->dbs_fetch_item(id);
|
retval=db_current->dbs_fetch_item(pe, id);
|
||||||
db_unlock();
|
db_unlock();
|
||||||
|
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
MP3FILE *db_fetch_path(char *path,int index) {
|
MP3FILE *db_fetch_path(char **pe, char *path,int index) {
|
||||||
MP3FILE *retval;
|
MP3FILE *retval;
|
||||||
|
|
||||||
db_readlock();
|
db_readlock();
|
||||||
retval=db_current->dbs_fetch_path(path, index);
|
retval=db_current->dbs_fetch_path(pe,path, index);
|
||||||
db_unlock();
|
db_unlock();
|
||||||
|
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
M3UFILE *db_fetch_playlist(char *path, int index) {
|
M3UFILE *db_fetch_playlist(char **pe, char *path, int index) {
|
||||||
M3UFILE *retval;
|
M3UFILE *retval;
|
||||||
|
|
||||||
db_readlock();
|
db_readlock();
|
||||||
retval=db_current->dbs_fetch_playlist(path,index);
|
retval=db_current->dbs_fetch_playlist(pe,path,index);
|
||||||
db_unlock();
|
db_unlock();
|
||||||
|
|
||||||
return retval;
|
return retval;
|
||||||
@ -676,7 +704,7 @@ int db_start_scan(void) {
|
|||||||
retval=db_current->dbs_start_scan();
|
retval=db_current->dbs_start_scan();
|
||||||
db_is_scanning=1;
|
db_is_scanning=1;
|
||||||
db_unlock();
|
db_unlock();
|
||||||
|
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -686,7 +714,7 @@ int db_end_song_scan(void) {
|
|||||||
db_writelock();
|
db_writelock();
|
||||||
retval=db_current->dbs_end_song_scan();
|
retval=db_current->dbs_end_song_scan();
|
||||||
db_unlock();
|
db_unlock();
|
||||||
|
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -697,10 +725,10 @@ int db_end_scan(void) {
|
|||||||
retval=db_current->dbs_end_scan();
|
retval=db_current->dbs_end_scan();
|
||||||
db_is_scanning=0;
|
db_is_scanning=0;
|
||||||
db_unlock();
|
db_unlock();
|
||||||
|
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
void db_dispose_item(MP3FILE *pmp3) {
|
void db_dispose_item(MP3FILE *pmp3) {
|
||||||
return db_current->dbs_dispose_item(pmp3);
|
return db_current->dbs_dispose_item(pmp3);
|
||||||
}
|
}
|
||||||
@ -709,26 +737,26 @@ void db_dispose_playlist(M3UFILE *pm3u) {
|
|||||||
return db_current->dbs_dispose_playlist(pm3u);
|
return db_current->dbs_dispose_playlist(pm3u);
|
||||||
}
|
}
|
||||||
|
|
||||||
int db_get_count(CountType_t type) {
|
int db_get_count(char **pe, int *count, CountType_t type) {
|
||||||
int retval;
|
int retval;
|
||||||
|
|
||||||
db_readlock();
|
db_readlock();
|
||||||
retval=db_current->dbs_get_count(type);
|
retval=db_current->dbs_get_count(pe,count,type);
|
||||||
db_unlock();
|
db_unlock();
|
||||||
|
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
|
/*
|
||||||
* FIXME: clearly a stub
|
* FIXME: clearly a stub
|
||||||
*/
|
*/
|
||||||
int db_get_song_count() {
|
int db_get_song_count(char **pe, int *count) {
|
||||||
return db_get_count(countSongs);
|
return db_get_count(pe, count, countSongs);
|
||||||
}
|
}
|
||||||
|
|
||||||
int db_get_playlist_count() {
|
int db_get_playlist_count(char **pe, int *count) {
|
||||||
return db_get_count(countPlaylists);
|
return db_get_count(pe, count, countPlaylists);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -799,7 +827,7 @@ int db_dmap_add_int(unsigned char *where, char *tag, int value) {
|
|||||||
where[9] = (value >> 16) & 0xFF;
|
where[9] = (value >> 16) & 0xFF;
|
||||||
where[10] = (value >> 8) & 0xFF;
|
where[10] = (value >> 8) & 0xFF;
|
||||||
where[11] = value & 0xFF;
|
where[11] = value & 0xFF;
|
||||||
|
|
||||||
return 12;
|
return 12;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -835,8 +863,8 @@ int db_dmap_add_string(unsigned char *where, char *tag, char *value) {
|
|||||||
* \param value what to put there
|
* \param value what to put there
|
||||||
* \param size how much data to cram in there
|
* \param size how much data to cram in there
|
||||||
*/
|
*/
|
||||||
int db_dmap_add_literal(unsigned char *where, char *tag,
|
int db_dmap_add_literal(unsigned char *where, char *tag,
|
||||||
char *value, int size) {
|
char *value, int size) {
|
||||||
/* tag */
|
/* tag */
|
||||||
memcpy(where,tag,4);
|
memcpy(where,tag,4);
|
||||||
|
|
||||||
@ -885,7 +913,7 @@ void db_utf8_validate(MP3FILE *pmp3) {
|
|||||||
int is_invalid=0;
|
int is_invalid=0;
|
||||||
|
|
||||||
/* we won't bother with path and fname... those were culled with the
|
/* we won't bother with path and fname... those were culled with the
|
||||||
* scan. Even if they are invalid (_could_ they be?), then we
|
* scan. Even if they are invalid (_could_ they be?), then we
|
||||||
* won't be able to open the file if we change them. Likewise,
|
* won't be able to open the file if we change them. Likewise,
|
||||||
* we won't do type or description, as these can't be bad, or they
|
* we won't do type or description, as these can't be bad, or they
|
||||||
* wouldn't have been scanned */
|
* wouldn't have been scanned */
|
||||||
@ -902,7 +930,7 @@ void db_utf8_validate(MP3FILE *pmp3) {
|
|||||||
is_invalid |= db_utf8_validate_string(pmp3->url);
|
is_invalid |= db_utf8_validate_string(pmp3->url);
|
||||||
|
|
||||||
if(is_invalid) {
|
if(is_invalid) {
|
||||||
DPRINTF(E_LOG,L_SCAN,"Invalid UTF-8 in %s\n",pmp3->path);
|
DPRINTF(E_LOG,L_SCAN,"Invalid UTF-8 in %s\n",pmp3->path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -919,38 +947,38 @@ int db_utf8_validate_string(char *string) {
|
|||||||
int retval=0;
|
int retval=0;
|
||||||
|
|
||||||
if(!string)
|
if(!string)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
while(*current) {
|
while(*current) {
|
||||||
if(!((*current) & 0x80)) {
|
if(!((*current) & 0x80)) {
|
||||||
current++;
|
current++;
|
||||||
} else {
|
} else {
|
||||||
run=0;
|
run=0;
|
||||||
|
|
||||||
/* it's a lead utf-8 character */
|
/* it's a lead utf-8 character */
|
||||||
if((*current & 0xE0) == 0xC0) run=1;
|
if((*current & 0xE0) == 0xC0) run=1;
|
||||||
if((*current & 0xF0) == 0xE0) run=2;
|
if((*current & 0xF0) == 0xE0) run=2;
|
||||||
if((*current & 0xF8) == 0xF0) run=3;
|
if((*current & 0xF8) == 0xF0) run=3;
|
||||||
|
|
||||||
if(!run) {
|
if(!run) {
|
||||||
/* high bit set, but invalid */
|
/* high bit set, but invalid */
|
||||||
*current++='?';
|
*current++='?';
|
||||||
retval=1;
|
retval=1;
|
||||||
} else {
|
} else {
|
||||||
r_current=0;
|
r_current=0;
|
||||||
while((r_current != run) && (*(current + r_current + 1)) &&
|
while((r_current != run) && (*(current + r_current + 1)) &&
|
||||||
((*(current + r_current + 1) & 0xC0) == 0x80)) {
|
((*(current + r_current + 1) & 0xC0) == 0x80)) {
|
||||||
r_current++;
|
r_current++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(r_current != run) {
|
if(r_current != run) {
|
||||||
*current++ = '?';
|
*current++ = '?';
|
||||||
retval=1;
|
retval=1;
|
||||||
} else {
|
} else {
|
||||||
current += (1 + run);
|
current += (1 + run);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return retval;
|
return retval;
|
||||||
@ -986,9 +1014,9 @@ void db_trim_strings(MP3FILE *pmp3) {
|
|||||||
*/
|
*/
|
||||||
void db_trim_string(char *string) {
|
void db_trim_string(char *string) {
|
||||||
if(!string)
|
if(!string)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
while(strlen(string) && (string[strlen(string) - 1] == ' '))
|
while(strlen(string) && (string[strlen(string) - 1] == ' '))
|
||||||
string[strlen(string) - 1] = '\0';
|
string[strlen(string) - 1] = '\0';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,7 +32,7 @@ typedef enum {
|
|||||||
metaPersistentId,
|
metaPersistentId,
|
||||||
metaContainerItemId,
|
metaContainerItemId,
|
||||||
metaParentContainerId,
|
metaParentContainerId,
|
||||||
|
|
||||||
firstTypeSpecificMetaId,
|
firstTypeSpecificMetaId,
|
||||||
|
|
||||||
// song meta data
|
// song meta data
|
||||||
@ -123,7 +123,7 @@ typedef struct tag_dbqueryinfo {
|
|||||||
} DBQUERYINFO;
|
} DBQUERYINFO;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
const char* tag;
|
const char* tag;
|
||||||
MetaFieldName_t bit;
|
MetaFieldName_t bit;
|
||||||
} METAMAP;
|
} METAMAP;
|
||||||
|
|
||||||
@ -137,34 +137,36 @@ extern DAAP_ITEMS taglist[];
|
|||||||
|
|
||||||
extern int db_set_backend(char *type);
|
extern int db_set_backend(char *type);
|
||||||
|
|
||||||
extern int db_open(char *parameters);
|
extern int db_open(char **pe, char *parameters);
|
||||||
extern int db_init(int reload);
|
extern int db_init(int reload);
|
||||||
extern int db_deinit(void);
|
extern int db_deinit(void);
|
||||||
|
|
||||||
extern int db_revision(void);
|
extern int db_revision(void);
|
||||||
|
|
||||||
extern int db_add(MP3FILE *pmp3);
|
extern int db_add(char **pe, MP3FILE *pmp3);
|
||||||
|
|
||||||
extern int db_enum_start(DBQUERYINFO *pinfo);
|
extern int db_enum_start(char **pe, DBQUERYINFO *pinfo);
|
||||||
extern int db_enum_size(DBQUERYINFO *pinfo, int *count);
|
extern int db_enum_size(char **pe, DBQUERYINFO *pinfo, int *count, int *total_size);
|
||||||
extern int db_enum_fetch(DBQUERYINFO *pinfo, unsigned char **pdmap);
|
extern int db_enum_fetch(char **pe, DBQUERYINFO *pinfo, int *size, unsigned char **pdmap);
|
||||||
extern int db_enum_reset(DBQUERYINFO *pinfo);
|
extern int db_enum_reset(char **pe, DBQUERYINFO *pinfo);
|
||||||
extern int db_enum_end(void);
|
extern int db_enum_end(char **pe);
|
||||||
extern int db_start_scan(void);
|
extern int db_start_scan(void);
|
||||||
extern int db_end_song_scan(void);
|
extern int db_end_song_scan(void);
|
||||||
extern int db_end_scan(void);
|
extern int db_end_scan(void);
|
||||||
extern int db_exists(char *path);
|
extern int db_exists(char *path);
|
||||||
extern int db_scanning(void);
|
extern int db_scanning(void);
|
||||||
|
|
||||||
extern int db_add_playlist(char *name, int type, char *clause, char *path, int index, int *playlistid);
|
extern int db_add_playlist(char **pe, char *name, int type, char *clause, char *path, int index, int *playlistid);
|
||||||
extern int db_add_playlist_item(int playlistid, int songid);
|
extern int db_add_playlist_item(char **pe, int playlistid, int songid);
|
||||||
extern int db_edit_playlist(int id, char *name, char *clause);
|
extern int db_edit_playlist(char **pe, int id, char *name, char *clause);
|
||||||
extern int db_delete_playlist(int playlistid);
|
extern int db_delete_playlist(char **pe, int playlistid);
|
||||||
extern int db_delete_playlist_item(int playlistid, int songid);
|
extern int db_delete_playlist_item(char **pe, int playlistid, int songid);
|
||||||
|
|
||||||
extern MP3FILE *db_fetch_item(int id);
|
extern void db_get_error(char **pe, int err, ...);
|
||||||
extern MP3FILE *db_fetch_path(char *path, int index);
|
|
||||||
extern M3UFILE *db_fetch_playlist(char *path, int index);
|
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 */
|
/* metatag parsing */
|
||||||
@ -182,8 +184,9 @@ extern int db_dmap_add_container(unsigned char *where, char *tag, int size);
|
|||||||
/* Holdover functions from old db interface...
|
/* Holdover functions from old db interface...
|
||||||
* should these be removed? Refactored?
|
* should these be removed? Refactored?
|
||||||
*/
|
*/
|
||||||
extern int db_get_song_count(void);
|
|
||||||
extern int db_get_playlist_count(void);
|
extern int db_get_song_count(char **pe, int *count);
|
||||||
|
extern int db_get_playlist_count(char **pe, int *count);
|
||||||
extern void db_dispose_item(MP3FILE *pmp3);
|
extern void db_dispose_item(MP3FILE *pmp3);
|
||||||
extern void db_dispose_playlist(M3UFILE *pm3u);
|
extern void db_dispose_playlist(M3UFILE *pm3u);
|
||||||
|
|
||||||
|
456
src/db-sql-sqlite2.c
Normal file
456
src/db-sql-sqlite2.c
Normal file
@ -0,0 +1,456 @@
|
|||||||
|
/*
|
||||||
|
* $Id$
|
||||||
|
* sqlite2-specific db implementation
|
||||||
|
*
|
||||||
|
* Copyright (C) 2005 Ron Pedde (ron@pedde.com)
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file handles sqlite2 databases. SQLite2 databases
|
||||||
|
* should have a dsn of:
|
||||||
|
*
|
||||||
|
* sqlite2:/path/to/folder
|
||||||
|
*
|
||||||
|
* The actual db will be appended to the passed path.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
# include "config.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define _XOPEN_SOURCE 500
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <limits.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sqlite.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "err.h"
|
||||||
|
#include "db-generic.h"
|
||||||
|
#include "db-sql.h"
|
||||||
|
#include "db-sql-sqlite2.h"
|
||||||
|
|
||||||
|
#ifndef TRUE
|
||||||
|
# define TRUE 1
|
||||||
|
# define FALSE 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/* Globals */
|
||||||
|
static sqlite *db_sqlite2_songs; /**< Database that holds the mp3 info */
|
||||||
|
static pthread_mutex_t db_sqlite2_mutex = PTHREAD_MUTEX_INITIALIZER; /**< sqlite not reentrant */
|
||||||
|
static sqlite_vm *db_sqlite2_pvm;
|
||||||
|
static int db_sqlite2_reload=0;
|
||||||
|
static char *db_sqlite2_enum_query;
|
||||||
|
static int db_sqlite2_in_enum=0;
|
||||||
|
|
||||||
|
static char db_sqlite2_path[PATH_MAX + 1];
|
||||||
|
|
||||||
|
#define DB_SQLITE2_VERSION 8
|
||||||
|
|
||||||
|
|
||||||
|
/* Forwards */
|
||||||
|
void db_sqlite2_lock(void);
|
||||||
|
void db_sqlite2_unlock(void);
|
||||||
|
extern char *db_initial;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* lock the db_mutex
|
||||||
|
*/
|
||||||
|
void db_sqlite2_lock(void) {
|
||||||
|
int err;
|
||||||
|
|
||||||
|
if((err=pthread_mutex_lock(&db_sqlite2_mutex))) {
|
||||||
|
DPRINTF(E_FATAL,L_DB,"cannot lock sqlite lock: %s\n",strerror(err));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* unlock the db_mutex
|
||||||
|
*/
|
||||||
|
void db_sqlite2_unlock(void) {
|
||||||
|
int err;
|
||||||
|
|
||||||
|
if((err=pthread_mutex_unlock(&db_sqlite2_mutex))) {
|
||||||
|
DPRINTF(E_FATAL,L_DB,"cannot unlock sqlite2 lock: %s\n",strerror(err));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
char *db_sqlite2_vmquery(char *fmt,va_list ap) {
|
||||||
|
return sqlite_vmprintf(fmt,ap);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void db_sqlite2_vmfree(char *query) {
|
||||||
|
sqlite_freemem(query);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* open a sqlite2 database
|
||||||
|
*
|
||||||
|
* @param dsn the full dns to the database
|
||||||
|
* (sqlite2:/path/to/database)
|
||||||
|
*
|
||||||
|
* @returns DB_E_SUCCESS on success
|
||||||
|
*/
|
||||||
|
int db_sqlite2_open(char **pe, char *dsn) {
|
||||||
|
char *perr;
|
||||||
|
int ver;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
snprintf(db_sqlite2_path,sizeof(db_sqlite2_path),"%s/songs.db",dsn);
|
||||||
|
|
||||||
|
db_sqlite2_lock();
|
||||||
|
db_sqlite2_songs=sqlite_open(db_sqlite2_path,0666,&perr);
|
||||||
|
if(!db_sqlite2_songs) {
|
||||||
|
db_get_error(pe,DB_E_SQL_ERROR,perr);
|
||||||
|
DPRINTF(E_LOG,L_DB,"db_sqlite2_open: %s (%s)\n",perr,
|
||||||
|
db_sqlite2_path);
|
||||||
|
sqlite_freemem(perr);
|
||||||
|
db_sqlite2_unlock();
|
||||||
|
return DB_E_SQL_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
sqlite_busy_timeout(db_sqlite2_songs,30000); /* 30 seconds */
|
||||||
|
|
||||||
|
err = db_sql_fetch_int(pe,&ver,"select value from config where term='version'");
|
||||||
|
if(err != DB_E_SUCCESS) {
|
||||||
|
free(*pe);
|
||||||
|
/* create the table */
|
||||||
|
DPRINTF(E_FATAL,L_DB,"Can't create table yet!\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if(ver != DB_SQLITE2_VERSION) {
|
||||||
|
DPRINTF(E_FATAL,L_DB,"Can't upgrade database!\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
db_sqlite2_unlock();
|
||||||
|
return DB_E_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* close the database
|
||||||
|
*/
|
||||||
|
int db_sqlite2_close(void) {
|
||||||
|
db_sqlite2_lock();
|
||||||
|
sqlite_close(db_sqlite2_songs);
|
||||||
|
db_sqlite2_unlock();
|
||||||
|
return DB_E_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* execute a throwaway query against the database, disregarding
|
||||||
|
* the outcome
|
||||||
|
*
|
||||||
|
* @param pe db error structure
|
||||||
|
* @param loglevel error level to return if the query fails
|
||||||
|
* @param fmt sprintf-style arguements
|
||||||
|
*
|
||||||
|
* @returns DB_E_SUCCESS on success
|
||||||
|
*/
|
||||||
|
int db_sqlite2_exec(char **pe, int loglevel, char *fmt, ...) {
|
||||||
|
va_list ap;
|
||||||
|
char *query;
|
||||||
|
int err;
|
||||||
|
char *perr;
|
||||||
|
|
||||||
|
va_start(ap,fmt);
|
||||||
|
query=sqlite_vmprintf(fmt,ap);
|
||||||
|
va_end(ap);
|
||||||
|
|
||||||
|
DPRINTF(E_DBG,L_DB,"Executing: %s\n",query);
|
||||||
|
|
||||||
|
db_sqlite2_lock();
|
||||||
|
err=sqlite_exec(db_sqlite2_songs,query,NULL,NULL,&perr);
|
||||||
|
if(err != SQLITE_OK) {
|
||||||
|
db_get_error(pe,DB_E_SQL_ERROR,perr);
|
||||||
|
|
||||||
|
DPRINTF(loglevel == E_FATAL ? E_LOG : loglevel,L_DB,"Query: %s\n",
|
||||||
|
query);
|
||||||
|
DPRINTF(loglevel,L_DB,"Error: %s\n",perr);
|
||||||
|
sqlite_freemem(perr);
|
||||||
|
} else {
|
||||||
|
DPRINTF(E_DBG,L_DB,"Rows: %d\n",sqlite_changes(db_sqlite2_songs));
|
||||||
|
}
|
||||||
|
sqlite_freemem(query);
|
||||||
|
|
||||||
|
db_sqlite2_unlock();
|
||||||
|
|
||||||
|
if(err != SQLITE_OK)
|
||||||
|
return DB_E_SQL_ERROR;
|
||||||
|
return DB_E_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* start enumerating rows in a select
|
||||||
|
*/
|
||||||
|
int db_sqlite2_enum_begin(char **pe, char *fmt, ...) {
|
||||||
|
va_list ap;
|
||||||
|
int err;
|
||||||
|
char *perr;
|
||||||
|
const char *ptail;
|
||||||
|
|
||||||
|
if(!db_sqlite2_in_enum) {
|
||||||
|
va_start(ap, fmt);
|
||||||
|
db_sqlite2_lock();
|
||||||
|
db_sqlite2_enum_query = sqlite_vmprintf(fmt,ap);
|
||||||
|
va_end(ap);
|
||||||
|
}
|
||||||
|
|
||||||
|
DPRINTF(E_DBG,L_DB,"Executing :%s\n",db_sqlite2_enum_query);
|
||||||
|
db_sqlite2_in_enum=1;
|
||||||
|
|
||||||
|
err=sqlite_compile(db_sqlite2_songs,db_sqlite2_enum_query,&ptail,&db_sqlite2_pvm,&perr);
|
||||||
|
|
||||||
|
if(err != SQLITE_OK) {
|
||||||
|
db_get_error(pe,DB_E_SQL_ERROR,perr);
|
||||||
|
sqlite_freemem(perr);
|
||||||
|
db_sqlite2_in_enum=0;
|
||||||
|
db_sqlite2_unlock();
|
||||||
|
sqlite_freemem(db_sqlite2_enum_query);
|
||||||
|
return DB_E_SQL_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* otherwise, we leave the db locked while we walk through the enums */
|
||||||
|
return DB_E_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* fetch the next row
|
||||||
|
*
|
||||||
|
* @param pe error string, if result isn't DB_E_SUCCESS
|
||||||
|
* @param pr pointer to a row struct
|
||||||
|
*
|
||||||
|
* @returns DB_E_SUCCESS with *pr=NULL when end of table,
|
||||||
|
* DB_E_SUCCESS with a valid row when more data,
|
||||||
|
* DB_E_* on error
|
||||||
|
*/
|
||||||
|
int db_sqlite2_enum_fetch(char **pe, SQL_ROW *pr) {
|
||||||
|
int err;
|
||||||
|
char *perr=NULL;;
|
||||||
|
const char **colarray;
|
||||||
|
int cols;
|
||||||
|
int counter=10;
|
||||||
|
|
||||||
|
while(counter--) {
|
||||||
|
err=sqlite_step(db_sqlite2_pvm,&cols,(const char ***)pr,&colarray);
|
||||||
|
if(err != SQLITE_BUSY)
|
||||||
|
break;
|
||||||
|
usleep(100);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(err == SQLITE_DONE) {
|
||||||
|
*pr = NULL;
|
||||||
|
return DB_E_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(err == SQLITE_ROW) {
|
||||||
|
return DB_E_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
db_get_error(pe,DB_E_SQL_ERROR,perr);
|
||||||
|
return DB_E_SQL_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* end the db enumeration
|
||||||
|
*/
|
||||||
|
int db_sqlite2_enum_end(char **pe) {
|
||||||
|
int err;
|
||||||
|
char *perr;
|
||||||
|
|
||||||
|
db_sqlite2_in_enum=0;
|
||||||
|
sqlite_freemem(db_sqlite2_enum_query);
|
||||||
|
|
||||||
|
err = sqlite_finalize(db_sqlite2_pvm,&perr);
|
||||||
|
if(err != SQLITE_OK) {
|
||||||
|
db_get_error(pe,DB_E_SQL_ERROR,perr);
|
||||||
|
sqlite_freemem(perr);
|
||||||
|
db_sqlite2_unlock();
|
||||||
|
return DB_E_SQL_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
db_sqlite2_unlock();
|
||||||
|
return DB_E_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* restart the enumeration
|
||||||
|
*/
|
||||||
|
int db_sqlite2_enum_restart(char **pe) {
|
||||||
|
return db_sqlite2_enum_begin(pe,NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int db_sqlite2_event(int event_type) {
|
||||||
|
switch(event_type) {
|
||||||
|
|
||||||
|
case DB_SQL_EVENT_STARTUP: /* this is a startup with existing songs */
|
||||||
|
db_sqlite2_exec(NULL,E_FATAL,"vacuum");
|
||||||
|
db_sqlite2_reload=0;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DB_SQL_EVENT_FULLRELOAD: /* either a fresh load or force load */
|
||||||
|
db_sqlite2_exec(NULL,E_DBG,"delete index idx_path");
|
||||||
|
db_sqlite2_exec(NULL,E_DBG,"delete index idx_songid");
|
||||||
|
db_sqlite2_exec(NULL,E_DBG,"delete index idx_playlistid");
|
||||||
|
|
||||||
|
db_sqlite2_exec(NULL,E_DBG,"drop table songs");
|
||||||
|
db_sqlite2_exec(NULL,E_DBG,"drop table playlists");
|
||||||
|
db_sqlite2_exec(NULL,E_DBG,"drop table playlistitems");
|
||||||
|
db_sqlite2_exec(NULL,E_DBG,"drop table config");
|
||||||
|
|
||||||
|
db_sqlite2_exec(NULL,E_DBG,"vacuum");
|
||||||
|
|
||||||
|
db_sqlite2_exec(NULL,E_DBG,db_initial);
|
||||||
|
db_sqlite2_reload=1;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DB_SQL_EVENT_SONGSCANSTART:
|
||||||
|
if(db_sqlite2_reload) {
|
||||||
|
db_sqlite2_exec(NULL,E_FATAL,"pragma synchronous = off");
|
||||||
|
db_sqlite2_exec(NULL,E_FATAL,"begin transaction");
|
||||||
|
} else {
|
||||||
|
db_sqlite2_exec(NULL,E_DBG,"drop table updated");
|
||||||
|
db_sqlite2_exec(NULL,E_FATAL,"create temp table updated (id int)");
|
||||||
|
db_sqlite2_exec(NULL,E_DBG,"drop table plupdated");
|
||||||
|
db_sqlite2_exec(NULL,E_FATAL,"create temp table plupdated(id int)");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DB_SQL_EVENT_SONGSCANEND:
|
||||||
|
if(db_sqlite2_reload) {
|
||||||
|
db_sqlite2_exec(NULL,E_FATAL,"commit transaction");
|
||||||
|
db_sqlite2_exec(NULL,E_FATAL,"create index idx_path on songs(path)");
|
||||||
|
db_sqlite2_exec(NULL,E_DBG,"delete from config where term='rescan'");
|
||||||
|
} else {
|
||||||
|
db_sqlite2_exec(NULL,E_FATAL,"delete from songs where id not in (select id from updated)");
|
||||||
|
db_sqlite2_exec(NULL,E_FATAL,"update songs set force_update=0");
|
||||||
|
db_sqlite2_exec(NULL,E_FATAL,"drop table updated");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DB_SQL_EVENT_PLSCANSTART:
|
||||||
|
db_sqlite2_exec(NULL,E_FATAL,"begin transaction");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DB_SQL_EVENT_PLSCANEND:
|
||||||
|
db_sqlite2_exec(NULL,E_FATAL,"end transaction");
|
||||||
|
|
||||||
|
if(db_sqlite2_reload) {
|
||||||
|
db_sqlite2_exec(NULL,E_FATAL,"pragma synchronous=normal");
|
||||||
|
db_sqlite2_exec(NULL,E_FATAL,"create index idx_songid on playlistitems(songid)");
|
||||||
|
db_sqlite2_exec(NULL,E_FATAL,"create index idx_playlistid on playlistitems(playlistid)");
|
||||||
|
|
||||||
|
} else {
|
||||||
|
db_sqlite2_exec(NULL,E_FATAL,"delete from playlists where "
|
||||||
|
"((type=%d) OR (type=%d)) and "
|
||||||
|
"id not in (select id from plupdated)",
|
||||||
|
PL_STATICFILE,PL_STATICXML);
|
||||||
|
db_sqlite2_exec(NULL,E_FATAL,"delete from playlistitems where "
|
||||||
|
"playlistid not in (select distinct "
|
||||||
|
"id from playlists)");
|
||||||
|
db_sqlite2_exec(NULL,E_FATAL,"drop table plupdated");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return DB_E_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *db_initial =
|
||||||
|
"create table songs (\n"
|
||||||
|
" id INTEGER PRIMARY KEY NOT NULL,\n"
|
||||||
|
" path VARCHAR(4096) UNIQUE NOT NULL,\n"
|
||||||
|
" fname VARCHAR(255) NOT NULL,\n"
|
||||||
|
" title VARCHAR(1024) DEFAULT NULL,\n"
|
||||||
|
" artist VARCHAR(1024) DEFAULT NULL,\n"
|
||||||
|
" album VARCHAR(1024) DEFAULT NULL,\n"
|
||||||
|
" genre VARCHAR(255) DEFAULT NULL,\n"
|
||||||
|
" comment VARCHAR(4096) DEFAULT NULL,\n"
|
||||||
|
" type VARCHAR(255) DEFAULT NULL,\n"
|
||||||
|
" composer VARCHAR(1024) DEFAULT NULL,\n"
|
||||||
|
" orchestra VARCHAR(1024) DEFAULT NULL,\n"
|
||||||
|
" conductor VARCHAR(1024) DEFAULT NULL,\n"
|
||||||
|
" grouping VARCHAR(1024) DEFAULT NULL,\n"
|
||||||
|
" url VARCHAR(1024) DEFAULT NULL,\n"
|
||||||
|
" bitrate INTEGER DEFAULT 0,\n"
|
||||||
|
" samplerate INTEGER DEFAULT 0,\n"
|
||||||
|
" song_length INTEGER DEFAULT 0,\n"
|
||||||
|
" file_size INTEGER DEFAULT 0,\n"
|
||||||
|
" year INTEGER DEFAULT 0,\n"
|
||||||
|
" track INTEGER DEFAULT 0,\n"
|
||||||
|
" total_tracks INTEGER DEFAULT 0,\n"
|
||||||
|
" disc INTEGER DEFAULT 0,\n"
|
||||||
|
" total_discs INTEGER DEFAULT 0,\n"
|
||||||
|
" bpm INTEGER DEFAULT 0,\n"
|
||||||
|
" compilation INTEGER DEFAULT 0,\n"
|
||||||
|
" rating INTEGER DEFAULT 0,\n"
|
||||||
|
" play_count INTEGER DEFAULT 0,\n"
|
||||||
|
" data_kind INTEGER DEFAULT 0,\n"
|
||||||
|
" item_kind INTEGER DEFAULT 0,\n"
|
||||||
|
" description INTEGER DEFAULT 0,\n"
|
||||||
|
" time_added INTEGER DEFAULT 0,\n"
|
||||||
|
" time_modified INTEGER DEFAULT 0,\n"
|
||||||
|
" time_played INTEGER DEFAULT 0,\n"
|
||||||
|
" db_timestamp INTEGER DEFAULT 0,\n"
|
||||||
|
" disabled INTEGER DEFAULT 0,\n"
|
||||||
|
" sample_count INTEGER DEFAULT 0,\n"
|
||||||
|
" force_update INTEGER DEFAULT 0,\n"
|
||||||
|
" codectype VARCHAR(5) DEFAULT NULL,\n"
|
||||||
|
" idx INTEGER NOT NULL\n"
|
||||||
|
");\n"
|
||||||
|
"create table config (\n"
|
||||||
|
" term VARCHAR(255) NOT NULL,\n"
|
||||||
|
" subterm VARCHAR(255) DEFAULT NULL,\n"
|
||||||
|
" value VARCHAR(1024) NOT NULL\n"
|
||||||
|
");\n"
|
||||||
|
"create table playlistitems (\n"
|
||||||
|
" id INTEGER PRIMARY KEY NOT NULL,\n"
|
||||||
|
" playlistid INTEGER NOT NULL,\n"
|
||||||
|
" songid INTEGER NOT NULL\n"
|
||||||
|
");\n"
|
||||||
|
"create table playlists (\n"
|
||||||
|
" id INTEGER PRIMARY KEY NOT NULL,\n"
|
||||||
|
" title VARCHAR(255) NOT NULL,\n"
|
||||||
|
" type INTEGER NOT NULL,\n"
|
||||||
|
" items INTEGER NOT NULL,\n"
|
||||||
|
" query VARCHAR(1024),\n"
|
||||||
|
" db_timestamp INTEGER NOT NULL,\n"
|
||||||
|
" path VARCHAR(4096),\n"
|
||||||
|
" idx INTEGER NOT NULL\n"
|
||||||
|
");\n"
|
||||||
|
"insert into config values ('version','','1');\n"
|
||||||
|
"insert into playlists values (1,'Library',1,0,'1',0,'',0);\n"
|
||||||
|
"create index idx_path on songs(path);\n"
|
||||||
|
"create index idx_songid on playlistitems(songid);\n"
|
||||||
|
"create index idx_playlistid on playlistitems(playlistid);\n";
|
||||||
|
|
||||||
|
|
42
src/db-sql-sqlite2.h
Normal file
42
src/db-sql-sqlite2.h
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
/*
|
||||||
|
* $Id$
|
||||||
|
* sqlite2-specific db implementation
|
||||||
|
*
|
||||||
|
* Copyright (C) 2005 Ron Pedde (ron@pedde.com)
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _DB_SQL_SQLITE2_
|
||||||
|
#define _DB_SQL_SQLITE2_
|
||||||
|
|
||||||
|
extern int db_sqlite2_open(char **pe, char *dsn);
|
||||||
|
extern int db_sqlite2_close(void);
|
||||||
|
|
||||||
|
/* simple utility functions */
|
||||||
|
extern int db_sqlite2_exec(char **pe, int loglevel, char *fmt, ...);
|
||||||
|
extern char *db_sqlite2_vmquery(char *fmt,va_list ap);
|
||||||
|
extern void db_sqlite2_vmfree(char *query);
|
||||||
|
|
||||||
|
/* walk through a table */
|
||||||
|
extern int db_sqlite2_enum_begin(char **pe, char *fmt, ...);
|
||||||
|
extern int db_sqlite2_enum_fetch(char **pe, SQL_ROW *pr);
|
||||||
|
extern int db_sqlite2_enum_end(char **pe);
|
||||||
|
extern int db_sqlite2_enum_restart(char **pe);
|
||||||
|
|
||||||
|
int db_sqlite2_event(int event_type);
|
||||||
|
|
||||||
|
#endif /* _DB_SQL_SQLITE2_ */
|
||||||
|
|
1636
src/db-sql.c
Normal file
1636
src/db-sql.c
Normal file
File diff suppressed because it is too large
Load Diff
114
src/db-sql.h
Normal file
114
src/db-sql.h
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
/*
|
||||||
|
* $Id$
|
||||||
|
* sql-specific db implementation
|
||||||
|
*
|
||||||
|
* Copyright (C) 2005 Ron Pedde (ron@pedde.com)
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _DB_SQL_H_
|
||||||
|
#define _DB_SQL_H_
|
||||||
|
|
||||||
|
typedef char** SQL_ROW;
|
||||||
|
|
||||||
|
extern int db_sql_open(char **pe, char *parameters);
|
||||||
|
extern int db_sql_init(int reload);
|
||||||
|
extern int db_sql_deinit(void);
|
||||||
|
extern int db_sql_add(char **pe, MP3FILE *pmp3);
|
||||||
|
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_reset(char **pe, DBQUERYINFO *pinfo);
|
||||||
|
extern int db_sql_enum_end(char **pe);
|
||||||
|
extern int db_sql_start_scan(void);
|
||||||
|
extern int db_sql_end_song_scan(void);
|
||||||
|
extern int db_sql_end_scan(void);
|
||||||
|
extern int db_sql_get_count(char **pe, int *count, CountType_t type);
|
||||||
|
extern MP3FILE *db_sql_fetch_item(char **pe, int id);
|
||||||
|
extern MP3FILE *db_sql_fetch_path(char **pe, char *path,int index);
|
||||||
|
extern M3UFILE *db_sql_fetch_playlist(char **pe, char *path, int index);
|
||||||
|
extern void db_sql_dispose_item(MP3FILE *pmp3);
|
||||||
|
extern void db_sql_dispose_playlist(M3UFILE *pm3u);
|
||||||
|
extern int db_sql_add_playlist(char **pe, char *name, int type, char *clause, char *path, int index, int *playlistid);
|
||||||
|
extern int db_sql_add_playlist_item(char **pe, int playlistid, int songid);
|
||||||
|
extern int db_sql_edit_playlist(char **pe, int id, char *name, char *clause);
|
||||||
|
extern int db_sql_delete_playlist(char **pe, int playlistid);
|
||||||
|
extern int db_sql_delete_playlist_item(char **pe, int playlistid, int songid);
|
||||||
|
|
||||||
|
extern int db_sql_fetch_row(char **pe, SQL_ROW *row, char *fmt, ...);
|
||||||
|
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,
|
||||||
|
songFname,
|
||||||
|
songTitle,
|
||||||
|
songArtist,
|
||||||
|
songAlbum,
|
||||||
|
songGenre,
|
||||||
|
songComment,
|
||||||
|
songType,
|
||||||
|
songComposer,
|
||||||
|
songOrchestra,
|
||||||
|
songGrouping,
|
||||||
|
songURL,
|
||||||
|
songBitrate,
|
||||||
|
songSampleRate,
|
||||||
|
songLength,
|
||||||
|
songFilesize,
|
||||||
|
songYear,
|
||||||
|
songTrack,
|
||||||
|
songTotalTracks,
|
||||||
|
songDisc,
|
||||||
|
songTotalDiscs,
|
||||||
|
songBPM,
|
||||||
|
songCompilation,
|
||||||
|
songRating,
|
||||||
|
songPlayCount,
|
||||||
|
songDataKind,
|
||||||
|
songItemKind,
|
||||||
|
songDescription,
|
||||||
|
songTimeAdded,
|
||||||
|
songTimeModified,
|
||||||
|
songTimePlayed,
|
||||||
|
songDBTimestamp,
|
||||||
|
songDisabled,
|
||||||
|
songSampleCount,
|
||||||
|
songForceUpdate,
|
||||||
|
songCodecType
|
||||||
|
} SongField_t;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
plID,
|
||||||
|
plTitle,
|
||||||
|
plType,
|
||||||
|
plItems,
|
||||||
|
plQuery,
|
||||||
|
plDBTimestamp,
|
||||||
|
plPath
|
||||||
|
} PlaylistField_t;
|
||||||
|
|
||||||
|
#define DB_SQL_EVENT_STARTUP 0
|
||||||
|
#define DB_SQL_EVENT_SONGSCANSTART 1
|
||||||
|
#define DB_SQL_EVENT_SONGSCANEND 2
|
||||||
|
#define DB_SQL_EVENT_PLSCANSTART 3
|
||||||
|
#define DB_SQL_EVENT_PLSCANEND 4
|
||||||
|
#define DB_SQL_EVENT_FULLRELOAD 5
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* _DB_SQL_H_ */
|
1935
src/dbs-sqlite.c
1935
src/dbs-sqlite.c
File diff suppressed because it is too large
Load Diff
100
src/dbs-sqlite.h
100
src/dbs-sqlite.h
@ -1,100 +0,0 @@
|
|||||||
/*
|
|
||||||
* $Id$
|
|
||||||
* sqlite-specific db implementation
|
|
||||||
*
|
|
||||||
* Copyright (C) 2005 Ron Pedde (ron@pedde.com)
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation; either version 2 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef _DBS_SQLITE_H_
|
|
||||||
#define _DBS_SQLITE_H_
|
|
||||||
|
|
||||||
extern int db_sqlite_open(char *parameters);
|
|
||||||
extern int db_sqlite_init(int reload);
|
|
||||||
extern int db_sqlite_deinit(void);
|
|
||||||
extern int db_sqlite_add(MP3FILE *pmp3);
|
|
||||||
extern int db_sqlite_enum_start(DBQUERYINFO *pinfo);
|
|
||||||
extern int db_sqlite_enum_size(DBQUERYINFO *pinfo, int *count);
|
|
||||||
extern int db_sqlite_enum_fetch(DBQUERYINFO *pinfo, unsigned char **pdmap);
|
|
||||||
extern int db_sqlite_enum_reset(DBQUERYINFO *pinfo);
|
|
||||||
extern int db_sqlite_enum_end(void);
|
|
||||||
extern int db_sqlite_start_scan(void);
|
|
||||||
extern int db_sqlite_end_song_scan(void);
|
|
||||||
extern int db_sqlite_end_scan(void);
|
|
||||||
extern int db_sqlite_get_count(CountType_t type);
|
|
||||||
extern MP3FILE *db_sqlite_fetch_item(int id);
|
|
||||||
extern MP3FILE *db_sqlite_fetch_path(char *path,int index);
|
|
||||||
extern M3UFILE *db_sqlite_fetch_playlist(char *path, int index);
|
|
||||||
extern void db_sqlite_dispose_item(MP3FILE *pmp3);
|
|
||||||
extern void db_sqlite_dispose_playlist(M3UFILE *pm3u);
|
|
||||||
extern int db_sqlite_add_playlist(char *name, int type, char *clause, char *path, int index, int *playlistid);
|
|
||||||
extern int db_sqlite_add_playlist_item(int playlistid, int songid);
|
|
||||||
extern int db_sqlite_edit_playlist(int id, char *name, char *clause);
|
|
||||||
extern int db_sqlite_delete_playlist(int playlistid);
|
|
||||||
extern int db_sqlite_delete_playlist_item(int playlistid, int songid);
|
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
songID,
|
|
||||||
songPath,
|
|
||||||
songFname,
|
|
||||||
songTitle,
|
|
||||||
songArtist,
|
|
||||||
songAlbum,
|
|
||||||
songGenre,
|
|
||||||
songComment,
|
|
||||||
songType,
|
|
||||||
songComposer,
|
|
||||||
songOrchestra,
|
|
||||||
songGrouping,
|
|
||||||
songURL,
|
|
||||||
songBitrate,
|
|
||||||
songSampleRate,
|
|
||||||
songLength,
|
|
||||||
songFilesize,
|
|
||||||
songYear,
|
|
||||||
songTrack,
|
|
||||||
songTotalTracks,
|
|
||||||
songDisc,
|
|
||||||
songTotalDiscs,
|
|
||||||
songBPM,
|
|
||||||
songCompilation,
|
|
||||||
songRating,
|
|
||||||
songPlayCount,
|
|
||||||
songDataKind,
|
|
||||||
songItemKind,
|
|
||||||
songDescription,
|
|
||||||
songTimeAdded,
|
|
||||||
songTimeModified,
|
|
||||||
songTimePlayed,
|
|
||||||
songDBTimestamp,
|
|
||||||
songDisabled,
|
|
||||||
songSampleCount,
|
|
||||||
songForceUpdate,
|
|
||||||
songCodecType
|
|
||||||
} SongField_t;
|
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
plID,
|
|
||||||
plTitle,
|
|
||||||
plType,
|
|
||||||
plItems,
|
|
||||||
plQuery,
|
|
||||||
plDBTimestamp,
|
|
||||||
plPath
|
|
||||||
} PlaylistField_t;
|
|
||||||
|
|
||||||
|
|
||||||
#endif /* _DBS_SQLITE_H_ */
|
|
199
src/dispatch.c
199
src/dispatch.c
@ -70,7 +70,7 @@ static char *dispatch_xml_encode(char *original, int len);
|
|||||||
static int dispatch_output_xml_write(WS_CONNINFO *pwsc, DBQUERYINFO *pqi, unsigned char *block, int len);
|
static int dispatch_output_xml_write(WS_CONNINFO *pwsc, DBQUERYINFO *pqi, unsigned char *block, int len);
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hold the inf for the output serializer
|
* Hold the inf for the output serializer
|
||||||
*/
|
*/
|
||||||
typedef struct tag_xml_stack {
|
typedef struct tag_xml_stack {
|
||||||
@ -90,11 +90,11 @@ typedef struct tag_output_info {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles authentication for the daap server. This isn't the
|
* Handles authentication for the daap server. This isn't the
|
||||||
* authenticator for the web admin page, but rather the iTunes
|
* authenticator for the web admin page, but rather the iTunes
|
||||||
* authentication when trying to connect to the server. Note that most
|
* authentication when trying to connect to the server. Note that most
|
||||||
* of this is actually handled in the web server registration, which
|
* of this is actually handled in the web server registration, which
|
||||||
* decides when to apply the authentication or not. If you mess with
|
* decides when to apply the authentication or not. If you mess with
|
||||||
* when and where the webserver applies auth or not, you'll likely
|
* when and where the webserver applies auth or not, you'll likely
|
||||||
* break something. It seems that some requests must be authed, and others
|
* break something. It seems that some requests must be authed, and others
|
||||||
* not. If you apply authentication somewhere that iTunes doesn't expect
|
* not. If you apply authentication somewhere that iTunes doesn't expect
|
||||||
* it, it happily disconnects.
|
* it, it happily disconnects.
|
||||||
@ -104,7 +104,7 @@ typedef struct tag_output_info {
|
|||||||
* \returns 1 if auth successful, 0 otherwise
|
* \returns 1 if auth successful, 0 otherwise
|
||||||
*/
|
*/
|
||||||
int daap_auth(char *username, char *password) {
|
int daap_auth(char *username, char *password) {
|
||||||
if((password == NULL) &&
|
if((password == NULL) &&
|
||||||
((config.readpassword == NULL) || (strlen(config.readpassword)==0)))
|
((config.readpassword == NULL) || (strlen(config.readpassword)==0)))
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
@ -151,24 +151,24 @@ void daap_handler(WS_CONNINFO *pwsc) {
|
|||||||
/* nm... backing this out. Really do need a "quirks" mode
|
/* nm... backing this out. Really do need a "quirks" mode
|
||||||
pwsc->close=0;
|
pwsc->close=0;
|
||||||
if(ws_testrequestheader(pwsc,"Connection","Close")) {
|
if(ws_testrequestheader(pwsc,"Connection","Close")) {
|
||||||
pwsc->close = 1;
|
pwsc->close = 1;
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
if(ws_getvar(pwsc,"session-id"))
|
if(ws_getvar(pwsc,"session-id"))
|
||||||
pqi->session_id = atoi(ws_getvar(pwsc,"session-id"));
|
pqi->session_id = atoi(ws_getvar(pwsc,"session-id"));
|
||||||
|
|
||||||
/* tokenize the uri for easier decoding */
|
/* tokenize the uri for easier decoding */
|
||||||
string=(pwsc->uri)+1;
|
string=(pwsc->uri)+1;
|
||||||
while((token=strtok_r(string,"/",&save))) {
|
while((token=strtok_r(string,"/",&save))) {
|
||||||
string=NULL;
|
string=NULL;
|
||||||
pqi->uri_sections[pqi->uri_count++] = token;
|
pqi->uri_sections[pqi->uri_count++] = token;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Start dispatching */
|
/* Start dispatching */
|
||||||
if(!strcasecmp(pqi->uri_sections[0],"server-info"))
|
if(!strcasecmp(pqi->uri_sections[0],"server-info"))
|
||||||
return dispatch_server_info(pwsc,pqi);
|
return dispatch_server_info(pwsc,pqi);
|
||||||
|
|
||||||
if(!strcasecmp(pqi->uri_sections[0],"content-codes"))
|
if(!strcasecmp(pqi->uri_sections[0],"content-codes"))
|
||||||
return dispatch_content_codes(pwsc,pqi);
|
return dispatch_content_codes(pwsc,pqi);
|
||||||
|
|
||||||
@ -194,13 +194,13 @@ void daap_handler(WS_CONNINFO *pwsc) {
|
|||||||
}
|
}
|
||||||
pqi->db_id=atoi(pqi->uri_sections[1]);
|
pqi->db_id=atoi(pqi->uri_sections[1]);
|
||||||
if(pqi->uri_count == 3) {
|
if(pqi->uri_count == 3) {
|
||||||
if(!strcasecmp(pqi->uri_sections[2],"items"))
|
if(!strcasecmp(pqi->uri_sections[2],"items"))
|
||||||
/* /databases/id/items */
|
/* /databases/id/items */
|
||||||
return dispatch_items(pwsc,pqi);
|
return dispatch_items(pwsc,pqi);
|
||||||
if(!strcasecmp(pqi->uri_sections[2],"containers"))
|
if(!strcasecmp(pqi->uri_sections[2],"containers"))
|
||||||
/* /databases/id/containers */
|
/* /databases/id/containers */
|
||||||
return dispatch_playlists(pwsc,pqi);
|
return dispatch_playlists(pwsc,pqi);
|
||||||
|
|
||||||
pwsc->close=1;
|
pwsc->close=1;
|
||||||
free(pqi);
|
free(pqi);
|
||||||
ws_returnerror(pwsc,404,"Page not found");
|
ws_returnerror(pwsc,404,"Page not found");
|
||||||
@ -252,7 +252,7 @@ void daap_handler(WS_CONNINFO *pwsc) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pwsc->close=1;
|
pwsc->close=1;
|
||||||
free(pqi);
|
free(pqi);
|
||||||
ws_returnerror(pwsc,404,"Page not found");
|
ws_returnerror(pwsc,404,"Page not found");
|
||||||
@ -319,7 +319,7 @@ int dispatch_output_write(WS_CONNINFO *pwsc, DBQUERYINFO *pqi, unsigned char *bl
|
|||||||
OUTPUT_INFO *poi=(pqi->output_info);
|
OUTPUT_INFO *poi=(pqi->output_info);
|
||||||
int result;
|
int result;
|
||||||
|
|
||||||
if(poi->xml_output)
|
if(poi->xml_output)
|
||||||
return dispatch_output_xml_write(pwsc, pqi, block, len);
|
return dispatch_output_xml_write(pwsc, pqi, block, len);
|
||||||
|
|
||||||
result=r_write(pwsc->fd,block,len);
|
result=r_write(pwsc->fd,block,len);
|
||||||
@ -375,7 +375,7 @@ int dispatch_output_xml_write(WS_CONNINFO *pwsc, DBQUERYINFO *pqi, unsigned char
|
|||||||
/* lookup and serialize */
|
/* lookup and serialize */
|
||||||
DPRINTF(E_SPAM,L_DAAP,"%*s %s: %d\n",poi->stack_height,"",block_tag,block_len);
|
DPRINTF(E_SPAM,L_DAAP,"%*s %s: %d\n",poi->stack_height,"",block_tag,block_len);
|
||||||
pitem=dispatch_xml_lookup_tag(block_tag);
|
pitem=dispatch_xml_lookup_tag(block_tag);
|
||||||
if(poi->readable)
|
if(poi->readable)
|
||||||
r_fdprintf(pwsc->fd,"%*s",poi->stack_height,"");
|
r_fdprintf(pwsc->fd,"%*s",poi->stack_height,"");
|
||||||
r_fdprintf(pwsc->fd,"<%s>",pitem->description);
|
r_fdprintf(pwsc->fd,"<%s>",pitem->description);
|
||||||
switch(pitem->type) {
|
switch(pitem->type) {
|
||||||
@ -497,11 +497,11 @@ int dispatch_output_xml_write(WS_CONNINFO *pwsc, DBQUERYINFO *pqi, unsigned char
|
|||||||
if(poi->stack[stack_ptr].bytes_left < 0) {
|
if(poi->stack[stack_ptr].bytes_left < 0) {
|
||||||
DPRINTF(E_FATAL,L_DAAP,"negative container\n");
|
DPRINTF(E_FATAL,L_DAAP,"negative container\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!poi->stack[stack_ptr].bytes_left) {
|
if(!poi->stack[stack_ptr].bytes_left) {
|
||||||
poi->stack_height--;
|
poi->stack_height--;
|
||||||
pitem=dispatch_xml_lookup_tag(poi->stack[stack_ptr].tag);
|
pitem=dispatch_xml_lookup_tag(poi->stack[stack_ptr].tag);
|
||||||
if(poi->readable)
|
if(poi->readable)
|
||||||
r_fdprintf(pwsc->fd,"%*s",poi->stack_height,"");
|
r_fdprintf(pwsc->fd,"%*s",poi->stack_height,"");
|
||||||
r_fdprintf(pwsc->fd,"</%s>",pitem->description);
|
r_fdprintf(pwsc->fd,"</%s>",pitem->description);
|
||||||
if(poi->readable)
|
if(poi->readable)
|
||||||
@ -518,8 +518,8 @@ int dispatch_output_xml_write(WS_CONNINFO *pwsc, DBQUERYINFO *pqi, unsigned char
|
|||||||
/**
|
/**
|
||||||
* finish streaming output to the client, freeing any allocated
|
* finish streaming output to the client, freeing any allocated
|
||||||
* memory, and cleaning up
|
* memory, and cleaning up
|
||||||
*
|
*
|
||||||
* \param pwsc current conninfo struct
|
* \param pwsc current conninfo struct
|
||||||
* \param pqi current dbquery struct
|
* \param pqi current dbquery struct
|
||||||
*/
|
*/
|
||||||
int dispatch_output_end(WS_CONNINFO *pwsc, DBQUERYINFO *pqi) {
|
int dispatch_output_end(WS_CONNINFO *pwsc, DBQUERYINFO *pqi) {
|
||||||
@ -629,11 +629,12 @@ void dispatch_stream(WS_CONNINFO *pwsc, DBQUERYINFO *pqi) {
|
|||||||
|
|
||||||
item=atoi(pqi->uri_sections[3]);
|
item=atoi(pqi->uri_sections[3]);
|
||||||
|
|
||||||
if(ws_getrequestheader(pwsc,"range")) {
|
if(ws_getrequestheader(pwsc,"range")) {
|
||||||
offset=(off_t)atol(ws_getrequestheader(pwsc,"range") + 6);
|
offset=(off_t)atol(ws_getrequestheader(pwsc,"range") + 6);
|
||||||
}
|
}
|
||||||
|
|
||||||
pmp3=db_fetch_item(item);
|
/* FIXME: error handling */
|
||||||
|
pmp3=db_fetch_item(NULL,item);
|
||||||
if(!pmp3) {
|
if(!pmp3) {
|
||||||
DPRINTF(E_LOG,L_DAAP|L_WS|L_DB,"Could not find requested item %lu\n",item);
|
DPRINTF(E_LOG,L_DAAP|L_WS|L_DB,"Could not find requested item %lu\n",item);
|
||||||
ws_returnerror(pwsc,404,"File Not Found");
|
ws_returnerror(pwsc,404,"File Not Found");
|
||||||
@ -646,7 +647,7 @@ void dispatch_stream(WS_CONNINFO *pwsc, DBQUERYINFO *pqi) {
|
|||||||
file_ptr = server_side_convert_open(pmp3->path,
|
file_ptr = server_side_convert_open(pmp3->path,
|
||||||
offset,
|
offset,
|
||||||
pmp3->song_length,
|
pmp3->song_length,
|
||||||
pmp3->codectype);
|
pmp3->codectype);
|
||||||
if (file_ptr) {
|
if (file_ptr) {
|
||||||
file_fd = fileno(file_ptr);
|
file_fd = fileno(file_ptr);
|
||||||
} else {
|
} else {
|
||||||
@ -699,7 +700,7 @@ void dispatch_stream(WS_CONNINFO *pwsc, DBQUERYINFO *pqi) {
|
|||||||
DPRINTF(E_LOG,L_WS,
|
DPRINTF(E_LOG,L_WS,
|
||||||
"Session %d: Streaming file '%s' to %s (offset %ld)\n",
|
"Session %d: Streaming file '%s' to %s (offset %ld)\n",
|
||||||
pqi->session_id,pmp3->fname, pwsc->hostname,(long)offset);
|
pqi->session_id,pmp3->fname, pwsc->hostname,(long)offset);
|
||||||
|
|
||||||
if(!offset)
|
if(!offset)
|
||||||
config.stats.songs_served++; /* FIXME: remove stat races */
|
config.stats.songs_served++; /* FIXME: remove stat races */
|
||||||
if((bytes_copied=copyfile(file_fd,pwsc->fd)) == -1) {
|
if((bytes_copied=copyfile(file_fd,pwsc->fd)) == -1) {
|
||||||
@ -736,33 +737,33 @@ void dispatch_stream(WS_CONNINFO *pwsc, DBQUERYINFO *pqi) {
|
|||||||
fstat(img_fd, &sb);
|
fstat(img_fd, &sb);
|
||||||
img_size = sb.st_size;
|
img_size = sb.st_size;
|
||||||
r_close(img_fd);
|
r_close(img_fd);
|
||||||
|
|
||||||
if (strncasecmp(pmp3->type,"mp3",4) ==0) {
|
if (strncasecmp(pmp3->type,"mp3",4) ==0) {
|
||||||
/*PENDING*/
|
/*PENDING*/
|
||||||
} else if (strncasecmp(pmp3->type, "m4a", 4) == 0) {
|
} else if (strncasecmp(pmp3->type, "m4a", 4) == 0) {
|
||||||
real_len += img_size + 24;
|
real_len += img_size + 24;
|
||||||
|
|
||||||
if (offset > img_size + 24) {
|
if (offset > img_size + 24) {
|
||||||
offset -= img_size + 24;
|
offset -= img_size + 24;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
file_len = real_len - offset;
|
file_len = real_len - offset;
|
||||||
|
|
||||||
DPRINTF(E_DBG,L_WS,"Thread %d: Length of file (remaining) is %ld\n",
|
DPRINTF(E_DBG,L_WS,"Thread %d: Length of file (remaining) is %ld\n",
|
||||||
pwsc->threadno,(long)file_len);
|
pwsc->threadno,(long)file_len);
|
||||||
|
|
||||||
// DWB: fix content-type to correctly reflect data
|
// DWB: fix content-type to correctly reflect data
|
||||||
// content type (dmap tagged) should only be used on
|
// content type (dmap tagged) should only be used on
|
||||||
// dmap protocol requests, not the actually song data
|
// dmap protocol requests, not the actually song data
|
||||||
if(pmp3->type)
|
if(pmp3->type)
|
||||||
ws_addresponseheader(pwsc,"Content-Type","audio/%s",pmp3->type);
|
ws_addresponseheader(pwsc,"Content-Type","audio/%s",pmp3->type);
|
||||||
|
|
||||||
ws_addresponseheader(pwsc,"Content-Length","%ld",(long)file_len);
|
ws_addresponseheader(pwsc,"Content-Length","%ld",(long)file_len);
|
||||||
ws_addresponseheader(pwsc,"Connection","Close");
|
ws_addresponseheader(pwsc,"Connection","Close");
|
||||||
|
|
||||||
|
|
||||||
if(!offset)
|
if(!offset)
|
||||||
ws_writefd(pwsc,"HTTP/1.1 200 OK\r\n");
|
ws_writefd(pwsc,"HTTP/1.1 200 OK\r\n");
|
||||||
else {
|
else {
|
||||||
@ -771,16 +772,16 @@ void dispatch_stream(WS_CONNINFO *pwsc, DBQUERYINFO *pqi) {
|
|||||||
(long)real_len+1);
|
(long)real_len+1);
|
||||||
ws_writefd(pwsc,"HTTP/1.1 206 Partial Content\r\n");
|
ws_writefd(pwsc,"HTTP/1.1 206 Partial Content\r\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
ws_emitheaders(pwsc);
|
ws_emitheaders(pwsc);
|
||||||
|
|
||||||
config_set_status(pwsc,pqi->session_id,"Streaming file '%s'",pmp3->fname);
|
config_set_status(pwsc,pqi->session_id,"Streaming file '%s'",pmp3->fname);
|
||||||
DPRINTF(E_LOG,L_WS,"Session %d: Streaming file '%s' to %s (offset %d)\n",
|
DPRINTF(E_LOG,L_WS,"Session %d: Streaming file '%s' to %s (offset %d)\n",
|
||||||
pqi->session_id,pmp3->fname, pwsc->hostname,(long)offset);
|
pqi->session_id,pmp3->fname, pwsc->hostname,(long)offset);
|
||||||
|
|
||||||
if(!offset)
|
if(!offset)
|
||||||
config.stats.songs_served++; /* FIXME: remove stat races */
|
config.stats.songs_served++; /* FIXME: remove stat races */
|
||||||
|
|
||||||
if((config.artfilename) &&
|
if((config.artfilename) &&
|
||||||
(!offset) &&
|
(!offset) &&
|
||||||
((img_fd=da_get_image_fd(pmp3->path)) != -1)) {
|
((img_fd=da_get_image_fd(pmp3->path)) != -1)) {
|
||||||
@ -789,7 +790,7 @@ void dispatch_stream(WS_CONNINFO *pwsc, DBQUERYINFO *pqi) {
|
|||||||
pmp3->fname, img_fd);
|
pmp3->fname, img_fd);
|
||||||
da_attach_image(img_fd, pwsc->fd, file_fd, offset);
|
da_attach_image(img_fd, pwsc->fd, file_fd, offset);
|
||||||
} else if (strncasecmp(pmp3->type, "m4a", 4) == 0) {
|
} else if (strncasecmp(pmp3->type, "m4a", 4) == 0) {
|
||||||
DPRINTF(E_INF,L_WS|L_ART,"Dynamic add artwork to %s (fd %d)\n",
|
DPRINTF(E_INF,L_WS|L_ART,"Dynamic add artwork to %s (fd %d)\n",
|
||||||
pmp3->fname, img_fd);
|
pmp3->fname, img_fd);
|
||||||
da_aac_attach_image(img_fd, pwsc->fd, file_fd, offset);
|
da_aac_attach_image(img_fd, pwsc->fd, file_fd, offset);
|
||||||
}
|
}
|
||||||
@ -797,7 +798,7 @@ void dispatch_stream(WS_CONNINFO *pwsc, DBQUERYINFO *pqi) {
|
|||||||
DPRINTF(E_INF,L_WS,"Seeking to offset %ld\n",(long)offset);
|
DPRINTF(E_INF,L_WS,"Seeking to offset %ld\n",(long)offset);
|
||||||
lseek(file_fd,offset,SEEK_SET);
|
lseek(file_fd,offset,SEEK_SET);
|
||||||
}
|
}
|
||||||
|
|
||||||
if((bytes_copied=copyfile(file_fd,pwsc->fd)) == -1) {
|
if((bytes_copied=copyfile(file_fd,pwsc->fd)) == -1) {
|
||||||
DPRINTF(E_INF,L_WS,"Error copying file to remote... %s\n",
|
DPRINTF(E_INF,L_WS,"Error copying file to remote... %s\n",
|
||||||
strerror(errno));
|
strerror(errno));
|
||||||
@ -805,7 +806,7 @@ void dispatch_stream(WS_CONNINFO *pwsc, DBQUERYINFO *pqi) {
|
|||||||
DPRINTF(E_INF,L_WS,"Finished streaming file to remote: %d bytes\n",
|
DPRINTF(E_INF,L_WS,"Finished streaming file to remote: %d bytes\n",
|
||||||
bytes_copied);
|
bytes_copied);
|
||||||
}
|
}
|
||||||
|
|
||||||
config_set_status(pwsc,pqi->session_id,NULL);
|
config_set_status(pwsc,pqi->session_id,NULL);
|
||||||
r_close(file_fd);
|
r_close(file_fd);
|
||||||
db_dispose_item(pmp3);
|
db_dispose_item(pmp3);
|
||||||
@ -836,10 +837,11 @@ void dispatch_addplaylistitems(WS_CONNINFO *pwsc, DBQUERYINFO *pqi) {
|
|||||||
|
|
||||||
while((token=strsep((char**)¤t,","))) {
|
while((token=strsep((char**)¤t,","))) {
|
||||||
if(token) {
|
if(token) {
|
||||||
db_add_playlist_item(pqi->playlist_id,atoi(token));
|
/* FIXME: error handling */
|
||||||
|
db_add_playlist_item(NULL,pqi->playlist_id,atoi(token));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
free(tempstring);
|
free(tempstring);
|
||||||
|
|
||||||
/* success(ish)... spool out a dmap block */
|
/* success(ish)... spool out a dmap block */
|
||||||
@ -869,7 +871,8 @@ void dispatch_deleteplaylist(WS_CONNINFO *pwsc, DBQUERYINFO *pqi) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
db_delete_playlist(atoi(ws_getvar(pwsc,"dmap.itemid")));
|
/* FIXME: error handling */
|
||||||
|
db_delete_playlist(NULL,atoi(ws_getvar(pwsc,"dmap.itemid")));
|
||||||
|
|
||||||
/* success(ish)... spool out a dmap block */
|
/* success(ish)... spool out a dmap block */
|
||||||
current = playlist_response;
|
current = playlist_response;
|
||||||
@ -905,10 +908,11 @@ void dispatch_deleteplaylistitems(WS_CONNINFO *pwsc, DBQUERYINFO *pqi) {
|
|||||||
|
|
||||||
while((token=strsep((char**)¤t,","))) {
|
while((token=strsep((char**)¤t,","))) {
|
||||||
if(token) {
|
if(token) {
|
||||||
db_delete_playlist_item(pqi->playlist_id,atoi(token));
|
/* FIXME: Error handling */
|
||||||
|
db_delete_playlist_item(NULL,pqi->playlist_id,atoi(token));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
free(tempstring);
|
free(tempstring);
|
||||||
|
|
||||||
/* success(ish)... spool out a dmap block */
|
/* success(ish)... spool out a dmap block */
|
||||||
@ -946,7 +950,8 @@ void dispatch_addplaylist(WS_CONNINFO *pwsc, DBQUERYINFO *pqi) {
|
|||||||
name=ws_getvar(pwsc,"dmap.itemname");
|
name=ws_getvar(pwsc,"dmap.itemname");
|
||||||
query=ws_getvar(pwsc,"org.mt-daapd.smart-playlist-spec");
|
query=ws_getvar(pwsc,"org.mt-daapd.smart-playlist-spec");
|
||||||
|
|
||||||
retval=db_add_playlist(name,type,query,NULL,0,&playlistid);
|
/* FIXME: Error handling */
|
||||||
|
retval=db_add_playlist(NULL,name,type,query,NULL,0,&playlistid);
|
||||||
if(retval != DB_E_SUCCESS) {
|
if(retval != DB_E_SUCCESS) {
|
||||||
DPRINTF(E_LOG,L_DAAP,"error adding playlist. aborting\n");
|
DPRINTF(E_LOG,L_DAAP,"error adding playlist. aborting\n");
|
||||||
ws_returnerror(pwsc,500,"error adding playlist");
|
ws_returnerror(pwsc,500,"error adding playlist");
|
||||||
@ -973,37 +978,38 @@ void dispatch_addplaylist(WS_CONNINFO *pwsc, DBQUERYINFO *pqi) {
|
|||||||
void dispatch_editplaylist(WS_CONNINFO *pwsc, DBQUERYINFO *pqi) {
|
void dispatch_editplaylist(WS_CONNINFO *pwsc, DBQUERYINFO *pqi) {
|
||||||
unsigned char edit_response[20];
|
unsigned char edit_response[20];
|
||||||
unsigned char *current = edit_response;
|
unsigned char *current = edit_response;
|
||||||
|
|
||||||
char *name, *query;
|
char *name, *query;
|
||||||
int id;
|
int id;
|
||||||
|
|
||||||
int retval;
|
int retval;
|
||||||
|
|
||||||
if((!ws_getvar(pwsc,"dmap.itemname")) ||
|
if((!ws_getvar(pwsc,"dmap.itemname")) ||
|
||||||
(!ws_getvar(pwsc,"dmap.itemid"))) {
|
(!ws_getvar(pwsc,"dmap.itemid"))) {
|
||||||
DPRINTF(E_LOG,L_DAAP,"Missing name on playlist edit");
|
DPRINTF(E_LOG,L_DAAP,"Missing name on playlist edit");
|
||||||
ws_returnerror(pwsc,500,"missing playlist name");
|
ws_returnerror(pwsc,500,"missing playlist name");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
name=ws_getvar(pwsc,"dmap.itemname");
|
name=ws_getvar(pwsc,"dmap.itemname");
|
||||||
query=ws_getvar(pwsc,"org.mt-daapd.smart-playlist-spec");
|
query=ws_getvar(pwsc,"org.mt-daapd.smart-playlist-spec");
|
||||||
id=atoi(ws_getvar(pwsc,"dmap.itemid"));
|
id=atoi(ws_getvar(pwsc,"dmap.itemid"));
|
||||||
|
|
||||||
retval=db_edit_playlist(id,name,query);
|
/* FIXME: Error handling */
|
||||||
|
retval=db_edit_playlist(NULL,id,name,query);
|
||||||
if(retval != DB_E_SUCCESS) {
|
if(retval != DB_E_SUCCESS) {
|
||||||
DPRINTF(E_LOG,L_DAAP,"error editing playlist.");
|
DPRINTF(E_LOG,L_DAAP,"error editing playlist.");
|
||||||
ws_returnerror(pwsc,500,"Error editing playlist");
|
ws_returnerror(pwsc,500,"Error editing playlist");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
current += db_dmap_add_container(current,"MEPR",12);
|
current += db_dmap_add_container(current,"MEPR",12);
|
||||||
current += db_dmap_add_int(current,"mstt",200); /* 12 */
|
current += db_dmap_add_int(current,"mstt",200); /* 12 */
|
||||||
|
|
||||||
dispatch_output_start(pwsc,pqi,20);
|
dispatch_output_start(pwsc,pqi,20);
|
||||||
dispatch_output_write(pwsc,pqi,edit_response,20);
|
dispatch_output_write(pwsc,pqi,edit_response,20);
|
||||||
dispatch_output_end(pwsc,pqi);
|
dispatch_output_end(pwsc,pqi);
|
||||||
|
|
||||||
pwsc->close=1;
|
pwsc->close=1;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -1031,13 +1037,16 @@ void dispatch_playlistitems(WS_CONNINFO *pwsc, DBQUERYINFO *pqi) {
|
|||||||
|
|
||||||
pqi->query_type = queryTypePlaylistItems;
|
pqi->query_type = queryTypePlaylistItems;
|
||||||
pqi->index_type=indexTypeNone;
|
pqi->index_type=indexTypeNone;
|
||||||
if(db_enum_start(pqi)) {
|
|
||||||
|
/* FIXME: Error handling */
|
||||||
|
if(db_enum_start(NULL,pqi)) {
|
||||||
DPRINTF(E_LOG,L_DAAP,"Could not start enum\n");
|
DPRINTF(E_LOG,L_DAAP,"Could not start enum\n");
|
||||||
ws_returnerror(pwsc,500,"Internal server error: out of memory!");
|
ws_returnerror(pwsc,500,"Internal server error: out of memory!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
list_length=db_enum_size(pqi,&song_count);
|
/* FIXME: Error handling */
|
||||||
|
db_enum_size(NULL,pqi,&song_count,&list_length);
|
||||||
|
|
||||||
DPRINTF(E_DBG,L_DAAP,"Item enum: got %d songs, dmap size: %d\n",song_count,list_length);
|
DPRINTF(E_DBG,L_DAAP,"Item enum: got %d songs, dmap size: %d\n",song_count,list_length);
|
||||||
|
|
||||||
@ -1046,12 +1055,14 @@ void dispatch_playlistitems(WS_CONNINFO *pwsc, DBQUERYINFO *pqi) {
|
|||||||
current += db_dmap_add_char(current,"muty",0); /* 9 */
|
current += db_dmap_add_char(current,"muty",0); /* 9 */
|
||||||
current += db_dmap_add_int(current,"mtco",song_count); /* 12 */
|
current += db_dmap_add_int(current,"mtco",song_count); /* 12 */
|
||||||
current += db_dmap_add_int(current,"mrco",song_count); /* 12 */
|
current += db_dmap_add_int(current,"mrco",song_count); /* 12 */
|
||||||
current += db_dmap_add_container(current,"mlcl",list_length);
|
current += db_dmap_add_container(current,"mlcl",list_length);
|
||||||
|
|
||||||
dispatch_output_start(pwsc,pqi,61+list_length);
|
dispatch_output_start(pwsc,pqi,61+list_length);
|
||||||
dispatch_output_write(pwsc,pqi,items_response,61);
|
dispatch_output_write(pwsc,pqi,items_response,61);
|
||||||
|
|
||||||
while((list_length=db_enum_fetch(pqi,&block)) > 0) {
|
while((db_enum_fetch(NULL,pqi,&list_length,&block) == DB_E_SUCCESS) &&
|
||||||
|
(list_length))
|
||||||
|
{
|
||||||
DPRINTF(E_SPAM,L_DAAP,"Got block of size %d\n",list_length);
|
DPRINTF(E_SPAM,L_DAAP,"Got block of size %d\n",list_length);
|
||||||
dispatch_output_write(pwsc,pqi,block,list_length);
|
dispatch_output_write(pwsc,pqi,block,list_length);
|
||||||
free(block);
|
free(block);
|
||||||
@ -1059,7 +1070,7 @@ void dispatch_playlistitems(WS_CONNINFO *pwsc, DBQUERYINFO *pqi) {
|
|||||||
|
|
||||||
DPRINTF(E_DBG,L_DAAP,"Done enumerating.\n");
|
DPRINTF(E_DBG,L_DAAP,"Done enumerating.\n");
|
||||||
|
|
||||||
db_enum_end();
|
db_enum_end(NULL);
|
||||||
|
|
||||||
dispatch_output_end(pwsc,pqi);
|
dispatch_output_end(pwsc,pqi);
|
||||||
return;
|
return;
|
||||||
@ -1095,15 +1106,16 @@ void dispatch_browse(WS_CONNINFO *pwsc, DBQUERYINFO *pqi) {
|
|||||||
|
|
||||||
pqi->index_type = indexTypeNone;
|
pqi->index_type = indexTypeNone;
|
||||||
|
|
||||||
if(db_enum_start(pqi)) {
|
if(db_enum_start(NULL,pqi)) {
|
||||||
DPRINTF(E_LOG,L_DAAP|L_BROW,"Could not start enum\n");
|
DPRINTF(E_LOG,L_DAAP|L_BROW,"Could not start enum\n");
|
||||||
ws_returnerror(pwsc,500,"Internal server error: out of memory!\n");
|
ws_returnerror(pwsc,500,"Internal server error: out of memory!\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
DPRINTF(E_DBG,L_DAAP|L_BROW,"Getting enum size.\n");
|
DPRINTF(E_DBG,L_DAAP|L_BROW,"Getting enum size.\n");
|
||||||
|
|
||||||
list_length=db_enum_size(pqi,&item_count);
|
/* FIXME: Error handling */
|
||||||
|
db_enum_size(NULL,pqi,&item_count,&list_length);
|
||||||
|
|
||||||
DPRINTF(E_DBG,L_DAAP|L_BROW,"Item enum: got %d items, dmap size: %d\n",
|
DPRINTF(E_DBG,L_DAAP|L_BROW,"Item enum: got %d items, dmap size: %d\n",
|
||||||
item_count,list_length);
|
item_count,list_length);
|
||||||
@ -1117,15 +1129,17 @@ void dispatch_browse(WS_CONNINFO *pwsc, DBQUERYINFO *pqi) {
|
|||||||
dispatch_output_start(pwsc,pqi,52+list_length);
|
dispatch_output_start(pwsc,pqi,52+list_length);
|
||||||
dispatch_output_write(pwsc,pqi,browse_response,52);
|
dispatch_output_write(pwsc,pqi,browse_response,52);
|
||||||
|
|
||||||
while((list_length=db_enum_fetch(pqi,&block)) > 0) {
|
while((db_enum_fetch(NULL,pqi,&list_length,&block) == DB_E_SUCCESS) &&
|
||||||
|
(list_length))
|
||||||
|
{
|
||||||
DPRINTF(E_SPAM,L_DAAP|L_BROW,"Got block of size %d\n",list_length);
|
DPRINTF(E_SPAM,L_DAAP|L_BROW,"Got block of size %d\n",list_length);
|
||||||
dispatch_output_write(pwsc,pqi,block,list_length);
|
dispatch_output_write(pwsc,pqi,block,list_length);
|
||||||
free(block);
|
free(block);
|
||||||
}
|
}
|
||||||
|
|
||||||
DPRINTF(E_DBG,L_DAAP|L_BROW,"Done enumerating\n");
|
DPRINTF(E_DBG,L_DAAP|L_BROW,"Done enumerating\n");
|
||||||
|
|
||||||
db_enum_end();
|
db_enum_end(NULL);
|
||||||
|
|
||||||
dispatch_output_end(pwsc,pqi);
|
dispatch_output_end(pwsc,pqi);
|
||||||
return;
|
return;
|
||||||
@ -1151,13 +1165,14 @@ void dispatch_playlists(WS_CONNINFO *pwsc, DBQUERYINFO *pqi) {
|
|||||||
|
|
||||||
pqi->query_type = queryTypePlaylists;
|
pqi->query_type = queryTypePlaylists;
|
||||||
pqi->index_type = indexTypeNone;
|
pqi->index_type = indexTypeNone;
|
||||||
if(db_enum_start(pqi)) {
|
if(db_enum_start(NULL,pqi)) {
|
||||||
DPRINTF(E_LOG,L_DAAP,"Could not start enum\n");
|
DPRINTF(E_LOG,L_DAAP,"Could not start enum\n");
|
||||||
ws_returnerror(pwsc,500,"Internal server error: out of memory!\n");
|
ws_returnerror(pwsc,500,"Internal server error: out of memory!\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
list_length=db_enum_size(pqi,&pl_count);
|
/* FIXME: Error handling */
|
||||||
|
db_enum_size(NULL,pqi,&pl_count,&list_length);
|
||||||
|
|
||||||
DPRINTF(E_DBG,L_DAAP,"Item enum: got %d playlists, dmap size: %d\n",pl_count,list_length);
|
DPRINTF(E_DBG,L_DAAP,"Item enum: got %d playlists, dmap size: %d\n",pl_count,list_length);
|
||||||
|
|
||||||
@ -1166,12 +1181,14 @@ void dispatch_playlists(WS_CONNINFO *pwsc, DBQUERYINFO *pqi) {
|
|||||||
current += db_dmap_add_char(current,"muty",0); /* 9 */
|
current += db_dmap_add_char(current,"muty",0); /* 9 */
|
||||||
current += db_dmap_add_int(current,"mtco",pl_count); /* 12 */
|
current += db_dmap_add_int(current,"mtco",pl_count); /* 12 */
|
||||||
current += db_dmap_add_int(current,"mrco",pl_count); /* 12 */
|
current += db_dmap_add_int(current,"mrco",pl_count); /* 12 */
|
||||||
current += db_dmap_add_container(current,"mlcl",list_length);
|
current += db_dmap_add_container(current,"mlcl",list_length);
|
||||||
|
|
||||||
dispatch_output_start(pwsc,pqi,61+list_length);
|
dispatch_output_start(pwsc,pqi,61+list_length);
|
||||||
dispatch_output_write(pwsc,pqi,playlist_response,61);
|
dispatch_output_write(pwsc,pqi,playlist_response,61);
|
||||||
|
|
||||||
while((list_length=db_enum_fetch(pqi,&block)) > 0) {
|
while((db_enum_fetch(NULL,pqi,&list_length,&block) == DB_E_SUCCESS) &&
|
||||||
|
(list_length))
|
||||||
|
{
|
||||||
DPRINTF(E_SPAM,L_DAAP,"Got block of size %d\n",list_length);
|
DPRINTF(E_SPAM,L_DAAP,"Got block of size %d\n",list_length);
|
||||||
dispatch_output_write(pwsc,pqi,block,list_length);
|
dispatch_output_write(pwsc,pqi,block,list_length);
|
||||||
free(block);
|
free(block);
|
||||||
@ -1179,7 +1196,7 @@ void dispatch_playlists(WS_CONNINFO *pwsc, DBQUERYINFO *pqi) {
|
|||||||
|
|
||||||
DPRINTF(E_DBG,L_DAAP,"Done enumerating.\n");
|
DPRINTF(E_DBG,L_DAAP,"Done enumerating.\n");
|
||||||
|
|
||||||
db_enum_end();
|
db_enum_end(NULL);
|
||||||
|
|
||||||
dispatch_output_end(pwsc,pqi);
|
dispatch_output_end(pwsc,pqi);
|
||||||
return;
|
return;
|
||||||
@ -1200,13 +1217,14 @@ void dispatch_items(WS_CONNINFO *pwsc, DBQUERYINFO *pqi) {
|
|||||||
|
|
||||||
pqi->query_type = queryTypeItems;
|
pqi->query_type = queryTypeItems;
|
||||||
pqi->index_type=indexTypeNone;
|
pqi->index_type=indexTypeNone;
|
||||||
if(db_enum_start(pqi)) {
|
if(db_enum_start(NULL,pqi)) {
|
||||||
DPRINTF(E_LOG,L_DAAP,"Could not start enum\n");
|
DPRINTF(E_LOG,L_DAAP,"Could not start enum\n");
|
||||||
ws_returnerror(pwsc,500,"Internal server error: out of memory!");
|
ws_returnerror(pwsc,500,"Internal server error: out of memory!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
list_length=db_enum_size(pqi,&song_count);
|
/* FIXME: Error handling */
|
||||||
|
db_enum_size(NULL,pqi,&song_count,&list_length);
|
||||||
|
|
||||||
DPRINTF(E_DBG,L_DAAP,"Item enum: got %d songs, dmap size: %d\n",song_count,list_length);
|
DPRINTF(E_DBG,L_DAAP,"Item enum: got %d songs, dmap size: %d\n",song_count,list_length);
|
||||||
|
|
||||||
@ -1215,12 +1233,14 @@ void dispatch_items(WS_CONNINFO *pwsc, DBQUERYINFO *pqi) {
|
|||||||
current += db_dmap_add_char(current,"muty",0); /* 9 */
|
current += db_dmap_add_char(current,"muty",0); /* 9 */
|
||||||
current += db_dmap_add_int(current,"mtco",song_count); /* 12 */
|
current += db_dmap_add_int(current,"mtco",song_count); /* 12 */
|
||||||
current += db_dmap_add_int(current,"mrco",song_count); /* 12 */
|
current += db_dmap_add_int(current,"mrco",song_count); /* 12 */
|
||||||
current += db_dmap_add_container(current,"mlcl",list_length);
|
current += db_dmap_add_container(current,"mlcl",list_length);
|
||||||
|
|
||||||
dispatch_output_start(pwsc,pqi,61+list_length);
|
dispatch_output_start(pwsc,pqi,61+list_length);
|
||||||
dispatch_output_write(pwsc,pqi,items_response,61);
|
dispatch_output_write(pwsc,pqi,items_response,61);
|
||||||
|
|
||||||
while((list_length=db_enum_fetch(pqi,&block)) > 0) {
|
while((db_enum_fetch(NULL,pqi,&list_length,&block) == DB_E_SUCCESS) &&
|
||||||
|
(list_length))
|
||||||
|
{
|
||||||
DPRINTF(E_SPAM,L_DAAP,"Got block of size %d\n",list_length);
|
DPRINTF(E_SPAM,L_DAAP,"Got block of size %d\n",list_length);
|
||||||
dispatch_output_write(pwsc,pqi,block,list_length);
|
dispatch_output_write(pwsc,pqi,block,list_length);
|
||||||
free(block);
|
free(block);
|
||||||
@ -1228,7 +1248,7 @@ void dispatch_items(WS_CONNINFO *pwsc, DBQUERYINFO *pqi) {
|
|||||||
|
|
||||||
DPRINTF(E_DBG,L_DAAP,"Done enumerating.\n");
|
DPRINTF(E_DBG,L_DAAP,"Done enumerating.\n");
|
||||||
|
|
||||||
db_enum_end();
|
db_enum_end(NULL);
|
||||||
|
|
||||||
dispatch_output_end(pwsc,pqi);
|
dispatch_output_end(pwsc,pqi);
|
||||||
return;
|
return;
|
||||||
@ -1259,7 +1279,7 @@ void dispatch_update(WS_CONNINFO *pwsc, DBQUERYINFO *pqi) {
|
|||||||
if(FD_ISSET(pwsc->fd,&rset)) {
|
if(FD_ISSET(pwsc->fd,&rset)) {
|
||||||
/* can't be ready for read, must be error */
|
/* can't be ready for read, must be error */
|
||||||
DPRINTF(E_DBG,L_DAAP,"Socket closed?\n");
|
DPRINTF(E_DBG,L_DAAP,"Socket closed?\n");
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1280,6 +1300,7 @@ void dispatch_dbinfo(WS_CONNINFO *pwsc, DBQUERYINFO *pqi) {
|
|||||||
unsigned char dbinfo_response[255]; /* FIXME */
|
unsigned char dbinfo_response[255]; /* FIXME */
|
||||||
unsigned char *current = dbinfo_response;
|
unsigned char *current = dbinfo_response;
|
||||||
int namelen;
|
int namelen;
|
||||||
|
int count;
|
||||||
|
|
||||||
namelen=strlen(config.servername);
|
namelen=strlen(config.servername);
|
||||||
|
|
||||||
@ -1289,11 +1310,13 @@ void dispatch_dbinfo(WS_CONNINFO *pwsc, DBQUERYINFO *pqi) {
|
|||||||
current += db_dmap_add_int(current,"mtco",1); /* 12 */
|
current += db_dmap_add_int(current,"mtco",1); /* 12 */
|
||||||
current += db_dmap_add_int(current,"mrco",1); /* 12 */
|
current += db_dmap_add_int(current,"mrco",1); /* 12 */
|
||||||
current += db_dmap_add_container(current,"mlcl",52 + namelen);
|
current += db_dmap_add_container(current,"mlcl",52 + namelen);
|
||||||
current += db_dmap_add_container(current,"mlit",44 + namelen);
|
current += db_dmap_add_container(current,"mlit",44 + namelen);
|
||||||
current += db_dmap_add_int(current,"miid",1); /* 12 */
|
current += db_dmap_add_int(current,"miid",1); /* 12 */
|
||||||
current += db_dmap_add_string(current,"minm",config.servername); /* 8 + namelen */
|
current += db_dmap_add_string(current,"minm",config.servername); /* 8 + namelen */
|
||||||
current += db_dmap_add_int(current,"mimc",db_get_song_count()); /* 12 */
|
db_get_song_count(NULL,&count);
|
||||||
current += db_dmap_add_int(current,"mctc",db_get_playlist_count()); /* 12 */
|
current += db_dmap_add_int(current,"mimc",count); /* 12 */
|
||||||
|
db_get_playlist_count(NULL,&count);
|
||||||
|
current += db_dmap_add_int(current,"mctc",count); /* 12 */
|
||||||
|
|
||||||
dispatch_output_start(pwsc,pqi,113+namelen);
|
dispatch_output_start(pwsc,pqi,113+namelen);
|
||||||
dispatch_output_write(pwsc,pqi,dbinfo_response,113+namelen);
|
dispatch_output_write(pwsc,pqi,dbinfo_response,113+namelen);
|
||||||
@ -1313,7 +1336,7 @@ void dispatch_login(WS_CONNINFO *pwsc, DBQUERYINFO *pqi) {
|
|||||||
unsigned char login_response[32];
|
unsigned char login_response[32];
|
||||||
unsigned char *current = login_response;
|
unsigned char *current = login_response;
|
||||||
int session;
|
int session;
|
||||||
|
|
||||||
session = config_get_next_session();
|
session = config_get_next_session();
|
||||||
|
|
||||||
current += db_dmap_add_container(current,"mlog",24);
|
current += db_dmap_add_container(current,"mlog",24);
|
||||||
@ -1342,7 +1365,7 @@ void dispatch_content_codes(WS_CONNINFO *pwsc, DBQUERYINFO *pqi) {
|
|||||||
|
|
||||||
current += db_dmap_add_container(current,"mccr",len + 12);
|
current += db_dmap_add_container(current,"mccr",len + 12);
|
||||||
current += db_dmap_add_int(current,"mstt",200);
|
current += db_dmap_add_int(current,"mstt",200);
|
||||||
|
|
||||||
dispatch_output_start(pwsc,pqi,len+20);
|
dispatch_output_start(pwsc,pqi,len+20);
|
||||||
dispatch_output_write(pwsc,pqi,content_codes,20);
|
dispatch_output_write(pwsc,pqi,content_codes,20);
|
||||||
|
|
||||||
@ -1357,7 +1380,7 @@ void dispatch_content_codes(WS_CONNINFO *pwsc, DBQUERYINFO *pqi) {
|
|||||||
dispatch_output_write(pwsc,pqi,mdcl,len+8);
|
dispatch_output_write(pwsc,pqi,mdcl,len+8);
|
||||||
dicurrent++;
|
dicurrent++;
|
||||||
}
|
}
|
||||||
|
|
||||||
dispatch_output_end(pwsc,pqi);
|
dispatch_output_end(pwsc,pqi);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -1376,7 +1399,7 @@ void dispatch_server_info(WS_CONNINFO *pwsc, DBQUERYINFO *pqi) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
client_version=ws_getrequestheader(pwsc,"Client-DAAP-Version");
|
client_version=ws_getrequestheader(pwsc,"Client-DAAP-Version");
|
||||||
|
|
||||||
current += db_dmap_add_container(current,"msrv",actual_length - 8);
|
current += db_dmap_add_container(current,"msrv",actual_length - 8);
|
||||||
current += db_dmap_add_int(current,"mstt",200); /* 12 */
|
current += db_dmap_add_int(current,"mstt",200); /* 12 */
|
||||||
|
|
||||||
@ -1396,7 +1419,7 @@ void dispatch_server_info(WS_CONNINFO *pwsc, DBQUERYINFO *pqi) {
|
|||||||
current += db_dmap_add_string(current,"minm",config.servername); /* 8 + strlen(name) */
|
current += db_dmap_add_string(current,"minm",config.servername); /* 8 + strlen(name) */
|
||||||
|
|
||||||
current += db_dmap_add_char(current,"msau", /* 9 */
|
current += db_dmap_add_char(current,"msau", /* 9 */
|
||||||
config.readpassword != NULL ? 2 : 0);
|
config.readpassword != NULL ? 2 : 0);
|
||||||
current += db_dmap_add_char(current,"msex",0); /* 9 */
|
current += db_dmap_add_char(current,"msex",0); /* 9 */
|
||||||
current += db_dmap_add_char(current,"msix",0); /* 9 */
|
current += db_dmap_add_char(current,"msix",0); /* 9 */
|
||||||
current += db_dmap_add_char(current,"msbr",0); /* 9 */
|
current += db_dmap_add_char(current,"msbr",0); /* 9 */
|
||||||
|
24
src/main.c
24
src/main.c
@ -117,7 +117,7 @@
|
|||||||
*/
|
*/
|
||||||
CONFIG config; /**< Main configuration structure, as read from configfile */
|
CONFIG config; /**< Main configuration structure, as read from configfile */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Forwards
|
* Forwards
|
||||||
*/
|
*/
|
||||||
static int daemon_start(void);
|
static int daemon_start(void);
|
||||||
@ -158,7 +158,7 @@ int daemon_start(void) {
|
|||||||
if((fd = open("/dev/null", O_RDWR, 0)) != -1) {
|
if((fd = open("/dev/null", O_RDWR, 0)) != -1) {
|
||||||
dup2(fd, STDIN_FILENO);
|
dup2(fd, STDIN_FILENO);
|
||||||
dup2(fd, STDOUT_FILENO);
|
dup2(fd, STDOUT_FILENO);
|
||||||
dup2(fd, STDERR_FILENO);
|
dup2(fd, STDERR_FILENO);
|
||||||
if (fd > 2)
|
if (fd > 2)
|
||||||
close(fd);
|
close(fd);
|
||||||
}
|
}
|
||||||
@ -219,7 +219,7 @@ int drop_privs(char *user) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(pw) {
|
if(pw) {
|
||||||
if(initgroups(user,pw->pw_gid) != 0 ||
|
if(initgroups(user,pw->pw_gid) != 0 ||
|
||||||
setgid(pw->pw_gid) != 0 ||
|
setgid(pw->pw_gid) != 0 ||
|
||||||
setuid(pw->pw_uid) != 0) {
|
setuid(pw->pw_uid) != 0) {
|
||||||
err=errno;
|
err=errno;
|
||||||
@ -353,13 +353,14 @@ int main(int argc, char *argv[]) {
|
|||||||
int start_time;
|
int start_time;
|
||||||
int end_time;
|
int end_time;
|
||||||
int rescan_counter=0;
|
int rescan_counter=0;
|
||||||
int old_song_count;
|
int old_song_count, song_count;
|
||||||
int force_non_root=0;
|
int force_non_root=0;
|
||||||
int skip_initial=0;
|
int skip_initial=0;
|
||||||
pthread_t signal_tid;
|
pthread_t signal_tid;
|
||||||
|
|
||||||
int pid_fd;
|
int pid_fd;
|
||||||
FILE *pid_fp=NULL;
|
FILE *pid_fp=NULL;
|
||||||
|
char *perr;
|
||||||
|
|
||||||
config.use_mdns=1;
|
config.use_mdns=1;
|
||||||
err_debuglevel=1;
|
err_debuglevel=1;
|
||||||
@ -482,8 +483,8 @@ int main(int argc, char *argv[]) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* this will require that the db be readable by the runas user */
|
/* this will require that the db be readable by the runas user */
|
||||||
if(db_open(config.dbdir))
|
if(db_open(&perr,config.dbdir))
|
||||||
DPRINTF(E_FATAL,L_MAIN|L_DB,"Error in db_open: %s\n",strerror(errno));
|
DPRINTF(E_FATAL,L_MAIN|L_DB,"Error in db_open: %s\n",perr);
|
||||||
|
|
||||||
/* Initialize the database before starting */
|
/* Initialize the database before starting */
|
||||||
DPRINTF(E_LOG,L_MAIN|L_DB,"Initializing database\n");
|
DPRINTF(E_LOG,L_MAIN|L_DB,"Initializing database\n");
|
||||||
@ -529,7 +530,8 @@ int main(int argc, char *argv[]) {
|
|||||||
|
|
||||||
end_time=time(NULL);
|
end_time=time(NULL);
|
||||||
|
|
||||||
DPRINTF(E_LOG,L_MAIN,"Scanned %d songs in %d seconds\n",db_get_song_count(),
|
db_get_song_count(NULL,&song_count);
|
||||||
|
DPRINTF(E_LOG,L_MAIN,"Scanned %d songs in %d seconds\n",song_count,
|
||||||
end_time-start_time);
|
end_time-start_time);
|
||||||
|
|
||||||
while(!config.stop) {
|
while(!config.stop) {
|
||||||
@ -543,7 +545,7 @@ int main(int argc, char *argv[]) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(config.reload) {
|
if(config.reload) {
|
||||||
old_song_count = db_get_song_count();
|
old_song_count = song_count;
|
||||||
start_time=time(NULL);
|
start_time=time(NULL);
|
||||||
|
|
||||||
DPRINTF(E_LOG,L_MAIN|L_DB|L_SCAN,"Rescanning database\n");
|
DPRINTF(E_LOG,L_MAIN|L_DB|L_SCAN,"Rescanning database\n");
|
||||||
@ -552,8 +554,10 @@ int main(int argc, char *argv[]) {
|
|||||||
config.stop=1;
|
config.stop=1;
|
||||||
}
|
}
|
||||||
config.reload=0;
|
config.reload=0;
|
||||||
DPRINTF(E_INF,L_MAIN|L_DB|L_SCAN,"Scanned %d songs (was %d) in %d seconds\n",
|
db_get_song_count(NULL,&song_count);
|
||||||
db_get_song_count(),old_song_count,time(NULL)-start_time);
|
DPRINTF(E_INF,L_MAIN|L_DB|L_SCAN,"Scanned %d songs (was %d) in "
|
||||||
|
"%d seconds\n",song_count,old_song_count,
|
||||||
|
time(NULL)-start_time);
|
||||||
}
|
}
|
||||||
|
|
||||||
sleep(MAIN_SLEEP_INTERVAL);
|
sleep(MAIN_SLEEP_INTERVAL);
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
* but the name is the same for historical purposes, not to mention
|
* but the name is the same for historical purposes, not to mention
|
||||||
* the fact that sf.net makes it virtually impossible to manage a cvs
|
* the fact that sf.net makes it virtually impossible to manage a cvs
|
||||||
* root reasonably. Perhaps one day soon they will move to subversion.
|
* root reasonably. Perhaps one day soon they will move to subversion.
|
||||||
*
|
*
|
||||||
* /me crosses his fingers
|
* /me crosses his fingers
|
||||||
*
|
*
|
||||||
* Copyright (C) 2003 Ron Pedde (ron@pedde.com)
|
* Copyright (C) 2003 Ron Pedde (ron@pedde.com)
|
||||||
@ -107,20 +107,20 @@ static int scan_static_playlist(char *path);
|
|||||||
/* For known types, I'm gong to use the "official" apple
|
/* For known types, I'm gong to use the "official" apple
|
||||||
* daap.songformat, daap.songdescription, and daap.songcodecsubtype.
|
* daap.songformat, daap.songdescription, and daap.songcodecsubtype.
|
||||||
* If I we don't have "official" ones, we can make them up the
|
* If I we don't have "official" ones, we can make them up the
|
||||||
* way we currently are: using extension or whatver.
|
* way we currently are: using extension or whatver.
|
||||||
*
|
*
|
||||||
* This means that you can test to see if something is, say, an un-drmed
|
* This means that you can test to see if something is, say, an un-drmed
|
||||||
* aac file by just testing for ->type "m4a", rather than checking every
|
* aac file by just testing for ->type "m4a", rather than checking every
|
||||||
* different flavor of file extension.
|
* different flavor of file extension.
|
||||||
*
|
*
|
||||||
* NOTE: Although they are represented here as strings, the codectype is
|
* NOTE: Although they are represented here as strings, the codectype is
|
||||||
* *really* an unsigned short. So when it gets serialized, it gets
|
* *really* an unsigned short. So when it gets serialized, it gets
|
||||||
* serialized as a short int. If you put something other than 3 or 4
|
* serialized as a short int. If you put something other than 3 or 4
|
||||||
* characters as your codectype, you'll see strange results.
|
* characters as your codectype, you'll see strange results.
|
||||||
*
|
*
|
||||||
* FIXME: url != pls -- this method of dispatching handlers based on file type
|
* FIXME: url != pls -- this method of dispatching handlers based on file type
|
||||||
* is completely wrong. There needs to be a separate type that gets carried
|
* is completely wrong. There needs to be a separate type that gets carried
|
||||||
* around with it, at least outside the database that says where the info
|
* around with it, at least outside the database that says where the info
|
||||||
* CAME FROM.
|
* CAME FROM.
|
||||||
*
|
*
|
||||||
* This system is broken, and won't work with something like a .cue file
|
* This system is broken, and won't work with something like a .cue file
|
||||||
@ -160,8 +160,8 @@ static PLAYLISTLIST scan_playlistlist = { NULL, NULL };
|
|||||||
/**
|
/**
|
||||||
* add a playlist to the playlistlist. The playlistlist is a
|
* add a playlist to the playlistlist. The playlistlist is a
|
||||||
* list of playlists that need to be processed once the current
|
* list of playlists that need to be processed once the current
|
||||||
* scan is done. THIS IS NOT REENTRANT, and it meant to be
|
* scan is done. THIS IS NOT REENTRANT, and it meant to be
|
||||||
* called only inside the rescan loop.
|
* called only inside the rescan loop.
|
||||||
*
|
*
|
||||||
* \param path path of the playlist to add
|
* \param path path of the playlist to add
|
||||||
*/
|
*/
|
||||||
@ -243,7 +243,7 @@ int scan_init(char *path) {
|
|||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
scan_process_playlistlist();
|
scan_process_playlistlist();
|
||||||
|
|
||||||
if(db_end_scan())
|
if(db_end_scan())
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
@ -317,7 +317,7 @@ int scan_path(char *path) {
|
|||||||
|
|
||||||
if(!pde)
|
if(!pde)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
if(pde->d_name[0] == '.') /* skip hidden and directories */
|
if(pde->d_name[0] == '.') /* skip hidden and directories */
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
@ -344,9 +344,9 @@ int scan_path(char *path) {
|
|||||||
(strcasestr(config.extensions, ext))) {
|
(strcasestr(config.extensions, ext))) {
|
||||||
/* only scan if it's been changed, or empty db */
|
/* only scan if it's been changed, or empty db */
|
||||||
modified_time=sb.st_mtime;
|
modified_time=sb.st_mtime;
|
||||||
pmp3=db_fetch_path(mp3_path,0);
|
pmp3=db_fetch_path(NULL,mp3_path,0);
|
||||||
|
|
||||||
if((!pmp3) || (pmp3->db_timestamp < modified_time) ||
|
if((!pmp3) || (pmp3->db_timestamp < modified_time) ||
|
||||||
(pmp3->force_update)) {
|
(pmp3->force_update)) {
|
||||||
scan_music_file(path,pde,&sb,is_compdir);
|
scan_music_file(path,pde,&sb,is_compdir);
|
||||||
} else {
|
} else {
|
||||||
@ -380,6 +380,7 @@ int scan_static_playlist(char *path) {
|
|||||||
MP3FILE *pmp3;
|
MP3FILE *pmp3;
|
||||||
struct stat sb;
|
struct stat sb;
|
||||||
char *current;
|
char *current;
|
||||||
|
char *perr;
|
||||||
|
|
||||||
DPRINTF(E_WARN,L_SCAN|L_PL,"Processing static playlist: %s\n",path);
|
DPRINTF(E_WARN,L_SCAN|L_PL,"Processing static playlist: %s\n",path);
|
||||||
if(stat(path,&sb)) {
|
if(stat(path,&sb)) {
|
||||||
@ -399,7 +400,7 @@ int scan_static_playlist(char *path) {
|
|||||||
*current='\x0';
|
*current='\x0';
|
||||||
}
|
}
|
||||||
|
|
||||||
pm3u = db_fetch_playlist(path,0);
|
pm3u = db_fetch_playlist(NULL,path,0);
|
||||||
if(pm3u && (pm3u->db_timestamp > sb.st_mtime)) {
|
if(pm3u && (pm3u->db_timestamp > sb.st_mtime)) {
|
||||||
/* already up-to-date */
|
/* already up-to-date */
|
||||||
DPRINTF(E_DBG,L_SCAN,"Playlist already up-to-date\n");
|
DPRINTF(E_DBG,L_SCAN,"Playlist already up-to-date\n");
|
||||||
@ -407,13 +408,15 @@ int scan_static_playlist(char *path) {
|
|||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(pm3u)
|
if(pm3u)
|
||||||
db_delete_playlist(pm3u->id);
|
db_delete_playlist(NULL,pm3u->id);
|
||||||
|
|
||||||
fd=open(path,O_RDONLY);
|
fd=open(path,O_RDONLY);
|
||||||
if(fd != -1) {
|
if(fd != -1) {
|
||||||
if(db_add_playlist(base_path,PL_STATICFILE,NULL,path,0,&playlistid) != DB_E_SUCCESS) {
|
if(db_add_playlist(&perr,base_path,PL_STATICFILE,NULL,path,
|
||||||
DPRINTF(E_LOG,L_SCAN,"Error adding m3u playlist %s\n",path);
|
0,&playlistid) != DB_E_SUCCESS) {
|
||||||
|
DPRINTF(E_LOG,L_SCAN,"Error adding m3u %s: %s\n",path,perr);
|
||||||
|
free(perr);
|
||||||
db_dispose_playlist(pm3u);
|
db_dispose_playlist(pm3u);
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
@ -428,7 +431,7 @@ int scan_static_playlist(char *path) {
|
|||||||
memset(linebuffer,0x00,sizeof(linebuffer));
|
memset(linebuffer,0x00,sizeof(linebuffer));
|
||||||
while(readline(fd,linebuffer,sizeof(linebuffer)) > 0) {
|
while(readline(fd,linebuffer,sizeof(linebuffer)) > 0) {
|
||||||
while((linebuffer[strlen(linebuffer)-1] == '\n') ||
|
while((linebuffer[strlen(linebuffer)-1] == '\n') ||
|
||||||
(linebuffer[strlen(linebuffer)-1] == '\r'))
|
(linebuffer[strlen(linebuffer)-1] == '\r'))
|
||||||
linebuffer[strlen(linebuffer)-1] = '\0';
|
linebuffer[strlen(linebuffer)-1] = '\0';
|
||||||
|
|
||||||
if((linebuffer[0] == ';') || (linebuffer[0] == '#'))
|
if((linebuffer[0] == ';') || (linebuffer[0] == '#'))
|
||||||
@ -447,12 +450,14 @@ int scan_static_playlist(char *path) {
|
|||||||
DPRINTF(E_DBG,L_SCAN|L_PL,"Checking %s\n",real_path);
|
DPRINTF(E_DBG,L_SCAN|L_PL,"Checking %s\n",real_path);
|
||||||
|
|
||||||
// might be valid, might not...
|
// might be valid, might not...
|
||||||
if((pmp3=db_fetch_path(real_path,0))) {
|
if((pmp3=db_fetch_path(&perr,real_path,0))) {
|
||||||
db_add_playlist_item(playlistid,pmp3->id);
|
/* FIXME: better error handling */
|
||||||
|
db_add_playlist_item(NULL,playlistid,pmp3->id);
|
||||||
db_dispose_item(pmp3);
|
db_dispose_item(pmp3);
|
||||||
} else {
|
} else {
|
||||||
DPRINTF(E_WARN,L_SCAN|L_PL,"Playlist entry %s bad: %s\n",
|
DPRINTF(E_WARN,L_SCAN|L_PL,"Playlist entry %s bad: %s\n",
|
||||||
path,strerror(errno));
|
path,perr);
|
||||||
|
free(perr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
close(fd);
|
close(fd);
|
||||||
@ -469,7 +474,7 @@ int scan_static_playlist(char *path) {
|
|||||||
*
|
*
|
||||||
* scan a particular file as a music file
|
* scan a particular file as a music file
|
||||||
*/
|
*/
|
||||||
void scan_music_file(char *path, struct dirent *pde,
|
void scan_music_file(char *path, struct dirent *pde,
|
||||||
struct stat *psb, int is_compdir) {
|
struct stat *psb, int is_compdir) {
|
||||||
MP3FILE mp3file;
|
MP3FILE mp3file;
|
||||||
char mp3_path[PATH_MAX];
|
char mp3_path[PATH_MAX];
|
||||||
@ -482,7 +487,7 @@ void scan_music_file(char *path, struct dirent *pde,
|
|||||||
|
|
||||||
/* we found an mp3 file */
|
/* we found an mp3 file */
|
||||||
DPRINTF(E_INF,L_SCAN,"Found music file: %s\n",pde->d_name);
|
DPRINTF(E_INF,L_SCAN,"Found music file: %s\n",pde->d_name);
|
||||||
|
|
||||||
memset((void*)&mp3file,0,sizeof(mp3file));
|
memset((void*)&mp3file,0,sizeof(mp3file));
|
||||||
mp3file.path=strdup(mp3_path);
|
mp3file.path=strdup(mp3_path);
|
||||||
mp3file.fname=strdup(pde->d_name);
|
mp3file.fname=strdup(pde->d_name);
|
||||||
@ -511,14 +516,14 @@ void scan_music_file(char *path, struct dirent *pde,
|
|||||||
*current=tolower(*current);
|
*current=tolower(*current);
|
||||||
current++;
|
current++;
|
||||||
}
|
}
|
||||||
|
|
||||||
sprintf(fdescr,"%s audio file",mp3file.type);
|
sprintf(fdescr,"%s audio file",mp3file.type);
|
||||||
mp3file.description = strdup(fdescr);
|
mp3file.description = strdup(fdescr);
|
||||||
/* we'll just dodge the codectype */
|
/* we'll just dodge the codectype */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Do the tag lookup here */
|
/* Do the tag lookup here */
|
||||||
if(scan_get_info(mp3file.path,&mp3file)) {
|
if(scan_get_info(mp3file.path,&mp3file)) {
|
||||||
make_composite_tags(&mp3file);
|
make_composite_tags(&mp3file);
|
||||||
@ -538,11 +543,12 @@ void scan_music_file(char *path, struct dirent *pde,
|
|||||||
if(is_compdir)
|
if(is_compdir)
|
||||||
mp3file.compilation = 1;
|
mp3file.compilation = 1;
|
||||||
|
|
||||||
db_add(&mp3file);
|
/* FIXME: error handling */
|
||||||
|
db_add(NULL,&mp3file);
|
||||||
} else {
|
} else {
|
||||||
DPRINTF(E_WARN,L_SCAN,"Skipping %s - scan failed\n",mp3file.path);
|
DPRINTF(E_WARN,L_SCAN,"Skipping %s - scan failed\n",mp3file.path);
|
||||||
}
|
}
|
||||||
|
|
||||||
scan_freetags(&mp3file);
|
scan_freetags(&mp3file);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -624,7 +630,7 @@ int scan_get_info(char *file, MP3FILE *pmp3) {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Manually build tags. Set artist to computer/orchestra
|
* Manually build tags. Set artist to computer/orchestra
|
||||||
* if there is already no artist. Perhaps this could be
|
* if there is already no artist. Perhaps this could be
|
||||||
* done better, but I'm not sure what else to do here.
|
* done better, but I'm not sure what else to do here.
|
||||||
*
|
*
|
||||||
* @param song MP3FILE of the file to build composite tags for
|
* @param song MP3FILE of the file to build composite tags for
|
||||||
|
757
src/scan-xml.c
757
src/scan-xml.c
@ -131,16 +131,16 @@ int scan_xml_datedecode(char *string) {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* comparison for the red-black tree. @see redblack.c
|
* comparison for the red-black tree. @see redblack.c
|
||||||
*
|
*
|
||||||
* @param pa one node to compare
|
* @param pa one node to compare
|
||||||
* @param pb other node to compare
|
* @param pb other node to compare
|
||||||
* @param cfg opaque pointer I'm not using
|
* @param cfg opaque pointer I'm not using
|
||||||
*/
|
*/
|
||||||
int scan_xml_rb_compare(const void *pa, const void *pb, const void *cfg) {
|
int scan_xml_rb_compare(const void *pa, const void *pb, const void *cfg) {
|
||||||
if(((SCAN_XML_RB*)pa)->itunes_index < ((SCAN_XML_RB*)pb)->itunes_index)
|
if(((SCAN_XML_RB*)pa)->itunes_index < ((SCAN_XML_RB*)pb)->itunes_index)
|
||||||
return -1;
|
return -1;
|
||||||
if(((SCAN_XML_RB*)pb)->itunes_index < ((SCAN_XML_RB*)pa)->itunes_index)
|
if(((SCAN_XML_RB*)pb)->itunes_index < ((SCAN_XML_RB*)pa)->itunes_index)
|
||||||
return 1;
|
return 1;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -156,10 +156,10 @@ int scan_xml_is_file(char *path) {
|
|||||||
struct stat sb;
|
struct stat sb;
|
||||||
|
|
||||||
if(stat(path,&sb))
|
if(stat(path,&sb))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if(sb.st_mode & S_IFREG)
|
if(sb.st_mode & S_IFREG)
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -182,35 +182,35 @@ int scan_xml_translate_path(char *pold, char *pnew) {
|
|||||||
char *pbase;
|
char *pbase;
|
||||||
|
|
||||||
if((!pold)||(!strlen(pold)))
|
if((!pold)||(!strlen(pold)))
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
||||||
if(!path_found) {
|
if(!path_found) {
|
||||||
strcpy(working_path,pold);
|
strcpy(working_path,pold);
|
||||||
|
|
||||||
DPRINTF(E_DBG,L_SCAN,"Translating %s\n",pold);
|
DPRINTF(E_DBG,L_SCAN,"Translating %s\n",pold);
|
||||||
|
|
||||||
/* let's try to find the path by brute force.
|
/* let's try to find the path by brute force.
|
||||||
* We'll assume that it is under the xml file somewhere
|
* We'll assume that it is under the xml file somewhere
|
||||||
*/
|
*/
|
||||||
while(!path_found && ((current = strrchr(working_path,'/')))) {
|
while(!path_found && ((current = strrchr(working_path,'/')))) {
|
||||||
strcpy(base_path,scan_xml_file);
|
strcpy(base_path,scan_xml_file);
|
||||||
pbase = strrchr(base_path,'/');
|
pbase = strrchr(base_path,'/');
|
||||||
if(!pbase) return FALSE;
|
if(!pbase) return FALSE;
|
||||||
|
|
||||||
strcpy(pbase,pold + (current-working_path));
|
strcpy(pbase,pold + (current-working_path));
|
||||||
if(base_path[strlen(base_path)-1] == '/')
|
if(base_path[strlen(base_path)-1] == '/')
|
||||||
base_path[strlen(base_path)-1] = '\0';
|
base_path[strlen(base_path)-1] = '\0';
|
||||||
|
|
||||||
DPRINTF(E_DBG,L_SCAN,"Trying %s\n",base_path);
|
DPRINTF(E_DBG,L_SCAN,"Trying %s\n",base_path);
|
||||||
if(scan_xml_is_file(base_path)) {
|
if(scan_xml_is_file(base_path)) {
|
||||||
path_found=1;
|
path_found=1;
|
||||||
discard = (current - working_path);
|
discard = (current - working_path);
|
||||||
DPRINTF(E_DBG,L_SCAN,"Found it!\n");
|
DPRINTF(E_DBG,L_SCAN,"Found it!\n");
|
||||||
}
|
}
|
||||||
*current='\0';
|
*current='\0';
|
||||||
}
|
}
|
||||||
if(!current)
|
if(!current)
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
strcpy(base_path,scan_xml_file);
|
strcpy(base_path,scan_xml_file);
|
||||||
@ -219,7 +219,7 @@ int scan_xml_translate_path(char *pold, char *pnew) {
|
|||||||
strcpy(pbase,pold + discard);
|
strcpy(pbase,pold + discard);
|
||||||
|
|
||||||
if(base_path[strlen(base_path)-1] == '/')
|
if(base_path[strlen(base_path)-1] == '/')
|
||||||
base_path[strlen(base_path)-1] = '\0';
|
base_path[strlen(base_path)-1] = '\0';
|
||||||
|
|
||||||
realpath(base_path,pnew);
|
realpath(base_path,pnew);
|
||||||
|
|
||||||
@ -241,16 +241,16 @@ void scan_xml_add_lookup(int itunes_index, int mtd_index) {
|
|||||||
|
|
||||||
pnew=(SCAN_XML_RB*)malloc(sizeof(SCAN_XML_RB));
|
pnew=(SCAN_XML_RB*)malloc(sizeof(SCAN_XML_RB));
|
||||||
if(!pnew)
|
if(!pnew)
|
||||||
DPRINTF(E_FATAL,L_SCAN,"malloc error in scan_xml_add_lookup\n");
|
DPRINTF(E_FATAL,L_SCAN,"malloc error in scan_xml_add_lookup\n");
|
||||||
|
|
||||||
pnew->itunes_index = itunes_index;
|
pnew->itunes_index = itunes_index;
|
||||||
pnew->mtd_index = mtd_index;
|
pnew->mtd_index = mtd_index;
|
||||||
|
|
||||||
val = rbsearch((const void*)pnew,scan_xml_db);
|
val = rbsearch((const void*)pnew,scan_xml_db);
|
||||||
if(!val) {
|
if(!val) {
|
||||||
/* couldn't alloc the rb tree structure -- if we don't
|
/* couldn't alloc the rb tree structure -- if we don't
|
||||||
* die now, we are going to soon enough*/
|
* die now, we are going to soon enough*/
|
||||||
DPRINTF(E_FATAL,L_SCAN,"redblack tree insert error\n");
|
DPRINTF(E_FATAL,L_SCAN,"redblack tree insert error\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -268,9 +268,9 @@ int scan_xml_get_index(int itunes_index, int *mtd_index) {
|
|||||||
rb.itunes_index = itunes_index;
|
rb.itunes_index = itunes_index;
|
||||||
prb = (SCAN_XML_RB*) rbfind((void*)&rb,scan_xml_db);
|
prb = (SCAN_XML_RB*) rbfind((void*)&rb,scan_xml_db);
|
||||||
if(prb) {
|
if(prb) {
|
||||||
*mtd_index = prb->mtd_index;
|
*mtd_index = prb->mtd_index;
|
||||||
DPRINTF(E_SPAM,L_SCAN,"Matching %d to %d\n",itunes_index,*mtd_index);
|
DPRINTF(E_SPAM,L_SCAN,"Matching %d to %d\n",itunes_index,*mtd_index);
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
return FALSE;
|
return FALSE;
|
||||||
@ -287,12 +287,12 @@ int scan_xml_get_tagindex(char *tag) {
|
|||||||
int index=0;
|
int index=0;
|
||||||
|
|
||||||
while(*ptag && (strcasecmp(tag,*ptag) != 0)) {
|
while(*ptag && (strcasecmp(tag,*ptag) != 0)) {
|
||||||
ptag++;
|
ptag++;
|
||||||
index++;
|
index++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(*ptag)
|
if(*ptag)
|
||||||
return index;
|
return index;
|
||||||
|
|
||||||
return SCAN_XML_T_UNKNOWN;
|
return SCAN_XML_T_UNKNOWN;
|
||||||
}
|
}
|
||||||
@ -311,45 +311,45 @@ char *scan_xml_urldecode(char *string, int space_as_plus) {
|
|||||||
|
|
||||||
pnew=(char*)malloc(strlen(string)+1);
|
pnew=(char*)malloc(strlen(string)+1);
|
||||||
if(!pnew)
|
if(!pnew)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
src=string;
|
src=string;
|
||||||
dst=pnew;
|
dst=pnew;
|
||||||
|
|
||||||
while(*src) {
|
while(*src) {
|
||||||
switch(*src) {
|
switch(*src) {
|
||||||
case '+':
|
case '+':
|
||||||
if(space_as_plus) {
|
if(space_as_plus) {
|
||||||
*dst++=' ';
|
*dst++=' ';
|
||||||
} else {
|
} else {
|
||||||
*dst++=*src;
|
*dst++=*src;
|
||||||
}
|
}
|
||||||
src++;
|
src++;
|
||||||
break;
|
break;
|
||||||
case '%':
|
case '%':
|
||||||
/* this is hideous */
|
/* this is hideous */
|
||||||
src++;
|
src++;
|
||||||
if(*src) {
|
if(*src) {
|
||||||
if((*src <= '9') && (*src >='0'))
|
if((*src <= '9') && (*src >='0'))
|
||||||
val=(*src - '0');
|
val=(*src - '0');
|
||||||
else if((tolower(*src) <= 'f')&&(tolower(*src) >= 'a'))
|
else if((tolower(*src) <= 'f')&&(tolower(*src) >= 'a'))
|
||||||
val=10+(tolower(*src) - 'a');
|
val=10+(tolower(*src) - 'a');
|
||||||
src++;
|
src++;
|
||||||
}
|
}
|
||||||
if(*src) {
|
if(*src) {
|
||||||
val *= 16;
|
val *= 16;
|
||||||
if((*src <= '9') && (*src >='0'))
|
if((*src <= '9') && (*src >='0'))
|
||||||
val+=(*src - '0');
|
val+=(*src - '0');
|
||||||
else if((tolower(*src) <= 'f')&&(tolower(*src) >= 'a'))
|
else if((tolower(*src) <= 'f')&&(tolower(*src) >= 'a'))
|
||||||
val+=(10+(tolower(*src) - 'a'));
|
val+=(10+(tolower(*src) - 'a'));
|
||||||
src++;
|
src++;
|
||||||
}
|
}
|
||||||
*dst++=val;
|
*dst++=val;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
*dst++=*src++;
|
*dst++=*src++;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
*dst='\0';
|
*dst='\0';
|
||||||
@ -381,31 +381,31 @@ int scan_xml_playlist(char *filename) {
|
|||||||
|
|
||||||
/* initialize the redblack tree */
|
/* initialize the redblack tree */
|
||||||
if((scan_xml_db = rbinit(scan_xml_rb_compare,NULL)) == NULL) {
|
if((scan_xml_db = rbinit(scan_xml_rb_compare,NULL)) == NULL) {
|
||||||
DPRINTF(E_LOG,L_SCAN,"Could not initialize red/black tree\n");
|
DPRINTF(E_LOG,L_SCAN,"Could not initialize red/black tree\n");
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* find the base dir of the itunes playlist itself */
|
/* find the base dir of the itunes playlist itself */
|
||||||
working_base = strdup(filename);
|
working_base = strdup(filename);
|
||||||
if(strrchr(working_base,'/')) {
|
if(strrchr(working_base,'/')) {
|
||||||
*(strrchr(working_base,'/') + 1) = '\x0';
|
*(strrchr(working_base,'/') + 1) = '\x0';
|
||||||
scan_xml_real_base_path = strdup(working_base);
|
scan_xml_real_base_path = strdup(working_base);
|
||||||
} else {
|
} else {
|
||||||
scan_xml_real_base_path = strdup("/");
|
scan_xml_real_base_path = strdup("/");
|
||||||
}
|
}
|
||||||
free(working_base);
|
free(working_base);
|
||||||
|
|
||||||
DPRINTF(E_SPAM,L_SCAN,"Parsing xml file: %s\n",filename);
|
DPRINTF(E_SPAM,L_SCAN,"Parsing xml file: %s\n",filename);
|
||||||
|
|
||||||
if(!rxml_open(&xml_handle,filename,scan_xml_handler,NULL)) {
|
if(!rxml_open(&xml_handle,filename,scan_xml_handler,NULL)) {
|
||||||
DPRINTF(E_LOG,L_SCAN,"Error opening xml file %s: %s\n",
|
DPRINTF(E_LOG,L_SCAN,"Error opening xml file %s: %s\n",
|
||||||
filename,rxml_errorstring(xml_handle));
|
filename,rxml_errorstring(xml_handle));
|
||||||
} else {
|
} else {
|
||||||
if(!rxml_parse(xml_handle)) {
|
if(!rxml_parse(xml_handle)) {
|
||||||
retval=FALSE;
|
retval=FALSE;
|
||||||
DPRINTF(E_LOG,L_SCAN,"Error parsing xml file %s: %s\n",
|
DPRINTF(E_LOG,L_SCAN,"Error parsing xml file %s: %s\n",
|
||||||
filename,rxml_errorstring(xml_handle));
|
filename,rxml_errorstring(xml_handle));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
rxml_close(xml_handle);
|
rxml_close(xml_handle);
|
||||||
@ -413,11 +413,11 @@ int scan_xml_playlist(char *filename) {
|
|||||||
/* destroy the redblack tree */
|
/* destroy the redblack tree */
|
||||||
val = rblookup(RB_LUFIRST,NULL,scan_xml_db);
|
val = rblookup(RB_LUFIRST,NULL,scan_xml_db);
|
||||||
while(val) {
|
while(val) {
|
||||||
lookup_val.itunes_index = ((SCAN_XML_RB*)val)->itunes_index;
|
lookup_val.itunes_index = ((SCAN_XML_RB*)val)->itunes_index;
|
||||||
lookup_ptr = (SCAN_XML_RB *)rbdelete((void*)&lookup_val,scan_xml_db);
|
lookup_ptr = (SCAN_XML_RB *)rbdelete((void*)&lookup_val,scan_xml_db);
|
||||||
if(lookup_ptr)
|
if(lookup_ptr)
|
||||||
free(lookup_ptr);
|
free(lookup_ptr);
|
||||||
val = rblookup(RB_LUFIRST,NULL,scan_xml_db);
|
val = rblookup(RB_LUFIRST,NULL,scan_xml_db);
|
||||||
}
|
}
|
||||||
|
|
||||||
rbdestroy(scan_xml_db);
|
rbdestroy(scan_xml_db);
|
||||||
@ -445,32 +445,32 @@ void scan_xml_handler(int action,void* puser,char* info) {
|
|||||||
|
|
||||||
switch(action) {
|
switch(action) {
|
||||||
case RXML_EVT_OPEN: /* file opened */
|
case RXML_EVT_OPEN: /* file opened */
|
||||||
state = XML_STATE_PREAMBLE;
|
state = XML_STATE_PREAMBLE;
|
||||||
/* send this event to all dispatches to allow them
|
/* send this event to all dispatches to allow them
|
||||||
* to reset
|
* to reset
|
||||||
*/
|
*/
|
||||||
scan_xml_preamble_section(action,info);
|
scan_xml_preamble_section(action,info);
|
||||||
scan_xml_tracks_section(action,info);
|
scan_xml_tracks_section(action,info);
|
||||||
scan_xml_playlists_section(action,info);
|
scan_xml_playlists_section(action,info);
|
||||||
break;
|
break;
|
||||||
case RXML_EVT_BEGIN:
|
case RXML_EVT_BEGIN:
|
||||||
case RXML_EVT_END:
|
case RXML_EVT_END:
|
||||||
case RXML_EVT_TEXT:
|
case RXML_EVT_TEXT:
|
||||||
switch(state) {
|
switch(state) {
|
||||||
case XML_STATE_PREAMBLE:
|
case XML_STATE_PREAMBLE:
|
||||||
state=scan_xml_preamble_section(action,info);
|
state=scan_xml_preamble_section(action,info);
|
||||||
break;
|
break;
|
||||||
case XML_STATE_TRACKS:
|
case XML_STATE_TRACKS:
|
||||||
state=scan_xml_tracks_section(action,info);
|
state=scan_xml_tracks_section(action,info);
|
||||||
break;
|
break;
|
||||||
case XML_STATE_PLAYLISTS:
|
case XML_STATE_PLAYLISTS:
|
||||||
state=scan_xml_playlists_section(action,info);
|
state=scan_xml_playlists_section(action,info);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -489,62 +489,62 @@ void scan_xml_handler(int action,void* puser,char* info) {
|
|||||||
int scan_xml_preamble_section(int action, char *info) {
|
int scan_xml_preamble_section(int action, char *info) {
|
||||||
static int expecting_next;
|
static int expecting_next;
|
||||||
static int done;
|
static int done;
|
||||||
|
|
||||||
switch(action) {
|
switch(action) {
|
||||||
case RXML_EVT_OPEN: /* initialization */
|
case RXML_EVT_OPEN: /* initialization */
|
||||||
expecting_next=0;
|
expecting_next=0;
|
||||||
done=0;
|
done=0;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case RXML_EVT_END:
|
case RXML_EVT_END:
|
||||||
if(expecting_next == SCAN_XML_PRE_TRACKS) { /* end of tracks tag */
|
if(expecting_next == SCAN_XML_PRE_TRACKS) { /* end of tracks tag */
|
||||||
expecting_next=0;
|
expecting_next=0;
|
||||||
DPRINTF(E_DBG,L_SCAN,"Scanning tracks\n");
|
DPRINTF(E_DBG,L_SCAN,"Scanning tracks\n");
|
||||||
return XML_STATE_TRACKS;
|
return XML_STATE_TRACKS;
|
||||||
}
|
}
|
||||||
if(expecting_next == SCAN_XML_PRE_PLAYLISTS) {
|
if(expecting_next == SCAN_XML_PRE_PLAYLISTS) {
|
||||||
expecting_next=0;
|
expecting_next=0;
|
||||||
DPRINTF(E_DBG,L_SCAN,"Scanning playlists\n");
|
DPRINTF(E_DBG,L_SCAN,"Scanning playlists\n");
|
||||||
return XML_STATE_PLAYLISTS;
|
return XML_STATE_PLAYLISTS;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case RXML_EVT_TEXT: /* scan for the tags we expect */
|
case RXML_EVT_TEXT: /* scan for the tags we expect */
|
||||||
if(!expecting_next) {
|
if(!expecting_next) {
|
||||||
if(strcmp(info,"Application Version") == 0) {
|
if(strcmp(info,"Application Version") == 0) {
|
||||||
expecting_next = SCAN_XML_PRE_VERSION;
|
expecting_next = SCAN_XML_PRE_VERSION;
|
||||||
} else if (strcmp(info,"Music Folder") == 0) {
|
} else if (strcmp(info,"Music Folder") == 0) {
|
||||||
expecting_next = SCAN_XML_PRE_PATH;
|
expecting_next = SCAN_XML_PRE_PATH;
|
||||||
} else if (strcmp(info,"Tracks") == 0) {
|
} else if (strcmp(info,"Tracks") == 0) {
|
||||||
expecting_next = SCAN_XML_PRE_TRACKS;
|
expecting_next = SCAN_XML_PRE_TRACKS;
|
||||||
} else if (strcmp(info,"Playlists") == 0) {
|
} else if (strcmp(info,"Playlists") == 0) {
|
||||||
expecting_next = SCAN_XML_PRE_PLAYLISTS;
|
expecting_next = SCAN_XML_PRE_PLAYLISTS;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
/* we were expecting someting! */
|
/* we were expecting someting! */
|
||||||
switch(expecting_next) {
|
switch(expecting_next) {
|
||||||
case SCAN_XML_PRE_VERSION:
|
case SCAN_XML_PRE_VERSION:
|
||||||
if(!scan_xml_itunes_version) {
|
if(!scan_xml_itunes_version) {
|
||||||
scan_xml_itunes_version=strdup(info);
|
scan_xml_itunes_version=strdup(info);
|
||||||
DPRINTF(E_DBG,L_SCAN,"iTunes Version: %s\n",info);
|
DPRINTF(E_DBG,L_SCAN,"iTunes Version: %s\n",info);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case SCAN_XML_PRE_PATH:
|
case SCAN_XML_PRE_PATH:
|
||||||
if(!scan_xml_itunes_base_path) {
|
if(!scan_xml_itunes_base_path) {
|
||||||
scan_xml_itunes_base_path=strdup(info);
|
scan_xml_itunes_base_path=strdup(info);
|
||||||
scan_xml_itunes_decoded_base_path=scan_xml_urldecode(info,0);
|
scan_xml_itunes_decoded_base_path=scan_xml_urldecode(info,0);
|
||||||
DPRINTF(E_DBG,L_SCAN,"iTunes base path: %s\n",info);
|
DPRINTF(E_DBG,L_SCAN,"iTunes base path: %s\n",info);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
expecting_next=0;
|
expecting_next=0;
|
||||||
}
|
}
|
||||||
break; /* RXML_EVT_TEXT */
|
break; /* RXML_EVT_TEXT */
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return XML_STATE_PREAMBLE;
|
return XML_STATE_PREAMBLE;
|
||||||
@ -569,7 +569,7 @@ int scan_xml_preamble_section(int action, char *info) {
|
|||||||
state = (c); \
|
state = (c); \
|
||||||
return XML_STATE_TRACKS; \
|
return XML_STATE_TRACKS; \
|
||||||
}}
|
}}
|
||||||
|
|
||||||
int scan_xml_tracks_section(int action, char *info) {
|
int scan_xml_tracks_section(int action, char *info) {
|
||||||
static int state;
|
static int state;
|
||||||
static int current_track_id;
|
static int current_track_id;
|
||||||
@ -580,143 +580,144 @@ int scan_xml_tracks_section(int action, char *info) {
|
|||||||
MP3FILE *pmp3;
|
MP3FILE *pmp3;
|
||||||
|
|
||||||
if(action == RXML_EVT_OPEN) {
|
if(action == RXML_EVT_OPEN) {
|
||||||
state = XML_TRACK_ST_INITIAL;
|
state = XML_TRACK_ST_INITIAL;
|
||||||
memset((void*)&mp3,0,sizeof(MP3FILE));
|
memset((void*)&mp3,0,sizeof(MP3FILE));
|
||||||
song_path = NULL;
|
song_path = NULL;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* walk through the states */
|
/* walk through the states */
|
||||||
switch(state) {
|
switch(state) {
|
||||||
case XML_TRACK_ST_INITIAL:
|
case XML_TRACK_ST_INITIAL:
|
||||||
/* expection only a <dict> */
|
/* expection only a <dict> */
|
||||||
MAYBESETSTATE_TR(RXML_EVT_BEGIN,"dict",XML_TRACK_ST_MAIN_DICT);
|
MAYBESETSTATE_TR(RXML_EVT_BEGIN,"dict",XML_TRACK_ST_MAIN_DICT);
|
||||||
return XML_STATE_ERROR;
|
return XML_STATE_ERROR;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case XML_TRACK_ST_MAIN_DICT:
|
case XML_TRACK_ST_MAIN_DICT:
|
||||||
/* either get a <key>, or a </dict> */
|
/* either get a <key>, or a </dict> */
|
||||||
MAYBESETSTATE_TR(RXML_EVT_BEGIN,"key",XML_TRACK_ST_EXPECTING_TRACK_ID);
|
MAYBESETSTATE_TR(RXML_EVT_BEGIN,"key",XML_TRACK_ST_EXPECTING_TRACK_ID);
|
||||||
if ((action == RXML_EVT_END) && (strcasecmp(info,"dict") == 0)) {
|
if ((action == RXML_EVT_END) && (strcasecmp(info,"dict") == 0)) {
|
||||||
return XML_STATE_PREAMBLE;
|
return XML_STATE_PREAMBLE;
|
||||||
}
|
}
|
||||||
return XML_STATE_ERROR;
|
return XML_STATE_ERROR;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case XML_TRACK_ST_EXPECTING_TRACK_ID:
|
case XML_TRACK_ST_EXPECTING_TRACK_ID:
|
||||||
/* this is somewhat loose - <key>id</key> */
|
/* this is somewhat loose - <key>id</key> */
|
||||||
MAYBESETSTATE_TR(RXML_EVT_BEGIN,"key",XML_TRACK_ST_EXPECTING_TRACK_ID);
|
MAYBESETSTATE_TR(RXML_EVT_BEGIN,"key",XML_TRACK_ST_EXPECTING_TRACK_ID);
|
||||||
MAYBESETSTATE_TR(RXML_EVT_END,"key",XML_TRACK_ST_EXPECTING_TRACK_DICT);
|
MAYBESETSTATE_TR(RXML_EVT_END,"key",XML_TRACK_ST_EXPECTING_TRACK_DICT);
|
||||||
if (action == RXML_EVT_TEXT) {
|
if (action == RXML_EVT_TEXT) {
|
||||||
current_track_id = atoi(info);
|
current_track_id = atoi(info);
|
||||||
DPRINTF(E_DBG,L_SCAN,"Scanning iTunes id #%d\n",current_track_id);
|
DPRINTF(E_DBG,L_SCAN,"Scanning iTunes id #%d\n",current_track_id);
|
||||||
} else {
|
} else {
|
||||||
return XML_STATE_ERROR;
|
return XML_STATE_ERROR;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case XML_TRACK_ST_EXPECTING_TRACK_DICT:
|
case XML_TRACK_ST_EXPECTING_TRACK_DICT:
|
||||||
/* waiting for a dict */
|
/* waiting for a dict */
|
||||||
MAYBESETSTATE_TR(RXML_EVT_BEGIN,"dict",XML_TRACK_ST_TRACK_INFO);
|
MAYBESETSTATE_TR(RXML_EVT_BEGIN,"dict",XML_TRACK_ST_TRACK_INFO);
|
||||||
return XML_STATE_ERROR;
|
return XML_STATE_ERROR;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case XML_TRACK_ST_TRACK_INFO:
|
case XML_TRACK_ST_TRACK_INFO:
|
||||||
/* again, kind of loose */
|
/* again, kind of loose */
|
||||||
MAYBESETSTATE_TR(RXML_EVT_BEGIN,"key",XML_TRACK_ST_TRACK_INFO);
|
MAYBESETSTATE_TR(RXML_EVT_BEGIN,"key",XML_TRACK_ST_TRACK_INFO);
|
||||||
MAYBESETSTATE_TR(RXML_EVT_END,"key",XML_TRACK_ST_TRACK_DATA);
|
MAYBESETSTATE_TR(RXML_EVT_END,"key",XML_TRACK_ST_TRACK_DATA);
|
||||||
if(action == RXML_EVT_TEXT) {
|
if(action == RXML_EVT_TEXT) {
|
||||||
current_field=scan_xml_get_tagindex(info);
|
current_field=scan_xml_get_tagindex(info);
|
||||||
if(current_field == SCAN_XML_T_DISABLED) {
|
if(current_field == SCAN_XML_T_DISABLED) {
|
||||||
mp3.disabled = 1;
|
mp3.disabled = 1;
|
||||||
} else if(current_field == SCAN_XML_T_COMPILATION) {
|
} else if(current_field == SCAN_XML_T_COMPILATION) {
|
||||||
mp3.compilation = 1;
|
mp3.compilation = 1;
|
||||||
}
|
}
|
||||||
} else if((action == RXML_EVT_END) && (strcmp(info,"dict")==0)) {
|
} else if((action == RXML_EVT_END) && (strcmp(info,"dict")==0)) {
|
||||||
state = XML_TRACK_ST_MAIN_DICT;
|
state = XML_TRACK_ST_MAIN_DICT;
|
||||||
/* but more importantly, we gotta process the track */
|
/* but more importantly, we gotta process the track */
|
||||||
if(scan_xml_translate_path(song_path,real_path)) {
|
if(scan_xml_translate_path(song_path,real_path)) {
|
||||||
pmp3=db_fetch_path(real_path,0);
|
/* FIXME: Error handling */
|
||||||
if(pmp3) {
|
pmp3=db_fetch_path(NULL,real_path,0);
|
||||||
/* Update the existing record with the
|
if(pmp3) {
|
||||||
* updated stuff we got from the iTunes xml file
|
/* Update the existing record with the
|
||||||
*/
|
* updated stuff we got from the iTunes xml file
|
||||||
MAYBECOPYSTRING(title);
|
*/
|
||||||
MAYBECOPYSTRING(artist);
|
MAYBECOPYSTRING(title);
|
||||||
MAYBECOPYSTRING(album);
|
MAYBECOPYSTRING(artist);
|
||||||
MAYBECOPYSTRING(genre);
|
MAYBECOPYSTRING(album);
|
||||||
MAYBECOPY(song_length);
|
MAYBECOPYSTRING(genre);
|
||||||
MAYBECOPY(track);
|
MAYBECOPY(song_length);
|
||||||
MAYBECOPY(total_tracks);
|
MAYBECOPY(track);
|
||||||
MAYBECOPY(year);
|
MAYBECOPY(total_tracks);
|
||||||
MAYBECOPY(bitrate);
|
MAYBECOPY(year);
|
||||||
MAYBECOPY(samplerate);
|
MAYBECOPY(bitrate);
|
||||||
MAYBECOPY(play_count);
|
MAYBECOPY(samplerate);
|
||||||
MAYBECOPY(rating);
|
MAYBECOPY(play_count);
|
||||||
MAYBECOPY(disc);
|
MAYBECOPY(rating);
|
||||||
MAYBECOPY(total_discs);
|
MAYBECOPY(disc);
|
||||||
MAYBECOPY(time_added);
|
MAYBECOPY(total_discs);
|
||||||
|
MAYBECOPY(time_added);
|
||||||
|
|
||||||
/* must add to the red-black tree */
|
/* must add to the red-black tree */
|
||||||
scan_xml_add_lookup(current_track_id,pmp3->id);
|
scan_xml_add_lookup(current_track_id,pmp3->id);
|
||||||
|
|
||||||
db_add(pmp3);
|
db_add(NULL,pmp3);
|
||||||
db_dispose_item(pmp3);
|
db_dispose_item(pmp3);
|
||||||
|
|
||||||
memset((void*)&mp3,0,sizeof(MP3FILE));
|
memset((void*)&mp3,0,sizeof(MP3FILE));
|
||||||
MAYBEFREE(song_path);
|
MAYBEFREE(song_path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return XML_STATE_ERROR;
|
return XML_STATE_ERROR;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case XML_TRACK_ST_TRACK_DATA:
|
case XML_TRACK_ST_TRACK_DATA:
|
||||||
if(action == RXML_EVT_BEGIN) {
|
if(action == RXML_EVT_BEGIN) {
|
||||||
break;
|
break;
|
||||||
} else if(action == RXML_EVT_TEXT) {
|
} else if(action == RXML_EVT_TEXT) {
|
||||||
if(current_field == SCAN_XML_T_NAME) {
|
if(current_field == SCAN_XML_T_NAME) {
|
||||||
mp3.title = strdup(info);
|
mp3.title = strdup(info);
|
||||||
} else if(current_field == SCAN_XML_T_ARTIST) {
|
} else if(current_field == SCAN_XML_T_ARTIST) {
|
||||||
mp3.artist = strdup(info);
|
mp3.artist = strdup(info);
|
||||||
} else if(current_field == SCAN_XML_T_ALBUM) {
|
} else if(current_field == SCAN_XML_T_ALBUM) {
|
||||||
mp3.album = strdup(info);
|
mp3.album = strdup(info);
|
||||||
} else if(current_field == SCAN_XML_T_GENRE) {
|
} else if(current_field == SCAN_XML_T_GENRE) {
|
||||||
mp3.genre = strdup(info);
|
mp3.genre = strdup(info);
|
||||||
} else if(current_field == SCAN_XML_T_TOTALTIME) {
|
} else if(current_field == SCAN_XML_T_TOTALTIME) {
|
||||||
mp3.song_length = atoi(info);
|
mp3.song_length = atoi(info);
|
||||||
} else if(current_field == SCAN_XML_T_TRACKNUMBER) {
|
} else if(current_field == SCAN_XML_T_TRACKNUMBER) {
|
||||||
mp3.track = atoi(info);
|
mp3.track = atoi(info);
|
||||||
} else if(current_field == SCAN_XML_T_TRACKCOUNT) {
|
} else if(current_field == SCAN_XML_T_TRACKCOUNT) {
|
||||||
mp3.total_tracks = atoi(info);
|
mp3.total_tracks = atoi(info);
|
||||||
} else if(current_field == SCAN_XML_T_YEAR) {
|
} else if(current_field == SCAN_XML_T_YEAR) {
|
||||||
mp3.year = atoi(info);
|
mp3.year = atoi(info);
|
||||||
} else if(current_field == SCAN_XML_T_BITRATE) {
|
} else if(current_field == SCAN_XML_T_BITRATE) {
|
||||||
mp3.bitrate = atoi(info);
|
mp3.bitrate = atoi(info);
|
||||||
} else if(current_field == SCAN_XML_T_SAMPLERATE) {
|
} else if(current_field == SCAN_XML_T_SAMPLERATE) {
|
||||||
mp3.samplerate = atoi(info);
|
mp3.samplerate = atoi(info);
|
||||||
} else if(current_field == SCAN_XML_T_PLAYCOUNT) {
|
} else if(current_field == SCAN_XML_T_PLAYCOUNT) {
|
||||||
mp3.play_count = atoi(info);
|
mp3.play_count = atoi(info);
|
||||||
} else if(current_field == SCAN_XML_T_RATING) {
|
} else if(current_field == SCAN_XML_T_RATING) {
|
||||||
mp3.rating = atoi(info);
|
mp3.rating = atoi(info);
|
||||||
} else if(current_field == SCAN_XML_T_DISCNO) {
|
} else if(current_field == SCAN_XML_T_DISCNO) {
|
||||||
mp3.disc = atoi(info);
|
mp3.disc = atoi(info);
|
||||||
} else if(current_field == SCAN_XML_T_DISCCOUNT) {
|
} else if(current_field == SCAN_XML_T_DISCCOUNT) {
|
||||||
mp3.total_discs = atoi(info);
|
mp3.total_discs = atoi(info);
|
||||||
} else if(current_field == SCAN_XML_T_LOCATION) {
|
} else if(current_field == SCAN_XML_T_LOCATION) {
|
||||||
song_path = scan_xml_urldecode(info,0);
|
song_path = scan_xml_urldecode(info,0);
|
||||||
} else if(current_field == SCAN_XML_T_DATE_ADDED) {
|
} else if(current_field == SCAN_XML_T_DATE_ADDED) {
|
||||||
mp3.time_added = scan_xml_datedecode(info);
|
mp3.time_added = scan_xml_datedecode(info);
|
||||||
}
|
}
|
||||||
} else if(action == RXML_EVT_END) {
|
} else if(action == RXML_EVT_END) {
|
||||||
state = XML_TRACK_ST_TRACK_INFO;
|
state = XML_TRACK_ST_TRACK_INFO;
|
||||||
} else {
|
} else {
|
||||||
return XML_STATE_ERROR;
|
return XML_STATE_ERROR;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
return XML_STATE_ERROR;
|
return XML_STATE_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
return XML_STATE_TRACKS;
|
return XML_STATE_TRACKS;
|
||||||
@ -738,7 +739,7 @@ int scan_xml_tracks_section(int action, char *info) {
|
|||||||
state = (c); \
|
state = (c); \
|
||||||
return XML_STATE_PLAYLISTS; \
|
return XML_STATE_PLAYLISTS; \
|
||||||
}}
|
}}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* collect playlist data for each playlist in the itunes xml file
|
* collect playlist data for each playlist in the itunes xml file
|
||||||
* this again is implemented as a sloppy state machine, and assumes
|
* this again is implemented as a sloppy state machine, and assumes
|
||||||
@ -748,7 +749,7 @@ int scan_xml_tracks_section(int action, char *info) {
|
|||||||
* @param info text data associated with event
|
* @param info text data associated with event
|
||||||
*/
|
*/
|
||||||
int scan_xml_playlists_section(int action, char *info) {
|
int scan_xml_playlists_section(int action, char *info) {
|
||||||
static int state = XML_PL_ST_INITIAL;
|
static int state = XML_PL_ST_INITIAL;
|
||||||
static int next_value=0; /** < what's next song info id or name */
|
static int next_value=0; /** < what's next song info id or name */
|
||||||
static int native_plid=0; /** < the iTunes playlist id */
|
static int native_plid=0; /** < the iTunes playlist id */
|
||||||
static int current_id=0; /** < the mt-daapd playlist id */
|
static int current_id=0; /** < the mt-daapd playlist id */
|
||||||
@ -761,112 +762,116 @@ int scan_xml_playlists_section(int action, char *info) {
|
|||||||
|
|
||||||
/* do initialization */
|
/* do initialization */
|
||||||
if(action == RXML_EVT_OPEN) {
|
if(action == RXML_EVT_OPEN) {
|
||||||
state = XML_PL_ST_INITIAL;
|
state = XML_PL_ST_INITIAL;
|
||||||
if(current_name)
|
if(current_name)
|
||||||
free(current_name);
|
free(current_name);
|
||||||
current_name = NULL;
|
current_name = NULL;
|
||||||
dont_scan=0;
|
dont_scan=0;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch(state) {
|
switch(state) {
|
||||||
case XML_PL_ST_INITIAL:
|
case XML_PL_ST_INITIAL:
|
||||||
/* expecting <array> or error */
|
/* expecting <array> or error */
|
||||||
MAYBESETSTATE_PL(RXML_EVT_BEGIN,"array",XML_PL_ST_EXPECTING_PL);
|
MAYBESETSTATE_PL(RXML_EVT_BEGIN,"array",XML_PL_ST_EXPECTING_PL);
|
||||||
return XML_STATE_ERROR;
|
return XML_STATE_ERROR;
|
||||||
case XML_PL_ST_EXPECTING_PL:
|
case XML_PL_ST_EXPECTING_PL:
|
||||||
/* either a new playlist, or end of playlist list */
|
/* either a new playlist, or end of playlist list */
|
||||||
MAYBESETSTATE_PL(RXML_EVT_BEGIN,"dict",XML_PL_ST_EXPECTING_PL_DATA);
|
MAYBESETSTATE_PL(RXML_EVT_BEGIN,"dict",XML_PL_ST_EXPECTING_PL_DATA);
|
||||||
if((action == RXML_EVT_END) && (strcasecmp(info,"array") == 0))
|
if((action == RXML_EVT_END) && (strcasecmp(info,"array") == 0))
|
||||||
return XML_STATE_PREAMBLE;
|
return XML_STATE_PREAMBLE;
|
||||||
return XML_STATE_ERROR;
|
return XML_STATE_ERROR;
|
||||||
case XML_PL_ST_EXPECTING_PL_DATA:
|
case XML_PL_ST_EXPECTING_PL_DATA:
|
||||||
/* either a key/data pair, or an array, signaling start of playlist
|
/* either a key/data pair, or an array, signaling start of playlist
|
||||||
* or the end of the dict (end of playlist data) */
|
* or the end of the dict (end of playlist data) */
|
||||||
MAYBESETSTATE_PL(RXML_EVT_BEGIN,"key",XML_PL_ST_EXPECTING_PL_DATA);
|
MAYBESETSTATE_PL(RXML_EVT_BEGIN,"key",XML_PL_ST_EXPECTING_PL_DATA);
|
||||||
MAYBESETSTATE_PL(RXML_EVT_END,"key",XML_PL_ST_EXPECTING_PL_VALUE);
|
MAYBESETSTATE_PL(RXML_EVT_END,"key",XML_PL_ST_EXPECTING_PL_VALUE);
|
||||||
MAYBESETSTATE_PL(RXML_EVT_END,"dict",XML_PL_ST_EXPECTING_PL);
|
MAYBESETSTATE_PL(RXML_EVT_END,"dict",XML_PL_ST_EXPECTING_PL);
|
||||||
if(action == RXML_EVT_TEXT) {
|
if(action == RXML_EVT_TEXT) {
|
||||||
next_value=XML_PL_NEXT_VALUE_NONE;
|
next_value=XML_PL_NEXT_VALUE_NONE;
|
||||||
if(strcasecmp(info,"Name") == 0) {
|
if(strcasecmp(info,"Name") == 0) {
|
||||||
next_value = XML_PL_NEXT_VALUE_NAME;
|
next_value = XML_PL_NEXT_VALUE_NAME;
|
||||||
} else if(strcasecmp(info,"Playlist ID") == 0) {
|
} else if(strcasecmp(info,"Playlist ID") == 0) {
|
||||||
next_value = XML_PL_NEXT_VALUE_ID;
|
next_value = XML_PL_NEXT_VALUE_ID;
|
||||||
} else if(strcasecmp(info,"Master") == 0) {
|
} else if(strcasecmp(info,"Master") == 0) {
|
||||||
/* No point adding the master library... we have one */
|
/* No point adding the master library... we have one */
|
||||||
dont_scan=1;
|
dont_scan=1;
|
||||||
}
|
}
|
||||||
return XML_STATE_PLAYLISTS;
|
return XML_STATE_PLAYLISTS;
|
||||||
}
|
}
|
||||||
return XML_STATE_ERROR;
|
return XML_STATE_ERROR;
|
||||||
case XML_PL_ST_EXPECTING_PL_VALUE:
|
case XML_PL_ST_EXPECTING_PL_VALUE:
|
||||||
/* any tag, value we are looking for, any close tag */
|
/* any tag, value we are looking for, any close tag */
|
||||||
if((action == RXML_EVT_BEGIN) && (strcasecmp(info,"array") == 0)) {
|
if((action == RXML_EVT_BEGIN) && (strcasecmp(info,"array") == 0)) {
|
||||||
/* we are about to get track list... must register the playlist */
|
/* we are about to get track list... must register the playlist */
|
||||||
current_id=0;
|
current_id=0;
|
||||||
if(dont_scan == 0) {
|
if(dont_scan == 0) {
|
||||||
DPRINTF(E_DBG,L_SCAN,"Creating playlist for %s\n",current_name);
|
DPRINTF(E_DBG,L_SCAN,"Creating playlist for %s\n",current_name);
|
||||||
/* delete the old one first */
|
/* delete the old one first */
|
||||||
pm3u = db_fetch_playlist(scan_xml_file,native_plid);
|
/* FIXME: Error handling */
|
||||||
if(pm3u) {
|
pm3u = db_fetch_playlist(NULL,scan_xml_file,native_plid);
|
||||||
db_delete_playlist(pm3u->id);
|
if(pm3u) {
|
||||||
db_dispose_playlist(pm3u);
|
db_delete_playlist(NULL,pm3u->id);
|
||||||
}
|
db_dispose_playlist(pm3u);
|
||||||
if(db_add_playlist(current_name,PL_STATICXML,NULL,scan_xml_file,
|
}
|
||||||
native_plid,¤t_id) != DB_E_SUCCESS) {
|
if(db_add_playlist(NULL,current_name,PL_STATICXML,NULL,
|
||||||
DPRINTF(E_LOG,L_SCAN,"err adding playlist %s\n",current_name);
|
scan_xml_file,native_plid,
|
||||||
current_id=0;
|
¤t_id) != DB_E_SUCCESS)
|
||||||
}
|
{
|
||||||
}
|
DPRINTF(E_LOG,L_SCAN,"err adding playlist %s\n",current_name);
|
||||||
dont_scan=0;
|
current_id=0;
|
||||||
state=XML_PL_ST_EXPECTING_PL_TRACKLIST;
|
}
|
||||||
return XML_STATE_PLAYLISTS;
|
}
|
||||||
}
|
dont_scan=0;
|
||||||
if(action == RXML_EVT_BEGIN)
|
state=XML_PL_ST_EXPECTING_PL_TRACKLIST;
|
||||||
return XML_STATE_PLAYLISTS;
|
return XML_STATE_PLAYLISTS;
|
||||||
if(action == RXML_EVT_END) {
|
}
|
||||||
state = XML_PL_ST_EXPECTING_PL_DATA;
|
if(action == RXML_EVT_BEGIN)
|
||||||
return XML_STATE_PLAYLISTS;
|
return XML_STATE_PLAYLISTS;
|
||||||
}
|
if(action == RXML_EVT_END) {
|
||||||
if(action == RXML_EVT_TEXT) {
|
state = XML_PL_ST_EXPECTING_PL_DATA;
|
||||||
/* got the value we were hoping for */
|
return XML_STATE_PLAYLISTS;
|
||||||
if(next_value == XML_PL_NEXT_VALUE_NAME) {
|
}
|
||||||
if(current_name)
|
if(action == RXML_EVT_TEXT) {
|
||||||
free(current_name);
|
/* got the value we were hoping for */
|
||||||
current_name = strdup(info);
|
if(next_value == XML_PL_NEXT_VALUE_NAME) {
|
||||||
/* disallow specific playlists */
|
if(current_name)
|
||||||
if(strcasecmp(current_name,"Party Shuffle") == 0) {
|
free(current_name);
|
||||||
dont_scan=1;
|
current_name = strdup(info);
|
||||||
}
|
/* disallow specific playlists */
|
||||||
} else if(next_value == XML_PL_NEXT_VALUE_ID) {
|
if(strcasecmp(current_name,"Party Shuffle") == 0) {
|
||||||
native_plid = atoi(info);
|
dont_scan=1;
|
||||||
}
|
}
|
||||||
return XML_STATE_PLAYLISTS;
|
} else if(next_value == XML_PL_NEXT_VALUE_ID) {
|
||||||
}
|
native_plid = atoi(info);
|
||||||
return XML_STATE_ERROR;
|
}
|
||||||
|
return XML_STATE_PLAYLISTS;
|
||||||
|
}
|
||||||
|
return XML_STATE_ERROR;
|
||||||
|
|
||||||
case XML_PL_ST_EXPECTING_PL_TRACKLIST:
|
case XML_PL_ST_EXPECTING_PL_TRACKLIST:
|
||||||
if((strcasecmp(info,"dict") == 0) || (strcasecmp(info,"key") == 0))
|
if((strcasecmp(info,"dict") == 0) || (strcasecmp(info,"key") == 0))
|
||||||
return XML_STATE_PLAYLISTS;
|
return XML_STATE_PLAYLISTS;
|
||||||
MAYBESETSTATE_PL(RXML_EVT_END,"array",XML_PL_ST_EXPECTING_PL_DATA);
|
MAYBESETSTATE_PL(RXML_EVT_END,"array",XML_PL_ST_EXPECTING_PL_DATA);
|
||||||
if(action == RXML_EVT_TEXT) {
|
if(action == RXML_EVT_TEXT) {
|
||||||
if(strcasecmp(info,"Track ID") != 0) {
|
if(strcasecmp(info,"Track ID") != 0) {
|
||||||
native_track_id = atoi(info);
|
native_track_id = atoi(info);
|
||||||
DPRINTF(E_DBG,L_SCAN,"Adding itunes track #%s\n",info);
|
DPRINTF(E_DBG,L_SCAN,"Adding itunes track #%s\n",info);
|
||||||
/* add it to the current playlist (current_id) */
|
/* add it to the current playlist (current_id) */
|
||||||
if(current_id && scan_xml_get_index(native_track_id, &track_id)) {
|
if(current_id && scan_xml_get_index(native_track_id, &track_id)) {
|
||||||
db_add_playlist_item(current_id,track_id);
|
/* FIXME: Error handling */
|
||||||
}
|
db_add_playlist_item(NULL,current_id,track_id);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return XML_STATE_PLAYLISTS;
|
return XML_STATE_PLAYLISTS;
|
||||||
}
|
}
|
||||||
return XML_STATE_PLAYLISTS;
|
return XML_STATE_PLAYLISTS;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return XML_STATE_ERROR;
|
return XML_STATE_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
return XML_STATE_PLAYLISTS;
|
return XML_STATE_PLAYLISTS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,7 +27,7 @@ typedef struct tag_xmlstack {
|
|||||||
typedef struct tag_xmlstruct {
|
typedef struct tag_xmlstruct {
|
||||||
WS_CONNINFO *pwsc;
|
WS_CONNINFO *pwsc;
|
||||||
int stack_level;
|
int stack_level;
|
||||||
XMLSTACK stack;
|
XMLSTACK stack;
|
||||||
} XMLSTRUCT;
|
} XMLSTRUCT;
|
||||||
|
|
||||||
/* Forwards */
|
/* Forwards */
|
||||||
@ -50,16 +50,16 @@ void xml_deinit(XMLSTRUCT *pxml);
|
|||||||
*/
|
*/
|
||||||
XMLSTRUCT *xml_init(WS_CONNINFO *pwsc, int emit_header) {
|
XMLSTRUCT *xml_init(WS_CONNINFO *pwsc, int emit_header) {
|
||||||
XMLSTRUCT *pxml;
|
XMLSTRUCT *pxml;
|
||||||
|
|
||||||
pxml=(XMLSTRUCT*)malloc(sizeof(XMLSTRUCT));
|
pxml=(XMLSTRUCT*)malloc(sizeof(XMLSTRUCT));
|
||||||
if(!pxml) {
|
if(!pxml) {
|
||||||
DPRINTF(E_FATAL,L_XML,"Malloc error\n");
|
DPRINTF(E_FATAL,L_XML,"Malloc error\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
memset(pxml,0,sizeof(XMLSTRUCT));
|
memset(pxml,0,sizeof(XMLSTRUCT));
|
||||||
|
|
||||||
pxml->pwsc = pwsc;
|
pxml->pwsc = pwsc;
|
||||||
|
|
||||||
if(emit_header) {
|
if(emit_header) {
|
||||||
ws_addresponseheader(pwsc,"Content-Type","text/xml; charset=utf-8");
|
ws_addresponseheader(pwsc,"Content-Type","text/xml; charset=utf-8");
|
||||||
ws_writefd(pwsc,"HTTP/1.0 200 OK\r\n");
|
ws_writefd(pwsc,"HTTP/1.0 200 OK\r\n");
|
||||||
@ -67,7 +67,7 @@ XMLSTRUCT *xml_init(WS_CONNINFO *pwsc, int emit_header) {
|
|||||||
|
|
||||||
ws_writefd(pwsc,"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>");
|
ws_writefd(pwsc,"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>");
|
||||||
}
|
}
|
||||||
|
|
||||||
return pxml;
|
return pxml;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -75,16 +75,16 @@ XMLSTRUCT *xml_init(WS_CONNINFO *pwsc, int emit_header) {
|
|||||||
* push a new term on the stack
|
* push a new term on the stack
|
||||||
*
|
*
|
||||||
* @param pxml xml struct obtained from xml_init
|
* @param pxml xml struct obtained from xml_init
|
||||||
* @param term next xlm section to start
|
* @param term next xlm section to start
|
||||||
*/
|
*/
|
||||||
void xml_push(XMLSTRUCT *pxml, char *term) {
|
void xml_push(XMLSTRUCT *pxml, char *term) {
|
||||||
XMLSTACK *pstack;
|
XMLSTACK *pstack;
|
||||||
|
|
||||||
pstack = (XMLSTACK *)malloc(sizeof(XMLSTACK));
|
pstack = (XMLSTACK *)malloc(sizeof(XMLSTACK));
|
||||||
pstack->next=pxml->stack.next;
|
pstack->next=pxml->stack.next;
|
||||||
pstack->tag=strdup(term);
|
pstack->tag=strdup(term);
|
||||||
pxml->stack.next=pstack;
|
pxml->stack.next=pstack;
|
||||||
|
|
||||||
pxml->stack_level++;
|
pxml->stack_level++;
|
||||||
|
|
||||||
ws_writefd(pxml->pwsc,"<%s>",term);
|
ws_writefd(pxml->pwsc,"<%s>",term);
|
||||||
@ -97,19 +97,19 @@ void xml_push(XMLSTRUCT *pxml, char *term) {
|
|||||||
*/
|
*/
|
||||||
void xml_pop(XMLSTRUCT *pxml) {
|
void xml_pop(XMLSTRUCT *pxml) {
|
||||||
XMLSTACK *pstack;
|
XMLSTACK *pstack;
|
||||||
|
|
||||||
pstack=pxml->stack.next;
|
pstack=pxml->stack.next;
|
||||||
if(!pstack) {
|
if(!pstack) {
|
||||||
DPRINTF(E_LOG,L_XML,"xml_pop: tried to pop an empty stack\n");
|
DPRINTF(E_LOG,L_XML,"xml_pop: tried to pop an empty stack\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
pxml->stack.next = pstack->next;
|
pxml->stack.next = pstack->next;
|
||||||
|
|
||||||
ws_writefd(pxml->pwsc,"</%s>",pstack->tag);
|
ws_writefd(pxml->pwsc,"</%s>",pstack->tag);
|
||||||
free(pstack->tag);
|
free(pstack->tag);
|
||||||
free(pstack);
|
free(pstack);
|
||||||
|
|
||||||
pxml->stack_level--;
|
pxml->stack_level--;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -143,11 +143,11 @@ void xml_output(XMLSTRUCT *pxml, char *section, char *fmt, ...) {
|
|||||||
*/
|
*/
|
||||||
void xml_deinit(XMLSTRUCT *pxml) {
|
void xml_deinit(XMLSTRUCT *pxml) {
|
||||||
XMLSTACK *pstack;
|
XMLSTACK *pstack;
|
||||||
|
|
||||||
if(pxml->stack.next) {
|
if(pxml->stack.next) {
|
||||||
DPRINTF(E_LOG,L_XML,"xml_deinit: entries still on stack (%s)\n",pxml->stack.next->tag);
|
DPRINTF(E_LOG,L_XML,"xml_deinit: entries still on stack (%s)\n",pxml->stack.next->tag);
|
||||||
}
|
}
|
||||||
|
|
||||||
while((pstack=pxml->stack.next)) {
|
while((pstack=pxml->stack.next)) {
|
||||||
pxml->stack.next=pstack->next;
|
pxml->stack.next=pstack->next;
|
||||||
free(pstack->tag);
|
free(pstack->tag);
|
||||||
@ -187,18 +187,18 @@ void xml_get_stats(WS_CONNINFO *pwsc) {
|
|||||||
WS_CONNINFO *pci;
|
WS_CONNINFO *pci;
|
||||||
SCAN_STATUS *pss;
|
SCAN_STATUS *pss;
|
||||||
WSTHREADENUM wste;
|
WSTHREADENUM wste;
|
||||||
|
int count;
|
||||||
XMLSTRUCT *pxml;
|
XMLSTRUCT *pxml;
|
||||||
|
|
||||||
pxml=xml_init(pwsc,1);
|
pxml=xml_init(pwsc,1);
|
||||||
xml_push(pxml,"status");
|
xml_push(pxml,"status");
|
||||||
|
|
||||||
xml_push(pxml,"service_status");
|
xml_push(pxml,"service_status");
|
||||||
|
|
||||||
xml_push(pxml,"service");
|
xml_push(pxml,"service");
|
||||||
|
|
||||||
xml_output(pxml,"name","Rendezvous");
|
xml_output(pxml,"name","Rendezvous");
|
||||||
|
|
||||||
#ifndef WITHOUT_MDNS
|
#ifndef WITHOUT_MDNS
|
||||||
if(config.use_mdns) {
|
if(config.use_mdns) {
|
||||||
xml_output(pxml,"status",rend_running() ? "Stopped" : "Running"); /* ??? */
|
xml_output(pxml,"status",rend_running() ? "Stopped" : "Running"); /* ??? */
|
||||||
@ -222,7 +222,7 @@ void xml_get_stats(WS_CONNINFO *pwsc) {
|
|||||||
xml_pop(pxml); /* service */
|
xml_pop(pxml); /* service */
|
||||||
|
|
||||||
xml_pop(pxml); /* service_status */
|
xml_pop(pxml); /* service_status */
|
||||||
|
|
||||||
xml_push(pxml,"thread_status");
|
xml_push(pxml,"thread_status");
|
||||||
|
|
||||||
pci = ws_thread_enum_first(config.server,&wste);
|
pci = ws_thread_enum_first(config.server,&wste);
|
||||||
@ -237,11 +237,11 @@ void xml_get_stats(WS_CONNINFO *pwsc) {
|
|||||||
}
|
}
|
||||||
pci=ws_thread_enum_next(config.server,&wste);
|
pci=ws_thread_enum_next(config.server,&wste);
|
||||||
}
|
}
|
||||||
|
|
||||||
xml_pop(pxml); /* thread_status */
|
xml_pop(pxml); /* thread_status */
|
||||||
|
|
||||||
xml_push(pxml,"statistics");
|
xml_push(pxml,"statistics");
|
||||||
|
|
||||||
r_secs=time(NULL)-config.stats.start_time;
|
r_secs=time(NULL)-config.stats.start_time;
|
||||||
|
|
||||||
r_days=r_secs/(3600 * 24);
|
r_days=r_secs/(3600 * 24);
|
||||||
@ -254,36 +254,37 @@ void xml_get_stats(WS_CONNINFO *pwsc) {
|
|||||||
r_secs -= 60 * r_mins;
|
r_secs -= 60 * r_mins;
|
||||||
|
|
||||||
memset(buf,0x0,sizeof(buf));
|
memset(buf,0x0,sizeof(buf));
|
||||||
if(r_days)
|
if(r_days)
|
||||||
sprintf((char*)&buf[strlen(buf)],"%d day%s, ", r_days,
|
sprintf((char*)&buf[strlen(buf)],"%d day%s, ", r_days,
|
||||||
r_days == 1 ? "" : "s");
|
r_days == 1 ? "" : "s");
|
||||||
|
|
||||||
if(r_days || r_hours)
|
if(r_days || r_hours)
|
||||||
sprintf((char*)&buf[strlen(buf)],"%d hour%s, ", r_hours,
|
sprintf((char*)&buf[strlen(buf)],"%d hour%s, ", r_hours,
|
||||||
r_hours == 1 ? "" : "s");
|
r_hours == 1 ? "" : "s");
|
||||||
|
|
||||||
if(r_days || r_hours || r_mins)
|
if(r_days || r_hours || r_mins)
|
||||||
sprintf((char*)&buf[strlen(buf)],"%d minute%s, ", r_mins,
|
sprintf((char*)&buf[strlen(buf)],"%d minute%s, ", r_mins,
|
||||||
r_mins == 1 ? "" : "s");
|
r_mins == 1 ? "" : "s");
|
||||||
|
|
||||||
sprintf((char*)&buf[strlen(buf)],"%d second%s ", r_secs,
|
sprintf((char*)&buf[strlen(buf)],"%d second%s ", r_secs,
|
||||||
r_secs == 1 ? "" : "s");
|
r_secs == 1 ? "" : "s");
|
||||||
|
|
||||||
xml_push(pxml,"stat");
|
xml_push(pxml,"stat");
|
||||||
xml_output(pxml,"name","Uptime");
|
xml_output(pxml,"name","Uptime");
|
||||||
xml_output(pxml,"value","%s",buf);
|
xml_output(pxml,"value","%s",buf);
|
||||||
xml_pop(pxml); /* stat */
|
xml_pop(pxml); /* stat */
|
||||||
|
|
||||||
xml_push(pxml,"stat");
|
xml_push(pxml,"stat");
|
||||||
xml_output(pxml,"name","Songs");
|
xml_output(pxml,"name","Songs");
|
||||||
xml_output(pxml,"value","%d",db_get_song_count());
|
db_get_song_count(NULL,&count);
|
||||||
|
xml_output(pxml,"value","%d",count);
|
||||||
xml_pop(pxml); /* stat */
|
xml_pop(pxml); /* stat */
|
||||||
|
|
||||||
xml_push(pxml,"stat");
|
xml_push(pxml,"stat");
|
||||||
xml_output(pxml,"name","Songs Served");
|
xml_output(pxml,"name","Songs Served");
|
||||||
xml_output(pxml,"value","%d",config.stats.songs_served);
|
xml_output(pxml,"value","%d",config.stats.songs_served);
|
||||||
xml_pop(pxml); /* stat */
|
xml_pop(pxml); /* stat */
|
||||||
|
|
||||||
xml_pop(pxml); /* statistics */
|
xml_pop(pxml); /* statistics */
|
||||||
xml_pop(pxml); /* status */
|
xml_pop(pxml); /* status */
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user