diff options
Diffstat (limited to '')
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 */ |