2 * Copyright (C) 2003-2010 The Music Player Daemon Project
3 * 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.
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 * Functions for editing the playlist (adding, removing, reordering
22 * songs in the queue).
27 #include "playlist_internal.h"
28 #include "player_control.h"
34 #include <sys/types.h>
39 static void playlist_increment_version(struct playlist
*playlist
)
41 queue_increment_version(&playlist
->queue
);
43 idle_add(IDLE_PLAYLIST
);
46 void playlist_clear(struct playlist
*playlist
)
48 playlist_stop(playlist
);
50 /* make sure there are no references to allocated songs
52 for (unsigned i
= 0; i
< queue_length(&playlist
->queue
); i
++) {
53 const struct song
*song
= queue_get(&playlist
->queue
, i
);
54 if (!song_in_database(song
))
55 pc_song_deleted(song
);
58 queue_clear(&playlist
->queue
);
60 playlist
->current
= -1;
62 playlist_increment_version(playlist
);
67 playlist_append_file(struct playlist
*playlist
, const char *path
, int uid
,
75 /* unauthenticated client */
76 return PLAYLIST_RESULT_DENIED
;
78 ret
= stat(path
, &st
);
80 return PLAYLIST_RESULT_ERRNO
;
82 if (st
.st_uid
!= (uid_t
)uid
&& (st
.st_mode
& 0444) != 0444)
83 /* client is not owner */
84 return PLAYLIST_RESULT_DENIED
;
86 song
= song_file_load(path
, NULL
);
88 return PLAYLIST_RESULT_NO_SUCH_SONG
;
90 return playlist_append_song(playlist
, song
, added_id
);
95 playlist_append_song(struct playlist
*playlist
,
96 struct song
*song
, unsigned *added_id
)
98 const struct song
*queued
;
101 if (queue_is_full(&playlist
->queue
))
102 return PLAYLIST_RESULT_TOO_LARGE
;
104 queued
= playlist_get_queued_song(playlist
);
106 id
= queue_append(&playlist
->queue
, song
);
108 if (playlist
->queue
.random
) {
109 /* shuffle the new song into the list of remaining
113 if (playlist
->queued
>= 0)
114 start
= playlist
->queued
+ 1;
116 start
= playlist
->current
+ 1;
117 if (start
< queue_length(&playlist
->queue
))
118 queue_shuffle_order_last(&playlist
->queue
, start
,
119 queue_length(&playlist
->queue
));
122 playlist_increment_version(playlist
);
124 playlist_update_queued_song(playlist
, queued
);
129 return PLAYLIST_RESULT_SUCCESS
;
133 song_by_uri(const char *uri
)
137 song
= db_get_song(uri
);
141 if (uri_has_scheme(uri
))
142 return song_remote_new(uri
);
148 playlist_append_uri(struct playlist
*playlist
, const char *uri
,
153 g_debug("add to playlist: %s", uri
);
155 song
= song_by_uri(uri
);
157 return PLAYLIST_RESULT_NO_SUCH_SONG
;
159 return playlist_append_song(playlist
, song
, added_id
);
163 playlist_swap_songs(struct playlist
*playlist
, unsigned song1
, unsigned song2
)
165 const struct song
*queued
;
167 if (!queue_valid_position(&playlist
->queue
, song1
) ||
168 !queue_valid_position(&playlist
->queue
, song2
))
169 return PLAYLIST_RESULT_BAD_RANGE
;
171 queued
= playlist_get_queued_song(playlist
);
173 queue_swap(&playlist
->queue
, song1
, song2
);
175 if (playlist
->queue
.random
) {
176 /* update the queue order, so that playlist->current
177 still points to the current song order */
179 queue_swap_order(&playlist
->queue
,
180 queue_position_to_order(&playlist
->queue
,
182 queue_position_to_order(&playlist
->queue
,
185 /* correct the "current" song order */
187 if (playlist
->current
== (int)song1
)
188 playlist
->current
= song2
;
189 else if (playlist
->current
== (int)song2
)
190 playlist
->current
= song1
;
193 playlist_increment_version(playlist
);
195 playlist_update_queued_song(playlist
, queued
);
197 return PLAYLIST_RESULT_SUCCESS
;
201 playlist_swap_songs_id(struct playlist
*playlist
, unsigned id1
, unsigned id2
)
203 int song1
= queue_id_to_position(&playlist
->queue
, id1
);
204 int song2
= queue_id_to_position(&playlist
->queue
, id2
);
206 if (song1
< 0 || song2
< 0)
207 return PLAYLIST_RESULT_NO_SUCH_SONG
;
209 return playlist_swap_songs(playlist
, song1
, song2
);
213 playlist_delete_internal(struct playlist
*playlist
, unsigned song
,
214 const struct song
**queued_p
)
218 assert(song
< queue_length(&playlist
->queue
));
220 songOrder
= queue_position_to_order(&playlist
->queue
, song
);
222 if (playlist
->playing
&& playlist
->current
== (int)songOrder
) {
223 bool paused
= pc_get_state() == PLAYER_STATE_PAUSE
;
225 /* the current song is going to be deleted: stop the player */
228 playlist
->playing
= false;
230 /* see which song is going to be played instead */
232 playlist
->current
= queue_next_order(&playlist
->queue
,
234 if (playlist
->current
== (int)songOrder
)
235 playlist
->current
= -1;
237 if (playlist
->current
>= 0 && !paused
)
238 /* play the song after the deleted one */
239 playlist_play_order(playlist
, playlist
->current
);
241 /* no songs left to play, stop playback
243 playlist_stop(playlist
);
246 } else if (playlist
->current
== (int)songOrder
)
247 /* there's a "current song" but we're not playing
248 currently - clear "current" */
249 playlist
->current
= -1;
251 /* now do it: remove the song */
253 if (!song_in_database(queue_get(&playlist
->queue
, song
)))
254 pc_song_deleted(queue_get(&playlist
->queue
, song
));
256 queue_delete(&playlist
->queue
, song
);
258 /* update the "current" and "queued" variables */
260 if (playlist
->current
> (int)songOrder
) {
266 playlist_delete(struct playlist
*playlist
, unsigned song
)
268 const struct song
*queued
;
270 if (song
>= queue_length(&playlist
->queue
))
271 return PLAYLIST_RESULT_BAD_RANGE
;
273 queued
= playlist_get_queued_song(playlist
);
275 playlist_delete_internal(playlist
, song
, &queued
);
277 playlist_increment_version(playlist
);
278 playlist_update_queued_song(playlist
, queued
);
280 return PLAYLIST_RESULT_SUCCESS
;
284 playlist_delete_range(struct playlist
*playlist
, unsigned start
, unsigned end
)
286 const struct song
*queued
;
288 if (start
>= queue_length(&playlist
->queue
))
289 return PLAYLIST_RESULT_BAD_RANGE
;
291 if (end
> queue_length(&playlist
->queue
))
292 end
= queue_length(&playlist
->queue
);
295 return PLAYLIST_RESULT_SUCCESS
;
297 queued
= playlist_get_queued_song(playlist
);
300 playlist_delete_internal(playlist
, --end
, &queued
);
301 } while (end
!= start
);
303 playlist_increment_version(playlist
);
304 playlist_update_queued_song(playlist
, queued
);
306 return PLAYLIST_RESULT_SUCCESS
;
310 playlist_delete_id(struct playlist
*playlist
, unsigned id
)
312 int song
= queue_id_to_position(&playlist
->queue
, id
);
314 return PLAYLIST_RESULT_NO_SUCH_SONG
;
316 return playlist_delete(playlist
, song
);
320 playlist_delete_song(struct playlist
*playlist
, const struct song
*song
)
322 for (int i
= queue_length(&playlist
->queue
) - 1; i
>= 0; --i
)
323 if (song
== queue_get(&playlist
->queue
, i
))
324 playlist_delete(playlist
, i
);
326 pc_song_deleted(song
);
330 playlist_move_range(struct playlist
*playlist
,
331 unsigned start
, unsigned end
, int to
)
333 const struct song
*queued
;
336 if (!queue_valid_position(&playlist
->queue
, start
) ||
337 !queue_valid_position(&playlist
->queue
, end
- 1))
338 return PLAYLIST_RESULT_BAD_RANGE
;
340 if ((to
>= 0 && to
+ end
- start
- 1 >= queue_length(&playlist
->queue
)) ||
341 (to
< 0 && abs(to
) > (int)queue_length(&playlist
->queue
)))
342 return PLAYLIST_RESULT_BAD_RANGE
;
344 if ((int)start
== to
)
345 /* nothing happens */
346 return PLAYLIST_RESULT_SUCCESS
;
348 queued
= playlist_get_queued_song(playlist
);
351 * (to < 0) => move to offset from current song
352 * (-playlist.length == to) => move to position BEFORE current song
354 currentSong
= playlist
->current
>= 0
355 ? (int)queue_order_to_position(&playlist
->queue
,
358 if (to
< 0 && playlist
->current
>= 0) {
359 if (start
<= (unsigned)currentSong
&& (unsigned)currentSong
<= end
)
360 /* no-op, can't be moved to offset of itself */
361 return PLAYLIST_RESULT_SUCCESS
;
362 to
= (currentSong
+ abs(to
)) % queue_length(&playlist
->queue
);
363 if (start
< (unsigned)to
)
367 queue_move_range(&playlist
->queue
, start
, end
, to
);
369 if (!playlist
->queue
.random
) {
370 /* update current/queued */
371 if ((int)start
<= playlist
->current
&&
372 (unsigned)playlist
->current
< end
)
373 playlist
->current
+= to
- start
;
374 else if (playlist
->current
>= (int)end
&&
375 playlist
->current
<= to
) {
376 playlist
->current
-= end
- start
;
377 } else if (playlist
->current
>= to
&&
378 playlist
->current
< (int)start
) {
379 playlist
->current
+= end
- start
;
383 playlist_increment_version(playlist
);
385 playlist_update_queued_song(playlist
, queued
);
387 return PLAYLIST_RESULT_SUCCESS
;
391 playlist_move_id(struct playlist
*playlist
, unsigned id1
, int to
)
393 int song
= queue_id_to_position(&playlist
->queue
, id1
);
395 return PLAYLIST_RESULT_NO_SUCH_SONG
;
397 return playlist_move_range(playlist
, song
, song
+1, to
);
401 playlist_shuffle(struct playlist
*playlist
, unsigned start
, unsigned end
)
403 const struct song
*queued
;
405 if (end
> queue_length(&playlist
->queue
))
406 /* correct the "end" offset */
407 end
= queue_length(&playlist
->queue
);
409 if ((start
+1) >= end
)
410 /* needs at least two entries. */
413 queued
= playlist_get_queued_song(playlist
);
414 if (playlist
->playing
&& playlist
->current
>= 0) {
415 unsigned current_position
;
416 current_position
= queue_order_to_position(&playlist
->queue
,
419 if (current_position
>= start
&& current_position
< end
)
421 /* put current playing song first */
422 queue_swap(&playlist
->queue
, start
, current_position
);
424 if (playlist
->queue
.random
) {
426 queue_position_to_order(&playlist
->queue
, start
);
428 playlist
->current
= start
;
430 /* start shuffle after the current song */
434 /* no playback currently: reset playlist->current */
436 playlist
->current
= -1;
439 queue_shuffle_range(&playlist
->queue
, start
, end
);
441 playlist_increment_version(playlist
);
443 playlist_update_queued_song(playlist
, queued
);