aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--TODO23
-rw-r--r--configure.ac2
-rw-r--r--doc/mpdconf.example4
-rw-r--r--src/Makefile.am6
-rw-r--r--src/audioOutputs/audioOutput_shout.c18
-rw-r--r--src/command.c65
-rw-r--r--src/conf.c1
-rw-r--r--src/conf.h1
-rw-r--r--src/dbUtils.c267
-rw-r--r--src/dbUtils.h42
-rw-r--r--src/decode.c4
-rw-r--r--src/directory.c189
-rw-r--r--src/directory.h33
-rw-r--r--src/inputPlugins/flac_plugin.c122
-rw-r--r--src/inputPlugins/mod_plugin.c4
-rw-r--r--src/inputPlugins/mp3_plugin.c20
-rw-r--r--src/inputPlugins/mp4_plugin.c42
-rw-r--r--src/inputPlugins/ogg_plugin.c28
-rw-r--r--src/list.c32
-rw-r--r--src/list.h4
-rw-r--r--src/main.c10
-rw-r--r--src/metadataChunk.c28
-rw-r--r--src/player.c3
-rw-r--r--src/song.c56
-rw-r--r--src/stats.c7
-rw-r--r--src/tables.c200
-rw-r--r--src/tables.h43
-rw-r--r--src/tag.c305
-rw-r--r--src/tag.h42
-rw-r--r--src/tagTracker.c142
-rw-r--r--src/tagTracker.h20
31 files changed, 1039 insertions, 724 deletions
diff --git a/TODO b/TODO
index bcb0ef076..1999e5b09 100644
--- a/TODO
+++ b/TODO
@@ -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
diff --git a/src/tag.c b/src/tag.c
index b5c71c500..df066dc03 100644
--- a/src/tag.c
+++ b/src/tag.c
@@ -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;
+}
diff --git a/src/tag.h b/src/tag.h
index 06129b767..03eb8c7bf 100644
--- a/src/tag.h
+++ b/src/tag.h
@@ -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