void
freetags(struct song_metadata *psong)
{
	int role;

	MAYBEFREE(psong->path);
	MAYBEFREE(psong->image);
	MAYBEFREE(psong->title);
	MAYBEFREE(psong->album);
	MAYBEFREE(psong->genre);
	MAYBEFREE(psong->comment);
	for(role = ROLE_START; role <= ROLE_LAST; role++)
	{
		MAYBEFREE(psong->contributor[role]);
		MAYBEFREE(psong->contributor_sort[role]);
	}
	MAYBEFREE(psong->grouping);
	MAYBEFREE(psong->mime);
	MAYBEFREE(psong->dlna_pn);
	MAYBEFREE(psong->tagversion);
	MAYBEFREE(psong->musicbrainz_albumid);
	MAYBEFREE(psong->musicbrainz_trackid);
	MAYBEFREE(psong->musicbrainz_artistid);
	MAYBEFREE(psong->musicbrainz_albumartistid);
}
Example #2
0
int scan_xml_tracks_section(int action, char *info) {
    static int state;
    static int current_track_id;
    static int current_field;
    static int is_streaming;
    static MP3FILE mp3;
    static char *song_path=NULL;
    char real_path[PATH_MAX];
    MP3FILE *pmp3;
    int added_id;

    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> */
        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_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_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);
        } else {
            return XML_STATE_ERROR;
        }
        break;

    case XML_TRACK_ST_EXPECTING_TRACK_DICT:
        /* waiting for a dict */
        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_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) {
                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 */
            is_streaming = 0;
            if((song_path) && strncasecmp(song_path,"http://",7) == 0)
                is_streaming = 1;

            if((!is_streaming)&&scan_xml_translate_path(song_path,real_path)) {
                /* FIXME: Error handling */
                pmp3=db_fetch_path(NULL,real_path,0);
                if(!pmp3) {
                    /* file doesn't exist... let's add it? */
                    scan_filename(real_path,SCAN_TEST_COMPDIR,NULL,NULL);
                    pmp3=db_fetch_path(NULL,real_path,0);
                }
                if(pmp3) {
                    /* Update the existing record with the
                     * updated stuff we got from the iTunes xml file
                     */
                    MAYBECOPYSTRING(title);
                    MAYBECOPYSTRING(artist);
                    MAYBECOPYSTRING(album);
                    MAYBECOPYSTRING(genre);
                    MAYBECOPYSTRING(comment);
                    MAYBECOPYSTRING(composer);
                    MAYBECOPY(song_length);
                    MAYBECOPY(track);
                    MAYBECOPY(total_tracks);
                    MAYBECOPY(year);
                    MAYBECOPY(bitrate);
                    MAYBECOPY(samplerate);
                    MAYBECOPY(play_count);
                    MAYBECOPY(rating);
                    MAYBECOPY(disc);
                    MAYBECOPY(total_discs);
                    MAYBECOPY(time_added);
                    MAYBECOPY(disabled);
                    MAYBECOPYSTRING(album_artist);

                    /* must add to the red-black tree */
                    scan_xml_add_lookup(current_track_id,pmp3->id);

                    make_composite_tags(pmp3);
                    db_add(NULL,pmp3,NULL);
                    db_dispose_item(pmp3);
                }
            } else if(is_streaming) {
                /* add/update a http:// url */
                pmp3=db_fetch_path(NULL,scan_xml_file,current_track_id);
                if(!pmp3) {
                    /* gotta add it! */
                    DPRINTF(E_DBG,L_SCAN,"Adding %s\n",song_path);
                    pmp3 = calloc(sizeof(MP3FILE),1);

                    if(!pmp3)
                        DPRINTF(E_FATAL,L_SCAN,
                                "malloc: scan_xml_tracks_section\n");
                } else {
                    DPRINTF(E_DBG,L_SCAN,"updating %s\n",song_path);
                }
                pmp3->url = strdup(song_path);
                pmp3->type = strdup("pls");
                pmp3->description = strdup("Playlist URL");
                pmp3->data_kind = 1;
                pmp3->item_kind = 2;

                pmp3->path = strdup(scan_xml_file);
                pmp3->index = current_track_id;

                MAYBECOPYSTRING(title);
                MAYBECOPYSTRING(artist);
                MAYBECOPYSTRING(album);
                MAYBECOPYSTRING(genre);
                MAYBECOPYSTRING(comment);
                MAYBECOPY(bitrate);
                MAYBECOPY(samplerate);
                MAYBECOPY(play_count);
                MAYBECOPY(rating);
                MAYBECOPY(time_added);
                MAYBECOPY(disabled);
                MAYBECOPYSTRING(album_artist);

                make_composite_tags(pmp3);
                if(db_add(NULL,pmp3,&added_id) == DB_E_SUCCESS) {
                    scan_xml_add_lookup(current_track_id,added_id);
                    DPRINTF(E_DBG,L_SCAN,"Added %s\n",song_path);
                } else {
                    DPRINTF(E_DBG,L_SCAN,"Error adding %s\n",song_path);
                }

                db_dispose_item(pmp3);
            }

            /* cleanup what's left */
            MAYBEFREE(mp3.title);
            MAYBEFREE(mp3.artist);
            MAYBEFREE(mp3.album);
            MAYBEFREE(mp3.genre);
            MAYBEFREE(mp3.comment);
            MAYBEFREE(mp3.album_artist);
            MAYBEFREE(song_path);

            memset((void*)&mp3,0,sizeof(MP3FILE));
        } else {
            return XML_STATE_ERROR;
        }
        break;

    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);
                DPRINTF(E_DBG,L_SCAN,"scan_path: %s\n",song_path);
            } else if(current_field == SCAN_XML_T_DATE_ADDED) {
                mp3.time_added = scan_xml_datedecode(info);
            } else if(current_field == SCAN_XML_T_COMMENTS) {
                mp3.comment = strdup(info);
            } else if(current_field == SCAN_XML_T_COMPOSER) {
                mp3.composer = strdup(info);
            } else if(current_field == SCAN_XML_T_ALBUM_ARTIST) {
                mp3.album_artist = strdup(info);
            }
        } else if(action == RXML_EVT_END) {
            state = XML_TRACK_ST_TRACK_INFO;
        } else {
            return XML_STATE_ERROR;
        }
        break;
    default:
        return XML_STATE_ERROR;
    }

    return XML_STATE_TRACKS;
}
Example #3
0
/**
 * collect playlist data for each playlist in the itunes xml file
 * this again is implemented as a sloppy state machine, and assumes
 * that the playlist items are after all the playlist metainfo.
 *
 * @param action xml action (RXML_EVT_TEXT, etc)
 * @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 */
    static int dont_scan=0;          /** < playlist we don't want */
    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;
        dont_scan=0;
        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 */
        dont_scan=0;
        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;
            } else if(strcasecmp(info,"Master") == 0) {
                /* No point adding the master library... we have one */
                dont_scan=1;
            }
            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 */
            current_id=0;
            if(dont_scan == 0) {
                DPRINTF(E_DBG,L_SCAN,"Creating playlist for %s\n",current_name);
                /* we won't actually use the iTunes pl_id, as it seems
                 *  to change for no good reason.  We'll hash the name,
                 * instead. */

                /* delete the old one first */
                /* FIXME: Error handling */
                DPRINTF(E_DBG,L_SCAN,"Converting native plid (%d) to %d\n",
                        native_plid, util_djb_hash_str(current_name));
                native_plid = util_djb_hash_str(current_name);

                pm3u = db_fetch_playlist(NULL,scan_xml_file,native_plid);
                if(pm3u) {
                    db_delete_playlist(NULL,pm3u->id);
                    db_dispose_playlist(pm3u);
                }
                if(db_add_playlist(NULL,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;
                }
            }
            dont_scan=0;
            state=XML_PL_ST_EXPECTING_PL_TRACKLIST;
            MAYBEFREE(current_name);
            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) {
                MAYBEFREE(current_name);
                current_name = strdup(info);
                DPRINTF(E_DBG,L_SCAN,"Found playlist: %s\n",current_name);
                /* disallow specific playlists */
                if(strcasecmp(current_name,"Party Shuffle") == 0) {
                    dont_scan=1;
                }
            } 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)) {
                    /* FIXME: Error handling */
                    db_add_playlist_item(NULL,current_id,track_id);
                }
            }

            return XML_STATE_PLAYLISTS;
        }
        return XML_STATE_PLAYLISTS;

    default:
        return XML_STATE_ERROR;
    }

    return XML_STATE_PLAYLISTS;
}
Example #4
0
/**
 * scan an iTunes xml music database file, augmenting
 * the metainfo with that found in the xml file
 *
 * @param filename xml file to parse
 * @returns TRUE if playlist parsed successfully, FALSE otherwise
 */
int scan_xml_playlist(char *filename) {
    char *working_base;
    const void *val;
    int retval=TRUE;
    SCAN_XML_RB *lookup_ptr;
    SCAN_XML_RB lookup_val;

    RXMLHANDLE xml_handle;

    MAYBEFREE(scan_xml_itunes_version);
    MAYBEFREE(scan_xml_itunes_base_path);
    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 FALSE;
    }

    /* 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);

    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)) {
            retval=FALSE;
            DPRINTF(E_LOG,L_SCAN,"Error parsing xml file %s: %s\n",
                    filename,rxml_errorstring(xml_handle));
        }
    }

    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);

    MAYBEFREE(scan_xml_itunes_version);
    MAYBEFREE(scan_xml_itunes_base_path);
    MAYBEFREE(scan_xml_itunes_decoded_base_path);
    MAYBEFREE(scan_xml_real_base_path);

    return retval;
}