configure.ac: Don't allow UNIX IPC to be configured for a native Windows build.
[mpd-mk.git] / src / playlist_edit.c
blobc54b72750aff0ce7b9f82763b7fb79a4eebcbb82
1 /*
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).
26 #include "config.h"
27 #include "playlist_internal.h"
28 #include "player_control.h"
29 #include "database.h"
30 #include "uri.h"
31 #include "song.h"
32 #include "idle.h"
34 #include <sys/types.h>
35 #include <sys/stat.h>
36 #include <unistd.h>
37 #include <stdlib.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
51 anymore */
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);
65 #ifndef WIN32
66 enum playlist_result
67 playlist_append_file(struct playlist *playlist, const char *path, int uid,
68 unsigned *added_id)
70 int ret;
71 struct stat st;
72 struct song *song;
74 if (uid <= 0)
75 /* unauthenticated client */
76 return PLAYLIST_RESULT_DENIED;
78 ret = stat(path, &st);
79 if (ret < 0)
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);
87 if (song == NULL)
88 return PLAYLIST_RESULT_NO_SUCH_SONG;
90 return playlist_append_song(playlist, song, added_id);
92 #endif
94 enum playlist_result
95 playlist_append_song(struct playlist *playlist,
96 struct song *song, unsigned *added_id)
98 const struct song *queued;
99 unsigned id;
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
110 songs to play */
112 unsigned start;
113 if (playlist->queued >= 0)
114 start = playlist->queued + 1;
115 else
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);
126 if (added_id)
127 *added_id = id;
129 return PLAYLIST_RESULT_SUCCESS;
132 static struct song *
133 song_by_uri(const char *uri)
135 struct song *song;
137 song = db_get_song(uri);
138 if (song != NULL)
139 return song;
141 if (uri_has_scheme(uri))
142 return song_remote_new(uri);
144 return NULL;
147 enum playlist_result
148 playlist_append_uri(struct playlist *playlist, const char *uri,
149 unsigned *added_id)
151 struct song *song;
153 g_debug("add to playlist: %s", uri);
155 song = song_by_uri(uri);
156 if (song == NULL)
157 return PLAYLIST_RESULT_NO_SUCH_SONG;
159 return playlist_append_song(playlist, song, added_id);
162 enum playlist_result
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,
181 song1),
182 queue_position_to_order(&playlist->queue,
183 song2));
184 } else {
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;
200 enum playlist_result
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);
212 static void
213 playlist_delete_internal(struct playlist *playlist, unsigned song,
214 const struct song **queued_p)
216 unsigned songOrder;
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 */
227 pc_stop();
228 playlist->playing = false;
230 /* see which song is going to be played instead */
232 playlist->current = queue_next_order(&playlist->queue,
233 playlist->current);
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);
240 else
241 /* no songs left to play, stop playback
242 completely */
243 playlist_stop(playlist);
245 *queued_p = NULL;
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) {
261 playlist->current--;
265 enum playlist_result
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;
283 enum playlist_result
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);
294 if (start >= end)
295 return PLAYLIST_RESULT_SUCCESS;
297 queued = playlist_get_queued_song(playlist);
299 do {
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;
309 enum playlist_result
310 playlist_delete_id(struct playlist *playlist, unsigned id)
312 int song = queue_id_to_position(&playlist->queue, id);
313 if (song < 0)
314 return PLAYLIST_RESULT_NO_SUCH_SONG;
316 return playlist_delete(playlist, song);
319 void
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);
329 enum playlist_result
330 playlist_move_range(struct playlist *playlist,
331 unsigned start, unsigned end, int to)
333 const struct song *queued;
334 int currentSong;
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,
356 playlist->current)
357 : -1;
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)
364 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;
390 enum playlist_result
391 playlist_move_id(struct playlist *playlist, unsigned id1, int to)
393 int song = queue_id_to_position(&playlist->queue, id1);
394 if (song < 0)
395 return PLAYLIST_RESULT_NO_SUCH_SONG;
397 return playlist_move_range(playlist, song, song+1, to);
400 void
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. */
411 return;
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,
417 playlist->current);
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) {
425 playlist->current =
426 queue_position_to_order(&playlist->queue, start);
427 } else
428 playlist->current = start;
430 /* start shuffle after the current song */
431 start++;
433 } else {
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);