diff options
Diffstat (limited to '')
-rw-r--r-- | TODO | 23 | ||||
-rw-r--r-- | configure.ac | 2 | ||||
-rw-r--r-- | doc/mpdconf.example | 4 | ||||
-rw-r--r-- | src/Makefile.am | 6 | ||||
-rw-r--r-- | src/audioOutputs/audioOutput_shout.c | 18 | ||||
-rw-r--r-- | src/command.c | 65 | ||||
-rw-r--r-- | src/conf.c | 1 | ||||
-rw-r--r-- | src/conf.h | 1 | ||||
-rw-r--r-- | src/dbUtils.c | 267 | ||||
-rw-r--r-- | src/dbUtils.h | 42 | ||||
-rw-r--r-- | src/decode.c | 4 | ||||
-rw-r--r-- | src/directory.c | 189 | ||||
-rw-r--r-- | src/directory.h | 33 | ||||
-rw-r--r-- | src/inputPlugins/flac_plugin.c | 122 | ||||
-rw-r--r-- | src/inputPlugins/mod_plugin.c | 4 | ||||
-rw-r--r-- | src/inputPlugins/mp3_plugin.c | 20 | ||||
-rw-r--r-- | src/inputPlugins/mp4_plugin.c | 42 | ||||
-rw-r--r-- | src/inputPlugins/ogg_plugin.c | 28 | ||||
-rw-r--r-- | src/list.c | 32 | ||||
-rw-r--r-- | src/list.h | 4 | ||||
-rw-r--r-- | src/main.c | 10 | ||||
-rw-r--r-- | src/metadataChunk.c | 28 | ||||
-rw-r--r-- | src/player.c | 3 | ||||
-rw-r--r-- | src/song.c | 56 | ||||
-rw-r--r-- | src/stats.c | 7 | ||||
-rw-r--r-- | src/tables.c | 200 | ||||
-rw-r--r-- | src/tables.h | 43 | ||||
-rw-r--r-- | src/tag.c | 305 | ||||
-rw-r--r-- | src/tag.h | 42 | ||||
-rw-r--r-- | src/tagTracker.c | 142 | ||||
-rw-r--r-- | src/tagTracker.h | 20 |
31 files changed, 1039 insertions, 724 deletions
@@ -1,18 +1,21 @@ 0.12 ---- -*) disable mod support by default - *) add genre, date, composer metadata *) rewrite metadata/db handling - *) store all metadata entries in table - *) each contains a counter reference - *) new MpdDBTag, which instead of strings allocated for each metadata - entry, store a char * string to the metadata entry in the table - *) multiple artist support - *) multiple genre support {char *, int pairs for each metadata entry - indicating the type of metadata and its value) - *) MpdDBTag <-> MpdTag interface + *) rewrite search and find to be more flexible and elegant + *) implemnt list command + *) for each TagTrackerItem, include a temp varaible for storing + if it has been printed already + *) reset this variable before list command processing + +*) rewrite filename handling + *) use memory more efficiently, by iteratively constructing filename + *) create a function for creating directory names: + *) returns a static string + *) when creating the string, check that the same directory + string wasn't just created (store last Directory * ptr) + *) this should be very fast! and save memory *) implement listener socket protocol as documented here: http://www.musicpd.org/wiki/moin.cgi/MpdListenerProtocol diff --git a/configure.ac b/configure.ac index 492ea84d1..c15a115ba 100644 --- a/configure.ac +++ b/configure.ac @@ -42,7 +42,7 @@ AC_ARG_ENABLE(flac,[ --disable-flac disable flac support],,enable_flac=yes) AC_ARG_ENABLE(mp3,[ --disable-mp3 disable mp3 support],,enable_mp3=yes) AC_ARG_ENABLE(aac,[ --disable-aac disable AAC support],,enable_aac=yes) AC_ARG_ENABLE(audiofile,[ --disable-audiofile disable audiofile support, disables wave support],,enable_audiofile=yes) -AC_ARG_ENABLE(mod,[ --disable-mod disable MOD support],,enable_mod=yes) +AC_ARG_ENABLE(mod,[ --enable-mod enable MOD support],enable_mod=no,) AC_ARG_ENABLE(id3,[ --disable-id3 disable id3 support],,enable_id3=yes) AC_ARG_ENABLE(mpd_mad,[ --enable-mpd-mad use mpd libmad],use_mpd_mad=yes,) AC_ARG_ENABLE(mpd_id3tag,[ --enable-mpd-id3tag use mpd libid3tag],use_mpd_id3tag=yes,) diff --git a/doc/mpdconf.example b/doc/mpdconf.example index 7c9cdab86..4f6600bd8 100644 --- a/doc/mpdconf.example +++ b/doc/mpdconf.example @@ -199,6 +199,10 @@ audio_output { ################ MISCELLANEOUS OPTIONS ################### # +# This sets the metadata mpd will use, to disable all metadata, set to "none" +# +#metadata_to_use "artist,album,title,genre,date,track" +# # This setting exists as precaution against attacks. # #max_playlist_length "16384" diff --git a/src/Makefile.am b/src/Makefile.am index 5897f5e1c..dbde9a3a0 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -24,6 +24,7 @@ mpd_headers = \ charConv.h \ command.h \ conf.h \ + dbUtils.h \ decode.h \ directory.h \ inputPlugin.h \ @@ -51,7 +52,7 @@ mpd_headers = \ song.h \ stats.h \ tag.h \ - tables.h \ + tagTracker.h \ utf8.h \ utils.h \ volume.h @@ -67,6 +68,7 @@ mpd_SOURCES = \ charConv.c \ command.c \ conf.c \ + dbUtils.c \ decode.c \ directory.c \ inputPlugin.c \ @@ -93,8 +95,8 @@ mpd_SOURCES = \ signal_check.c \ song.c \ stats.c \ - tables.c \ tag.c \ + tagTracker.c \ utils.c \ volume.c \ utf8.c diff --git a/src/audioOutputs/audioOutput_shout.c b/src/audioOutputs/audioOutput_shout.c index 8eb4f4672..661acd07d 100644 --- a/src/audioOutputs/audioOutput_shout.c +++ b/src/audioOutputs/audioOutput_shout.c @@ -375,9 +375,21 @@ static void myShout_closeDevice(AudioOutput * audioOutput) { static void copyTagToVorbisComment(ShoutData * sd) { if(sd->tag) { - addTag("ARTIST", sd->tag->artist); - addTag("ALBUM", sd->tag->album); - addTag("TITLE", sd->tag->title); + int i; + + for(i = 0; i < sd->tag->numOfItems; i++) { + switch(sd->tag->items[i].type) { + case TAG_ITEM_ARTIST: + addTag("ARTIST", sd->tag->items[i].value); + break; + case TAG_ITEM_ALBUM: + addTag("ALBUM", sd->tag->items[i].value); + break; + case TAG_ITEM_TITLE: + addTag("TITLE", sd->tag->items[i].value); + break; + } + } } } diff --git a/src/command.c b/src/command.c index 6341972cf..5b3616a40 100644 --- a/src/command.c +++ b/src/command.c @@ -21,7 +21,6 @@ #include "playlist.h" #include "ls.h" #include "directory.h" -#include "tables.h" #include "volume.h" #include "path.h" #include "stats.h" @@ -32,6 +31,7 @@ #include "audio.h" #include "buffer2array.h" #include "log.h" +#include "dbUtils.h" #include <stdlib.h> #include <string.h> @@ -442,13 +442,41 @@ int handlePlaylistId(FILE * fp, unsigned int * permission, int handleFind(FILE * fp, unsigned int * permission, int argArrayLength, char ** argArray) { - return findSongsIn(fp,NULL,argArray[1],argArray[2]); + int ret; + + LocateTagItem * item = newLocateTagItem(argArray[1], argArray[2]); + + if(!item) { + commandError(fp, ACK_ERROR_ARG, "\%s\" isn't recognized", + argArray[1]); + return -1; + } + + ret = findSongsIn(fp, NULL, item); + + freeLocateTagItem(item); + + return ret; } int handleSearch(FILE * fp, unsigned int * permission, int argArrayLength, char ** argArray) { - return searchForSongsIn(fp,NULL,argArray[1],argArray[2]); + int ret; + + LocateTagItem * item = newLocateTagItem(argArray[1], argArray[2]); + + if(!item) { + commandError(fp, ACK_ERROR_ARG, "\%s\" isn't recognized", + argArray[1]); + return -1; + } + + ret = searchForSongsIn(fp, NULL, item); + + freeLocateTagItem(item); + + return ret; } int listHandleUpdate(FILE * fp, unsigned int * permission, int argArrayLength, @@ -585,10 +613,35 @@ int handleClearError(FILE * fp, unsigned int * permission, int argArrayLength, int handleList(FILE * fp, unsigned int * permission, int argArrayLength, char ** argArray) { - char * arg1 = NULL; + int numConditionals = 0; + LocateTagItem * conditionals = NULL; + int tagType = getLocateTagItemType(argArray[1]); + int ret; + + if(tagType < 0) { + commandError(fp, ACK_ERROR_ARG, + "\"%s\" is not known", argArray[1]); + return -1; + } - if(argArrayLength==3) arg1 = argArray[2]; - return printAllKeysOfTable(fp,argArray[1],arg1); + /* for compatibility with < 0.12.0 */ + if(argArrayLength==3) { + if(tagType != TAG_ITEM_ALBUM) { + commandError(fp, ACK_ERROR_ARG, + "should be \"%s\" for 3 arguments", + mpdTagItemKeys[TAG_ITEM_ALBUM]); + return -1; + } + conditionals = newLocateTagItem(mpdTagItemKeys[TAG_ITEM_ARTIST], + argArray[2]); + numConditionals = 1; + } + + ret = listAllUniqueTags(fp, tagType, numConditionals,conditionals); + + if(conditionals) free(conditionals); + + return ret; } int handleMove(FILE * fp, unsigned int * permission, int argArrayLength, diff --git a/src/conf.c b/src/conf.c index 874c5ca5d..856740038 100644 --- a/src/conf.c +++ b/src/conf.c @@ -151,6 +151,7 @@ void initConf() { registerConfigParam(CONF_HTTP_PREBUFFER_SIZE, 0, 0); registerConfigParam(CONF_REPLAYGAIN_PREAMP, 0, 0); registerConfigParam(CONF_ID3V1_ENCODING, 0, 0); + registerConfigParam(CONF_METADATA_TO_USE, 0, 0); } static void addBlockParam(ConfigParam * param, char * name, char * value, diff --git a/src/conf.h b/src/conf.h index 0a95335fb..ca9da3acf 100644 --- a/src/conf.h +++ b/src/conf.h @@ -57,6 +57,7 @@ #define CONF_HTTP_BUFFER_SIZE "http_buffer_size" #define CONF_HTTP_PREBUFFER_SIZE "http_prebuffer_size" #define CONF_ID3V1_ENCODING "id3v1_encoding" +#define CONF_METADATA_TO_USE "metadata_to_use" typedef struct _BlockParam { char * name; diff --git a/src/dbUtils.c b/src/dbUtils.c new file mode 100644 index 000000000..5e73da348 --- /dev/null +++ b/src/dbUtils.c @@ -0,0 +1,267 @@ +#include "dbUtils.h" + +#include "directory.h" +#include "myfprintf.h" +#include "utils.h" +#include "playlist.h" +#include "tag.h" +#include "tagTracker.h" + +typedef struct ListCommandItem { + mpd_sint8 tagType; + int numConditionals; + LocateTagItem * conditionals; +} ListCommandItem; + +int getLocateTagItemType(char * str) { + int i; + + if(0 == strcasecmp(str, LOCATE_TAG_FILE_KEY)) { + return LOCATE_TAG_FILE_TYPE; + } + + for(i = 0; i < TAG_NUM_OF_ITEM_TYPES; i++) { + if(0 == strcasecmp(str, mpdTagItemKeys[i])) return i; + } + + return -1; +} + +LocateTagItem * newLocateTagItem(char * typeStr, char * needle) { + LocateTagItem * ret = malloc(sizeof(LocateTagItem)); + + ret->tagType = getLocateTagItemType(typeStr); + + if(ret->tagType < 0) { + free(ret); + return NULL; + } + + ret->needle = strdup(needle); + + return ret; +} + +void freeLocateTagItem(LocateTagItem * item) { + free(item->needle); + free(item); +} + +int countSongsInDirectory(FILE * fp, Directory * directory, void * data) { + int * count = (int *)data; + + *count+=directory->songs->numberOfNodes; + + return 0; +} + +int printDirectoryInDirectory(FILE * fp, Directory * directory, void * data) { + if(directory->utf8name) { + myfprintf(fp,"directory: %s\n",directory->utf8name); + } + return 0; +} + +int printSongInDirectory(FILE * fp, Song * song, void * data) { + myfprintf(fp,"file: %s\n",song->utf8url); + return 0; +} + +static inline int strstrSearchTag(Song * song, int type, char * str) { + int i; + char * dup; + int ret = 0; + + if(type == LOCATE_TAG_FILE_TYPE) { + dup = strDupToUpper(song->utf8url); + if(strstr(dup, str)) ret = 1; + free(dup); + return ret; + } + + if(!song->tag) return 0; + + for(i = 0; i < song->tag->numOfItems && !ret; i++) { + if(song->tag->items[i].type != type) continue; + + dup = strDupToUpper(song->tag->items[i].value); + if(strstr(dup, str)) ret = 1; + free(dup); + } + + return ret; +} + +int searchInDirectory(FILE * fp, Song * song, void * item) { + if(strstrSearchTag(song, ((LocateTagItem *)item)->tagType, + ((LocateTagItem *)item)->needle)) { + printSongInfo(fp, song); + } + return 0; +} + +int searchForSongsIn(FILE * fp, char * name, LocateTagItem * item) { + char * originalNeedle = item->needle; + int ret = -1; + + item->needle = strDupToUpper(originalNeedle); + + ret = traverseAllIn(fp,name,searchInDirectory, NULL, + (void *)item); + + free(item->needle); + item->needle = originalNeedle; + + return ret; +} + +static inline int tagItemFoundAndMatches(Song * song, int type, char * str) { + int i; + + if(type == LOCATE_TAG_FILE_TYPE) { + if(0 == strcmp(str, song->utf8url)) return 1; + } + + if(!song->tag) return 0; + + for(i = 0; i < song->tag->numOfItems; i++) { + if(song->tag->items[i].type != type) continue; + + if(0 == strcmp(str, song->tag->items[i].value)) return 1; + } + + return 0; +} + +int findInDirectory(FILE * fp, Song * song, void * item) { + if(tagItemFoundAndMatches(song, ((LocateTagItem *)item)->tagType, + ((LocateTagItem *)item)->needle)) { + printSongInfo(fp, song); + } + return 0; +} + +int findSongsIn(FILE * fp, char * name, LocateTagItem * item) { + return traverseAllIn(fp, name, findInDirectory, NULL, + (void *)item); +} + +int printAllIn(FILE * fp, char * name) { + return traverseAllIn(fp,name,printSongInDirectory, + printDirectoryInDirectory,NULL); +} + +int directoryAddSongToPlaylist(FILE * fp, Song * song, void * data) { + return addSongToPlaylist(fp, song, 0); +} + +int addAllIn(FILE * fp, char * name) { + return traverseAllIn(fp,name,directoryAddSongToPlaylist,NULL,NULL); +} + +int directoryPrintSongInfo(FILE * fp, Song * song, void * data) { + return printSongInfo(fp,song); +} + +int sumSongTime(FILE * fp, Song * song, void * data) { + unsigned long * time = (unsigned long *)data; + + if(song->tag && song->tag->time>=0) *time+=song->tag->time; + + return 0; +} + +int printInfoForAllIn(FILE * fp, char * name) { + return traverseAllIn(fp,name,directoryPrintSongInfo,printDirectoryInDirectory,NULL); +} + +int countSongsIn(FILE * fp, char * name) { + int count = 0; + void * ptr = (void *)&count; + + traverseAllIn(fp,name,NULL,countSongsInDirectory,ptr); + + return count; +} + +unsigned long sumSongTimesIn(FILE * fp, char * name) { + unsigned long dbPlayTime = 0; + void * ptr = (void *)&dbPlayTime; + + traverseAllIn(fp,name,sumSongTime,NULL,ptr); + + return dbPlayTime; +} + +ListCommandItem * newListCommandItem(int tagType, int numConditionals, + LocateTagItem * conditionals) +{ + ListCommandItem * item = malloc(sizeof(ListCommandItem)); + + item->tagType = tagType; + item->numConditionals = numConditionals; + item->conditionals = conditionals; + + return item; +} + +void freeListCommandItem(ListCommandItem * item) { + free(item); +} + +void printUnvisitedTags(FILE * fp, Song * song, int tagType) { + int i; + MpdTag * tag = song->tag; + + if(tagType == LOCATE_TAG_FILE_TYPE) { + myfprintf(fp, "file: %s\n", song->utf8url); + return; + } + + if(!tag) return; + + for(i = 0; i < tag->numOfItems; i++) { + if(tag->items[i].type == tagType && + !wasVisitedInTagTracker(tagType, tag->items[i].value)) + { + myfprintf(fp, "%s: %s\n", mpdTagItemKeys[tagType], + tag->items[i].value); + } + } +} + +int listUniqueTagsInDirectory(FILE * fp, Song * song, void * data) { + ListCommandItem * item = data; + int i; + + for(i = 0; i < item->numConditionals; i++) { + if(!tagItemFoundAndMatches(song, item->conditionals[i].tagType, + item->conditionals[i].needle)) + { + return 0; + } + } + + printUnvisitedTags(fp, song, item->tagType); + + return 0; +} + +int listAllUniqueTags(FILE * fp, int type, int numConditionals, + LocateTagItem * conditionals) +{ + int ret; + ListCommandItem * item = newListCommandItem(type, numConditionals, + conditionals); + + if(type >= 0 && type <= TAG_NUM_OF_ITEM_TYPES) { + resetVisitedFlagsInTagTracker(type); + } + + ret = traverseAllIn(fp, NULL, listUniqueTagsInDirectory, NULL, + (void *)item); + + freeListCommandItem(item); + + return ret; +} diff --git a/src/dbUtils.h b/src/dbUtils.h new file mode 100644 index 000000000..d0f084701 --- /dev/null +++ b/src/dbUtils.h @@ -0,0 +1,42 @@ +#ifndef DB_UTILS_H +#define DB_UTILS_H + +#include <stdio.h> + +#include "tag.h" + +#define LOCATE_TAG_FILE_TYPE TAG_NUM_OF_ITEM_TYPES+10 +#define LOCATE_TAG_FILE_KEY "filename" + +/* struct used for search, find, list queries */ +typedef struct _LocateTagItem { + mpd_sint8 tagType; + /* what we are looking for */ + char * needle; +} LocateTagItem; + +/* returns NULL if not a known type */ +LocateTagItem * newLocateTagItem(char * typeString, char * needle); + +int getLocateTagItemType(char * str); + +void freeLocateTagItem(LocateTagItem * item); + +int printAllIn(FILE * fp, char * name); + +int addAllIn(FILE * fp, char * name); + +int printInfoForAllIn(FILE * fp, char * name); + +int searchForSongsIn(FILE * fp, char * name, LocateTagItem * item); + +int findSongsIn(FILE * fp, char * name, LocateTagItem * item); + +int countSongsIn(FILE * fp, char * name); + +unsigned long sumSongTimesIn(FILE * fp, char * name); + +int listAllUniqueTags(FILE * fp, int type, int numConditiionals, + LocateTagItem * conditionals); + +#endif diff --git a/src/decode.c b/src/decode.c index 52923ce87..893e1a55c 100644 --- a/src/decode.c +++ b/src/decode.c @@ -158,10 +158,6 @@ int waitOnDecode(PlayerControl * pc, DecoderControl * dc, OutputBuffer * cb, } if((tag = metadataChunkToMpdTagDup(&(pc->fileMetadataChunk)))) { - /* lets put the filename in the title if no tag info */ - if(!tag->title && !tag->artist && !tag->album) { - tag->title = strdup(pc->currentUrl); - } sendMetadataToAudioDevice(tag); freeMpdTag(tag); } diff --git a/src/directory.c b/src/directory.c index 38043276f..5ac78afaf 100644 --- a/src/directory.c +++ b/src/directory.c @@ -32,6 +32,9 @@ #include "mpd_types.h" #include "sig_handlers.h" #include "player.h" +#include "tagTracker.h" +#include "list.h" +#include "dbUtils.h" #include <string.h> #include <sys/types.h> @@ -54,11 +57,6 @@ #define DIRECTORY_MPD_VERSION "mpd_version: " #define DIRECTORY_FS_CHARSET "fs_charset: " -#define DIRECTORY_SEARCH_ALBUM "album" -#define DIRECTORY_SEARCH_ARTIST "artist" -#define DIRECTORY_SEARCH_TITLE "title" -#define DIRECTORY_SEARCH_FILENAME "filename" - #define DIRECTORY_UPDATE_EXIT_NOUPDATE 0 #define DIRECTORY_UPDATE_EXIT_UPDATE 1 #define DIRECTORY_UPDATE_EXIT_ERROR 2 @@ -67,21 +65,6 @@ #define DIRECTORY_RETURN_UPDATE 1 #define DIRECTORY_RETURN_ERROR -1 -typedef List DirectoryList; - -typedef struct _DirectoryStat { - ino_t inode; - dev_t device; -} DirectoryStat; - -typedef struct _Directory { - char * utf8name; - DirectoryList * subDirectories; - SongList * songs; - struct _Directory * parent; - DirectoryStat * stat; -} Directory; - Directory * mp3rootDirectory = NULL; char * directory_db; @@ -1000,6 +983,8 @@ int writeDirectoryDB() { while(fclose(fp) && errno==EINTR); + sortTagTrackerInfo(); + return 0; } @@ -1088,6 +1073,8 @@ int readDirectoryDB() { if(stat(directory_db,&st)==0) directory_dbModTime = st.st_mtime; + sortTagTrackerInfo(); + return 0; } @@ -1168,159 +1155,6 @@ int traverseAllIn(FILE * fp, char * name, data); } -int countSongsInDirectory(FILE * fp, Directory * directory, void * data) { - int * count = (int *)data; - - *count+=directory->songs->numberOfNodes; - - return 0; -} - -int printDirectoryInDirectory(FILE * fp, Directory * directory, void * data) { - if(directory->utf8name) { - myfprintf(fp,"directory: %s\n",directory->utf8name); - } - return 0; -} - -int printSongInDirectory(FILE * fp, Song * song, void * data) { - myfprintf(fp,"file: %s\n",song->utf8url); - return 0; -} - -int searchForAlbumInDirectory(FILE * fp, Song * song, void * string) { - if(song->tag && song->tag->album) { - char * dup = strDupToUpper(song->tag->album); - if(strstr(dup,(char *)string)) printSongInfo(fp,song); - free(dup); - } - return 0; -} - -int searchForArtistInDirectory(FILE * fp, Song * song, void * string) { - if(song->tag && song->tag->artist) { - char * dup = strDupToUpper(song->tag->artist); - if(strstr(dup,(char *)string)) printSongInfo(fp,song); - free(dup); - } - return 0; -} - -int searchForTitleInDirectory(FILE * fp, Song * song, void * string) { - if(song->tag && song->tag->title) { - char * dup = strDupToUpper(song->tag->title); - if(strstr(dup,(char *)string)) printSongInfo(fp,song); - free(dup); - } - return 0; -} - -int searchForFilenameInDirectory(FILE * fp, Song * song, void * string) { - char * dup = strDupToUpper(song->utf8url); - if(strstr(dup,(char *)string)) printSongInfo(fp,song); - free(dup); - return 0; -} - -int searchForSongsIn(FILE * fp, char * name, char * item, char * string) { - char * dup = strDupToUpper(string); - int ret = -1; - - if(strcmp(item,DIRECTORY_SEARCH_ALBUM)==0) { - ret = traverseAllIn(fp,name,searchForAlbumInDirectory,NULL, - (void *)dup); - } - else if(strcmp(item,DIRECTORY_SEARCH_ARTIST)==0) { - ret = traverseAllIn(fp,name,searchForArtistInDirectory,NULL, - (void *)dup); - } - else if(strcmp(item,DIRECTORY_SEARCH_TITLE)==0) { - ret = traverseAllIn(fp,name,searchForTitleInDirectory,NULL, - (void *)dup); - } - else if(strcmp(item,DIRECTORY_SEARCH_FILENAME)==0) { - ret = traverseAllIn(fp,name,searchForFilenameInDirectory,NULL, - (void *)dup); - } - else commandError(fp, ACK_ERROR_ARG, "unknown table", NULL); - - free(dup); - - return ret; -} - -int findAlbumInDirectory(FILE * fp, Song * song, void * string) { - if(song->tag && song->tag->album && - strcmp((char *)string,song->tag->album)==0) - { - printSongInfo(fp,song); - } - - return 0; -} - -int findArtistInDirectory(FILE * fp, Song * song, void * string) { - if(song->tag && song->tag->artist && - strcmp((char *)string,song->tag->artist)==0) - { - printSongInfo(fp,song); - } - - return 0; -} - -int findSongsIn(FILE * fp, char * name, char * item, char * string) { - if(strcmp(item,DIRECTORY_SEARCH_ALBUM)==0) { - return traverseAllIn(fp,name,findAlbumInDirectory,NULL, - (void *)string); - } - else if(strcmp(item,DIRECTORY_SEARCH_ARTIST)==0) { - return traverseAllIn(fp,name,findArtistInDirectory,NULL, - (void *)string); - } - - commandError(fp, ACK_ERROR_ARG, "unknown table", NULL); - return -1; -} - -int printAllIn(FILE * fp, char * name) { - return traverseAllIn(fp,name,printSongInDirectory, - printDirectoryInDirectory,NULL); -} - -int directoryAddSongToPlaylist(FILE * fp, Song * song, void * data) { - return addSongToPlaylist(fp, song, 0); -} - -int addAllIn(FILE * fp, char * name) { - return traverseAllIn(fp,name,directoryAddSongToPlaylist,NULL,NULL); -} - -int directoryPrintSongInfo(FILE * fp, Song * song, void * data) { - return printSongInfo(fp,song); -} - -int sumSongTime(FILE * fp, Song * song, void * data) { - unsigned long * time = (unsigned long *)data; - - if(song->tag && song->tag->time>=0) *time+=song->tag->time; - - return 0; -} - -int printInfoForAllIn(FILE * fp, char * name) { - return traverseAllIn(fp,name,directoryPrintSongInfo,printDirectoryInDirectory,NULL); -} - -int countSongsIn(FILE * fp, char * name) { - int count = 0; - void * ptr = (void *)&count; - - traverseAllIn(fp,name,NULL,countSongsInDirectory,ptr); - - return count; -} - void freeAllDirectoryStats(Directory * directory) { ListNode * node = directory->subDirectories->firstNode; @@ -1332,15 +1166,6 @@ void freeAllDirectoryStats(Directory * directory) { freeDirectoryStatFromDirectory(directory); } -unsigned long sumSongTimesIn(FILE * fp, char * name) { - unsigned long dbPlayTime = 0; - void * ptr = (void *)&dbPlayTime; - - traverseAllIn(fp,name,sumSongTime,NULL,ptr); - - return dbPlayTime; -} - void initMp3Directory() { struct stat st; diff --git a/src/directory.h b/src/directory.h index b11f18ffb..b0b2c843c 100644 --- a/src/directory.h +++ b/src/directory.h @@ -27,6 +27,21 @@ #include <stdio.h> #include <sys/param.h> +typedef List DirectoryList; + +typedef struct _DirectoryStat { + ino_t inode; + dev_t device; +} DirectoryStat; + +typedef struct _Directory { + char * utf8name; + DirectoryList * subDirectories; + SongList * songs; + struct _Directory * parent; + DirectoryStat * stat; +} Directory; + extern char * directory_db; void readDirectoryDBIfUpdateIsFinished(); @@ -51,22 +66,12 @@ int readDirectoryDB(); void updateMp3Directory(); -int printAllIn(FILE * fp, char * name); - -int addAllIn(FILE * fp, char * name); - -int printInfoForAllIn(FILE * fp, char * name); - -int searchForSongsIn(FILE * fp, char * name, char * item, char * string); - -int findSongsIn(FILE * fp, char * name, char * item, char * string); - -int countSongsIn(FILE * fp, char * name); - -unsigned long sumSongTimesIn(FILE * fp, char * name); - Song * getSongFromDB(char * file); time_t getDbModTime(); +int traverseAllIn(FILE * fp, char * name, + int (*forEachSong)(FILE *, Song *, void *), + int (*forEachDir)(FILE *, Directory *, void *), + void * data); #endif diff --git a/src/inputPlugins/flac_plugin.c b/src/inputPlugins/flac_plugin.c index d038f1418..bc706ff64 100644 --- a/src/inputPlugins/flac_plugin.c +++ b/src/inputPlugins/flac_plugin.c @@ -450,12 +450,73 @@ FLAC__StreamDecoderWriteStatus flacWrite(const FLAC__SeekableStreamDecoder *dec, return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE; } +static int commentMatchesAddToTag( + char * str, + FLAC__StreamMetadata_VorbisComment_Entry * entry, + int itemType, + MpdTag ** tag) +{ + int slen = strlen(str); + int vlen = entry->length - slen; + + if( vlen <= 0 ) return 0; + + if( 0 == strncasecmp(str, entry->entry, slen) ) { + if(*tag == NULL) *tag = newMpdTag(); + addItemToMpdTagWithLen(*tag, itemType, + entry->entry+slen, vlen); + return 1; + } + + return 0; +} + + +static MpdTag * copyVorbisCommentBlockToMpdTag(FLAC__StreamMetadata * block, + MpdTag * tag) +{ + int i; + + for(i = 0; i < block->data.vorbis_comment.num_comments; i++) { + if(commentMatchesAddToTag( + "artist=", + block->data.vorbis_comment.comments+i, + TAG_ITEM_ARTIST, + &tag)); + else if(commentMatchesAddToTag( + "title=", + block->data.vorbis_comment.comments+i, + TAG_ITEM_TITLE, + &tag)); + else if(commentMatchesAddToTag( + "album=", + block->data.vorbis_comment.comments+i, + TAG_ITEM_ALBUM, + &tag)); + else if(commentMatchesAddToTag( + "tracknumber=", + block->data.vorbis_comment.comments+i, + TAG_ITEM_TRACK, + &tag)); + else if(commentMatchesAddToTag( + "genre=", + block->data.vorbis_comment.comments+i, + TAG_ITEM_GENRE, + &tag)); + else if(commentMatchesAddToTag( + "date=", + block->data.vorbis_comment.comments+i, + TAG_ITEM_DATE, + &tag)); + } + + return tag; +} + MpdTag * flacMetadataDup(char * file, int * vorbisCommentFound) { MpdTag * ret = NULL; FLAC__Metadata_SimpleIterator * it; FLAC__StreamMetadata * block = NULL; - int offset; - int len, pos; *vorbisCommentFound = 0; @@ -469,60 +530,9 @@ MpdTag * flacMetadataDup(char * file, int * vorbisCommentFound) { block = FLAC__metadata_simple_iterator_get_block(it); if(!block) break; if(block->type == FLAC__METADATA_TYPE_VORBIS_COMMENT) { - char * dup; - - offset = FLAC__metadata_object_vorbiscomment_find_entry_from(block,0,"artist"); - if(offset>=0) { - *vorbisCommentFound = 1; - if(!ret) ret = newMpdTag(); - pos = strlen("artist="); - len = block->data.vorbis_comment.comments[offset].length-pos; - if(len>0) { - dup = malloc(len+1); - memcpy(dup,&(block->data.vorbis_comment.comments[offset].entry[pos]),len); - dup[len] = '\0'; - ret->artist = dup; - } - } - offset = FLAC__metadata_object_vorbiscomment_find_entry_from(block,0,"album"); - if(offset>=0) { - *vorbisCommentFound = 1; - if(!ret) ret = newMpdTag(); - pos = strlen("album="); - len = block->data.vorbis_comment.comments[offset].length-pos; - if(len>0) { - dup = malloc(len+1); - memcpy(dup,&(block->data.vorbis_comment.comments[offset].entry[pos]),len); - dup[len] = '\0'; - ret->album = dup; - } - } - offset = FLAC__metadata_object_vorbiscomment_find_entry_from(block,0,"title"); - if(offset>=0) { - *vorbisCommentFound = 1; - if(!ret) ret = newMpdTag(); - pos = strlen("title="); - len = block->data.vorbis_comment.comments[offset].length-pos; - if(len>0) { - dup = malloc(len+1); - memcpy(dup,&(block->data.vorbis_comment.comments[offset].entry[pos]),len); - dup[len] = '\0'; - ret->title = dup; - } - } - offset = FLAC__metadata_object_vorbiscomment_find_entry_from(block,0,"tracknumber"); - if(offset>=0) { - *vorbisCommentFound = 1; - if(!ret) ret = newMpdTag(); - pos = strlen("tracknumber="); - len = block->data.vorbis_comment.comments[offset].length-pos; - if(len>0) { - dup = malloc(len+1); - memcpy(dup,&(block->data.vorbis_comment.comments[offset].entry[pos]),len); - dup[len] = '\0'; - ret->track = dup; - } - } + ret = copyVorbisCommentBlockToMpdTag(block, ret); + + if(ret) *vorbisCommentFound = 1; } else if(block->type == FLAC__METADATA_TYPE_STREAMINFO) { if(!ret) ret = newMpdTag(); diff --git a/src/inputPlugins/mod_plugin.c b/src/inputPlugins/mod_plugin.c index 0ea7dffad..f9b23b595 100644 --- a/src/inputPlugins/mod_plugin.c +++ b/src/inputPlugins/mod_plugin.c @@ -212,6 +212,7 @@ int mod_decode(OutputBuffer * cb, DecoderControl * dc, char * path) { MpdTag * modTagDup(char * file) { MpdTag * ret = NULL; MODULE * moduleHandle; + char * title; if(mod_initMikMod() < 0) return NULL; @@ -222,7 +223,8 @@ MpdTag * modTagDup(char * file) { ret = newMpdTag(); ret->time = 0; - ret->title = strdup(Player_LoadTitle(file)); + title = strdup(Player_LoadTitle(file)); + if(title) mpdItemToMpdTag(ret, TAG_ITEM_TITLE, title); fail: MikMod_Exit(); diff --git a/src/inputPlugins/mp3_plugin.c b/src/inputPlugins/mp3_plugin.c index ab204ffa9..cdb2a74c0 100644 --- a/src/inputPlugins/mp3_plugin.c +++ b/src/inputPlugins/mp3_plugin.c @@ -549,9 +549,12 @@ int mp3Read(mp3DecodeData * data, OutputBuffer * cb, DecoderControl * dc) { if(data->inStream->metaTitle) { MpdTag * tag = newMpdTag(); if(data->inStream->metaName) { - tag->name = strdup(data->inStream->metaName); + addItemToMpdTag(tag, + TAG_ITEM_NAME, + data->inStream->metaName); } - tag->title = strdup(data->inStream->metaTitle); + addItemToMpdTag(tag, TAG_ITEM_TITLE, + data->inStream->metaTitle); free(data->inStream->metaTitle); data->inStream->metaTitle = NULL; copyMpdTagToOutputBuffer(cb, tag); @@ -676,19 +679,21 @@ int mp3_decode(OutputBuffer * cb, DecoderControl * dc, InputStream * inStream) { if(inStream->metaTitle) { if(tag) freeMpdTag(tag); tag = newMpdTag(); - tag->title = strdup(inStream->metaTitle); + addItemToMpdTag(tag, TAG_ITEM_TITLE, inStream->metaTitle); free(inStream->metaTitle); inStream->metaTitle = NULL; if(inStream->metaName) { - tag->name = strdup(inStream->metaName); + addItemToMpdTag(tag, TAG_ITEM_NAME, + inStream->metaName); } copyMpdTagToOutputBuffer(cb, tag); freeMpdTag(tag); } else if(tag) { if(inStream->metaName) { - if(tag->name) free(tag->name); - tag->name = strdup(inStream->metaName); + clearItemsFromMpdTag(tag, TAG_ITEM_NAME); + addItemToMpdTag(tag, TAG_ITEM_NAME, + inStream->metaName); } copyMpdTagToOutputBuffer(cb, tag); freeMpdTag(tag); @@ -696,7 +701,8 @@ int mp3_decode(OutputBuffer * cb, DecoderControl * dc, InputStream * inStream) { else if(inStream->metaName) { tag = newMpdTag(); if(inStream->metaName) { - tag->name = strdup(inStream->metaName); + addItemToMpdTag(tag, TAG_ITEM_NAME, + inStream->metaName); } copyMpdTagToOutputBuffer(cb, tag); freeMpdTag(tag); diff --git a/src/inputPlugins/mp4_plugin.c b/src/inputPlugins/mp4_plugin.c index 66d5600b4..a66f7d43d 100644 --- a/src/inputPlugins/mp4_plugin.c +++ b/src/inputPlugins/mp4_plugin.c @@ -322,6 +322,7 @@ MpdTag * mp4DataDup(char * file, int * mp4MetadataFound) { int32_t track; int32_t time; int32_t scale; + int i; *mp4MetadataFound = 0; @@ -359,20 +360,39 @@ MpdTag * mp4DataDup(char * file, int * mp4MetadataFound) { } ret->time = ((float)time)/scale+0.5; - if(!mp4ff_meta_get_artist(mp4fh,&ret->artist)) { - *mp4MetadataFound = 1; - } + for(i = 0; i < mp4ff_meta_get_num_items(mp4fh); i++) { + char * item; + char * value; - if(!mp4ff_meta_get_album(mp4fh,&ret->album)) { - *mp4MetadataFound = 1; - } + mp4ff_meta_get_by_index(mp4fh, i, &item, &value); - if(!mp4ff_meta_get_title(mp4fh,&ret->title)) { - *mp4MetadataFound = 1; - } + if(0 == strcasecmp("artist", item)) { + addItemToMpdTag(ret, TAG_ITEM_ARTIST, value); + *mp4MetadataFound = 1; + } + else if(0 == strcasecmp("title", item)) { + addItemToMpdTag(ret, TAG_ITEM_TITLE, value); + *mp4MetadataFound = 1; + } + else if(0 == strcasecmp("album", item)) { + addItemToMpdTag(ret, TAG_ITEM_ALBUM, value); + *mp4MetadataFound = 1; + } + else if(0 == strcasecmp("track", item)) { + addItemToMpdTag(ret, TAG_ITEM_TRACK, value); + *mp4MetadataFound = 1; + } + else if(0 == strcasecmp("genre", item)) { + addItemToMpdTag(ret, TAG_ITEM_GENRE, value); + *mp4MetadataFound = 1; + } + else if(0 == strcasecmp("date", item)) { + addItemToMpdTag(ret, TAG_ITEM_DATE, value); + *mp4MetadataFound = 1; + } - if(!mp4ff_meta_get_track(mp4fh,&ret->track)) { - *mp4MetadataFound = 1; + free(item); + free(value); } mp4ff_close(mp4fh); diff --git a/src/inputPlugins/ogg_plugin.c b/src/inputPlugins/ogg_plugin.c index d6add6d3a..b681a27e0 100644 --- a/src/inputPlugins/ogg_plugin.c +++ b/src/inputPlugins/ogg_plugin.c @@ -162,27 +162,27 @@ MpdTag * oggCommentsParse(char ** comments) { while(*comments) { if((temp = ogg_parseComment(*comments,"artist"))) { if(!ret) ret = newMpdTag(); - if(!ret->artist) { - ret->artist = strdup(temp); - } + addItemToMpdTag(ret, TAG_ITEM_ARTIST, temp); } else if((temp = ogg_parseComment(*comments,"title"))) { if(!ret) ret = newMpdTag(); - if(!ret->title) { - ret->title = strdup(temp); - } + addItemToMpdTag(ret, TAG_ITEM_TITLE, temp); } else if((temp = ogg_parseComment(*comments,"album"))) { if(!ret) ret = newMpdTag(); - if(!ret->album) { - ret->album = strdup(temp); - } + addItemToMpdTag(ret, TAG_ITEM_ALBUM, temp); } else if((temp = ogg_parseComment(*comments,"tracknumber"))) { if(!ret) ret = newMpdTag(); - if(!ret->track) { - ret->track = strdup(temp); - } + addItemToMpdTag(ret, TAG_ITEM_TRACK, temp); + } + else if((temp = ogg_parseComment(*comments,"genre"))) { + if(!ret) ret = newMpdTag(); + addItemToMpdTag(ret, TAG_ITEM_GENRE, temp); + } + else if((temp = ogg_parseComment(*comments,"date"))) { + if(!ret) ret = newMpdTag(); + addItemToMpdTag(ret, TAG_ITEM_DATE, temp); } comments++; @@ -208,8 +208,8 @@ void putOggCommentsIntoOutputBuffer(OutputBuffer * cb, char * streamName, if(tag->title) printf("Title: %s\n", tag->title);*/ if(streamName) { - if(tag->name) free(tag->name); - tag->name = strdup(streamName); + clearItemsFromMpdTag(tag, TAG_ITEM_NAME); + addItemToMpdTag(tag, TAG_ITEM_NAME, streamName); } copyMpdTagToOutputBuffer(cb, tag); diff --git a/src/list.c b/src/list.c index c7d49397a..48f1e1c62 100644 --- a/src/list.c +++ b/src/list.c @@ -104,7 +104,7 @@ int insertInListBeforeNode(List * list, ListNode * beforeNode, char * key, return 1; } -int insertInList(List * list,char * key,void * data) { +ListNode * insertInList(List * list,char * key,void * data) { ListNode * node; assert(list!=NULL); @@ -137,7 +137,7 @@ int insertInList(List * list,char * key,void * data) { list->numberOfNodes++; - return 1; + return node; } int insertInListWithoutKey(List * list, void * data) { @@ -173,7 +173,7 @@ int insertInListWithoutKey(List * list, void * data) { return 1; } -int findInList(List * list,char * key,void ** data) { +ListNode * findNodeInList(List * list, char * key) { static long high; static long low; static long cur; @@ -191,10 +191,7 @@ int findInList(List * list,char * key,void ** data) { cur = (high+low)/2; tmpNode = list->nodesArray[cur]; cmp = strcmp(tmpNode->key,key); - if(cmp==0) { - if(data) *data = tmpNode->data; - return 1; - } + if(cmp==0) return tmpNode; else if(cmp>0) high = cur; else { if(low==cur) break; @@ -205,10 +202,7 @@ int findInList(List * list,char * key,void ** data) { cur = high; if(cur>=0) { tmpNode = list->nodesArray[cur]; - if(strcmp(tmpNode->key,key)==0) { - (*data) = tmpNode->data; - return 1; - } + if(strcmp(tmpNode->key,key)==0) return tmpNode; } } else { @@ -218,10 +212,18 @@ int findInList(List * list,char * key,void ** data) { tmpNode = tmpNode->nextNode; } - if(tmpNode!=NULL) { - (*data) = tmpNode->data; - return 1; - } + return tmpNode; + } + + return NULL; +} + +int findInList(List * list, char * key, void ** data) { + ListNode * node = findNodeInList(list, key); + + if(node) { + if(data) *data = node->data; + return 1; } return 0; diff --git a/src/list.h b/src/list.h index 4653237f0..86f4a239a 100644 --- a/src/list.h +++ b/src/list.h @@ -67,7 +67,7 @@ List * makeList(ListFreeDataFunc * freeDataFunc); * _data_ -> data to be inserted in list * returns 1 if successful, 0 otherwise */ -int insertInList(List * list,char * key,void * data); +ListNode * insertInList(List * list,char * key,void * data); int insertInListBeforeNode(List * list, ListNode * beforeNode, char * key, void * data); @@ -93,6 +93,8 @@ void deleteNodeFromList(List * list,ListNode * node); */ int findInList(List * list, char * key, void ** data); +ListNode * findNodeInList(List * list, char * key); + /* frees memory malloc'd for list and its nodes * _list_ -> List to be free'd */ diff --git a/src/main.c b/src/main.c index bb1146cb0..65557a4b8 100644 --- a/src/main.c +++ b/src/main.c @@ -20,7 +20,6 @@ #include "command.h" #include "playlist.h" #include "directory.h" -#include "tables.h" #include "player.h" #include "listen.h" #include "conf.h" @@ -35,6 +34,8 @@ #include "replayGain.h" #include "inputPlugin.h" #include "inputStream.h" +#include "tag.h" +#include "tagTracker.h" #include "../config.h" #include <stdio.h> @@ -403,6 +404,7 @@ int main(int argc, char * argv[]) { parseOptions(argc, argv, &options); initStats(); + initTagConfig(); initLog(); establishListen(&options); @@ -415,7 +417,6 @@ int main(int argc, char * argv[]) { initPermissions(); initReplayGainState(); - initTables(); initPlaylist(); initInputPlugins(); @@ -427,8 +428,10 @@ int main(int argc, char * argv[]) { initAudioDriver(); initVolume(); initInterfaces(); - initInputStream(); + initInputStream(); + printMemorySavedByTagTracker(); + daemonize(&options); setupLogOutput(&options, out, err); @@ -450,7 +453,6 @@ int main(int argc, char * argv[]) { freeAllInterfaces(); closeAllListenSockets(); closeMp3Directory(); - closeTables(); finishPlaylist(); freePlayerData(); finishAudioDriver(); diff --git a/src/metadataChunk.c b/src/metadataChunk.c index 84817eb0e..40da4c512 100644 --- a/src/metadataChunk.c +++ b/src/metadataChunk.c @@ -29,9 +29,9 @@ void initMetadataChunk(MetadataChunk * chunk) { chunk->title = -1; } -#define dupElementToTag(string, element) { \ +#define dupElementToTag(item, element) { \ if(element >= 0 && element < METADATA_BUFFER_LENGTH) { \ - string = strdup(chunk->buffer+element); \ + addItemToMpdTag(ret, item, chunk->buffer+element); \ } \ } @@ -40,10 +40,10 @@ MpdTag * metadataChunkToMpdTagDup(MetadataChunk * chunk) { chunk->buffer[METADATA_BUFFER_LENGTH-1] = '\0'; - dupElementToTag(ret->name, chunk->name); - dupElementToTag(ret->title, chunk->title); - dupElementToTag(ret->artist, chunk->artist); - dupElementToTag(ret->album, chunk->album); + dupElementToTag(TAG_ITEM_NAME, chunk->name); + dupElementToTag(TAG_ITEM_TITLE, chunk->title); + dupElementToTag(TAG_ITEM_ARTIST, chunk->artist); + dupElementToTag(TAG_ITEM_ALBUM, chunk->album); return ret; } @@ -67,8 +67,16 @@ void copyMpdTagToMetadataChunk(MpdTag * tag, MetadataChunk * chunk) { if(!tag) return; - copyStringToChunk(tag->name, chunk->name); - copyStringToChunk(tag->title, chunk->title); - copyStringToChunk(tag->artist, chunk->artist); - copyStringToChunk(tag->album, chunk->album); + copyStringToChunk( + getNextItemFromMpdTag(tag, TAG_ITEM_NAME, NULL), + chunk->name); + copyStringToChunk( + getNextItemFromMpdTag(tag, TAG_ITEM_TITLE, NULL), + chunk->title); + copyStringToChunk( + getNextItemFromMpdTag(tag, TAG_ITEM_ARTIST, NULL), + chunk->title); + copyStringToChunk( + getNextItemFromMpdTag(tag, TAG_ITEM_ALBUM, NULL), + chunk->album); } diff --git a/src/player.c b/src/player.c index c16cad22e..5b8298970 100644 --- a/src/player.c +++ b/src/player.c @@ -25,7 +25,6 @@ #include "listen.h" #include "log.h" #include "utils.h" -#include "tables.h" #include "directory.h" #include "volume.h" #include "playerData.h" @@ -116,7 +115,6 @@ int playerInit() { freeAllInterfaces(); closeMp3Directory(); finishPlaylist(); - closeTables(); finishPermissions(); finishCommands(); finishVolume(); @@ -480,7 +478,6 @@ Song * playerCurrentDecodeSong() { song = newNullSong(); song->utf8url = strdup(pc->currentUrl); song->tag = metadataChunkToMpdTagDup(prev); - validateUtf8Tag(song->tag); ret = song; resetPlayerMetadata(); } diff --git a/src/song.c b/src/song.c index 06dd67a89..462a434cc 100644 --- a/src/song.c +++ b/src/song.c @@ -19,22 +19,15 @@ #include "song.h" #include "ls.h" #include "directory.h" -#include "tables.h" #include "utils.h" #include "tag.h" #include "log.h" #include "path.h" #include "playlist.h" -#include "tables.h" #include "inputPlugin.h" #define SONG_KEY "key: " #define SONG_FILE "file: " -#define SONG_ARTIST "Artist: " -#define SONG_ALBUM "Album: " -#define SONG_TRACK "Track: " -#define SONG_TITLE "Title: " -#define SONG_NAME "Name: " #define SONG_TIME "Time: " #define SONG_MTIME "mtime: " @@ -66,13 +59,11 @@ Song * newSong(char * utf8url, SONG_TYPE type) { if((plugin = isMusic(utf8url,&(song->mtime)))) { song->tag = plugin->tagDupFunc( rmp2amp(utf8ToFsCharset(utf8url))); - if(song->tag) validateUtf8Tag(song->tag); } if(!song->tag || song->tag->time<0) { freeSong(song); song = NULL; } - else addSongToTables(song); } return song; @@ -80,7 +71,6 @@ Song * newSong(char * utf8url, SONG_TYPE type) { void freeSong(Song * song) { deleteASongFromPlaylist(song); - if(song->type == SONG_TYPE_FILE) removeASongFromTables(song); free(song->utf8url); if(song->tag) freeMpdTag(song->tag); free(song); @@ -172,27 +162,38 @@ void insertSongIntoList(SongList * list, ListNode ** nextSongNode, char * key, if(!(*nextSongNode)) { insertInList(list,key,(void *)song); - addSongToTables(song); } else if(cmpRet == 0) { Song * tempSong = (Song *)((*nextSongNode)->data); if(tempSong->mtime != song->mtime) { - removeASongFromTables(tempSong); freeMpdTag(tempSong->tag); tempSong->tag = song->tag; tempSong->mtime = song->mtime; song->tag = NULL; - addSongToTables(tempSong); } freeJustSong(song); *nextSongNode = (*nextSongNode)->nextNode; } else { - addSongToTables(song); insertInListBeforeNode(list,*nextSongNode,key,(void *)song); } } +static int matchesAnMpdTagItemKey(char * buffer, int * itemType) { + int i; + + for(i = 0; i < TAG_NUM_OF_ITEM_TYPES; i++) { + if( 0 == strncmp(mpdTagItemKeys[i], buffer, + strlen(mpdTagItemKeys[i]))) + { + *itemType = i; + return 1; + } + } + + return 0; +} + void readSongInfoIntoList(FILE * fp, SongList * list) { char buffer[MAXPATHLEN+1024]; int bufferSize = MAXPATHLEN+1024; @@ -200,6 +201,7 @@ void readSongInfoIntoList(FILE * fp, SongList * list) { char * key = NULL; ListNode * nextSongNode = list->firstNode; ListNode * nodeTemp; + int itemType; while(myFgets(buffer,bufferSize,fp) && 0!=strcmp(SONG_END,buffer)) { if(0==strncmp(SONG_KEY,buffer,strlen(SONG_KEY))) { @@ -220,32 +222,17 @@ void readSongInfoIntoList(FILE * fp, SongList * list) { } song->utf8url = strdup(&(buffer[strlen(SONG_FILE)])); } - else if(0==strncmp(SONG_ARTIST,buffer,strlen(SONG_ARTIST))) { - if(!song->tag) song->tag = newMpdTag(); - song->tag->artist = strdup(&(buffer[strlen(SONG_ARTIST)])); - } - else if(0==strncmp(SONG_ALBUM,buffer,strlen(SONG_ALBUM))) { - if(!song->tag) song->tag = newMpdTag(); - song->tag->album = strdup(&(buffer[strlen(SONG_ALBUM)])); - } - else if(0==strncmp(SONG_TRACK,buffer,strlen(SONG_TRACK))) { - if(!song->tag) song->tag = newMpdTag(); - song->tag->track = strdup(&(buffer[strlen(SONG_TRACK)])); - } - else if(0==strncmp(SONG_TITLE,buffer,strlen(SONG_TITLE))) { - if(!song->tag) song->tag = newMpdTag(); - song->tag->title = strdup(&(buffer[strlen(SONG_TITLE)])); - } - else if(0==strncmp(SONG_NAME,buffer,strlen(SONG_NAME))) { + else if(matchesAnMpdTagItemKey(buffer, &itemType)) { if(!song->tag) song->tag = newMpdTag(); - song->tag->name = strdup(&(buffer[strlen(SONG_NAME)])); + addItemToMpdTag(song->tag, itemType, + &(buffer[strlen(mpdTagItemKeys[itemType])+2])); } else if(0==strncmp(SONG_TIME,buffer,strlen(SONG_TIME))) { if(!song->tag) song->tag = newMpdTag(); song->tag->time = atoi(&(buffer[strlen(SONG_TIME)])); } else if(0==strncmp(SONG_MTIME,buffer,strlen(SONG_MTIME))) { - song->mtime = atoi(&(buffer[strlen(SONG_TITLE)])); + song->mtime = atoi(&(buffer[strlen(SONG_MTIME)])); } else { ERROR("songinfo: unknown line in db: %s\n",buffer); @@ -272,7 +259,6 @@ int updateSongInfo(Song * song) { if(song->type == SONG_TYPE_FILE) { InputPlugin * plugin; - removeASongFromTables(song); if(song->tag) freeMpdTag(song->tag); song->tag = NULL; @@ -280,10 +266,8 @@ int updateSongInfo(Song * song) { if((plugin = isMusic(utf8url,&(song->mtime)))) { song->tag = plugin->tagDupFunc( rmp2amp(utf8ToFsCharset(utf8url))); - if(song->tag) validateUtf8Tag(song->tag); } if(!song->tag || song->tag->time<0) return -1; - else addSongToTables(song); } return 0; diff --git a/src/stats.c b/src/stats.c index b114e1764..6efa8cb8f 100644 --- a/src/stats.c +++ b/src/stats.c @@ -18,10 +18,11 @@ #include "stats.h" -#include "tables.h" #include "directory.h" #include "myfprintf.h" #include "player.h" +#include "tag.h" +#include "tagTracker.h" #include <time.h> @@ -35,8 +36,8 @@ void initStats() { } int printStats(FILE * fp) { - myfprintf(fp,"artists: %li\n",numberOfArtists()); - myfprintf(fp,"albums: %li\n",numberOfAlbums()); + myfprintf(fp,"artists: %li\n", getNumberOfTagItems(TAG_ITEM_ARTIST)); + myfprintf(fp,"albums: %li\n", getNumberOfTagItems(TAG_ITEM_ALBUM)); myfprintf(fp,"songs: %i\n",stats.numberOfSongs); myfprintf(fp,"uptime: %li\n",time(NULL)-stats.daemonStart); myfprintf(fp,"playtime: %li\n",(long)(getPlayerTotalPlayTime()+0.5)); diff --git a/src/tables.c b/src/tables.c deleted file mode 100644 index a29f60163..000000000 --- a/src/tables.c +++ /dev/null @@ -1,200 +0,0 @@ -/* the Music Player Daemon (MPD) - * (c)2003-2004 by Warren Dukes (shank@mercury.chem.pitt.edu) - * This project's homepage is: http://www.musicpd.org - * - * 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 - */ - - -#include "tables.h" -#include "list.h" -#include "command.h" -#include "utils.h" -#include "myfprintf.h" - -#include <string.h> - -#define TABLES_ARTIST "artist" -#define TABLES_ALBUM "album" - -List * albumTable; -List * artistTable; - -typedef struct _ArtistData { - int songs; - List * albums; -} ArtistData; - -ArtistData * newArtistData() { - ArtistData * ad = malloc(sizeof(ArtistData)); - - ad->songs = 0; - ad->albums = makeList(free); - - return ad; -} - -void freeArtistData(ArtistData * ad) { - freeList(ad->albums); -} - -void initTables() { - albumTable = makeList(free); - artistTable = makeList((ListFreeDataFunc *)freeArtistData); -} - -void closeTables() { - freeList(albumTable); - freeList(artistTable); -} - -void addSongToSomeAlbumTable(List * table, Song * song) { - void * songs; - if(!song->tag) return; - if(!song->tag->album || !strlen(song->tag->album)) return; - if(!findInList(table,song->tag->album,&songs)) { - songs = malloc(sizeof(int)); - *((int *)songs) = 0; - insertInList(table,song->tag->album,songs); - } - (*((int *)songs))++; -} - -void addSongToAlbumTable(Song * song) { - addSongToSomeAlbumTable(albumTable,song); -} - -void addSongToArtistTable(Song * song) { - void * artist; - if(!song->tag) return; - if(!song->tag->artist || !strlen(song->tag->artist)) return; - if(!findInList(artistTable,song->tag->artist,&artist)) { - artist = newArtistData(); - insertInList(artistTable,song->tag->artist,artist); - } - ((ArtistData *)artist)->songs++; - addSongToSomeAlbumTable(((ArtistData *)artist)->albums,song); -} - -void addSongToTables(Song * song) { - addSongToAlbumTable(song); - addSongToArtistTable(song); -} - -void removeSongFromSomeAlbumTable(List * table, Song * song) { - void * songs; - - if(!song->tag) return; - if(!song->tag->album || !strlen(song->tag->album)) return; - if(findInList(table,song->tag->album,&songs)) { - (*((int *)songs))--; - if(*((int *)songs)<=0) { - deleteFromList(table,song->tag->album); - } - } -} - -void removeSongFromAlbumTable(Song * song) { - removeSongFromSomeAlbumTable(albumTable,song); -} - -void removeSongFromArtistTable(Song * song) { - void * artist; - - if(!song->tag) return; - if(!song->tag->artist || !strlen(song->tag->artist)) return; - if(findInList(artistTable,song->tag->artist,&artist)) { - removeSongFromSomeAlbumTable(((ArtistData *)artist)->albums, - song); - ((ArtistData*)artist)->songs--; - if(((ArtistData *)artist)->songs<=0) { - deleteFromList(artistTable,song->tag->artist); - } - } -} - -void removeASongFromTables(Song * song) { - removeSongFromAlbumTable(song); - removeSongFromArtistTable(song); -} - -unsigned long numberOfSongs() { - return 0; -} - -unsigned long numberOfArtists() { - return artistTable->numberOfNodes; -} - -unsigned long numberOfAlbums() { - return albumTable->numberOfNodes; -} - -int printAllArtists(FILE * fp) { - ListNode * node = artistTable->firstNode; - - while(node) { - myfprintf(fp,"Artist: %s\n",node->key); - node = node->nextNode; - } - - return 0; -} - -int printAllAlbums(FILE * fp, char * artist) { - if(artist==NULL) { - ListNode * node = albumTable->firstNode; - - while(node) { - myfprintf(fp,"Album: %s\n",node->key); - node = node->nextNode; - } - } - else { - void * ad; - - if(findInList(artistTable,artist,&ad)) { - ListNode * node = ((ArtistData *)ad)->albums->firstNode; - while(node) { - myfprintf(fp,"Album: %s\n",node->key); - node = node->nextNode; - } - } - else { - commandError(fp, ACK_ERROR_NO_EXIST, - "artist \"%s\" not found", artist); - return -1; - } - } - - return 0; -} - -int printAllKeysOfTable(FILE * fp, char * table, char * arg1) { - if(strcmp(table,TABLES_ARTIST)==0) { - if(arg1!=NULL) { - commandError(fp, ACK_ERROR_ARG, - "%s table takes no args", table); - return -1; - } - return printAllArtists(fp); - } - else if(strcmp(table,TABLES_ALBUM)==0) { - return printAllAlbums(fp,arg1); - } - else { - commandError(fp, ACK_ERROR_ARG, "unknown table", table); - return -1; - } -} diff --git a/src/tables.h b/src/tables.h deleted file mode 100644 index e797f638a..000000000 --- a/src/tables.h +++ /dev/null @@ -1,43 +0,0 @@ -/* the Music Player Daemon (MPD) - * (c)2003-2004 by Warren Dukes (shank@mercury.chem.pitt.edu) - * This project's homepage is: http://www.musicpd.org - * - * 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 TABLES_H -#define TABLES_H - -#include "../config.h" - -#include "song.h" - -#include <stdio.h> - -void initTables(); -void closeTables(); - -void addSongToTables(Song * song); - -void removeASongFromTables(Song * song); - -unsigned long numberOfSongs(); - -unsigned long numberOfArtists(); - -unsigned long numberOfAlbums(); - -int printAllKeysOfTable(FILE * fp, char * table, char * arg1); - -#endif @@ -25,6 +25,8 @@ #include "inputStream.h" #include "conf.h" #include "charConv.h" +#include "tagTracker.h" +#include "mpd_types.h" #include <sys/stat.h> #include <stdlib.h> @@ -40,91 +42,120 @@ #include <FLAC/metadata.h> #endif -void printMpdTag(FILE * fp, MpdTag * tag) { - if(tag->artist) myfprintf(fp,"Artist: %s\n",tag->artist); - if(tag->album) myfprintf(fp,"Album: %s\n",tag->album); - if(tag->track) myfprintf(fp,"Track: %s\n",tag->track); - if(tag->title) myfprintf(fp,"Title: %s\n",tag->title); - if(tag->name) myfprintf(fp,"Name: %s\n",tag->name); - if(tag->time>=0) myfprintf(fp,"Time: %i\n",tag->time); -} +char * mpdTagItemKeys[TAG_NUM_OF_ITEM_TYPES] = +{ + "Artist", + "Album", + "Title", + "Track", + "Name", + "Genre", + "Date" +}; + +static mpd_sint8 ignoreTagItems[TAG_NUM_OF_ITEM_TYPES]; + +void initTagConfig() { + int quit = 0; + char * temp; + char * s; + char * c; + ConfigParam * param; + int i; + + memset(ignoreTagItems, 0, TAG_NUM_OF_ITEM_TYPES); + + param = getConfigParam(CONF_METADATA_TO_USE); + + if(!param) return; + + memset(ignoreTagItems, 1, TAG_NUM_OF_ITEM_TYPES); + + if(0 == strcasecmp(param->value, "none")) return; + + temp = c = s = strdup(param->value); + while(!quit) { + if(*s == ',' || *s == '\0') { + if(*s == '\0') quit = 1; + *s = '\0'; + for(i = 0; i < TAG_NUM_OF_ITEM_TYPES; i++) { + if(strcasecmp(c, mpdTagItemKeys[i]) == 0) { + ignoreTagItems[i] = 0; + break; + } + } + if(strlen(c) && i == TAG_NUM_OF_ITEM_TYPES) { + ERROR("error parsing metadata item \"%s\" at " + "line %i\n", c, param->line); + exit(EXIT_FAILURE); + } + s++; + c = s; + } + s++; + } -#define fixUtf8(str) { \ - if(str && !validUtf8String(str)) { \ - char * temp; \ - DEBUG("not valid utf8 in tag: %s\n",str); \ - temp = latin1StrToUtf8Dup(str); \ - free(str); \ - str = temp; \ - } \ + free(temp); } -void validateUtf8Tag(MpdTag * tag) { - fixUtf8(tag->artist); - stripReturnChar(tag->artist); - fixUtf8(tag->album); - stripReturnChar(tag->album); - fixUtf8(tag->track); - stripReturnChar(tag->track); - fixUtf8(tag->title); - stripReturnChar(tag->title); - fixUtf8(tag->name); - stripReturnChar(tag->name); +void printMpdTag(FILE * fp, MpdTag * tag) { + int i; + + if(tag->time>=0) myfprintf(fp,"Time: %i\n",tag->time); + + for(i = 0; i < tag->numOfItems; i++) { + myfprintf(fp, "%s: %s\n", mpdTagItemKeys[tag->items[i].type], + tag->items[i].value); + } } #ifdef HAVE_ID3TAG -char * getID3Info(struct id3_tag * tag, char * id) { +MpdTag * getID3Info(struct id3_tag * tag, char * id, int type, MpdTag * mpdTag) +{ struct id3_frame const * frame; id3_ucs4_t const * ucs4; id3_utf8_t * utf8; union id3_field const * field; unsigned int nstrings; + int i; frame = id3_tag_findframe(tag, id, 0); - if(!frame) return NULL; + if(!frame) return mpdTag; field = &frame->fields[1]; nstrings = id3_field_getnstrings(field); - if(nstrings<1) return NULL; - ucs4 = id3_field_getstrings(field,0); - assert(ucs4); + for(i = 0; i < nstrings; i++) { + ucs4 = id3_field_getstrings(field, i); + assert(ucs4); + + if(type == TAG_ITEM_GENRE) { + ucs4 = id3_genre_name(ucs4); + } + + utf8 = id3_ucs4_utf8duplicate(ucs4); + if(!utf8) continue; - utf8 = id3_ucs4_utf8duplicate(ucs4); - if(!utf8) return NULL; + if( NULL == mpdTag ) mpdTag = newMpdTag(); + addItemToMpdTag(mpdTag, type, utf8); + + free(utf8); + } - return utf8; + return mpdTag; } #endif #ifdef HAVE_ID3TAG MpdTag * parseId3Tag(struct id3_tag * tag) { MpdTag * ret = NULL; - char * str; - str = getID3Info(tag,ID3_FRAME_ARTIST); - if(str) { - if(!ret) ret = newMpdTag(); - ret->artist = str; - } - - str = getID3Info(tag,ID3_FRAME_TITLE); - if(str) { - if(!ret) ret = newMpdTag(); - ret->title = str; - } - - str = getID3Info(tag,ID3_FRAME_ALBUM); - if(str) { - if(!ret) ret = newMpdTag(); - ret->album = str; - } - - str = getID3Info(tag,ID3_FRAME_TRACK); - if(str) { - if(!ret) ret = newMpdTag(); - ret->track = str; - } + ret = getID3Info(tag, ID3_FRAME_ARTIST, TAG_ITEM_ARTIST, ret); + ret = getID3Info(tag, ID3_FRAME_TITLE, TAG_ITEM_TITLE, ret); + ret = getID3Info(tag, ID3_FRAME_ALBUM, TAG_ITEM_ALBUM, ret); + ret = getID3Info(tag, ID3_FRAME_TRACK, TAG_ITEM_TRACK, ret); + ret = getID3Info(tag, ID3_FRAME_YEAR, TAG_ITEM_DATE, ret); + ret = getID3Info(tag, ID3_FRAME_GENRE, TAG_ITEM_GENRE, ret); return ret; } @@ -158,21 +189,61 @@ MpdTag * id3Dup(char * file) { MpdTag * newMpdTag() { MpdTag * ret = malloc(sizeof(MpdTag)); - ret->album = NULL; - ret->artist = NULL; - ret->title = NULL; - ret->track = NULL; - ret->name = NULL; + ret->items = NULL; ret->time = -1; + ret->numOfItems = 0; return ret; } +static void deleteItem(MpdTag * tag, int index) { + tag->numOfItems--; + + assert(index < tag->numOfItems); + + removeTagItemString(tag->items[index].type, tag->items[index].value); + //free(tag->items[index].value); + + if(tag->numOfItems-index > 0) { + memmove(tag->items+index, tag->items+index+1, + tag->numOfItems-index); + } + + if(tag->numOfItems > 0) { + tag->items = realloc(tag->items, + tag->numOfItems*sizeof(MpdTagItem)); + } + else { + free(tag->items); + tag->items = NULL; + } +} + +void clearItemsFromMpdTag(MpdTag * tag, int type) { + int i = 0; + + for(i = 0; i < tag->numOfItems; i++) { + if(tag->items[i].type == type) { + deleteItem(tag, i); + /* decrement since when just deleted this node */ + i--; + } + } +} + void clearMpdTag(MpdTag * tag) { - if(tag->artist) free(tag->artist); - if(tag->album) free(tag->album); - if(tag->title) free(tag->title); - if(tag->name) free(tag->name); - if(tag->track) free(tag->track); + int i; + + for(i = 0; i < tag->numOfItems; i++) { + removeTagItemString(tag->items[i].type, tag->items[i].value); + //free(tag->items[i].value); + } + + if(tag->items) free(tag->items); + tag->items = NULL; + + tag->numOfItems = 0; + + tag->time = -1; } void freeMpdTag(MpdTag * tag) { @@ -182,40 +253,92 @@ void freeMpdTag(MpdTag * tag) { MpdTag * mpdTagDup(MpdTag * tag) { MpdTag * ret = NULL; + int i; - if(tag) { - ret = newMpdTag(); - if(tag->artist) ret->artist = strdup(tag->artist); - if(tag->album) ret->album = strdup(tag->album); - if(tag->title) ret->title = strdup(tag->title); - if(tag->track) ret->track = strdup(tag->track); - if(tag->name) ret->name = strdup(tag->name); - ret->time = tag->time; - } + if(!tag) return NULL; - return ret; -} + ret = newMpdTag(); + ret->time = tag->time; -int mpdTagStringsAreEqual(char * s1, char * s2) { - if(s1 && s2) { - if(strcmp(s1, s2)) return 0; - } - else if(s1 || s2) return 0; + for(i = 0; i < tag->numOfItems; i++) { + addItemToMpdTag(ret, tag->items[i].type, tag->items[i].value); + } - return 1; + return ret; } int mpdTagsAreEqual(MpdTag * tag1, MpdTag * tag2) { + int i; + if(tag1 == NULL && tag2 == NULL) return 1; else if(!tag1 || !tag2) return 0; if(tag1->time != tag2->time) return 0; - if(!mpdTagStringsAreEqual(tag1->artist, tag2->artist)) return 0; - if(!mpdTagStringsAreEqual(tag1->album, tag2->album)) return 0; - if(!mpdTagStringsAreEqual(tag1->track, tag2->track)) return 0; - if(!mpdTagStringsAreEqual(tag1->title, tag2->title)) return 0; - if(!mpdTagStringsAreEqual(tag1->name, tag2->name)) return 0; + if(tag1->numOfItems != tag2->numOfItems) return 0; + + for(i = 0; i < tag1->numOfItems; i++) { + if(tag1->items[i].type != tag2->items[i].type) return 0; + if(strcmp(tag1->items[i].value, tag2->items[i].value)) { + return 0; + } + } return 1; } + +#define fixUtf8(str) { \ + if(str && !validUtf8String(str)) { \ + char * temp; \ + DEBUG("not valid utf8 in tag: %s\n",str); \ + temp = latin1StrToUtf8Dup(str); \ + free(str); \ + str = temp; \ + } \ +} + +inline static void appendToTagItems(MpdTag * tag, int type, char * value, + int len) +{ + int i = tag->numOfItems; + + char * dup; + dup = malloc(len+1); + strncpy(dup, value, len); + dup[len] = '\0'; + + fixUtf8(dup); + + tag->numOfItems++; + tag->items = realloc(tag->items, tag->numOfItems*sizeof(MpdTagItem)); + + tag->items[i].type = type; + tag->items[i].value = getTagItemString(type, dup); + //tag->items[i].value = strdup(dup); + + free(dup); +} + +void addItemToMpdTagWithLen(MpdTag * tag, int itemType, char * value, int len) { + if(ignoreTagItems[itemType]) return; + + if(!value || !len) return; + + appendToTagItems(tag, itemType, value, len); +} + +char * getNextItemFromMpdTag(MpdTag * tag, int itemType, int * last) { + int i = 0; + + if(last && *last >=0) i = *last+1; + + for(i = 0; i < tag->numOfItems; i++) { + if(itemType == tag->items[i].type) { + if(last) *last = i; + return tag->items[i].value; + } + i++; + } + + return NULL; +} @@ -21,6 +21,10 @@ #include "../config.h" +#include "mpd_types.h" + +#include <string.h> + #include <stdio.h> #ifdef HAVE_ID3TAG #ifdef USE_MPD_ID3TAG @@ -30,13 +34,27 @@ #endif #endif +#define TAG_ITEM_ARTIST 0 +#define TAG_ITEM_ALBUM 1 +#define TAG_ITEM_TITLE 2 +#define TAG_ITEM_TRACK 3 +#define TAG_ITEM_NAME 4 +#define TAG_ITEM_GENRE 5 +#define TAG_ITEM_DATE 6 + +#define TAG_NUM_OF_ITEM_TYPES 7 + +extern char * mpdTagItemKeys[]; + +typedef struct _MpdTagItem { + mpd_sint8 type; + char * value; +} MpdTagItem; + typedef struct _MpdTag { - char * artist; - char * album; - char * track; - char * title; - char * name; int time; + MpdTagItem * items; + mpd_uint8 numOfItems; } MpdTag; #ifdef HAVE_ID3TAG @@ -47,16 +65,26 @@ MpdTag * id3Dup(char * file); MpdTag * newMpdTag(); +void initTagConfig(); + +void clearItemsFromMpdTag(MpdTag * tag, int itemType); + void clearMpdTag(MpdTag * tag); void freeMpdTag(MpdTag * tag); +void addItemToMpdTagWithLen(MpdTag * tag, int itemType, char * value, int len); + +#define addItemToMpdTag(tag, itemType, value) \ + addItemToMpdTagWithLen(tag, itemType, value, strlen(value)) + void printMpdTag(FILE * fp, MpdTag * tag); MpdTag * mpdTagDup(MpdTag * tag); -void validateUtf8Tag(MpdTag * tag); - int mpdTagsAreEqual(MpdTag * tag1, MpdTag * tag2); +/* *last shoudl be initialzed to -1 before calling this function */ +char * getNextItemFromMpdTag(MpdTag * tag, int itemType, int * last); + #endif diff --git a/src/tagTracker.c b/src/tagTracker.c new file mode 100644 index 000000000..30ae7641b --- /dev/null +++ b/src/tagTracker.c @@ -0,0 +1,142 @@ +#include "tagTracker.h" + +#include "list.h" +#include "log.h" + +#include <assert.h> + +static List * tagLists[TAG_NUM_OF_ITEM_TYPES] = +{ + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL +}; + +typedef struct tagTrackerItem { + int count; + mpd_sint8 visited; +} TagTrackerItem; + +char * getTagItemString(int type, char * string) { + ListNode * node; + + if(type == TAG_ITEM_TITLE) return strdup(string); + + if(tagLists[type] == NULL) { + tagLists[type] = makeList(free); + } + + if((node = findNodeInList(tagLists[type], string))) { + ((TagTrackerItem *)node->data)->count++; + } + else { + TagTrackerItem * item = malloc(sizeof(TagTrackerItem)); + item->count = 1; + item->visited = 0; + node = insertInList(tagLists[type], string, item); + } + + return node->key; +} + +void removeTagItemString(int type, char * string) { + ListNode * node; + + assert(string); + + if(type == TAG_ITEM_TITLE) { + free(string); + return; + } + + assert(tagLists[type]); + if(tagLists[type] == NULL) return; + + node = findNodeInList(tagLists[type], string); + assert(node); + if(node) { + TagTrackerItem * item = node->data; + item->count--; + if(item->count <= 0) deleteNodeFromList(tagLists[type], node); + } + + if(tagLists[type]->numberOfNodes == 0) { + freeList(tagLists[type]); + tagLists[type] = NULL; + } +} + +int getNumberOfTagItems(int type) { + if(tagLists[type] == NULL) return 0; + + return tagLists[type]->numberOfNodes; +} + +void printMemorySavedByTagTracker() { + int i; + ListNode * node; + size_t sum = 0; + + for(i = 0; i < TAG_NUM_OF_ITEM_TYPES; i++) { + if(!tagLists[i]) continue; + + sum -= sizeof(List); + + node = tagLists[i]->firstNode; + + while(node != NULL) { + sum -= sizeof(ListNode); + sum -= sizeof(TagTrackerItem); + sum -= sizeof(node->key); + sum += (strlen(node->key)+1)*(*((int *)node->data)); + node = node->nextNode; + } + } + + DEBUG("saved memory: %li\n", (long)sum); +} + +void sortTagTrackerInfo() { + int i; + + for(i = 0; i < TAG_NUM_OF_ITEM_TYPES; i++) { + if(!tagLists[i]) continue; + + sortList(tagLists[i]); + } +} + +void resetVisitedFlagsInTagTracker(int type) { + ListNode * node; + + if(!tagLists[type]) return; + + node = tagLists[type]->firstNode; + + while(node) { + ((TagTrackerItem *)node->data)->visited = 0; + node = node->nextNode; + } +} + +int wasVisitedInTagTracker(int type, char * str) { + int ret; + ListNode * node; + TagTrackerItem * item; + + if(!tagLists[type]) return 0; + + node = findNodeInList(tagLists[type], str); + + if(!node) return 0; + + item = node->data; + ret = item->visited; + item->visited = 1; + + return ret; +} diff --git a/src/tagTracker.h b/src/tagTracker.h new file mode 100644 index 000000000..7c9740c64 --- /dev/null +++ b/src/tagTracker.h @@ -0,0 +1,20 @@ +#ifndef TAG_TRACKER_H +#define TAG_TRACKER_H + +#include "tag.h" + +char * getTagItemString(int type, char * string); + +void removeTagItemString(int type, char * string); + +int getNumberOfTagItems(int type); + +void printMemorySavedByTagTracker(); + +void sortTagTrackerInfo(); + +void resetVisitedFlagsInTagTracker(int type); + +int wasVisitedInTagTracker(int type, char * str); + +#endif |