mirror of
https://github.com/owntone/owntone-server.git
synced 2024-12-25 14:45:55 -05:00
Add iTunes playlist support
This commit is contained in:
parent
8b30cbce9a
commit
9ae132a127
@ -36,7 +36,7 @@ mt_daapd_SOURCES = main.c daapd.h rend.h uici.c uici.h webserver.c \
|
||||
mp3-scanner.h mp3-scanner.c rend-unix.h strcasestr.c strcasestr.h \
|
||||
strsep.c dynamic-art.c dynamic-art.h query.c query.h ssc.c ssc.h \
|
||||
db-generic.c db-generic.h dispatch.c dispatch.h wma.c \
|
||||
scan-xml.c rxml.c rxml.h \
|
||||
scan-xml.c rxml.c rxml.h redblack.c redblack.h \
|
||||
$(PRENDSRC) $(ORENDSRC) $(HRENDSRC) $(OGGVORBISSRC) $(FLACSRC) \
|
||||
$(SQLITEDB)
|
||||
|
||||
|
@ -46,7 +46,7 @@ typedef struct tag_db_functions {
|
||||
int(*dbs_init)(int);
|
||||
int(*dbs_deinit)(void);
|
||||
int(*dbs_add)(MP3FILE*);
|
||||
int(*dbs_add_playlist)(char *, int, char *,char *, int *);
|
||||
int(*dbs_add_playlist)(char *, int, char *,char *, int, int *);
|
||||
int(*dbs_add_playlist_item)(int, int);
|
||||
int(*dbs_delete_playlist)(int);
|
||||
int(*dbs_delete_playlist_item)(int, int);
|
||||
@ -480,11 +480,11 @@ int db_add(MP3FILE *pmp3) {
|
||||
* \param playlistid returns the id of the playlist created
|
||||
* \returns 0 on success, error code otherwise
|
||||
*/
|
||||
int db_add_playlist(char *name, int type, char *clause, char *path, int *playlistid) {
|
||||
int db_add_playlist(char *name, int type, char *clause, char *path, int index, int *playlistid) {
|
||||
int retval;
|
||||
|
||||
db_writelock();
|
||||
retval=db_current->dbs_add_playlist(name,type,clause,path,playlistid);
|
||||
retval=db_current->dbs_add_playlist(name,type,clause,path,index,playlistid);
|
||||
if(retval == DB_E_SUCCESS)
|
||||
db_revision_no++;
|
||||
db_unlock();
|
||||
|
@ -156,7 +156,7 @@ extern int db_end_scan(void);
|
||||
extern int db_exists(char *path);
|
||||
extern int db_scanning(void);
|
||||
|
||||
extern int db_add_playlist(char *name, int type, char *clause, char *path,int *playlistid);
|
||||
extern int db_add_playlist(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_delete_playlist(int playlistid);
|
||||
extern int db_delete_playlist_item(int playlistid, int songid);
|
||||
|
@ -405,7 +405,7 @@ extern int db_sqlite_delete_playlist_item(int playlistid, int songid) {
|
||||
* \param type playlist type: 0 - static, 1 - smart, 2 - m3u
|
||||
* \param clause: "where" clause for smart playlist
|
||||
*/
|
||||
int db_sqlite_add_playlist(char *name, int type, char *clause, char *path, int *playlistid) {
|
||||
int db_sqlite_add_playlist(char *name, int type, char *clause, char *path, int index, int *playlistid) {
|
||||
int cnt=0;
|
||||
int result=DB_E_SUCCESS;
|
||||
|
||||
@ -419,16 +419,17 @@ int db_sqlite_add_playlist(char *name, int type, char *clause, char *path, int *
|
||||
switch(type) {
|
||||
case PL_STATICWEB: /* static, maintained in web interface */
|
||||
case PL_STATICFILE: /* static, from file */
|
||||
case PL_STATICXML: /* from iTunes XML file */
|
||||
result = db_sqlite_exec(E_LOG,"insert into playlists "
|
||||
"(title,type,items,query,db_timestamp,path) "
|
||||
"values ('%q',%d,0,NULL,%d,'%q')",name,type,time(NULL),path);
|
||||
"(title,type,items,query,db_timestamp,path,idx) "
|
||||
"values ('%q',%d,0,NULL,%d,'%q',%d)",name,type,time(NULL),path,index);
|
||||
break;
|
||||
case PL_SMART: /* smart */
|
||||
result=db_sqlite_get_int(E_DBG,&cnt,"select count (*) from songs where %s",clause);
|
||||
if(result != DB_E_SUCCESS) return result;
|
||||
result = db_sqlite_exec(E_LOG,"insert into playlists "
|
||||
"(title,type,items,query,db_timestamp) "
|
||||
"values ('%q',%d,%d,'%q',%d)",name,PL_SMART,cnt,clause,time(NULL));
|
||||
"(title,type,items,query,db_timestamp,idx) "
|
||||
"values ('%q',%d,%d,'%q',%d,0)",name,PL_SMART,cnt,clause,time(NULL));
|
||||
break;
|
||||
}
|
||||
|
||||
@ -439,7 +440,7 @@ int db_sqlite_add_playlist(char *name, int type, char *clause, char *path, int *
|
||||
"select id from playlists where title='%q'", name);
|
||||
|
||||
if(((type==PL_STATICFILE)||(type==PL_STATICXML))
|
||||
&& (db_sqlite_in_scan) && (!db_sqlite_reload)) {
|
||||
&& (db_sqlite_in_playlist_scan) && (!db_sqlite_reload)) {
|
||||
db_sqlite_exec(E_FATAL,"insert into plupdated values (%d)",*playlistid);
|
||||
}
|
||||
|
||||
@ -1307,7 +1308,7 @@ void db_sqlite_build_m3ufile(char **valarray, M3UFILE *pm3u) {
|
||||
pm3u->query=db_sqlite_strdup(valarray[4]);
|
||||
pm3u->db_timestamp=db_sqlite_atoi(valarray[5]);
|
||||
pm3u->path=db_sqlite_strdup(valarray[6]);
|
||||
pm3u->index=0; //db_sqlite_atoi(valarray[7]);
|
||||
pm3u->index=db_sqlite_atoi(valarray[7]);
|
||||
return;
|
||||
}
|
||||
void db_sqlite_build_mp3file(char **valarray, MP3FILE *pmp3) {
|
||||
@ -1364,7 +1365,8 @@ M3UFILE *db_sqlite_fetch_playlist(char *path, int index) {
|
||||
M3UFILE *pm3u=NULL;
|
||||
|
||||
result = db_sqlite_get_table(E_DBG,&resarray,&rows,&cols,
|
||||
"select * from playlists where path='%q'",path);
|
||||
"select * from playlists where path='%q' and idx=%d",
|
||||
path,index);
|
||||
|
||||
if(result != DB_E_SUCCESS)
|
||||
return NULL;
|
||||
@ -1372,7 +1374,7 @@ M3UFILE *db_sqlite_fetch_playlist(char *path, int index) {
|
||||
if(rows != 0) {
|
||||
pm3u=(M3UFILE*)malloc(sizeof(M3UFILE));
|
||||
if(!pm3u)
|
||||
DPRINTF(E_FATAL,L_MISC,"malloc error in db_sqlite_fetch_playlist\n");
|
||||
DPRINTF(E_FATAL,L_MISC,"malloc error: db_sqlite_fetch_playlist\n");
|
||||
|
||||
db_sqlite_build_m3ufile((char**)&resarray[cols],pm3u);
|
||||
}
|
||||
@ -1396,7 +1398,8 @@ MP3FILE *db_sqlite_fetch_item(int id) {
|
||||
char **resarray;
|
||||
MP3FILE *pmp3=NULL;
|
||||
|
||||
db_sqlite_get_table(E_DBG,&resarray,&rows,&cols,"SELECT * FROM songs WHERE id=%d",id);
|
||||
db_sqlite_get_table(E_DBG,&resarray,&rows,&cols,
|
||||
"SELECT * FROM songs WHERE id=%d",id);
|
||||
|
||||
if(rows != 0) {
|
||||
pmp3=(MP3FILE*)malloc(sizeof(MP3FILE));
|
||||
@ -1425,7 +1428,8 @@ MP3FILE *db_sqlite_fetch_path(char *path) {
|
||||
char **resarray;
|
||||
MP3FILE *pmp3=NULL;
|
||||
|
||||
db_sqlite_get_table(E_DBG,&resarray,&rows,&cols,"SELECT * FROM songs WHERE path='%q'",path);
|
||||
db_sqlite_get_table(E_DBG,&resarray,&rows,&cols,
|
||||
"SELECT * FROM songs WHERE path='%q'",path);
|
||||
|
||||
if(rows != 0) {
|
||||
pmp3=(MP3FILE*)malloc(sizeof(MP3FILE));
|
||||
@ -1505,7 +1509,8 @@ int db_sqlite_get_count(CountType_t type) {
|
||||
break;
|
||||
}
|
||||
|
||||
db_sqlite_get_table(E_DBG,&resarray, &rows, &cols,"SELECT COUNT(*) FROM %q", table);
|
||||
db_sqlite_get_table(E_DBG,&resarray, &rows, &cols,
|
||||
"SELECT COUNT(*) FROM %q", table);
|
||||
|
||||
if(rows != 0) {
|
||||
retval=atoi(resarray[cols]);
|
||||
@ -1683,6 +1688,25 @@ char *db_sqlite_upgrade_scripts[] = {
|
||||
"insert into playlists select *,0,NULL from tempplaylists;\n"
|
||||
"drop table tempplaylists;\n"
|
||||
"update config set value=4 where term='version';\n",
|
||||
|
||||
/* version 4 -> version 5 */
|
||||
/* add index to playlist table */
|
||||
"create temp table tempplaylists as select * from playlists;\n"
|
||||
"drop table playlists;\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 playlists select *,0 from tempplaylists;\n"
|
||||
"drop table tempplaylists;\n"
|
||||
"update config set value=5 where term='version';\n",
|
||||
|
||||
NULL /* No more versions! */
|
||||
};
|
||||
|
||||
@ -1712,13 +1736,15 @@ int db_sqlite_update_version(int from_version) {
|
||||
to_fd=r_open3(db_new_path,O_RDWR | O_CREAT,0666);
|
||||
|
||||
if((from_fd == -1) || (to_fd == -1)) {
|
||||
DPRINTF(E_FATAL,L_DB,"Could not make backup copy of database (%s). Check write permissions for runas user.\n",db_new_path);
|
||||
DPRINTF(E_FATAL,L_DB,"Could not make backup copy of database "
|
||||
"(%s). Check write permissions for runas user.\n",
|
||||
db_new_path);
|
||||
}
|
||||
|
||||
while((result=readwrite(from_fd, to_fd) > 0));
|
||||
|
||||
if(result == -1) {
|
||||
DPRINTF(E_FATAL,L_DB,"Could not make backup copy of database (%s)\n",
|
||||
DPRINTF(E_FATAL,L_DB,"Could not make db backup (%s)\n",
|
||||
strerror(errno));
|
||||
}
|
||||
|
||||
@ -1728,10 +1754,11 @@ int db_sqlite_update_version(int from_version) {
|
||||
copied=1;
|
||||
}
|
||||
|
||||
if(db_sqlite_exec(E_LOG,db_sqlite_upgrade_scripts[from_version])!=DB_E_SUCCESS) {
|
||||
DPRINTF(E_FATAL,L_DB,"Error upgrading database. A backup copy of your "
|
||||
"original database is located at %s. Please save it somewhere "
|
||||
"and report to the forums at www.mt-daapd.org. Thanks.\n",
|
||||
if(db_sqlite_exec(E_LOG,db_sqlite_upgrade_scripts[from_version]) != DB_E_SUCCESS) {
|
||||
DPRINTF(E_FATAL,L_DB,"Error upgrading database. A backup copy of "
|
||||
"you original database is located at %s. Please save it "
|
||||
" somewhere and report to the forums at www.mt-daapd.org. "
|
||||
" Thanks.\n",
|
||||
db_new_path);
|
||||
}
|
||||
from_version++;
|
||||
|
@ -40,7 +40,7 @@ extern MP3FILE *db_sqlite_fetch_path(char *path);
|
||||
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 *playlistid);
|
||||
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_delete_playlist(int playlistid);
|
||||
extern int db_sqlite_delete_playlist_item(int playlistid, int songid);
|
||||
|
@ -924,7 +924,7 @@ void dispatch_addplaylist(WS_CONNINFO *pwsc, DBQUERYINFO *pqi) {
|
||||
name=ws_getvar(pwsc,"dmap.itemname");
|
||||
query=ws_getvar(pwsc,"org.mt-daapd.smart-playlist-spec");
|
||||
|
||||
retval=db_add_playlist(name,type,query,NULL,&playlistid);
|
||||
retval=db_add_playlist(name,type,query,NULL,0,&playlistid);
|
||||
if(retval != DB_E_SUCCESS) {
|
||||
DPRINTF(E_LOG,L_DAAP,"error adding playlist. aborting\n");
|
||||
ws_returnerror(pwsc,500,"error adding playlist");
|
||||
|
@ -421,6 +421,8 @@ int main(int argc, char *argv[]) {
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
DPRINTF(E_LOG,"Starting with debuglevel %d\n",err_debuglevel);
|
||||
|
||||
if(!foreground) {
|
||||
if(config.logfile) {
|
||||
err_setdest(config.logfile,LOGDEST_LOGFILE);
|
||||
|
@ -326,17 +326,18 @@ int scan_xml_playlist(char *filename);
|
||||
* way we currently are: using extension or whatver.
|
||||
*
|
||||
* This means that you can test to see if something is, say, an un-drmed
|
||||
* aac file by just testing for ->type "m4a", rather than checking every different
|
||||
* flavor of file extension.
|
||||
* aac file by just testing for ->type "m4a", rather than checking every
|
||||
* different flavor of file extension.
|
||||
*
|
||||
* NOTE: Although they are represented here as strings, the codectype is *really*
|
||||
* an unsigned short. So when it gets serialized, it gets serialized as a short int.
|
||||
* If you put something other than 3 or 4 characters as your codectype, you'll see
|
||||
* strange results.
|
||||
* NOTE: Although they are represented here as strings, the codectype is
|
||||
* *really* an unsigned short. So when it gets serialized, it gets
|
||||
* serialized as a short int. If you put something other than 3 or 4
|
||||
* characters as your codectype, you'll see strange results.
|
||||
*
|
||||
* FIXME: url != pls -- this method of dispatching handlers based on file type
|
||||
* is completely wrong. There needs to be a separate type that gets carried around
|
||||
* with it, at least outside the database that says where the info CAME FROM.
|
||||
* is completely wrong. There needs to be a separate type that gets carried
|
||||
* around with it, at least outside the database that says where the info
|
||||
* CAME FROM.
|
||||
*
|
||||
* This system is broken, and won't work with something like a .cue file
|
||||
*/
|
||||
@ -481,7 +482,7 @@ int scan_init(char *path) {
|
||||
*/
|
||||
int scan_path(char *path) {
|
||||
DIR *current_dir;
|
||||
char de[sizeof(struct dirent) + MAXNAMLEN + 1]; /* overcommit for solaris */
|
||||
char de[sizeof(struct dirent) + MAXNAMLEN + 1]; /* extra for solaris */
|
||||
struct dirent *pde;
|
||||
int err;
|
||||
char relative_path[PATH_MAX];
|
||||
@ -498,7 +499,7 @@ int scan_path(char *path) {
|
||||
|
||||
while(1) {
|
||||
if(config.stop) {
|
||||
DPRINTF(E_WARN,L_SCAN,"Stop detected. Aborting scan of %s.\n",path);
|
||||
DPRINTF(E_WARN,L_SCAN,"Stop req. Aborting scan of %s.\n",path);
|
||||
closedir(current_dir);
|
||||
return 0;
|
||||
}
|
||||
@ -610,7 +611,7 @@ void scan_static_playlist(char *path) {
|
||||
|
||||
fd=open(path,O_RDONLY);
|
||||
if(fd != -1) {
|
||||
if(db_add_playlist(base_path,PL_STATICFILE,NULL,path,&playlistid) != DB_E_SUCCESS) {
|
||||
if(db_add_playlist(base_path,PL_STATICFILE,NULL,path,0,&playlistid) != DB_E_SUCCESS) {
|
||||
DPRINTF(E_LOG,L_SCAN,"Error adding m3u playlist %s\n",path);
|
||||
db_dispose_playlist(pm3u);
|
||||
return;
|
||||
|
278
src/scan-xml.c
278
src/scan-xml.c
@ -33,6 +33,7 @@
|
||||
#include "err.h"
|
||||
#include "mp3-scanner.h"
|
||||
#include "rxml.h"
|
||||
#include "redblack.h"
|
||||
|
||||
/* Forwards */
|
||||
int scan_xml_playlist(char *filename);
|
||||
@ -40,18 +41,21 @@ void scan_xml_handler(int action,void* puser,char* info);
|
||||
int scan_xml_preamble_section(int action,char *info);
|
||||
int scan_xml_tracks_section(int action,char *info);
|
||||
int scan_xml_playlists_section(int action,char *info);
|
||||
void scan_xml_add_lookup(int itunes_index, int mtd_index);
|
||||
|
||||
/* Globals */
|
||||
static char *scan_xml_itunes_version = NULL;
|
||||
static char *scan_xml_itunes_base_path = NULL;
|
||||
static char *scan_xml_itunes_decoded_base_path = NULL;
|
||||
static char *scan_xml_real_base_path = NULL;
|
||||
static char *scan_xml_file; /** < The actual file we are scanning */
|
||||
static struct rbtree *scan_xml_db;
|
||||
|
||||
#define MAYBECOPY(a) if(mp3.a) pmp3->a = mp3.a
|
||||
#define MAYBECOPYSTRING(a) if(mp3.a) { free(pmp3->a); pmp3->a = mp3.a; }
|
||||
|
||||
#define MAYBEFREE(a) if((a)) { free((a)); (a)=NULL; }
|
||||
|
||||
/** iTunes xml values we are interested in */
|
||||
static char *scan_xml_track_tags[] = {
|
||||
"Name",
|
||||
"Artist",
|
||||
@ -73,6 +77,7 @@ static char *scan_xml_track_tags[] = {
|
||||
NULL
|
||||
};
|
||||
|
||||
/** Indexes to the iTunes xml fields we are interested in */
|
||||
#define SCAN_XML_T_UNKNOWN -1
|
||||
#define SCAN_XML_T_NAME 0
|
||||
#define SCAN_XML_T_ARTIST 1
|
||||
@ -92,6 +97,82 @@ static char *scan_xml_track_tags[] = {
|
||||
#define SCAN_XML_T_COMPILATION 15
|
||||
#define SCAN_XML_T_LOCATION 16
|
||||
|
||||
|
||||
#ifndef TRUE
|
||||
# define TRUE 1
|
||||
# define FALSE 0
|
||||
#endif
|
||||
|
||||
typedef struct scan_xml_rb_t {
|
||||
int itunes_index;
|
||||
int mtd_index;
|
||||
} SCAN_XML_RB;
|
||||
|
||||
/**
|
||||
* comparison for the red-black tree. @see redblack.c
|
||||
*
|
||||
* @param pa one node to compare
|
||||
* @param pb other node to compare
|
||||
* @param cfg I have no idea
|
||||
*/
|
||||
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)
|
||||
return -1;
|
||||
if(((SCAN_XML_RB*)pb)->itunes_index < ((SCAN_XML_RB*)pa)->itunes_index)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* add a mapping from iTunes song index to the mt-daapd song
|
||||
* index. This is so we can add resolve mt-daapd song indexes
|
||||
* when it comes time to build the iTunes playlist
|
||||
*
|
||||
* @param itunes_index the index from the itunes xml file
|
||||
* @param mtd_index the index from db_fetch_path
|
||||
*/
|
||||
void scan_xml_add_lookup(int itunes_index, int mtd_index) {
|
||||
SCAN_XML_RB *pnew;
|
||||
const void *val;
|
||||
|
||||
pnew=(SCAN_XML_RB*)malloc(sizeof(SCAN_XML_RB));
|
||||
if(!pnew)
|
||||
DPRINTF(E_FATAL,L_SCAN,"malloc error in scan_xml_add_lookup\n");
|
||||
|
||||
pnew->itunes_index = itunes_index;
|
||||
pnew->mtd_index = mtd_index;
|
||||
|
||||
val = rbsearch((const void*)pnew,scan_xml_db);
|
||||
if(!val) {
|
||||
/* couldn't alloc the rb tree structure -- if we don't
|
||||
* die now, we are going to soon enough*/
|
||||
DPRINTF(E_FATAL,L_SCAN,"redblack tree insert error\n");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the mt-daapd index that corresponds with a particular
|
||||
* itunes song id
|
||||
*
|
||||
* @param itunes_index index from the iTunes xml file
|
||||
* @returns the mt-daapd index
|
||||
*/
|
||||
int scan_xml_get_index(int itunes_index, int *mtd_index) {
|
||||
SCAN_XML_RB rb;
|
||||
SCAN_XML_RB *prb;
|
||||
|
||||
rb.itunes_index = itunes_index;
|
||||
prb = (SCAN_XML_RB*) rbfind((void*)&rb,scan_xml_db);
|
||||
if(prb) {
|
||||
*mtd_index = prb->mtd_index;
|
||||
DPRINTF(E_SPAM,L_SCAN,"Matching %d to %d\n",itunes_index,*mtd_index);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* get the tag index of a particular tag
|
||||
*
|
||||
@ -177,6 +258,10 @@ char *scan_xml_urldecode(char *string, int space_as_plus) {
|
||||
*/
|
||||
int scan_xml_playlist(char *filename) {
|
||||
char *working_base;
|
||||
const void *val;
|
||||
SCAN_XML_RB *lookup_ptr;
|
||||
SCAN_XML_RB lookup_val;
|
||||
|
||||
RXMLHANDLE xml_handle;
|
||||
|
||||
MAYBEFREE(scan_xml_itunes_version);
|
||||
@ -184,6 +269,13 @@ int scan_xml_playlist(char *filename) {
|
||||
MAYBEFREE(scan_xml_itunes_decoded_base_path);
|
||||
MAYBEFREE(scan_xml_real_base_path);
|
||||
|
||||
scan_xml_file = filename;
|
||||
|
||||
/* initialize the redblack tree */
|
||||
if((scan_xml_db = rbinit(scan_xml_rb_compare,NULL)) == NULL) {
|
||||
DPRINTF(E_LOG,L_SCAN,"Could not initialize red/black tree\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* find the base dir of the itunes playlist itself */
|
||||
working_base = strdup(filename);
|
||||
@ -208,6 +300,18 @@ int scan_xml_playlist(char *filename) {
|
||||
}
|
||||
|
||||
rxml_close(xml_handle);
|
||||
|
||||
/* destroy the redblack tree */
|
||||
val = rblookup(RB_LUFIRST,NULL,scan_xml_db);
|
||||
while(val) {
|
||||
lookup_val.itunes_index = ((SCAN_XML_RB*)val)->itunes_index;
|
||||
lookup_ptr = (SCAN_XML_RB *)rbdelete((void*)&lookup_val,scan_xml_db);
|
||||
if(lookup_ptr)
|
||||
free(lookup_ptr);
|
||||
val = rblookup(RB_LUFIRST,NULL,scan_xml_db);
|
||||
}
|
||||
|
||||
rbdestroy(scan_xml_db);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -261,10 +365,11 @@ void scan_xml_handler(int action,void* puser,char* info) {
|
||||
}
|
||||
}
|
||||
|
||||
#define SCAN_XML_PRE_NOTHING 0
|
||||
#define SCAN_XML_PRE_VERSION 1
|
||||
#define SCAN_XML_PRE_PATH 2
|
||||
#define SCAN_XML_PRE_DONE 3
|
||||
#define SCAN_XML_PRE_NOTHING 0
|
||||
#define SCAN_XML_PRE_VERSION 1
|
||||
#define SCAN_XML_PRE_PATH 2
|
||||
#define SCAN_XML_PRE_TRACKS 3
|
||||
#define SCAN_XML_PRE_PLAYLISTS 4
|
||||
|
||||
/**
|
||||
* collect preamble data... version, library id, etc.
|
||||
@ -283,8 +388,16 @@ int scan_xml_preamble_section(int action, char *info) {
|
||||
break;
|
||||
|
||||
case RXML_EVT_END:
|
||||
if(expecting_next == SCAN_XML_PRE_DONE) /* end of tracks tag */
|
||||
if(expecting_next == SCAN_XML_PRE_TRACKS) { /* end of tracks tag */
|
||||
expecting_next=0;
|
||||
DPRINTF(E_DBG,L_SCAN,"Scanning tracks\n");
|
||||
return XML_STATE_TRACKS;
|
||||
}
|
||||
if(expecting_next == SCAN_XML_PRE_PLAYLISTS) {
|
||||
expecting_next=0;
|
||||
DPRINTF(E_DBG,L_SCAN,"Scanning playlists\n");
|
||||
return XML_STATE_PLAYLISTS;
|
||||
}
|
||||
break;
|
||||
|
||||
case RXML_EVT_TEXT: /* scan for the tags we expect */
|
||||
@ -294,7 +407,9 @@ int scan_xml_preamble_section(int action, char *info) {
|
||||
} else if (strcmp(info,"Music Folder") == 0) {
|
||||
expecting_next = SCAN_XML_PRE_PATH;
|
||||
} else if (strcmp(info,"Tracks") == 0) {
|
||||
expecting_next = SCAN_XML_PRE_DONE;
|
||||
expecting_next = SCAN_XML_PRE_TRACKS;
|
||||
} else if (strcmp(info,"Playlists") == 0) {
|
||||
expecting_next = SCAN_XML_PRE_PLAYLISTS;
|
||||
}
|
||||
} else {
|
||||
/* we were expecting someting! */
|
||||
@ -333,7 +448,6 @@ int scan_xml_preamble_section(int action, char *info) {
|
||||
#define XML_TRACK_ST_EXPECTING_TRACK_DICT 3
|
||||
#define XML_TRACK_ST_TRACK_INFO 4
|
||||
#define XML_TRACK_ST_TRACK_DATA 5
|
||||
#define XML_TRACK_ST_EXPECTING_PLAYLISTS 6
|
||||
|
||||
/**
|
||||
* collect track data for each track in the itunes playlist
|
||||
@ -341,10 +455,9 @@ int scan_xml_preamble_section(int action, char *info) {
|
||||
* @param action xml action (RXML_EVT_TEXT, etc)
|
||||
* @param info text data associated with event
|
||||
*/
|
||||
#define MAYBESETSTATE(a,b,c) { if((action==(a)) && \
|
||||
#define MAYBESETSTATE_TR(a,b,c) { if((action==(a)) && \
|
||||
(strcmp(info,(b)) == 0)) { \
|
||||
state = (c); \
|
||||
DPRINTF(E_SPAM,L_SCAN,"New state: %d\n",state); \
|
||||
return XML_STATE_TRACKS; \
|
||||
}}
|
||||
|
||||
@ -353,7 +466,7 @@ int scan_xml_tracks_section(int action, char *info) {
|
||||
static int current_track_id;
|
||||
static int current_field;
|
||||
static MP3FILE mp3;
|
||||
static char *song_path;
|
||||
static char *song_path=NULL;
|
||||
char physical_path[PATH_MAX];
|
||||
char real_path[PATH_MAX];
|
||||
MP3FILE *pmp3;
|
||||
@ -369,21 +482,23 @@ int scan_xml_tracks_section(int action, char *info) {
|
||||
switch(state) {
|
||||
case XML_TRACK_ST_INITIAL:
|
||||
/* expection only a <dict> */
|
||||
MAYBESETSTATE(RXML_EVT_BEGIN,"dict",XML_TRACK_ST_MAIN_DICT);
|
||||
MAYBESETSTATE_TR(RXML_EVT_BEGIN,"dict",XML_TRACK_ST_MAIN_DICT);
|
||||
return XML_STATE_ERROR;
|
||||
break;
|
||||
|
||||
case XML_TRACK_ST_MAIN_DICT:
|
||||
/* either get a <key>, or a </dict> */
|
||||
MAYBESETSTATE(RXML_EVT_BEGIN,"key",XML_TRACK_ST_EXPECTING_TRACK_ID);
|
||||
MAYBESETSTATE(RXML_EVT_END,"dict",XML_TRACK_ST_EXPECTING_PLAYLISTS);
|
||||
MAYBESETSTATE_TR(RXML_EVT_BEGIN,"key",XML_TRACK_ST_EXPECTING_TRACK_ID);
|
||||
if ((action == RXML_EVT_END) && (strcasecmp(info,"dict") == 0)) {
|
||||
return XML_STATE_PREAMBLE;
|
||||
}
|
||||
return XML_STATE_ERROR;
|
||||
break;
|
||||
|
||||
case XML_TRACK_ST_EXPECTING_TRACK_ID:
|
||||
/* this is somewhat loose - <key>id</key> */
|
||||
MAYBESETSTATE(RXML_EVT_BEGIN,"key",XML_TRACK_ST_EXPECTING_TRACK_ID);
|
||||
MAYBESETSTATE(RXML_EVT_END,"key",XML_TRACK_ST_EXPECTING_TRACK_DICT);
|
||||
MAYBESETSTATE_TR(RXML_EVT_BEGIN,"key",XML_TRACK_ST_EXPECTING_TRACK_ID);
|
||||
MAYBESETSTATE_TR(RXML_EVT_END,"key",XML_TRACK_ST_EXPECTING_TRACK_DICT);
|
||||
if (action == RXML_EVT_TEXT) {
|
||||
current_track_id = atoi(info);
|
||||
DPRINTF(E_DBG,L_SCAN,"Scanning iTunes id #%d\n",current_track_id);
|
||||
@ -394,14 +509,14 @@ int scan_xml_tracks_section(int action, char *info) {
|
||||
|
||||
case XML_TRACK_ST_EXPECTING_TRACK_DICT:
|
||||
/* waiting for a dict */
|
||||
MAYBESETSTATE(RXML_EVT_BEGIN,"dict",XML_TRACK_ST_TRACK_INFO);
|
||||
MAYBESETSTATE_TR(RXML_EVT_BEGIN,"dict",XML_TRACK_ST_TRACK_INFO);
|
||||
return XML_STATE_ERROR;
|
||||
break;
|
||||
|
||||
case XML_TRACK_ST_TRACK_INFO:
|
||||
/* again, kind of loose */
|
||||
MAYBESETSTATE(RXML_EVT_BEGIN,"key",XML_TRACK_ST_TRACK_INFO);
|
||||
MAYBESETSTATE(RXML_EVT_END,"key",XML_TRACK_ST_TRACK_DATA);
|
||||
MAYBESETSTATE_TR(RXML_EVT_BEGIN,"key",XML_TRACK_ST_TRACK_INFO);
|
||||
MAYBESETSTATE_TR(RXML_EVT_END,"key",XML_TRACK_ST_TRACK_DATA);
|
||||
if(action == RXML_EVT_TEXT) {
|
||||
current_field=scan_xml_get_tagindex(info);
|
||||
if(current_field == SCAN_XML_T_DISABLED) {
|
||||
@ -438,6 +553,9 @@ int scan_xml_tracks_section(int action, char *info) {
|
||||
MAYBECOPY(disc);
|
||||
MAYBECOPY(total_discs);
|
||||
|
||||
/* must add to the red-black tree */
|
||||
scan_xml_add_lookup(current_track_id,pmp3->id);
|
||||
|
||||
db_add(pmp3);
|
||||
db_dispose_item(pmp3);
|
||||
|
||||
@ -498,6 +616,23 @@ int scan_xml_tracks_section(int action, char *info) {
|
||||
return XML_STATE_TRACKS;
|
||||
}
|
||||
|
||||
|
||||
#define XML_PL_ST_INITIAL 0
|
||||
#define XML_PL_ST_EXPECTING_PL 1
|
||||
#define XML_PL_ST_EXPECTING_PL_DATA 2
|
||||
#define XML_PL_ST_EXPECTING_PL_VALUE 3
|
||||
#define XML_PL_ST_EXPECTING_PL_TRACKLIST 4
|
||||
|
||||
#define XML_PL_NEXT_VALUE_NONE 0
|
||||
#define XML_PL_NEXT_VALUE_NAME 1
|
||||
#define XML_PL_NEXT_VALUE_ID 2
|
||||
|
||||
#define MAYBESETSTATE_PL(a,b,c) { if((action==(a)) && \
|
||||
(strcmp(info,(b)) == 0)) { \
|
||||
state = (c); \
|
||||
return XML_STATE_PLAYLISTS; \
|
||||
}}
|
||||
|
||||
/**
|
||||
* collect playlist data for each playlist in the itunes xml file
|
||||
*
|
||||
@ -505,6 +640,111 @@ int scan_xml_tracks_section(int action, char *info) {
|
||||
* @param info text data associated with event
|
||||
*/
|
||||
int scan_xml_playlists_section(int action, char *info) {
|
||||
static int state = XML_PL_ST_INITIAL;
|
||||
static int next_value=0; /** < what's next song info id or name */
|
||||
static int native_plid=0; /** < the iTunes playlist id */
|
||||
static int current_id=0; /** < the mt-daapd playlist id */
|
||||
static char *current_name=NULL; /** < the iTunes playlist name */
|
||||
int native_track_id; /** < the iTunes id of the track */
|
||||
int track_id; /** < the mt-daapd track id */
|
||||
M3UFILE *pm3u;
|
||||
|
||||
/* do initialization */
|
||||
if(action == RXML_EVT_OPEN) {
|
||||
state = XML_PL_ST_INITIAL;
|
||||
if(current_name)
|
||||
free(current_name);
|
||||
current_name = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch(state) {
|
||||
case XML_PL_ST_INITIAL:
|
||||
/* expecting <array> or error */
|
||||
MAYBESETSTATE_PL(RXML_EVT_BEGIN,"array",XML_PL_ST_EXPECTING_PL);
|
||||
return XML_STATE_ERROR;
|
||||
case XML_PL_ST_EXPECTING_PL:
|
||||
/* either a new playlist, or end of playlist list */
|
||||
MAYBESETSTATE_PL(RXML_EVT_BEGIN,"dict",XML_PL_ST_EXPECTING_PL_DATA);
|
||||
if((action == RXML_EVT_END) && (strcasecmp(info,"array") == 0))
|
||||
return XML_STATE_PREAMBLE;
|
||||
return XML_STATE_ERROR;
|
||||
case XML_PL_ST_EXPECTING_PL_DATA:
|
||||
/* either a key/data pair, or an array, signaling start of playlist
|
||||
* 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_END,"key",XML_PL_ST_EXPECTING_PL_VALUE);
|
||||
MAYBESETSTATE_PL(RXML_EVT_END,"dict",XML_PL_ST_EXPECTING_PL);
|
||||
if(action == RXML_EVT_TEXT) {
|
||||
next_value=XML_PL_NEXT_VALUE_NONE;
|
||||
if(strcasecmp(info,"Name") == 0) {
|
||||
next_value = XML_PL_NEXT_VALUE_NAME;
|
||||
} else if(strcasecmp(info,"Playlist ID") == 0) {
|
||||
next_value = XML_PL_NEXT_VALUE_ID;
|
||||
}
|
||||
return XML_STATE_PLAYLISTS;
|
||||
}
|
||||
return XML_STATE_ERROR;
|
||||
case XML_PL_ST_EXPECTING_PL_VALUE:
|
||||
/* any tag, value we are looking for, any close tag */
|
||||
if((action == RXML_EVT_BEGIN) && (strcasecmp(info,"array") == 0)) {
|
||||
/* we are about to get track list... must register the playlist */
|
||||
DPRINTF(E_DBG,L_SCAN,"Creating playlist for %s\n",current_name);
|
||||
/* delete the old one first */
|
||||
pm3u = db_fetch_playlist(scan_xml_file,native_plid);
|
||||
if(pm3u) {
|
||||
db_delete_playlist(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) {
|
||||
DPRINTF(E_LOG,L_SCAN,"err adding playlist %s\n",current_name);
|
||||
current_id=0;
|
||||
}
|
||||
state=XML_PL_ST_EXPECTING_PL_TRACKLIST;
|
||||
return XML_STATE_PLAYLISTS;
|
||||
}
|
||||
if(action == RXML_EVT_BEGIN)
|
||||
return XML_STATE_PLAYLISTS;
|
||||
if(action == RXML_EVT_END) {
|
||||
state = XML_PL_ST_EXPECTING_PL_DATA;
|
||||
return XML_STATE_PLAYLISTS;
|
||||
}
|
||||
if(action == RXML_EVT_TEXT) {
|
||||
/* got the value we were hoping for */
|
||||
if(next_value == XML_PL_NEXT_VALUE_NAME) {
|
||||
if(current_name)
|
||||
free(current_name);
|
||||
current_name = strdup(info);
|
||||
} else if(next_value == XML_PL_NEXT_VALUE_ID) {
|
||||
native_plid = atoi(info);
|
||||
}
|
||||
return XML_STATE_PLAYLISTS;
|
||||
}
|
||||
return XML_STATE_ERROR;
|
||||
|
||||
case XML_PL_ST_EXPECTING_PL_TRACKLIST:
|
||||
if((strcasecmp(info,"dict") == 0) || (strcasecmp(info,"key") == 0))
|
||||
return XML_STATE_PLAYLISTS;
|
||||
MAYBESETSTATE_PL(RXML_EVT_END,"array",XML_PL_ST_EXPECTING_PL_DATA);
|
||||
if(action == RXML_EVT_TEXT) {
|
||||
if(strcasecmp(info,"Track ID") != 0) {
|
||||
native_track_id = atoi(info);
|
||||
DPRINTF(E_DBG,L_SCAN,"Adding itunes track #%s\n",info);
|
||||
/* add it to the current playlist (current_id) */
|
||||
if(current_id && scan_xml_get_index(native_track_id, &track_id)) {
|
||||
db_add_playlist_item(current_id,track_id);
|
||||
}
|
||||
}
|
||||
|
||||
return XML_STATE_PLAYLISTS;
|
||||
}
|
||||
return XML_STATE_PLAYLISTS;
|
||||
|
||||
default:
|
||||
return XML_STATE_ERROR;
|
||||
}
|
||||
|
||||
return XML_STATE_PLAYLISTS;
|
||||
}
|
||||
|
||||
|
@ -113,7 +113,7 @@ int server_side_convert(char *fname) {
|
||||
int server_side_convert_set(MP3FILE *pmp3)
|
||||
{
|
||||
char *fname, *path, *description, *ext;
|
||||
DPRINTF(E_DBG,L_SCAN,"Checking for ssc: %s\n",pmp3->fname);
|
||||
DPRINTF(E_SPAM,L_SCAN,"Checking for ssc: %s\n",pmp3->fname);
|
||||
if ((!config.ssc_extensions) ||
|
||||
(!config.ssc_extensions[0]) ||
|
||||
(!config.ssc_prog) ||
|
||||
@ -126,11 +126,11 @@ int server_side_convert_set(MP3FILE *pmp3)
|
||||
strlen(pmp3->fname) -
|
||||
strlen(SERVER_SIDE_CONVERT_SUFFIX),
|
||||
SERVER_SIDE_CONVERT_SUFFIX) == 0))) {
|
||||
DPRINTF(E_DBG,L_SCAN,"Nope\n");
|
||||
DPRINTF(E_SPAM,L_SCAN,"Nope\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
DPRINTF(E_DBG,L_SCAN,"Yup\n");
|
||||
DPRINTF(E_SPAM,L_SCAN,"Yup\n");
|
||||
if (((ext = strrchr(pmp3->path, '.')) != NULL) &&
|
||||
(strcasestr(config.ssc_extensions, ext))) {
|
||||
fname = (char *)malloc(strlen(pmp3->fname) +
|
||||
|
Loading…
Reference in New Issue
Block a user