diff options
Diffstat (limited to '')
70 files changed, 2368 insertions, 6980 deletions
diff --git a/src/playlist.c b/src/playlist.c deleted file mode 100644 index dc6d8c340..000000000 --- a/src/playlist.c +++ /dev/null @@ -1,452 +0,0 @@ -/* - * 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_internal.h" -#include "playlist_save.h" -#include "player_control.h" -#include "command.h" -#include "tag.h" -#include "song.h" -#include "conf.h" -#include "stored_playlist.h" -#include "idle.h" - -#include <glib.h> - -#include <assert.h> - -#undef G_LOG_DOMAIN -#define G_LOG_DOMAIN "playlist" - -void -playlist_increment_version_all(struct playlist *playlist) -{ - queue_modify_all(&playlist->queue); - idle_add(IDLE_PLAYLIST); -} - -void -playlist_tag_changed(struct playlist *playlist) -{ - if (!playlist->playing) - return; - - assert(playlist->current >= 0); - - queue_modify(&playlist->queue, playlist->current); - idle_add(IDLE_PLAYLIST); -} - -void -playlist_init(struct playlist *playlist) -{ - queue_init(&playlist->queue, - config_get_positive(CONF_MAX_PLAYLIST_LENGTH, - DEFAULT_PLAYLIST_MAX_LENGTH)); - - playlist->queued = -1; - playlist->current = -1; -} - -void -playlist_finish(struct playlist *playlist) -{ - queue_finish(&playlist->queue); -} - -/** - * Queue a song, addressed by its order number. - */ -static void -playlist_queue_song_order(struct playlist *playlist, struct player_control *pc, - unsigned order) -{ - struct song *song; - char *uri; - - assert(queue_valid_order(&playlist->queue, order)); - - playlist->queued = order; - - song = queue_get_order(&playlist->queue, order); - uri = song_get_uri(song); - g_debug("queue song %i:\"%s\"", playlist->queued, uri); - g_free(uri); - - pc_enqueue_song(pc, song); -} - -/** - * Called if the player thread has started playing the "queued" song. - */ -static void -playlist_song_started(struct playlist *playlist, struct player_control *pc) -{ - assert(pc->next_song == NULL); - assert(playlist->queued >= -1); - - /* queued song has started: copy queued to current, - and notify the clients */ - - int current = playlist->current; - playlist->current = playlist->queued; - playlist->queued = -1; - - if(playlist->queue.consume) - playlist_delete(playlist, pc, - queue_order_to_position(&playlist->queue, - current)); - - idle_add(IDLE_PLAYER); -} - -const struct song * -playlist_get_queued_song(struct playlist *playlist) -{ - if (!playlist->playing || playlist->queued < 0) - return NULL; - - return queue_get_order(&playlist->queue, playlist->queued); -} - -void -playlist_update_queued_song(struct playlist *playlist, - struct player_control *pc, - const struct song *prev) -{ - int next_order; - const struct song *next_song; - - if (!playlist->playing) - return; - - assert(!queue_is_empty(&playlist->queue)); - assert((playlist->queued < 0) == (prev == NULL)); - - next_order = playlist->current >= 0 - ? queue_next_order(&playlist->queue, playlist->current) - : 0; - - if (next_order == 0 && playlist->queue.random && - !playlist->queue.single) { - /* shuffle the song order again, so we get a different - order each time the playlist is played - completely */ - unsigned current_position = - queue_order_to_position(&playlist->queue, - playlist->current); - - queue_shuffle_order(&playlist->queue); - - /* make sure that the playlist->current still points to - the current song, after the song order has been - shuffled */ - playlist->current = - queue_position_to_order(&playlist->queue, - current_position); - } - - if (next_order >= 0) - next_song = queue_get_order(&playlist->queue, next_order); - else - next_song = NULL; - - if (prev != NULL && next_song != prev) { - /* clear the currently queued song */ - pc_cancel(pc); - playlist->queued = -1; - } - - if (next_order >= 0) { - if (next_song != prev) - playlist_queue_song_order(playlist, pc, next_order); - else - playlist->queued = next_order; - } -} - -void -playlist_play_order(struct playlist *playlist, struct player_control *pc, - int orderNum) -{ - struct song *song; - char *uri; - - playlist->playing = true; - playlist->queued = -1; - - song = queue_get_order(&playlist->queue, orderNum); - - uri = song_get_uri(song); - g_debug("play %i:\"%s\"", orderNum, uri); - g_free(uri); - - pc_play(pc, song); - playlist->current = orderNum; -} - -static void -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, 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(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, 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, pc); - - player_lock(pc); - pc_next_song = pc->next_song; - player_unlock(pc); - - /* make sure the queued song is always set (if - possible) */ - if (pc_next_song == NULL && playlist->queued < 0) - playlist_update_queued_song(playlist, pc, NULL); - } -} - -/** - * The player has stopped for some reason. Check the error, and - * decide whether to re-start playback - */ -static void -playlist_resume_playback(struct playlist *playlist, struct player_control *pc) -{ - enum player_error error; - - assert(playlist->playing); - assert(pc_get_state(pc) == PLAYER_STATE_STOP); - - error = pc_get_error(pc); - if (error == PLAYER_ERROR_NOERROR) - playlist->error_count = 0; - else - ++playlist->error_count; - - if ((playlist->stop_on_error && error != PLAYER_ERROR_NOERROR) || - error == PLAYER_ERROR_AUDIO || error == PLAYER_ERROR_SYSTEM || - playlist->error_count >= queue_length(&playlist->queue)) - /* too many errors, or critical error: stop - playback */ - playlist_stop(playlist, pc); - else - /* continue playback at the next song */ - playlist_next(playlist, pc); -} - -bool -playlist_get_repeat(const struct playlist *playlist) -{ - return playlist->queue.repeat; -} - -bool -playlist_get_random(const struct playlist *playlist) -{ - return playlist->queue.random; -} - -bool -playlist_get_single(const struct playlist *playlist) -{ - return playlist->queue.single; -} - -bool -playlist_get_consume(const struct playlist *playlist) -{ - return playlist->queue.consume; -} - -void -playlist_set_repeat(struct playlist *playlist, struct player_control *pc, - bool status) -{ - if (status == playlist->queue.repeat) - return; - - struct queue *queue = &playlist->queue; - - queue->repeat = status; - - pc_set_border_pause(pc, queue->single && !queue->repeat); - - /* if the last song is currently being played, the "next song" - might change when repeat mode is toggled */ - playlist_update_queued_song(playlist, pc, - playlist_get_queued_song(playlist)); - - idle_add(IDLE_OPTIONS); -} - -static void -playlist_order(struct playlist *playlist) -{ - if (playlist->current >= 0) - /* update playlist.current, order==position now */ - playlist->current = queue_order_to_position(&playlist->queue, - playlist->current); - - queue_restore_order(&playlist->queue); -} - -void -playlist_set_single(struct playlist *playlist, struct player_control *pc, - bool status) -{ - if (status == playlist->queue.single) - return; - - struct queue *queue = &playlist->queue; - - queue->single = status; - - pc_set_border_pause(pc, queue->single && !queue->repeat); - - /* if the last song is currently being played, the "next song" - might change when single mode is toggled */ - playlist_update_queued_song(playlist, pc, - playlist_get_queued_song(playlist)); - - idle_add(IDLE_OPTIONS); -} - -void -playlist_set_consume(struct playlist *playlist, bool status) -{ - if (status == playlist->queue.consume) - return; - - playlist->queue.consume = status; - idle_add(IDLE_OPTIONS); -} - -void -playlist_set_random(struct playlist *playlist, struct player_control *pc, - bool status) -{ - const struct song *queued; - - if (status == playlist->queue.random) - return; - - queued = playlist_get_queued_song(playlist); - - playlist->queue.random = status; - - if (playlist->queue.random) { - /* shuffle the queue order, but preserve - playlist->current */ - - int current_position = - playlist->playing && playlist->current >= 0 - ? (int)queue_order_to_position(&playlist->queue, - playlist->current) - : -1; - - queue_shuffle_order(&playlist->queue); - - if (current_position >= 0) { - /* make sure the current song is the first in - the order list, so the whole rest of the - playlist is played after that */ - unsigned current_order = - queue_position_to_order(&playlist->queue, - current_position); - queue_swap_order(&playlist->queue, 0, current_order); - playlist->current = 0; - } else - playlist->current = -1; - } else - playlist_order(playlist); - - playlist_update_queued_song(playlist, pc, queued); - - idle_add(IDLE_OPTIONS); -} - -int -playlist_get_current_song(const struct playlist *playlist) -{ - if (playlist->current >= 0) - return queue_order_to_position(&playlist->queue, - playlist->current); - - return -1; -} - -int -playlist_get_next_song(const struct playlist *playlist) -{ - if (playlist->current >= 0) - { - if (playlist->queue.single == 1 && playlist->queue.repeat == 1) - return queue_order_to_position(&playlist->queue, - playlist->current); - else if (playlist->current + 1 < (int)queue_length(&playlist->queue)) - return queue_order_to_position(&playlist->queue, - playlist->current + 1); - else if (playlist->queue.repeat == 1) - return queue_order_to_position(&playlist->queue, 0); - } - - return -1; -} - -unsigned long -playlist_get_version(const struct playlist *playlist) -{ - return playlist->queue.version; -} - -int -playlist_get_length(const struct playlist *playlist) -{ - return queue_length(&playlist->queue); -} - -unsigned -playlist_get_song_id(const struct playlist *playlist, unsigned song) -{ - return queue_position_to_id(&playlist->queue, song); -} diff --git a/src/playlist.h b/src/playlist.h deleted file mode 100644 index a21bdf24a..000000000 --- a/src/playlist.h +++ /dev/null @@ -1,256 +0,0 @@ -/* - * 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_H -#define MPD_PLAYLIST_H - -#include "queue.h" -#include "playlist_error.h" - -#include <stdbool.h> - -struct player_control; - -struct playlist { - /** - * The song queue - it contains the "real" playlist. - */ - struct queue queue; - - /** - * This value is true if the player is currently playing (or - * should be playing). - */ - bool playing; - - /** - * If true, then any error is fatal; if false, MPD will - * attempt to play the next song on non-fatal errors. During - * seeking, this flag is set. - */ - bool stop_on_error; - - /** - * Number of errors since playback was started. If this - * number exceeds the length of the playlist, MPD gives up, - * because all songs have been tried. - */ - unsigned error_count; - - /** - * The "current song pointer". This is the song which is - * played when we get the "play" command. It is also the song - * which is currently being played. - */ - int current; - - /** - * The "next" song to be played, when the current one - * finishes. The decoder thread may start decoding and - * buffering it, while the "current" song is still playing. - * - * This variable is only valid if #playing is true. - */ - int queued; -}; - -/** the global playlist object */ -extern struct playlist g_playlist; - -void -playlist_global_init(void); - -void -playlist_global_finish(void); - -void -playlist_init(struct playlist *playlist); - -void -playlist_finish(struct playlist *playlist); - -void -playlist_tag_changed(struct playlist *playlist); - -/** - * Returns the "queue" object of the global playlist instance. - */ -static inline const struct queue * -playlist_get_queue(const struct playlist *playlist) -{ - return &playlist->queue; -} - -void -playlist_clear(struct playlist *playlist, struct player_control *pc); - -/** - * 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, struct player_control *pc, - const char *path_fs, unsigned *added_id); - -enum playlist_result -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, struct player_control *pc, - struct song *song, unsigned *added_id); - -enum playlist_result -playlist_delete(struct playlist *playlist, struct player_control *pc, - unsigned song); - -/** - * Deletes a range of songs from the playlist. - * - * @param start the position of the first song to delete - * @param end the position after the last song to delete - */ -enum playlist_result -playlist_delete_range(struct playlist *playlist, struct player_control *pc, - unsigned start, unsigned end); - -enum playlist_result -playlist_delete_id(struct playlist *playlist, struct player_control *pc, - unsigned song); - -void -playlist_stop(struct playlist *playlist, struct player_control *pc); - -enum playlist_result -playlist_play(struct playlist *playlist, struct player_control *pc, - int song); - -enum playlist_result -playlist_play_id(struct playlist *playlist, struct player_control *pc, - int song); - -void -playlist_next(struct playlist *playlist, struct player_control *pc); - -void -playlist_sync(struct playlist *playlist, struct player_control *pc); - -void -playlist_previous(struct playlist *playlist, struct player_control *pc); - -void -playlist_shuffle(struct playlist *playlist, struct player_control *pc, - unsigned start, unsigned end); - -void -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_swap_songs(struct playlist *playlist, struct player_control *pc, - unsigned song1, unsigned song2); - -enum playlist_result -playlist_swap_songs_id(struct playlist *playlist, struct player_control *pc, - unsigned id1, unsigned id2); - -enum playlist_result -playlist_set_priority(struct playlist *playlist, struct player_control *pc, - unsigned start_position, unsigned end_position, - uint8_t priority); - -enum playlist_result -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, struct player_control *pc, - bool status); - -bool -playlist_get_random(const struct playlist *playlist); - -void -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, struct player_control *pc, - bool status); - -bool -playlist_get_consume(const struct playlist *playlist); - -void -playlist_set_consume(struct playlist *playlist, bool status); - -int -playlist_get_current_song(const struct playlist *playlist); - -int -playlist_get_next_song(const struct playlist *playlist); - -unsigned -playlist_get_song_id(const struct playlist *playlist, unsigned song); - -int -playlist_get_length(const struct playlist *playlist); - -unsigned long -playlist_get_version(const struct playlist *playlist); - -enum playlist_result -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, 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); - -#endif diff --git a/src/playlist/AsxPlaylistPlugin.cxx b/src/playlist/AsxPlaylistPlugin.cxx new file mode 100644 index 000000000..19fa5a0fb --- /dev/null +++ b/src/playlist/AsxPlaylistPlugin.cxx @@ -0,0 +1,282 @@ +/* + * Copyright (C) 2003-2013 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 "AsxPlaylistPlugin.hxx" +#include "PlaylistPlugin.hxx" +#include "MemorySongEnumerator.hxx" +#include "InputStream.hxx" +#include "Song.hxx" +#include "tag/Tag.hxx" +#include "util/Error.hxx" + +#include <glib.h> + +#include <assert.h> +#include <string.h> + +#undef G_LOG_DOMAIN +#define G_LOG_DOMAIN "asx" + +/** + * This is the state object for the GLib XML parser. + */ +struct AsxParser { + /** + * The list of songs (in reverse order because that's faster + * while adding). + */ + std::forward_list<SongPointer> songs; + + /** + * The current position in the XML file. + */ + enum { + ROOT, ENTRY, + } state; + + /** + * The current tag within the "entry" element. This is only + * valid if state==ENTRY. TAG_NUM_OF_ITEM_TYPES means there + * is no (known) tag. + */ + enum tag_type tag; + + /** + * The current song. It is allocated after the "location" + * element. + */ + Song *song; + + AsxParser() + :state(ROOT) {} + +}; + +static const gchar * +get_attribute(const gchar **attribute_names, const gchar **attribute_values, + const gchar *name) +{ + for (unsigned i = 0; attribute_names[i] != NULL; ++i) + if (g_ascii_strcasecmp(attribute_names[i], name) == 0) + return attribute_values[i]; + + return NULL; +} + +static void +asx_start_element(gcc_unused GMarkupParseContext *context, + const gchar *element_name, + const gchar **attribute_names, + const gchar **attribute_values, + gpointer user_data, gcc_unused GError **error) +{ + AsxParser *parser = (AsxParser *)user_data; + + switch (parser->state) { + case AsxParser::ROOT: + if (g_ascii_strcasecmp(element_name, "entry") == 0) { + parser->state = AsxParser::ENTRY; + parser->song = Song::NewRemote("asx:"); + parser->tag = TAG_NUM_OF_ITEM_TYPES; + } + + break; + + case AsxParser::ENTRY: + if (g_ascii_strcasecmp(element_name, "ref") == 0) { + const gchar *href = get_attribute(attribute_names, + attribute_values, + "href"); + if (href != NULL) { + /* create new song object, and copy + the existing tag over; we cannot + replace the existing song's URI, + because that attribute is + immutable */ + Song *song = Song::NewRemote(href); + + if (parser->song != NULL) { + song->tag = parser->song->tag; + parser->song->tag = NULL; + parser->song->Free(); + } + + parser->song = song; + } + } else if (g_ascii_strcasecmp(element_name, "author") == 0) + /* is that correct? or should it be COMPOSER + or PERFORMER? */ + parser->tag = TAG_ARTIST; + else if (g_ascii_strcasecmp(element_name, "title") == 0) + parser->tag = TAG_TITLE; + + break; + } +} + +static void +asx_end_element(gcc_unused GMarkupParseContext *context, + const gchar *element_name, + gpointer user_data, gcc_unused GError **error) +{ + AsxParser *parser = (AsxParser *)user_data; + + switch (parser->state) { + case AsxParser::ROOT: + break; + + case AsxParser::ENTRY: + if (g_ascii_strcasecmp(element_name, "entry") == 0) { + if (strcmp(parser->song->uri, "asx:") != 0) + parser->songs.emplace_front(parser->song); + else + parser->song->Free(); + + parser->state = AsxParser::ROOT; + } else + parser->tag = TAG_NUM_OF_ITEM_TYPES; + + break; + } +} + +static void +asx_text(gcc_unused GMarkupParseContext *context, + const gchar *text, gsize text_len, + gpointer user_data, gcc_unused GError **error) +{ + AsxParser *parser = (AsxParser *)user_data; + + switch (parser->state) { + case AsxParser::ROOT: + break; + + case AsxParser::ENTRY: + if (parser->tag != TAG_NUM_OF_ITEM_TYPES) { + if (parser->song->tag == NULL) + parser->song->tag = new Tag(); + parser->song->tag->AddItem(parser->tag, + text, text_len); + } + + break; + } +} + +static const GMarkupParser asx_parser = { + asx_start_element, + asx_end_element, + asx_text, + nullptr, + nullptr, +}; + +static void +asx_parser_destroy(gpointer data) +{ + AsxParser *parser = (AsxParser *)data; + + if (parser->state >= AsxParser::ENTRY) + parser->song->Free(); +} + +/* + * The playlist object + * + */ + +static SongEnumerator * +asx_open_stream(struct input_stream *is) +{ + AsxParser parser; + GMarkupParseContext *context; + char buffer[1024]; + size_t nbytes; + bool success; + Error error2; + GError *error = NULL; + + /* parse the ASX XML file */ + + context = g_markup_parse_context_new(&asx_parser, + G_MARKUP_TREAT_CDATA_AS_TEXT, + &parser, asx_parser_destroy); + + while (true) { + nbytes = is->LockRead(buffer, sizeof(buffer), error2); + if (nbytes == 0) { + if (error2.IsDefined()) { + g_markup_parse_context_free(context); + g_warning("%s", error2.GetMessage()); + return NULL; + } + + break; + } + + success = g_markup_parse_context_parse(context, buffer, nbytes, + &error); + if (!success) { + g_warning("XML parser failed: %s", error->message); + g_error_free(error); + g_markup_parse_context_free(context); + return NULL; + } + } + + success = g_markup_parse_context_end_parse(context, &error); + if (!success) { + g_warning("XML parser failed: %s", error->message); + g_error_free(error); + g_markup_parse_context_free(context); + return NULL; + } + + parser.songs.reverse(); + MemorySongEnumerator *playlist = + new MemorySongEnumerator(std::move(parser.songs)); + + g_markup_parse_context_free(context); + + return playlist; +} + +static const char *const asx_suffixes[] = { + "asx", + NULL +}; + +static const char *const asx_mime_types[] = { + "video/x-ms-asf", + NULL +}; + +const struct playlist_plugin asx_playlist_plugin = { + "asx", + + nullptr, + nullptr, + nullptr, + asx_open_stream, + + nullptr, + asx_suffixes, + asx_mime_types, +}; diff --git a/src/playlist/AsxPlaylistPlugin.hxx b/src/playlist/AsxPlaylistPlugin.hxx new file mode 100644 index 000000000..240c1824a --- /dev/null +++ b/src/playlist/AsxPlaylistPlugin.hxx @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2003-2013 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_ASX_PLAYLIST_PLUGIN_HXX +#define MPD_ASX_PLAYLIST_PLUGIN_HXX + +extern const struct playlist_plugin asx_playlist_plugin; + +#endif diff --git a/src/playlist/CuePlaylistPlugin.cxx b/src/playlist/CuePlaylistPlugin.cxx new file mode 100644 index 000000000..0ec5c3068 --- /dev/null +++ b/src/playlist/CuePlaylistPlugin.cxx @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2003-2013 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 "CuePlaylistPlugin.hxx" +#include "PlaylistPlugin.hxx" +#include "SongEnumerator.hxx" +#include "tag/Tag.hxx" +#include "Song.hxx" +#include "cue/CueParser.hxx" +#include "TextInputStream.hxx" + +#include <assert.h> +#include <string.h> + +#undef G_LOG_DOMAIN +#define G_LOG_DOMAIN "cue" + +class CuePlaylist final : public SongEnumerator { + struct input_stream *is; + TextInputStream tis; + CueParser parser; + + public: + CuePlaylist(struct input_stream *_is) + :is(_is), tis(is) { + } + + virtual Song *NextSong() override; +}; + +static SongEnumerator * +cue_playlist_open_stream(struct input_stream *is) +{ + return new CuePlaylist(is); +} + +Song * +CuePlaylist::NextSong() +{ + Song *song = parser.Get(); + if (song != NULL) + return song; + + std::string line; + while (tis.ReadLine(line)) { + parser.Feed(line.c_str()); + song = parser.Get(); + if (song != NULL) + return song; + } + + parser.Finish(); + return parser.Get(); +} + +static const char *const cue_playlist_suffixes[] = { + "cue", + NULL +}; + +static const char *const cue_playlist_mime_types[] = { + "application/x-cue", + NULL +}; + +const struct playlist_plugin cue_playlist_plugin = { + "cue", + + nullptr, + nullptr, + nullptr, + cue_playlist_open_stream, + + nullptr, + cue_playlist_suffixes, + cue_playlist_mime_types, +}; diff --git a/src/playlist/CuePlaylistPlugin.hxx b/src/playlist/CuePlaylistPlugin.hxx new file mode 100644 index 000000000..cf5e3a8f0 --- /dev/null +++ b/src/playlist/CuePlaylistPlugin.hxx @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2003-2013 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_CUE_PLAYLIST_PLUGIN_HXX +#define MPD_CUE_PLAYLIST_PLUGIN_HXX + +extern const struct playlist_plugin cue_playlist_plugin; + +#endif diff --git a/src/playlist/DespotifyPlaylistPlugin.cxx b/src/playlist/DespotifyPlaylistPlugin.cxx new file mode 100644 index 000000000..b0394a5da --- /dev/null +++ b/src/playlist/DespotifyPlaylistPlugin.cxx @@ -0,0 +1,145 @@ +/* + * Copyright (C) 2011-2013 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 "DespotifyPlaylistPlugin.hxx" +#include "DespotifyUtils.hxx" +#include "PlaylistPlugin.hxx" +#include "MemorySongEnumerator.hxx" +#include "tag/Tag.hxx" +#include "Song.hxx" + +extern "C" { +#include <despotify.h> +} + +#include <glib.h> + +#include <string.h> +#include <stdlib.h> + +static void +add_song(std::forward_list<SongPointer> &songs, struct ds_track *track) +{ + const char *dsp_scheme = despotify_playlist_plugin.schemes[0]; + Song *song; + char uri[128]; + char *ds_uri; + + /* Create a spt://... URI for MPD */ + 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::NewRemote(uri); + song->tag = mpd_despotify_tag_from_track(track); + + songs.emplace_front(song); +} + +static bool +parse_track(struct despotify_session *session, + std::forward_list<SongPointer> &songs, + struct ds_link *link) +{ + struct ds_track *track = despotify_link_get_track(session, link); + if (track == nullptr) + return false; + + add_song(songs, track); + return true; +} + +static bool +parse_playlist(struct despotify_session *session, + std::forward_list<SongPointer> &songs, + struct ds_link *link) +{ + ds_playlist *playlist = despotify_link_get_playlist(session, link); + if (playlist == nullptr) + return false; + + for (ds_track *track = playlist->tracks; track != nullptr; + track = track->next) + add_song(songs, track); + + return true; +} + +static SongEnumerator * +despotify_playlist_open_uri(const char *url, + gcc_unused Mutex &mutex, gcc_unused Cond &cond) +{ + despotify_session *session = mpd_despotify_get_session(); + if (session == nullptr) + return nullptr; + + /* Get link without spt:// */ + ds_link *link = + despotify_link_from_uri(url + strlen(despotify_playlist_plugin.schemes[0]) + 3); + if (link == nullptr) { + g_debug("Can't find %s\n", url); + return nullptr; + } + + std::forward_list<SongPointer> songs; + + bool parse_result; + switch (link->type) { + case LINK_TYPE_TRACK: + parse_result = parse_track(session, songs, link); + break; + case LINK_TYPE_PLAYLIST: + parse_result = parse_playlist(session, songs, link); + break; + default: + parse_result = false; + break; + } + + despotify_free_link(link); + if (!parse_result) + return nullptr; + + songs.reverse(); + return new MemorySongEnumerator(std::move(songs)); +} + +static const char *const despotify_schemes[] = { + "spt", + nullptr +}; + +const struct playlist_plugin despotify_playlist_plugin = { + "despotify", + + nullptr, + nullptr, + despotify_playlist_open_uri, + nullptr, + + despotify_schemes, + nullptr, + nullptr, +}; diff --git a/src/playlist/DespotifyPlaylistPlugin.hxx b/src/playlist/DespotifyPlaylistPlugin.hxx new file mode 100644 index 000000000..c1e5b7f39 --- /dev/null +++ b/src/playlist/DespotifyPlaylistPlugin.hxx @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2011-2013 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_HXX +#define MPD_PLAYLIST_DESPOTIFY_PLAYLIST_PLUGIN_HXX + +extern const struct playlist_plugin despotify_playlist_plugin; + +#endif diff --git a/src/playlist/EmbeddedCuePlaylistPlugin.cxx b/src/playlist/EmbeddedCuePlaylistPlugin.cxx new file mode 100644 index 000000000..6b497d50c --- /dev/null +++ b/src/playlist/EmbeddedCuePlaylistPlugin.cxx @@ -0,0 +1,183 @@ +/* + * Copyright (C) 2003-2013 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 "EmbeddedCuePlaylistPlugin.hxx" +#include "PlaylistPlugin.hxx" +#include "SongEnumerator.hxx" +#include "tag/Tag.hxx" +#include "tag/TagHandler.hxx" +#include "tag/TagId3.hxx" +#include "tag/ApeTag.hxx" +#include "Song.hxx" +#include "TagFile.hxx" +#include "cue/CueParser.hxx" + +#include <glib.h> +#include <assert.h> +#include <string.h> + +#undef G_LOG_DOMAIN +#define G_LOG_DOMAIN "cue" + +class EmbeddedCuePlaylist final : public SongEnumerator { +public: + /** + * 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; + + CueParser *parser; + +public: + EmbeddedCuePlaylist() + :filename(nullptr), cuesheet(nullptr), parser(nullptr) { + } + + virtual ~EmbeddedCuePlaylist() { + delete parser; + g_free(cuesheet); + g_free(filename); + } + + virtual Song *NextSong() override; +}; + +static void +embcue_tag_pair(const char *name, const char *value, void *ctx) +{ + EmbeddedCuePlaylist *playlist = (EmbeddedCuePlaylist *)ctx; + + if (playlist->cuesheet == NULL && + g_ascii_strcasecmp(name, "cuesheet") == 0) + playlist->cuesheet = g_strdup(value); +} + +static const struct tag_handler embcue_tag_handler = { + nullptr, + nullptr, + embcue_tag_pair, +}; + +static SongEnumerator * +embcue_playlist_open_uri(const char *uri, + gcc_unused Mutex &mutex, + gcc_unused Cond &cond) +{ + if (!g_path_is_absolute(uri)) + /* only local files supported */ + return NULL; + + const auto playlist = new EmbeddedCuePlaylist(); + + 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 */ + delete playlist; + return NULL; + } + + playlist->filename = g_path_get_basename(uri); + + playlist->next = playlist->cuesheet; + playlist->parser = new CueParser(); + + return playlist; +} + +Song * +EmbeddedCuePlaylist::NextSong() +{ + Song *song = parser->Get(); + if (song != NULL) + return song; + + while (*next != 0) { + const char *line = next; + char *eol = strpbrk(next, "\r\n"); + if (eol != NULL) { + /* null-terminate the line */ + *eol = 0; + next = eol + 1; + } else + /* last line; put the "next" pointer to the + end of the buffer */ + next += strlen(line); + + parser->Feed(line); + song = parser->Get(); + if (song != NULL) + return song->ReplaceURI(filename); + } + + parser->Finish(); + song = parser->Get(); + if (song != NULL) + song = song->ReplaceURI(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 = { + "cue", + + nullptr, + nullptr, + embcue_playlist_open_uri, + nullptr, + + embcue_playlist_suffixes, + nullptr, + nullptr, +}; diff --git a/src/playlist/EmbeddedCuePlaylistPlugin.hxx b/src/playlist/EmbeddedCuePlaylistPlugin.hxx new file mode 100644 index 000000000..e306730f4 --- /dev/null +++ b/src/playlist/EmbeddedCuePlaylistPlugin.hxx @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2003-2013 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_EMBCUE_PLAYLIST_PLUGIN_HXX +#define MPD_EMBCUE_PLAYLIST_PLUGIN_HXX + +extern const struct playlist_plugin embcue_playlist_plugin; + +#endif diff --git a/src/playlist/ExtM3uPlaylistPlugin.cxx b/src/playlist/ExtM3uPlaylistPlugin.cxx new file mode 100644 index 000000000..177e8857d --- /dev/null +++ b/src/playlist/ExtM3uPlaylistPlugin.cxx @@ -0,0 +1,158 @@ +/* + * Copyright (C) 2003-2013 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 "ExtM3uPlaylistPlugin.hxx" +#include "PlaylistPlugin.hxx" +#include "SongEnumerator.hxx" +#include "Song.hxx" +#include "tag/Tag.hxx" +#include "util/StringUtil.hxx" +#include "TextInputStream.hxx" + +#include <glib.h> + +#include <string.h> +#include <stdlib.h> + +class ExtM3uPlaylist final : public SongEnumerator { + TextInputStream tis; + +public: + ExtM3uPlaylist(input_stream *is) + :tis(is) { + } + + bool CheckFirstLine() { + std::string line; + return tis.ReadLine(line) && + strcmp(line.c_str(), "#EXTM3U") == 0; + } + + virtual Song *NextSong() override; +}; + +static SongEnumerator * +extm3u_open_stream(struct input_stream *is) +{ + ExtM3uPlaylist *playlist = new ExtM3uPlaylist(is); + + if (!playlist->CheckFirstLine()) { + /* no EXTM3U header: fall back to the plain m3u + plugin */ + delete playlist; + return NULL; + } + + return playlist; +} + +/** + * Parse a EXTINF line. + * + * @param line the rest of the input line after the colon + */ +static Tag * +extm3u_parse_tag(const char *line) +{ + long duration; + char *endptr; + const char *name; + Tag *tag; + + duration = strtol(line, &endptr, 10); + if (endptr[0] != ',') + /* malformed line */ + return NULL; + + if (duration < 0) + /* 0 means unknown duration */ + duration = 0; + + name = strchug_fast_c(endptr + 1); + if (*name == 0 && duration == 0) + /* no information available; don't allocate a tag + object */ + return NULL; + + tag = new Tag(); + tag->time = duration; + + /* unfortunately, there is no real specification for the + EXTM3U format, so we must assume that the string after the + comma is opaque, and is just the song name*/ + if (*name != 0) + tag->AddItem(TAG_NAME, name); + + return tag; +} + +Song * +ExtM3uPlaylist::NextSong() +{ + Tag *tag = NULL; + std::string line; + const char *line_s; + Song *song; + + do { + if (!tis.ReadLine(line)) { + delete tag; + return NULL; + } + + line_s = line.c_str(); + + if (g_str_has_prefix(line_s, "#EXTINF:")) { + delete tag; + tag = extm3u_parse_tag(line_s + 8); + continue; + } + + while (*line_s != 0 && g_ascii_isspace(*line_s)) + ++line_s; + } while (line_s[0] == '#' || *line_s == 0); + + song = Song::NewRemote(line_s); + song->tag = tag; + return song; +} + +static const char *const extm3u_suffixes[] = { + "m3u", + NULL +}; + +static const char *const extm3u_mime_types[] = { + "audio/x-mpegurl", + NULL +}; + +const struct playlist_plugin extm3u_playlist_plugin = { + "extm3u", + + nullptr, + nullptr, + nullptr, + extm3u_open_stream, + + nullptr, + extm3u_suffixes, + extm3u_mime_types, +}; diff --git a/src/playlist/ExtM3uPlaylistPlugin.hxx b/src/playlist/ExtM3uPlaylistPlugin.hxx new file mode 100644 index 000000000..844fba15c --- /dev/null +++ b/src/playlist/ExtM3uPlaylistPlugin.hxx @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2003-2013 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_EXTM3U_PLAYLIST_PLUGIN_HXX +#define MPD_EXTM3U_PLAYLIST_PLUGIN_HXX + +extern const struct playlist_plugin extm3u_playlist_plugin; + +#endif diff --git a/src/playlist/M3uPlaylistPlugin.cxx b/src/playlist/M3uPlaylistPlugin.cxx new file mode 100644 index 000000000..8854be8d7 --- /dev/null +++ b/src/playlist/M3uPlaylistPlugin.cxx @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2003-2013 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 "M3uPlaylistPlugin.hxx" +#include "PlaylistPlugin.hxx" +#include "SongEnumerator.hxx" +#include "Song.hxx" +#include "TextInputStream.hxx" + +#include <glib.h> + +class M3uPlaylist final : public SongEnumerator { + TextInputStream tis; + +public: + M3uPlaylist(input_stream *is) + :tis(is) { + } + + virtual Song *NextSong() override; +}; + +static SongEnumerator * +m3u_open_stream(struct input_stream *is) +{ + return new M3uPlaylist(is); +} + +Song * +M3uPlaylist::NextSong() +{ + std::string line; + const char *line_s; + + do { + if (!tis.ReadLine(line)) + return NULL; + + line_s = line.c_str(); + + while (*line_s != 0 && g_ascii_isspace(*line_s)) + ++line_s; + } while (line_s[0] == '#' || *line_s == 0); + + return Song::NewRemote(line_s); +} + +static const char *const m3u_suffixes[] = { + "m3u", + NULL +}; + +static const char *const m3u_mime_types[] = { + "audio/x-mpegurl", + NULL +}; + +const struct playlist_plugin m3u_playlist_plugin = { + "m3u", + + nullptr, + nullptr, + nullptr, + m3u_open_stream, + + nullptr, + m3u_suffixes, + m3u_mime_types, +}; diff --git a/src/playlist/M3uPlaylistPlugin.hxx b/src/playlist/M3uPlaylistPlugin.hxx new file mode 100644 index 000000000..a2058bb29 --- /dev/null +++ b/src/playlist/M3uPlaylistPlugin.hxx @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2003-2013 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_M3U_PLAYLIST_PLUGIN_HXX +#define MPD_M3U_PLAYLIST_PLUGIN_HXX + +extern const struct playlist_plugin m3u_playlist_plugin; + +#endif diff --git a/src/playlist/PlsPlaylistPlugin.cxx b/src/playlist/PlsPlaylistPlugin.cxx new file mode 100644 index 000000000..721f9adab --- /dev/null +++ b/src/playlist/PlsPlaylistPlugin.cxx @@ -0,0 +1,178 @@ +/* + * Copyright (C) 2003-2013 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 "PlsPlaylistPlugin.hxx" +#include "PlaylistPlugin.hxx" +#include "MemorySongEnumerator.hxx" +#include "InputStream.hxx" +#include "Song.hxx" +#include "tag/Tag.hxx" +#include "util/Error.hxx" + +#include <glib.h> + +#include <string> + +static void +pls_parser(GKeyFile *keyfile, std::forward_list<SongPointer> &songs) +{ + gchar *key; + gchar *value; + int length; + GError *error = NULL; + int num_entries = g_key_file_get_integer(keyfile, "playlist", + "NumberOfEntries", &error); + if (error) { + g_debug("Invalid PLS file: '%s'", error->message); + g_error_free(error); + error = NULL; + + /* Hack to work around shoutcast failure to comform to spec */ + num_entries = g_key_file_get_integer(keyfile, "playlist", + "numberofentries", &error); + if (error) { + g_error_free(error); + error = NULL; + } + } + + while (num_entries > 0) { + Song *song; + key = g_strdup_printf("File%i", num_entries); + value = g_key_file_get_string(keyfile, "playlist", key, + &error); + if(error) { + g_debug("Invalid PLS entry %s: '%s'",key, error->message); + g_error_free(error); + g_free(key); + return; + } + g_free(key); + + song = Song::NewRemote(value); + g_free(value); + + key = g_strdup_printf("Title%i", num_entries); + value = g_key_file_get_string(keyfile, "playlist", key, + &error); + g_free(key); + if(error == NULL && value){ + if (song->tag == NULL) + song->tag = new Tag(); + song->tag->AddItem(TAG_TITLE, value); + } + /* Ignore errors? Most likely value not present */ + if(error) g_error_free(error); + error = NULL; + g_free(value); + + key = g_strdup_printf("Length%i", num_entries); + length = g_key_file_get_integer(keyfile, "playlist", key, + &error); + g_free(key); + if(error == NULL && length > 0){ + if (song->tag == NULL) + song->tag = new Tag(); + song->tag->time = length; + } + /* Ignore errors? Most likely value not present */ + if(error) g_error_free(error); + error = NULL; + + songs.emplace_front(song); + num_entries--; + } + +} + +static SongEnumerator * +pls_open_stream(struct input_stream *is) +{ + GError *error = NULL; + Error error2; + size_t nbytes; + char buffer[1024]; + bool success; + GKeyFile *keyfile; + + std::string kf_data; + + do { + nbytes = is->LockRead(buffer, sizeof(buffer), error2); + if (nbytes == 0) { + if (error2.IsDefined()) { + g_warning("%s", error2.GetMessage()); + return NULL; + } + + break; + } + + kf_data.append(buffer, nbytes); + /* Limit to 64k */ + } while (kf_data.length() < 65536); + + if (kf_data.empty()) { + g_warning("KeyFile parser failed: No Data"); + return NULL; + } + + keyfile = g_key_file_new(); + success = g_key_file_load_from_data(keyfile, + kf_data.data(), kf_data.length(), + G_KEY_FILE_NONE, &error); + + if (!success) { + g_warning("KeyFile parser failed: %s", error->message); + g_error_free(error); + g_key_file_free(keyfile); + return NULL; + } + + std::forward_list<SongPointer> songs; + pls_parser(keyfile, songs); + g_key_file_free(keyfile); + + songs.reverse(); + return new MemorySongEnumerator(std::move(songs)); +} + +static const char *const pls_suffixes[] = { + "pls", + NULL +}; + +static const char *const pls_mime_types[] = { + "audio/x-scpls", + NULL +}; + +const struct playlist_plugin pls_playlist_plugin = { + "pls", + + nullptr, + nullptr, + nullptr, + pls_open_stream, + + nullptr, + pls_suffixes, + pls_mime_types, +}; diff --git a/src/playlist/PlsPlaylistPlugin.hxx b/src/playlist/PlsPlaylistPlugin.hxx new file mode 100644 index 000000000..3fafd36d0 --- /dev/null +++ b/src/playlist/PlsPlaylistPlugin.hxx @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2003-2013 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_PLS_PLAYLIST_PLUGIN_HXX +#define MPD_PLS_PLAYLIST_PLUGIN_HXX + +extern const struct playlist_plugin pls_playlist_plugin; + +#endif diff --git a/src/playlist/RssPlaylistPlugin.cxx b/src/playlist/RssPlaylistPlugin.cxx new file mode 100644 index 000000000..cd42606d6 --- /dev/null +++ b/src/playlist/RssPlaylistPlugin.cxx @@ -0,0 +1,280 @@ +/* + * Copyright (C) 2003-2013 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 "RssPlaylistPlugin.hxx" +#include "PlaylistPlugin.hxx" +#include "MemorySongEnumerator.hxx" +#include "InputStream.hxx" +#include "Song.hxx" +#include "tag/Tag.hxx" +#include "util/Error.hxx" + +#include <glib.h> + +#include <assert.h> +#include <string.h> + +#undef G_LOG_DOMAIN +#define G_LOG_DOMAIN "rss" + +/** + * This is the state object for the GLib XML parser. + */ +struct RssParser { + /** + * The list of songs (in reverse order because that's faster + * while adding). + */ + std::forward_list<SongPointer> songs; + + /** + * The current position in the XML file. + */ + enum { + ROOT, ITEM, + } state; + + /** + * The current tag within the "entry" element. This is only + * valid if state==ITEM. TAG_NUM_OF_ITEM_TYPES means there + * is no (known) tag. + */ + enum tag_type tag; + + /** + * The current song. It is allocated after the "location" + * element. + */ + Song *song; + + RssParser() + :state(ROOT) {} +}; + +static const gchar * +get_attribute(const gchar **attribute_names, const gchar **attribute_values, + const gchar *name) +{ + for (unsigned i = 0; attribute_names[i] != NULL; ++i) + if (g_ascii_strcasecmp(attribute_names[i], name) == 0) + return attribute_values[i]; + + return NULL; +} + +static void +rss_start_element(gcc_unused GMarkupParseContext *context, + const gchar *element_name, + const gchar **attribute_names, + const gchar **attribute_values, + gpointer user_data, gcc_unused GError **error) +{ + RssParser *parser = (RssParser *)user_data; + + switch (parser->state) { + case RssParser::ROOT: + if (g_ascii_strcasecmp(element_name, "item") == 0) { + parser->state = RssParser::ITEM; + parser->song = Song::NewRemote("rss:"); + parser->tag = TAG_NUM_OF_ITEM_TYPES; + } + + break; + + case RssParser::ITEM: + if (g_ascii_strcasecmp(element_name, "enclosure") == 0) { + const gchar *href = get_attribute(attribute_names, + attribute_values, + "url"); + if (href != NULL) { + /* create new song object, and copy + the existing tag over; we cannot + replace the existing song's URI, + because that attribute is + immutable */ + Song *song = Song::NewRemote(href); + + if (parser->song != NULL) { + song->tag = parser->song->tag; + parser->song->tag = NULL; + parser->song->Free(); + } + + parser->song = song; + } + } else if (g_ascii_strcasecmp(element_name, "title") == 0) + parser->tag = TAG_TITLE; + else if (g_ascii_strcasecmp(element_name, "itunes:author") == 0) + parser->tag = TAG_ARTIST; + + break; + } +} + +static void +rss_end_element(gcc_unused GMarkupParseContext *context, + const gchar *element_name, + gpointer user_data, gcc_unused GError **error) +{ + RssParser *parser = (RssParser *)user_data; + + switch (parser->state) { + case RssParser::ROOT: + break; + + case RssParser::ITEM: + if (g_ascii_strcasecmp(element_name, "item") == 0) { + if (strcmp(parser->song->uri, "rss:") != 0) + parser->songs.emplace_front(parser->song); + else + parser->song->Free(); + + parser->state = RssParser::ROOT; + } else + parser->tag = TAG_NUM_OF_ITEM_TYPES; + + break; + } +} + +static void +rss_text(gcc_unused GMarkupParseContext *context, + const gchar *text, gsize text_len, + gpointer user_data, gcc_unused GError **error) +{ + RssParser *parser = (RssParser *)user_data; + + switch (parser->state) { + case RssParser::ROOT: + break; + + case RssParser::ITEM: + if (parser->tag != TAG_NUM_OF_ITEM_TYPES) { + if (parser->song->tag == NULL) + parser->song->tag = new Tag(); + parser->song->tag->AddItem(parser->tag, + text, text_len); + } + + break; + } +} + +static const GMarkupParser rss_parser = { + rss_start_element, + rss_end_element, + rss_text, + nullptr, + nullptr, +}; + +static void +rss_parser_destroy(gpointer data) +{ + RssParser *parser = (RssParser *)data; + + if (parser->state >= RssParser::ITEM) + parser->song->Free(); +} + +/* + * The playlist object + * + */ + +static SongEnumerator * +rss_open_stream(struct input_stream *is) +{ + RssParser parser; + GMarkupParseContext *context; + char buffer[1024]; + size_t nbytes; + bool success; + Error error2; + GError *error = NULL; + + /* parse the RSS XML file */ + + context = g_markup_parse_context_new(&rss_parser, + G_MARKUP_TREAT_CDATA_AS_TEXT, + &parser, rss_parser_destroy); + + while (true) { + nbytes = is->LockRead(buffer, sizeof(buffer), error2); + if (nbytes == 0) { + if (error2.IsDefined()) { + g_markup_parse_context_free(context); + g_warning("%s", error2.GetMessage()); + return NULL; + } + + break; + } + + success = g_markup_parse_context_parse(context, buffer, nbytes, + &error); + if (!success) { + g_warning("XML parser failed: %s", error->message); + g_error_free(error); + g_markup_parse_context_free(context); + return NULL; + } + } + + success = g_markup_parse_context_end_parse(context, &error); + if (!success) { + g_warning("XML parser failed: %s", error->message); + g_error_free(error); + g_markup_parse_context_free(context); + return NULL; + } + + parser.songs.reverse(); + MemorySongEnumerator *playlist = + new MemorySongEnumerator(std::move(parser.songs)); + + g_markup_parse_context_free(context); + + return playlist; +} + +static const char *const rss_suffixes[] = { + "rss", + NULL +}; + +static const char *const rss_mime_types[] = { + "application/rss+xml", + "text/xml", + NULL +}; + +const struct playlist_plugin rss_playlist_plugin = { + "rss", + + nullptr, + nullptr, + nullptr, + rss_open_stream, + + nullptr, + rss_suffixes, + rss_mime_types, +}; diff --git a/src/playlist/RssPlaylistPlugin.hxx b/src/playlist/RssPlaylistPlugin.hxx new file mode 100644 index 000000000..f49f7e9cf --- /dev/null +++ b/src/playlist/RssPlaylistPlugin.hxx @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2003-2013 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_RSS_PLAYLIST_PLUGIN_HXX +#define MPD_RSS_PLAYLIST_PLUGIN_HXX + +extern const struct playlist_plugin rss_playlist_plugin; + +#endif diff --git a/src/playlist/SoundCloudPlaylistPlugin.cxx b/src/playlist/SoundCloudPlaylistPlugin.cxx new file mode 100644 index 000000000..e6dbf5cf9 --- /dev/null +++ b/src/playlist/SoundCloudPlaylistPlugin.cxx @@ -0,0 +1,414 @@ +/* + * Copyright (C) 2003-2013 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 "SoundCloudPlaylistPlugin.hxx" +#include "PlaylistPlugin.hxx" +#include "MemorySongEnumerator.hxx" +#include "ConfigData.hxx" +#include "InputStream.hxx" +#include "Song.hxx" +#include "tag/Tag.hxx" +#include "util/Error.hxx" + +#include <glib.h> +#include <yajl/yajl_parse.h> + +#include <string.h> + +static struct { + char *apikey; +} soundcloud_config; + +static bool +soundcloud_init(const config_param ¶m) +{ + soundcloud_config.apikey = param.DupBlockString("apikey"); + 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 */ + + std::forward_list<SongPointer> 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; + + Song *s; + char *u; + + u = g_strconcat(data->stream_url, "?client_id=", soundcloud_config.apikey, NULL); + s = Song::NewRemote(u); + g_free(u); + + Tag *t = new Tag(); + t->time = data->duration / 1000; + if (data->title != NULL) + t->AddItem(TAG_NAME, data->title); + s->tag = t; + + data->songs.emplace_front(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, + Mutex &mutex, Cond &cond) +{ + char buffer[4096]; + unsigned char *ubuffer = (unsigned char *)buffer; + + Error error; + input_stream *input_stream = input_stream::Open(url, mutex, cond, + error); + if (input_stream == NULL) { + if (error.IsDefined()) + g_warning("%s", error.GetMessage()); + return -1; + } + + mutex.lock(); + input_stream->WaitReady(); + + yajl_status stat; + int done = 0; + + while (!done) { + const size_t nbytes = + input_stream->Read(buffer, sizeof(buffer), error); + if (nbytes == 0) { + if (error.IsDefined()) + g_warning("%s", error.GetMessage()); + + if (input_stream->IsEOF()) { + done = true; + } else { + mutex.unlock(); + input_stream->Close(); + 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; + } + } + + mutex.unlock(); + input_stream->Close(); + + 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 SongEnumerator * +soundcloud_open_uri(const char *uri, Mutex &mutex, Cond &cond) +{ + 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.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; + + data.songs.reverse(); + return new MemorySongEnumerator(std::move(data.songs)); +} + +static const char *const soundcloud_schemes[] = { + "soundcloud", + NULL +}; + +const struct playlist_plugin soundcloud_playlist_plugin = { + "soundcloud", + + soundcloud_init, + soundcloud_finish, + soundcloud_open_uri, + nullptr, + + soundcloud_schemes, + nullptr, + nullptr, +}; + + diff --git a/src/playlist/SoundCloudPlaylistPlugin.hxx b/src/playlist/SoundCloudPlaylistPlugin.hxx new file mode 100644 index 000000000..7c121328c --- /dev/null +++ b/src/playlist/SoundCloudPlaylistPlugin.hxx @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2003-2013 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_SOUNDCLOUD_PLAYLIST_PLUGIN_HXX +#define MPD_SOUNDCLOUD_PLAYLIST_PLUGIN_HXX + +extern const struct playlist_plugin soundcloud_playlist_plugin; + +#endif diff --git a/src/playlist/XspfPlaylistPlugin.cxx b/src/playlist/XspfPlaylistPlugin.cxx new file mode 100644 index 000000000..797fd7f3a --- /dev/null +++ b/src/playlist/XspfPlaylistPlugin.cxx @@ -0,0 +1,298 @@ +/* + * Copyright (C) 2003-2013 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 "XspfPlaylistPlugin.hxx" +#include "PlaylistPlugin.hxx" +#include "MemorySongEnumerator.hxx" +#include "InputStream.hxx" +#include "tag/Tag.hxx" +#include "util/Error.hxx" + +#include <glib.h> + +#include <assert.h> +#include <string.h> + +#undef G_LOG_DOMAIN +#define G_LOG_DOMAIN "xspf" + +/** + * This is the state object for the GLib XML parser. + */ +struct XspfParser { + /** + * The list of songs (in reverse order because that's faster + * while adding). + */ + std::forward_list<SongPointer> songs; + + /** + * The current position in the XML file. + */ + enum { + ROOT, PLAYLIST, TRACKLIST, TRACK, + LOCATION, + } state; + + /** + * The current tag within the "track" element. This is only + * valid if state==TRACK. TAG_NUM_OF_ITEM_TYPES means there + * is no (known) tag. + */ + enum tag_type tag; + + /** + * The current song. It is allocated after the "location" + * element. + */ + Song *song; + + XspfParser() + :state(ROOT) {} +}; + +static void +xspf_start_element(gcc_unused GMarkupParseContext *context, + const gchar *element_name, + gcc_unused const gchar **attribute_names, + gcc_unused const gchar **attribute_values, + gpointer user_data, gcc_unused GError **error) +{ + XspfParser *parser = (XspfParser *)user_data; + + switch (parser->state) { + case XspfParser::ROOT: + if (strcmp(element_name, "playlist") == 0) + parser->state = XspfParser::PLAYLIST; + + break; + + case XspfParser::PLAYLIST: + if (strcmp(element_name, "trackList") == 0) + parser->state = XspfParser::TRACKLIST; + + break; + + case XspfParser::TRACKLIST: + if (strcmp(element_name, "track") == 0) { + parser->state = XspfParser::TRACK; + parser->song = NULL; + parser->tag = TAG_NUM_OF_ITEM_TYPES; + } + + break; + + case XspfParser::TRACK: + if (strcmp(element_name, "location") == 0) + parser->state = XspfParser::LOCATION; + else if (strcmp(element_name, "title") == 0) + parser->tag = TAG_TITLE; + else if (strcmp(element_name, "creator") == 0) + /* TAG_COMPOSER would be more correct + according to the XSPF spec */ + parser->tag = TAG_ARTIST; + else if (strcmp(element_name, "annotation") == 0) + parser->tag = TAG_COMMENT; + else if (strcmp(element_name, "album") == 0) + parser->tag = TAG_ALBUM; + else if (strcmp(element_name, "trackNum") == 0) + parser->tag = TAG_TRACK; + + break; + + case XspfParser::LOCATION: + break; + } +} + +static void +xspf_end_element(gcc_unused GMarkupParseContext *context, + const gchar *element_name, + gpointer user_data, gcc_unused GError **error) +{ + XspfParser *parser = (XspfParser *)user_data; + + switch (parser->state) { + case XspfParser::ROOT: + break; + + case XspfParser::PLAYLIST: + if (strcmp(element_name, "playlist") == 0) + parser->state = XspfParser::ROOT; + + break; + + case XspfParser::TRACKLIST: + if (strcmp(element_name, "tracklist") == 0) + parser->state = XspfParser::PLAYLIST; + + break; + + case XspfParser::TRACK: + if (strcmp(element_name, "track") == 0) { + if (parser->song != NULL) + parser->songs.emplace_front(parser->song); + + parser->state = XspfParser::TRACKLIST; + } else + parser->tag = TAG_NUM_OF_ITEM_TYPES; + + break; + + case XspfParser::LOCATION: + parser->state = XspfParser::TRACK; + break; + } +} + +static void +xspf_text(gcc_unused GMarkupParseContext *context, + const gchar *text, gsize text_len, + gpointer user_data, gcc_unused GError **error) +{ + XspfParser *parser = (XspfParser *)user_data; + + switch (parser->state) { + case XspfParser::ROOT: + case XspfParser::PLAYLIST: + case XspfParser::TRACKLIST: + break; + + case XspfParser::TRACK: + if (parser->song != NULL && + parser->tag != TAG_NUM_OF_ITEM_TYPES) { + if (parser->song->tag == NULL) + parser->song->tag = new Tag(); + parser->song->tag->AddItem(parser->tag, text, text_len); + } + + break; + + case XspfParser::LOCATION: + if (parser->song == NULL) { + char *uri = g_strndup(text, text_len); + parser->song = Song::NewRemote(uri); + g_free(uri); + } + + break; + } +} + +static const GMarkupParser xspf_parser = { + xspf_start_element, + xspf_end_element, + xspf_text, + nullptr, + nullptr, +}; + +static void +xspf_parser_destroy(gpointer data) +{ + XspfParser *parser = (XspfParser *)data; + + if (parser->state >= XspfParser::TRACK && parser->song != NULL) + parser->song->Free(); +} + +/* + * The playlist object + * + */ + +static SongEnumerator * +xspf_open_stream(struct input_stream *is) +{ + XspfParser parser; + GMarkupParseContext *context; + char buffer[1024]; + size_t nbytes; + bool success; + Error error2; + GError *error = NULL; + + /* parse the XSPF XML file */ + + context = g_markup_parse_context_new(&xspf_parser, + G_MARKUP_TREAT_CDATA_AS_TEXT, + &parser, xspf_parser_destroy); + + while (true) { + nbytes = is->LockRead(buffer, sizeof(buffer), error2); + if (nbytes == 0) { + if (error2.IsDefined()) { + g_markup_parse_context_free(context); + g_warning("%s", error2.GetMessage()); + return NULL; + } + + break; + } + + success = g_markup_parse_context_parse(context, buffer, nbytes, + &error); + if (!success) { + g_warning("XML parser failed: %s", error->message); + g_error_free(error); + g_markup_parse_context_free(context); + return NULL; + } + } + + success = g_markup_parse_context_end_parse(context, &error); + if (!success) { + g_warning("XML parser failed: %s", error->message); + g_error_free(error); + g_markup_parse_context_free(context); + return NULL; + } + + parser.songs.reverse(); + MemorySongEnumerator *playlist = + new MemorySongEnumerator(std::move(parser.songs)); + + g_markup_parse_context_free(context); + + return playlist; +} + +static const char *const xspf_suffixes[] = { + "xspf", + NULL +}; + +static const char *const xspf_mime_types[] = { + "application/xspf+xml", + NULL +}; + +const struct playlist_plugin xspf_playlist_plugin = { + "xspf", + + nullptr, + nullptr, + nullptr, + xspf_open_stream, + + nullptr, + xspf_suffixes, + xspf_mime_types, +}; diff --git a/src/playlist/XspfPlaylistPlugin.hxx b/src/playlist/XspfPlaylistPlugin.hxx new file mode 100644 index 000000000..fc9bbd2c6 --- /dev/null +++ b/src/playlist/XspfPlaylistPlugin.hxx @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2003-2013 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_XSPF_PLAYLIST_PLUGIN_HXX +#define MPD_XSPF_PLAYLIST_PLUGIN_HXX + +extern const struct playlist_plugin xspf_playlist_plugin; + +#endif diff --git a/src/playlist/asx_playlist_plugin.c b/src/playlist/asx_playlist_plugin.c deleted file mode 100644 index 298687859..000000000 --- a/src/playlist/asx_playlist_plugin.c +++ /dev/null @@ -1,323 +0,0 @@ -/* - * 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/asx_playlist_plugin.h" -#include "playlist_plugin.h" -#include "input_stream.h" -#include "song.h" -#include "tag.h" - -#include <glib.h> - -#include <assert.h> -#include <string.h> - -#undef G_LOG_DOMAIN -#define G_LOG_DOMAIN "asx" - -/** - * This is the state object for the GLib XML parser. - */ -struct asx_parser { - /** - * The list of songs (in reverse order because that's faster - * while adding). - */ - GSList *songs; - - /** - * The current position in the XML file. - */ - enum { - ROOT, ENTRY, - } state; - - /** - * The current tag within the "entry" element. This is only - * valid if state==ENTRY. TAG_NUM_OF_ITEM_TYPES means there - * is no (known) tag. - */ - enum tag_type tag; - - /** - * The current song. It is allocated after the "location" - * element. - */ - struct song *song; -}; - -static const gchar * -get_attribute(const gchar **attribute_names, const gchar **attribute_values, - const gchar *name) -{ - for (unsigned i = 0; attribute_names[i] != NULL; ++i) - if (g_ascii_strcasecmp(attribute_names[i], name) == 0) - return attribute_values[i]; - - return NULL; -} - -static void -asx_start_element(G_GNUC_UNUSED GMarkupParseContext *context, - const gchar *element_name, - const gchar **attribute_names, - const gchar **attribute_values, - gpointer user_data, G_GNUC_UNUSED GError **error) -{ - struct asx_parser *parser = user_data; - - switch (parser->state) { - case ROOT: - if (g_ascii_strcasecmp(element_name, "entry") == 0) { - parser->state = ENTRY; - parser->song = song_remote_new("asx:"); - parser->tag = TAG_NUM_OF_ITEM_TYPES; - } - - break; - - case ENTRY: - if (g_ascii_strcasecmp(element_name, "ref") == 0) { - const gchar *href = get_attribute(attribute_names, - attribute_values, - "href"); - if (href != NULL) { - /* create new song object, and copy - the existing tag over; we cannot - replace the existing song's URI, - because that attribute is - immutable */ - struct song *song = song_remote_new(href); - - if (parser->song != NULL) { - song->tag = parser->song->tag; - parser->song->tag = NULL; - song_free(parser->song); - } - - parser->song = song; - } - } else if (g_ascii_strcasecmp(element_name, "author") == 0) - /* is that correct? or should it be COMPOSER - or PERFORMER? */ - parser->tag = TAG_ARTIST; - else if (g_ascii_strcasecmp(element_name, "title") == 0) - parser->tag = TAG_TITLE; - - break; - } -} - -static void -asx_end_element(G_GNUC_UNUSED GMarkupParseContext *context, - const gchar *element_name, - gpointer user_data, G_GNUC_UNUSED GError **error) -{ - struct asx_parser *parser = user_data; - - switch (parser->state) { - case ROOT: - break; - - case ENTRY: - if (g_ascii_strcasecmp(element_name, "entry") == 0) { - if (strcmp(parser->song->uri, "asx:") != 0) - parser->songs = g_slist_prepend(parser->songs, - parser->song); - else - song_free(parser->song); - - parser->state = ROOT; - } else - parser->tag = TAG_NUM_OF_ITEM_TYPES; - - break; - } -} - -static void -asx_text(G_GNUC_UNUSED GMarkupParseContext *context, - const gchar *text, gsize text_len, - gpointer user_data, G_GNUC_UNUSED GError **error) -{ - struct asx_parser *parser = user_data; - - switch (parser->state) { - case ROOT: - break; - - case ENTRY: - if (parser->tag != TAG_NUM_OF_ITEM_TYPES) { - if (parser->song->tag == NULL) - parser->song->tag = tag_new(); - tag_add_item_n(parser->song->tag, parser->tag, - text, text_len); - } - - break; - } -} - -static const GMarkupParser asx_parser = { - .start_element = asx_start_element, - .end_element = asx_end_element, - .text = asx_text, -}; - -static void -song_free_callback(gpointer data, G_GNUC_UNUSED gpointer user_data) -{ - struct song *song = data; - - song_free(song); -} - -static void -asx_parser_destroy(gpointer data) -{ - struct asx_parser *parser = data; - - if (parser->state >= ENTRY) - song_free(parser->song); - - g_slist_foreach(parser->songs, song_free_callback, NULL); - g_slist_free(parser->songs); -} - -/* - * The playlist object - * - */ - -struct asx_playlist { - struct playlist_provider base; - - GSList *songs; -}; - -static struct playlist_provider * -asx_open_stream(struct input_stream *is) -{ - struct asx_parser parser = { - .songs = NULL, - .state = ROOT, - }; - struct asx_playlist *playlist; - GMarkupParseContext *context; - char buffer[1024]; - size_t nbytes; - bool success; - GError *error = NULL; - - /* parse the ASX XML file */ - - context = g_markup_parse_context_new(&asx_parser, - G_MARKUP_TREAT_CDATA_AS_TEXT, - &parser, asx_parser_destroy); - - while (true) { - nbytes = input_stream_lock_read(is, buffer, sizeof(buffer), - &error); - if (nbytes == 0) { - if (error != NULL) { - g_markup_parse_context_free(context); - g_warning("%s", error->message); - g_error_free(error); - return NULL; - } - - break; - } - - success = g_markup_parse_context_parse(context, buffer, nbytes, - &error); - if (!success) { - g_warning("XML parser failed: %s", error->message); - g_error_free(error); - g_markup_parse_context_free(context); - return NULL; - } - } - - success = g_markup_parse_context_end_parse(context, &error); - if (!success) { - g_warning("XML parser failed: %s", error->message); - g_error_free(error); - g_markup_parse_context_free(context); - return NULL; - } - - /* create a #asx_playlist object from the parsed song list */ - - playlist = g_new(struct asx_playlist, 1); - playlist_provider_init(&playlist->base, &asx_playlist_plugin); - playlist->songs = g_slist_reverse(parser.songs); - parser.songs = NULL; - - g_markup_parse_context_free(context); - - return &playlist->base; -} - -static void -asx_close(struct playlist_provider *_playlist) -{ - struct asx_playlist *playlist = (struct asx_playlist *)_playlist; - - g_slist_foreach(playlist->songs, song_free_callback, NULL); - g_slist_free(playlist->songs); - g_free(playlist); -} - -static struct song * -asx_read(struct playlist_provider *_playlist) -{ - struct asx_playlist *playlist = (struct asx_playlist *)_playlist; - struct song *song; - - if (playlist->songs == NULL) - return NULL; - - song = playlist->songs->data; - playlist->songs = g_slist_remove(playlist->songs, song); - - return song; -} - -static const char *const asx_suffixes[] = { - "asx", - NULL -}; - -static const char *const asx_mime_types[] = { - "video/x-ms-asf", - NULL -}; - -const struct playlist_plugin asx_playlist_plugin = { - .name = "asx", - - .open_stream = asx_open_stream, - .close = asx_close, - .read = asx_read, - - .suffixes = asx_suffixes, - .mime_types = asx_mime_types, -}; diff --git a/src/playlist/asx_playlist_plugin.h b/src/playlist/asx_playlist_plugin.h deleted file mode 100644 index 6c01c1209..000000000 --- a/src/playlist/asx_playlist_plugin.h +++ /dev/null @@ -1,25 +0,0 @@ -/* - * 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_ASX_PLAYLIST_PLUGIN_H -#define MPD_PLAYLIST_ASX_PLAYLIST_PLUGIN_H - -extern const struct playlist_plugin asx_playlist_plugin; - -#endif diff --git a/src/playlist/cue_playlist_plugin.c b/src/playlist/cue_playlist_plugin.c deleted file mode 100644 index b85de77d3..000000000 --- a/src/playlist/cue_playlist_plugin.c +++ /dev/null @@ -1,108 +0,0 @@ -/* - * 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/cue_playlist_plugin.h" -#include "playlist_plugin.h" -#include "tag.h" -#include "song.h" -#include "cue/cue_parser.h" -#include "input_stream.h" -#include "text_input_stream.h" - -#include <glib.h> -#include <assert.h> -#include <string.h> - -#undef G_LOG_DOMAIN -#define G_LOG_DOMAIN "cue" - -struct cue_playlist { - struct playlist_provider base; - - struct input_stream *is; - struct text_input_stream *tis; - struct cue_parser *parser; -}; - -static struct playlist_provider * -cue_playlist_open_stream(struct input_stream *is) -{ - struct cue_playlist *playlist = g_new(struct cue_playlist, 1); - playlist_provider_init(&playlist->base, &cue_playlist_plugin); - - playlist->is = is; - playlist->tis = text_input_stream_new(is); - playlist->parser = cue_parser_new(); - - - return &playlist->base; -} - -static void -cue_playlist_close(struct playlist_provider *_playlist) -{ - struct cue_playlist *playlist = (struct cue_playlist *)_playlist; - - cue_parser_free(playlist->parser); - text_input_stream_free(playlist->tis); - g_free(playlist); -} - -static struct song * -cue_playlist_read(struct playlist_provider *_playlist) -{ - struct cue_playlist *playlist = (struct cue_playlist *)_playlist; - - 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; - } - - cue_parser_finish(playlist->parser); - return cue_parser_get(playlist->parser); -} - -static const char *const cue_playlist_suffixes[] = { - "cue", - NULL -}; - -static const char *const cue_playlist_mime_types[] = { - "application/x-cue", - NULL -}; - -const struct playlist_plugin cue_playlist_plugin = { - .name = "cue", - - .open_stream = cue_playlist_open_stream, - .close = cue_playlist_close, - .read = cue_playlist_read, - - .suffixes = cue_playlist_suffixes, - .mime_types = cue_playlist_mime_types, -}; diff --git a/src/playlist/cue_playlist_plugin.h b/src/playlist/cue_playlist_plugin.h deleted file mode 100644 index c02e2235a..000000000 --- a/src/playlist/cue_playlist_plugin.h +++ /dev/null @@ -1,25 +0,0 @@ -/* - * 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_CUE_PLAYLIST_PLUGIN_H -#define MPD_PLAYLIST_CUE_PLAYLIST_PLUGIN_H - -extern const struct playlist_plugin cue_playlist_plugin; - -#endif diff --git a/src/playlist/despotify_playlist_plugin.c b/src/playlist/despotify_playlist_plugin.c deleted file mode 100644 index 30b852c73..000000000 --- a/src/playlist/despotify_playlist_plugin.c +++ /dev/null @@ -1,217 +0,0 @@ -/* - * 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 "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 deleted file mode 100644 index f8ee20de0..000000000 --- a/src/playlist/despotify_playlist_plugin.h +++ /dev/null @@ -1,25 +0,0 @@ -/* - * 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 deleted file mode 100644 index 6d9a957f9..000000000 --- a/src/playlist/embcue_playlist_plugin.c +++ /dev/null @@ -1,181 +0,0 @@ -/* - * 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 deleted file mode 100644 index c5f21b27e..000000000 --- a/src/playlist/embcue_playlist_plugin.h +++ /dev/null @@ -1,25 +0,0 @@ -/* - * 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 deleted file mode 100644 index 19be8d1c4..000000000 --- a/src/playlist/extm3u_playlist_plugin.c +++ /dev/null @@ -1,162 +0,0 @@ -/* - * 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/extm3u_playlist_plugin.h" -#include "playlist_plugin.h" -#include "text_input_stream.h" -#include "uri.h" -#include "song.h" -#include "tag.h" -#include "string_util.h" - -#include <glib.h> - -#include <string.h> -#include <stdlib.h> - -struct extm3u_playlist { - struct playlist_provider base; - - struct text_input_stream *tis; -}; - -static struct playlist_provider * -extm3u_open_stream(struct input_stream *is) -{ - struct extm3u_playlist *playlist; - const char *line; - - playlist = g_new(struct extm3u_playlist, 1); - playlist->tis = text_input_stream_new(is); - - line = text_input_stream_read(playlist->tis); - if (line == NULL || strcmp(line, "#EXTM3U") != 0) { - /* no EXTM3U header: fall back to the plain m3u - plugin */ - text_input_stream_free(playlist->tis); - g_free(playlist); - return NULL; - } - - playlist_provider_init(&playlist->base, &extm3u_playlist_plugin); - return &playlist->base; -} - -static void -extm3u_close(struct playlist_provider *_playlist) -{ - struct extm3u_playlist *playlist = (struct extm3u_playlist *)_playlist; - - text_input_stream_free(playlist->tis); - g_free(playlist); -} - -/** - * Parse a EXTINF line. - * - * @param line the rest of the input line after the colon - */ -static struct tag * -extm3u_parse_tag(const char *line) -{ - long duration; - char *endptr; - const char *name; - struct tag *tag; - - duration = strtol(line, &endptr, 10); - if (endptr[0] != ',') - /* malformed line */ - return NULL; - - if (duration < 0) - /* 0 means unknown duration */ - duration = 0; - - name = strchug_fast_c(endptr + 1); - if (*name == 0 && duration == 0) - /* no information available; don't allocate a tag - object */ - return NULL; - - tag = tag_new(); - tag->time = duration; - - /* unfortunately, there is no real specification for the - EXTM3U format, so we must assume that the string after the - comma is opaque, and is just the song name*/ - if (*name != 0) - tag_add_item(tag, TAG_NAME, name); - - return tag; -} - -static struct song * -extm3u_read(struct playlist_provider *_playlist) -{ - struct extm3u_playlist *playlist = (struct extm3u_playlist *)_playlist; - struct tag *tag = NULL; - const char *line; - struct song *song; - - do { - line = text_input_stream_read(playlist->tis); - if (line == NULL) { - if (tag != NULL) - tag_free(tag); - return NULL; - } - - if (g_str_has_prefix(line, "#EXTINF:")) { - if (tag != NULL) - tag_free(tag); - tag = extm3u_parse_tag(line + 8); - continue; - } - - while (*line != 0 && g_ascii_isspace(*line)) - ++line; - } while (line[0] == '#' || *line == 0); - - song = song_remote_new(line); - song->tag = tag; - return song; -} - -static const char *const extm3u_suffixes[] = { - "m3u", - NULL -}; - -static const char *const extm3u_mime_types[] = { - "audio/x-mpegurl", - NULL -}; - -const struct playlist_plugin extm3u_playlist_plugin = { - .name = "extm3u", - - .open_stream = extm3u_open_stream, - .close = extm3u_close, - .read = extm3u_read, - - .suffixes = extm3u_suffixes, - .mime_types = extm3u_mime_types, -}; diff --git a/src/playlist/extm3u_playlist_plugin.h b/src/playlist/extm3u_playlist_plugin.h deleted file mode 100644 index 5f611ac9c..000000000 --- a/src/playlist/extm3u_playlist_plugin.h +++ /dev/null @@ -1,25 +0,0 @@ -/* - * 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_EXTM3U_PLAYLIST_PLUGIN_H -#define MPD_PLAYLIST_EXTM3U_PLAYLIST_PLUGIN_H - -extern const struct playlist_plugin extm3u_playlist_plugin; - -#endif diff --git a/src/playlist/lastfm_playlist_plugin.c b/src/playlist/lastfm_playlist_plugin.c deleted file mode 100644 index ead14deaa..000000000 --- a/src/playlist/lastfm_playlist_plugin.c +++ /dev/null @@ -1,296 +0,0 @@ -/* - * 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/lastfm_playlist_plugin.h" -#include "playlist_plugin.h" -#include "playlist_list.h" -#include "conf.h" -#include "uri.h" -#include "song.h" -#include "input_stream.h" - -#include <glib.h> - -#include <assert.h> -#include <string.h> - -struct lastfm_playlist { - struct playlist_provider base; - - struct input_stream *is; - - struct playlist_provider *xspf; -}; - -static struct { - char *user; - char *md5; -} lastfm_config; - -static bool -lastfm_init(const struct config_param *param) -{ - const char *user = config_get_block_string(param, "user", NULL); - const char *passwd = config_get_block_string(param, "password", NULL); - - if (user == NULL || passwd == NULL) { - g_debug("disabling the last.fm playlist plugin " - "because account is not configured"); - return false; - } - - lastfm_config.user = g_uri_escape_string(user, NULL, false); - - if (strlen(passwd) != 32) - lastfm_config.md5 = g_compute_checksum_for_string(G_CHECKSUM_MD5, - passwd, strlen(passwd)); - else - lastfm_config.md5 = g_strdup(passwd); - - return true; -} - -static void -lastfm_finish(void) -{ - g_free(lastfm_config.user); - g_free(lastfm_config.md5); -} - -/** - * Simple data fetcher. - * @param url path or url of data to fetch. - * @return data fetched, or NULL on error. Must be freed with g_free. - */ -static char * -lastfm_get(const char *url, GMutex *mutex, GCond *cond) -{ - struct input_stream *input_stream; - GError *error = NULL; - char buffer[4096]; - size_t length = 0, 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 NULL; - } - - g_mutex_lock(mutex); - - input_stream_wait_ready(input_stream); - - do { - nbytes = input_stream_read(input_stream, buffer + length, - sizeof(buffer) - length, &error); - if (nbytes == 0) { - if (error != NULL) { - g_warning("%s", error->message); - g_error_free(error); - } - - if (input_stream_eof(input_stream)) - break; - - /* I/O error */ - g_mutex_unlock(mutex); - input_stream_close(input_stream); - return NULL; - } - - length += nbytes; - } while (length < sizeof(buffer)); - - g_mutex_unlock(mutex); - - input_stream_close(input_stream); - return g_strndup(buffer, length); -} - -/** - * 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 response or NULL on error. Free with g_free. - */ -static char * -lastfm_find(const char *response, const char *name) -{ - size_t name_length = strlen(name); - - while (true) { - const char *eol = strchr(response, '\n'); - if (eol == NULL) - return NULL; - - if (strncmp(response, name, name_length) == 0 && - response[name_length] == '=') { - response += name_length + 1; - return g_strndup(response, eol - response); - } - - response = eol + 1; - } -} - -static struct playlist_provider * -lastfm_open_uri(const char *uri, GMutex *mutex, GCond *cond) -{ - struct lastfm_playlist *playlist; - GError *error = NULL; - char *p, *q, *response, *session; - - /* handshake */ - - p = g_strconcat("http://ws.audioscrobbler.com/radio/handshake.php?" - "version=1.1.1&platform=linux&" - "username=", lastfm_config.user, "&" - "passwordmd5=", lastfm_config.md5, "&" - "debug=0&partner=", NULL); - response = lastfm_get(p, mutex, cond); - g_free(p); - if (response == NULL) - return NULL; - - /* extract session id from response */ - - session = lastfm_find(response, "session"); - g_free(response); - if (session == NULL) { - g_warning("last.fm handshake failed"); - return NULL; - } - - q = g_uri_escape_string(session, NULL, false); - g_free(session); - session = q; - - g_debug("session='%s'", session); - - /* "adjust" last.fm radio */ - - if (strlen(uri) > 9) { - char *escaped_uri; - - escaped_uri = g_uri_escape_string(uri, NULL, false); - - p = g_strconcat("http://ws.audioscrobbler.com/radio/adjust.php?" - "session=", session, "&url=", escaped_uri, "&debug=0", - NULL); - g_free(escaped_uri); - - response = lastfm_get(p, mutex, cond); - g_free(response); - g_free(p); - - if (response == NULL) { - g_free(session); - return NULL; - } - } - - /* create the playlist object */ - - playlist = g_new(struct lastfm_playlist, 1); - playlist_provider_init(&playlist->base, &lastfm_playlist_plugin); - - /* open the last.fm playlist */ - - p = g_strconcat("http://ws.audioscrobbler.com/radio/xspf.php?" - "sk=", session, "&discovery=0&desktop=1.5.1.31879", - NULL); - g_free(session); - - playlist->is = input_stream_open(p, mutex, cond, &error); - g_free(p); - - if (playlist->is == NULL) { - if (error != NULL) { - g_warning("Failed to load XSPF playlist: %s", - error->message); - g_error_free(error); - } else - g_warning("Failed to load XSPF playlist"); - g_free(playlist); - return NULL; - } - - g_mutex_lock(mutex); - - 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); - if (playlist->xspf == NULL) { - input_stream_close(playlist->is); - g_free(playlist); - g_warning("Failed to parse XSPF playlist"); - return NULL; - } - - return &playlist->base; -} - -static void -lastfm_close(struct playlist_provider *_playlist) -{ - struct lastfm_playlist *playlist = (struct lastfm_playlist *)_playlist; - - playlist_plugin_close(playlist->xspf); - input_stream_close(playlist->is); - g_free(playlist); -} - -static struct song * -lastfm_read(struct playlist_provider *_playlist) -{ - struct lastfm_playlist *playlist = (struct lastfm_playlist *)_playlist; - - return playlist_plugin_read(playlist->xspf); -} - -static const char *const lastfm_schemes[] = { - "lastfm", - NULL -}; - -const struct playlist_plugin lastfm_playlist_plugin = { - .name = "lastfm", - - .init = lastfm_init, - .finish = lastfm_finish, - .open_uri = lastfm_open_uri, - .close = lastfm_close, - .read = lastfm_read, - - .schemes = lastfm_schemes, -}; diff --git a/src/playlist/lastfm_playlist_plugin.h b/src/playlist/lastfm_playlist_plugin.h deleted file mode 100644 index 46a8b0caf..000000000 --- a/src/playlist/lastfm_playlist_plugin.h +++ /dev/null @@ -1,25 +0,0 @@ -/* - * 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_LASTFM_PLAYLIST_PLUGIN_H -#define MPD_PLAYLIST_LASTFM_PLAYLIST_PLUGIN_H - -extern const struct playlist_plugin lastfm_playlist_plugin; - -#endif diff --git a/src/playlist/m3u_playlist_plugin.c b/src/playlist/m3u_playlist_plugin.c deleted file mode 100644 index 45b70d2b1..000000000 --- a/src/playlist/m3u_playlist_plugin.c +++ /dev/null @@ -1,92 +0,0 @@ -/* - * 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/m3u_playlist_plugin.h" -#include "playlist_plugin.h" -#include "text_input_stream.h" -#include "uri.h" -#include "song.h" - -#include <glib.h> - -struct m3u_playlist { - struct playlist_provider base; - - struct text_input_stream *tis; -}; - -static struct playlist_provider * -m3u_open_stream(struct input_stream *is) -{ - struct m3u_playlist *playlist = g_new(struct m3u_playlist, 1); - - playlist_provider_init(&playlist->base, &m3u_playlist_plugin); - playlist->tis = text_input_stream_new(is); - - return &playlist->base; -} - -static void -m3u_close(struct playlist_provider *_playlist) -{ - struct m3u_playlist *playlist = (struct m3u_playlist *)_playlist; - - text_input_stream_free(playlist->tis); - g_free(playlist); -} - -static struct song * -m3u_read(struct playlist_provider *_playlist) -{ - struct m3u_playlist *playlist = (struct m3u_playlist *)_playlist; - const char *line; - - do { - line = text_input_stream_read(playlist->tis); - if (line == NULL) - return NULL; - - while (*line != 0 && g_ascii_isspace(*line)) - ++line; - } while (line[0] == '#' || *line == 0); - - return song_remote_new(line); -} - -static const char *const m3u_suffixes[] = { - "m3u", - NULL -}; - -static const char *const m3u_mime_types[] = { - "audio/x-mpegurl", - NULL -}; - -const struct playlist_plugin m3u_playlist_plugin = { - .name = "m3u", - - .open_stream = m3u_open_stream, - .close = m3u_close, - .read = m3u_read, - - .suffixes = m3u_suffixes, - .mime_types = m3u_mime_types, -}; diff --git a/src/playlist/m3u_playlist_plugin.h b/src/playlist/m3u_playlist_plugin.h deleted file mode 100644 index 3890a5fc2..000000000 --- a/src/playlist/m3u_playlist_plugin.h +++ /dev/null @@ -1,25 +0,0 @@ -/* - * 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_M3U_PLAYLIST_PLUGIN_H -#define MPD_PLAYLIST_M3U_PLAYLIST_PLUGIN_H - -extern const struct playlist_plugin m3u_playlist_plugin; - -#endif diff --git a/src/playlist/pls_playlist_plugin.c b/src/playlist/pls_playlist_plugin.c deleted file mode 100644 index c4e5492af..000000000 --- a/src/playlist/pls_playlist_plugin.c +++ /dev/null @@ -1,220 +0,0 @@ -/* - * 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/pls_playlist_plugin.h" -#include "playlist_plugin.h" -#include "input_stream.h" -#include "uri.h" -#include "song.h" -#include "tag.h" -#include <glib.h> - -struct pls_playlist { - struct playlist_provider base; - - GSList *songs; -}; - -static void pls_parser(GKeyFile *keyfile, struct pls_playlist *playlist) -{ - gchar *key; - gchar *value; - int length; - GError *error = NULL; - int num_entries = g_key_file_get_integer(keyfile, "playlist", - "NumberOfEntries", &error); - if (error) { - g_debug("Invalid PLS file: '%s'", error->message); - g_error_free(error); - error = NULL; - - /* Hack to work around shoutcast failure to comform to spec */ - num_entries = g_key_file_get_integer(keyfile, "playlist", - "numberofentries", &error); - if (error) { - g_error_free(error); - error = NULL; - } - } - - while (num_entries > 0) { - struct song *song; - key = g_strdup_printf("File%i", num_entries); - value = g_key_file_get_string(keyfile, "playlist", key, - &error); - if(error) { - g_debug("Invalid PLS entry %s: '%s'",key, error->message); - g_error_free(error); - g_free(key); - return; - } - g_free(key); - - song = song_remote_new(value); - g_free(value); - - key = g_strdup_printf("Title%i", num_entries); - value = g_key_file_get_string(keyfile, "playlist", key, - &error); - g_free(key); - if(error == NULL && value){ - if (song->tag == NULL) - song->tag = tag_new(); - tag_add_item(song->tag,TAG_TITLE, value); - } - /* Ignore errors? Most likely value not present */ - if(error) g_error_free(error); - error = NULL; - g_free(value); - - key = g_strdup_printf("Length%i", num_entries); - length = g_key_file_get_integer(keyfile, "playlist", key, - &error); - g_free(key); - if(error == NULL && length > 0){ - if (song->tag == NULL) - song->tag = tag_new(); - song->tag->time = length; - } - /* Ignore errors? Most likely value not present */ - if(error) g_error_free(error); - error = NULL; - - playlist->songs = g_slist_prepend(playlist->songs, song); - num_entries--; - } - -} - -static struct playlist_provider * -pls_open_stream(struct input_stream *is) -{ - GError *error = NULL; - size_t nbytes; - char buffer[1024]; - bool success; - GKeyFile *keyfile; - struct pls_playlist *playlist; - GString *kf_data = g_string_new(""); - - do { - nbytes = input_stream_lock_read(is, buffer, sizeof(buffer), - &error); - if (nbytes == 0) { - if (error != NULL) { - g_string_free(kf_data, TRUE); - g_warning("%s", error->message); - g_error_free(error); - return NULL; - } - - break; - } - - kf_data = g_string_append_len(kf_data, buffer,nbytes); - /* Limit to 64k */ - } while(kf_data->len < 65536); - - if (kf_data->len == 0) { - g_warning("KeyFile parser failed: No Data"); - g_string_free(kf_data, TRUE); - return NULL; - } - - keyfile = g_key_file_new(); - success = g_key_file_load_from_data(keyfile, - kf_data->str, kf_data->len, - G_KEY_FILE_NONE, &error); - - g_string_free(kf_data, TRUE); - - if (!success) { - g_warning("KeyFile parser failed: %s", error->message); - g_error_free(error); - g_key_file_free(keyfile); - return NULL; - } - - playlist = g_new(struct pls_playlist, 1); - playlist_provider_init(&playlist->base, &pls_playlist_plugin); - playlist->songs = NULL; - - pls_parser(keyfile, playlist); - - g_key_file_free(keyfile); - return &playlist->base; -} - - -static void -song_free_callback(gpointer data, G_GNUC_UNUSED gpointer user_data) -{ - struct song *song = data; - - song_free(song); -} - -static void -pls_close(struct playlist_provider *_playlist) -{ - struct pls_playlist *playlist = (struct pls_playlist *)_playlist; - - g_slist_foreach(playlist->songs, song_free_callback, NULL); - g_slist_free(playlist->songs); - - g_free(playlist); - -} - -static struct song * -pls_read(struct playlist_provider *_playlist) -{ - struct pls_playlist *playlist = (struct pls_playlist *)_playlist; - struct song *song; - - if (playlist->songs == NULL) - return NULL; - - song = playlist->songs->data; - playlist->songs = g_slist_remove(playlist->songs, song); - - return song; -} - -static const char *const pls_suffixes[] = { - "pls", - NULL -}; - -static const char *const pls_mime_types[] = { - "audio/x-scpls", - NULL -}; - -const struct playlist_plugin pls_playlist_plugin = { - .name = "pls", - - .open_stream = pls_open_stream, - .close = pls_close, - .read = pls_read, - - .suffixes = pls_suffixes, - .mime_types = pls_mime_types, -}; diff --git a/src/playlist/pls_playlist_plugin.h b/src/playlist/pls_playlist_plugin.h deleted file mode 100644 index d03435f6d..000000000 --- a/src/playlist/pls_playlist_plugin.h +++ /dev/null @@ -1,25 +0,0 @@ -/* - * 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_PLS_PLAYLIST_PLUGIN_H -#define MPD_PLAYLIST_PLS_PLAYLIST_PLUGIN_H - -extern const struct playlist_plugin pls_playlist_plugin; - -#endif diff --git a/src/playlist/rss_playlist_plugin.c b/src/playlist/rss_playlist_plugin.c deleted file mode 100644 index 6740cba7e..000000000 --- a/src/playlist/rss_playlist_plugin.c +++ /dev/null @@ -1,322 +0,0 @@ -/* - * 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/rss_playlist_plugin.h" -#include "playlist_plugin.h" -#include "input_stream.h" -#include "song.h" -#include "tag.h" - -#include <glib.h> - -#include <assert.h> -#include <string.h> - -#undef G_LOG_DOMAIN -#define G_LOG_DOMAIN "rss" - -/** - * This is the state object for the GLib XML parser. - */ -struct rss_parser { - /** - * The list of songs (in reverse order because that's faster - * while adding). - */ - GSList *songs; - - /** - * The current position in the XML file. - */ - enum { - ROOT, ITEM, - } state; - - /** - * The current tag within the "entry" element. This is only - * valid if state==ITEM. TAG_NUM_OF_ITEM_TYPES means there - * is no (known) tag. - */ - enum tag_type tag; - - /** - * The current song. It is allocated after the "location" - * element. - */ - struct song *song; -}; - -static const gchar * -get_attribute(const gchar **attribute_names, const gchar **attribute_values, - const gchar *name) -{ - for (unsigned i = 0; attribute_names[i] != NULL; ++i) - if (g_ascii_strcasecmp(attribute_names[i], name) == 0) - return attribute_values[i]; - - return NULL; -} - -static void -rss_start_element(G_GNUC_UNUSED GMarkupParseContext *context, - const gchar *element_name, - const gchar **attribute_names, - const gchar **attribute_values, - gpointer user_data, G_GNUC_UNUSED GError **error) -{ - struct rss_parser *parser = user_data; - - switch (parser->state) { - case ROOT: - if (g_ascii_strcasecmp(element_name, "item") == 0) { - parser->state = ITEM; - parser->song = song_remote_new("rss:"); - parser->tag = TAG_NUM_OF_ITEM_TYPES; - } - - break; - - case ITEM: - if (g_ascii_strcasecmp(element_name, "enclosure") == 0) { - const gchar *href = get_attribute(attribute_names, - attribute_values, - "url"); - if (href != NULL) { - /* create new song object, and copy - the existing tag over; we cannot - replace the existing song's URI, - because that attribute is - immutable */ - struct song *song = song_remote_new(href); - - if (parser->song != NULL) { - song->tag = parser->song->tag; - parser->song->tag = NULL; - song_free(parser->song); - } - - parser->song = song; - } - } else if (g_ascii_strcasecmp(element_name, "title") == 0) - parser->tag = TAG_TITLE; - else if (g_ascii_strcasecmp(element_name, "itunes:author") == 0) - parser->tag = TAG_ARTIST; - - break; - } -} - -static void -rss_end_element(G_GNUC_UNUSED GMarkupParseContext *context, - const gchar *element_name, - gpointer user_data, G_GNUC_UNUSED GError **error) -{ - struct rss_parser *parser = user_data; - - switch (parser->state) { - case ROOT: - break; - - case ITEM: - if (g_ascii_strcasecmp(element_name, "item") == 0) { - if (strcmp(parser->song->uri, "rss:") != 0) - parser->songs = g_slist_prepend(parser->songs, - parser->song); - else - song_free(parser->song); - - parser->state = ROOT; - } else - parser->tag = TAG_NUM_OF_ITEM_TYPES; - - break; - } -} - -static void -rss_text(G_GNUC_UNUSED GMarkupParseContext *context, - const gchar *text, gsize text_len, - gpointer user_data, G_GNUC_UNUSED GError **error) -{ - struct rss_parser *parser = user_data; - - switch (parser->state) { - case ROOT: - break; - - case ITEM: - if (parser->tag != TAG_NUM_OF_ITEM_TYPES) { - if (parser->song->tag == NULL) - parser->song->tag = tag_new(); - tag_add_item_n(parser->song->tag, parser->tag, - text, text_len); - } - - break; - } -} - -static const GMarkupParser rss_parser = { - .start_element = rss_start_element, - .end_element = rss_end_element, - .text = rss_text, -}; - -static void -song_free_callback(gpointer data, G_GNUC_UNUSED gpointer user_data) -{ - struct song *song = data; - - song_free(song); -} - -static void -rss_parser_destroy(gpointer data) -{ - struct rss_parser *parser = data; - - if (parser->state >= ITEM) - song_free(parser->song); - - g_slist_foreach(parser->songs, song_free_callback, NULL); - g_slist_free(parser->songs); -} - -/* - * The playlist object - * - */ - -struct rss_playlist { - struct playlist_provider base; - - GSList *songs; -}; - -static struct playlist_provider * -rss_open_stream(struct input_stream *is) -{ - struct rss_parser parser = { - .songs = NULL, - .state = ROOT, - }; - struct rss_playlist *playlist; - GMarkupParseContext *context; - char buffer[1024]; - size_t nbytes; - bool success; - GError *error = NULL; - - /* parse the RSS XML file */ - - context = g_markup_parse_context_new(&rss_parser, - G_MARKUP_TREAT_CDATA_AS_TEXT, - &parser, rss_parser_destroy); - - while (true) { - nbytes = input_stream_lock_read(is, buffer, sizeof(buffer), - &error); - if (nbytes == 0) { - if (error != NULL) { - g_markup_parse_context_free(context); - g_warning("%s", error->message); - g_error_free(error); - return NULL; - } - - break; - } - - success = g_markup_parse_context_parse(context, buffer, nbytes, - &error); - if (!success) { - g_warning("XML parser failed: %s", error->message); - g_error_free(error); - g_markup_parse_context_free(context); - return NULL; - } - } - - success = g_markup_parse_context_end_parse(context, &error); - if (!success) { - g_warning("XML parser failed: %s", error->message); - g_error_free(error); - g_markup_parse_context_free(context); - return NULL; - } - - /* create a #rss_playlist object from the parsed song list */ - - playlist = g_new(struct rss_playlist, 1); - playlist_provider_init(&playlist->base, &rss_playlist_plugin); - playlist->songs = g_slist_reverse(parser.songs); - parser.songs = NULL; - - g_markup_parse_context_free(context); - - return &playlist->base; -} - -static void -rss_close(struct playlist_provider *_playlist) -{ - struct rss_playlist *playlist = (struct rss_playlist *)_playlist; - - g_slist_foreach(playlist->songs, song_free_callback, NULL); - g_slist_free(playlist->songs); - g_free(playlist); -} - -static struct song * -rss_read(struct playlist_provider *_playlist) -{ - struct rss_playlist *playlist = (struct rss_playlist *)_playlist; - struct song *song; - - if (playlist->songs == NULL) - return NULL; - - song = playlist->songs->data; - playlist->songs = g_slist_remove(playlist->songs, song); - - return song; -} - -static const char *const rss_suffixes[] = { - "rss", - NULL -}; - -static const char *const rss_mime_types[] = { - "application/rss+xml", - "text/xml", - NULL -}; - -const struct playlist_plugin rss_playlist_plugin = { - .name = "rss", - - .open_stream = rss_open_stream, - .close = rss_close, - .read = rss_read, - - .suffixes = rss_suffixes, - .mime_types = rss_mime_types, -}; diff --git a/src/playlist/rss_playlist_plugin.h b/src/playlist/rss_playlist_plugin.h deleted file mode 100644 index 3b376de79..000000000 --- a/src/playlist/rss_playlist_plugin.h +++ /dev/null @@ -1,25 +0,0 @@ -/* - * 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_RSS_PLAYLIST_PLUGIN_H -#define MPD_PLAYLIST_RSS_PLAYLIST_PLUGIN_H - -extern const struct playlist_plugin rss_playlist_plugin; - -#endif diff --git a/src/playlist/soundcloud_playlist_plugin.c b/src/playlist/soundcloud_playlist_plugin.c deleted file mode 100644 index 7c79f880a..000000000 --- a/src/playlist/soundcloud_playlist_plugin.c +++ /dev/null @@ -1,448 +0,0 @@ -/* - * 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 deleted file mode 100644 index e09e2dd46..000000000 --- a/src/playlist/soundcloud_playlist_plugin.h +++ /dev/null @@ -1,25 +0,0 @@ -/* - * 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 deleted file mode 100644 index 17d9040e2..000000000 --- a/src/playlist/xspf_playlist_plugin.c +++ /dev/null @@ -1,343 +0,0 @@ -/* - * 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/xspf_playlist_plugin.h" -#include "playlist_plugin.h" -#include "input_stream.h" -#include "uri.h" -#include "song.h" -#include "tag.h" - -#include <glib.h> - -#include <assert.h> -#include <string.h> - -#undef G_LOG_DOMAIN -#define G_LOG_DOMAIN "xspf" - -/** - * This is the state object for the GLib XML parser. - */ -struct xspf_parser { - /** - * The list of songs (in reverse order because that's faster - * while adding). - */ - GSList *songs; - - /** - * The current position in the XML file. - */ - enum { - ROOT, PLAYLIST, TRACKLIST, TRACK, - LOCATION, - } state; - - /** - * The current tag within the "track" element. This is only - * valid if state==TRACK. TAG_NUM_OF_ITEM_TYPES means there - * is no (known) tag. - */ - enum tag_type tag; - - /** - * The current song. It is allocated after the "location" - * element. - */ - struct song *song; -}; - -static void -xspf_start_element(G_GNUC_UNUSED GMarkupParseContext *context, - const gchar *element_name, - G_GNUC_UNUSED const gchar **attribute_names, - G_GNUC_UNUSED const gchar **attribute_values, - gpointer user_data, G_GNUC_UNUSED GError **error) -{ - struct xspf_parser *parser = user_data; - - switch (parser->state) { - case ROOT: - if (strcmp(element_name, "playlist") == 0) - parser->state = PLAYLIST; - - break; - - case PLAYLIST: - if (strcmp(element_name, "trackList") == 0) - parser->state = TRACKLIST; - - break; - - case TRACKLIST: - if (strcmp(element_name, "track") == 0) { - parser->state = TRACK; - parser->song = NULL; - parser->tag = TAG_NUM_OF_ITEM_TYPES; - } - - break; - - case TRACK: - if (strcmp(element_name, "location") == 0) - parser->state = LOCATION; - else if (strcmp(element_name, "title") == 0) - parser->tag = TAG_TITLE; - else if (strcmp(element_name, "creator") == 0) - /* TAG_COMPOSER would be more correct - according to the XSPF spec */ - parser->tag = TAG_ARTIST; - else if (strcmp(element_name, "annotation") == 0) - parser->tag = TAG_COMMENT; - else if (strcmp(element_name, "album") == 0) - parser->tag = TAG_ALBUM; - else if (strcmp(element_name, "trackNum") == 0) - parser->tag = TAG_TRACK; - - break; - - case LOCATION: - break; - } -} - -static void -xspf_end_element(G_GNUC_UNUSED GMarkupParseContext *context, - const gchar *element_name, - gpointer user_data, G_GNUC_UNUSED GError **error) -{ - struct xspf_parser *parser = user_data; - - switch (parser->state) { - case ROOT: - break; - - case PLAYLIST: - if (strcmp(element_name, "playlist") == 0) - parser->state = ROOT; - - break; - - case TRACKLIST: - if (strcmp(element_name, "tracklist") == 0) - parser->state = PLAYLIST; - - break; - - case TRACK: - if (strcmp(element_name, "track") == 0) { - if (parser->song != NULL) - parser->songs = g_slist_prepend(parser->songs, - parser->song); - - parser->state = TRACKLIST; - } else - parser->tag = TAG_NUM_OF_ITEM_TYPES; - - break; - - case LOCATION: - parser->state = TRACK; - break; - } -} - -static void -xspf_text(G_GNUC_UNUSED GMarkupParseContext *context, - const gchar *text, gsize text_len, - gpointer user_data, G_GNUC_UNUSED GError **error) -{ - struct xspf_parser *parser = user_data; - - switch (parser->state) { - case ROOT: - case PLAYLIST: - case TRACKLIST: - break; - - case TRACK: - if (parser->song != NULL && - parser->tag != TAG_NUM_OF_ITEM_TYPES) { - if (parser->song->tag == NULL) - parser->song->tag = tag_new(); - tag_add_item_n(parser->song->tag, parser->tag, - text, text_len); - } - - break; - - case LOCATION: - if (parser->song == NULL) { - char *uri = g_strndup(text, text_len); - parser->song = song_remote_new(uri); - g_free(uri); - } - - break; - } -} - -static const GMarkupParser xspf_parser = { - .start_element = xspf_start_element, - .end_element = xspf_end_element, - .text = xspf_text, -}; - -static void -song_free_callback(gpointer data, G_GNUC_UNUSED gpointer user_data) -{ - struct song *song = data; - - song_free(song); -} - -static void -xspf_parser_destroy(gpointer data) -{ - struct xspf_parser *parser = data; - - if (parser->state >= TRACK && parser->song != NULL) - song_free(parser->song); - - g_slist_foreach(parser->songs, song_free_callback, NULL); - g_slist_free(parser->songs); -} - -/* - * The playlist object - * - */ - -struct xspf_playlist { - struct playlist_provider base; - - GSList *songs; -}; - -static struct playlist_provider * -xspf_open_stream(struct input_stream *is) -{ - struct xspf_parser parser = { - .songs = NULL, - .state = ROOT, - }; - struct xspf_playlist *playlist; - GMarkupParseContext *context; - char buffer[1024]; - size_t nbytes; - bool success; - GError *error = NULL; - - /* parse the XSPF XML file */ - - context = g_markup_parse_context_new(&xspf_parser, - G_MARKUP_TREAT_CDATA_AS_TEXT, - &parser, xspf_parser_destroy); - - while (true) { - nbytes = input_stream_lock_read(is, buffer, sizeof(buffer), - &error); - if (nbytes == 0) { - if (error != NULL) { - g_markup_parse_context_free(context); - g_warning("%s", error->message); - g_error_free(error); - return NULL; - } - - break; - } - - success = g_markup_parse_context_parse(context, buffer, nbytes, - &error); - if (!success) { - g_warning("XML parser failed: %s", error->message); - g_error_free(error); - g_markup_parse_context_free(context); - return NULL; - } - } - - success = g_markup_parse_context_end_parse(context, &error); - if (!success) { - g_warning("XML parser failed: %s", error->message); - g_error_free(error); - g_markup_parse_context_free(context); - return NULL; - } - - /* create a #xspf_playlist object from the parsed song list */ - - playlist = g_new(struct xspf_playlist, 1); - playlist_provider_init(&playlist->base, &xspf_playlist_plugin); - playlist->songs = g_slist_reverse(parser.songs); - parser.songs = NULL; - - g_markup_parse_context_free(context); - - return &playlist->base; -} - -static void -xspf_close(struct playlist_provider *_playlist) -{ - struct xspf_playlist *playlist = (struct xspf_playlist *)_playlist; - - g_slist_foreach(playlist->songs, song_free_callback, NULL); - g_slist_free(playlist->songs); - g_free(playlist); -} - -static struct song * -xspf_read(struct playlist_provider *_playlist) -{ - struct xspf_playlist *playlist = (struct xspf_playlist *)_playlist; - struct song *song; - - if (playlist->songs == NULL) - return NULL; - - song = playlist->songs->data; - playlist->songs = g_slist_remove(playlist->songs, song); - - return song; -} - -static const char *const xspf_suffixes[] = { - "xspf", - NULL -}; - -static const char *const xspf_mime_types[] = { - "application/xspf+xml", - NULL -}; - -const struct playlist_plugin xspf_playlist_plugin = { - .name = "xspf", - - .open_stream = xspf_open_stream, - .close = xspf_close, - .read = xspf_read, - - .suffixes = xspf_suffixes, - .mime_types = xspf_mime_types, -}; diff --git a/src/playlist/xspf_playlist_plugin.h b/src/playlist/xspf_playlist_plugin.h deleted file mode 100644 index 4636d7e83..000000000 --- a/src/playlist/xspf_playlist_plugin.h +++ /dev/null @@ -1,25 +0,0 @@ -/* - * 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_XSPF_PLAYLIST_PLUGIN_H -#define MPD_PLAYLIST_XSPF_PLAYLIST_PLUGIN_H - -extern const struct playlist_plugin xspf_playlist_plugin; - -#endif diff --git a/src/playlist_any.c b/src/playlist_any.c deleted file mode 100644 index 450ca5932..000000000 --- a/src/playlist_any.c +++ /dev/null @@ -1,71 +0,0 @@ -/* - * 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_any.h" -#include "playlist_list.h" -#include "playlist_mapper.h" -#include "uri.h" -#include "input_stream.h" - -#include <assert.h> - -static struct playlist_provider * -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, mutex, cond); - if (playlist != NULL) { - *is_r = NULL; - return playlist; - } - - GError *error = NULL; - struct input_stream *is = input_stream_open(uri, mutex, cond, &error); - if (is == NULL) { - if (error != NULL) { - g_warning("Failed to open %s: %s", - uri, error->message); - g_error_free(error); - } - - return NULL; - } - - playlist = playlist_list_open_stream(is, uri); - if (playlist == NULL) { - input_stream_close(is); - return NULL; - } - - *is_r = is; - return playlist; -} - -struct playlist_provider * -playlist_open_any(const char *uri, GMutex *mutex, GCond *cond, - struct input_stream **is_r) -{ - return uri_has_scheme(uri) - ? 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 deleted file mode 100644 index 310913de9..000000000 --- a/src/playlist_any.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * 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_ANY_H -#define MPD_PLAYLIST_ANY_H - -#include <glib.h> - -struct playlist_provider; -struct input_stream; - -/** - * Opens a playlist from the specified URI, which can be either an - * absolute remote URI (with a scheme) or a relative path to the - * music orplaylist directory. - * - * @param is_r on success, an input_stream object may be returned - * here, which must be closed after the playlist_provider object is - * freed - */ -struct playlist_provider * -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 deleted file mode 100644 index 0dea7676a..000000000 --- a/src/playlist_control.c +++ /dev/null @@ -1,288 +0,0 @@ -/* - * 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. - */ - -/* - * Functions for controlling playback on the playlist level. - * - */ - -#include "config.h" -#include "playlist_internal.h" -#include "player_control.h" -#include "idle.h" - -#include <glib.h> - -#undef G_LOG_DOMAIN -#define G_LOG_DOMAIN "playlist" - -void -playlist_stop(struct playlist *playlist, struct player_control *pc) -{ - if (!playlist->playing) - return; - - assert(playlist->current >= 0); - - g_debug("stop"); - pc_stop(pc); - playlist->queued = -1; - playlist->playing = false; - - if (playlist->queue.random) { - /* shuffle the playlist, so the next playback will - result in a new random order */ - - unsigned current_position = - queue_order_to_position(&playlist->queue, - playlist->current); - - queue_shuffle_order(&playlist->queue); - - /* make sure that "current" stays valid, and the next - "play" command plays the same song again */ - playlist->current = - queue_position_to_order(&playlist->queue, - current_position); - } -} - -enum playlist_result -playlist_play(struct playlist *playlist, struct player_control *pc, - int song) -{ - unsigned i = song; - - pc_clear_error(pc); - - if (song == -1) { - /* play any song ("current" song, or the first song */ - - if (queue_is_empty(&playlist->queue)) - return PLAYLIST_RESULT_SUCCESS; - - if (playlist->playing) { - /* already playing: unpause playback, just in - case it was paused, and return */ - pc_set_pause(pc, false); - return PLAYLIST_RESULT_SUCCESS; - } - - /* select a song: "current" song, or the first one */ - i = playlist->current >= 0 - ? playlist->current - : 0; - } else if (!queue_valid_position(&playlist->queue, song)) - return PLAYLIST_RESULT_BAD_RANGE; - - if (playlist->queue.random) { - if (song >= 0) - /* "i" is currently the song position (which - would be equal to the order number in - no-random mode); convert it to a order - number, because random mode is enabled */ - i = queue_position_to_order(&playlist->queue, song); - - if (!playlist->playing) - playlist->current = 0; - - /* swap the new song with the previous "current" one, - so playback continues as planned */ - queue_swap_order(&playlist->queue, - i, playlist->current); - i = playlist->current; - } - - playlist->stop_on_error = false; - playlist->error_count = 0; - - playlist_play_order(playlist, pc, i); - return PLAYLIST_RESULT_SUCCESS; -} - -enum playlist_result -playlist_play_id(struct playlist *playlist, struct player_control *pc, - int id) -{ - int song; - - if (id == -1) { - 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, pc, song); -} - -void -playlist_next(struct playlist *playlist, struct player_control *pc) -{ - int next_order; - int current; - - if (!playlist->playing) - return; - - assert(!queue_is_empty(&playlist->queue)); - assert(queue_valid_order(&playlist->queue, playlist->current)); - - current = playlist->current; - playlist->stop_on_error = false; - - /* determine the next song from the queue's order list */ - - next_order = queue_next_order(&playlist->queue, playlist->current); - if (next_order < 0) { - /* no song after this one: stop playback */ - playlist_stop(playlist, pc); - - /* reset "current song" */ - playlist->current = -1; - } - else - { - if (next_order == 0 && playlist->queue.random) { - /* The queue told us that the next song is the first - song. This means we are in repeat mode. Shuffle - the queue order, so this time, the user hears the - songs in a different than before */ - assert(playlist->queue.repeat); - - queue_shuffle_order(&playlist->queue); - - /* note that playlist->current and playlist->queued are - now invalid, but playlist_play_order() will - discard them anyway */ - } - - playlist_play_order(playlist, pc, next_order); - } - - /* Consume mode removes each played songs. */ - if(playlist->queue.consume) - playlist_delete(playlist, pc, - queue_order_to_position(&playlist->queue, - current)); -} - -void -playlist_previous(struct playlist *playlist, struct player_control *pc) -{ - if (!playlist->playing) - return; - - assert(queue_length(&playlist->queue) > 0); - - if (playlist->current > 0) { - /* play the preceding song */ - playlist_play_order(playlist, pc, - playlist->current - 1); - } else if (playlist->queue.repeat) { - /* play the last song in "repeat" mode */ - 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, pc, playlist->current); - } -} - -enum playlist_result -playlist_seek_song(struct playlist *playlist, struct player_control *pc, - unsigned song, float seek_time) -{ - const struct song *queued; - unsigned i; - bool success; - - if (!queue_valid_position(&playlist->queue, song)) - return PLAYLIST_RESULT_BAD_RANGE; - - queued = playlist_get_queued_song(playlist); - - if (playlist->queue.random) - i = queue_position_to_order(&playlist->queue, song); - else - i = song; - - pc_clear_error(pc); - playlist->stop_on_error = true; - playlist->error_count = 0; - - if (!playlist->playing || (unsigned)playlist->current != i) { - /* seeking is not within the current song - prepare - song change */ - - playlist->playing = true; - playlist->current = i; - - queued = NULL; - } - - success = pc_seek(pc, queue_get_order(&playlist->queue, i), seek_time); - if (!success) { - playlist_update_queued_song(playlist, pc, queued); - - return PLAYLIST_RESULT_NOT_PLAYING; - } - - playlist->queued = -1; - playlist_update_queued_song(playlist, pc, NULL); - - return PLAYLIST_RESULT_SUCCESS; -} - -enum playlist_result -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, 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 deleted file mode 100644 index 6b9d87155..000000000 --- a/src/playlist_database.c +++ /dev/null @@ -1,79 +0,0 @@ -/* - * 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_database.h" -#include "playlist_vector.h" -#include "text_file.h" -#include "string_util.h" - -#include <string.h> -#include <stdlib.h> - -static GQuark -playlist_database_quark(void) -{ - return g_quark_from_static_string("playlist_database"); -} - -void -playlist_vector_save(FILE *fp, const struct list_head *pv) -{ - struct playlist_metadata *pm; - playlist_vector_for_each(pm, pv) - fprintf(fp, PLAYLIST_META_BEGIN "%s\n" - "mtime: %li\n" - "playlist_end\n", - pm->name, (long)pm->mtime); -} - -bool -playlist_metadata_load(FILE *fp, struct list_head *pv, const char *name, - GString *buffer, GError **error_r) -{ - struct playlist_metadata pm = { - .mtime = 0, - }; - char *line, *colon; - const char *value; - - while ((line = read_text_line(fp, buffer)) != NULL && - strcmp(line, "playlist_end") != 0) { - colon = strchr(line, ':'); - if (colon == NULL || colon == line) { - g_set_error(error_r, playlist_database_quark(), 0, - "unknown line in db: %s", line); - return false; - } - - *colon++ = 0; - value = strchug_fast_c(colon); - - if (strcmp(line, "mtime") == 0) - pm.mtime = strtol(value, NULL, 10); - else { - g_set_error(error_r, playlist_database_quark(), 0, - "unknown line in db: %s", line); - return false; - } - } - - playlist_vector_update_or_add(pv, name, pm.mtime); - return true; -} diff --git a/src/playlist_database.h b/src/playlist_database.h deleted file mode 100644 index 3238fa06b..000000000 --- a/src/playlist_database.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * 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_DATABASE_H -#define MPD_PLAYLIST_DATABASE_H - -#include "check.h" - -#include <stdbool.h> -#include <stdio.h> -#include <glib.h> - -#define PLAYLIST_META_BEGIN "playlist_begin: " - -struct list_head; - -void -playlist_vector_save(FILE *fp, const struct list_head *pv); - -bool -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 deleted file mode 100644 index 8042f2f76..000000000 --- a/src/playlist_edit.c +++ /dev/null @@ -1,492 +0,0 @@ -/* - * 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. - */ - -/* - * Functions for editing the playlist (adding, removing, reordering - * songs in the queue). - * - */ - -#include "config.h" -#include "playlist_internal.h" -#include "player_control.h" -#include "database.h" -#include "uri.h" -#include "song.h" -#include "idle.h" - -#include <stdlib.h> - -static void playlist_increment_version(struct playlist *playlist) -{ - queue_increment_version(&playlist->queue); - - idle_add(IDLE_PLAYLIST); -} - -void -playlist_clear(struct playlist *playlist, struct player_control *pc) -{ - 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(pc, song); - } - - queue_clear(&playlist->queue); - - playlist->current = -1; - - playlist_increment_version(playlist); -} - -enum playlist_result -playlist_append_file(struct playlist *playlist, struct player_control *pc, - const char *path_fs, unsigned *added_id) -{ - struct song *song = song_file_load(path_fs, NULL); - if (song == NULL) - return PLAYLIST_RESULT_NO_SUCH_SONG; - - return playlist_append_song(playlist, pc, song, added_id); -} - -enum playlist_result -playlist_append_song(struct playlist *playlist, struct player_control *pc, - struct song *song, unsigned *added_id) -{ - const struct song *queued; - unsigned id; - - if (queue_is_full(&playlist->queue)) - return PLAYLIST_RESULT_TOO_LARGE; - - queued = playlist_get_queued_song(playlist); - - id = queue_append(&playlist->queue, song, 0); - - if (playlist->queue.random) { - /* shuffle the new song into the list of remaining - songs to play */ - - unsigned start; - if (playlist->queued >= 0) - start = playlist->queued + 1; - else - start = playlist->current + 1; - if (start < queue_length(&playlist->queue)) - queue_shuffle_order_last(&playlist->queue, start, - queue_length(&playlist->queue)); - } - - playlist_increment_version(playlist); - - playlist_update_queued_song(playlist, pc, queued); - - if (added_id) - *added_id = id; - - return PLAYLIST_RESULT_SUCCESS; -} - -static struct song * -song_by_uri(const char *uri) -{ - struct song *song; - - song = db_get_song(uri); - if (song != NULL) - return song; - - if (uri_has_scheme(uri)) - return song_remote_new(uri); - - return NULL; -} - -enum playlist_result -playlist_append_uri(struct playlist *playlist, struct player_control *pc, - const char *uri, unsigned *added_id) -{ - struct song *song; - - g_debug("add to playlist: %s", uri); - - song = song_by_uri(uri); - if (song == NULL) - return PLAYLIST_RESULT_NO_SUCH_SONG; - - return playlist_append_song(playlist, pc, song, added_id); -} - -enum playlist_result -playlist_swap_songs(struct playlist *playlist, struct player_control *pc, - unsigned song1, unsigned song2) -{ - const struct song *queued; - - if (!queue_valid_position(&playlist->queue, song1) || - !queue_valid_position(&playlist->queue, song2)) - return PLAYLIST_RESULT_BAD_RANGE; - - queued = playlist_get_queued_song(playlist); - - queue_swap(&playlist->queue, song1, song2); - - if (playlist->queue.random) { - /* update the queue order, so that playlist->current - still points to the current song order */ - - queue_swap_order(&playlist->queue, - queue_position_to_order(&playlist->queue, - song1), - queue_position_to_order(&playlist->queue, - song2)); - } else { - /* correct the "current" song order */ - - if (playlist->current == (int)song1) - playlist->current = song2; - else if (playlist->current == (int)song2) - playlist->current = song1; - } - - playlist_increment_version(playlist); - - playlist_update_queued_song(playlist, pc, queued); - - return PLAYLIST_RESULT_SUCCESS; -} - -enum playlist_result -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); - - if (song1 < 0 || song2 < 0) - return PLAYLIST_RESULT_NO_SUCH_SONG; - - 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, struct player_control *pc, - unsigned song, const struct song **queued_p) -{ - unsigned songOrder; - - assert(song < queue_length(&playlist->queue)); - - songOrder = queue_position_to_order(&playlist->queue, song); - - if (playlist->playing && playlist->current == (int)songOrder) { - bool paused = pc_get_state(pc) == PLAYER_STATE_PAUSE; - - /* the current song is going to be deleted: stop the player */ - - pc_stop(pc); - playlist->playing = false; - - /* see which song is going to be played instead */ - - playlist->current = queue_next_order(&playlist->queue, - playlist->current); - if (playlist->current == (int)songOrder) - playlist->current = -1; - - if (playlist->current >= 0 && !paused) - /* play the song after the deleted one */ - playlist_play_order(playlist, pc, playlist->current); - else - /* no songs left to play, stop playback - completely */ - playlist_stop(playlist, pc); - - *queued_p = NULL; - } else if (playlist->current == (int)songOrder) - /* there's a "current song" but we're not playing - currently - clear "current" */ - playlist->current = -1; - - /* now do it: remove the song */ - - if (!song_in_database(queue_get(&playlist->queue, song))) - pc_song_deleted(pc, queue_get(&playlist->queue, song)); - - queue_delete(&playlist->queue, song); - - /* update the "current" and "queued" variables */ - - if (playlist->current > (int)songOrder) { - playlist->current--; - } -} - -enum playlist_result -playlist_delete(struct playlist *playlist, struct player_control *pc, - unsigned song) -{ - const struct song *queued; - - if (song >= queue_length(&playlist->queue)) - return PLAYLIST_RESULT_BAD_RANGE; - - queued = playlist_get_queued_song(playlist); - - playlist_delete_internal(playlist, pc, song, &queued); - - playlist_increment_version(playlist); - playlist_update_queued_song(playlist, pc, queued); - - return PLAYLIST_RESULT_SUCCESS; -} - -enum playlist_result -playlist_delete_range(struct playlist *playlist, struct player_control *pc, - unsigned start, unsigned end) -{ - const struct song *queued; - - 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; - - queued = playlist_get_queued_song(playlist); - - do { - playlist_delete_internal(playlist, pc, --end, &queued); - } while (end != start); - - playlist_increment_version(playlist); - playlist_update_queued_song(playlist, pc, queued); - - return PLAYLIST_RESULT_SUCCESS; -} - -enum playlist_result -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, pc, song); -} - -void -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, pc, i); - - pc_song_deleted(pc, song); -} - -enum playlist_result -playlist_move_range(struct playlist *playlist, struct player_control *pc, - unsigned start, unsigned end, int to) -{ - const struct song *queued; - int currentSong; - - if (!queue_valid_position(&playlist->queue, start) || - !queue_valid_position(&playlist->queue, end - 1)) - return PLAYLIST_RESULT_BAD_RANGE; - - if ((to >= 0 && to + end - start - 1 >= queue_length(&playlist->queue)) || - (to < 0 && abs(to) > (int)queue_length(&playlist->queue))) - return PLAYLIST_RESULT_BAD_RANGE; - - if ((int)start == to) - /* nothing happens */ - return PLAYLIST_RESULT_SUCCESS; - - queued = playlist_get_queued_song(playlist); - - /* - * (to < 0) => move to offset from current song - * (-playlist.length == to) => move to position BEFORE current song - */ - currentSong = playlist->current >= 0 - ? (int)queue_order_to_position(&playlist->queue, - playlist->current) - : -1; - if (to < 0) { - if (currentSong < 0) - /* can't move relative to current song, - because there is no current song */ - return PLAYLIST_RESULT_BAD_RANGE; - - if (start <= (unsigned)currentSong && (unsigned)currentSong < end) - /* no-op, can't be moved to offset of itself */ - return PLAYLIST_RESULT_SUCCESS; - to = (currentSong + abs(to)) % queue_length(&playlist->queue); - if (start < (unsigned)to) - to--; - } - - queue_move_range(&playlist->queue, start, end, to); - - if (!playlist->queue.random) { - /* update current/queued */ - if ((int)start <= playlist->current && - (unsigned)playlist->current < end) - playlist->current += to - start; - else if (playlist->current >= (int)end && - playlist->current <= to) { - playlist->current -= end - start; - } else if (playlist->current >= to && - playlist->current < (int)start) { - playlist->current += end - start; - } - } - - playlist_increment_version(playlist); - - playlist_update_queued_song(playlist, pc, queued); - - return PLAYLIST_RESULT_SUCCESS; -} - -enum playlist_result -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, pc, song, song+1, to); -} - -void -playlist_shuffle(struct playlist *playlist, struct player_control *pc, - unsigned start, unsigned end) -{ - const struct song *queued; - - if (end > queue_length(&playlist->queue)) - /* correct the "end" offset */ - end = queue_length(&playlist->queue); - - if ((start+1) >= end) - /* needs at least two entries. */ - return; - - queued = playlist_get_queued_song(playlist); - if (playlist->playing && playlist->current >= 0) { - unsigned current_position; - current_position = queue_order_to_position(&playlist->queue, - playlist->current); - - if (current_position >= start && current_position < end) - { - /* put current playing song first */ - queue_swap(&playlist->queue, start, current_position); - - if (playlist->queue.random) { - playlist->current = - queue_position_to_order(&playlist->queue, start); - } else - playlist->current = start; - - /* start shuffle after the current song */ - start++; - } - } else { - /* no playback currently: reset playlist->current */ - - playlist->current = -1; - } - - queue_shuffle_range(&playlist->queue, start, end); - - playlist_increment_version(playlist); - - playlist_update_queued_song(playlist, pc, queued); -} diff --git a/src/playlist_error.h b/src/playlist_error.h deleted file mode 100644 index ad9c62cf1..000000000 --- a/src/playlist_error.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * 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 deleted file mode 100644 index 650b88bb8..000000000 --- a/src/playlist_global.c +++ /dev/null @@ -1,58 +0,0 @@ -/* - * 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. - */ - -/* - * The manager of the global "struct playlist" instance (g_playlist). - * - */ - -#include "config.h" -#include "playlist.h" -#include "playlist_state.h" -#include "event_pipe.h" -#include "main.h" - -struct playlist g_playlist; - -static void -playlist_tag_event(void) -{ - playlist_tag_changed(&g_playlist); -} - -static void -playlist_event(void) -{ - playlist_sync(&g_playlist, global_player_control); -} - -void -playlist_global_init(void) -{ - playlist_init(&g_playlist); - - event_pipe_register(PIPE_EVENT_TAG, playlist_tag_event); - event_pipe_register(PIPE_EVENT_PLAYLIST, playlist_event); -} - -void -playlist_global_finish(void) -{ - playlist_finish(&g_playlist); -} diff --git a/src/playlist_internal.h b/src/playlist_internal.h deleted file mode 100644 index 81b175176..000000000 --- a/src/playlist_internal.h +++ /dev/null @@ -1,56 +0,0 @@ -/* - * 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. - */ - -/* - * Internal header for the components of the playlist code. - * - */ - -#ifndef PLAYLIST_INTERNAL_H -#define PLAYLIST_INTERNAL_H - -#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. - */ -const struct song * -playlist_get_queued_song(struct playlist *playlist); - -/** - * Updates the "queued song". Calculates the next song according to - * the current one (if MPD isn't playing, it takes the first song), - * and queues this song. Clears the old queued song if there was one. - * - * @param prev the song which was previously queued, as determined by - * playlist_get_queued_song() - */ -void -playlist_update_queued_song(struct playlist *playlist, - struct player_control *pc, - const struct song *prev); - -void -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 deleted file mode 100644 index 68d24fe49..000000000 --- a/src/playlist_list.c +++ /dev/null @@ -1,348 +0,0 @@ -/* - * 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_list.h" -#include "playlist_plugin.h" -#include "playlist/extm3u_playlist_plugin.h" -#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/embcue_playlist_plugin.h" -#include "input_stream.h" -#include "uri.h" -#include "string_util.h" -#include "conf.h" -#include "mpd_error.h" - -#include <assert.h> -#include <string.h> -#include <stdio.h> - -const struct playlist_plugin *const playlist_plugins[] = { - &extm3u_playlist_plugin, - &m3u_playlist_plugin, - &xspf_playlist_plugin, - &pls_playlist_plugin, - &asx_playlist_plugin, - &rss_playlist_plugin, -#ifdef ENABLE_DESPOTIFY - &despotify_playlist_plugin, -#endif -#ifdef ENABLE_LASTFM - &lastfm_playlist_plugin, -#endif -#ifdef ENABLE_SOUNDCLOUD - &soundcloud_playlist_plugin, -#endif - &cue_playlist_plugin, - &embcue_playlist_plugin, - NULL -}; - -/** which plugins have been initialized successfully? */ -static bool playlist_plugins_enabled[G_N_ELEMENTS(playlist_plugins)]; - -#define playlist_plugins_for_each_enabled(plugin) \ - playlist_plugins_for_each(plugin) \ - if (playlist_plugins_enabled[playlist_plugin_iterator - playlist_plugins]) - -/** - * Find the "playlist" configuration block for the specified plugin. - * - * @param plugin_name the name of the playlist plugin - * @return the configuration block, or NULL if none was configured - */ -static const struct config_param * -playlist_plugin_config(const char *plugin_name) -{ - const struct config_param *param = NULL; - - assert(plugin_name != NULL); - - while ((param = config_get_next_param(CONF_PLAYLIST_PLUGIN, param)) != NULL) { - const char *name = - config_get_block_string(param, "name", NULL); - if (name == NULL) - MPD_ERROR("playlist configuration without 'plugin' name in line %d", - param->line); - - if (strcmp(name, plugin_name) == 0) - return param; - } - - return NULL; -} - -void -playlist_list_global_init(void) -{ - for (unsigned i = 0; playlist_plugins[i] != NULL; ++i) { - const struct playlist_plugin *plugin = playlist_plugins[i]; - const struct config_param *param = - playlist_plugin_config(plugin->name); - - if (!config_get_block_bool(param, "enabled", true)) - /* the plugin is disabled in mpd.conf */ - continue; - - playlist_plugins_enabled[i] = - playlist_plugin_init(playlist_plugins[i], param); - } -} - -void -playlist_list_global_finish(void) -{ - playlist_plugins_for_each_enabled(plugin) - playlist_plugin_finish(plugin); -} - -static struct playlist_provider * -playlist_list_open_uri_scheme(const char *uri, GMutex *mutex, GCond *cond, - bool *tried) -{ - char *scheme; - struct playlist_provider *playlist = NULL; - - assert(uri != NULL); - - scheme = g_uri_parse_scheme(uri); - if (scheme == NULL) - return NULL; - - for (unsigned i = 0; playlist_plugins[i] != NULL; ++i) { - const struct playlist_plugin *plugin = playlist_plugins[i]; - - assert(!tried[i]); - - 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, - mutex, cond); - if (playlist != NULL) - break; - - tried[i] = true; - } - } - - g_free(scheme); - return playlist; -} - -static struct playlist_provider * -playlist_list_open_uri_suffix(const char *uri, GMutex *mutex, GCond *cond, - const bool *tried) -{ - const char *suffix; - struct playlist_provider *playlist = NULL; - - assert(uri != NULL); - - suffix = uri_get_suffix(uri); - if (suffix == NULL) - return NULL; - - for (unsigned i = 0; playlist_plugins[i] != NULL; ++i) { - const struct playlist_plugin *plugin = playlist_plugins[i]; - - 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, - mutex, cond); - if (playlist != NULL) - break; - } - } - - return playlist; -} - -struct playlist_provider * -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 - playlist_list_open_uri_scheme() */ - bool tried[G_N_ELEMENTS(playlist_plugins) - 1]; - - assert(uri != NULL); - - memset(tried, false, sizeof(tried)); - - playlist = playlist_list_open_uri_scheme(uri, mutex, cond, tried); - if (playlist == NULL) - playlist = playlist_list_open_uri_suffix(uri, mutex, cond, - tried); - - return playlist; -} - -static struct playlist_provider * -playlist_list_open_stream_mime2(struct input_stream *is, const char *mime) -{ - struct playlist_provider *playlist; - - assert(is != NULL); - assert(mime != NULL); - - playlist_plugins_for_each_enabled(plugin) { - if (plugin->open_stream != NULL && - plugin->mime_types != NULL && - string_array_contains(plugin->mime_types, mime)) { - /* rewind the stream, so each plugin gets a - fresh start */ - input_stream_seek(is, 0, SEEK_SET, NULL); - - playlist = playlist_plugin_open_stream(plugin, is); - if (playlist != NULL) - return playlist; - } - } - - return NULL; -} - -static struct playlist_provider * -playlist_list_open_stream_mime(struct input_stream *is) -{ - assert(is->mime != NULL); - - const char *semicolon = strchr(is->mime, ';'); - if (semicolon == NULL) - return playlist_list_open_stream_mime2(is, is->mime); - - if (semicolon == is->mime) - return NULL; - - /* probe only the portion before the semicolon*/ - char *mime = g_strndup(is->mime, semicolon - is->mime); - struct playlist_provider *playlist = - playlist_list_open_stream_mime2(is, mime); - g_free(mime); - return playlist; -} - -static struct playlist_provider * -playlist_list_open_stream_suffix(struct input_stream *is, const char *suffix) -{ - struct playlist_provider *playlist; - - assert(is != NULL); - assert(suffix != NULL); - - playlist_plugins_for_each_enabled(plugin) { - if (plugin->open_stream != NULL && - plugin->suffixes != NULL && - string_array_contains(plugin->suffixes, suffix)) { - /* rewind the stream, so each plugin gets a - fresh start */ - input_stream_seek(is, 0, SEEK_SET, NULL); - - playlist = playlist_plugin_open_stream(plugin, is); - if (playlist != NULL) - return playlist; - } - } - - return NULL; -} - -struct playlist_provider * -playlist_list_open_stream(struct input_stream *is, const char *uri) -{ - const char *suffix; - struct playlist_provider *playlist; - - input_stream_lock_wait_ready(is); - - if (is->mime != NULL) { - playlist = playlist_list_open_stream_mime(is); - if (playlist != NULL) - return playlist; - } - - suffix = uri != NULL ? uri_get_suffix(uri) : NULL; - if (suffix != NULL) { - playlist = playlist_list_open_stream_suffix(is, suffix); - if (playlist != NULL) - return playlist; - } - - return NULL; -} - -bool -playlist_suffix_supported(const char *suffix) -{ - assert(suffix != NULL); - - playlist_plugins_for_each_enabled(plugin) { - if (plugin->suffixes != NULL && - string_array_contains(plugin->suffixes, suffix)) - return true; - } - - return false; -} - -struct playlist_provider * -playlist_list_open_path(const char *path_fs, GMutex *mutex, GCond *cond, - struct input_stream **is_r) -{ - GError *error = NULL; - const char *suffix; - struct input_stream *is; - struct playlist_provider *playlist; - - assert(path_fs != NULL); - - suffix = uri_get_suffix(path_fs); - if (suffix == NULL || !playlist_suffix_supported(suffix)) - return NULL; - - is = input_stream_open(path_fs, mutex, cond, &error); - if (is == NULL) { - if (error != NULL) { - 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) - *is_r = is; - else - input_stream_close(is); - - return playlist; -} diff --git a/src/playlist_list.h b/src/playlist_list.h deleted file mode 100644 index c3967d5ae..000000000 --- a/src/playlist_list.h +++ /dev/null @@ -1,85 +0,0 @@ -/* - * 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_LIST_H -#define MPD_PLAYLIST_LIST_H - -#include <glib.h> - -#include <stdbool.h> - -struct playlist_provider; -struct input_stream; - -extern const struct playlist_plugin *const playlist_plugins[]; - -#define playlist_plugins_for_each(plugin) \ - for (const struct playlist_plugin *plugin, \ - *const*playlist_plugin_iterator = &playlist_plugins[0]; \ - (plugin = *playlist_plugin_iterator) != NULL; \ - ++playlist_plugin_iterator) - -/** - * Initializes all playlist plugins. - */ -void -playlist_list_global_init(void); - -/** - * Deinitializes all playlist plugins. - */ -void -playlist_list_global_finish(void); - -/** - * Opens a playlist by its URI. - */ -struct playlist_provider * -playlist_list_open_uri(const char *uri, GMutex *mutex, GCond *cond); - -/** - * Opens a playlist from an input stream. - * - * @param is an #input_stream object which is open and ready - * @param uri optional URI which was used to open the stream; may be - * used to select the appropriate playlist plugin - */ -struct playlist_provider * -playlist_list_open_stream(struct input_stream *is, const char *uri); - -/** - * Determines if there is a playlist plugin which can handle the - * specified file name suffix. - */ -bool -playlist_suffix_supported(const char *suffix); - -/** - * Opens a playlist from a local file. - * - * @param path_fs the path of the playlist file - * @param is_r on success, an input_stream object is returned here, - * which must be closed after the playlist_provider object is freed - * @return a playlist, or NULL on error - */ -struct playlist_provider * -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 deleted file mode 100644 index 13adb80d0..000000000 --- a/src/playlist_mapper.c +++ /dev/null @@ -1,110 +0,0 @@ -/* - * 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_mapper.h" -#include "playlist_list.h" -#include "stored_playlist.h" -#include "mapper.h" -#include "uri.h" - -#include <assert.h> - -static struct playlist_provider * -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, mutex, cond); - if (playlist != NULL) - *is_r = NULL; - else - playlist = playlist_list_open_path(path_fs, mutex, cond, is_r); - - return playlist; -} - -/** - * Load a playlist from the configured playlist directory. - */ -static struct playlist_provider * -playlist_open_in_playlist_dir(const char *uri, GMutex *mutex, GCond *cond, - struct input_stream **is_r) -{ - char *path_fs; - - assert(spl_valid_name(uri)); - - const char *playlist_directory_fs = map_spl_path(); - if (playlist_directory_fs == NULL) - return NULL; - - path_fs = g_build_filename(playlist_directory_fs, uri, NULL); - - struct playlist_provider *playlist = - playlist_open_path(path_fs, mutex, cond, is_r); - g_free(path_fs); - - return playlist; -} - -/** - * Load a playlist from the configured music directory. - */ -static struct playlist_provider * -playlist_open_in_music_dir(const char *uri, GMutex *mutex, GCond *cond, - struct input_stream **is_r) -{ - char *path_fs; - - assert(uri_safe_local(uri)); - - path_fs = map_uri_fs(uri); - if (path_fs == NULL) - return NULL; - - 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, 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, mutex, cond, - is_r); - if (playlist != NULL) - return playlist; - } - - if (uri_safe_local(uri)) { - playlist = playlist_open_in_music_dir(uri, mutex, cond, is_r); - if (playlist != NULL) - return playlist; - } - - return NULL; -} diff --git a/src/playlist_mapper.h b/src/playlist_mapper.h deleted file mode 100644 index 9a7187d93..000000000 --- a/src/playlist_mapper.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * 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_MAPPER_H -#define MPD_PLAYLIST_MAPPER_H - -#include <glib.h> - -struct input_stream; - -/** - * Opens a playlist from an URI relative to the playlist or music - * directory. - * - * @param is_r on success, an input_stream object may be returned - * here, which must be closed after the playlist_provider object is - * freed - */ -struct playlist_provider * -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 deleted file mode 100644 index a27f651c0..000000000 --- a/src/playlist_plugin.h +++ /dev/null @@ -1,141 +0,0 @@ -/* - * 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_PLUGIN_H -#define MPD_PLAYLIST_PLUGIN_H - -#include <glib.h> - -#include <stdbool.h> -#include <stddef.h> - -struct config_param; -struct input_stream; -struct tag; - -/** - * An object which provides the contents of a playlist. - */ -struct playlist_provider { - const struct playlist_plugin *plugin; -}; - -static inline void -playlist_provider_init(struct playlist_provider *playlist, - const struct playlist_plugin *plugin) -{ - playlist->plugin = plugin; -} - -struct playlist_plugin { - const char *name; - - /** - * Initialize the plugin. Optional method. - * - * @param param a configuration block for this plugin, or NULL - * if none is configured - * @return true if the plugin was initialized successfully, - * false if the plugin is not available - */ - bool (*init)(const struct config_param *param); - - /** - * Deinitialize a plugin which was initialized successfully. - * Optional method. - */ - void (*finish)(void); - - /** - * 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, - GMutex *mutex, GCond *cond); - - /** - * Opens the playlist in the specified input stream. It has - * either matched one of the suffixes or one of the MIME - * types. - */ - struct playlist_provider *(*open_stream)(struct input_stream *is); - - void (*close)(struct playlist_provider *playlist); - - struct song *(*read)(struct playlist_provider *playlist); - - const char *const*schemes; - const char *const*suffixes; - const char *const*mime_types; -}; - -/** - * Initialize a plugin. - * - * @param param a configuration block for this plugin, or NULL if none - * is configured - * @return true if the plugin was initialized successfully, false if - * the plugin is not available - */ -static inline bool -playlist_plugin_init(const struct playlist_plugin *plugin, - const struct config_param *param) -{ - return plugin->init != NULL - ? plugin->init(param) - : true; -} - -/** - * Deinitialize a plugin which was initialized successfully. - */ -static inline void -playlist_plugin_finish(const struct playlist_plugin *plugin) -{ - if (plugin->finish != NULL) - plugin->finish(); -} - -static inline struct playlist_provider * -playlist_plugin_open_uri(const struct playlist_plugin *plugin, const char *uri, - GMutex *mutex, GCond *cond) -{ - return plugin->open_uri(uri, mutex, cond); -} - -static inline struct playlist_provider * -playlist_plugin_open_stream(const struct playlist_plugin *plugin, - struct input_stream *is) -{ - return plugin->open_stream(is); -} - -static inline void -playlist_plugin_close(struct playlist_provider *playlist) -{ - playlist->plugin->close(playlist); -} - -static inline struct song * -playlist_plugin_read(struct playlist_provider *playlist) -{ - return playlist->plugin->read(playlist); -} - -#endif diff --git a/src/playlist_print.c b/src/playlist_print.c deleted file mode 100644 index 59c42f969..000000000 --- a/src/playlist_print.c +++ /dev/null @@ -1,199 +0,0 @@ -/* - * 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_print.h" -#include "playlist_list.h" -#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" -#include "song.h" -#include "database.h" -#include "client.h" -#include "input_stream.h" - -void -playlist_print_uris(struct client *client, const struct playlist *playlist) -{ - const struct queue *queue = &playlist->queue; - - queue_print_uris(client, queue, 0, queue_length(queue)); -} - -bool -playlist_print_info(struct client *client, const struct playlist *playlist, - unsigned start, unsigned end) -{ - const struct queue *queue = &playlist->queue; - - if (end > queue_length(queue)) - /* correct the "end" offset */ - end = queue_length(queue); - - if (start > end) - /* an invalid "start" offset is fatal */ - return false; - - queue_print_info(client, queue, start, end); - return true; -} - -bool -playlist_print_id(struct client *client, const struct playlist *playlist, - unsigned id) -{ - const struct queue *queue = &playlist->queue; - int position; - - position = queue_id_to_position(queue, id); - if (position < 0) - /* no such song */ - return false; - - return playlist_print_info(client, playlist, position, position + 1); -} - -bool -playlist_print_current(struct client *client, const struct playlist *playlist) -{ - int current_position = playlist_get_current_song(playlist); - - if (current_position < 0) - return false; - - queue_print_info(client, &playlist->queue, - current_position, current_position + 1); - return true; -} - -void -playlist_print_find(struct client *client, const struct playlist *playlist, - const struct locate_item_list *list) -{ - queue_find(client, &playlist->queue, list); -} - -void -playlist_print_search(struct client *client, const struct playlist *playlist, - const struct locate_item_list *list) -{ - queue_search(client, &playlist->queue, list); -} - -void -playlist_print_changes_info(struct client *client, - const struct playlist *playlist, - uint32_t version) -{ - queue_print_changes_info(client, &playlist->queue, version); -} - -void -playlist_print_changes_position(struct client *client, - const struct playlist *playlist, - uint32_t version) -{ - queue_print_changes_position(client, &playlist->queue, version); -} - -bool -spl_print(struct client *client, const char *name_utf8, bool detail, - GError **error_r) -{ - GPtrArray *list; - - list = spl_load(name_utf8, error_r); - if (list == NULL) - return false; - - for (unsigned i = 0; i < list->len; ++i) { - const char *temp = g_ptr_array_index(list, i); - bool wrote = false; - - if (detail) { - struct song *song = db_get_song(temp); - if (song) { - song_print_info(client, song); - wrote = true; - } - } - - if (!wrote) { - client_printf(client, SONG_FILE "%s\n", temp); - } - } - - spl_free(list); - return true; -} - -static void -playlist_provider_print(struct client *client, const char *uri, - struct playlist_provider *playlist, bool detail) -{ - struct song *song; - 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, false); - if (song == NULL) - continue; - - if (detail) - song_print_info(client, song); - else - song_print_uri(client, song); - - if (!song_in_database(song)) - song_free(song); - } - - g_free(base_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, 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); - - 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 deleted file mode 100644 index d4f1911d2..000000000 --- a/src/playlist_print.h +++ /dev/null @@ -1,117 +0,0 @@ -/* - * 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 PLAYLIST_PRINT_H -#define PLAYLIST_PRINT_H - -#include <glib.h> -#include <stdbool.h> -#include <stdint.h> - -struct client; -struct playlist; -struct locate_item_list; - -/** - * Sends the whole playlist to the client, song URIs only. - */ -void -playlist_print_uris(struct client *client, const struct playlist *playlist); - -/** - * Sends a range of the playlist to the client, including all known - * information about the songs. The "end" offset is decreased - * automatically if it is too large; passing UINT_MAX is allowed. - * This function however fails when the start offset is invalid. - */ -bool -playlist_print_info(struct client *client, const struct playlist *playlist, - unsigned start, unsigned end); - -/** - * Sends the song with the specified id to the client. - * - * @return true on suite, false if there is no such song - */ -bool -playlist_print_id(struct client *client, const struct playlist *playlist, - unsigned id); - -/** - * Sends the current song to the client. - * - * @return true on success, false if there is no current song - */ -bool -playlist_print_current(struct client *client, const struct playlist *playlist); - -/** - * Find songs in the playlist. - */ -void -playlist_print_find(struct client *client, const struct playlist *playlist, - const struct locate_item_list *list); - -/** - * Search for songs in the playlist. - */ -void -playlist_print_search(struct client *client, const struct playlist *playlist, - const struct locate_item_list *list); - -/** - * Print detailed changes since the specified playlist version. - */ -void -playlist_print_changes_info(struct client *client, - const struct playlist *playlist, - uint32_t version); - -/** - * Print changes since the specified playlist version, position only. - */ -void -playlist_print_changes_position(struct client *client, - const struct playlist *playlist, - uint32_t version); - -/** - * Send the stored playlist to the client. - * - * @param client the client which requested the playlist - * @param name_utf8 the name of the stored playlist in UTF-8 encoding - * @param detail true if all details should be printed - * @return true on success, false if the playlist does not exist - */ -bool -spl_print(struct client *client, const char *name_utf8, bool detail, - GError **error_r); - -/** - * Send the playlist file to the client. - * - * @param client the client which requested the playlist - * @param uri the URI of the playlist file in UTF-8 encoding - * @param detail true if all details should be printed - * @return true on success, false if the playlist does not exist - */ -bool -playlist_file_print(struct client *client, const char *uri, bool detail); - -#endif diff --git a/src/playlist_queue.c b/src/playlist_queue.c deleted file mode 100644 index aada94984..000000000 --- a/src/playlist_queue.c +++ /dev/null @@ -1,97 +0,0 @@ -/* - * 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_queue.h" -#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, - 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; - - 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, pc, song, NULL); - if (result != PLAYLIST_RESULT_SUCCESS) { - if (!song_in_database(song)) - song_free(song); - g_free(base_uri); - return result; - } - } - - g_free(base_uri); - - return PLAYLIST_RESULT_SUCCESS; -} - -enum playlist_result -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, 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, 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 deleted file mode 100644 index 24a851aab..000000000 --- a/src/playlist_queue.h +++ /dev/null @@ -1,61 +0,0 @@ -/* - * 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. - */ - -/*! \file - * \brief Glue between playlist plugin and the play queue - */ - -#ifndef MPD_PLAYLIST_QUEUE_H -#define MPD_PLAYLIST_QUEUE_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 - * play queue. - * - * @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, - 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, - 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 deleted file mode 100644 index 334159e0d..000000000 --- a/src/playlist_save.c +++ /dev/null @@ -1,150 +0,0 @@ -/* - * 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_save.h" -#include "playlist.h" -#include "stored_playlist.h" -#include "queue.h" -#include "song.h" -#include "mapper.h" -#include "path.h" -#include "uri.h" -#include "database.h" -#include "idle.h" -#include "glib_compat.h" - -#include <glib.h> - -void -playlist_print_song(FILE *file, const struct song *song) -{ - if (playlist_saveAbsolutePaths && song_in_database(song)) { - char *path = map_song_fs(song); - if (path != NULL) { - fprintf(file, "%s\n", path); - g_free(path); - } - } else { - char *uri = song_get_uri(song), *uri_fs; - - uri_fs = utf8_to_fs_charset(uri); - g_free(uri); - - fprintf(file, "%s\n", uri_fs); - g_free(uri_fs); - } -} - -void -playlist_print_uri(FILE *file, const char *uri) -{ - char *s; - - if (playlist_saveAbsolutePaths && !uri_has_scheme(uri) && - !g_path_is_absolute(uri)) - s = map_uri_fs(uri); - else - s = utf8_to_fs_charset(uri); - - if (s != NULL) { - fprintf(file, "%s\n", s); - g_free(s); - } -} - -enum playlist_result -spl_save_queue(const char *name_utf8, const struct queue *queue) -{ - char *path_fs; - FILE *file; - - if (map_spl_path() == NULL) - return PLAYLIST_RESULT_DISABLED; - - if (!spl_valid_name(name_utf8)) - return PLAYLIST_RESULT_BAD_NAME; - - path_fs = map_spl_utf8_to_fs(name_utf8); - if (path_fs == NULL) - return PLAYLIST_RESULT_BAD_NAME; - - if (g_file_test(path_fs, G_FILE_TEST_EXISTS)) { - g_free(path_fs); - return PLAYLIST_RESULT_LIST_EXISTS; - } - - file = fopen(path_fs, "w"); - g_free(path_fs); - - if (file == NULL) - return PLAYLIST_RESULT_ERRNO; - - for (unsigned i = 0; i < queue_length(queue); i++) - playlist_print_song(file, queue_get(queue, i)); - - fclose(file); - - idle_add(IDLE_STORED_PLAYLIST); - return PLAYLIST_RESULT_SUCCESS; -} - -enum playlist_result -spl_save_playlist(const char *name_utf8, const struct playlist *playlist) -{ - return spl_save_queue(name_utf8, &playlist->queue); -} - -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, error_r); - if (list == NULL) - return false; - - if (list->len < end_index) - end_index = list->len; - - for (unsigned i = start_index; i < end_index; ++i) { - const char *temp = g_ptr_array_index(list, i); - if ((playlist_append_uri(playlist, pc, temp, NULL)) != PLAYLIST_RESULT_SUCCESS) { - /* for windows compatibility, convert slashes */ - char *temp2 = g_strdup(temp); - char *p = temp2; - while (*p) { - if (*p == '\\') - *p = '/'; - p++; - } - if ((playlist_append_uri(playlist, pc, temp2, - NULL)) != PLAYLIST_RESULT_SUCCESS) { - g_warning("can't add file \"%s\"", temp2); - } - g_free(temp2); - } - } - - spl_free(list); - return true; -} diff --git a/src/playlist_save.h b/src/playlist_save.h deleted file mode 100644 index a6c31a9a6..000000000 --- a/src/playlist_save.h +++ /dev/null @@ -1,61 +0,0 @@ -/* - * 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_SAVE_H -#define MPD_PLAYLIST_SAVE_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); - -void -playlist_print_uri(FILE *fp, const char *uri); - -/** - * Saves a queue object into a stored playlist file. - */ -enum playlist_result -spl_save_queue(const char *name_utf8, const struct queue *queue); - -/** - * Saves a playlist object into a stored playlist file. - */ -enum playlist_result -spl_save_playlist(const char *name_utf8, const struct playlist *playlist); - -/** - * Loads a stored playlist file, and append all songs to the global - * playlist. - */ -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 deleted file mode 100644 index a3d9ab4d9..000000000 --- a/src/playlist_song.c +++ /dev/null @@ -1,169 +0,0 @@ -/* - * 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_song.h" -#include "database.h" -#include "mapper.h" -#include "song.h" -#include "uri.h" -#include "path.h" -#include "ls.h" -#include "tag.h" - -#include <assert.h> -#include <string.h> - -static void -merge_song_metadata(struct song *dest, const struct song *base, - const struct song *add) -{ - dest->tag = base->tag != NULL - ? (add->tag != NULL - ? tag_merge(base->tag, add->tag) - : tag_dup(base->tag)) - : (add->tag != NULL - ? tag_dup(add->tag) - : NULL); - - dest->mtime = base->mtime; - dest->start_ms = add->start_ms; - dest->end_ms = add->end_ms; -} - -static struct song * -apply_song_metadata(struct song *dest, const struct song *src) -{ - struct song *tmp; - - assert(dest != NULL); - assert(src != NULL); - - if (src->tag == NULL && src->start_ms == 0 && src->end_ms == 0) - return dest; - - if (song_in_database(dest)) { - char *path_fs = map_song_fs(dest); - if (path_fs == NULL) - return dest; - - char *path_utf8 = fs_charset_to_utf8(path_fs); - if (path_utf8 != NULL) - g_free(path_fs); - else - path_utf8 = path_fs; - - tmp = song_file_new(path_utf8, NULL); - g_free(path_utf8); - - merge_song_metadata(tmp, dest, src); - } else { - tmp = song_file_new(dest->uri, NULL); - merge_song_metadata(tmp, dest, src); - } - - if (dest->tag != NULL && dest->tag->time > 0 && - src->start_ms > 0 && src->end_ms == 0 && - src->start_ms / 1000 < (unsigned)dest->tag->time) - /* the range is open-ended, and the playlist plugin - did not know the total length of the song file - (e.g. last track on a CUE file); fix it up here */ - tmp->tag->time = dest->tag->time - src->start_ms / 1000; - - if (!song_in_database(dest)) - song_free(dest); - - return tmp; -} - -static struct song * -playlist_check_load_song(const struct song *song, const char *uri, bool secure) -{ - struct song *dest; - - if (uri_has_scheme(uri)) { - dest = song_remote_new(uri); - } else if (g_path_is_absolute(uri) && secure) { - dest = song_file_load(uri, NULL); - if (dest == NULL) - return NULL; - } else { - dest = db_get_song(uri); - if (dest == NULL) - /* not found in database */ - return NULL; - } - - return apply_song_metadata(dest, song); -} - -struct song * -playlist_check_translate_song(struct song *song, const char *base_uri, - bool secure) -{ - if (song_in_database(song)) - /* already ok */ - return song; - - const char *uri = song->uri; - - if (uri_has_scheme(uri)) { - if (uri_supported_scheme(uri)) - /* valid remote song */ - return song; - else { - /* unsupported remote song */ - song_free(song); - return NULL; - } - } - - if (base_uri != NULL && strcmp(base_uri, ".") == 0) - /* g_path_get_dirname() returns "." when there is no - directory name in the given path; clear that now, - because it would break the database lookup - functions */ - base_uri = NULL; - - if (g_path_is_absolute(uri)) { - /* XXX fs_charset vs utf8? */ - const char *suffix = map_to_relative_path(uri); - assert(suffix != NULL); - - if (suffix != uri) - uri = suffix; - else if (!secure) { - /* local files must be relative to the music - directory when "secure" is enabled */ - song_free(song); - return NULL; - } - - base_uri = NULL; - } - - char *allocated = NULL; - if (base_uri != NULL) - uri = allocated = g_build_filename(base_uri, uri, NULL); - - struct song *dest = playlist_check_load_song(song, uri, secure); - song_free(song); - g_free(allocated); - return dest; -} diff --git a/src/playlist_song.h b/src/playlist_song.h deleted file mode 100644 index ea8786912..000000000 --- a/src/playlist_song.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * 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_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, - bool secure); - -#endif diff --git a/src/playlist_state.c b/src/playlist_state.c deleted file mode 100644 index 4aa2c2c92..000000000 --- a/src/playlist_state.c +++ /dev/null @@ -1,250 +0,0 @@ -/* - * 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. - */ - -/* - * Saving and loading the playlist to/from the state file. - * - */ - -#include "config.h" -#include "playlist_state.h" -#include "playlist.h" -#include "player_control.h" -#include "queue_save.h" -#include "path.h" -#include "text_file.h" -#include "conf.h" - -#include <string.h> -#include <stdlib.h> - -#define PLAYLIST_STATE_FILE_STATE "state: " -#define PLAYLIST_STATE_FILE_RANDOM "random: " -#define PLAYLIST_STATE_FILE_REPEAT "repeat: " -#define PLAYLIST_STATE_FILE_SINGLE "single: " -#define PLAYLIST_STATE_FILE_CONSUME "consume: " -#define PLAYLIST_STATE_FILE_CURRENT "current: " -#define PLAYLIST_STATE_FILE_TIME "time: " -#define PLAYLIST_STATE_FILE_CROSSFADE "crossfade: " -#define PLAYLIST_STATE_FILE_MIXRAMPDB "mixrampdb: " -#define PLAYLIST_STATE_FILE_MIXRAMPDELAY "mixrampdelay: " -#define PLAYLIST_STATE_FILE_PLAYLIST_BEGIN "playlist_begin" -#define PLAYLIST_STATE_FILE_PLAYLIST_END "playlist_end" - -#define PLAYLIST_STATE_FILE_STATE_PLAY "play" -#define PLAYLIST_STATE_FILE_STATE_PAUSE "pause" -#define PLAYLIST_STATE_FILE_STATE_STOP "stop" - -#define PLAYLIST_BUFFER_SIZE 2*MPD_PATH_MAX - -void -playlist_state_save(FILE *fp, const struct playlist *playlist, - struct player_control *pc) -{ - struct player_status player_status; - - pc_get_status(pc, &player_status); - - fputs(PLAYLIST_STATE_FILE_STATE, fp); - - if (playlist->playing) { - switch (player_status.state) { - case PLAYER_STATE_PAUSE: - fputs(PLAYLIST_STATE_FILE_STATE_PAUSE "\n", fp); - break; - default: - fputs(PLAYLIST_STATE_FILE_STATE_PLAY "\n", fp); - } - fprintf(fp, PLAYLIST_STATE_FILE_CURRENT "%i\n", - queue_order_to_position(&playlist->queue, - playlist->current)); - fprintf(fp, PLAYLIST_STATE_FILE_TIME "%i\n", - (int)player_status.elapsed_time); - } else { - fputs(PLAYLIST_STATE_FILE_STATE_STOP "\n", fp); - - if (playlist->current >= 0) - fprintf(fp, PLAYLIST_STATE_FILE_CURRENT "%i\n", - queue_order_to_position(&playlist->queue, - playlist->current)); - } - - fprintf(fp, PLAYLIST_STATE_FILE_RANDOM "%i\n", playlist->queue.random); - fprintf(fp, PLAYLIST_STATE_FILE_REPEAT "%i\n", playlist->queue.repeat); - fprintf(fp, PLAYLIST_STATE_FILE_SINGLE "%i\n", playlist->queue.single); - fprintf(fp, PLAYLIST_STATE_FILE_CONSUME "%i\n", - playlist->queue.consume); - fprintf(fp, PLAYLIST_STATE_FILE_CROSSFADE "%i\n", - (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)); - fputs(PLAYLIST_STATE_FILE_PLAYLIST_BEGIN "\n", fp); - queue_save(fp, &playlist->queue); - fputs(PLAYLIST_STATE_FILE_PLAYLIST_END "\n", fp); -} - -static void -playlist_state_load(FILE *fp, GString *buffer, struct playlist *playlist) -{ - const char *line = read_text_line(fp, buffer); - if (line == NULL) { - g_warning("No playlist in state file"); - return; - } - - while (!g_str_has_prefix(line, PLAYLIST_STATE_FILE_PLAYLIST_END)) { - queue_load_song(fp, buffer, line, &playlist->queue); - - line = read_text_line(fp, buffer); - if (line == NULL) { - g_warning("'" PLAYLIST_STATE_FILE_PLAYLIST_END - "' not found in state file"); - break; - } - } - - queue_increment_version(&playlist->queue); -} - -bool -playlist_state_restore(const char *line, FILE *fp, GString *buffer, - struct playlist *playlist, struct player_control *pc) -{ - int current = -1; - int seek_time = 0; - enum player_state state = PLAYER_STATE_STOP; - bool random_mode = false; - - if (!g_str_has_prefix(line, PLAYLIST_STATE_FILE_STATE)) - return false; - - line += sizeof(PLAYLIST_STATE_FILE_STATE) - 1; - - if (strcmp(line, PLAYLIST_STATE_FILE_STATE_PLAY) == 0) - state = PLAYER_STATE_PLAY; - else if (strcmp(line, PLAYLIST_STATE_FILE_STATE_PAUSE) == 0) - state = PLAYER_STATE_PAUSE; - - while ((line = read_text_line(fp, buffer)) != NULL) { - if (g_str_has_prefix(line, PLAYLIST_STATE_FILE_TIME)) { - seek_time = - atoi(&(line[strlen(PLAYLIST_STATE_FILE_TIME)])); - } else if (g_str_has_prefix(line, PLAYLIST_STATE_FILE_REPEAT)) { - if (strcmp - (&(line[strlen(PLAYLIST_STATE_FILE_REPEAT)]), - "1") == 0) { - playlist_set_repeat(playlist, pc, true); - } else - 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, pc, true); - } else - 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)]), - "1") == 0) { - playlist_set_consume(playlist, true); - } else - playlist_set_consume(playlist, false); - } else if (g_str_has_prefix(line, 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(pc, - atof(line + strlen(PLAYLIST_STATE_FILE_MIXRAMPDB))); - } else if (g_str_has_prefix(line, 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), - "1") == 0; - } else if (g_str_has_prefix(line, PLAYLIST_STATE_FILE_CURRENT)) { - current = atoi(&(line - [strlen - (PLAYLIST_STATE_FILE_CURRENT)])); - } else if (g_str_has_prefix(line, - PLAYLIST_STATE_FILE_PLAYLIST_BEGIN)) { - playlist_state_load(fp, buffer, playlist); - } - } - - 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); - - if (state == PLAYER_STATE_STOP /* && config_option */) - playlist->current = current; - else if (seek_time == 0) - playlist_play(playlist, pc, current); - else - playlist_seek_song(playlist, pc, current, seek_time); - - if (state == PLAYER_STATE_PAUSE) - pc_pause(pc); - } - - return true; -} - -unsigned -playlist_state_get_hash(const struct playlist *playlist, - struct player_control *pc) -{ - struct player_status player_status; - - pc_get_status(pc, &player_status); - - return playlist->queue.version ^ - (player_status.state != PLAYER_STATE_STOP - ? ((int)player_status.elapsed_time << 8) - : 0) ^ - (playlist->current >= 0 - ? (queue_order_to_position(&playlist->queue, - playlist->current) << 16) - : 0) ^ - ((int)pc_get_cross_fade(pc) << 20) ^ - (player_status.state << 24) ^ - (playlist->queue.random << 27) ^ - (playlist->queue.repeat << 28) ^ - (playlist->queue.single << 29) ^ - (playlist->queue.consume << 30) ^ - (playlist->queue.random << 31); -} diff --git a/src/playlist_state.h b/src/playlist_state.h deleted file mode 100644 index f67d01d2c..000000000 --- a/src/playlist_state.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * 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. - */ - -/* - * Saving and loading the playlist to/from the state file. - * - */ - -#ifndef PLAYLIST_STATE_H -#define PLAYLIST_STATE_H - -#include <glib.h> -#include <stdbool.h> -#include <stdio.h> - -struct playlist; -struct player_control; - -void -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 player_control *pc); - -/** - * Generates a hash number for the current state of the playlist and - * the playback options. This is used by timer_save_state_file() to - * determine whether the state has changed and the state file should - * be saved. - */ -unsigned -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 deleted file mode 100644 index 74c7bf089..000000000 --- a/src/playlist_vector.c +++ /dev/null @@ -1,114 +0,0 @@ -/* - * 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_vector.h" -#include "db_lock.h" - -#include <assert.h> -#include <string.h> -#include <glib.h> - -static struct playlist_metadata * -playlist_metadata_new(const char *name, time_t mtime) -{ - assert(name != NULL); - - struct playlist_metadata *pm = g_slice_new(struct playlist_metadata); - pm->name = g_strdup(name); - pm->mtime = mtime; - return pm; -} - -static void -playlist_metadata_free(struct playlist_metadata *pm) -{ - assert(pm != NULL); - assert(pm->name != NULL); - - g_free(pm->name); - g_slice_free(struct playlist_metadata, pm); -} - -void -playlist_vector_deinit(struct list_head *pv) -{ - assert(pv != NULL); - - struct playlist_metadata *pm, *n; - playlist_vector_for_each_safe(pm, n, pv) - playlist_metadata_free(pm); -} - -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 *pm; - playlist_vector_for_each(pm, pv) - if (strcmp(pm->name, name) == 0) - return pm; - - return NULL; -} - -void -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); - list_add_tail(&pm->siblings, pv); -} - -bool -playlist_vector_update_or_add(struct list_head *pv, - const char *name, time_t mtime) -{ - assert(holding_db_lock()); - - struct playlist_metadata *pm = playlist_vector_find(pv, name); - if (pm != NULL) { - if (mtime == pm->mtime) - return false; - - pm->mtime = mtime; - } else - playlist_vector_add(pv, name, mtime); - - return true; -} - -bool -playlist_vector_remove(struct list_head *pv, const char *name) -{ - assert(holding_db_lock()); - - 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 deleted file mode 100644 index 0af6df8b4..000000000 --- a/src/playlist_vector.h +++ /dev/null @@ -1,80 +0,0 @@ -/* - * 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_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 list_head siblings; - - /** - * The UTF-8 encoded name of the playlist file. - */ - char *name; - - time_t mtime; -}; - -void -playlist_vector_deinit(struct list_head *pv); - -/** - * Caller must lock the #db_mutex. - */ -struct playlist_metadata * -playlist_vector_find(struct list_head *pv, const char *name); - -/** - * Caller must lock the #db_mutex. - */ -void -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 list_head *pv, - const char *name, time_t mtime); - -/** - * Caller must lock the #db_mutex. - */ -bool -playlist_vector_remove(struct list_head *pv, const char *name); - -#endif /* SONGVEC_H */ |