Update to 6762
[qball-mpd.git] / src / playlist.c
blob537779a7c7a9aa4b42b294fcd885cab6787a8f10
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
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.
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
19 #include "playlist.h"
20 #include "player.h"
21 #include "command.h"
22 #include "ls.h"
23 #include "tag.h"
24 #include "conf.h"
25 #include "directory.h"
26 #include "log.h"
27 #include "path.h"
28 #include "utils.h"
29 #include "sig_handlers.h"
30 #include "state_file.h"
31 #include "storedPlaylist.h"
33 #include <string.h>
34 #include <stdlib.h>
35 #include <sys/stat.h>
36 #include <sys/param.h>
37 #include <errno.h>
38 #include <unistd.h>
39 #include <time.h>
41 #define PLAYLIST_STATE_STOP 0
42 #define PLAYLIST_STATE_PLAY 1
44 #define PLAYLIST_PREV_UNLESS_ELAPSED 10
46 #define PLAYLIST_STATE_FILE_STATE "state: "
47 #define PLAYLIST_STATE_FILE_RANDOM "random: "
48 #define PLAYLIST_STATE_FILE_REPEAT "repeat: "
49 #define PLAYLIST_STATE_FILE_CURRENT "current: "
50 #define PLAYLIST_STATE_FILE_TIME "time: "
51 #define PLAYLIST_STATE_FILE_CROSSFADE "crossfade: "
52 #define PLAYLIST_STATE_FILE_PLAYLIST_BEGIN "playlist_begin"
53 #define PLAYLIST_STATE_FILE_PLAYLIST_END "playlist_end"
55 #define PLAYLIST_STATE_FILE_STATE_PLAY "play"
56 #define PLAYLIST_STATE_FILE_STATE_PAUSE "pause"
57 #define PLAYLIST_STATE_FILE_STATE_STOP "stop"
59 #define PLAYLIST_BUFFER_SIZE 2*MAXPATHLEN
61 #define PLAYLIST_HASH_MULT 4
63 #define DEFAULT_PLAYLIST_MAX_LENGTH (1024*16)
64 #define DEFAULT_PLAYLIST_SAVE_ABSOLUTE_PATHS 0
66 static Playlist playlist;
67 static int playlist_state = PLAYLIST_STATE_STOP;
68 static int playlist_max_length = DEFAULT_PLAYLIST_MAX_LENGTH;
69 static int playlist_stopOnError;
70 static int playlist_errorCount;
71 static int playlist_queueError;
72 static int playlist_noGoToNext;
74 int playlist_saveAbsolutePaths = DEFAULT_PLAYLIST_SAVE_ABSOLUTE_PATHS;
76 static List *playlistQueue;
78 static void swapOrder(int a, int b);
79 static int playPlaylistOrderNumber(int fd, int orderNum);
80 static void randomizeOrder(int start, int end);
81 static void clearPlayerQueue(void);
83 static void incrPlaylistVersion(void)
85 static unsigned long max = ((mpd_uint32) 1 << 31) - 1;
86 playlist.version++;
87 if (playlist.version >= max) {
88 int i;
90 for (i = 0; i < playlist.length; i++) {
91 playlist.songMod[i] = 0;
94 playlist.version = 1;
98 void playlistVersionChange(void)
100 int i = 0;
102 for (i = 0; i < playlist.length; i++) {
103 playlist.songMod[i] = playlist.version;
106 incrPlaylistVersion();
109 static void incrPlaylistQueueVersion(void)
111 static unsigned long max = ((mpd_uint32) 1 << 31) - 1;
112 playlist.queueversion++;
113 if (playlist.queueversion >= max)
114 playlist.queueversion = 1;
117 static void incrPlaylistCurrent(void)
119 if (playlist.current < 0)
120 return;
122 if (playlist.current >= playlist.length - 1) {
123 if (playlist.repeat)
124 playlist.current = 0;
125 else
126 playlist.current = -1;
127 } else
128 playlist.current++;
131 void initPlaylist(void)
133 char *test;
134 int i;
135 ConfigParam *param;
137 playlist.length = 0;
138 playlist.repeat = 0;
139 playlist.version = 1;
140 playlist.queueversion = 1;
141 playlist.random = 0;
142 playlist.queued = -1;
143 playlist.current = -1;
145 param = getConfigParam(CONF_MAX_PLAYLIST_LENGTH);
147 if (param) {
148 playlist_max_length = strtol(param->value, &test, 10);
149 if (*test != '\0') {
150 FATAL("max playlist length \"%s\" is not an integer, "
151 "line %i\n", param->value, param->line);
155 playlist_saveAbsolutePaths = getBoolConfigParam(CONF_SAVE_ABSOLUTE_PATHS);
156 if (playlist_saveAbsolutePaths == -1)
157 playlist_saveAbsolutePaths = DEFAULT_PLAYLIST_SAVE_ABSOLUTE_PATHS;
158 else if (playlist_saveAbsolutePaths < 0)
159 exit(EXIT_FAILURE);
161 playlist.songs = xmalloc(sizeof(Song *) * playlist_max_length);
162 playlist.songMod = xmalloc(sizeof(mpd_uint32) * playlist_max_length);
163 playlist.order = xmalloc(sizeof(int) * playlist_max_length);
164 playlist.idToPosition = xmalloc(sizeof(int) * playlist_max_length *
165 PLAYLIST_HASH_MULT);
166 playlist.positionToId = xmalloc(sizeof(int) * playlist_max_length);
168 memset(playlist.songs, 0, sizeof(char *) * playlist_max_length);
170 srandom(time(NULL));
172 for (i = 0; i < playlist_max_length * PLAYLIST_HASH_MULT; i++) {
173 playlist.idToPosition[i] = -1;
176 playlistQueue = makeList(DEFAULT_FREE_DATA_FUNC, 0);
179 static int getNextId(void)
181 static int cur = -1;
183 do {
184 cur++;
185 if (cur >= playlist_max_length * PLAYLIST_HASH_MULT) {
186 cur = 0;
188 } while (playlist.idToPosition[cur] != -1);
190 return cur;
193 void finishPlaylist(void)
195 int i;
196 for (i = 0; i < playlist.length; i++) {
197 if (playlist.songs[i]->type == SONG_TYPE_URL) {
198 freeJustSong(playlist.songs[i]);
202 playlist.length = 0;
204 free(playlist.songs);
205 playlist.songs = NULL;
206 free(playlist.songMod);
207 playlist.songMod = NULL;
208 free(playlist.order);
209 playlist.order = NULL;
210 free(playlist.idToPosition);
211 playlist.idToPosition = NULL;
212 free(playlist.positionToId);
213 playlist.positionToId = NULL;
216 int clearPlaylist(int fd)
218 int i;
220 if (stopPlaylist(fd) < 0)
221 return -1;
222 clearPlaylistQueue();
224 for (i = 0; i < playlist.length; i++) {
225 if (playlist.songs[i]->type == SONG_TYPE_URL) {
226 freeJustSong(playlist.songs[i]);
228 playlist.idToPosition[playlist.positionToId[i]] = -1;
229 playlist.songs[i] = NULL;
231 playlist.length = 0;
232 playlist.current = -1;
234 incrPlaylistVersion();
236 return 0;
239 int clearStoredPlaylist(int fd, char *utf8file)
241 removeAllFromStoredPlaylistByPath(fd, utf8file);
242 return 0;
245 int showPlaylist(int fd)
247 int i;
249 for (i = 0; i < playlist.length; i++) {
250 fdprintf(fd, "%i:%s\n", i, getSongUrl(playlist.songs[i]));
253 return 0;
256 void savePlaylistState(FILE *fp)
258 fprintf(fp, "%s", PLAYLIST_STATE_FILE_STATE);
259 switch (playlist_state) {
260 case PLAYLIST_STATE_PLAY:
261 switch (getPlayerState()) {
262 case PLAYER_STATE_PAUSE:
263 fprintf(fp, "%s\n", PLAYLIST_STATE_FILE_STATE_PAUSE);
264 break;
265 default:
266 fprintf(fp, "%s\n", PLAYLIST_STATE_FILE_STATE_PLAY);
268 fprintf(fp, "%s%i\n", PLAYLIST_STATE_FILE_CURRENT,
269 playlist.order[playlist.current]);
270 fprintf(fp, "%s%i\n", PLAYLIST_STATE_FILE_TIME,
271 getPlayerElapsedTime());
272 break;
273 default:
274 fprintf(fp, "%s\n", PLAYLIST_STATE_FILE_STATE_STOP);
275 break;
277 fprintf(fp, "%s%i\n", PLAYLIST_STATE_FILE_RANDOM, playlist.random);
278 fprintf(fp, "%s%i\n", PLAYLIST_STATE_FILE_REPEAT, playlist.repeat);
279 fprintf(fp, "%s%i\n", PLAYLIST_STATE_FILE_CROSSFADE,
280 (int)(getPlayerCrossFade()));
281 fprintf(fp, "%s\n", PLAYLIST_STATE_FILE_PLAYLIST_BEGIN);
282 fflush(fp);
283 showPlaylist(fileno(fp));
284 fprintf(fp, "%s\n", PLAYLIST_STATE_FILE_PLAYLIST_END);
287 static void loadPlaylistFromStateFile(FILE *fp, char *buffer,
288 int state, int current, int time)
290 char *temp;
291 int song;
293 if (!myFgets(buffer, PLAYLIST_BUFFER_SIZE, fp))
294 state_file_fatal();
295 while (strcmp(buffer, PLAYLIST_STATE_FILE_PLAYLIST_END)) {
296 song = atoi(strtok(buffer, ":"));
297 if (!(temp = strtok(NULL, "")))
298 state_file_fatal();
299 if (!addToPlaylist(STDERR_FILENO, temp, 0) && current == song) {
300 if (state != PLAYER_STATE_STOP) {
301 playPlaylist(STDERR_FILENO,
302 playlist.length - 1, 0);
304 if (state == PLAYER_STATE_PAUSE) {
305 playerPause(STDERR_FILENO);
307 if (state != PLAYER_STATE_STOP) {
308 seekSongInPlaylist(STDERR_FILENO,
309 playlist.length - 1, time);
312 if (!myFgets(buffer, PLAYLIST_BUFFER_SIZE, fp))
313 state_file_fatal();
317 void readPlaylistState(FILE *fp)
319 int current = -1;
320 int time = 0;
321 int state = PLAYER_STATE_STOP;
322 char buffer[PLAYLIST_BUFFER_SIZE];
324 while (myFgets(buffer, PLAYLIST_BUFFER_SIZE, fp)) {
325 if (strncmp(buffer, PLAYLIST_STATE_FILE_STATE,
326 strlen(PLAYLIST_STATE_FILE_STATE)) == 0) {
327 if (strcmp(&(buffer[strlen(PLAYLIST_STATE_FILE_STATE)]),
328 PLAYLIST_STATE_FILE_STATE_PLAY) == 0) {
329 state = PLAYER_STATE_PLAY;
330 } else
331 if (strcmp
332 (&(buffer[strlen(PLAYLIST_STATE_FILE_STATE)]),
333 PLAYLIST_STATE_FILE_STATE_PAUSE)
334 == 0) {
335 state = PLAYER_STATE_PAUSE;
337 } else if (strncmp(buffer, PLAYLIST_STATE_FILE_TIME,
338 strlen(PLAYLIST_STATE_FILE_TIME)) == 0) {
339 time =
340 atoi(&(buffer[strlen(PLAYLIST_STATE_FILE_TIME)]));
341 } else
342 if (strncmp
343 (buffer, PLAYLIST_STATE_FILE_REPEAT,
344 strlen(PLAYLIST_STATE_FILE_REPEAT)) == 0) {
345 if (strcmp
346 (&(buffer[strlen(PLAYLIST_STATE_FILE_REPEAT)]),
347 "1") == 0) {
348 setPlaylistRepeatStatus(STDERR_FILENO, 1);
349 } else
350 setPlaylistRepeatStatus(STDERR_FILENO, 0);
351 } else
352 if (strncmp
353 (buffer, PLAYLIST_STATE_FILE_CROSSFADE,
354 strlen(PLAYLIST_STATE_FILE_CROSSFADE)) == 0) {
355 setPlayerCrossFade(atoi
357 (buffer
358 [strlen
359 (PLAYLIST_STATE_FILE_CROSSFADE)])));
360 } else
361 if (strncmp
362 (buffer, PLAYLIST_STATE_FILE_RANDOM,
363 strlen(PLAYLIST_STATE_FILE_RANDOM)) == 0) {
364 if (strcmp
366 (buffer
367 [strlen(PLAYLIST_STATE_FILE_RANDOM)]),
368 "1") == 0) {
369 setPlaylistRandomStatus(STDERR_FILENO, 1);
370 } else
371 setPlaylistRandomStatus(STDERR_FILENO, 0);
372 } else if (strncmp(buffer, PLAYLIST_STATE_FILE_CURRENT,
373 strlen(PLAYLIST_STATE_FILE_CURRENT))
374 == 0) {
375 if (strlen(buffer) ==
376 strlen(PLAYLIST_STATE_FILE_CURRENT))
377 state_file_fatal();
378 current = atoi(&(buffer
379 [strlen
380 (PLAYLIST_STATE_FILE_CURRENT)]));
381 } else
382 if (strncmp
383 (buffer, PLAYLIST_STATE_FILE_PLAYLIST_BEGIN,
384 strlen(PLAYLIST_STATE_FILE_PLAYLIST_BEGIN)
385 ) == 0) {
386 if (state == PLAYER_STATE_STOP)
387 current = -1;
388 loadPlaylistFromStateFile(fp, buffer, state,
389 current, time);
394 static void printPlaylistSongInfo(int fd, int song)
396 printSongInfo(fd, playlist.songs[song]);
397 fdprintf(fd, "Pos: %i\nId: %i\n", song, playlist.positionToId[song]);
400 int playlistChanges(int fd, mpd_uint32 version)
402 int i;
404 for (i = 0; i < playlist.length; i++) {
405 if (version > playlist.version ||
406 playlist.songMod[i] >= version ||
407 playlist.songMod[i] == 0) {
408 printPlaylistSongInfo(fd, i);
412 return 0;
415 int playlistChangesPosId(int fd, mpd_uint32 version)
417 int i;
419 for (i = 0; i < playlist.length; i++) {
420 if (version > playlist.version ||
421 playlist.songMod[i] >= version ||
422 playlist.songMod[i] == 0) {
423 fdprintf(fd, "cpos: %i\nId: %i\n",
424 i, playlist.positionToId[i]);
428 return 0;
431 int playlistInfo(int fd, int song)
433 int i;
434 int begin = 0;
435 int end = playlist.length;
437 if (song >= 0) {
438 begin = song;
439 end = song + 1;
441 if (song >= playlist.length) {
442 commandError(fd, ACK_ERROR_NO_EXIST,
443 "song doesn't exist: \"%i\"", song);
444 return -1;
447 for (i = begin; i < end; i++)
448 printPlaylistSongInfo(fd, i);
450 return 0;
453 # define checkSongId(id) { \
454 if(id < 0 || id >= PLAYLIST_HASH_MULT*playlist_max_length || \
455 playlist.idToPosition[id] == -1 ) \
457 commandError(fd, ACK_ERROR_NO_EXIST, \
458 "song id doesn't exist: \"%i\"", id); \
459 return -1; \
463 int playlistId(int fd, int id)
465 int i;
466 int begin = 0;
467 int end = playlist.length;
469 if (id >= 0) {
470 checkSongId(id);
471 begin = playlist.idToPosition[id];
472 end = begin + 1;
475 for (i = begin; i < end; i++)
476 printPlaylistSongInfo(fd, i);
478 return 0;
481 static void swapSongs(int song1, int song2)
483 Song *sTemp;
484 int iTemp;
486 sTemp = playlist.songs[song1];
487 playlist.songs[song1] = playlist.songs[song2];
488 playlist.songs[song2] = sTemp;
490 playlist.songMod[song1] = playlist.version;
491 playlist.songMod[song2] = playlist.version;
493 playlist.idToPosition[playlist.positionToId[song1]] = song2;
494 playlist.idToPosition[playlist.positionToId[song2]] = song1;
496 iTemp = playlist.positionToId[song1];
497 playlist.positionToId[song1] = playlist.positionToId[song2];
498 playlist.positionToId[song2] = iTemp;
501 static void queueNextSongInPlaylist(void)
503 if (playlistQueue->numberOfNodes != 0) {
504 int i;
505 /* we need to find where in order[] is first song from queue */
506 for (i=0;i < playlist.length; i++)
507 if (playlist.order[i] == playlist.
508 idToPosition[*(int *)playlistQueue->
509 firstNode->data])
510 break;
511 clearPlayerQueue();
512 playlist.queued = i;
513 DEBUG("playlist: queue song %i:\"%s\"\n",
514 playlist.queued,
515 getSongUrl(playlist.
516 songs[playlist.order[playlist.queued]]));
518 if (queueSong(playlist.songs[playlist.order[playlist.queued]]) <
519 0) {
520 playlist.queued = -1;
521 playlist_queueError = 1;
523 } else if (playlist.current < playlist.length - 1) {
524 clearPlayerQueue();
525 playlist.queued = playlist.current + 1;
526 DEBUG("playlist: queue song %i:\"%s\"\n",
527 playlist.queued,
528 getSongUrl(playlist.
529 songs[playlist.order[playlist.queued]]));
530 if (queueSong(playlist.songs[playlist.order[playlist.queued]]) <
531 0) {
532 playlist.queued = -1;
533 playlist_queueError = 1;
535 } else if (playlist.length && playlist.repeat) {
536 if (playlist.length > 1 && playlist.random) {
537 randomizeOrder(0, playlist.length - 1);
539 clearPlayerQueue();
540 playlist.queued = 0;
541 DEBUG("playlist: queue song %i:\"%s\"\n",
542 playlist.queued,
543 getSongUrl(playlist.
544 songs[playlist.order[playlist.queued]]));
545 if (queueSong(playlist.songs[playlist.order[playlist.queued]]) <
546 0) {
547 playlist.queued = -1;
548 playlist_queueError = 1;
553 static void syncPlaylistWithQueue(int queue)
555 if (queue && getPlayerQueueState() == PLAYER_QUEUE_BLANK) {
556 queueNextSongInPlaylist();
557 } else if (getPlayerQueueState() == PLAYER_QUEUE_DECODE) {
558 if (playlist.queued != -1)
559 setQueueState(PLAYER_QUEUE_PLAY);
560 else
561 setQueueState(PLAYER_QUEUE_STOP);
562 } else if (getPlayerQueueState() == PLAYER_QUEUE_EMPTY) {
563 setQueueState(PLAYER_QUEUE_BLANK);
564 if (playlist.queued >= 0) {
565 DEBUG("playlist: now playing queued song\n");
566 playlist.current = playlist.queued;
567 if (playlistQueue->numberOfNodes > 0) {
568 deleteFromPlaylistQueueInternal(0);
571 playlist.queued = -1;
572 if (queue)
573 queueNextSongInPlaylist();
577 static void lockPlaylistInteraction(void)
579 if (getPlayerQueueState() == PLAYER_QUEUE_PLAY ||
580 getPlayerQueueState() == PLAYER_QUEUE_FULL) {
581 playerQueueLock();
582 syncPlaylistWithQueue(0);
586 static void unlockPlaylistInteraction(void)
588 playerQueueUnlock();
591 static void clearPlayerQueue(void)
593 playlist.queued = -1;
594 switch (getPlayerQueueState()) {
595 case PLAYER_QUEUE_FULL:
596 DEBUG("playlist: dequeue song\n");
597 setQueueState(PLAYER_QUEUE_BLANK);
598 break;
599 case PLAYER_QUEUE_PLAY:
600 DEBUG("playlist: stop decoding queued song\n");
601 setQueueState(PLAYER_QUEUE_STOP);
602 break;
606 int addToPlaylist(int fd, char *url, int printId)
608 Song *song;
610 DEBUG("add to playlist: %s\n", url);
612 if ((song = getSongFromDB(url))) {
613 } else if (!(isValidRemoteUtf8Url(url) &&
614 (song = newSong(url, SONG_TYPE_URL, NULL)))) {
615 commandError(fd, ACK_ERROR_NO_EXIST,
616 "\"%s\" is not in the music db or is "
617 "not a valid url", url);
618 return -1;
621 return addSongToPlaylist(fd, song, printId);
624 int addToStoredPlaylist(int fd, char *url, char *utf8file)
626 Song *song;
628 DEBUG("add to stored playlist: %s\n", url);
630 song = getSongFromDB(url);
631 if (song) {
632 appendSongToStoredPlaylistByPath(fd, utf8file, song);
633 return 0;
636 if (!isValidRemoteUtf8Url(url))
637 goto fail;
639 song = newSong(url, SONG_TYPE_URL, NULL);
640 if (song) {
641 appendSongToStoredPlaylistByPath(fd, utf8file, song);
642 freeJustSong(song);
643 return 0;
646 fail:
647 commandError(fd, ACK_ERROR_NO_EXIST, "\"%s\" is not in the music db"
648 "or is not a valid url", url);
649 return -1;
652 int addSongToPlaylist(int fd, Song * song, int printId)
654 int id;
656 if (playlist.length == playlist_max_length) {
657 commandError(fd, ACK_ERROR_PLAYLIST_MAX,
658 "playlist is at the max size");
659 return -1;
662 if (playlist_state == PLAYLIST_STATE_PLAY) {
663 if (playlist.queued >= 0
664 && playlist.current == playlist.length - 1) {
665 lockPlaylistInteraction();
666 clearPlayerQueue();
667 unlockPlaylistInteraction();
671 id = getNextId();
673 playlist.songs[playlist.length] = song;
674 playlist.songMod[playlist.length] = playlist.version;
675 playlist.order[playlist.length] = playlist.length;
676 playlist.positionToId[playlist.length] = id;
677 playlist.idToPosition[playlist.positionToId[playlist.length]] =
678 playlist.length;
679 playlist.length++;
681 if (playlist.random) {
682 int swap;
683 int start;
684 /*if(playlist_state==PLAYLIST_STATE_STOP) start = 0;
685 else */ if (playlist.queued >= 0)
686 start = playlist.queued + 1;
687 else
688 start = playlist.current + 1;
689 if (start < playlist.length) {
690 swap = random() % (playlist.length - start);
691 swap += start;
692 swapOrder(playlist.length - 1, swap);
696 incrPlaylistVersion();
698 if (printId)
699 fdprintf(fd, "Id: %i\n", id);
701 return 0;
704 int swapSongsInPlaylist(int fd, int song1, int song2)
706 int queuedSong = -1;
707 int currentSong = -1;
709 if (song1 < 0 || song1 >= playlist.length) {
710 commandError(fd, ACK_ERROR_NO_EXIST,
711 "song doesn't exist: \"%i\"", song1);
712 return -1;
714 if (song2 < 0 || song2 >= playlist.length) {
715 commandError(fd, ACK_ERROR_NO_EXIST,
716 "song doesn't exist: \"%i\"", song2);
717 return -1;
720 if (playlist_state == PLAYLIST_STATE_PLAY) {
721 if (playlist.queued >= 0) {
722 queuedSong = playlist.order[playlist.queued];
724 currentSong = playlist.order[playlist.current];
726 if (queuedSong == song1 || queuedSong == song2
727 || currentSong == song1 || currentSong == song2) {
728 lockPlaylistInteraction();
729 clearPlayerQueue();
730 unlockPlaylistInteraction();
734 swapSongs(song1, song2);
735 if (playlist.random) {
736 int i;
737 int k;
738 int j = -1;
739 for (i = 0; playlist.order[i] != song1; i++) {
740 if (playlist.order[i] == song2)
741 j = i;
743 k = i;
744 for (; j == -1; i++)
745 if (playlist.order[i] == song2)
746 j = i;
747 swapOrder(k, j);
748 } else {
749 if (playlist.current == song1)
750 playlist.current = song2;
751 else if (playlist.current == song2)
752 playlist.current = song1;
755 incrPlaylistVersion();
757 return 0;
760 int swapSongsInPlaylistById(int fd, int id1, int id2)
762 checkSongId(id1);
763 checkSongId(id2);
765 return swapSongsInPlaylist(fd, playlist.idToPosition[id1],
766 playlist.idToPosition[id2]);
769 #define moveSongFromTo(from, to) { \
770 playlist.idToPosition[playlist.positionToId[from]] = to; \
771 playlist.positionToId[to] = playlist.positionToId[from]; \
772 playlist.songs[to] = playlist.songs[from]; \
773 playlist.songMod[to] = playlist.version; \
776 int deleteFromPlaylist(int fd, int song)
778 int i;
779 int songOrder;
780 ListNode *qItem;
782 if (song < 0 || song >= playlist.length) {
783 commandError(fd, ACK_ERROR_NO_EXIST,
784 "song doesn't exist: \"%i\"", song);
785 return -1;
788 /* we need to clear song from queue */
789 i = 0;
790 qItem = playlistQueue->firstNode;
791 while (qItem) {
792 if (playlist.idToPosition[*(int *)qItem->data] ==
793 song) {
795 qItem = qItem->nextNode;
796 deleteFromPlaylistQueueInternal(i);
797 /* can be queued multiple times */
798 continue;
800 i++;
801 qItem = qItem->nextNode;
804 if (playlist_state == PLAYLIST_STATE_PLAY) {
805 if (playlist.queued >= 0
806 && (playlist.order[playlist.queued] == song
807 || playlist.order[playlist.current] == song)) {
808 lockPlaylistInteraction();
809 clearPlayerQueue();
810 unlockPlaylistInteraction();
814 if (playlist.songs[song]->type == SONG_TYPE_URL) {
815 freeJustSong(playlist.songs[song]);
818 playlist.idToPosition[playlist.positionToId[song]] = -1;
820 /* delete song from songs array */
821 for (i = song; i < playlist.length - 1; i++) {
822 moveSongFromTo(i + 1, i);
824 /* now find it in the order array */
825 for (i = 0; i < playlist.length - 1; i++) {
826 if (playlist.order[i] == song)
827 break;
829 songOrder = i;
830 /* delete the entry from the order array */
831 for (; i < playlist.length - 1; i++)
832 playlist.order[i] = playlist.order[i + 1];
833 /* readjust values in the order array */
834 for (i = 0; i < playlist.length - 1; i++) {
835 if (playlist.order[i] > song)
836 playlist.order[i]--;
838 /* now take care of other misc stuff */
839 playlist.songs[playlist.length - 1] = NULL;
840 playlist.length--;
842 incrPlaylistVersion();
844 if (playlist_state != PLAYLIST_STATE_STOP
845 && playlist.current == songOrder) {
846 /*if(playlist.current>=playlist.length) return playerStop(fd);
847 else return playPlaylistOrderNumber(fd,playlist.current); */
848 playerWait(STDERR_FILENO);
849 playlist_noGoToNext = 1;
852 if (playlist.current > songOrder) {
853 playlist.current--;
854 } else if (playlist.current >= playlist.length) {
855 incrPlaylistCurrent();
858 if (playlist.queued > songOrder) {
859 playlist.queued--;
862 return 0;
865 int deleteFromPlaylistById(int fd, int id)
867 checkSongId(id);
869 return deleteFromPlaylist(fd, playlist.idToPosition[id]);
872 void deleteASongFromPlaylist(Song * song)
874 int i;
876 if (NULL == playlist.songs)
877 return;
879 for (i = 0; i < playlist.length; i++) {
880 if (song == playlist.songs[i]) {
881 deleteFromPlaylist(STDERR_FILENO, i);
886 int stopPlaylist(int fd)
888 DEBUG("playlist: stop\n");
889 if (playerWait(fd) < 0)
890 return -1;
891 playlist.queued = -1;
892 playlist_state = PLAYLIST_STATE_STOP;
893 playlist_noGoToNext = 0;
894 if (playlist.random)
895 randomizeOrder(0, playlist.length - 1);
896 return 0;
899 static int playPlaylistOrderNumber(int fd, int orderNum)
901 if (playerStop(fd) < 0)
902 return -1;
904 playlist_state = PLAYLIST_STATE_PLAY;
905 playlist_noGoToNext = 0;
906 playlist.queued = -1;
907 playlist_queueError = 0;
909 DEBUG("playlist: play %i:\"%s\"\n", orderNum,
910 getSongUrl(playlist.songs[playlist.order[orderNum]]));
912 if (playerPlay(fd, (playlist.songs[playlist.order[orderNum]])) < 0) {
913 stopPlaylist(fd);
914 return -1;
917 playlist.current = orderNum;
919 /* are we playing from queue ? */
920 if (playlistQueue->numberOfNodes > 0 &&
921 playlist.idToPosition[*(int *)playlistQueue->
922 firstNode->data] == playlist.order[orderNum]) {
923 deleteFromPlaylistQueueInternal(0);
924 queueNextSongInPlaylist();
927 return 0;
930 int playNextPlaylistQueue(int fd, int stopOnError)
932 int ret;
933 if (playlistQueue->numberOfNodes == 0)
934 return -1;
936 ret = playPlaylistById(fd, *(int *)playlistQueue->firstNode->data,
937 stopOnError);
938 return ret;
941 int playPlaylist(int fd, int song, int stopOnError)
943 int i = song;
945 clearPlayerError();
947 if (song == -1) {
948 if (playlist.length == 0)
949 return 0;
951 if (playlist_state == PLAYLIST_STATE_PLAY) {
952 return playerSetPause(fd, 0);
955 if (playlist_state != PLAYLIST_STATE_STOP &&
956 playNextPlaylistQueue(fd, stopOnError) == 0) {
957 return 0;
960 if (playlist.current >= 0 && playlist.current < playlist.length) {
961 i = playlist.current;
962 } else {
963 i = 0;
965 } else if (song < 0 || song >= playlist.length) {
966 commandError(fd, ACK_ERROR_NO_EXIST,
967 "song doesn't exist: \"%i\"", song);
968 return -1;
971 if (playlist.random) {
972 if (song == -1 && playlist_state == PLAYLIST_STATE_PLAY) {
973 randomizeOrder(0, playlist.length - 1);
974 } else {
975 if (song >= 0)
976 for (i = 0; song != playlist.order[i]; i++) ;
977 if (playlist_state == PLAYLIST_STATE_STOP) {
978 playlist.current = 0;
980 swapOrder(i, playlist.current);
981 i = playlist.current;
985 playlist_stopOnError = stopOnError;
986 playlist_errorCount = 0;
988 return playPlaylistOrderNumber(fd, i);
991 int playPlaylistById(int fd, int id, int stopOnError)
993 if (id == -1) {
994 return playPlaylist(fd, id, stopOnError);
997 checkSongId(id);
999 return playPlaylist(fd, playlist.idToPosition[id], stopOnError);
1002 static void syncCurrentPlayerDecodeMetadata(void)
1004 Song *songPlayer = playerCurrentDecodeSong();
1005 Song *song;
1006 int songNum;
1008 if (!songPlayer)
1009 return;
1011 if (playlist_state != PLAYLIST_STATE_PLAY)
1012 return;
1014 songNum = playlist.order[playlist.current];
1015 song = playlist.songs[songNum];
1017 if (song->type == SONG_TYPE_URL &&
1018 0 == strcmp(getSongUrl(song), songPlayer->url) &&
1019 !mpdTagsAreEqual(song->tag, songPlayer->tag)) {
1020 if (song->tag)
1021 freeMpdTag(song->tag);
1022 song->tag = mpdTagDup(songPlayer->tag);
1023 playlist.songMod[songNum] = playlist.version;
1024 incrPlaylistVersion();
1028 void syncPlayerAndPlaylist(void)
1030 if (playlist_state != PLAYLIST_STATE_PLAY)
1031 return;
1033 if (getPlayerState() == PLAYER_STATE_STOP)
1034 playPlaylistIfPlayerStopped();
1035 else
1036 syncPlaylistWithQueue(!playlist_queueError);
1038 syncCurrentPlayerDecodeMetadata();
1041 static int currentSongInPlaylist(int fd)
1043 if (playlist_state != PLAYLIST_STATE_PLAY)
1044 return 0;
1046 playlist_stopOnError = 0;
1048 syncPlaylistWithQueue(0);
1050 if (playlist.current >= 0 && playlist.current < playlist.length) {
1051 return playPlaylistOrderNumber(fd, playlist.current);
1052 } else
1053 return stopPlaylist(fd);
1055 return 0;
1058 int nextSongInPlaylist(int fd)
1060 if (playlist_state != PLAYLIST_STATE_PLAY)
1061 return 0;
1063 syncPlaylistWithQueue(0);
1065 playlist_stopOnError = 0;
1067 if (playNextPlaylistQueue(fd, 0) == 0)
1068 return 0;
1070 if (playlist.current < playlist.length - 1) {
1071 return playPlaylistOrderNumber(fd, playlist.current + 1);
1072 } else if (playlist.length && playlist.repeat) {
1073 if (playlist.random)
1074 randomizeOrder(0, playlist.length - 1);
1075 return playPlaylistOrderNumber(fd, 0);
1076 } else {
1077 incrPlaylistCurrent();
1078 return stopPlaylist(fd);
1081 return 0;
1084 void playPlaylistIfPlayerStopped(void)
1086 if (getPlayerState() == PLAYER_STATE_STOP) {
1087 int error = getPlayerError();
1089 if (error == PLAYER_ERROR_NOERROR)
1090 playlist_errorCount = 0;
1091 else
1092 playlist_errorCount++;
1094 if (playlist_state == PLAYLIST_STATE_PLAY
1095 && ((playlist_stopOnError && error != PLAYER_ERROR_NOERROR)
1096 || error == PLAYER_ERROR_AUDIO
1097 || error == PLAYER_ERROR_SYSTEM
1098 || playlist_errorCount >= playlist.length)) {
1099 stopPlaylist(STDERR_FILENO);
1100 } else if (playlist_noGoToNext)
1101 currentSongInPlaylist(STDERR_FILENO);
1102 else
1103 nextSongInPlaylist(STDERR_FILENO);
1107 int getPlaylistRepeatStatus(void)
1109 return playlist.repeat;
1112 int getPlaylistRandomStatus(void)
1114 return playlist.random;
1117 int setPlaylistRepeatStatus(int fd, int status)
1119 if (status != 0 && status != 1) {
1120 commandError(fd, ACK_ERROR_ARG, "\"%i\" is not 0 or 1", status);
1121 return -1;
1124 if (playlist_state == PLAYLIST_STATE_PLAY) {
1125 if (playlist.repeat && !status && playlist.queued == 0) {
1126 lockPlaylistInteraction();
1127 clearPlayerQueue();
1128 unlockPlaylistInteraction();
1132 playlist.repeat = status;
1134 return 0;
1137 int moveSongInPlaylist(int fd, int from, int to)
1139 int i;
1140 Song *tmpSong;
1141 int tmpId;
1142 int queuedSong = -1;
1143 int currentSong = -1;
1145 if (from < 0 || from >= playlist.length) {
1146 commandError(fd, ACK_ERROR_NO_EXIST,
1147 "song doesn't exist: \"%i\"", from);
1148 return -1;
1151 if (to < 0 || to >= playlist.length) {
1152 commandError(fd, ACK_ERROR_NO_EXIST,
1153 "song doesn't exist: \"%i\"", to);
1154 return -1;
1157 if (playlist_state == PLAYLIST_STATE_PLAY) {
1158 if (playlist.queued >= 0) {
1159 queuedSong = playlist.order[playlist.queued];
1161 currentSong = playlist.order[playlist.current];
1162 if (queuedSong == from || queuedSong == to
1163 || currentSong == from || currentSong == to) {
1164 lockPlaylistInteraction();
1165 clearPlayerQueue();
1166 unlockPlaylistInteraction();
1170 tmpSong = playlist.songs[from];
1171 tmpId = playlist.positionToId[from];
1172 /* move songs to one less in from->to */
1173 for (i = from; i < to; i++) {
1174 moveSongFromTo(i + 1, i);
1176 /* move songs to one more in to->from */
1177 for (i = from; i > to; i--) {
1178 moveSongFromTo(i - 1, i);
1180 /* put song at _to_ */
1181 playlist.idToPosition[tmpId] = to;
1182 playlist.positionToId[to] = tmpId;
1183 playlist.songs[to] = tmpSong;
1184 playlist.songMod[to] = playlist.version;
1185 /* now deal with order */
1186 if (playlist.random) {
1187 for (i = 0; i < playlist.length; i++) {
1188 if (playlist.order[i] > from && playlist.order[i] <= to) {
1189 playlist.order[i]--;
1190 } else if (playlist.order[i] < from &&
1191 playlist.order[i] >= to) {
1192 playlist.order[i]++;
1193 } else if (from == playlist.order[i]) {
1194 playlist.order[i] = to;
1198 else
1200 if (playlist.current == from)
1201 playlist.current = to;
1202 else if (playlist.current > from && playlist.current <= to) {
1203 playlist.current--;
1204 } else if (playlist.current >= to && playlist.current < from) {
1205 playlist.current++;
1208 /* this first if statement isn't necessary since the queue
1209 * would have been cleared out if queued == from */
1210 if (playlist.queued == from)
1211 playlist.queued = to;
1212 else if (playlist.queued > from && playlist.queued <= to) {
1213 playlist.queued--;
1214 } else if (playlist.queued>= to && playlist.queued < from) {
1215 playlist.queued++;
1219 incrPlaylistVersion();
1221 return 0;
1224 int moveSongInPlaylistById(int fd, int id1, int to)
1226 checkSongId(id1);
1228 return moveSongInPlaylist(fd, playlist.idToPosition[id1], to);
1231 static void orderPlaylist(void)
1233 int i;
1235 if (playlist.current >= 0 && playlist.current < playlist.length) {
1236 playlist.current = playlist.order[playlist.current];
1239 if (playlist_state == PLAYLIST_STATE_PLAY) {
1240 if (playlist.queued >= 0) {
1241 lockPlaylistInteraction();
1242 clearPlayerQueue();
1243 unlockPlaylistInteraction();
1247 for (i = 0; i < playlist.length; i++) {
1248 playlist.order[i] = i;
1253 static void swapOrder(int a, int b)
1255 int bak = playlist.order[a];
1256 playlist.order[a] = playlist.order[b];
1257 playlist.order[b] = bak;
1260 static void randomizeOrder(int start, int end)
1262 int i;
1263 int ri;
1265 DEBUG("playlist: randomize from %i to %i\n", start, end);
1267 if (playlist_state == PLAYLIST_STATE_PLAY) {
1268 if (playlist.queued >= start && playlist.queued <= end) {
1269 lockPlaylistInteraction();
1270 clearPlayerQueue();
1271 unlockPlaylistInteraction();
1275 for (i = start; i <= end; i++) {
1276 ri = random() % (end - start + 1) + start;
1277 if (ri == playlist.current)
1278 playlist.current = i;
1279 else if (i == playlist.current)
1280 playlist.current = ri;
1281 swapOrder(i, ri);
1285 int setPlaylistRandomStatus(int fd, int status)
1287 int statusWas = playlist.random;
1289 if (status != 0 && status != 1) {
1290 commandError(fd, ACK_ERROR_ARG, "\"%i\" is not 0 or 1", status);
1291 return -1;
1294 playlist.random = status;
1296 if (status != statusWas) {
1297 if (playlist.random) {
1298 /*if(playlist_state==PLAYLIST_STATE_PLAY) {
1299 randomizeOrder(playlist.current+1,
1300 playlist.length-1);
1302 else */ randomizeOrder(0, playlist.length - 1);
1303 if (playlist.current >= 0 &&
1304 playlist.current < playlist.length) {
1305 swapOrder(playlist.current, 0);
1306 playlist.current = 0;
1308 } else
1309 orderPlaylist();
1312 return 0;
1315 int previousSongInPlaylist(int fd)
1317 static time_t lastTime;
1318 time_t diff = time(NULL) - lastTime;
1320 lastTime += diff;
1322 if (playlist_state != PLAYLIST_STATE_PLAY)
1323 return 0;
1325 syncPlaylistWithQueue(0);
1327 if (diff && getPlayerElapsedTime() > PLAYLIST_PREV_UNLESS_ELAPSED) {
1328 return playPlaylistOrderNumber(fd, playlist.current);
1329 } else {
1330 if (playlist.current > 0) {
1331 return playPlaylistOrderNumber(fd,
1332 playlist.current - 1);
1333 } else if (playlist.repeat) {
1334 return playPlaylistOrderNumber(fd, playlist.length - 1);
1335 } else {
1336 return playPlaylistOrderNumber(fd, playlist.current);
1340 return 0;
1343 int shufflePlaylist(int fd)
1345 int i;
1346 int ri;
1348 if (playlist.length > 1) {
1349 if (playlist_state == PLAYLIST_STATE_PLAY) {
1350 lockPlaylistInteraction();
1351 clearPlayerQueue();
1352 unlockPlaylistInteraction();
1353 /* put current playing song first */
1354 swapSongs(0, playlist.order[playlist.current]);
1355 if (playlist.random) {
1356 int j;
1357 for (j = 0; 0 != playlist.order[j]; j++) ;
1358 playlist.current = j;
1359 } else
1360 playlist.current = 0;
1361 i = 1;
1362 } else {
1363 i = 0;
1364 playlist.current = -1;
1366 /* shuffle the rest of the list */
1367 for (; i < playlist.length; i++) {
1368 ri = random() % (playlist.length - 1) + 1;
1369 swapSongs(i, ri);
1372 incrPlaylistVersion();
1375 return 0;
1378 int deletePlaylist(int fd, char *utf8file)
1380 char *file = utf8ToFsCharset(utf8file);
1381 char *rfile = xmalloc(strlen(file) + strlen(".") +
1382 strlen(PLAYLIST_FILE_SUFFIX) + 1);
1383 char *actualFile;
1385 strcpy(rfile, file);
1386 strcat(rfile, ".");
1387 strcat(rfile, PLAYLIST_FILE_SUFFIX);
1389 if ((actualFile = rpp2app(rfile)) && isPlaylist(actualFile))
1390 free(rfile);
1391 else {
1392 free(rfile);
1393 commandError(fd, ACK_ERROR_NO_EXIST,
1394 "playlist \"%s\" not found", utf8file);
1395 return -1;
1398 if (unlink(actualFile) < 0) {
1399 commandError(fd, ACK_ERROR_SYSTEM,
1400 "problems deleting file");
1401 return -1;
1404 return 0;
1407 int savePlaylist(int fd, char *utf8file)
1409 StoredPlaylist *sp = newStoredPlaylist(utf8file, fd, 0);
1410 if (!sp)
1411 return -1;
1413 appendPlaylistToStoredPlaylist(sp, &playlist);
1414 if (writeStoredPlaylist(sp) != 0) {
1415 freeStoredPlaylist(sp);
1416 return -1;
1419 freeStoredPlaylist(sp);
1420 return 0;
1423 int getPlaylistCurrentSong(void)
1425 if (playlist.current >= 0 && playlist.current < playlist.length) {
1426 return playlist.order[playlist.current];
1429 return -1;
1432 unsigned long getPlaylistVersion(void)
1434 return playlist.version;
1437 unsigned long getPlaylistQueueVersion(void)
1439 return playlist.queueversion;
1442 int getPlaylistLength(void)
1444 return playlist.length;
1447 int seekSongInPlaylist(int fd, int song, float time)
1449 int i = song;
1451 if (song < 0 || song >= playlist.length) {
1452 commandError(fd, ACK_ERROR_NO_EXIST,
1453 "song doesn't exist: \"%i\"", song);
1454 return -1;
1457 if (playlist.random)
1458 for (i = 0; song != playlist.order[i]; i++) ;
1460 clearPlayerError();
1461 playlist_stopOnError = 1;
1462 playlist_errorCount = 0;
1464 if (playlist_state == PLAYLIST_STATE_PLAY) {
1465 if (playlist.queued >= 0) {
1466 lockPlaylistInteraction();
1467 clearPlayerQueue();
1468 unlockPlaylistInteraction();
1470 } else if (playPlaylistOrderNumber(fd, i) < 0)
1471 return -1;
1473 if (playlist.current != i) {
1474 if (playPlaylistOrderNumber(fd, i) < 0)
1475 return -1;
1478 return playerSeek(fd, playlist.songs[playlist.order[i]], time);
1481 int seekSongInPlaylistById(int fd, int id, float time)
1483 checkSongId(id);
1485 return seekSongInPlaylist(fd, playlist.idToPosition[id], time);
1488 int getPlaylistSongId(int song)
1490 return playlist.positionToId[song];
1493 int PlaylistInfo(int fd, char *utf8file, int detail)
1495 ListNode *node;
1496 StoredPlaylist *sp = loadStoredPlaylist(utf8file, fd);
1497 if (sp == NULL)
1498 return -1;
1500 node = sp->list->firstNode;
1501 while (node != NULL) {
1502 char *temp = node->data;
1503 int wrote = 0;
1505 if (detail) {
1506 Song *song = getSongFromDB(temp);
1507 if (song) {
1508 printSongInfo(fd, song);
1509 wrote = 1;
1513 if (!wrote) {
1514 fdprintf(fd, SONG_FILE "%s\n", temp);
1517 node = node->nextNode;
1520 freeStoredPlaylist(sp);
1521 return 0;
1524 int loadPlaylist(int fd, char *utf8file)
1526 ListNode *node;
1527 StoredPlaylist *sp = loadStoredPlaylist(utf8file, fd);
1528 if (sp == NULL)
1529 return -1;
1531 node = sp->list->firstNode;
1532 while (node != NULL) {
1533 char *temp = node->data;
1534 if ((addToPlaylist(STDERR_FILENO, temp, 0)) < 0) {
1535 /* for windows compatibility, convert slashes */
1536 char *temp2 = xstrdup(temp);
1537 char *p = temp2;
1538 while (*p) {
1539 if (*p == '\\')
1540 *p = '/';
1541 p++;
1543 if ((addToPlaylist(STDERR_FILENO, temp2, 0)) < 0) {
1544 commandError(fd, ACK_ERROR_PLAYLIST_LOAD,
1545 "can't add file \"%s\"", temp2);
1547 free(temp2);
1550 node = node->nextNode;
1553 freeStoredPlaylist(sp);
1554 return 0;
1557 void searchForSongsInPlaylist(int fd, int numItems, LocateTagItem * items)
1559 int i;
1560 char **originalNeedles = xmalloc(numItems * sizeof(char *));
1562 for (i = 0; i < numItems; i++) {
1563 originalNeedles[i] = items[i].needle;
1564 items[i].needle = strDupToUpper(originalNeedles[i]);
1567 for (i = 0; i < playlist.length; i++) {
1568 if (strstrSearchTags(playlist.songs[i], numItems, items))
1569 printPlaylistSongInfo(fd, i);
1572 for (i = 0; i < numItems; i++) {
1573 free(items[i].needle);
1574 items[i].needle = originalNeedles[i];
1577 free(originalNeedles);
1580 void findSongsInPlaylist(int fd, int numItems, LocateTagItem * items)
1582 int i;
1584 for (i = 0; i < playlist.length; i++) {
1585 if (tagItemsFoundAndMatches(playlist.songs[i], numItems, items))
1586 printPlaylistSongInfo(fd, i);
1590 void clearPlaylistQueue(void)
1592 freeList(playlistQueue);
1593 playlistQueue = makeList(DEFAULT_FREE_DATA_FUNC, 0);
1594 incrPlaylistQueueVersion();
1597 int addToPlaylistQueueById(int fd, int song, int toPosition)
1599 int pos, *data;
1600 ListNode *prevItem;
1602 pos = playlist.idToPosition[song];
1603 if (pos < 0 || pos >= playlist.length) {
1604 commandError(fd, ACK_ERROR_NO_EXIST,
1605 "song doesn't exist: \"%i\"", song);
1606 return -1;
1608 if (toPosition < -1 || toPosition > playlistQueue->numberOfNodes) {
1609 commandError(fd, ACK_ERROR_ARG,
1610 "queue position out of range: \"%i\"", toPosition);
1611 return -1;
1613 data = xmalloc(sizeof(int));
1614 *data = song;
1615 if (toPosition == -1) {
1616 insertInList(playlistQueue, (char *)1, data);
1617 } else {
1618 prevItem = getNodeByPosition(playlistQueue, toPosition);
1619 if (prevItem == NULL) {
1620 insertInList(playlistQueue, (char *)1, data);
1621 } else
1622 insertInListBeforeNode(playlistQueue, prevItem, -1,
1623 (char*) 1, data);
1626 queueNextSongInPlaylist();
1627 incrPlaylistQueueVersion();
1628 return 0;
1631 int deleteFromPlaylistQueue(int fd, int song)
1633 if (song < 0 || song >= playlistQueue->numberOfNodes) {
1634 commandError(fd, ACK_ERROR_NO_EXIST,
1635 "song doesn't exist: \"%i\"", song);
1636 return -1;
1639 return deleteFromPlaylistQueueInternal(song);
1642 int deleteFromPlaylistQueueInternal(int song)
1644 ListNode *delItem;
1646 delItem = getNodeByPosition(playlistQueue, song);
1647 if (delItem == NULL)
1648 return -1;
1650 deleteNodeFromList(playlistQueue, delItem);
1651 if (song == 0)
1652 queueNextSongInPlaylist();
1654 incrPlaylistQueueVersion();
1655 return 0;
1658 int playlistQueueInfo(int fd)
1660 ListNode *cur = playlistQueue->firstNode;
1661 int no = 0;
1662 while (cur) {
1663 printSongInfo(fd, playlist.songs[playlist.idToPosition[*(int *)cur->data]]);
1664 fdprintf(fd, "Pos: %i\nId: %i\n", no++, *(int *)cur->data);
1665 cur = cur->nextNode;
1667 return 0;