owntone-server/src/scan-xml.c

751 lines
21 KiB
C
Raw Normal View History

/*
* $Id$
* Implementation file iTunes metainfo scanning
*
* 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
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <ctype.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "db-generic.h"
#include "err.h"
#include "mp3-scanner.h"
2005-05-17 00:26:20 -04:00
#include "rxml.h"
2005-05-21 01:53:11 -04:00
#include "redblack.h"
/* Forwards */
int scan_xml_playlist(char *filename);
2005-05-17 00:26:20 -04:00
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);
2005-05-21 01:53:11 -04:00
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;
2005-05-17 00:26:20 -04:00
static char *scan_xml_itunes_decoded_base_path = NULL;
static char *scan_xml_real_base_path = NULL;
2005-05-21 01:53:11 -04:00
static char *scan_xml_file; /** < The actual file we are scanning */
static struct rbtree *scan_xml_db;
2005-05-18 01:48:22 -04:00
#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; }
2005-05-17 00:26:20 -04:00
2005-05-21 01:53:11 -04:00
/** iTunes xml values we are interested in */
2005-05-17 00:26:20 -04:00
static char *scan_xml_track_tags[] = {
"Name",
"Artist",
"Album",
"Genre",
"Total Time",
"Track Number",
"Track Count",
"Year",
"Bit Rate",
"Sample Rate",
"Play Count",
"Rating",
"Disabled",
"Disc Number",
"Disc Count",
"Compilation",
"Location",
NULL
};
2005-05-21 01:53:11 -04:00
/** Indexes to the iTunes xml fields we are interested in */
2005-05-17 00:26:20 -04:00
#define SCAN_XML_T_UNKNOWN -1
#define SCAN_XML_T_NAME 0
#define SCAN_XML_T_ARTIST 1
#define SCAN_XML_T_ALBUM 2
#define SCAN_XML_T_GENRE 3
#define SCAN_XML_T_TOTALTIME 4
#define SCAN_XML_T_TRACKNUMBER 5
#define SCAN_XML_T_TRACKCOUNT 6
#define SCAN_XML_T_YEAR 7
#define SCAN_XML_T_BITRATE 8
#define SCAN_XML_T_SAMPLERATE 9
#define SCAN_XML_T_PLAYCOUNT 10
#define SCAN_XML_T_RATING 11
#define SCAN_XML_T_DISABLED 12
#define SCAN_XML_T_DISCNO 13
#define SCAN_XML_T_DISCCOUNT 14
#define SCAN_XML_T_COMPILATION 15
#define SCAN_XML_T_LOCATION 16
2005-05-21 01:53:11 -04:00
#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;
}
2005-05-17 00:26:20 -04:00
/**
* get the tag index of a particular tag
*
* @param tag tag to determine tag index for
*/
int scan_xml_get_tagindex(char *tag) {
char **ptag = scan_xml_track_tags;
int index=0;
while(*ptag && (strcasecmp(tag,*ptag) != 0)) {
ptag++;
index++;
}
if(*ptag)
return index;
return SCAN_XML_T_UNKNOWN;
}
/**
* urldecode a string, returning a string pointer which must
* be freed by the calling function or NULL on error (ENOMEM)
*
* \param string string to convert
* \param space as plus whether to convert '+' chars to spaces (no, for iTunes)
*/
char *scan_xml_urldecode(char *string, int space_as_plus) {
char *pnew;
char *src,*dst;
int val=0;
pnew=(char*)malloc(strlen(string)+1);
if(!pnew)
return NULL;
src=string;
dst=pnew;
while(*src) {
switch(*src) {
case '+':
if(space_as_plus) {
*dst++=' ';
} else {
*dst++=*src;
}
src++;
break;
case '%':
/* this is hideous */
src++;
if(*src) {
if((*src <= '9') && (*src >='0'))
val=(*src - '0');
else if((tolower(*src) <= 'f')&&(tolower(*src) >= 'a'))
val=10+(tolower(*src) - 'a');
src++;
}
if(*src) {
val *= 16;
if((*src <= '9') && (*src >='0'))
val+=(*src - '0');
else if((tolower(*src) <= 'f')&&(tolower(*src) >= 'a'))
val+=(10+(tolower(*src) - 'a'));
src++;
}
*dst++=val;
break;
default:
*dst++=*src++;
break;
}
}
*dst='\0';
return pnew;
}
/**
* scan an iTunes xml music database file, augmenting
* the metainfo with that found in the xml file
*/
int scan_xml_playlist(char *filename) {
char *working_base;
2005-05-21 01:53:11 -04:00
const void *val;
SCAN_XML_RB *lookup_ptr;
SCAN_XML_RB lookup_val;
2005-05-17 00:26:20 -04:00
RXMLHANDLE xml_handle;
2005-05-17 00:26:20 -04:00
MAYBEFREE(scan_xml_itunes_version);
MAYBEFREE(scan_xml_itunes_base_path);
MAYBEFREE(scan_xml_itunes_decoded_base_path);
MAYBEFREE(scan_xml_real_base_path);
2005-05-21 01:53:11 -04:00
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);
if(strrchr(working_base,'/')) {
*(strrchr(working_base,'/') + 1) = '\x0';
scan_xml_real_base_path = strdup(working_base);
} else {
scan_xml_real_base_path = strdup("/");
}
free(working_base);
DPRINTF(E_SPAM,L_SCAN,"Parsing xml file: %s\n",filename);
2005-05-17 00:26:20 -04:00
if(!rxml_open(&xml_handle,filename,scan_xml_handler,NULL)) {
DPRINTF(E_LOG,L_SCAN,"Error opening xml file %s: %s\n",
filename,rxml_errorstring(xml_handle));
} else {
if(!rxml_parse(xml_handle)) {
DPRINTF(E_LOG,L_SCAN,"Error parsing xml file %s: %s\n",
filename,rxml_errorstring(xml_handle));
}
}
rxml_close(xml_handle);
2005-05-21 01:53:11 -04:00
/* 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);
2005-05-17 00:26:20 -04:00
return 0;
}
2005-05-17 00:26:20 -04:00
#define XML_STATE_PREAMBLE 0
#define XML_STATE_TRACKS 1
#define XML_STATE_PLAYLISTS 2
#define XML_STATE_ERROR 3
/**
* handle new xml events, and dispatch it to the
* appropriate handler. This is a callback from the
* xml parser.
*
* @param action what event (RXML_EVT_OPEN, etc)
* @param puser opaqe data object passed in open (unused)
* @param info char data associated with the event
*/
void scan_xml_handler(int action,void* puser,char* info) {
static int state;
switch(action) {
case RXML_EVT_OPEN: /* file opened */
state = XML_STATE_PREAMBLE;
/* send this event to all dispatches to allow them
* to reset
*/
scan_xml_preamble_section(action,info);
scan_xml_tracks_section(action,info);
scan_xml_playlists_section(action,info);
break;
case RXML_EVT_BEGIN:
case RXML_EVT_END:
case RXML_EVT_TEXT:
switch(state) {
case XML_STATE_PREAMBLE:
state=scan_xml_preamble_section(action,info);
break;
case XML_STATE_TRACKS:
state=scan_xml_tracks_section(action,info);
break;
case XML_STATE_PLAYLISTS:
state=scan_xml_playlists_section(action,info);
break;
default:
break;
}
default:
break;
}
2005-05-17 00:26:20 -04:00
}
2005-05-21 01:53:11 -04:00
#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
2005-05-17 00:26:20 -04:00
/**
* collect preamble data... version, library id, etc.
*
* @param action xml action (RXML_EVT_TEXT, etc)
* @param info text data associated with event
*/
int scan_xml_preamble_section(int action, char *info) {
static int expecting_next;
static int done;
switch(action) {
case RXML_EVT_OPEN: /* initialization */
expecting_next=0;
done=0;
break;
case RXML_EVT_END:
2005-05-21 01:53:11 -04:00
if(expecting_next == SCAN_XML_PRE_TRACKS) { /* end of tracks tag */
expecting_next=0;
DPRINTF(E_DBG,L_SCAN,"Scanning tracks\n");
2005-05-17 00:26:20 -04:00
return XML_STATE_TRACKS;
2005-05-21 01:53:11 -04:00
}
if(expecting_next == SCAN_XML_PRE_PLAYLISTS) {
expecting_next=0;
DPRINTF(E_DBG,L_SCAN,"Scanning playlists\n");
return XML_STATE_PLAYLISTS;
}
2005-05-17 00:26:20 -04:00
break;
case RXML_EVT_TEXT: /* scan for the tags we expect */
if(!expecting_next) {
if(strcmp(info,"Application Version") == 0) {
expecting_next = SCAN_XML_PRE_VERSION;
} else if (strcmp(info,"Music Folder") == 0) {
expecting_next = SCAN_XML_PRE_PATH;
} else if (strcmp(info,"Tracks") == 0) {
2005-05-21 01:53:11 -04:00
expecting_next = SCAN_XML_PRE_TRACKS;
} else if (strcmp(info,"Playlists") == 0) {
expecting_next = SCAN_XML_PRE_PLAYLISTS;
2005-05-17 00:26:20 -04:00
}
} else {
/* we were expecting someting! */
switch(expecting_next) {
case SCAN_XML_PRE_VERSION:
if(!scan_xml_itunes_version) {
scan_xml_itunes_version=strdup(info);
DPRINTF(E_DBG,L_SCAN,"iTunes Version: %s\n",info);
}
break;
case SCAN_XML_PRE_PATH:
if(!scan_xml_itunes_base_path) {
scan_xml_itunes_base_path=strdup(info);
scan_xml_itunes_decoded_base_path=scan_xml_urldecode(info,0);
DPRINTF(E_DBG,L_SCAN,"iTunes base path: %s\n",info);
}
break;
default:
break;
}
expecting_next=0;
}
break; /* RXML_EVT_TEXT */
default:
break;
}
return XML_STATE_PREAMBLE;
}
2005-05-17 00:26:20 -04:00
#define XML_TRACK_ST_INITIAL 0
#define XML_TRACK_ST_MAIN_DICT 1
#define XML_TRACK_ST_EXPECTING_TRACK_ID 2
#define XML_TRACK_ST_EXPECTING_TRACK_DICT 3
#define XML_TRACK_ST_TRACK_INFO 4
#define XML_TRACK_ST_TRACK_DATA 5
2005-05-17 00:26:20 -04:00
/**
* collect track data for each track in the itunes playlist
*
* @param action xml action (RXML_EVT_TEXT, etc)
* @param info text data associated with event
*/
2005-05-21 01:53:11 -04:00
#define MAYBESETSTATE_TR(a,b,c) { if((action==(a)) && \
2005-05-17 00:26:20 -04:00
(strcmp(info,(b)) == 0)) { \
state = (c); \
return XML_STATE_TRACKS; \
}}
int scan_xml_tracks_section(int action, char *info) {
static int state;
static int current_track_id;
static int current_field;
static MP3FILE mp3;
2005-05-21 01:53:11 -04:00
static char *song_path=NULL;
2005-05-17 00:26:20 -04:00
char physical_path[PATH_MAX];
char real_path[PATH_MAX];
MP3FILE *pmp3;
if(action == RXML_EVT_OPEN) {
state = XML_TRACK_ST_INITIAL;
memset((void*)&mp3,0,sizeof(MP3FILE));
song_path = NULL;
return 0;
}
/* walk through the states */
switch(state) {
case XML_TRACK_ST_INITIAL:
/* expection only a <dict> */
2005-05-21 01:53:11 -04:00
MAYBESETSTATE_TR(RXML_EVT_BEGIN,"dict",XML_TRACK_ST_MAIN_DICT);
2005-05-17 00:26:20 -04:00
return XML_STATE_ERROR;
break;
case XML_TRACK_ST_MAIN_DICT:
/* either get a <key>, or a </dict> */
2005-05-21 01:53:11 -04:00
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;
}
2005-05-17 00:26:20 -04:00
return XML_STATE_ERROR;
break;
case XML_TRACK_ST_EXPECTING_TRACK_ID:
/* this is somewhat loose - <key>id</key> */
2005-05-21 01:53:11 -04:00
MAYBESETSTATE_TR(RXML_EVT_BEGIN,"key",XML_TRACK_ST_EXPECTING_TRACK_ID);
MAYBESETSTATE_TR(RXML_EVT_END,"key",XML_TRACK_ST_EXPECTING_TRACK_DICT);
2005-05-17 00:26:20 -04:00
if (action == RXML_EVT_TEXT) {
current_track_id = atoi(info);
DPRINTF(E_DBG,L_SCAN,"Scanning iTunes id #%d\n",current_track_id);
} else {
return XML_STATE_ERROR;
}
2005-05-17 00:26:20 -04:00
break;
case XML_TRACK_ST_EXPECTING_TRACK_DICT:
/* waiting for a dict */
2005-05-21 01:53:11 -04:00
MAYBESETSTATE_TR(RXML_EVT_BEGIN,"dict",XML_TRACK_ST_TRACK_INFO);
2005-05-17 00:26:20 -04:00
return XML_STATE_ERROR;
break;
case XML_TRACK_ST_TRACK_INFO:
/* again, kind of loose */
2005-05-21 01:53:11 -04:00
MAYBESETSTATE_TR(RXML_EVT_BEGIN,"key",XML_TRACK_ST_TRACK_INFO);
MAYBESETSTATE_TR(RXML_EVT_END,"key",XML_TRACK_ST_TRACK_DATA);
2005-05-17 00:26:20 -04:00
if(action == RXML_EVT_TEXT) {
current_field=scan_xml_get_tagindex(info);
if(current_field == SCAN_XML_T_DISABLED) {
mp3.disabled = 1;
} else if(current_field == SCAN_XML_T_COMPILATION) {
mp3.compilation = 1;
}
} else if((action == RXML_EVT_END) && (strcmp(info,"dict")==0)) {
state = XML_TRACK_ST_MAIN_DICT;
/* but more importantly, we gotta process the track */
if(song_path && (strlen(song_path) >
strlen(scan_xml_itunes_decoded_base_path))) {
sprintf(physical_path,"%siTunes Music/%s",
scan_xml_real_base_path,
(char*)&song_path[strlen(scan_xml_itunes_decoded_base_path)]);
realpath(physical_path,real_path);
pmp3=db_fetch_path(real_path);
if(pmp3) {
2005-05-18 01:48:22 -04:00
/* Update the existing record with the
* updated stuff we got from the iTunes xml file
*/
MAYBECOPYSTRING(title);
MAYBECOPYSTRING(artist);
MAYBECOPYSTRING(album);
MAYBECOPYSTRING(genre);
2005-05-17 00:26:20 -04:00
MAYBECOPY(song_length);
MAYBECOPY(track);
MAYBECOPY(total_tracks);
2005-05-18 01:48:22 -04:00
MAYBECOPY(year);
MAYBECOPY(bitrate);
MAYBECOPY(samplerate);
2005-05-17 00:26:20 -04:00
MAYBECOPY(play_count);
MAYBECOPY(rating);
2005-05-18 01:48:22 -04:00
MAYBECOPY(disc);
MAYBECOPY(total_discs);
2005-05-21 01:53:11 -04:00
/* must add to the red-black tree */
scan_xml_add_lookup(current_track_id,pmp3->id);
2005-05-18 01:48:22 -04:00
db_add(pmp3);
2005-05-17 00:26:20 -04:00
db_dispose_item(pmp3);
2005-05-18 01:48:22 -04:00
memset((void*)&mp3,0,sizeof(MP3FILE));
MAYBEFREE(song_path);
2005-05-17 00:26:20 -04:00
}
}
} else {
return XML_STATE_ERROR;
}
break;
2005-05-17 00:26:20 -04:00
case XML_TRACK_ST_TRACK_DATA:
if(action == RXML_EVT_BEGIN) {
break;
} else if(action == RXML_EVT_TEXT) {
if(current_field == SCAN_XML_T_NAME) {
mp3.title = strdup(info);
} else if(current_field == SCAN_XML_T_ARTIST) {
mp3.artist = strdup(info);
} else if(current_field == SCAN_XML_T_ALBUM) {
mp3.album = strdup(info);
} else if(current_field == SCAN_XML_T_GENRE) {
mp3.genre = strdup(info);
} else if(current_field == SCAN_XML_T_TOTALTIME) {
mp3.song_length = atoi(info);
} else if(current_field == SCAN_XML_T_TRACKNUMBER) {
mp3.track = atoi(info);
} else if(current_field == SCAN_XML_T_TRACKCOUNT) {
mp3.total_tracks = atoi(info);
} else if(current_field == SCAN_XML_T_YEAR) {
mp3.year = atoi(info);
} else if(current_field == SCAN_XML_T_BITRATE) {
mp3.bitrate = atoi(info);
} else if(current_field == SCAN_XML_T_SAMPLERATE) {
mp3.samplerate = atoi(info);
} else if(current_field == SCAN_XML_T_PLAYCOUNT) {
mp3.play_count = atoi(info);
} else if(current_field == SCAN_XML_T_RATING) {
mp3.rating = atoi(info);
} else if(current_field == SCAN_XML_T_DISCNO) {
mp3.disc = atoi(info);
} else if(current_field == SCAN_XML_T_DISCCOUNT) {
mp3.total_discs = atoi(info);
} else if(current_field == SCAN_XML_T_LOCATION) {
song_path = scan_xml_urldecode(info,0);
}
} else if(action == RXML_EVT_END) {
state = XML_TRACK_ST_TRACK_INFO;
} else {
return XML_STATE_ERROR;
}
2005-05-17 00:26:20 -04:00
break;
default:
return XML_STATE_ERROR;
}
2005-05-17 00:26:20 -04:00
return XML_STATE_TRACKS;
}
2005-05-21 01:53:11 -04:00
#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; \
}}
2005-05-17 00:26:20 -04:00
/**
* collect playlist data for each playlist in the itunes xml file
*
* @param action xml action (RXML_EVT_TEXT, etc)
* @param info text data associated with event
*/
int scan_xml_playlists_section(int action, char *info) {
2005-05-21 01:53:11 -04:00
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,&current_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;
}
2005-05-17 00:26:20 -04:00
return XML_STATE_PLAYLISTS;
}