From 257a0dee758049586efbf0dc3f0339b0cef03456 Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Sun, 27 Jan 2013 17:38:09 +0100 Subject: playlist/*: convert to C++ --- src/playlist/AsxPlaylistPlugin.cxx | 330 +++++++++++++++++++++++++++ src/playlist/AsxPlaylistPlugin.hxx | 25 +++ src/playlist/CuePlaylistPlugin.cxx | 114 ++++++++++ src/playlist/CuePlaylistPlugin.hxx | 25 +++ src/playlist/DespotifyPlaylistPlugin.cxx | 2 +- src/playlist/EmbeddedCuePlaylistPlugin.cxx | 2 +- src/playlist/ExtM3uPlaylistPlugin.cxx | 165 ++++++++++++++ src/playlist/ExtM3uPlaylistPlugin.hxx | 25 +++ src/playlist/LastFMPlaylistPlugin.cxx | 3 +- src/playlist/M3uPlaylistPlugin.cxx | 98 ++++++++ src/playlist/M3uPlaylistPlugin.hxx | 25 +++ src/playlist/PlsPlaylistPlugin.cxx | 224 +++++++++++++++++++ src/playlist/PlsPlaylistPlugin.hxx | 25 +++ src/playlist/RssPlaylistPlugin.cxx | 327 +++++++++++++++++++++++++++ src/playlist/RssPlaylistPlugin.hxx | 25 +++ src/playlist/SoundCloudPlaylistPlugin.cxx | 2 +- src/playlist/XspfPlaylistPlugin.cxx | 348 +++++++++++++++++++++++++++++ src/playlist/XspfPlaylistPlugin.hxx | 25 +++ src/playlist/asx_playlist_plugin.c | 323 -------------------------- src/playlist/asx_playlist_plugin.h | 25 --- src/playlist/cue_playlist_plugin.c | 108 --------- src/playlist/cue_playlist_plugin.h | 25 --- src/playlist/extm3u_playlist_plugin.c | 162 -------------- src/playlist/extm3u_playlist_plugin.h | 25 --- src/playlist/m3u_playlist_plugin.c | 92 -------- src/playlist/m3u_playlist_plugin.h | 25 --- src/playlist/pls_playlist_plugin.c | 220 ------------------ src/playlist/pls_playlist_plugin.h | 25 --- src/playlist/rss_playlist_plugin.c | 322 -------------------------- src/playlist/rss_playlist_plugin.h | 25 --- src/playlist/xspf_playlist_plugin.c | 343 ---------------------------- src/playlist/xspf_playlist_plugin.h | 25 --- 32 files changed, 1785 insertions(+), 1750 deletions(-) create mode 100644 src/playlist/AsxPlaylistPlugin.cxx create mode 100644 src/playlist/AsxPlaylistPlugin.hxx create mode 100644 src/playlist/CuePlaylistPlugin.cxx create mode 100644 src/playlist/CuePlaylistPlugin.hxx create mode 100644 src/playlist/ExtM3uPlaylistPlugin.cxx create mode 100644 src/playlist/ExtM3uPlaylistPlugin.hxx create mode 100644 src/playlist/M3uPlaylistPlugin.cxx create mode 100644 src/playlist/M3uPlaylistPlugin.hxx create mode 100644 src/playlist/PlsPlaylistPlugin.cxx create mode 100644 src/playlist/PlsPlaylistPlugin.hxx create mode 100644 src/playlist/RssPlaylistPlugin.cxx create mode 100644 src/playlist/RssPlaylistPlugin.hxx create mode 100644 src/playlist/XspfPlaylistPlugin.cxx create mode 100644 src/playlist/XspfPlaylistPlugin.hxx delete mode 100644 src/playlist/asx_playlist_plugin.c delete mode 100644 src/playlist/asx_playlist_plugin.h delete mode 100644 src/playlist/cue_playlist_plugin.c delete mode 100644 src/playlist/cue_playlist_plugin.h delete mode 100644 src/playlist/extm3u_playlist_plugin.c delete mode 100644 src/playlist/extm3u_playlist_plugin.h delete mode 100644 src/playlist/m3u_playlist_plugin.c delete mode 100644 src/playlist/m3u_playlist_plugin.h delete mode 100644 src/playlist/pls_playlist_plugin.c delete mode 100644 src/playlist/pls_playlist_plugin.h delete mode 100644 src/playlist/rss_playlist_plugin.c delete mode 100644 src/playlist/rss_playlist_plugin.h delete mode 100644 src/playlist/xspf_playlist_plugin.c delete mode 100644 src/playlist/xspf_playlist_plugin.h (limited to 'src/playlist') diff --git a/src/playlist/AsxPlaylistPlugin.cxx b/src/playlist/AsxPlaylistPlugin.cxx new file mode 100644 index 00000000..2a787f66 --- /dev/null +++ b/src/playlist/AsxPlaylistPlugin.cxx @@ -0,0 +1,330 @@ +/* + * 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 "input_stream.h" +#include "song.h" +#include "tag.h" + +#include + +#include +#include + +#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). + */ + 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; + + AsxParser() + :songs(nullptr), + 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(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) +{ + 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_remote_new("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 */ + 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) +{ + 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 = g_slist_prepend(parser->songs, + parser->song); + else + song_free(parser->song); + + parser->state = AsxParser::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) +{ + 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 = tag_new(); + tag_add_item_n(parser->song->tag, parser->tag, + text, text_len); + } + + break; + } +} + +static const GMarkupParser asx_parser = { + asx_start_element, + asx_end_element, + asx_text, + nullptr, + nullptr, +}; + +static void +song_free_callback(gpointer data, G_GNUC_UNUSED gpointer user_data) +{ + struct song *song = (struct song *)data; + + song_free(song); +} + +static void +asx_parser_destroy(gpointer data) +{ + AsxParser *parser = (AsxParser *)data; + + if (parser->state >= AsxParser::ENTRY) + song_free(parser->song); + + g_slist_foreach(parser->songs, song_free_callback, NULL); + g_slist_free(parser->songs); +} + +/* + * The playlist object + * + */ + +struct AsxPlaylist { + struct playlist_provider base; + + GSList *songs; +}; + +static struct playlist_provider * +asx_open_stream(struct input_stream *is) +{ + AsxParser parser; + AsxPlaylist *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 #AsxPlaylist object from the parsed song list */ + + playlist = g_new(AsxPlaylist, 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) +{ + AsxPlaylist *playlist = (AsxPlaylist *)_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) +{ + AsxPlaylist *playlist = (AsxPlaylist *)_playlist; + + if (playlist->songs == NULL) + return NULL; + + struct song *song = (struct 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 = { + "asx", + + nullptr, + nullptr, + nullptr, + asx_open_stream, + asx_close, + asx_read, + + nullptr, + asx_suffixes, + asx_mime_types, +}; diff --git a/src/playlist/AsxPlaylistPlugin.hxx b/src/playlist/AsxPlaylistPlugin.hxx new file mode 100644 index 00000000..240c1824 --- /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 00000000..07eb5e24 --- /dev/null +++ b/src/playlist/CuePlaylistPlugin.cxx @@ -0,0 +1,114 @@ +/* + * 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 "tag.h" +#include "song.h" +#include "input_stream.h" + +extern "C" { +#include "text_input_stream.h" +#include "cue/cue_parser.h" +} + +#include +#include +#include + +#undef G_LOG_DOMAIN +#define G_LOG_DOMAIN "cue" + +struct CuePlaylist { + 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) +{ + CuePlaylist *playlist = g_new(CuePlaylist, 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) +{ + CuePlaylist *playlist = (CuePlaylist *)_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) +{ + CuePlaylist *playlist = (CuePlaylist *)_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 = { + "cue", + + nullptr, + nullptr, + nullptr, + cue_playlist_open_stream, + cue_playlist_close, + cue_playlist_read, + + 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 00000000..cf5e3a8f --- /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 index 3466d568..7f647d58 100644 --- a/src/playlist/DespotifyPlaylistPlugin.cxx +++ b/src/playlist/DespotifyPlaylistPlugin.cxx @@ -20,7 +20,7 @@ #include "config.h" #include "DespotifyPlaylistPlugin.hxx" #include "DespotifyUtils.hxx" -#include "playlist_plugin.h" +#include "PlaylistPlugin.hxx" #include "PlaylistRegistry.hxx" #include "conf.h" #include "uri.h" diff --git a/src/playlist/EmbeddedCuePlaylistPlugin.cxx b/src/playlist/EmbeddedCuePlaylistPlugin.cxx index 5cd941df..f420aebf 100644 --- a/src/playlist/EmbeddedCuePlaylistPlugin.cxx +++ b/src/playlist/EmbeddedCuePlaylistPlugin.cxx @@ -25,7 +25,7 @@ #include "config.h" #include "EmbeddedCuePlaylistPlugin.hxx" -#include "playlist_plugin.h" +#include "PlaylistPlugin.hxx" #include "tag.h" #include "tag_handler.h" #include "song.h" diff --git a/src/playlist/ExtM3uPlaylistPlugin.cxx b/src/playlist/ExtM3uPlaylistPlugin.cxx new file mode 100644 index 00000000..ce026dab --- /dev/null +++ b/src/playlist/ExtM3uPlaylistPlugin.cxx @@ -0,0 +1,165 @@ +/* + * 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 "song.h" +#include "tag.h" +#include "string_util.h" + +extern "C" { +#include "text_input_stream.h" +} + +#include + +#include +#include + +struct ExtM3uPlaylist { + struct playlist_provider base; + + struct text_input_stream *tis; +}; + +static struct playlist_provider * +extm3u_open_stream(struct input_stream *is) +{ + ExtM3uPlaylist *playlist = g_new(ExtM3uPlaylist, 1); + playlist->tis = text_input_stream_new(is); + + const char *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) +{ + ExtM3uPlaylist *playlist = (ExtM3uPlaylist *)_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) +{ + ExtM3uPlaylist *playlist = (ExtM3uPlaylist *)_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 = { + "extm3u", + + nullptr, + nullptr, + nullptr, + extm3u_open_stream, + extm3u_close, + extm3u_read, + + nullptr, + extm3u_suffixes, + extm3u_mime_types, +}; diff --git a/src/playlist/ExtM3uPlaylistPlugin.hxx b/src/playlist/ExtM3uPlaylistPlugin.hxx new file mode 100644 index 00000000..844fba15 --- /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/LastFMPlaylistPlugin.cxx b/src/playlist/LastFMPlaylistPlugin.cxx index 36c89fea..3898ce31 100644 --- a/src/playlist/LastFMPlaylistPlugin.cxx +++ b/src/playlist/LastFMPlaylistPlugin.cxx @@ -19,10 +19,9 @@ #include "config.h" #include "LastFMPlaylistPlugin.hxx" -#include "playlist_plugin.h" +#include "PlaylistPlugin.hxx" #include "PlaylistRegistry.hxx" #include "conf.h" -#include "uri.h" #include "song.h" #include "input_stream.h" diff --git a/src/playlist/M3uPlaylistPlugin.cxx b/src/playlist/M3uPlaylistPlugin.cxx new file mode 100644 index 00000000..eeecd277 --- /dev/null +++ b/src/playlist/M3uPlaylistPlugin.cxx @@ -0,0 +1,98 @@ +/* + * 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 "song.h" + +extern "C" { +#include "text_input_stream.h" +} + +#include + +struct M3uPlaylist { + struct playlist_provider base; + + struct text_input_stream *tis; +}; + +static struct playlist_provider * +m3u_open_stream(struct input_stream *is) +{ + M3uPlaylist *playlist = g_new(M3uPlaylist, 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) +{ + M3uPlaylist *playlist = (M3uPlaylist *)_playlist; + + text_input_stream_free(playlist->tis); + g_free(playlist); +} + +static struct song * +m3u_read(struct playlist_provider *_playlist) +{ + M3uPlaylist *playlist = (M3uPlaylist *)_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 = { + "m3u", + + nullptr, + nullptr, + nullptr, + m3u_open_stream, + m3u_close, + m3u_read, + + nullptr, + m3u_suffixes, + m3u_mime_types, +}; diff --git a/src/playlist/M3uPlaylistPlugin.hxx b/src/playlist/M3uPlaylistPlugin.hxx new file mode 100644 index 00000000..a2058bb2 --- /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 00000000..8cb8b4f1 --- /dev/null +++ b/src/playlist/PlsPlaylistPlugin.cxx @@ -0,0 +1,224 @@ +/* + * 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 "input_stream.h" +#include "uri.h" +#include "song.h" +#include "tag.h" + +#include + +struct PlsPlaylist { + struct playlist_provider base; + + GSList *songs; +}; + +static void pls_parser(GKeyFile *keyfile, PlsPlaylist *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; + PlsPlaylist *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(PlsPlaylist, 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 = (struct song *)data; + + song_free(song); +} + +static void +pls_close(struct playlist_provider *_playlist) +{ + PlsPlaylist *playlist = (PlsPlaylist *)_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) +{ + PlsPlaylist *playlist = (PlsPlaylist *)_playlist; + + if (playlist->songs == NULL) + return NULL; + + struct song *song = (struct 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 = { + "pls", + + nullptr, + nullptr, + nullptr, + pls_open_stream, + pls_close, + pls_read, + + nullptr, + pls_suffixes, + pls_mime_types, +}; diff --git a/src/playlist/PlsPlaylistPlugin.hxx b/src/playlist/PlsPlaylistPlugin.hxx new file mode 100644 index 00000000..3fafd36d --- /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 00000000..9b95683c --- /dev/null +++ b/src/playlist/RssPlaylistPlugin.cxx @@ -0,0 +1,327 @@ +/* + * 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 "input_stream.h" +#include "song.h" +#include "tag.h" + +#include + +#include +#include + +#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). + */ + 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; + + RssParser() + :songs(nullptr), 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(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) +{ + 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_remote_new("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 */ + 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) +{ + 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 = g_slist_prepend(parser->songs, + parser->song); + else + song_free(parser->song); + + parser->state = RssParser::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) +{ + 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 = tag_new(); + tag_add_item_n(parser->song->tag, parser->tag, + text, text_len); + } + + break; + } +} + +static const GMarkupParser rss_parser = { + rss_start_element, + rss_end_element, + rss_text, + nullptr, + nullptr, +}; + +static void +song_free_callback(gpointer data, G_GNUC_UNUSED gpointer user_data) +{ + struct song *song = (struct song *)data; + + song_free(song); +} + +static void +rss_parser_destroy(gpointer data) +{ + RssParser *parser = (RssParser *)data; + + if (parser->state >= RssParser::ITEM) + song_free(parser->song); + + g_slist_foreach(parser->songs, song_free_callback, NULL); + g_slist_free(parser->songs); +} + +/* + * The playlist object + * + */ + +struct RssPlaylist { + struct playlist_provider base; + + GSList *songs; +}; + +static struct playlist_provider * +rss_open_stream(struct input_stream *is) +{ + RssParser parser; + RssPlaylist *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(RssPlaylist, 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) +{ + RssPlaylist *playlist = (RssPlaylist *)_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) +{ + RssPlaylist *playlist = (RssPlaylist *)_playlist; + + if (playlist->songs == NULL) + return NULL; + + struct song *song = (struct 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 = { + "rss", + + nullptr, + nullptr, + nullptr, + rss_open_stream, + rss_close, + rss_read, + + nullptr, + rss_suffixes, + rss_mime_types, +}; diff --git a/src/playlist/RssPlaylistPlugin.hxx b/src/playlist/RssPlaylistPlugin.hxx new file mode 100644 index 00000000..f49f7e9c --- /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 index 71a2af6c..fd2a99c6 100644 --- a/src/playlist/SoundCloudPlaylistPlugin.cxx +++ b/src/playlist/SoundCloudPlaylistPlugin.cxx @@ -19,9 +19,9 @@ #include "config.h" #include "SoundCloudPlaylistPlugin.hxx" +#include "PlaylistPlugin.hxx" #include "conf.h" #include "input_stream.h" -#include "playlist_plugin.h" #include "song.h" #include "tag.h" diff --git a/src/playlist/XspfPlaylistPlugin.cxx b/src/playlist/XspfPlaylistPlugin.cxx new file mode 100644 index 00000000..95033d4e --- /dev/null +++ b/src/playlist/XspfPlaylistPlugin.cxx @@ -0,0 +1,348 @@ +/* + * 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 "input_stream.h" +#include "uri.h" +#include "song.h" +#include "tag.h" + +#include + +#include +#include + +#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). + */ + 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; + + XspfParser() + :songs(nullptr), state(ROOT) {} +}; + +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) +{ + 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(G_GNUC_UNUSED GMarkupParseContext *context, + const gchar *element_name, + gpointer user_data, G_GNUC_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 = g_slist_prepend(parser->songs, + 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(G_GNUC_UNUSED GMarkupParseContext *context, + const gchar *text, gsize text_len, + gpointer user_data, G_GNUC_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 = tag_new(); + tag_add_item_n(parser->song->tag, parser->tag, + text, text_len); + } + + break; + + case XspfParser::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 = { + xspf_start_element, + xspf_end_element, + xspf_text, + nullptr, + nullptr, +}; + +static void +song_free_callback(gpointer data, G_GNUC_UNUSED gpointer user_data) +{ + struct song *song = (struct song *)data; + + song_free(song); +} + +static void +xspf_parser_destroy(gpointer data) +{ + XspfParser *parser = (XspfParser *)data; + + if (parser->state >= XspfParser::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 XspfPlaylist { + struct playlist_provider base; + + GSList *songs; +}; + +static struct playlist_provider * +xspf_open_stream(struct input_stream *is) +{ + XspfParser parser; + XspfPlaylist *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(XspfPlaylist, 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) +{ + XspfPlaylist *playlist = (XspfPlaylist *)_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) +{ + XspfPlaylist *playlist = (XspfPlaylist *)_playlist; + + if (playlist->songs == NULL) + return NULL; + + struct song *song = (struct 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 = { + "xspf", + + nullptr, + nullptr, + nullptr, + xspf_open_stream, + xspf_close, + xspf_read, + + nullptr, + xspf_suffixes, + xspf_mime_types, +}; diff --git a/src/playlist/XspfPlaylistPlugin.hxx b/src/playlist/XspfPlaylistPlugin.hxx new file mode 100644 index 00000000..fc9bbd2c --- /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 29868785..00000000 --- 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 - -#include -#include - -#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 6c01c120..00000000 --- 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 b85de77d..00000000 --- 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 -#include -#include - -#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 c02e2235..00000000 --- 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/extm3u_playlist_plugin.c b/src/playlist/extm3u_playlist_plugin.c deleted file mode 100644 index 19be8d1c..00000000 --- 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 - -#include -#include - -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 5f611ac9..00000000 --- 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/m3u_playlist_plugin.c b/src/playlist/m3u_playlist_plugin.c deleted file mode 100644 index 45b70d2b..00000000 --- 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 - -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 3890a5fc..00000000 --- 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 c4e5492a..00000000 --- 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 - -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 d03435f6..00000000 --- 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 6740cba7..00000000 --- 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 - -#include -#include - -#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 3b376de7..00000000 --- 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/xspf_playlist_plugin.c b/src/playlist/xspf_playlist_plugin.c deleted file mode 100644 index 17d9040e..00000000 --- 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 - -#include -#include - -#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 4636d7e8..00000000 --- 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 -- cgit v1.2.3