Initial revision 6759
[qball-mpd.git] / src / .svn / text-base / song.c.svn-base
blob9bcb1a0b422d9040f244122150cebaad03fa325a
1 /* the Music Player Daemon (MPD)
2  * Copyright (C) 2003-2007 by Warren Dukes (warren.dukes@gmail.com)
3  * This project's homepage is: http://www.musicpd.org
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17  */
19 #include "song.h"
20 #include "ls.h"
21 #include "directory.h"
22 #include "utils.h"
23 #include "tag.h"
24 #include "log.h"
25 #include "path.h"
26 #include "playlist.h"
27 #include "inputPlugin.h"
28 #include "myfprintf.h"
30 #define SONG_KEY        "key: "
31 #define SONG_MTIME      "mtime: "
33 #include <stdlib.h>
34 #include <string.h>
35 #include <assert.h>
37 Song *newNullSong(void)
39         Song *song = xmalloc(sizeof(Song));
41         song->tag = NULL;
42         song->url = NULL;
43         song->type = SONG_TYPE_FILE;
44         song->parentDir = NULL;
46         return song;
49 Song *newSong(char *url, int type, Directory * parentDir)
51         Song *song = NULL;
53         if (strchr(url, '\n')) {
54                 DEBUG("newSong: '%s' is not a valid uri\n", url);
55                 return NULL;
56         }
58         song = newNullSong();
60         song->url = xstrdup(url);
61         song->type = type;
62         song->parentDir = parentDir;
64         assert(type == SONG_TYPE_URL || parentDir);
66         if (song->type == SONG_TYPE_FILE) {
67                 InputPlugin *plugin;
68                 unsigned int next = 0;
69                 char *song_url = getSongUrl(song);
70                 char *abs_path = rmp2amp(utf8ToFsCharset(song_url));
71                 while (!song->tag && (plugin = isMusic(song_url,
72                                                        &(song->mtime),
73                                                        next++))) {
74                         song->tag = plugin->tagDupFunc(abs_path);
75                 }
76                 if (!song->tag || song->tag->time < 0) {
77                         freeSong(song);
78                         song = NULL;
79                 }
80         }
82         return song;
85 void freeSong(Song * song)
87         deleteASongFromPlaylist(song);
88         freeJustSong(song);
91 void freeJustSong(Song * song)
93         free(song->url);
94         if (song->tag)
95                 freeMpdTag(song->tag);
96         free(song);
97         getSongUrl(NULL);
100 SongList *newSongList(void)
102         return makeList((ListFreeDataFunc *) freeSong, 0);
105 Song *addSongToList(SongList * list, char *url, char *utf8path,
106                     int songType, Directory * parentDirectory)
108         Song *song = NULL;
110         switch (songType) {
111         case SONG_TYPE_FILE:
112                 if (isMusic(utf8path, NULL, 0)) {
113                         song = newSong(url, songType, parentDirectory);
114                 }
115                 break;
116         case SONG_TYPE_URL:
117                 song = newSong(url, songType, parentDirectory);
118                 break;
119         default:
120                 DEBUG("addSongToList: Trying to add an invalid song type\n");
121         }
123         if (song == NULL)
124                 return NULL;
126         insertInList(list, song->url, (void *)song);
128         return song;
131 void freeSongList(SongList * list)
133         freeList(list);
136 void printSongUrl(int fd, Song * song)
138         if (song->parentDir && song->parentDir->path) {
139                 fdprintf(fd, "%s%s/%s\n", SONG_FILE,
140                           getDirectoryPath(song->parentDir), song->url);
141         } else {
142                 fdprintf(fd, "%s%s\n", SONG_FILE, song->url);
143         }
146 int printSongInfo(int fd, Song * song)
148         printSongUrl(fd, song);
150         if (song->tag)
151                 printMpdTag(fd, song->tag);
153         return 0;
156 int printSongInfoFromList(int fd, SongList * list)
158         ListNode *tempNode = list->firstNode;
160         while (tempNode != NULL) {
161                 printSongInfo(fd, (Song *) tempNode->data);
162                 tempNode = tempNode->nextNode;
163         }
165         return 0;
168 void writeSongInfoFromList(FILE * fp, SongList * list)
170         ListNode *tempNode = list->firstNode;
172         fprintf(fp, "%s\n", SONG_BEGIN);
174         while (tempNode != NULL) {
175                 fprintf(fp, "%s%s\n", SONG_KEY, tempNode->key);
176                 fflush(fp);
177                 printSongInfo(fileno(fp), (Song *) tempNode->data);
178                 fprintf(fp, "%s%li\n", SONG_MTIME,
179                           (long)((Song *) tempNode->data)->mtime);
180                 tempNode = tempNode->nextNode;
181         }
183         fprintf(fp, "%s\n", SONG_END);
186 static void insertSongIntoList(SongList * list, ListNode ** nextSongNode,
187                                char *key, Song * song)
189         ListNode *nodeTemp;
190         int cmpRet = 0;
192         while (*nextSongNode
193                && (cmpRet = strcmp(key, (*nextSongNode)->key)) > 0) {
194                 nodeTemp = (*nextSongNode)->nextNode;
195                 deleteNodeFromList(list, *nextSongNode);
196                 *nextSongNode = nodeTemp;
197         }
199         if (!(*nextSongNode)) {
200                 insertInList(list, song->url, (void *)song);
201         } else if (cmpRet == 0) {
202                 Song *tempSong = (Song *) ((*nextSongNode)->data);
203                 if (tempSong->mtime != song->mtime) {
204                         freeMpdTag(tempSong->tag);
205                         tempSong->tag = song->tag;
206                         tempSong->mtime = song->mtime;
207                         song->tag = NULL;
208                 }
209                 freeJustSong(song);
210                 *nextSongNode = (*nextSongNode)->nextNode;
211         } else {
212                 insertInListBeforeNode(list, *nextSongNode, -1, song->url,
213                                        (void *)song);
214         }
217 static int matchesAnMpdTagItemKey(char *buffer, int *itemType)
219         int i;
221         for (i = 0; i < TAG_NUM_OF_ITEM_TYPES; i++) {
222                 if (0 == strncmp(mpdTagItemKeys[i], buffer,
223                                  strlen(mpdTagItemKeys[i]))) {
224                         *itemType = i;
225                         return 1;
226                 }
227         }
229         return 0;
232 void readSongInfoIntoList(FILE * fp, SongList * list, Directory * parentDir)
234         char buffer[MAXPATHLEN + 1024];
235         int bufferSize = MAXPATHLEN + 1024;
236         Song *song = NULL;
237         ListNode *nextSongNode = list->firstNode;
238         ListNode *nodeTemp;
239         int itemType;
241         while (myFgets(buffer, bufferSize, fp) && 0 != strcmp(SONG_END, buffer)) {
242                 if (0 == strncmp(SONG_KEY, buffer, strlen(SONG_KEY))) {
243                         if (song) {
244                                 insertSongIntoList(list, &nextSongNode,
245                                                    song->url, song);
246                                 song = NULL;
247                         }
249                         song = newNullSong();
250                         song->url = xstrdup(buffer + strlen(SONG_KEY));
251                         song->type = SONG_TYPE_FILE;
252                         song->parentDir = parentDir;
253                 } else if (0 == strncmp(SONG_FILE, buffer, strlen(SONG_FILE))) {
254                         if (!song)
255                                 FATAL("Problems reading song info\n");
256                         /* we don't need this info anymore
257                            song->url = xstrdup(&(buffer[strlen(SONG_FILE)]));
258                          */
259                 } else if (matchesAnMpdTagItemKey(buffer, &itemType)) {
260                         if (!song->tag)
261                                 song->tag = newMpdTag();
262                         addItemToMpdTag(song->tag, itemType,
263                                         &(buffer
264                                           [strlen(mpdTagItemKeys[itemType]) +
265                                            2]));
266                 } else if (0 == strncmp(SONG_TIME, buffer, strlen(SONG_TIME))) {
267                         if (!song->tag)
268                                 song->tag = newMpdTag();
269                         song->tag->time = atoi(&(buffer[strlen(SONG_TIME)]));
270                 } else if (0 == strncmp(SONG_MTIME, buffer, strlen(SONG_MTIME))) {
271                         song->mtime = atoi(&(buffer[strlen(SONG_MTIME)]));
272                 }
273                 /* ignore empty lines (starting with '\0') */
274                 else if (*buffer)
275                         FATAL("songinfo: unknown line in db: %s\n", buffer);
276         }
278         if (song) {
279                 insertSongIntoList(list, &nextSongNode, song->url, song);
280                 song = NULL;
281         }
283         while (nextSongNode) {
284                 nodeTemp = nextSongNode->nextNode;
285                 deleteNodeFromList(list, nextSongNode);
286                 nextSongNode = nodeTemp;
287         }
290 int updateSongInfo(Song * song)
292         if (song->type == SONG_TYPE_FILE) {
293                 InputPlugin *plugin;
294                 unsigned int next = 0;
295                 char *song_url = getSongUrl(song);
296                 char *abs_path = rmp2amp(song_url);
298                 if (song->tag)
299                         freeMpdTag(song->tag);
301                 song->tag = NULL;
303                 while (!song->tag && (plugin = isMusic(song_url,
304                                                        &(song->mtime),
305                                                        next++))) {
306                         song->tag = plugin->tagDupFunc(abs_path);
307                 }
308                 if (!song->tag || song->tag->time < 0)
309                         return -1;
310         }
312         return 0;
315 /* pass song = NULL to reset, we do this freeJustSong(), so that if
316  *      we free and recreate this memory we make sure to print it correctly*/
317 char *getSongUrl(Song * song)
319         static char *buffer;
320         static int bufferSize;
321         static Song *lastSong;
322         int slen;
323         int dlen;
324         int size;
326         if (!song) {
327                 lastSong = song;
328                 return NULL;
329         }
331         if (!song->parentDir || !song->parentDir->path)
332                 return song->url;
334         /* be careful with this! */
335         if (song == lastSong)
336                 return buffer;
338         slen = strlen(song->url);
339         dlen = strlen(getDirectoryPath(song->parentDir));
341         size = slen + dlen + 2;
343         if (size > bufferSize) {
344                 buffer = xrealloc(buffer, size);
345                 bufferSize = size;
346         }
348         strcpy(buffer, getDirectoryPath(song->parentDir));
349         buffer[dlen] = '/';
350         strcpy(buffer + dlen + 1, song->url);
352         return buffer;