aboutsummaryrefslogtreecommitdiffstats
path: root/src/playlist
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/decoder/dsdiff_decoder_plugin.h (renamed from src/playlist/flac_playlist_plugin.h)8
-rw-r--r--src/playlist.c75
-rw-r--r--src/playlist.h114
-rw-r--r--src/playlist/asx_playlist_plugin.c5
-rw-r--r--src/playlist/asx_playlist_plugin.h2
-rw-r--r--src/playlist/cue_playlist_plugin.c90
-rw-r--r--src/playlist/cue_playlist_plugin.h2
-rw-r--r--src/playlist/despotify_playlist_plugin.c217
-rw-r--r--src/playlist/despotify_playlist_plugin.h25
-rw-r--r--src/playlist/embcue_playlist_plugin.c181
-rw-r--r--src/playlist/embcue_playlist_plugin.h25
-rw-r--r--src/playlist/extm3u_playlist_plugin.c5
-rw-r--r--src/playlist/extm3u_playlist_plugin.h2
-rw-r--r--src/playlist/flac_playlist_plugin.c170
-rw-r--r--src/playlist/lastfm_playlist_plugin.c49
-rw-r--r--src/playlist/lastfm_playlist_plugin.h2
-rw-r--r--src/playlist/m3u_playlist_plugin.c2
-rw-r--r--src/playlist/m3u_playlist_plugin.h2
-rw-r--r--src/playlist/pls_playlist_plugin.c5
-rw-r--r--src/playlist/pls_playlist_plugin.h2
-rw-r--r--src/playlist/rss_playlist_plugin.c5
-rw-r--r--src/playlist/rss_playlist_plugin.h2
-rw-r--r--src/playlist/soundcloud_playlist_plugin.c448
-rw-r--r--src/playlist/soundcloud_playlist_plugin.h25
-rw-r--r--src/playlist/xspf_playlist_plugin.c5
-rw-r--r--src/playlist/xspf_playlist_plugin.h2
-rw-r--r--src/playlist_any.c17
-rw-r--r--src/playlist_any.h7
-rw-r--r--src/playlist_control.c83
-rw-r--r--src/playlist_database.c13
-rw-r--r--src/playlist_database.h8
-rw-r--r--src/playlist_edit.c167
-rw-r--r--src/playlist_error.h49
-rw-r--r--src/playlist_global.c5
-rw-r--r--src/playlist_internal.h8
-rw-r--r--src/playlist_list.c65
-rw-r--r--src/playlist_list.h9
-rw-r--r--src/playlist_mapper.c29
-rw-r--r--src/playlist_mapper.h7
-rw-r--r--src/playlist_plugin.h12
-rw-r--r--src/playlist_print.c24
-rw-r--r--src/playlist_print.h6
-rw-r--r--src/playlist_queue.c44
-rw-r--r--src/playlist_queue.h18
-rw-r--r--src/playlist_save.c26
-rw-r--r--src/playlist_save.h15
-rw-r--r--src/playlist_song.c24
-rw-r--r--src/playlist_song.h10
-rw-r--r--src/playlist_state.c60
-rw-r--r--src/playlist_state.h11
-rw-r--r--src/playlist_vector.c65
-rw-r--r--src/playlist_vector.h49
52 files changed, 1637 insertions, 664 deletions
diff --git a/src/playlist/flac_playlist_plugin.h b/src/decoder/dsdiff_decoder_plugin.h
index 7b141264f..34e1438de 100644
--- a/src/playlist/flac_playlist_plugin.h
+++ b/src/decoder/dsdiff_decoder_plugin.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2010 The Music Player Daemon Project
+ * Copyright (C) 2003-2011 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,9 +17,9 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_PLAYLIST_FLAC_PLAYLIST_PLUGIN_H
-#define MPD_PLAYLIST_FLAC_PLAYLIST_PLUGIN_H
+#ifndef MPD_DECODER_DSDIFF_H
+#define MPD_DECODER_DSDIFF_H
-extern const struct playlist_plugin flac_playlist_plugin;
+extern const struct decoder_plugin dsdiff_decoder_plugin;
#endif
diff --git a/src/playlist.c b/src/playlist.c
index 4a1e54814..0c9eea92d 100644
--- a/src/playlist.c
+++ b/src/playlist.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2010 The Music Player Daemon Project
+ * Copyright (C) 2003-2011 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -75,7 +75,8 @@ playlist_finish(struct playlist *playlist)
* Queue a song, addressed by its order number.
*/
static void
-playlist_queue_song_order(struct playlist *playlist, unsigned order)
+playlist_queue_song_order(struct playlist *playlist, struct player_control *pc,
+ unsigned order)
{
struct song *song;
char *uri;
@@ -89,16 +90,16 @@ playlist_queue_song_order(struct playlist *playlist, unsigned order)
g_debug("queue song %i:\"%s\"", playlist->queued, uri);
g_free(uri);
- pc_enqueue_song(song);
+ pc_enqueue_song(pc, song);
}
/**
* Called if the player thread has started playing the "queued" song.
*/
static void
-playlist_song_started(struct playlist *playlist)
+playlist_song_started(struct playlist *playlist, struct player_control *pc)
{
- assert(pc.next_song == NULL);
+ assert(pc->next_song == NULL);
assert(playlist->queued >= -1);
/* queued song has started: copy queued to current,
@@ -110,11 +111,13 @@ playlist_song_started(struct playlist *playlist)
/* Pause if we are in single mode. */
if(playlist->queue.single && !playlist->queue.repeat) {
- pc_set_pause(true);
+ pc_set_pause(pc, true);
}
if(playlist->queue.consume)
- playlist_delete(playlist, queue_order_to_position(&playlist->queue, current));
+ playlist_delete(playlist, pc,
+ queue_order_to_position(&playlist->queue,
+ current));
idle_add(IDLE_PLAYER);
}
@@ -129,7 +132,9 @@ playlist_get_queued_song(struct playlist *playlist)
}
void
-playlist_update_queued_song(struct playlist *playlist, const struct song *prev)
+playlist_update_queued_song(struct playlist *playlist,
+ struct player_control *pc,
+ const struct song *prev)
{
int next_order;
const struct song *next_song;
@@ -170,20 +175,21 @@ playlist_update_queued_song(struct playlist *playlist, const struct song *prev)
if (prev != NULL && next_song != prev) {
/* clear the currently queued song */
- pc_cancel();
+ pc_cancel(pc);
playlist->queued = -1;
}
if (next_order >= 0) {
if (next_song != prev)
- playlist_queue_song_order(playlist, next_order);
+ playlist_queue_song_order(playlist, pc, next_order);
else
playlist->queued = next_order;
}
}
void
-playlist_play_order(struct playlist *playlist, int orderNum)
+playlist_play_order(struct playlist *playlist, struct player_control *pc,
+ int orderNum)
{
struct song *song;
char *uri;
@@ -197,46 +203,46 @@ playlist_play_order(struct playlist *playlist, int orderNum)
g_debug("play %i:\"%s\"", orderNum, uri);
g_free(uri);
- pc_play(song);
+ pc_play(pc, song);
playlist->current = orderNum;
}
static void
-playlist_resume_playback(struct playlist *playlist);
+playlist_resume_playback(struct playlist *playlist, struct player_control *pc);
/**
* This is the "PLAYLIST" event handler. It is invoked by the player
* thread whenever it requests a new queued song, or when it exits.
*/
void
-playlist_sync(struct playlist *playlist)
+playlist_sync(struct playlist *playlist, struct player_control *pc)
{
if (!playlist->playing)
/* this event has reached us out of sync: we aren't
playing anymore; ignore the event */
return;
- player_lock();
- enum player_state pc_state = pc_get_state();
- const struct song *pc_next_song = pc.next_song;
- player_unlock();
+ player_lock(pc);
+ enum player_state pc_state = pc_get_state(pc);
+ const struct song *pc_next_song = pc->next_song;
+ player_unlock(pc);
if (pc_state == PLAYER_STATE_STOP)
/* the player thread has stopped: check if playback
should be restarted with the next song. That can
happen if the playlist isn't filling the queue fast
enough */
- playlist_resume_playback(playlist);
+ playlist_resume_playback(playlist, pc);
else {
/* check if the player thread has already started
playing the queued song */
if (pc_next_song == NULL && playlist->queued != -1)
- playlist_song_started(playlist);
+ playlist_song_started(playlist, pc);
/* make sure the queued song is always set (if
possible) */
- if (pc.next_song == NULL && playlist->queued < 0)
- playlist_update_queued_song(playlist, NULL);
+ if (pc->next_song == NULL && playlist->queued < 0)
+ playlist_update_queued_song(playlist, pc, NULL);
}
}
@@ -245,14 +251,14 @@ playlist_sync(struct playlist *playlist)
* decide whether to re-start playback
*/
static void
-playlist_resume_playback(struct playlist *playlist)
+playlist_resume_playback(struct playlist *playlist, struct player_control *pc)
{
enum player_error error;
assert(playlist->playing);
- assert(pc_get_state() == PLAYER_STATE_STOP);
+ assert(pc_get_state(pc) == PLAYER_STATE_STOP);
- error = pc_get_error();
+ error = pc_get_error(pc);
if (error == PLAYER_ERROR_NOERROR)
playlist->error_count = 0;
else
@@ -263,10 +269,10 @@ playlist_resume_playback(struct playlist *playlist)
playlist->error_count >= queue_length(&playlist->queue))
/* too many errors, or critical error: stop
playback */
- playlist_stop(playlist);
+ playlist_stop(playlist, pc);
else
/* continue playback at the next song */
- playlist_next(playlist);
+ playlist_next(playlist, pc);
}
bool
@@ -294,7 +300,8 @@ playlist_get_consume(const struct playlist *playlist)
}
void
-playlist_set_repeat(struct playlist *playlist, bool status)
+playlist_set_repeat(struct playlist *playlist, struct player_control *pc,
+ bool status)
{
if (status == playlist->queue.repeat)
return;
@@ -303,7 +310,7 @@ playlist_set_repeat(struct playlist *playlist, bool status)
/* if the last song is currently being played, the "next song"
might change when repeat mode is toggled */
- playlist_update_queued_song(playlist,
+ playlist_update_queued_song(playlist, pc,
playlist_get_queued_song(playlist));
idle_add(IDLE_OPTIONS);
@@ -321,7 +328,8 @@ playlist_order(struct playlist *playlist)
}
void
-playlist_set_single(struct playlist *playlist, bool status)
+playlist_set_single(struct playlist *playlist, struct player_control *pc,
+ bool status)
{
if (status == playlist->queue.single)
return;
@@ -330,7 +338,7 @@ playlist_set_single(struct playlist *playlist, bool status)
/* if the last song is currently being played, the "next song"
might change when single mode is toggled */
- playlist_update_queued_song(playlist,
+ playlist_update_queued_song(playlist, pc,
playlist_get_queued_song(playlist));
idle_add(IDLE_OPTIONS);
@@ -347,7 +355,8 @@ playlist_set_consume(struct playlist *playlist, bool status)
}
void
-playlist_set_random(struct playlist *playlist, bool status)
+playlist_set_random(struct playlist *playlist, struct player_control *pc,
+ bool status)
{
const struct song *queued;
@@ -384,7 +393,7 @@ playlist_set_random(struct playlist *playlist, bool status)
} else
playlist_order(playlist);
- playlist_update_queued_song(playlist, queued);
+ playlist_update_queued_song(playlist, pc, queued);
idle_add(IDLE_OPTIONS);
}
diff --git a/src/playlist.h b/src/playlist.h
index 3ba90ff91..a21bdf24a 100644
--- a/src/playlist.h
+++ b/src/playlist.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2010 The Music Player Daemon Project
+ * Copyright (C) 2003-2011 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -21,24 +21,11 @@
#define MPD_PLAYLIST_H
#include "queue.h"
+#include "playlist_error.h"
#include <stdbool.h>
-#define PLAYLIST_COMMENT '#'
-
-enum playlist_result {
- PLAYLIST_RESULT_SUCCESS,
- PLAYLIST_RESULT_ERRNO,
- PLAYLIST_RESULT_DENIED,
- PLAYLIST_RESULT_NO_SUCH_SONG,
- PLAYLIST_RESULT_NO_SUCH_LIST,
- PLAYLIST_RESULT_LIST_EXISTS,
- PLAYLIST_RESULT_BAD_NAME,
- PLAYLIST_RESULT_BAD_RANGE,
- PLAYLIST_RESULT_NOT_PLAYING,
- PLAYLIST_RESULT_TOO_LARGE,
- PLAYLIST_RESULT_DISABLED,
-};
+struct player_control;
struct playlist {
/**
@@ -111,28 +98,28 @@ playlist_get_queue(const struct playlist *playlist)
}
void
-playlist_clear(struct playlist *playlist);
+playlist_clear(struct playlist *playlist, struct player_control *pc);
-#ifndef WIN32
/**
- * Appends a local file (outside the music database) to the playlist,
- * but only if the file's owner is equal to the specified uid.
+ * Appends a local file (outside the music database) to the playlist.
+ *
+ * Note: the caller is responsible for checking permissions.
*/
enum playlist_result
-playlist_append_file(struct playlist *playlist, const char *path, int uid,
- unsigned *added_id);
-#endif
+playlist_append_file(struct playlist *playlist, struct player_control *pc,
+ const char *path_fs, unsigned *added_id);
enum playlist_result
-playlist_append_uri(struct playlist *playlist, const char *file,
- unsigned *added_id);
+playlist_append_uri(struct playlist *playlist, struct player_control *pc,
+ const char *file, unsigned *added_id);
enum playlist_result
-playlist_append_song(struct playlist *playlist,
+playlist_append_song(struct playlist *playlist, struct player_control *pc,
struct song *song, unsigned *added_id);
enum playlist_result
-playlist_delete(struct playlist *playlist, unsigned song);
+playlist_delete(struct playlist *playlist, struct player_control *pc,
+ unsigned song);
/**
* Deletes a range of songs from the playlist.
@@ -141,64 +128,86 @@ playlist_delete(struct playlist *playlist, unsigned song);
* @param end the position after the last song to delete
*/
enum playlist_result
-playlist_delete_range(struct playlist *playlist, unsigned start, unsigned end);
+playlist_delete_range(struct playlist *playlist, struct player_control *pc,
+ unsigned start, unsigned end);
enum playlist_result
-playlist_delete_id(struct playlist *playlist, unsigned song);
+playlist_delete_id(struct playlist *playlist, struct player_control *pc,
+ unsigned song);
void
-playlist_stop(struct playlist *playlist);
+playlist_stop(struct playlist *playlist, struct player_control *pc);
enum playlist_result
-playlist_play(struct playlist *playlist, int song);
+playlist_play(struct playlist *playlist, struct player_control *pc,
+ int song);
enum playlist_result
-playlist_play_id(struct playlist *playlist, int song);
+playlist_play_id(struct playlist *playlist, struct player_control *pc,
+ int song);
void
-playlist_next(struct playlist *playlist);
+playlist_next(struct playlist *playlist, struct player_control *pc);
void
-playlist_sync(struct playlist *playlist);
+playlist_sync(struct playlist *playlist, struct player_control *pc);
void
-playlist_previous(struct playlist *playlist);
+playlist_previous(struct playlist *playlist, struct player_control *pc);
void
-playlist_shuffle(struct playlist *playlist, unsigned start, unsigned end);
+playlist_shuffle(struct playlist *playlist, struct player_control *pc,
+ unsigned start, unsigned end);
void
-playlist_delete_song(struct playlist *playlist, const struct song *song);
+playlist_delete_song(struct playlist *playlist, struct player_control *pc,
+ const struct song *song);
+
+enum playlist_result
+playlist_move_range(struct playlist *playlist, struct player_control *pc,
+ unsigned start, unsigned end, int to);
+
+enum playlist_result
+playlist_move_id(struct playlist *playlist, struct player_control *pc,
+ unsigned id, int to);
enum playlist_result
-playlist_move_range(struct playlist *playlist, unsigned start, unsigned end, int to);
+playlist_swap_songs(struct playlist *playlist, struct player_control *pc,
+ unsigned song1, unsigned song2);
enum playlist_result
-playlist_move_id(struct playlist *playlist, unsigned id, int to);
+playlist_swap_songs_id(struct playlist *playlist, struct player_control *pc,
+ unsigned id1, unsigned id2);
enum playlist_result
-playlist_swap_songs(struct playlist *playlist, unsigned song1, unsigned song2);
+playlist_set_priority(struct playlist *playlist, struct player_control *pc,
+ unsigned start_position, unsigned end_position,
+ uint8_t priority);
enum playlist_result
-playlist_swap_songs_id(struct playlist *playlist, unsigned id1, unsigned id2);
+playlist_set_priority_id(struct playlist *playlist, struct player_control *pc,
+ unsigned song_id, uint8_t priority);
bool
playlist_get_repeat(const struct playlist *playlist);
void
-playlist_set_repeat(struct playlist *playlist, bool status);
+playlist_set_repeat(struct playlist *playlist, struct player_control *pc,
+ bool status);
bool
playlist_get_random(const struct playlist *playlist);
void
-playlist_set_random(struct playlist *playlist, bool status);
+playlist_set_random(struct playlist *playlist, struct player_control *pc,
+ bool status);
bool
playlist_get_single(const struct playlist *playlist);
void
-playlist_set_single(struct playlist *playlist, bool status);
+playlist_set_single(struct playlist *playlist, struct player_control *pc,
+ bool status);
bool
playlist_get_consume(const struct playlist *playlist);
@@ -222,12 +231,25 @@ unsigned long
playlist_get_version(const struct playlist *playlist);
enum playlist_result
-playlist_seek_song(struct playlist *playlist, unsigned song, float seek_time);
+playlist_seek_song(struct playlist *playlist, struct player_control *pc,
+ unsigned song, float seek_time);
enum playlist_result
-playlist_seek_song_id(struct playlist *playlist,
+playlist_seek_song_id(struct playlist *playlist, struct player_control *pc,
unsigned id, float seek_time);
+/**
+ * Seek within the current song. Fails if MPD is not currently
+ * playing.
+ *
+ * @param time the time in seconds
+ * @param relative if true, then the specified time is relative to the
+ * current position
+ */
+enum playlist_result
+playlist_seek_current(struct playlist *playlist, struct player_control *pc,
+ float seek_time, bool relative);
+
void
playlist_increment_version_all(struct playlist *playlist);
diff --git a/src/playlist/asx_playlist_plugin.c b/src/playlist/asx_playlist_plugin.c
index 39513e710..298687859 100644
--- a/src/playlist/asx_playlist_plugin.c
+++ b/src/playlist/asx_playlist_plugin.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2010 The Music Player Daemon Project
+ * Copyright (C) 2003-2011 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -233,7 +233,8 @@ asx_open_stream(struct input_stream *is)
&parser, asx_parser_destroy);
while (true) {
- nbytes = input_stream_read(is, buffer, sizeof(buffer), &error);
+ nbytes = input_stream_lock_read(is, buffer, sizeof(buffer),
+ &error);
if (nbytes == 0) {
if (error != NULL) {
g_markup_parse_context_free(context);
diff --git a/src/playlist/asx_playlist_plugin.h b/src/playlist/asx_playlist_plugin.h
index 7ce91aa41..6c01c1209 100644
--- a/src/playlist/asx_playlist_plugin.h
+++ b/src/playlist/asx_playlist_plugin.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2010 The Music Player Daemon Project
+ * Copyright (C) 2003-2011 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/playlist/cue_playlist_plugin.c b/src/playlist/cue_playlist_plugin.c
index b22712bc7..b85de77d3 100644
--- a/src/playlist/cue_playlist_plugin.c
+++ b/src/playlist/cue_playlist_plugin.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2010 The Music Player Daemon Project
+ * Copyright (C) 2003-2011 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -22,10 +22,11 @@
#include "playlist_plugin.h"
#include "tag.h"
#include "song.h"
-#include "cue/cue_tag.h"
+#include "cue/cue_parser.h"
+#include "input_stream.h"
+#include "text_input_stream.h"
#include <glib.h>
-#include <libcue/libcue.h>
#include <assert.h>
#include <string.h>
@@ -35,31 +36,21 @@
struct cue_playlist {
struct playlist_provider base;
- struct Cd *cd;
-
- unsigned next;
+ struct input_stream *is;
+ struct text_input_stream *tis;
+ struct cue_parser *parser;
};
static struct playlist_provider *
-cue_playlist_open_uri(const char *uri)
+cue_playlist_open_stream(struct input_stream *is)
{
- struct cue_playlist *playlist;
- FILE *file;
- struct Cd *cd;
-
- file = fopen(uri, "rt");
- if (file == NULL)
- return NULL;
+ struct cue_playlist *playlist = g_new(struct cue_playlist, 1);
+ playlist_provider_init(&playlist->base, &cue_playlist_plugin);
- cd = cue_parse_file(file);
- fclose(file);
- if (cd == NULL)
- return NULL;
+ playlist->is = is;
+ playlist->tis = text_input_stream_new(is);
+ playlist->parser = cue_parser_new();
- playlist = g_new(struct cue_playlist, 1);
- playlist_provider_init(&playlist->base, &cue_playlist_plugin);
- playlist->cd = cd;
- playlist->next = 1;
return &playlist->base;
}
@@ -69,7 +60,8 @@ cue_playlist_close(struct playlist_provider *_playlist)
{
struct cue_playlist *playlist = (struct cue_playlist *)_playlist;
- cd_delete(playlist->cd);
+ cue_parser_free(playlist->parser);
+ text_input_stream_free(playlist->tis);
g_free(playlist);
}
@@ -77,45 +69,21 @@ static struct song *
cue_playlist_read(struct playlist_provider *_playlist)
{
struct cue_playlist *playlist = (struct cue_playlist *)_playlist;
- struct Track *track;
- struct tag *tag;
- const char *filename;
- struct song *song;
-
- track = cd_get_track(playlist->cd, playlist->next);
- if (track == NULL)
- return NULL;
-
- tag = cue_tag(playlist->cd, playlist->next);
- if (tag == NULL)
- return NULL;
-
- ++playlist->next;
-
- filename = track_get_filename(track);
- if (*filename == 0 || filename[0] == '.' ||
- strchr(filename, '/') != NULL) {
- /* unsafe characters found, bail out */
- tag_free(tag);
- return NULL;
+
+ struct song *song = cue_parser_get(playlist->parser);
+ if (song != NULL)
+ return song;
+
+ const char *line;
+ while ((line = text_input_stream_read(playlist->tis)) != NULL) {
+ cue_parser_feed(playlist->parser, line);
+ song = cue_parser_get(playlist->parser);
+ if (song != NULL)
+ return song;
}
- song = song_remote_new(filename);
- song->tag = tag;
- song->start_ms = ((track_get_start(track)
- + track_get_index(track, 1)
- - track_get_zero_pre(track)) * 1000) / 75;
-
- /* append pregap of the next track to the end of this one */
- track = cd_get_track(playlist->cd, playlist->next);
- if (track != NULL)
- song->end_ms = ((track_get_start(track)
- + track_get_index(track, 1)
- - track_get_zero_pre(track)) * 1000) / 75;
- else
- song->end_ms = 0;
-
- return song;
+ cue_parser_finish(playlist->parser);
+ return cue_parser_get(playlist->parser);
}
static const char *const cue_playlist_suffixes[] = {
@@ -131,7 +99,7 @@ static const char *const cue_playlist_mime_types[] = {
const struct playlist_plugin cue_playlist_plugin = {
.name = "cue",
- .open_uri = cue_playlist_open_uri,
+ .open_stream = cue_playlist_open_stream,
.close = cue_playlist_close,
.read = cue_playlist_read,
diff --git a/src/playlist/cue_playlist_plugin.h b/src/playlist/cue_playlist_plugin.h
index c89ec55c5..c02e2235a 100644
--- a/src/playlist/cue_playlist_plugin.h
+++ b/src/playlist/cue_playlist_plugin.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2010 The Music Player Daemon Project
+ * Copyright (C) 2003-2011 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/playlist/despotify_playlist_plugin.c b/src/playlist/despotify_playlist_plugin.c
new file mode 100644
index 000000000..08a32d79d
--- /dev/null
+++ b/src/playlist/despotify_playlist_plugin.c
@@ -0,0 +1,217 @@
+/*
+ * Copyright (C) 2011 The Music Player Daemon Project
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+#include "config.h"
+#include "playlist/despotify_playlist_plugin.h"
+#include "playlist_plugin.h"
+#include "playlist_list.h"
+#include "conf.h"
+#include "uri.h"
+#include "tag.h"
+#include "song.h"
+#include "input_stream.h"
+#include "glib_compat.h"
+#include "despotify_utils.h"
+
+#include <glib.h>
+
+#include <assert.h>
+#include <string.h>
+#include <stdlib.h>
+#include <despotify.h>
+
+struct despotify_playlist {
+ struct playlist_provider base;
+
+ struct despotify_session *session;
+ GSList *list;
+};
+
+static void
+add_song(struct despotify_playlist *ctx, struct ds_track *track)
+{
+ const char *dsp_scheme = despotify_playlist_plugin.schemes[0];
+ struct song *song;
+ char uri[128];
+ char *ds_uri;
+
+ /* Create a spt://... URI for MPD */
+ g_snprintf(uri, sizeof(uri), "%s://", dsp_scheme);
+ ds_uri = uri + strlen(dsp_scheme) + 3;
+
+ if (despotify_track_to_uri(track, ds_uri) != ds_uri) {
+ /* Should never really fail, but let's be sure */
+ g_debug("Can't add track %s\n", track->title);
+ return;
+ }
+
+ song = song_remote_new(uri);
+ song->tag = mpd_despotify_tag_from_track(track);
+
+ ctx->list = g_slist_prepend(ctx->list, song);
+}
+
+static bool
+parse_track(struct despotify_playlist *ctx,
+ struct ds_link *link)
+{
+ struct ds_track *track;
+
+ track = despotify_link_get_track(ctx->session, link);
+ if (!track)
+ return false;
+ add_song(ctx, track);
+
+ return true;
+}
+
+static bool
+parse_playlist(struct despotify_playlist *ctx,
+ struct ds_link *link)
+{
+ struct ds_playlist *playlist;
+ struct ds_track *track;
+
+ playlist = despotify_link_get_playlist(ctx->session, link);
+ if (!playlist)
+ return false;
+
+ for (track = playlist->tracks; track; track = track->next)
+ add_song(ctx, track);
+
+ return true;
+}
+
+static bool
+despotify_playlist_init(G_GNUC_UNUSED const struct config_param *param)
+{
+ return true;
+}
+
+static void
+despotify_playlist_finish(void)
+{
+}
+
+
+static struct playlist_provider *
+despotify_playlist_open_uri(const char *url, G_GNUC_UNUSED GMutex *mutex,
+ G_GNUC_UNUSED GCond *cond)
+{
+ struct despotify_playlist *ctx;
+ struct despotify_session *session;
+ struct ds_link *link;
+ bool parse_result;
+
+ session = mpd_despotify_get_session();
+ if (!session)
+ goto clean_none;
+
+ /* Get link without spt:// */
+ link = despotify_link_from_uri(url + strlen(despotify_playlist_plugin.schemes[0]) + 3);
+ if (!link) {
+ g_debug("Can't find %s\n", url);
+ goto clean_none;
+ }
+
+ ctx = g_new(struct despotify_playlist, 1);
+
+ ctx->list = NULL;
+ ctx->session = session;
+ playlist_provider_init(&ctx->base, &despotify_playlist_plugin);
+
+ switch (link->type)
+ {
+ case LINK_TYPE_TRACK:
+ parse_result = parse_track(ctx, link);
+ break;
+ case LINK_TYPE_PLAYLIST:
+ parse_result = parse_playlist(ctx, link);
+ break;
+ default:
+ parse_result = false;
+ break;
+ }
+ despotify_free_link(link);
+ if (!parse_result)
+ goto clean_playlist;
+
+ ctx->list = g_slist_reverse(ctx->list);
+
+ return &ctx->base;
+
+clean_playlist:
+ g_slist_free(ctx->list);
+clean_none:
+
+ return NULL;
+}
+
+static void
+track_free_callback(gpointer data, G_GNUC_UNUSED gpointer user_data)
+{
+ struct song *song = (struct song *)data;
+
+ song_free(song);
+}
+
+static void
+despotify_playlist_close(struct playlist_provider *_playlist)
+{
+ struct despotify_playlist *ctx = (struct despotify_playlist *)_playlist;
+
+ g_slist_foreach(ctx->list, track_free_callback, NULL);
+ g_slist_free(ctx->list);
+
+ g_free(ctx);
+}
+
+
+static struct song *
+despotify_playlist_read(struct playlist_provider *_playlist)
+{
+ struct despotify_playlist *ctx = (struct despotify_playlist *)_playlist;
+ struct song *out;
+
+ if (!ctx->list)
+ return NULL;
+
+ /* Remove the current track */
+ out = ctx->list->data;
+ ctx->list = g_slist_remove(ctx->list, out);
+
+ return out;
+}
+
+
+static const char *const despotify_schemes[] = {
+ "spt",
+ NULL
+};
+
+const struct playlist_plugin despotify_playlist_plugin = {
+ .name = "despotify",
+
+ .init = despotify_playlist_init,
+ .finish = despotify_playlist_finish,
+ .open_uri = despotify_playlist_open_uri,
+ .read = despotify_playlist_read,
+ .close = despotify_playlist_close,
+
+ .schemes = despotify_schemes,
+};
diff --git a/src/playlist/despotify_playlist_plugin.h b/src/playlist/despotify_playlist_plugin.h
new file mode 100644
index 000000000..f8ee20de0
--- /dev/null
+++ b/src/playlist/despotify_playlist_plugin.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2011 The Music Player Daemon Project
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_PLAYLIST_DESPOTIFY_PLAYLIST_PLUGIN_H
+#define MPD_PLAYLIST_DESPOTIFY_PLAYLIST_PLUGIN_H
+
+extern const struct playlist_plugin despotify_playlist_plugin;
+
+#endif
diff --git a/src/playlist/embcue_playlist_plugin.c b/src/playlist/embcue_playlist_plugin.c
new file mode 100644
index 000000000..6d9a957f9
--- /dev/null
+++ b/src/playlist/embcue_playlist_plugin.c
@@ -0,0 +1,181 @@
+/*
+ * Copyright (C) 2003-2012 The Music Player Daemon Project
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+/** \file
+ *
+ * Playlist plugin that reads embedded cue sheets from the "CUESHEET"
+ * tag of a music file.
+ */
+
+#include "config.h"
+#include "playlist/embcue_playlist_plugin.h"
+#include "playlist_plugin.h"
+#include "tag.h"
+#include "tag_handler.h"
+#include "tag_file.h"
+#include "tag_ape.h"
+#include "tag_id3.h"
+#include "song.h"
+#include "cue/cue_parser.h"
+
+#include <glib.h>
+#include <assert.h>
+#include <string.h>
+
+#undef G_LOG_DOMAIN
+#define G_LOG_DOMAIN "cue"
+
+struct embcue_playlist {
+ struct playlist_provider base;
+
+ /**
+ * This is an override for the CUE's "FILE". An embedded CUE
+ * sheet must always point to the song file it is contained
+ * in.
+ */
+ char *filename;
+
+ /**
+ * The value of the file's "CUESHEET" tag.
+ */
+ char *cuesheet;
+
+ /**
+ * The offset of the next line within "cuesheet".
+ */
+ char *next;
+
+ struct cue_parser *parser;
+};
+
+static void
+embcue_tag_pair(const char *name, const char *value, void *ctx)
+{
+ struct embcue_playlist *playlist = ctx;
+
+ if (playlist->cuesheet == NULL &&
+ g_ascii_strcasecmp(name, "cuesheet") == 0)
+ playlist->cuesheet = g_strdup(value);
+}
+
+static const struct tag_handler embcue_tag_handler = {
+ .pair = embcue_tag_pair,
+};
+
+static struct playlist_provider *
+embcue_playlist_open_uri(const char *uri,
+ G_GNUC_UNUSED GMutex *mutex,
+ G_GNUC_UNUSED GCond *cond)
+{
+ if (!g_path_is_absolute(uri))
+ /* only local files supported */
+ return NULL;
+
+ struct embcue_playlist *playlist = g_new(struct embcue_playlist, 1);
+ playlist_provider_init(&playlist->base, &embcue_playlist_plugin);
+ playlist->cuesheet = NULL;
+
+ tag_file_scan(uri, &embcue_tag_handler, playlist);
+ if (playlist->cuesheet == NULL) {
+ tag_ape_scan2(uri, &embcue_tag_handler, playlist);
+ if (playlist->cuesheet == NULL)
+ tag_id3_scan(uri, &embcue_tag_handler, playlist);
+ }
+
+ if (playlist->cuesheet == NULL) {
+ /* no "CUESHEET" tag found */
+ g_free(playlist);
+ return NULL;
+ }
+
+ playlist->filename = g_path_get_basename(uri);
+
+ playlist->next = playlist->cuesheet;
+ playlist->parser = cue_parser_new();
+
+ return &playlist->base;
+}
+
+static void
+embcue_playlist_close(struct playlist_provider *_playlist)
+{
+ struct embcue_playlist *playlist = (struct embcue_playlist *)_playlist;
+
+ cue_parser_free(playlist->parser);
+ g_free(playlist->cuesheet);
+ g_free(playlist->filename);
+ g_free(playlist);
+}
+
+static struct song *
+embcue_playlist_read(struct playlist_provider *_playlist)
+{
+ struct embcue_playlist *playlist = (struct embcue_playlist *)_playlist;
+
+ struct song *song = cue_parser_get(playlist->parser);
+ if (song != NULL)
+ return song;
+
+ while (*playlist->next != 0) {
+ const char *line = playlist->next;
+ char *eol = strpbrk(playlist->next, "\r\n");
+ if (eol != NULL) {
+ /* null-terminate the line */
+ *eol = 0;
+ playlist->next = eol + 1;
+ } else
+ /* last line; put the "next" pointer to the
+ end of the buffer */
+ playlist->next += strlen(line);
+
+ cue_parser_feed(playlist->parser, line);
+ song = cue_parser_get(playlist->parser);
+ if (song != NULL)
+ return song_replace_uri(song, playlist->filename);
+ }
+
+ cue_parser_finish(playlist->parser);
+ song = cue_parser_get(playlist->parser);
+ if (song != NULL)
+ song = song_replace_uri(song, playlist->filename);
+ return song;
+}
+
+static const char *const embcue_playlist_suffixes[] = {
+ /* a few codecs that are known to be supported; there are
+ probably many more */
+ "flac",
+ "mp3", "mp2",
+ "mp4", "mp4a", "m4b",
+ "ape",
+ "wv",
+ "ogg", "oga",
+ NULL
+};
+
+const struct playlist_plugin embcue_playlist_plugin = {
+ .name = "cue",
+
+ .open_uri = embcue_playlist_open_uri,
+ .close = embcue_playlist_close,
+ .read = embcue_playlist_read,
+
+ .suffixes = embcue_playlist_suffixes,
+ .mime_types = NULL,
+};
diff --git a/src/playlist/embcue_playlist_plugin.h b/src/playlist/embcue_playlist_plugin.h
new file mode 100644
index 000000000..c5f21b27e
--- /dev/null
+++ b/src/playlist/embcue_playlist_plugin.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2003-2012 The Music Player Daemon Project
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_PLAYLIST_EMBCUE_PLAYLIST_PLUGIN_H
+#define MPD_PLAYLIST_EMBCUE_PLAYLIST_PLUGIN_H
+
+extern const struct playlist_plugin embcue_playlist_plugin;
+
+#endif
diff --git a/src/playlist/extm3u_playlist_plugin.c b/src/playlist/extm3u_playlist_plugin.c
index 9a04aa066..19be8d1c4 100644
--- a/src/playlist/extm3u_playlist_plugin.c
+++ b/src/playlist/extm3u_playlist_plugin.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2010 The Music Player Daemon Project
+ * Copyright (C) 2003-2011 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -24,6 +24,7 @@
#include "uri.h"
#include "song.h"
#include "tag.h"
+#include "string_util.h"
#include <glib.h>
@@ -89,7 +90,7 @@ extm3u_parse_tag(const char *line)
/* 0 means unknown duration */
duration = 0;
- name = g_strchug(endptr + 1);
+ name = strchug_fast_c(endptr + 1);
if (*name == 0 && duration == 0)
/* no information available; don't allocate a tag
object */
diff --git a/src/playlist/extm3u_playlist_plugin.h b/src/playlist/extm3u_playlist_plugin.h
index fa726c5f6..5f611ac9c 100644
--- a/src/playlist/extm3u_playlist_plugin.h
+++ b/src/playlist/extm3u_playlist_plugin.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2010 The Music Player Daemon Project
+ * Copyright (C) 2003-2011 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/playlist/flac_playlist_plugin.c b/src/playlist/flac_playlist_plugin.c
deleted file mode 100644
index 9d66fb331..000000000
--- a/src/playlist/flac_playlist_plugin.c
+++ /dev/null
@@ -1,170 +0,0 @@
-/*
- * Copyright (C) 2003-2010 The Music Player Daemon Project
- * 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.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#include "config.h"
-#include "playlist/flac_playlist_plugin.h"
-#include "playlist_plugin.h"
-#include "tag.h"
-#include "song.h"
-#include "decoder/flac_metadata.h"
-
-#include <FLAC/metadata.h>
-
-#include <glib.h>
-
-#undef G_LOG_DOMAIN
-#define G_LOG_DOMAIN "flac"
-
-#if defined(FLAC_API_VERSION_CURRENT) && FLAC_API_VERSION_CURRENT > 7
-
-struct flac_playlist {
- struct playlist_provider base;
-
- char *uri;
-
- FLAC__StreamMetadata *cuesheet;
- FLAC__StreamMetadata streaminfo;
-
- unsigned next_track;
-};
-
-static struct playlist_provider *
-flac_playlist_open_uri(const char *uri)
-{
- if (!g_path_is_absolute(uri))
- /* only local files supported */
- return NULL;
-
- FLAC__StreamMetadata *cuesheet;
- if (!FLAC__metadata_get_cuesheet(uri, &cuesheet))
- return NULL;
-
- struct flac_playlist *playlist = g_new(struct flac_playlist, 1);
- playlist_provider_init(&playlist->base, &flac_playlist_plugin);
-
- if (!FLAC__metadata_get_streaminfo(uri, &playlist->streaminfo)) {
- FLAC__metadata_object_delete(playlist->cuesheet);
- g_free(playlist);
- return NULL;
- }
-
- playlist->uri = g_strdup(uri);
- playlist->cuesheet = cuesheet;
- playlist->next_track = 0;
-
- return &playlist->base;
-}
-
-static void
-flac_playlist_close(struct playlist_provider *_playlist)
-{
- struct flac_playlist *playlist = (struct flac_playlist *)_playlist;
-
- g_free(playlist->uri);
- FLAC__metadata_object_delete(playlist->cuesheet);
- g_free(playlist);
-}
-
-static struct song *
-flac_playlist_read(struct playlist_provider *_playlist)
-{
- struct flac_playlist *playlist = (struct flac_playlist *)_playlist;
- const FLAC__StreamMetadata_CueSheet *cs =
- &playlist->cuesheet->data.cue_sheet;
-
- /* find the next audio track */
-
- while (playlist->next_track < cs->num_tracks &&
- (cs->tracks[playlist->next_track].number > cs->num_tracks ||
- cs->tracks[playlist->next_track].type != 0))
- ++playlist->next_track;
-
- if (playlist->next_track >= cs->num_tracks)
- return NULL;
-
- FLAC__uint64 start = cs->tracks[playlist->next_track].offset;
- ++playlist->next_track;
- FLAC__uint64 end = playlist->next_track < cs->num_tracks
- ? cs->tracks[playlist->next_track].offset
- : playlist->streaminfo.data.stream_info.total_samples;
-
- struct song *song = song_file_new(playlist->uri, NULL);
- song->start_ms = start * 1000 /
- playlist->streaminfo.data.stream_info.sample_rate;
- song->end_ms = end * 1000 /
- playlist->streaminfo.data.stream_info.sample_rate;
-
- char track[16];
- g_snprintf(track, sizeof(track), "%u", playlist->next_track);
- song->tag = flac_tag_load(playlist->uri, track);
- if (song->tag == NULL)
- song->tag = tag_new();
-
- song->tag->time = end > start
- ? ((end - start - 1 +
- playlist->streaminfo.data.stream_info.sample_rate) /
- playlist->streaminfo.data.stream_info.sample_rate)
- : 0;
-
- tag_clear_items_by_type(song->tag, TAG_TRACK);
- tag_add_item(song->tag, TAG_TRACK, track);
-
- return song;
-}
-
-static const char *const flac_playlist_suffixes[] = {
- "flac",
- NULL
-};
-
-static const char *const flac_playlist_mime_types[] = {
- "application/flac",
- "application/x-flac",
- "audio/flac",
- "audio/x-flac",
- NULL
-};
-
-const struct playlist_plugin flac_playlist_plugin = {
- .name = "flac",
-
- .open_uri = flac_playlist_open_uri,
- .close = flac_playlist_close,
- .read = flac_playlist_read,
-
- .suffixes = flac_playlist_suffixes,
- .mime_types = flac_playlist_mime_types,
-};
-
-#else /* FLAC_API_VERSION_CURRENT <= 7 */
-
-static bool
-flac_playlist_init(G_GNUC_UNUSED const struct config_param *param)
-{
- /* this libFLAC version does not support embedded CUE sheets;
- disable this plugin */
- return false;
-}
-
-const struct playlist_plugin flac_playlist_plugin = {
- .name = "flac",
- .init = flac_playlist_init,
-};
-
-#endif
diff --git a/src/playlist/lastfm_playlist_plugin.c b/src/playlist/lastfm_playlist_plugin.c
index afb3979d9..86113643c 100644
--- a/src/playlist/lastfm_playlist_plugin.c
+++ b/src/playlist/lastfm_playlist_plugin.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2010 The Music Player Daemon Project
+ * Copyright (C) 2003-2011 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -83,15 +83,14 @@ lastfm_finish(void)
* @return data fetched, or NULL on error. Must be freed with g_free.
*/
static char *
-lastfm_get(const char *url)
+lastfm_get(const char *url, GMutex *mutex, GCond *cond)
{
struct input_stream *input_stream;
GError *error = NULL;
- int ret;
char buffer[4096];
size_t length = 0, nbytes;
- input_stream = input_stream_open(url, &error);
+ input_stream = input_stream_open(url, mutex, cond, &error);
if (input_stream == NULL) {
if (error != NULL) {
g_warning("%s", error->message);
@@ -101,15 +100,9 @@ lastfm_get(const char *url)
return NULL;
}
- while (!input_stream->ready) {
- ret = input_stream_buffer(input_stream, &error);
- if (ret < 0) {
- input_stream_close(input_stream);
- g_warning("%s", error->message);
- g_error_free(error);
- return NULL;
- }
- }
+ g_mutex_lock(mutex);
+
+ input_stream_wait_ready(input_stream);
do {
nbytes = input_stream_read(input_stream, buffer + length,
@@ -124,6 +117,7 @@ lastfm_get(const char *url)
break;
/* I/O error */
+ g_mutex_unlock(mutex);
input_stream_close(input_stream);
return NULL;
}
@@ -131,6 +125,8 @@ lastfm_get(const char *url)
length += nbytes;
} while (length < sizeof(buffer));
+ g_mutex_unlock(mutex);
+
input_stream_close(input_stream);
return g_strndup(buffer, length);
}
@@ -139,7 +135,7 @@ lastfm_get(const char *url)
* Ini-style value fetcher.
* @param response data through which to search.
* @param name name of value to search for.
- * @return value for param name in param reponse or NULL on error. Free with g_free.
+ * @return value for param name in param response or NULL on error. Free with g_free.
*/
static char *
lastfm_find(const char *response, const char *name)
@@ -162,7 +158,7 @@ lastfm_find(const char *response, const char *name)
}
static struct playlist_provider *
-lastfm_open_uri(const char *uri)
+lastfm_open_uri(const char *uri, GMutex *mutex, GCond *cond)
{
struct lastfm_playlist *playlist;
GError *error = NULL;
@@ -175,7 +171,7 @@ lastfm_open_uri(const char *uri)
"username=", lastfm_config.user, "&"
"passwordmd5=", lastfm_config.md5, "&"
"debug=0&partner=", NULL);
- response = lastfm_get(p);
+ response = lastfm_get(p, mutex, cond);
g_free(p);
if (response == NULL)
return NULL;
@@ -207,7 +203,7 @@ lastfm_open_uri(const char *uri)
NULL);
g_free(escaped_uri);
- response = lastfm_get(p);
+ response = lastfm_get(p, mutex, cond);
g_free(response);
g_free(p);
@@ -229,7 +225,7 @@ lastfm_open_uri(const char *uri)
NULL);
g_free(session);
- playlist->is = input_stream_open(p, &error);
+ playlist->is = input_stream_open(p, mutex, cond, &error);
g_free(p);
if (playlist->is == NULL) {
@@ -243,26 +239,17 @@ lastfm_open_uri(const char *uri)
return NULL;
}
- while (!playlist->is->ready) {
- int ret = input_stream_buffer(playlist->is, &error);
- if (ret < 0) {
- input_stream_close(playlist->is);
- g_free(playlist);
- g_warning("%s", error->message);
- g_error_free(error);
- return NULL;
- }
+ g_mutex_lock(mutex);
- if (ret == 0)
- /* nothing was buffered - wait */
- g_usleep(10000);
- }
+ input_stream_wait_ready(playlist->is);
/* last.fm does not send a MIME type, we have to fake it here
:-( */
g_free(playlist->is->mime);
playlist->is->mime = g_strdup("application/xspf+xml");
+ g_mutex_unlock(mutex);
+
/* parse the XSPF playlist */
playlist->xspf = playlist_list_open_stream(playlist->is, NULL);
diff --git a/src/playlist/lastfm_playlist_plugin.h b/src/playlist/lastfm_playlist_plugin.h
index 363377c21..46a8b0caf 100644
--- a/src/playlist/lastfm_playlist_plugin.h
+++ b/src/playlist/lastfm_playlist_plugin.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2010 The Music Player Daemon Project
+ * Copyright (C) 2003-2011 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/playlist/m3u_playlist_plugin.c b/src/playlist/m3u_playlist_plugin.c
index 221c27277..45b70d2b1 100644
--- a/src/playlist/m3u_playlist_plugin.c
+++ b/src/playlist/m3u_playlist_plugin.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2010 The Music Player Daemon Project
+ * Copyright (C) 2003-2011 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/playlist/m3u_playlist_plugin.h b/src/playlist/m3u_playlist_plugin.h
index 98dcc4729..3890a5fc2 100644
--- a/src/playlist/m3u_playlist_plugin.h
+++ b/src/playlist/m3u_playlist_plugin.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2010 The Music Player Daemon Project
+ * Copyright (C) 2003-2011 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/playlist/pls_playlist_plugin.c b/src/playlist/pls_playlist_plugin.c
index 2a36f12f5..c4e5492af 100644
--- a/src/playlist/pls_playlist_plugin.c
+++ b/src/playlist/pls_playlist_plugin.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2010 The Music Player Daemon Project
+ * Copyright (C) 2003-2011 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -115,7 +115,8 @@ pls_open_stream(struct input_stream *is)
GString *kf_data = g_string_new("");
do {
- nbytes = input_stream_read(is, buffer, sizeof(buffer), &error);
+ nbytes = input_stream_lock_read(is, buffer, sizeof(buffer),
+ &error);
if (nbytes == 0) {
if (error != NULL) {
g_string_free(kf_data, TRUE);
diff --git a/src/playlist/pls_playlist_plugin.h b/src/playlist/pls_playlist_plugin.h
index c3bcf3f05..d03435f6d 100644
--- a/src/playlist/pls_playlist_plugin.h
+++ b/src/playlist/pls_playlist_plugin.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2010 The Music Player Daemon Project
+ * Copyright (C) 2003-2011 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/playlist/rss_playlist_plugin.c b/src/playlist/rss_playlist_plugin.c
index b5787bb68..6740cba7e 100644
--- a/src/playlist/rss_playlist_plugin.c
+++ b/src/playlist/rss_playlist_plugin.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2010 The Music Player Daemon Project
+ * Copyright (C) 2003-2011 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -231,7 +231,8 @@ rss_open_stream(struct input_stream *is)
&parser, rss_parser_destroy);
while (true) {
- nbytes = input_stream_read(is, buffer, sizeof(buffer), &error);
+ nbytes = input_stream_lock_read(is, buffer, sizeof(buffer),
+ &error);
if (nbytes == 0) {
if (error != NULL) {
g_markup_parse_context_free(context);
diff --git a/src/playlist/rss_playlist_plugin.h b/src/playlist/rss_playlist_plugin.h
index d8992f2e5..3b376de79 100644
--- a/src/playlist/rss_playlist_plugin.h
+++ b/src/playlist/rss_playlist_plugin.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2010 The Music Player Daemon Project
+ * Copyright (C) 2003-2011 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/playlist/soundcloud_playlist_plugin.c b/src/playlist/soundcloud_playlist_plugin.c
new file mode 100644
index 000000000..7c79f880a
--- /dev/null
+++ b/src/playlist/soundcloud_playlist_plugin.c
@@ -0,0 +1,448 @@
+/*
+ * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+#include "playlist/soundcloud_playlist_plugin.h"
+#include "conf.h"
+#include "input_stream.h"
+#include "playlist_plugin.h"
+#include "song.h"
+#include "tag.h"
+
+#include <glib.h>
+#include <yajl/yajl_parse.h>
+
+#include <string.h>
+
+struct soundcloud_playlist {
+ struct playlist_provider base;
+
+ GSList *songs;
+};
+
+static struct {
+ char *apikey;
+} soundcloud_config;
+
+static bool
+soundcloud_init(const struct config_param *param)
+{
+ soundcloud_config.apikey =
+ config_dup_block_string(param, "apikey", NULL);
+ if (soundcloud_config.apikey == NULL) {
+ g_debug("disabling the soundcloud playlist plugin "
+ "because API key is not set");
+ return false;
+ }
+
+ return true;
+}
+
+static void
+soundcloud_finish(void)
+{
+ g_free(soundcloud_config.apikey);
+}
+
+/**
+ * Construct a full soundcloud resolver URL from the given fragment.
+ * @param uri uri of a soundcloud page (or just the path)
+ * @return Constructed URL. Must be freed with g_free.
+ */
+static char *
+soundcloud_resolve(const char* uri) {
+ char *u, *ru;
+
+ if (g_str_has_prefix(uri, "http://")) {
+ u = g_strdup(uri);
+ } else if (g_str_has_prefix(uri, "soundcloud.com")) {
+ u = g_strconcat("http://", uri, NULL);
+ } else {
+ /* assume it's just a path on soundcloud.com */
+ u = g_strconcat("http://soundcloud.com/", uri, NULL);
+ }
+
+ ru = g_strconcat("http://api.soundcloud.com/resolve.json?url=",
+ u, "&client_id=", soundcloud_config.apikey, NULL);
+ g_free(u);
+
+ return ru;
+}
+
+/* YAJL parser for track data from both /tracks/ and /playlists/ JSON */
+
+enum key {
+ Duration,
+ Title,
+ Stream_URL,
+ Other,
+};
+
+const char* key_str[] = {
+ "duration",
+ "title",
+ "stream_url",
+ NULL,
+};
+
+struct parse_data {
+ int key;
+ char* stream_url;
+ long duration;
+ char* title;
+ int got_url; /* nesting level of last stream_url */
+ GSList* songs;
+};
+
+static int handle_integer(void *ctx,
+ long
+#ifndef HAVE_YAJL1
+ long
+#endif
+ intval)
+{
+ struct parse_data *data = (struct parse_data *) ctx;
+
+ switch (data->key) {
+ case Duration:
+ data->duration = intval;
+ break;
+ default:
+ break;
+ }
+
+ return 1;
+}
+
+static int handle_string(void *ctx, const unsigned char* stringval,
+#ifdef HAVE_YAJL1
+ unsigned int
+#else
+ size_t
+#endif
+ stringlen)
+{
+ struct parse_data *data = (struct parse_data *) ctx;
+ const char *s = (const char *) stringval;
+
+ switch (data->key) {
+ case Title:
+ if (data->title != NULL)
+ g_free(data->title);
+ data->title = g_strndup(s, stringlen);
+ break;
+ case Stream_URL:
+ if (data->stream_url != NULL)
+ g_free(data->stream_url);
+ data->stream_url = g_strndup(s, stringlen);
+ data->got_url = 1;
+ break;
+ default:
+ break;
+ }
+
+ return 1;
+}
+
+static int handle_mapkey(void *ctx, const unsigned char* stringval,
+#ifdef HAVE_YAJL1
+ unsigned int
+#else
+ size_t
+#endif
+ stringlen)
+{
+ struct parse_data *data = (struct parse_data *) ctx;
+
+ int i;
+ data->key = Other;
+
+ for (i = 0; i < Other; ++i) {
+ if (strncmp((const char *)stringval, key_str[i], stringlen) == 0) {
+ data->key = i;
+ break;
+ }
+ }
+
+ return 1;
+}
+
+static int handle_start_map(void *ctx)
+{
+ struct parse_data *data = (struct parse_data *) ctx;
+
+ if (data->got_url > 0)
+ data->got_url++;
+
+ return 1;
+}
+
+static int handle_end_map(void *ctx)
+{
+ struct parse_data *data = (struct parse_data *) ctx;
+
+ if (data->got_url > 1) {
+ data->got_url--;
+ return 1;
+ }
+
+ if (data->got_url == 0)
+ return 1;
+
+ /* got_url == 1, track finished, make it into a song */
+ data->got_url = 0;
+
+ struct song *s;
+ struct tag *t;
+ char *u;
+
+ u = g_strconcat(data->stream_url, "?client_id=", soundcloud_config.apikey, NULL);
+ s = song_remote_new(u);
+ g_free(u);
+ t = tag_new();
+ t->time = data->duration / 1000;
+ if (data->title != NULL)
+ tag_add_item(t, TAG_NAME, data->title);
+ s->tag = t;
+
+ data->songs = g_slist_prepend(data->songs, s);
+
+ return 1;
+}
+
+static yajl_callbacks parse_callbacks = {
+ NULL,
+ NULL,
+ handle_integer,
+ NULL,
+ NULL,
+ handle_string,
+ handle_start_map,
+ handle_mapkey,
+ handle_end_map,
+ NULL,
+ NULL,
+};
+
+/**
+ * Read JSON data and parse it using the given YAJL parser.
+ * @param url URL of the JSON data.
+ * @param hand YAJL parser handle.
+ * @return -1 on error, 0 on success.
+ */
+static int
+soundcloud_parse_json(const char *url, yajl_handle hand, GMutex* mutex, GCond* cond)
+{
+ struct input_stream *input_stream;
+ GError *error = NULL;
+ char buffer[4096];
+ unsigned char *ubuffer = (unsigned char *)buffer;
+ size_t nbytes;
+
+ input_stream = input_stream_open(url, mutex, cond, &error);
+ if (input_stream == NULL) {
+ if (error != NULL) {
+ g_warning("%s", error->message);
+ g_error_free(error);
+ }
+ return -1;
+ }
+
+ g_mutex_lock(mutex);
+ input_stream_wait_ready(input_stream);
+
+ yajl_status stat;
+ int done = 0;
+
+ while (!done) {
+ nbytes = input_stream_read(input_stream, buffer, sizeof(buffer), &error);
+ if (nbytes == 0) {
+ if (error != NULL) {
+ g_warning("%s", error->message);
+ g_error_free(error);
+ }
+ if (input_stream_eof(input_stream)) {
+ done = true;
+ } else {
+ g_mutex_unlock(mutex);
+ input_stream_close(input_stream);
+ return -1;
+ }
+ }
+
+ if (done) {
+#ifdef HAVE_YAJL1
+ stat = yajl_parse_complete(hand);
+#else
+ stat = yajl_complete_parse(hand);
+#endif
+ } else
+ stat = yajl_parse(hand, ubuffer, nbytes);
+
+ if (stat != yajl_status_ok
+#ifdef HAVE_YAJL1
+ && stat != yajl_status_insufficient_data
+#endif
+ )
+ {
+ unsigned char *str = yajl_get_error(hand, 1, ubuffer, nbytes);
+ g_warning("%s", str);
+ yajl_free_error(hand, str);
+ break;
+ }
+ }
+
+ g_mutex_unlock(mutex);
+ input_stream_close(input_stream);
+
+ return 0;
+}
+
+/**
+ * Parse a soundcloud:// URL and create a playlist.
+ * @param uri A soundcloud URL. Accepted forms:
+ * soundcloud://track/<track-id>
+ * soundcloud://playlist/<playlist-id>
+ * soundcloud://url/<url or path of soundcloud page>
+ */
+
+static struct playlist_provider *
+soundcloud_open_uri(const char *uri, GMutex *mutex, GCond *cond)
+{
+ struct soundcloud_playlist *playlist = NULL;
+
+ char *s, *p;
+ char *scheme, *arg, *rest;
+ s = g_strdup(uri);
+ scheme = s;
+ for (p = s; *p; p++) {
+ if (*p == ':' && *(p+1) == '/' && *(p+2) == '/') {
+ *p = 0;
+ p += 3;
+ break;
+ }
+ }
+ arg = p;
+ for (; *p; p++) {
+ if (*p == '/') {
+ *p = 0;
+ p++;
+ break;
+ }
+ }
+ rest = p;
+
+ if (strcmp(scheme, "soundcloud") != 0) {
+ g_warning("incompatible scheme for soundcloud plugin: %s", scheme);
+ g_free(s);
+ return NULL;
+ }
+
+ char *u = NULL;
+ if (strcmp(arg, "track") == 0) {
+ u = g_strconcat("http://api.soundcloud.com/tracks/",
+ rest, ".json?client_id=", soundcloud_config.apikey, NULL);
+ } else if (strcmp(arg, "playlist") == 0) {
+ u = g_strconcat("http://api.soundcloud.com/playlists/",
+ rest, ".json?client_id=", soundcloud_config.apikey, NULL);
+ } else if (strcmp(arg, "url") == 0) {
+ /* Translate to soundcloud resolver call. libcurl will automatically
+ follow the redirect to the right resource. */
+ u = soundcloud_resolve(rest);
+ }
+ g_free(s);
+
+ if (u == NULL) {
+ g_warning("unknown soundcloud URI");
+ return NULL;
+ }
+
+ yajl_handle hand;
+ struct parse_data data;
+
+ data.got_url = 0;
+ data.songs = NULL;
+ data.title = NULL;
+ data.stream_url = NULL;
+#ifdef HAVE_YAJL1
+ hand = yajl_alloc(&parse_callbacks, NULL, NULL, (void *) &data);
+#else
+ hand = yajl_alloc(&parse_callbacks, NULL, (void *) &data);
+#endif
+
+ int ret = soundcloud_parse_json(u, hand, mutex, cond);
+
+ g_free(u);
+ yajl_free(hand);
+ if (data.title != NULL)
+ g_free(data.title);
+ if (data.stream_url != NULL)
+ g_free(data.stream_url);
+
+ if (ret == -1)
+ return NULL;
+
+ playlist = g_new(struct soundcloud_playlist, 1);
+ playlist_provider_init(&playlist->base, &soundcloud_playlist_plugin);
+ playlist->songs = g_slist_reverse(data.songs);
+
+ return &playlist->base;
+}
+
+static void
+soundcloud_close(struct playlist_provider *_playlist)
+{
+ struct soundcloud_playlist *playlist = (struct soundcloud_playlist *)_playlist;
+
+ g_free(playlist);
+}
+
+
+static struct song *
+soundcloud_read(struct playlist_provider *_playlist)
+{
+ struct soundcloud_playlist *playlist = (struct soundcloud_playlist *)_playlist;
+
+ if (playlist->songs == NULL)
+ return NULL;
+
+ struct song* s;
+ s = (struct song *)playlist->songs->data;
+ playlist->songs = g_slist_remove(playlist->songs, s);
+ return s;
+}
+
+static const char *const soundcloud_schemes[] = {
+ "soundcloud",
+ NULL
+};
+
+const struct playlist_plugin soundcloud_playlist_plugin = {
+ .name = "soundcloud",
+
+ .init = soundcloud_init,
+ .finish = soundcloud_finish,
+ .open_uri = soundcloud_open_uri,
+ .close = soundcloud_close,
+ .read = soundcloud_read,
+
+ .schemes = soundcloud_schemes,
+};
+
+
diff --git a/src/playlist/soundcloud_playlist_plugin.h b/src/playlist/soundcloud_playlist_plugin.h
new file mode 100644
index 000000000..e09e2dd46
--- /dev/null
+++ b/src/playlist/soundcloud_playlist_plugin.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_PLAYLIST_SOUNDCLOUD_PLAYLIST_PLUGIN_H
+#define MPD_PLAYLIST_SOUNDCLOUD_PLAYLIST_PLUGIN_H
+
+extern const struct playlist_plugin soundcloud_playlist_plugin;
+
+#endif
diff --git a/src/playlist/xspf_playlist_plugin.c b/src/playlist/xspf_playlist_plugin.c
index 50f6bd1e7..17d9040e2 100644
--- a/src/playlist/xspf_playlist_plugin.c
+++ b/src/playlist/xspf_playlist_plugin.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2010 The Music Player Daemon Project
+ * Copyright (C) 2003-2011 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -253,7 +253,8 @@ xspf_open_stream(struct input_stream *is)
&parser, xspf_parser_destroy);
while (true) {
- nbytes = input_stream_read(is, buffer, sizeof(buffer), &error);
+ nbytes = input_stream_lock_read(is, buffer, sizeof(buffer),
+ &error);
if (nbytes == 0) {
if (error != NULL) {
g_markup_parse_context_free(context);
diff --git a/src/playlist/xspf_playlist_plugin.h b/src/playlist/xspf_playlist_plugin.h
index ea832207d..4636d7e83 100644
--- a/src/playlist/xspf_playlist_plugin.h
+++ b/src/playlist/xspf_playlist_plugin.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2010 The Music Player Daemon Project
+ * Copyright (C) 2003-2011 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/playlist_any.c b/src/playlist_any.c
index 39e21b178..450ca5932 100644
--- a/src/playlist_any.c
+++ b/src/playlist_any.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2010 The Music Player Daemon Project
+ * Copyright (C) 2003-2011 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -27,18 +27,20 @@
#include <assert.h>
static struct playlist_provider *
-playlist_open_remote(const char *uri, struct input_stream **is_r)
+playlist_open_remote(const char *uri, GMutex *mutex, GCond *cond,
+ struct input_stream **is_r)
{
assert(uri_has_scheme(uri));
- struct playlist_provider *playlist = playlist_list_open_uri(uri);
+ struct playlist_provider *playlist =
+ playlist_list_open_uri(uri, mutex, cond);
if (playlist != NULL) {
*is_r = NULL;
return playlist;
}
GError *error = NULL;
- struct input_stream *is = input_stream_open(uri, &error);
+ struct input_stream *is = input_stream_open(uri, mutex, cond, &error);
if (is == NULL) {
if (error != NULL) {
g_warning("Failed to open %s: %s",
@@ -60,9 +62,10 @@ playlist_open_remote(const char *uri, struct input_stream **is_r)
}
struct playlist_provider *
-playlist_open_any(const char *uri, struct input_stream **is_r)
+playlist_open_any(const char *uri, GMutex *mutex, GCond *cond,
+ struct input_stream **is_r)
{
return uri_has_scheme(uri)
- ? playlist_open_remote(uri, is_r)
- : playlist_mapper_open(uri, is_r);
+ ? playlist_open_remote(uri, mutex, cond, is_r)
+ : playlist_mapper_open(uri, mutex, cond, is_r);
}
diff --git a/src/playlist_any.h b/src/playlist_any.h
index 6fed97d15..310913de9 100644
--- a/src/playlist_any.h
+++ b/src/playlist_any.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2010 The Music Player Daemon Project
+ * Copyright (C) 2003-2011 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -20,7 +20,7 @@
#ifndef MPD_PLAYLIST_ANY_H
#define MPD_PLAYLIST_ANY_H
-#include <stdbool.h>
+#include <glib.h>
struct playlist_provider;
struct input_stream;
@@ -35,6 +35,7 @@ struct input_stream;
* freed
*/
struct playlist_provider *
-playlist_open_any(const char *uri, struct input_stream **is_r);
+playlist_open_any(const char *uri, GMutex *mutex, GCond *cond,
+ struct input_stream **is_r);
#endif
diff --git a/src/playlist_control.c b/src/playlist_control.c
index 76066d274..0dea7676a 100644
--- a/src/playlist_control.c
+++ b/src/playlist_control.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2010 The Music Player Daemon Project
+ * Copyright (C) 2003-2011 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -32,7 +32,8 @@
#undef G_LOG_DOMAIN
#define G_LOG_DOMAIN "playlist"
-void playlist_stop(struct playlist *playlist)
+void
+playlist_stop(struct playlist *playlist, struct player_control *pc)
{
if (!playlist->playing)
return;
@@ -40,7 +41,7 @@ void playlist_stop(struct playlist *playlist)
assert(playlist->current >= 0);
g_debug("stop");
- pc_stop();
+ pc_stop(pc);
playlist->queued = -1;
playlist->playing = false;
@@ -62,11 +63,13 @@ void playlist_stop(struct playlist *playlist)
}
}
-enum playlist_result playlist_play(struct playlist *playlist, int song)
+enum playlist_result
+playlist_play(struct playlist *playlist, struct player_control *pc,
+ int song)
{
unsigned i = song;
- pc_clear_error();
+ pc_clear_error(pc);
if (song == -1) {
/* play any song ("current" song, or the first song */
@@ -77,7 +80,7 @@ enum playlist_result playlist_play(struct playlist *playlist, int song)
if (playlist->playing) {
/* already playing: unpause playback, just in
case it was paused, and return */
- pc_set_pause(false);
+ pc_set_pause(pc, false);
return PLAYLIST_RESULT_SUCCESS;
}
@@ -109,28 +112,29 @@ enum playlist_result playlist_play(struct playlist *playlist, int song)
playlist->stop_on_error = false;
playlist->error_count = 0;
- playlist_play_order(playlist, i);
+ playlist_play_order(playlist, pc, i);
return PLAYLIST_RESULT_SUCCESS;
}
enum playlist_result
-playlist_play_id(struct playlist *playlist, int id)
+playlist_play_id(struct playlist *playlist, struct player_control *pc,
+ int id)
{
int song;
if (id == -1) {
- return playlist_play(playlist, id);
+ return playlist_play(playlist, pc, id);
}
song = queue_id_to_position(&playlist->queue, id);
if (song < 0)
return PLAYLIST_RESULT_NO_SUCH_SONG;
- return playlist_play(playlist, song);
+ return playlist_play(playlist, pc, song);
}
void
-playlist_next(struct playlist *playlist)
+playlist_next(struct playlist *playlist, struct player_control *pc)
{
int next_order;
int current;
@@ -149,7 +153,7 @@ playlist_next(struct playlist *playlist)
next_order = queue_next_order(&playlist->queue, playlist->current);
if (next_order < 0) {
/* no song after this one: stop playback */
- playlist_stop(playlist);
+ playlist_stop(playlist, pc);
/* reset "current song" */
playlist->current = -1;
@@ -170,15 +174,18 @@ playlist_next(struct playlist *playlist)
discard them anyway */
}
- playlist_play_order(playlist, next_order);
+ playlist_play_order(playlist, pc, next_order);
}
/* Consume mode removes each played songs. */
if(playlist->queue.consume)
- playlist_delete(playlist, queue_order_to_position(&playlist->queue, current));
+ playlist_delete(playlist, pc,
+ queue_order_to_position(&playlist->queue,
+ current));
}
-void playlist_previous(struct playlist *playlist)
+void
+playlist_previous(struct playlist *playlist, struct player_control *pc)
{
if (!playlist->playing)
return;
@@ -187,21 +194,22 @@ void playlist_previous(struct playlist *playlist)
if (playlist->current > 0) {
/* play the preceding song */
- playlist_play_order(playlist,
+ playlist_play_order(playlist, pc,
playlist->current - 1);
} else if (playlist->queue.repeat) {
/* play the last song in "repeat" mode */
- playlist_play_order(playlist,
+ playlist_play_order(playlist, pc,
queue_length(&playlist->queue) - 1);
} else {
/* re-start playing the current song if it's
the first one */
- playlist_play_order(playlist, playlist->current);
+ playlist_play_order(playlist, pc, playlist->current);
}
}
enum playlist_result
-playlist_seek_song(struct playlist *playlist, unsigned song, float seek_time)
+playlist_seek_song(struct playlist *playlist, struct player_control *pc,
+ unsigned song, float seek_time)
{
const struct song *queued;
unsigned i;
@@ -217,7 +225,7 @@ playlist_seek_song(struct playlist *playlist, unsigned song, float seek_time)
else
i = song;
- pc_clear_error();
+ pc_clear_error(pc);
playlist->stop_on_error = true;
playlist->error_count = 0;
@@ -231,25 +239,50 @@ playlist_seek_song(struct playlist *playlist, unsigned song, float seek_time)
queued = NULL;
}
- success = pc_seek(queue_get_order(&playlist->queue, i), seek_time);
+ success = pc_seek(pc, queue_get_order(&playlist->queue, i), seek_time);
if (!success) {
- playlist_update_queued_song(playlist, queued);
+ playlist_update_queued_song(playlist, pc, queued);
return PLAYLIST_RESULT_NOT_PLAYING;
}
playlist->queued = -1;
- playlist_update_queued_song(playlist, NULL);
+ playlist_update_queued_song(playlist, pc, NULL);
return PLAYLIST_RESULT_SUCCESS;
}
enum playlist_result
-playlist_seek_song_id(struct playlist *playlist, unsigned id, float seek_time)
+playlist_seek_song_id(struct playlist *playlist, struct player_control *pc,
+ unsigned id, float seek_time)
{
int song = queue_id_to_position(&playlist->queue, id);
if (song < 0)
return PLAYLIST_RESULT_NO_SUCH_SONG;
- return playlist_seek_song(playlist, song, seek_time);
+ return playlist_seek_song(playlist, pc, song, seek_time);
+}
+
+enum playlist_result
+playlist_seek_current(struct playlist *playlist, struct player_control *pc,
+ float seek_time, bool relative)
+{
+ if (!playlist->playing)
+ return PLAYLIST_RESULT_NOT_PLAYING;
+
+ if (relative) {
+ struct player_status status;
+ pc_get_status(pc, &status);
+
+ if (status.state != PLAYER_STATE_PLAY &&
+ status.state != PLAYER_STATE_PAUSE)
+ return PLAYLIST_RESULT_NOT_PLAYING;
+
+ seek_time += (int)status.elapsed_time;
+ }
+
+ if (seek_time < 0)
+ seek_time = 0;
+
+ return playlist_seek_song(playlist, pc, playlist->current, seek_time);
}
diff --git a/src/playlist_database.c b/src/playlist_database.c
index 0a8a6f139..6b9d87155 100644
--- a/src/playlist_database.c
+++ b/src/playlist_database.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2010 The Music Player Daemon Project
+ * Copyright (C) 2003-2011 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -21,6 +21,7 @@
#include "playlist_database.h"
#include "playlist_vector.h"
#include "text_file.h"
+#include "string_util.h"
#include <string.h>
#include <stdlib.h>
@@ -32,10 +33,10 @@ playlist_database_quark(void)
}
void
-playlist_vector_save(FILE *fp, const struct playlist_vector *pv)
+playlist_vector_save(FILE *fp, const struct list_head *pv)
{
- for (const struct playlist_metadata *pm = pv->head;
- pm != NULL; pm = pm->next)
+ struct playlist_metadata *pm;
+ playlist_vector_for_each(pm, pv)
fprintf(fp, PLAYLIST_META_BEGIN "%s\n"
"mtime: %li\n"
"playlist_end\n",
@@ -43,7 +44,7 @@ playlist_vector_save(FILE *fp, const struct playlist_vector *pv)
}
bool
-playlist_metadata_load(FILE *fp, struct playlist_vector *pv, const char *name,
+playlist_metadata_load(FILE *fp, struct list_head *pv, const char *name,
GString *buffer, GError **error_r)
{
struct playlist_metadata pm = {
@@ -62,7 +63,7 @@ playlist_metadata_load(FILE *fp, struct playlist_vector *pv, const char *name,
}
*colon++ = 0;
- value = g_strchug(colon);
+ value = strchug_fast_c(colon);
if (strcmp(line, "mtime") == 0)
pm.mtime = strtol(value, NULL, 10);
diff --git a/src/playlist_database.h b/src/playlist_database.h
index 7e114abdd..3238fa06b 100644
--- a/src/playlist_database.h
+++ b/src/playlist_database.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2010 The Music Player Daemon Project
+ * Copyright (C) 2003-2011 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -28,13 +28,13 @@
#define PLAYLIST_META_BEGIN "playlist_begin: "
-struct playlist_vector;
+struct list_head;
void
-playlist_vector_save(FILE *fp, const struct playlist_vector *pv);
+playlist_vector_save(FILE *fp, const struct list_head *pv);
bool
-playlist_metadata_load(FILE *fp, struct playlist_vector *pv, const char *name,
+playlist_metadata_load(FILE *fp, struct list_head *pv, const char *name,
GString *buffer, GError **error_r);
#endif
diff --git a/src/playlist_edit.c b/src/playlist_edit.c
index 3bcb2ce14..7adbccd7c 100644
--- a/src/playlist_edit.c
+++ b/src/playlist_edit.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2010 The Music Player Daemon Project
+ * Copyright (C) 2003-2011 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -31,9 +31,6 @@
#include "song.h"
#include "idle.h"
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <unistd.h>
#include <stdlib.h>
static void playlist_increment_version(struct playlist *playlist)
@@ -43,16 +40,17 @@ static void playlist_increment_version(struct playlist *playlist)
idle_add(IDLE_PLAYLIST);
}
-void playlist_clear(struct playlist *playlist)
+void
+playlist_clear(struct playlist *playlist, struct player_control *pc)
{
- playlist_stop(playlist);
+ playlist_stop(playlist, pc);
/* make sure there are no references to allocated songs
anymore */
for (unsigned i = 0; i < queue_length(&playlist->queue); i++) {
const struct song *song = queue_get(&playlist->queue, i);
if (!song_in_database(song))
- pc_song_deleted(song);
+ pc_song_deleted(pc, song);
}
queue_clear(&playlist->queue);
@@ -62,37 +60,19 @@ void playlist_clear(struct playlist *playlist)
playlist_increment_version(playlist);
}
-#ifndef WIN32
enum playlist_result
-playlist_append_file(struct playlist *playlist, const char *path, int uid,
- unsigned *added_id)
+playlist_append_file(struct playlist *playlist, struct player_control *pc,
+ const char *path_fs, unsigned *added_id)
{
- int ret;
- struct stat st;
- struct song *song;
-
- if (uid <= 0)
- /* unauthenticated client */
- return PLAYLIST_RESULT_DENIED;
-
- ret = stat(path, &st);
- if (ret < 0)
- return PLAYLIST_RESULT_ERRNO;
-
- if (st.st_uid != (uid_t)uid && (st.st_mode & 0444) != 0444)
- /* client is not owner */
- return PLAYLIST_RESULT_DENIED;
-
- song = song_file_load(path, NULL);
+ struct song *song = song_file_load(path_fs, NULL);
if (song == NULL)
return PLAYLIST_RESULT_NO_SUCH_SONG;
- return playlist_append_song(playlist, song, added_id);
+ return playlist_append_song(playlist, pc, song, added_id);
}
-#endif
enum playlist_result
-playlist_append_song(struct playlist *playlist,
+playlist_append_song(struct playlist *playlist, struct player_control *pc,
struct song *song, unsigned *added_id)
{
const struct song *queued;
@@ -121,7 +101,7 @@ playlist_append_song(struct playlist *playlist,
playlist_increment_version(playlist);
- playlist_update_queued_song(playlist, queued);
+ playlist_update_queued_song(playlist, pc, queued);
if (added_id)
*added_id = id;
@@ -145,8 +125,8 @@ song_by_uri(const char *uri)
}
enum playlist_result
-playlist_append_uri(struct playlist *playlist, const char *uri,
- unsigned *added_id)
+playlist_append_uri(struct playlist *playlist, struct player_control *pc,
+ const char *uri, unsigned *added_id)
{
struct song *song;
@@ -156,11 +136,12 @@ playlist_append_uri(struct playlist *playlist, const char *uri,
if (song == NULL)
return PLAYLIST_RESULT_NO_SUCH_SONG;
- return playlist_append_song(playlist, song, added_id);
+ return playlist_append_song(playlist, pc, song, added_id);
}
enum playlist_result
-playlist_swap_songs(struct playlist *playlist, unsigned song1, unsigned song2)
+playlist_swap_songs(struct playlist *playlist, struct player_control *pc,
+ unsigned song1, unsigned song2)
{
const struct song *queued;
@@ -192,13 +173,14 @@ playlist_swap_songs(struct playlist *playlist, unsigned song1, unsigned song2)
playlist_increment_version(playlist);
- playlist_update_queued_song(playlist, queued);
+ playlist_update_queued_song(playlist, pc, queued);
return PLAYLIST_RESULT_SUCCESS;
}
enum playlist_result
-playlist_swap_songs_id(struct playlist *playlist, unsigned id1, unsigned id2)
+playlist_swap_songs_id(struct playlist *playlist, struct player_control *pc,
+ unsigned id1, unsigned id2)
{
int song1 = queue_id_to_position(&playlist->queue, id1);
int song2 = queue_id_to_position(&playlist->queue, id2);
@@ -206,12 +188,67 @@ playlist_swap_songs_id(struct playlist *playlist, unsigned id1, unsigned id2)
if (song1 < 0 || song2 < 0)
return PLAYLIST_RESULT_NO_SUCH_SONG;
- return playlist_swap_songs(playlist, song1, song2);
+ return playlist_swap_songs(playlist, pc, song1, song2);
+}
+
+enum playlist_result
+playlist_set_priority(struct playlist *playlist, struct player_control *pc,
+ unsigned start, unsigned end,
+ uint8_t priority)
+{
+ if (start >= queue_length(&playlist->queue))
+ return PLAYLIST_RESULT_BAD_RANGE;
+
+ if (end > queue_length(&playlist->queue))
+ end = queue_length(&playlist->queue);
+
+ if (start >= end)
+ return PLAYLIST_RESULT_SUCCESS;
+
+ /* remember "current" and "queued" */
+
+ int current_position = playlist->current >= 0
+ ? (int)queue_order_to_position(&playlist->queue,
+ playlist->current)
+ : -1;
+
+ const struct song *queued = playlist_get_queued_song(playlist);
+
+ /* apply the priority changes */
+
+ queue_set_priority_range(&playlist->queue, start, end, priority,
+ playlist->current);
+
+ playlist_increment_version(playlist);
+
+ /* restore "current" and choose a new "queued" */
+
+ if (current_position >= 0)
+ playlist->current = queue_position_to_order(&playlist->queue,
+ current_position);
+
+ playlist_update_queued_song(playlist, pc, queued);
+
+ return PLAYLIST_RESULT_SUCCESS;
+}
+
+enum playlist_result
+playlist_set_priority_id(struct playlist *playlist, struct player_control *pc,
+ unsigned song_id, uint8_t priority)
+{
+ int song_position = queue_id_to_position(&playlist->queue, song_id);
+ if (song_position < 0)
+ return PLAYLIST_RESULT_NO_SUCH_SONG;
+
+ return playlist_set_priority(playlist, pc,
+ song_position, song_position + 1,
+ priority);
+
}
static void
-playlist_delete_internal(struct playlist *playlist, unsigned song,
- const struct song **queued_p)
+playlist_delete_internal(struct playlist *playlist, struct player_control *pc,
+ unsigned song, const struct song **queued_p)
{
unsigned songOrder;
@@ -220,11 +257,11 @@ playlist_delete_internal(struct playlist *playlist, unsigned song,
songOrder = queue_position_to_order(&playlist->queue, song);
if (playlist->playing && playlist->current == (int)songOrder) {
- bool paused = pc_get_state() == PLAYER_STATE_PAUSE;
+ bool paused = pc_get_state(pc) == PLAYER_STATE_PAUSE;
/* the current song is going to be deleted: stop the player */
- pc_stop();
+ pc_stop(pc);
playlist->playing = false;
/* see which song is going to be played instead */
@@ -236,11 +273,11 @@ playlist_delete_internal(struct playlist *playlist, unsigned song,
if (playlist->current >= 0 && !paused)
/* play the song after the deleted one */
- playlist_play_order(playlist, playlist->current);
+ playlist_play_order(playlist, pc, playlist->current);
else
/* no songs left to play, stop playback
completely */
- playlist_stop(playlist);
+ playlist_stop(playlist, pc);
*queued_p = NULL;
} else if (playlist->current == (int)songOrder)
@@ -251,7 +288,7 @@ playlist_delete_internal(struct playlist *playlist, unsigned song,
/* now do it: remove the song */
if (!song_in_database(queue_get(&playlist->queue, song)))
- pc_song_deleted(queue_get(&playlist->queue, song));
+ pc_song_deleted(pc, queue_get(&playlist->queue, song));
queue_delete(&playlist->queue, song);
@@ -263,7 +300,8 @@ playlist_delete_internal(struct playlist *playlist, unsigned song,
}
enum playlist_result
-playlist_delete(struct playlist *playlist, unsigned song)
+playlist_delete(struct playlist *playlist, struct player_control *pc,
+ unsigned song)
{
const struct song *queued;
@@ -272,16 +310,17 @@ playlist_delete(struct playlist *playlist, unsigned song)
queued = playlist_get_queued_song(playlist);
- playlist_delete_internal(playlist, song, &queued);
+ playlist_delete_internal(playlist, pc, song, &queued);
playlist_increment_version(playlist);
- playlist_update_queued_song(playlist, queued);
+ playlist_update_queued_song(playlist, pc, queued);
return PLAYLIST_RESULT_SUCCESS;
}
enum playlist_result
-playlist_delete_range(struct playlist *playlist, unsigned start, unsigned end)
+playlist_delete_range(struct playlist *playlist, struct player_control *pc,
+ unsigned start, unsigned end)
{
const struct song *queued;
@@ -297,37 +336,39 @@ playlist_delete_range(struct playlist *playlist, unsigned start, unsigned end)
queued = playlist_get_queued_song(playlist);
do {
- playlist_delete_internal(playlist, --end, &queued);
+ playlist_delete_internal(playlist, pc, --end, &queued);
} while (end != start);
playlist_increment_version(playlist);
- playlist_update_queued_song(playlist, queued);
+ playlist_update_queued_song(playlist, pc, queued);
return PLAYLIST_RESULT_SUCCESS;
}
enum playlist_result
-playlist_delete_id(struct playlist *playlist, unsigned id)
+playlist_delete_id(struct playlist *playlist, struct player_control *pc,
+ unsigned id)
{
int song = queue_id_to_position(&playlist->queue, id);
if (song < 0)
return PLAYLIST_RESULT_NO_SUCH_SONG;
- return playlist_delete(playlist, song);
+ return playlist_delete(playlist, pc, song);
}
void
-playlist_delete_song(struct playlist *playlist, const struct song *song)
+playlist_delete_song(struct playlist *playlist, struct player_control *pc,
+ const struct song *song)
{
for (int i = queue_length(&playlist->queue) - 1; i >= 0; --i)
if (song == queue_get(&playlist->queue, i))
- playlist_delete(playlist, i);
+ playlist_delete(playlist, pc, i);
- pc_song_deleted(song);
+ pc_song_deleted(pc, song);
}
enum playlist_result
-playlist_move_range(struct playlist *playlist,
+playlist_move_range(struct playlist *playlist, struct player_control *pc,
unsigned start, unsigned end, int to)
{
const struct song *queued;
@@ -382,23 +423,25 @@ playlist_move_range(struct playlist *playlist,
playlist_increment_version(playlist);
- playlist_update_queued_song(playlist, queued);
+ playlist_update_queued_song(playlist, pc, queued);
return PLAYLIST_RESULT_SUCCESS;
}
enum playlist_result
-playlist_move_id(struct playlist *playlist, unsigned id1, int to)
+playlist_move_id(struct playlist *playlist, struct player_control *pc,
+ unsigned id1, int to)
{
int song = queue_id_to_position(&playlist->queue, id1);
if (song < 0)
return PLAYLIST_RESULT_NO_SUCH_SONG;
- return playlist_move_range(playlist, song, song+1, to);
+ return playlist_move_range(playlist, pc, song, song+1, to);
}
void
-playlist_shuffle(struct playlist *playlist, unsigned start, unsigned end)
+playlist_shuffle(struct playlist *playlist, struct player_control *pc,
+ unsigned start, unsigned end)
{
const struct song *queued;
@@ -440,5 +483,5 @@ playlist_shuffle(struct playlist *playlist, unsigned start, unsigned end)
playlist_increment_version(playlist);
- playlist_update_queued_song(playlist, queued);
+ playlist_update_queued_song(playlist, pc, queued);
}
diff --git a/src/playlist_error.h b/src/playlist_error.h
new file mode 100644
index 000000000..ad9c62cf1
--- /dev/null
+++ b/src/playlist_error.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_PLAYLIST_ERROR_H
+#define MPD_PLAYLIST_ERROR_H
+
+#include <glib.h>
+
+enum playlist_result {
+ PLAYLIST_RESULT_SUCCESS,
+ PLAYLIST_RESULT_ERRNO,
+ PLAYLIST_RESULT_DENIED,
+ PLAYLIST_RESULT_NO_SUCH_SONG,
+ PLAYLIST_RESULT_NO_SUCH_LIST,
+ PLAYLIST_RESULT_LIST_EXISTS,
+ PLAYLIST_RESULT_BAD_NAME,
+ PLAYLIST_RESULT_BAD_RANGE,
+ PLAYLIST_RESULT_NOT_PLAYING,
+ PLAYLIST_RESULT_TOO_LARGE,
+ PLAYLIST_RESULT_DISABLED,
+};
+
+/**
+ * Quark for GError.domain; the code is an enum #playlist_result.
+ */
+G_GNUC_CONST
+static inline GQuark
+playlist_quark(void)
+{
+ return g_quark_from_static_string("playlist");
+}
+
+#endif
diff --git a/src/playlist_global.c b/src/playlist_global.c
index 2833b62ed..650b88bb8 100644
--- a/src/playlist_global.c
+++ b/src/playlist_global.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2010 The Music Player Daemon Project
+ * Copyright (C) 2003-2011 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -26,6 +26,7 @@
#include "playlist.h"
#include "playlist_state.h"
#include "event_pipe.h"
+#include "main.h"
struct playlist g_playlist;
@@ -38,7 +39,7 @@ playlist_tag_event(void)
static void
playlist_event(void)
{
- playlist_sync(&g_playlist);
+ playlist_sync(&g_playlist, global_player_control);
}
void
diff --git a/src/playlist_internal.h b/src/playlist_internal.h
index 9d205188f..81b175176 100644
--- a/src/playlist_internal.h
+++ b/src/playlist_internal.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2010 The Music Player Daemon Project
+ * Copyright (C) 2003-2011 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -27,6 +27,8 @@
#include "playlist.h"
+struct player_control;
+
/**
* Returns the song object which is currently queued. Returns none if
* there is none (yet?) or if MPD isn't playing.
@@ -44,9 +46,11 @@ playlist_get_queued_song(struct playlist *playlist);
*/
void
playlist_update_queued_song(struct playlist *playlist,
+ struct player_control *pc,
const struct song *prev);
void
-playlist_play_order(struct playlist *playlist, int orderNum);
+playlist_play_order(struct playlist *playlist, struct player_control *pc,
+ int orderNum);
#endif
diff --git a/src/playlist_list.c b/src/playlist_list.c
index 019654bfc..64f03d46d 100644
--- a/src/playlist_list.c
+++ b/src/playlist_list.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2010 The Music Player Daemon Project
+ * Copyright (C) 2003-2011 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -24,14 +24,16 @@
#include "playlist/m3u_playlist_plugin.h"
#include "playlist/xspf_playlist_plugin.h"
#include "playlist/lastfm_playlist_plugin.h"
+#include "playlist/despotify_playlist_plugin.h"
+#include "playlist/soundcloud_playlist_plugin.h"
#include "playlist/pls_playlist_plugin.h"
#include "playlist/asx_playlist_plugin.h"
#include "playlist/rss_playlist_plugin.h"
#include "playlist/cue_playlist_plugin.h"
-#include "playlist/flac_playlist_plugin.h"
+#include "playlist/embcue_playlist_plugin.h"
#include "input_stream.h"
#include "uri.h"
-#include "utils.h"
+#include "string_util.h"
#include "conf.h"
#include "glib_compat.h"
#include "mpd_error.h"
@@ -47,15 +49,17 @@ static const struct playlist_plugin *const playlist_plugins[] = {
&pls_playlist_plugin,
&asx_playlist_plugin,
&rss_playlist_plugin,
+#ifdef ENABLE_DESPOTIFY
+ &despotify_playlist_plugin,
+#endif
#ifdef ENABLE_LASTFM
&lastfm_playlist_plugin,
#endif
-#ifdef HAVE_CUE
- &cue_playlist_plugin,
-#endif
-#ifdef HAVE_FLAC
- &flac_playlist_plugin,
+#ifdef ENABLE_SOUNDCLOUD
+ &soundcloud_playlist_plugin,
#endif
+ &cue_playlist_plugin,
+ &embcue_playlist_plugin,
NULL
};
@@ -115,7 +119,8 @@ playlist_list_global_finish(void)
}
static struct playlist_provider *
-playlist_list_open_uri_scheme(const char *uri, bool *tried)
+playlist_list_open_uri_scheme(const char *uri, GMutex *mutex, GCond *cond,
+ bool *tried)
{
char *scheme;
struct playlist_provider *playlist = NULL;
@@ -134,7 +139,8 @@ playlist_list_open_uri_scheme(const char *uri, bool *tried)
if (playlist_plugins_enabled[i] && plugin->open_uri != NULL &&
plugin->schemes != NULL &&
string_array_contains(plugin->schemes, scheme)) {
- playlist = playlist_plugin_open_uri(plugin, uri);
+ playlist = playlist_plugin_open_uri(plugin, uri,
+ mutex, cond);
if (playlist != NULL)
break;
@@ -147,7 +153,8 @@ playlist_list_open_uri_scheme(const char *uri, bool *tried)
}
static struct playlist_provider *
-playlist_list_open_uri_suffix(const char *uri, const bool *tried)
+playlist_list_open_uri_suffix(const char *uri, GMutex *mutex, GCond *cond,
+ const bool *tried)
{
const char *suffix;
struct playlist_provider *playlist = NULL;
@@ -164,7 +171,8 @@ playlist_list_open_uri_suffix(const char *uri, const bool *tried)
if (playlist_plugins_enabled[i] && !tried[i] &&
plugin->open_uri != NULL && plugin->suffixes != NULL &&
string_array_contains(plugin->suffixes, suffix)) {
- playlist = playlist_plugin_open_uri(plugin, uri);
+ playlist = playlist_plugin_open_uri(plugin, uri,
+ mutex, cond);
if (playlist != NULL)
break;
}
@@ -174,7 +182,7 @@ playlist_list_open_uri_suffix(const char *uri, const bool *tried)
}
struct playlist_provider *
-playlist_list_open_uri(const char *uri)
+playlist_list_open_uri(const char *uri, GMutex *mutex, GCond *cond)
{
struct playlist_provider *playlist;
/** this array tracks which plugins have already been tried by
@@ -185,9 +193,10 @@ playlist_list_open_uri(const char *uri)
memset(tried, false, sizeof(tried));
- playlist = playlist_list_open_uri_scheme(uri, tried);
+ playlist = playlist_list_open_uri_scheme(uri, mutex, cond, tried);
if (playlist == NULL)
- playlist = playlist_list_open_uri_suffix(uri, tried);
+ playlist = playlist_list_open_uri_suffix(uri, mutex, cond,
+ tried);
return playlist;
}
@@ -274,16 +283,7 @@ playlist_list_open_stream(struct input_stream *is, const char *uri)
const char *suffix;
struct playlist_provider *playlist;
- GError *error = NULL;
- while (!is->ready) {
- int ret = input_stream_buffer(is, &error);
- if (ret < 0) {
- input_stream_close(is);
- g_warning("%s", error->message);
- g_error_free(error);
- return NULL;
- }
- }
+ input_stream_lock_wait_ready(is);
if (is->mime != NULL) {
playlist = playlist_list_open_stream_mime(is);
@@ -318,7 +318,8 @@ playlist_suffix_supported(const char *suffix)
}
struct playlist_provider *
-playlist_list_open_path(const char *path_fs, struct input_stream **is_r)
+playlist_list_open_path(const char *path_fs, GMutex *mutex, GCond *cond,
+ struct input_stream **is_r)
{
GError *error = NULL;
const char *suffix;
@@ -331,7 +332,7 @@ playlist_list_open_path(const char *path_fs, struct input_stream **is_r)
if (suffix == NULL || !playlist_suffix_supported(suffix))
return NULL;
- is = input_stream_open(path_fs, &error);
+ is = input_stream_open(path_fs, mutex, cond, &error);
if (is == NULL) {
if (error != NULL) {
g_warning("%s", error->message);
@@ -341,15 +342,7 @@ playlist_list_open_path(const char *path_fs, struct input_stream **is_r)
return NULL;
}
- while (!is->ready) {
- int ret = input_stream_buffer(is, &error);
- if (ret < 0) {
- input_stream_close(is);
- g_warning("%s", error->message);
- g_error_free(error);
- return NULL;
- }
- }
+ input_stream_lock_wait_ready(is);
playlist = playlist_list_open_stream_suffix(is, suffix);
if (playlist != NULL)
diff --git a/src/playlist_list.h b/src/playlist_list.h
index 3710589a2..4a2485303 100644
--- a/src/playlist_list.h
+++ b/src/playlist_list.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2010 The Music Player Daemon Project
+ * Copyright (C) 2003-2011 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -20,6 +20,8 @@
#ifndef MPD_PLAYLIST_LIST_H
#define MPD_PLAYLIST_LIST_H
+#include <glib.h>
+
#include <stdbool.h>
struct playlist_provider;
@@ -41,7 +43,7 @@ playlist_list_global_finish(void);
* Opens a playlist by its URI.
*/
struct playlist_provider *
-playlist_list_open_uri(const char *uri);
+playlist_list_open_uri(const char *uri, GMutex *mutex, GCond *cond);
/**
* Opens a playlist from an input stream.
@@ -69,6 +71,7 @@ playlist_suffix_supported(const char *suffix);
* @return a playlist, or NULL on error
*/
struct playlist_provider *
-playlist_list_open_path(const char *path_fs, struct input_stream **is_r);
+playlist_list_open_path(const char *path_fs, GMutex *mutex, GCond *cond,
+ struct input_stream **is_r);
#endif
diff --git a/src/playlist_mapper.c b/src/playlist_mapper.c
index 99b322073..13adb80d0 100644
--- a/src/playlist_mapper.c
+++ b/src/playlist_mapper.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2010 The Music Player Daemon Project
+ * Copyright (C) 2003-2011 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -27,15 +27,16 @@
#include <assert.h>
static struct playlist_provider *
-playlist_open_path(const char *path_fs, struct input_stream **is_r)
+playlist_open_path(const char *path_fs, GMutex *mutex, GCond *cond,
+ struct input_stream **is_r)
{
struct playlist_provider *playlist;
- playlist = playlist_list_open_uri(path_fs);
+ playlist = playlist_list_open_uri(path_fs, mutex, cond);
if (playlist != NULL)
*is_r = NULL;
else
- playlist = playlist_list_open_path(path_fs, is_r);
+ playlist = playlist_list_open_path(path_fs, mutex, cond, is_r);
return playlist;
}
@@ -44,7 +45,8 @@ playlist_open_path(const char *path_fs, struct input_stream **is_r)
* Load a playlist from the configured playlist directory.
*/
static struct playlist_provider *
-playlist_open_in_playlist_dir(const char *uri, struct input_stream **is_r)
+playlist_open_in_playlist_dir(const char *uri, GMutex *mutex, GCond *cond,
+ struct input_stream **is_r)
{
char *path_fs;
@@ -56,7 +58,8 @@ playlist_open_in_playlist_dir(const char *uri, struct input_stream **is_r)
path_fs = g_build_filename(playlist_directory_fs, uri, NULL);
- struct playlist_provider *playlist = playlist_open_path(path_fs, is_r);
+ struct playlist_provider *playlist =
+ playlist_open_path(path_fs, mutex, cond, is_r);
g_free(path_fs);
return playlist;
@@ -66,7 +69,8 @@ playlist_open_in_playlist_dir(const char *uri, struct input_stream **is_r)
* Load a playlist from the configured music directory.
*/
static struct playlist_provider *
-playlist_open_in_music_dir(const char *uri, struct input_stream **is_r)
+playlist_open_in_music_dir(const char *uri, GMutex *mutex, GCond *cond,
+ struct input_stream **is_r)
{
char *path_fs;
@@ -76,25 +80,28 @@ playlist_open_in_music_dir(const char *uri, struct input_stream **is_r)
if (path_fs == NULL)
return NULL;
- struct playlist_provider *playlist = playlist_open_path(path_fs, is_r);
+ struct playlist_provider *playlist =
+ playlist_open_path(path_fs, mutex, cond, is_r);
g_free(path_fs);
return playlist;
}
struct playlist_provider *
-playlist_mapper_open(const char *uri, struct input_stream **is_r)
+playlist_mapper_open(const char *uri, GMutex *mutex, GCond *cond,
+ struct input_stream **is_r)
{
struct playlist_provider *playlist;
if (spl_valid_name(uri)) {
- playlist = playlist_open_in_playlist_dir(uri, is_r);
+ playlist = playlist_open_in_playlist_dir(uri, mutex, cond,
+ is_r);
if (playlist != NULL)
return playlist;
}
if (uri_safe_local(uri)) {
- playlist = playlist_open_in_music_dir(uri, is_r);
+ playlist = playlist_open_in_music_dir(uri, mutex, cond, is_r);
if (playlist != NULL)
return playlist;
}
diff --git a/src/playlist_mapper.h b/src/playlist_mapper.h
index b98af1b13..9a7187d93 100644
--- a/src/playlist_mapper.h
+++ b/src/playlist_mapper.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2010 The Music Player Daemon Project
+ * Copyright (C) 2003-2011 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -20,6 +20,8 @@
#ifndef MPD_PLAYLIST_MAPPER_H
#define MPD_PLAYLIST_MAPPER_H
+#include <glib.h>
+
struct input_stream;
/**
@@ -31,6 +33,7 @@ struct input_stream;
* freed
*/
struct playlist_provider *
-playlist_mapper_open(const char *uri, struct input_stream **is_r);
+playlist_mapper_open(const char *uri, GMutex *mutex, GCond *cond,
+ struct input_stream **is_r);
#endif
diff --git a/src/playlist_plugin.h b/src/playlist_plugin.h
index 3d840573e..a27f651c0 100644
--- a/src/playlist_plugin.h
+++ b/src/playlist_plugin.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2010 The Music Player Daemon Project
+ * Copyright (C) 2003-2011 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -20,6 +20,8 @@
#ifndef MPD_PLAYLIST_PLUGIN_H
#define MPD_PLAYLIST_PLUGIN_H
+#include <glib.h>
+
#include <stdbool.h>
#include <stddef.h>
@@ -64,7 +66,8 @@ struct playlist_plugin {
* Opens the playlist on the specified URI. This URI has
* either matched one of the schemes or one of the suffixes.
*/
- struct playlist_provider *(*open_uri)(const char *uri);
+ struct playlist_provider *(*open_uri)(const char *uri,
+ GMutex *mutex, GCond *cond);
/**
* Opens the playlist in the specified input stream. It has
@@ -110,9 +113,10 @@ playlist_plugin_finish(const struct playlist_plugin *plugin)
}
static inline struct playlist_provider *
-playlist_plugin_open_uri(const struct playlist_plugin *plugin, const char *uri)
+playlist_plugin_open_uri(const struct playlist_plugin *plugin, const char *uri,
+ GMutex *mutex, GCond *cond)
{
- return plugin->open_uri(uri);
+ return plugin->open_uri(uri, mutex, cond);
}
static inline struct playlist_provider *
diff --git a/src/playlist_print.c b/src/playlist_print.c
index 89ab2e5ab..a6bf84ccd 100644
--- a/src/playlist_print.c
+++ b/src/playlist_print.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2010 The Music Player Daemon Project
+ * Copyright (C) 2003-2011 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -23,6 +23,7 @@
#include "playlist_plugin.h"
#include "playlist_any.h"
#include "playlist_song.h"
+#include "playlist.h"
#include "queue_print.h"
#include "stored_playlist.h"
#include "song_print.h"
@@ -116,11 +117,12 @@ playlist_print_changes_position(struct client *client,
}
bool
-spl_print(struct client *client, const char *name_utf8, bool detail)
+spl_print(struct client *client, const char *name_utf8, bool detail,
+ GError **error_r)
{
GPtrArray *list;
- list = spl_load(name_utf8);
+ list = spl_load(name_utf8, error_r);
if (list == NULL)
return false;
@@ -153,7 +155,7 @@ playlist_provider_print(struct client *client, const char *uri,
char *base_uri = uri != NULL ? g_path_get_dirname(uri) : NULL;
while ((song = playlist_plugin_read(playlist)) != NULL) {
- song = playlist_check_translate_song(song, base_uri);
+ song = playlist_check_translate_song(song, base_uri, false);
if (song == NULL)
continue;
@@ -169,10 +171,17 @@ playlist_provider_print(struct client *client, const char *uri,
bool
playlist_file_print(struct client *client, const char *uri, bool detail)
{
+ GMutex *mutex = g_mutex_new();
+ GCond *cond = g_cond_new();
+
struct input_stream *is;
- struct playlist_provider *playlist = playlist_open_any(uri, &is);
- if (playlist == NULL)
+ struct playlist_provider *playlist =
+ playlist_open_any(uri, mutex, cond, &is);
+ if (playlist == NULL) {
+ g_cond_free(cond);
+ g_mutex_free(mutex);
return false;
+ }
playlist_provider_print(client, uri, playlist, detail);
playlist_plugin_close(playlist);
@@ -180,5 +189,8 @@ playlist_file_print(struct client *client, const char *uri, bool detail)
if (is != NULL)
input_stream_close(is);
+ g_cond_free(cond);
+ g_mutex_free(mutex);
+
return true;
}
diff --git a/src/playlist_print.h b/src/playlist_print.h
index b3a0446ed..d4f1911d2 100644
--- a/src/playlist_print.h
+++ b/src/playlist_print.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2010 The Music Player Daemon Project
+ * Copyright (C) 2003-2011 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -20,6 +20,7 @@
#ifndef PLAYLIST_PRINT_H
#define PLAYLIST_PRINT_H
+#include <glib.h>
#include <stdbool.h>
#include <stdint.h>
@@ -99,7 +100,8 @@ playlist_print_changes_position(struct client *client,
* @return true on success, false if the playlist does not exist
*/
bool
-spl_print(struct client *client, const char *name_utf8, bool detail);
+spl_print(struct client *client, const char *name_utf8, bool detail,
+ GError **error_r);
/**
* Send the playlist file to the client.
diff --git a/src/playlist_queue.c b/src/playlist_queue.c
index 635e23a28..aada94984 100644
--- a/src/playlist_queue.c
+++ b/src/playlist_queue.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2010 The Music Player Daemon Project
+ * Copyright (C) 2003-2011 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -22,23 +22,35 @@
#include "playlist_plugin.h"
#include "playlist_any.h"
#include "playlist_song.h"
+#include "playlist.h"
#include "song.h"
#include "input_stream.h"
enum playlist_result
playlist_load_into_queue(const char *uri, struct playlist_provider *source,
- struct playlist *dest)
+ unsigned start_index, unsigned end_index,
+ struct playlist *dest, struct player_control *pc,
+ bool secure)
{
enum playlist_result result;
struct song *song;
char *base_uri = uri != NULL ? g_path_get_dirname(uri) : NULL;
- while ((song = playlist_plugin_read(source)) != NULL) {
- song = playlist_check_translate_song(song, base_uri);
+ for (unsigned i = 0;
+ i < end_index && (song = playlist_plugin_read(source)) != NULL;
+ ++i) {
+ if (i < start_index) {
+ /* skip songs before the start index */
+ if (!song_in_database(song))
+ song_free(song);
+ continue;
+ }
+
+ song = playlist_check_translate_song(song, base_uri, secure);
if (song == NULL)
continue;
- result = playlist_append_song(dest, song, NULL);
+ result = playlist_append_song(dest, pc, song, NULL);
if (result != PLAYLIST_RESULT_SUCCESS) {
if (!song_in_database(song))
song_free(song);
@@ -53,19 +65,33 @@ playlist_load_into_queue(const char *uri, struct playlist_provider *source,
}
enum playlist_result
-playlist_open_into_queue(const char *uri, struct playlist *dest)
+playlist_open_into_queue(const char *uri,
+ unsigned start_index, unsigned end_index,
+ struct playlist *dest, struct player_control *pc,
+ bool secure)
{
+ GMutex *mutex = g_mutex_new();
+ GCond *cond = g_cond_new();
+
struct input_stream *is;
- struct playlist_provider *playlist = playlist_open_any(uri, &is);
- if (playlist == NULL)
+ struct playlist_provider *playlist =
+ playlist_open_any(uri, mutex, cond, &is);
+ if (playlist == NULL) {
+ g_cond_free(cond);
+ g_mutex_free(mutex);
return PLAYLIST_RESULT_NO_SUCH_LIST;
+ }
enum playlist_result result =
- playlist_load_into_queue(uri, playlist, dest);
+ playlist_load_into_queue(uri, playlist, start_index, end_index,
+ dest, pc, secure);
playlist_plugin_close(playlist);
if (is != NULL)
input_stream_close(is);
+ g_cond_free(cond);
+ g_mutex_free(mutex);
+
return result;
}
diff --git a/src/playlist_queue.h b/src/playlist_queue.h
index 530d4b4be..24a851aab 100644
--- a/src/playlist_queue.h
+++ b/src/playlist_queue.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2010 The Music Player Daemon Project
+ * Copyright (C) 2003-2011 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -24,10 +24,13 @@
#ifndef MPD_PLAYLIST_QUEUE_H
#define MPD_PLAYLIST_QUEUE_H
-#include "playlist.h"
+#include "playlist_error.h"
+
+#include <stdbool.h>
struct playlist_provider;
struct playlist;
+struct player_control;
/**
* Loads the contents of a playlist and append it to the specified
@@ -35,17 +38,24 @@ struct playlist;
*
* @param uri the URI of the playlist, used to resolve relative song
* URIs
+ * @param start_index the index of the first song
+ * @param end_index the index of the last song (excluding)
*/
enum playlist_result
playlist_load_into_queue(const char *uri, struct playlist_provider *source,
- struct playlist *dest);
+ unsigned start_index, unsigned end_index,
+ struct playlist *dest, struct player_control *pc,
+ bool secure);
/**
* Opens a playlist with a playlist plugin and append to the specified
* play queue.
*/
enum playlist_result
-playlist_open_into_queue(const char *uri, struct playlist *dest);
+playlist_open_into_queue(const char *uri,
+ unsigned start_index, unsigned end_index,
+ struct playlist *dest, struct player_control *pc,
+ bool secure);
#endif
diff --git a/src/playlist_save.c b/src/playlist_save.c
index 8ddc93ec9..6571e286c 100644
--- a/src/playlist_save.c
+++ b/src/playlist_save.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2010 The Music Player Daemon Project
+ * Copyright (C) 2003-2011 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -19,7 +19,9 @@
#include "config.h"
#include "playlist_save.h"
+#include "playlist.h"
#include "stored_playlist.h"
+#include "queue.h"
#include "song.h"
#include "mapper.h"
#include "path.h"
@@ -108,18 +110,24 @@ spl_save_playlist(const char *name_utf8, const struct playlist *playlist)
return spl_save_queue(name_utf8, &playlist->queue);
}
-enum playlist_result
-playlist_load_spl(struct playlist *playlist, const char *name_utf8)
+bool
+playlist_load_spl(struct playlist *playlist, struct player_control *pc,
+ const char *name_utf8,
+ unsigned start_index, unsigned end_index,
+ GError **error_r)
{
GPtrArray *list;
- list = spl_load(name_utf8);
+ list = spl_load(name_utf8, error_r);
if (list == NULL)
- return PLAYLIST_RESULT_NO_SUCH_LIST;
+ return false;
+
+ if (list->len < end_index)
+ end_index = list->len;
- for (unsigned i = 0; i < list->len; ++i) {
+ for (unsigned i = start_index; i < end_index; ++i) {
const char *temp = g_ptr_array_index(list, i);
- if ((playlist_append_uri(playlist, temp, NULL)) != PLAYLIST_RESULT_SUCCESS) {
+ if ((playlist_append_uri(playlist, pc, temp, NULL)) != PLAYLIST_RESULT_SUCCESS) {
/* for windows compatibility, convert slashes */
char *temp2 = g_strdup(temp);
char *p = temp2;
@@ -128,7 +136,7 @@ playlist_load_spl(struct playlist *playlist, const char *name_utf8)
*p = '/';
p++;
}
- if ((playlist_append_uri(playlist, temp, NULL)) != PLAYLIST_RESULT_SUCCESS) {
+ if ((playlist_append_uri(playlist, pc, temp, NULL)) != PLAYLIST_RESULT_SUCCESS) {
g_warning("can't add file \"%s\"", temp2);
}
g_free(temp2);
@@ -136,5 +144,5 @@ playlist_load_spl(struct playlist *playlist, const char *name_utf8)
}
spl_free(list);
- return PLAYLIST_RESULT_SUCCESS;
+ return true;
}
diff --git a/src/playlist_save.h b/src/playlist_save.h
index a0131cf7f..a6c31a9a6 100644
--- a/src/playlist_save.h
+++ b/src/playlist_save.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2010 The Music Player Daemon Project
+ * Copyright (C) 2003-2011 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -20,11 +20,15 @@
#ifndef MPD_PLAYLIST_SAVE_H
#define MPD_PLAYLIST_SAVE_H
-#include "playlist.h"
+#include "playlist_error.h"
+#include <stdbool.h>
#include <stdio.h>
struct song;
+struct queue;
+struct playlist;
+struct player_control;
void
playlist_print_song(FILE *fp, const struct song *song);
@@ -48,7 +52,10 @@ spl_save_playlist(const char *name_utf8, const struct playlist *playlist);
* Loads a stored playlist file, and append all songs to the global
* playlist.
*/
-enum playlist_result
-playlist_load_spl(struct playlist *playlist, const char *name_utf8);
+bool
+playlist_load_spl(struct playlist *playlist, struct player_control *pc,
+ const char *name_utf8,
+ unsigned start_index, unsigned end_index,
+ GError **error_r);
#endif
diff --git a/src/playlist_song.c b/src/playlist_song.c
index 827098655..8a3ba303e 100644
--- a/src/playlist_song.c
+++ b/src/playlist_song.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2010 The Music Player Daemon Project
+ * Copyright (C) 2003-2011 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -84,7 +84,8 @@ apply_song_metadata(struct song *dest, const struct song *src)
}
struct song *
-playlist_check_translate_song(struct song *song, const char *base_uri)
+playlist_check_translate_song(struct song *song, const char *base_uri,
+ bool secure)
{
struct song *dest;
@@ -114,20 +115,19 @@ playlist_check_translate_song(struct song *song, const char *base_uri)
if (g_path_is_absolute(uri)) {
/* XXX fs_charset vs utf8? */
- char *prefix = map_directory_fs(db_get_root());
+ const char *prefix = mapper_get_music_directory();
- if (prefix == NULL || !g_str_has_prefix(uri, prefix) ||
- uri[strlen(prefix)] != '/') {
+ if (prefix != NULL && g_str_has_prefix(uri, prefix) &&
+ uri[strlen(prefix)] == '/')
+ uri += strlen(prefix) + 1;
+ else if (!secure) {
/* local files must be relative to the music
- directory */
- g_free(prefix);
+ directory when "secure" is enabled */
song_free(song);
return NULL;
}
base_uri = NULL;
- uri += strlen(prefix) + 1;
- g_free(prefix);
}
if (base_uri != NULL)
@@ -138,6 +138,12 @@ playlist_check_translate_song(struct song *song, const char *base_uri)
if (uri_has_scheme(uri)) {
dest = song_remote_new(uri);
g_free(uri);
+ } else if (g_path_is_absolute(uri) && secure) {
+ dest = song_file_load(uri, NULL);
+ if (dest == NULL) {
+ song_free(song);
+ return NULL;
+ }
} else {
dest = db_get_song(uri);
g_free(uri);
diff --git a/src/playlist_song.h b/src/playlist_song.h
index 5a2e4c2b0..ea8786912 100644
--- a/src/playlist_song.h
+++ b/src/playlist_song.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2010 The Music Player Daemon Project
+ * Copyright (C) 2003-2011 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -20,12 +20,18 @@
#ifndef MPD_PLAYLIST_SONG_H
#define MPD_PLAYLIST_SONG_H
+#include <stdbool.h>
+
/**
* Verifies the song, returns NULL if it is unsafe. Translate the
* song to a new song object within the database, if it is a local
* file. The old song object is freed.
+ *
+ * @param secure if true, then local files are only allowed if they
+ * are relative to base_uri
*/
struct song *
-playlist_check_translate_song(struct song *song, const char *base_uri);
+playlist_check_translate_song(struct song *song, const char *base_uri,
+ bool secure);
#endif
diff --git a/src/playlist_state.c b/src/playlist_state.c
index bb9897e01..4aa2c2c92 100644
--- a/src/playlist_state.c
+++ b/src/playlist_state.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2010 The Music Player Daemon Project
+ * Copyright (C) 2003-2011 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -29,6 +29,7 @@
#include "queue_save.h"
#include "path.h"
#include "text_file.h"
+#include "conf.h"
#include <string.h>
#include <stdlib.h>
@@ -53,11 +54,12 @@
#define PLAYLIST_BUFFER_SIZE 2*MPD_PATH_MAX
void
-playlist_state_save(FILE *fp, const struct playlist *playlist)
+playlist_state_save(FILE *fp, const struct playlist *playlist,
+ struct player_control *pc)
{
struct player_status player_status;
- pc_get_status(&player_status);
+ pc_get_status(pc, &player_status);
fputs(PLAYLIST_STATE_FILE_STATE, fp);
@@ -89,10 +91,11 @@ playlist_state_save(FILE *fp, const struct playlist *playlist)
fprintf(fp, PLAYLIST_STATE_FILE_CONSUME "%i\n",
playlist->queue.consume);
fprintf(fp, PLAYLIST_STATE_FILE_CROSSFADE "%i\n",
- (int)(pc_get_cross_fade()));
- fprintf(fp, PLAYLIST_STATE_FILE_MIXRAMPDB "%f\n", pc_get_mixramp_db());
+ (int)(pc_get_cross_fade(pc)));
+ fprintf(fp, PLAYLIST_STATE_FILE_MIXRAMPDB "%f\n",
+ pc_get_mixramp_db(pc));
fprintf(fp, PLAYLIST_STATE_FILE_MIXRAMPDELAY "%f\n",
- pc_get_mixramp_delay());
+ pc_get_mixramp_delay(pc));
fputs(PLAYLIST_STATE_FILE_PLAYLIST_BEGIN "\n", fp);
queue_save(fp, &playlist->queue);
fputs(PLAYLIST_STATE_FILE_PLAYLIST_END "\n", fp);
@@ -123,11 +126,11 @@ playlist_state_load(FILE *fp, GString *buffer, struct playlist *playlist)
bool
playlist_state_restore(const char *line, FILE *fp, GString *buffer,
- struct playlist *playlist)
+ struct playlist *playlist, struct player_control *pc)
{
int current = -1;
int seek_time = 0;
- int state = PLAYER_STATE_STOP;
+ enum player_state state = PLAYER_STATE_STOP;
bool random_mode = false;
if (!g_str_has_prefix(line, PLAYLIST_STATE_FILE_STATE))
@@ -148,16 +151,16 @@ playlist_state_restore(const char *line, FILE *fp, GString *buffer,
if (strcmp
(&(line[strlen(PLAYLIST_STATE_FILE_REPEAT)]),
"1") == 0) {
- playlist_set_repeat(playlist, true);
+ playlist_set_repeat(playlist, pc, true);
} else
- playlist_set_repeat(playlist, false);
+ playlist_set_repeat(playlist, pc, false);
} else if (g_str_has_prefix(line, PLAYLIST_STATE_FILE_SINGLE)) {
if (strcmp
(&(line[strlen(PLAYLIST_STATE_FILE_SINGLE)]),
"1") == 0) {
- playlist_set_single(playlist, true);
+ playlist_set_single(playlist, pc, true);
} else
- playlist_set_single(playlist, false);
+ playlist_set_single(playlist, pc, false);
} else if (g_str_has_prefix(line, PLAYLIST_STATE_FILE_CONSUME)) {
if (strcmp
(&(line[strlen(PLAYLIST_STATE_FILE_CONSUME)]),
@@ -166,11 +169,14 @@ playlist_state_restore(const char *line, FILE *fp, GString *buffer,
} else
playlist_set_consume(playlist, false);
} else if (g_str_has_prefix(line, PLAYLIST_STATE_FILE_CROSSFADE)) {
- pc_set_cross_fade(atoi(line + strlen(PLAYLIST_STATE_FILE_CROSSFADE)));
+ pc_set_cross_fade(pc,
+ atoi(line + strlen(PLAYLIST_STATE_FILE_CROSSFADE)));
} else if (g_str_has_prefix(line, PLAYLIST_STATE_FILE_MIXRAMPDB)) {
- pc_set_mixramp_db(atof(line + strlen(PLAYLIST_STATE_FILE_MIXRAMPDB)));
+ pc_set_mixramp_db(pc,
+ atof(line + strlen(PLAYLIST_STATE_FILE_MIXRAMPDB)));
} else if (g_str_has_prefix(line, PLAYLIST_STATE_FILE_MIXRAMPDELAY)) {
- pc_set_mixramp_delay(atof(line + strlen(PLAYLIST_STATE_FILE_MIXRAMPDELAY)));
+ pc_set_mixramp_delay(pc,
+ atof(line + strlen(PLAYLIST_STATE_FILE_MIXRAMPDELAY)));
} else if (g_str_has_prefix(line, PLAYLIST_STATE_FILE_RANDOM)) {
random_mode =
strcmp(line + strlen(PLAYLIST_STATE_FILE_RANDOM),
@@ -185,38 +191,46 @@ playlist_state_restore(const char *line, FILE *fp, GString *buffer,
}
}
- playlist_set_random(playlist, random_mode);
+ playlist_set_random(playlist, pc, random_mode);
if (!queue_is_empty(&playlist->queue)) {
if (!queue_valid_position(&playlist->queue, current))
current = 0;
+ if (state == PLAYER_STATE_PLAY &&
+ config_get_bool("restore_paused", false))
+ /* the user doesn't want MPD to auto-start
+ playback after startup; fall back to
+ "pause" */
+ state = PLAYER_STATE_PAUSE;
+
/* enable all devices for the first time; this must be
called here, after the audio output states were
restored, before playback begins */
if (state != PLAYER_STATE_STOP)
- pc_update_audio();
+ pc_update_audio(pc);
if (state == PLAYER_STATE_STOP /* && config_option */)
playlist->current = current;
else if (seek_time == 0)
- playlist_play(playlist, current);
+ playlist_play(playlist, pc, current);
else
- playlist_seek_song(playlist, current, seek_time);
+ playlist_seek_song(playlist, pc, current, seek_time);
if (state == PLAYER_STATE_PAUSE)
- pc_pause();
+ pc_pause(pc);
}
return true;
}
unsigned
-playlist_state_get_hash(const struct playlist *playlist)
+playlist_state_get_hash(const struct playlist *playlist,
+ struct player_control *pc)
{
struct player_status player_status;
- pc_get_status(&player_status);
+ pc_get_status(pc, &player_status);
return playlist->queue.version ^
(player_status.state != PLAYER_STATE_STOP
@@ -226,7 +240,7 @@ playlist_state_get_hash(const struct playlist *playlist)
? (queue_order_to_position(&playlist->queue,
playlist->current) << 16)
: 0) ^
- ((int)pc_get_cross_fade() << 20) ^
+ ((int)pc_get_cross_fade(pc) << 20) ^
(player_status.state << 24) ^
(playlist->queue.random << 27) ^
(playlist->queue.repeat << 28) ^
diff --git a/src/playlist_state.h b/src/playlist_state.h
index 8ca3657f2..f67d01d2c 100644
--- a/src/playlist_state.h
+++ b/src/playlist_state.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2010 The Music Player Daemon Project
+ * Copyright (C) 2003-2011 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -30,13 +30,15 @@
#include <stdio.h>
struct playlist;
+struct player_control;
void
-playlist_state_save(FILE *fp, const struct playlist *playlist);
+playlist_state_save(FILE *fp, const struct playlist *playlist,
+ struct player_control *pc);
bool
playlist_state_restore(const char *line, FILE *fp, GString *buffer,
- struct playlist *playlist);
+ struct playlist *playlist, struct player_control *pc);
/**
* Generates a hash number for the current state of the playlist and
@@ -45,6 +47,7 @@ playlist_state_restore(const char *line, FILE *fp, GString *buffer,
* be saved.
*/
unsigned
-playlist_state_get_hash(const struct playlist *playlist);
+playlist_state_get_hash(const struct playlist *playlist,
+ struct player_control *pc);
#endif
diff --git a/src/playlist_vector.c b/src/playlist_vector.c
index 7c1765a98..74c7bf089 100644
--- a/src/playlist_vector.c
+++ b/src/playlist_vector.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2010 The Music Player Daemon Project
+ * Copyright (C) 2003-2011 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -19,6 +19,7 @@
#include "config.h"
#include "playlist_vector.h"
+#include "db_lock.h"
#include <assert.h>
#include <string.h>
@@ -46,60 +47,48 @@ playlist_metadata_free(struct playlist_metadata *pm)
}
void
-playlist_vector_deinit(struct playlist_vector *pv)
+playlist_vector_deinit(struct list_head *pv)
{
assert(pv != NULL);
- while (pv->head != NULL) {
- struct playlist_metadata *pm = pv->head;
- pv->head = pm->next;
+ struct playlist_metadata *pm, *n;
+ playlist_vector_for_each_safe(pm, n, pv)
playlist_metadata_free(pm);
- }
}
-static struct playlist_metadata **
-playlist_vector_find_p(struct playlist_vector *pv, const char *name)
+struct playlist_metadata *
+playlist_vector_find(struct list_head *pv, const char *name)
{
+ assert(holding_db_lock());
assert(pv != NULL);
assert(name != NULL);
- struct playlist_metadata **pmp = &pv->head;
-
- for (;;) {
- struct playlist_metadata *pm = *pmp;
- if (pm == NULL)
- return NULL;
-
+ struct playlist_metadata *pm;
+ playlist_vector_for_each(pm, pv)
if (strcmp(pm->name, name) == 0)
- return pmp;
+ return pm;
- pmp = &pm->next;
- }
-}
-
-struct playlist_metadata *
-playlist_vector_find(struct playlist_vector *pv, const char *name)
-{
- struct playlist_metadata **pmp = playlist_vector_find_p(pv, name);
- return pmp != NULL ? *pmp : NULL;
+ return NULL;
}
void
-playlist_vector_add(struct playlist_vector *pv,
+playlist_vector_add(struct list_head *pv,
const char *name, time_t mtime)
{
+ assert(holding_db_lock());
+
struct playlist_metadata *pm = playlist_metadata_new(name, mtime);
- pm->next = pv->head;
- pv->head = pm;
+ list_add_tail(&pm->siblings, pv);
}
bool
-playlist_vector_update_or_add(struct playlist_vector *pv,
+playlist_vector_update_or_add(struct list_head *pv,
const char *name, time_t mtime)
{
- struct playlist_metadata **pmp = playlist_vector_find_p(pv, name);
- if (pmp != NULL) {
- struct playlist_metadata *pm = *pmp;
+ assert(holding_db_lock());
+
+ struct playlist_metadata *pm = playlist_vector_find(pv, name);
+ if (pm != NULL) {
if (mtime == pm->mtime)
return false;
@@ -111,15 +100,15 @@ playlist_vector_update_or_add(struct playlist_vector *pv,
}
bool
-playlist_vector_remove(struct playlist_vector *pv, const char *name)
+playlist_vector_remove(struct list_head *pv, const char *name)
{
- struct playlist_metadata **pmp = playlist_vector_find_p(pv, name);
- if (pmp == NULL)
- return false;
+ assert(holding_db_lock());
- struct playlist_metadata *pm = *pmp;
- *pmp = pm->next;
+ struct playlist_metadata *pm = playlist_vector_find(pv, name);
+ if (pm == NULL)
+ return false;
+ list_del(&pm->siblings);
playlist_metadata_free(pm);
return true;
}
diff --git a/src/playlist_vector.h b/src/playlist_vector.h
index 62861ae49..0af6df8b4 100644
--- a/src/playlist_vector.h
+++ b/src/playlist_vector.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2010 The Music Player Daemon Project
+ * Copyright (C) 2003-2011 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -20,15 +20,23 @@
#ifndef MPD_PLAYLIST_VECTOR_H
#define MPD_PLAYLIST_VECTOR_H
+#include "util/list.h"
+
#include <stdbool.h>
#include <stddef.h>
#include <sys/time.h>
+#define playlist_vector_for_each(pos, head) \
+ list_for_each_entry(pos, head, siblings)
+
+#define playlist_vector_for_each_safe(pos, n, head) \
+ list_for_each_entry_safe(pos, n, head, siblings)
+
/**
* A directory entry pointing to a playlist file.
*/
struct playlist_metadata {
- struct playlist_metadata *next;
+ struct list_head siblings;
/**
* The UTF-8 encoded name of the playlist file.
@@ -38,40 +46,35 @@ struct playlist_metadata {
time_t mtime;
};
-struct playlist_vector {
- struct playlist_metadata *head;
-};
-
-static inline void
-playlist_vector_init(struct playlist_vector *pv)
-{
- pv->head = NULL;
-}
-
void
-playlist_vector_deinit(struct playlist_vector *pv);
-
-static inline bool
-playlist_vector_is_empty(const struct playlist_vector *pv)
-{
- return pv->head == NULL;
-}
+playlist_vector_deinit(struct list_head *pv);
+/**
+ * Caller must lock the #db_mutex.
+ */
struct playlist_metadata *
-playlist_vector_find(struct playlist_vector *pv, const char *name);
+playlist_vector_find(struct list_head *pv, const char *name);
+/**
+ * Caller must lock the #db_mutex.
+ */
void
-playlist_vector_add(struct playlist_vector *pv,
+playlist_vector_add(struct list_head *pv,
const char *name, time_t mtime);
/**
+ * Caller must lock the #db_mutex.
+ *
* @return true if the vector or one of its items was modified
*/
bool
-playlist_vector_update_or_add(struct playlist_vector *pv,
+playlist_vector_update_or_add(struct list_head *pv,
const char *name, time_t mtime);
+/**
+ * Caller must lock the #db_mutex.
+ */
bool
-playlist_vector_remove(struct playlist_vector *pv, const char *name);
+playlist_vector_remove(struct list_head *pv, const char *name);
#endif /* SONGVEC_H */