From 86b0adc82ce64bb08518088c35ce754dc96d7062 Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Sat, 26 Jan 2013 01:04:02 +0100 Subject: playlist/*: convert to C++ --- Makefile.am | 16 +- src/CommandLine.cxx | 2 +- src/Main.cxx | 2 +- src/PlaylistAny.cxx | 2 +- src/PlaylistMapper.cxx | 2 +- src/PlaylistPrint.cxx | 2 +- src/PlaylistRegistry.cxx | 352 ++++++++++++++++++++++ src/PlaylistRegistry.hxx | 85 ++++++ src/UpdateWalk.cxx | 2 +- src/playlist/DespotifyPlaylistPlugin.cxx | 2 +- src/playlist/EmbeddedCuePlaylistPlugin.cxx | 190 ++++++++++++ src/playlist/EmbeddedCuePlaylistPlugin.hxx | 25 ++ src/playlist/LastFMPlaylistPlugin.cxx | 299 +++++++++++++++++++ src/playlist/LastFMPlaylistPlugin.hxx | 25 ++ src/playlist/SoundCloudPlaylistPlugin.cxx | 451 +++++++++++++++++++++++++++++ src/playlist/SoundCloudPlaylistPlugin.hxx | 25 ++ src/playlist/embcue_playlist_plugin.c | 181 ------------ src/playlist/embcue_playlist_plugin.h | 25 -- src/playlist/lastfm_playlist_plugin.c | 296 ------------------- src/playlist/lastfm_playlist_plugin.h | 25 -- src/playlist/soundcloud_playlist_plugin.c | 448 ---------------------------- src/playlist/soundcloud_playlist_plugin.h | 25 -- src/playlist_list.c | 348 ---------------------- src/playlist_list.h | 85 ------ test/dump_playlist.cxx | 4 +- 25 files changed, 1469 insertions(+), 1450 deletions(-) create mode 100644 src/PlaylistRegistry.cxx create mode 100644 src/PlaylistRegistry.hxx create mode 100644 src/playlist/EmbeddedCuePlaylistPlugin.cxx create mode 100644 src/playlist/EmbeddedCuePlaylistPlugin.hxx create mode 100644 src/playlist/LastFMPlaylistPlugin.cxx create mode 100644 src/playlist/LastFMPlaylistPlugin.hxx create mode 100644 src/playlist/SoundCloudPlaylistPlugin.cxx create mode 100644 src/playlist/SoundCloudPlaylistPlugin.hxx delete mode 100644 src/playlist/embcue_playlist_plugin.c delete mode 100644 src/playlist/embcue_playlist_plugin.h delete mode 100644 src/playlist/lastfm_playlist_plugin.c delete mode 100644 src/playlist/lastfm_playlist_plugin.h delete mode 100644 src/playlist/soundcloud_playlist_plugin.c delete mode 100644 src/playlist/soundcloud_playlist_plugin.h delete mode 100644 src/playlist_list.c delete mode 100644 src/playlist_list.h diff --git a/Makefile.am b/Makefile.am index d92ea9d5d..577055d51 100644 --- a/Makefile.am +++ b/Makefile.am @@ -97,14 +97,12 @@ mpd_headers = \ src/Playlist.hxx \ src/playlist_error.h \ src/playlist_plugin.h \ - src/playlist_list.h \ src/playlist/extm3u_playlist_plugin.h \ src/playlist/m3u_playlist_plugin.h \ src/playlist/pls_playlist_plugin.h \ src/playlist/xspf_playlist_plugin.h \ src/playlist/asx_playlist_plugin.h \ src/playlist/rss_playlist_plugin.h \ - src/playlist/lastfm_playlist_plugin.h \ src/playlist/cue_playlist_plugin.h \ src/poison.h \ src/riff.h \ @@ -928,9 +926,9 @@ libplaylist_plugins_a_SOURCES = \ src/playlist/asx_playlist_plugin.c \ src/playlist/rss_playlist_plugin.c \ src/playlist/cue_playlist_plugin.c \ - src/playlist/embcue_playlist_plugin.c \ - src/playlist/embcue_playlist_plugin.h \ - src/playlist_list.c + src/playlist/EmbeddedCuePlaylistPlugin.cxx \ + src/playlist/EmbeddedCuePlaylistPlugin.hxx \ + src/PlaylistRegistry.cxx src/PlaylistRegistry.hxx libplaylist_plugins_a_CPPFLAGS = $(AM_CPPFLAGS) \ $(YAJL_CFLAGS) \ $(patsubst -I%/FLAC,-I%,$(FLAC_CFLAGS)) @@ -940,7 +938,9 @@ PLAYLIST_LIBS = \ $(FLAC_LIBS) if ENABLE_LASTFM -libplaylist_plugins_a_SOURCES += src/playlist/lastfm_playlist_plugin.c +libplaylist_plugins_a_SOURCES += \ + src/playlist/LastFMPlaylistPlugin.cxx \ + src/playlist/LastFMPlaylistPlugin.hxx endif if ENABLE_DESPOTIFY @@ -951,8 +951,8 @@ endif if ENABLE_SOUNDCLOUD libplaylist_plugins_a_SOURCES += \ - src/playlist/soundcloud_playlist_plugin.h \ - src/playlist/soundcloud_playlist_plugin.c + src/playlist/SoundCloudPlaylistPlugin.cxx \ + src/playlist/SoundCloudPlaylistPlugin.hxx PLAYLIST_LIBS += $(YAJL_LIBS) endif diff --git a/src/CommandLine.cxx b/src/CommandLine.cxx index 6b7a8b1ca..c0b449859 100644 --- a/src/CommandLine.cxx +++ b/src/CommandLine.cxx @@ -28,7 +28,7 @@ #include "output_plugin.h" #include "InputRegistry.hxx" #include "InputPlugin.hxx" -#include "playlist_list.h" +#include "PlaylistRegistry.hxx" #include "playlist_plugin.h" #include "mpd_error.h" #include "glib_compat.h" diff --git a/src/Main.cxx b/src/Main.cxx index 1bacc0e3e..32374bcca 100644 --- a/src/Main.cxx +++ b/src/Main.cxx @@ -48,6 +48,7 @@ #include "event/Loop.hxx" #include "IOThread.hxx" #include "fs/Path.hxx" +#include "PlaylistRegistry.hxx" extern "C" { #include "daemon.h" @@ -55,7 +56,6 @@ extern "C" { #include "audio_config.h" #include "pcm_resample.h" #include "decoder_list.h" -#include "playlist_list.h" #include "zeroconf.h" } diff --git a/src/PlaylistAny.cxx b/src/PlaylistAny.cxx index f62634c96..5c3073682 100644 --- a/src/PlaylistAny.cxx +++ b/src/PlaylistAny.cxx @@ -20,10 +20,10 @@ #include "config.h" #include "PlaylistAny.hxx" #include "PlaylistMapper.hxx" +#include "PlaylistRegistry.hxx" #include "input_stream.h" extern "C" { -#include "playlist_list.h" #include "uri.h" } diff --git a/src/PlaylistMapper.cxx b/src/PlaylistMapper.cxx index d63681501..aa37453fb 100644 --- a/src/PlaylistMapper.cxx +++ b/src/PlaylistMapper.cxx @@ -20,11 +20,11 @@ #include "config.h" #include "PlaylistMapper.hxx" #include "PlaylistFile.hxx" +#include "PlaylistRegistry.hxx" #include "Mapper.hxx" #include "fs/Path.hxx" extern "C" { -#include "playlist_list.h" #include "uri.h" } diff --git a/src/PlaylistPrint.cxx b/src/PlaylistPrint.cxx index e6ece7e6b..5bde8238a 100644 --- a/src/PlaylistPrint.cxx +++ b/src/PlaylistPrint.cxx @@ -23,6 +23,7 @@ #include "PlaylistAny.hxx" #include "PlaylistSong.hxx" #include "Playlist.hxx" +#include "PlaylistRegistry.hxx" #include "QueuePrint.hxx" #include "SongPrint.hxx" #include "DatabaseGlue.hxx" @@ -31,7 +32,6 @@ #include "input_stream.h" extern "C" { -#include "playlist_list.h" #include "playlist_plugin.h" #include "song.h" } diff --git a/src/PlaylistRegistry.cxx b/src/PlaylistRegistry.cxx new file mode 100644 index 000000000..f919f388b --- /dev/null +++ b/src/PlaylistRegistry.cxx @@ -0,0 +1,352 @@ +/* + * 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 "PlaylistRegistry.hxx" +#include "playlist_plugin.h" +#include "playlist/extm3u_playlist_plugin.h" +#include "playlist/m3u_playlist_plugin.h" +#include "playlist/xspf_playlist_plugin.h" +#include "playlist/LastFMPlaylistPlugin.hxx" +#include "playlist/DespotifyPlaylistPlugin.hxx" +#include "playlist/SoundCloudPlaylistPlugin.hxx" +#include "playlist/pls_playlist_plugin.h" +#include "playlist/asx_playlist_plugin.h" +#include "playlist/rss_playlist_plugin.h" +#include "playlist/cue_playlist_plugin.h" +#include "playlist/EmbeddedCuePlaylistPlugin.hxx" +#include "input_stream.h" + +extern "C" { +#include "uri.h" +} + +#include "string_util.h" +#include "conf.h" +#include "mpd_error.h" + +#include +#include +#include + +const struct playlist_plugin *const playlist_plugins[] = { + &extm3u_playlist_plugin, + &m3u_playlist_plugin, + &xspf_playlist_plugin, + &pls_playlist_plugin, + &asx_playlist_plugin, + &rss_playlist_plugin, +#ifdef ENABLE_DESPOTIFY + &despotify_playlist_plugin, +#endif +#ifdef ENABLE_LASTFM + &lastfm_playlist_plugin, +#endif +#ifdef ENABLE_SOUNDCLOUD + &soundcloud_playlist_plugin, +#endif + &cue_playlist_plugin, + &embcue_playlist_plugin, + NULL +}; + +/** which plugins have been initialized successfully? */ +static bool playlist_plugins_enabled[G_N_ELEMENTS(playlist_plugins)]; + +#define playlist_plugins_for_each_enabled(plugin) \ + playlist_plugins_for_each(plugin) \ + if (playlist_plugins_enabled[playlist_plugin_iterator - playlist_plugins]) + +/** + * Find the "playlist" configuration block for the specified plugin. + * + * @param plugin_name the name of the playlist plugin + * @return the configuration block, or NULL if none was configured + */ +static const struct config_param * +playlist_plugin_config(const char *plugin_name) +{ + const struct config_param *param = NULL; + + assert(plugin_name != NULL); + + while ((param = config_get_next_param(CONF_PLAYLIST_PLUGIN, param)) != NULL) { + const char *name = + config_get_block_string(param, "name", NULL); + if (name == NULL) + MPD_ERROR("playlist configuration without 'plugin' name in line %d", + param->line); + + if (strcmp(name, plugin_name) == 0) + return param; + } + + return NULL; +} + +void +playlist_list_global_init(void) +{ + for (unsigned i = 0; playlist_plugins[i] != NULL; ++i) { + const struct playlist_plugin *plugin = playlist_plugins[i]; + const struct config_param *param = + playlist_plugin_config(plugin->name); + + if (!config_get_block_bool(param, "enabled", true)) + /* the plugin is disabled in mpd.conf */ + continue; + + playlist_plugins_enabled[i] = + playlist_plugin_init(playlist_plugins[i], param); + } +} + +void +playlist_list_global_finish(void) +{ + playlist_plugins_for_each_enabled(plugin) + playlist_plugin_finish(plugin); +} + +static struct playlist_provider * +playlist_list_open_uri_scheme(const char *uri, GMutex *mutex, GCond *cond, + bool *tried) +{ + char *scheme; + struct playlist_provider *playlist = NULL; + + assert(uri != NULL); + + scheme = g_uri_parse_scheme(uri); + if (scheme == NULL) + return NULL; + + for (unsigned i = 0; playlist_plugins[i] != NULL; ++i) { + const struct playlist_plugin *plugin = playlist_plugins[i]; + + assert(!tried[i]); + + if (playlist_plugins_enabled[i] && plugin->open_uri != NULL && + plugin->schemes != NULL && + string_array_contains(plugin->schemes, scheme)) { + playlist = playlist_plugin_open_uri(plugin, uri, + mutex, cond); + if (playlist != NULL) + break; + + tried[i] = true; + } + } + + g_free(scheme); + return playlist; +} + +static struct playlist_provider * +playlist_list_open_uri_suffix(const char *uri, GMutex *mutex, GCond *cond, + const bool *tried) +{ + const char *suffix; + struct playlist_provider *playlist = NULL; + + assert(uri != NULL); + + suffix = uri_get_suffix(uri); + if (suffix == NULL) + return NULL; + + for (unsigned i = 0; playlist_plugins[i] != NULL; ++i) { + const struct playlist_plugin *plugin = playlist_plugins[i]; + + if (playlist_plugins_enabled[i] && !tried[i] && + plugin->open_uri != NULL && plugin->suffixes != NULL && + string_array_contains(plugin->suffixes, suffix)) { + playlist = playlist_plugin_open_uri(plugin, uri, + mutex, cond); + if (playlist != NULL) + break; + } + } + + return playlist; +} + +struct playlist_provider * +playlist_list_open_uri(const char *uri, GMutex *mutex, GCond *cond) +{ + struct playlist_provider *playlist; + /** this array tracks which plugins have already been tried by + playlist_list_open_uri_scheme() */ + bool tried[G_N_ELEMENTS(playlist_plugins) - 1]; + + assert(uri != NULL); + + memset(tried, false, sizeof(tried)); + + playlist = playlist_list_open_uri_scheme(uri, mutex, cond, tried); + if (playlist == NULL) + playlist = playlist_list_open_uri_suffix(uri, mutex, cond, + tried); + + return playlist; +} + +static struct playlist_provider * +playlist_list_open_stream_mime2(struct input_stream *is, const char *mime) +{ + struct playlist_provider *playlist; + + assert(is != NULL); + assert(mime != NULL); + + playlist_plugins_for_each_enabled(plugin) { + if (plugin->open_stream != NULL && + plugin->mime_types != NULL && + string_array_contains(plugin->mime_types, mime)) { + /* rewind the stream, so each plugin gets a + fresh start */ + input_stream_seek(is, 0, SEEK_SET, NULL); + + playlist = playlist_plugin_open_stream(plugin, is); + if (playlist != NULL) + return playlist; + } + } + + return NULL; +} + +static struct playlist_provider * +playlist_list_open_stream_mime(struct input_stream *is) +{ + assert(is->mime != NULL); + + const char *semicolon = strchr(is->mime, ';'); + if (semicolon == NULL) + return playlist_list_open_stream_mime2(is, is->mime); + + if (semicolon == is->mime) + return NULL; + + /* probe only the portion before the semicolon*/ + char *mime = g_strndup(is->mime, semicolon - is->mime); + struct playlist_provider *playlist = + playlist_list_open_stream_mime2(is, mime); + g_free(mime); + return playlist; +} + +static struct playlist_provider * +playlist_list_open_stream_suffix(struct input_stream *is, const char *suffix) +{ + struct playlist_provider *playlist; + + assert(is != NULL); + assert(suffix != NULL); + + playlist_plugins_for_each_enabled(plugin) { + if (plugin->open_stream != NULL && + plugin->suffixes != NULL && + string_array_contains(plugin->suffixes, suffix)) { + /* rewind the stream, so each plugin gets a + fresh start */ + input_stream_seek(is, 0, SEEK_SET, NULL); + + playlist = playlist_plugin_open_stream(plugin, is); + if (playlist != NULL) + return playlist; + } + } + + return NULL; +} + +struct playlist_provider * +playlist_list_open_stream(struct input_stream *is, const char *uri) +{ + const char *suffix; + struct playlist_provider *playlist; + + input_stream_lock_wait_ready(is); + + if (is->mime != NULL) { + playlist = playlist_list_open_stream_mime(is); + if (playlist != NULL) + return playlist; + } + + suffix = uri != NULL ? uri_get_suffix(uri) : NULL; + if (suffix != NULL) { + playlist = playlist_list_open_stream_suffix(is, suffix); + if (playlist != NULL) + return playlist; + } + + return NULL; +} + +bool +playlist_suffix_supported(const char *suffix) +{ + assert(suffix != NULL); + + playlist_plugins_for_each_enabled(plugin) { + if (plugin->suffixes != NULL && + string_array_contains(plugin->suffixes, suffix)) + return true; + } + + return false; +} + +struct playlist_provider * +playlist_list_open_path(const char *path_fs, GMutex *mutex, GCond *cond, + struct input_stream **is_r) +{ + GError *error = NULL; + const char *suffix; + struct input_stream *is; + struct playlist_provider *playlist; + + assert(path_fs != NULL); + + suffix = uri_get_suffix(path_fs); + if (suffix == NULL || !playlist_suffix_supported(suffix)) + return NULL; + + is = input_stream_open(path_fs, mutex, cond, &error); + if (is == NULL) { + if (error != NULL) { + g_warning("%s", error->message); + g_error_free(error); + } + + return NULL; + } + + input_stream_lock_wait_ready(is); + + playlist = playlist_list_open_stream_suffix(is, suffix); + if (playlist != NULL) + *is_r = is; + else + input_stream_close(is); + + return playlist; +} diff --git a/src/PlaylistRegistry.hxx b/src/PlaylistRegistry.hxx new file mode 100644 index 000000000..5116b5082 --- /dev/null +++ b/src/PlaylistRegistry.hxx @@ -0,0 +1,85 @@ +/* + * 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_PLAYLIST_REGISTRY_HXX +#define MPD_PLAYLIST_REGISTRY_HXX + +#include + +#include + +struct playlist_provider; +struct input_stream; + +extern const struct playlist_plugin *const playlist_plugins[]; + +#define playlist_plugins_for_each(plugin) \ + for (const struct playlist_plugin *plugin, \ + *const*playlist_plugin_iterator = &playlist_plugins[0]; \ + (plugin = *playlist_plugin_iterator) != NULL; \ + ++playlist_plugin_iterator) + +/** + * Initializes all playlist plugins. + */ +void +playlist_list_global_init(void); + +/** + * Deinitializes all playlist plugins. + */ +void +playlist_list_global_finish(void); + +/** + * Opens a playlist by its URI. + */ +struct playlist_provider * +playlist_list_open_uri(const char *uri, GMutex *mutex, GCond *cond); + +/** + * Opens a playlist from an input stream. + * + * @param is an #input_stream object which is open and ready + * @param uri optional URI which was used to open the stream; may be + * used to select the appropriate playlist plugin + */ +struct playlist_provider * +playlist_list_open_stream(struct input_stream *is, const char *uri); + +/** + * Determines if there is a playlist plugin which can handle the + * specified file name suffix. + */ +bool +playlist_suffix_supported(const char *suffix); + +/** + * Opens a playlist from a local file. + * + * @param path_fs the path of the playlist file + * @param is_r on success, an input_stream object is returned here, + * which must be closed after the playlist_provider object is freed + * @return a playlist, or NULL on error + */ +struct playlist_provider * +playlist_list_open_path(const char *path_fs, GMutex *mutex, GCond *cond, + struct input_stream **is_r); + +#endif diff --git a/src/UpdateWalk.cxx b/src/UpdateWalk.cxx index fb6618111..dcac0f97e 100644 --- a/src/UpdateWalk.cxx +++ b/src/UpdateWalk.cxx @@ -28,6 +28,7 @@ #include "Directory.hxx" #include "song.h" #include "PlaylistVector.hxx" +#include "PlaylistRegistry.hxx" #include "Mapper.hxx" #include "ExcludeList.hxx" #include "conf.h" @@ -35,7 +36,6 @@ extern "C" { #include "uri.h" -#include "playlist_list.h" } #include diff --git a/src/playlist/DespotifyPlaylistPlugin.cxx b/src/playlist/DespotifyPlaylistPlugin.cxx index 049c9499c..3466d5685 100644 --- a/src/playlist/DespotifyPlaylistPlugin.cxx +++ b/src/playlist/DespotifyPlaylistPlugin.cxx @@ -21,7 +21,7 @@ #include "DespotifyPlaylistPlugin.hxx" #include "DespotifyUtils.hxx" #include "playlist_plugin.h" -#include "playlist_list.h" +#include "PlaylistRegistry.hxx" #include "conf.h" #include "uri.h" #include "tag.h" diff --git a/src/playlist/EmbeddedCuePlaylistPlugin.cxx b/src/playlist/EmbeddedCuePlaylistPlugin.cxx new file mode 100644 index 000000000..2337f806e --- /dev/null +++ b/src/playlist/EmbeddedCuePlaylistPlugin.cxx @@ -0,0 +1,190 @@ +/* + * Copyright (C) 2003-2013 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/** \file + * + * Playlist plugin that reads embedded cue sheets from the "CUESHEET" + * tag of a music file. + */ + +#include "config.h" +#include "EmbeddedCuePlaylistPlugin.hxx" +#include "playlist_plugin.h" +#include "tag.h" +#include "tag_handler.h" +#include "song.h" + +extern "C" { +#include "tag_file.h" +#include "tag_ape.h" +#include "tag_id3.h" +#include "cue/cue_parser.h" +} + +#include +#include +#include + +#undef G_LOG_DOMAIN +#define G_LOG_DOMAIN "cue" + +struct embcue_playlist { + struct playlist_provider base; + + /** + * This is an override for the CUE's "FILE". An embedded CUE + * sheet must always point to the song file it is contained + * in. + */ + char *filename; + + /** + * The value of the file's "CUESHEET" tag. + */ + char *cuesheet; + + /** + * The offset of the next line within "cuesheet". + */ + char *next; + + struct cue_parser *parser; +}; + +static void +embcue_tag_pair(const char *name, const char *value, void *ctx) +{ + struct embcue_playlist *playlist = (struct embcue_playlist *)ctx; + + if (playlist->cuesheet == NULL && + g_ascii_strcasecmp(name, "cuesheet") == 0) + playlist->cuesheet = g_strdup(value); +} + +static const struct tag_handler embcue_tag_handler = { + nullptr, + nullptr, + embcue_tag_pair, +}; + +static struct playlist_provider * +embcue_playlist_open_uri(const char *uri, + G_GNUC_UNUSED GMutex *mutex, + G_GNUC_UNUSED GCond *cond) +{ + if (!g_path_is_absolute(uri)) + /* only local files supported */ + return NULL; + + struct embcue_playlist *playlist = g_new(struct embcue_playlist, 1); + playlist_provider_init(&playlist->base, &embcue_playlist_plugin); + playlist->cuesheet = NULL; + + tag_file_scan(uri, &embcue_tag_handler, playlist); + if (playlist->cuesheet == NULL) { + tag_ape_scan2(uri, &embcue_tag_handler, playlist); + if (playlist->cuesheet == NULL) + tag_id3_scan(uri, &embcue_tag_handler, playlist); + } + + if (playlist->cuesheet == NULL) { + /* no "CUESHEET" tag found */ + g_free(playlist); + return NULL; + } + + playlist->filename = g_path_get_basename(uri); + + playlist->next = playlist->cuesheet; + playlist->parser = cue_parser_new(); + + return &playlist->base; +} + +static void +embcue_playlist_close(struct playlist_provider *_playlist) +{ + struct embcue_playlist *playlist = (struct embcue_playlist *)_playlist; + + cue_parser_free(playlist->parser); + g_free(playlist->cuesheet); + g_free(playlist->filename); + g_free(playlist); +} + +static struct song * +embcue_playlist_read(struct playlist_provider *_playlist) +{ + struct embcue_playlist *playlist = (struct embcue_playlist *)_playlist; + + struct song *song = cue_parser_get(playlist->parser); + if (song != NULL) + return song; + + while (*playlist->next != 0) { + const char *line = playlist->next; + char *eol = strpbrk(playlist->next, "\r\n"); + if (eol != NULL) { + /* null-terminate the line */ + *eol = 0; + playlist->next = eol + 1; + } else + /* last line; put the "next" pointer to the + end of the buffer */ + playlist->next += strlen(line); + + cue_parser_feed(playlist->parser, line); + song = cue_parser_get(playlist->parser); + if (song != NULL) + return song_replace_uri(song, playlist->filename); + } + + cue_parser_finish(playlist->parser); + song = cue_parser_get(playlist->parser); + if (song != NULL) + song = song_replace_uri(song, playlist->filename); + return song; +} + +static const char *const embcue_playlist_suffixes[] = { + /* a few codecs that are known to be supported; there are + probably many more */ + "flac", + "mp3", "mp2", + "mp4", "mp4a", "m4b", + "ape", + "wv", + "ogg", "oga", + NULL +}; + +const struct playlist_plugin embcue_playlist_plugin = { + "cue", + + nullptr, + nullptr, + embcue_playlist_open_uri, + nullptr, + embcue_playlist_close, + embcue_playlist_read, + + embcue_playlist_suffixes, + nullptr, + nullptr, +}; diff --git a/src/playlist/EmbeddedCuePlaylistPlugin.hxx b/src/playlist/EmbeddedCuePlaylistPlugin.hxx new file mode 100644 index 000000000..e306730f4 --- /dev/null +++ b/src/playlist/EmbeddedCuePlaylistPlugin.hxx @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2003-2013 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef MPD_EMBCUE_PLAYLIST_PLUGIN_HXX +#define MPD_EMBCUE_PLAYLIST_PLUGIN_HXX + +extern const struct playlist_plugin embcue_playlist_plugin; + +#endif diff --git a/src/playlist/LastFMPlaylistPlugin.cxx b/src/playlist/LastFMPlaylistPlugin.cxx new file mode 100644 index 000000000..67192574d --- /dev/null +++ b/src/playlist/LastFMPlaylistPlugin.cxx @@ -0,0 +1,299 @@ +/* + * 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 "LastFMPlaylistPlugin.hxx" +#include "playlist_plugin.h" +#include "PlaylistRegistry.hxx" +#include "conf.h" +#include "uri.h" +#include "song.h" +#include "input_stream.h" + +#include + +#include +#include + +struct lastfm_playlist { + struct playlist_provider base; + + struct input_stream *is; + + struct playlist_provider *xspf; +}; + +static struct { + char *user; + char *md5; +} lastfm_config; + +static bool +lastfm_init(const struct config_param *param) +{ + const char *user = config_get_block_string(param, "user", NULL); + const char *passwd = config_get_block_string(param, "password", NULL); + + if (user == NULL || passwd == NULL) { + g_debug("disabling the last.fm playlist plugin " + "because account is not configured"); + return false; + } + + lastfm_config.user = g_uri_escape_string(user, NULL, false); + + if (strlen(passwd) != 32) + lastfm_config.md5 = g_compute_checksum_for_string(G_CHECKSUM_MD5, + passwd, strlen(passwd)); + else + lastfm_config.md5 = g_strdup(passwd); + + return true; +} + +static void +lastfm_finish(void) +{ + g_free(lastfm_config.user); + g_free(lastfm_config.md5); +} + +/** + * Simple data fetcher. + * @param url path or url of data to fetch. + * @return data fetched, or NULL on error. Must be freed with g_free. + */ +static char * +lastfm_get(const char *url, GMutex *mutex, GCond *cond) +{ + struct input_stream *input_stream; + GError *error = NULL; + char buffer[4096]; + size_t length = 0, nbytes; + + input_stream = input_stream_open(url, mutex, cond, &error); + if (input_stream == NULL) { + if (error != NULL) { + g_warning("%s", error->message); + g_error_free(error); + } + + return NULL; + } + + g_mutex_lock(mutex); + + input_stream_wait_ready(input_stream); + + do { + nbytes = input_stream_read(input_stream, buffer + length, + sizeof(buffer) - length, &error); + if (nbytes == 0) { + if (error != NULL) { + g_warning("%s", error->message); + g_error_free(error); + } + + if (input_stream_eof(input_stream)) + break; + + /* I/O error */ + g_mutex_unlock(mutex); + input_stream_close(input_stream); + return NULL; + } + + length += nbytes; + } while (length < sizeof(buffer)); + + g_mutex_unlock(mutex); + + input_stream_close(input_stream); + return g_strndup(buffer, length); +} + +/** + * Ini-style value fetcher. + * @param response data through which to search. + * @param name name of value to search for. + * @return value for param name in param response or NULL on error. Free with g_free. + */ +static char * +lastfm_find(const char *response, const char *name) +{ + size_t name_length = strlen(name); + + while (true) { + const char *eol = strchr(response, '\n'); + if (eol == NULL) + return NULL; + + if (strncmp(response, name, name_length) == 0 && + response[name_length] == '=') { + response += name_length + 1; + return g_strndup(response, eol - response); + } + + response = eol + 1; + } +} + +static struct playlist_provider * +lastfm_open_uri(const char *uri, GMutex *mutex, GCond *cond) +{ + struct lastfm_playlist *playlist; + GError *error = NULL; + char *p, *q, *response, *session; + + /* handshake */ + + p = g_strconcat("http://ws.audioscrobbler.com/radio/handshake.php?" + "version=1.1.1&platform=linux&" + "username=", lastfm_config.user, "&" + "passwordmd5=", lastfm_config.md5, "&" + "debug=0&partner=", NULL); + response = lastfm_get(p, mutex, cond); + g_free(p); + if (response == NULL) + return NULL; + + /* extract session id from response */ + + session = lastfm_find(response, "session"); + g_free(response); + if (session == NULL) { + g_warning("last.fm handshake failed"); + return NULL; + } + + q = g_uri_escape_string(session, NULL, false); + g_free(session); + session = q; + + g_debug("session='%s'", session); + + /* "adjust" last.fm radio */ + + if (strlen(uri) > 9) { + char *escaped_uri; + + escaped_uri = g_uri_escape_string(uri, NULL, false); + + p = g_strconcat("http://ws.audioscrobbler.com/radio/adjust.php?" + "session=", session, "&url=", escaped_uri, "&debug=0", + NULL); + g_free(escaped_uri); + + response = lastfm_get(p, mutex, cond); + g_free(response); + g_free(p); + + if (response == NULL) { + g_free(session); + return NULL; + } + } + + /* create the playlist object */ + + playlist = g_new(struct lastfm_playlist, 1); + playlist_provider_init(&playlist->base, &lastfm_playlist_plugin); + + /* open the last.fm playlist */ + + p = g_strconcat("http://ws.audioscrobbler.com/radio/xspf.php?" + "sk=", session, "&discovery=0&desktop=1.5.1.31879", + NULL); + g_free(session); + + playlist->is = input_stream_open(p, mutex, cond, &error); + g_free(p); + + if (playlist->is == NULL) { + if (error != NULL) { + g_warning("Failed to load XSPF playlist: %s", + error->message); + g_error_free(error); + } else + g_warning("Failed to load XSPF playlist"); + g_free(playlist); + return NULL; + } + + g_mutex_lock(mutex); + + input_stream_wait_ready(playlist->is); + + /* last.fm does not send a MIME type, we have to fake it here + :-( */ + g_free(playlist->is->mime); + playlist->is->mime = g_strdup("application/xspf+xml"); + + g_mutex_unlock(mutex); + + /* parse the XSPF playlist */ + + playlist->xspf = playlist_list_open_stream(playlist->is, NULL); + if (playlist->xspf == NULL) { + input_stream_close(playlist->is); + g_free(playlist); + g_warning("Failed to parse XSPF playlist"); + return NULL; + } + + return &playlist->base; +} + +static void +lastfm_close(struct playlist_provider *_playlist) +{ + struct lastfm_playlist *playlist = (struct lastfm_playlist *)_playlist; + + playlist_plugin_close(playlist->xspf); + input_stream_close(playlist->is); + g_free(playlist); +} + +static struct song * +lastfm_read(struct playlist_provider *_playlist) +{ + struct lastfm_playlist *playlist = (struct lastfm_playlist *)_playlist; + + return playlist_plugin_read(playlist->xspf); +} + +static const char *const lastfm_schemes[] = { + "lastfm", + NULL +}; + +const struct playlist_plugin lastfm_playlist_plugin = { + "lastfm", + + lastfm_init, + lastfm_finish, + lastfm_open_uri, + nullptr, + lastfm_close, + lastfm_read, + + lastfm_schemes, + nullptr, + nullptr, +}; diff --git a/src/playlist/LastFMPlaylistPlugin.hxx b/src/playlist/LastFMPlaylistPlugin.hxx new file mode 100644 index 000000000..fe0e206d8 --- /dev/null +++ b/src/playlist/LastFMPlaylistPlugin.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_LASTFM_PLAYLIST_PLUGIN_HXX +#define MPD_LASTFM_PLAYLIST_PLUGIN_HXX + +extern const struct playlist_plugin lastfm_playlist_plugin; + +#endif diff --git a/src/playlist/SoundCloudPlaylistPlugin.cxx b/src/playlist/SoundCloudPlaylistPlugin.cxx new file mode 100644 index 000000000..71a2af6cd --- /dev/null +++ b/src/playlist/SoundCloudPlaylistPlugin.cxx @@ -0,0 +1,451 @@ +/* + * Copyright (C) 2003-2013 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "config.h" +#include "SoundCloudPlaylistPlugin.hxx" +#include "conf.h" +#include "input_stream.h" +#include "playlist_plugin.h" +#include "song.h" +#include "tag.h" + +#include +#include + +#include + +struct soundcloud_playlist { + struct playlist_provider base; + + GSList *songs; +}; + +static struct { + char *apikey; +} soundcloud_config; + +static bool +soundcloud_init(const struct config_param *param) +{ + soundcloud_config.apikey = + config_dup_block_string(param, "apikey", NULL); + if (soundcloud_config.apikey == NULL) { + g_debug("disabling the soundcloud playlist plugin " + "because API key is not set"); + return false; + } + + return true; +} + +static void +soundcloud_finish(void) +{ + g_free(soundcloud_config.apikey); +} + +/** + * Construct a full soundcloud resolver URL from the given fragment. + * @param uri uri of a soundcloud page (or just the path) + * @return Constructed URL. Must be freed with g_free. + */ +static char * +soundcloud_resolve(const char* uri) { + char *u, *ru; + + if (g_str_has_prefix(uri, "http://")) { + u = g_strdup(uri); + } else if (g_str_has_prefix(uri, "soundcloud.com")) { + u = g_strconcat("http://", uri, NULL); + } else { + /* assume it's just a path on soundcloud.com */ + u = g_strconcat("http://soundcloud.com/", uri, NULL); + } + + ru = g_strconcat("http://api.soundcloud.com/resolve.json?url=", + u, "&client_id=", soundcloud_config.apikey, NULL); + g_free(u); + + return ru; +} + +/* YAJL parser for track data from both /tracks/ and /playlists/ JSON */ + +enum key { + Duration, + Title, + Stream_URL, + Other, +}; + +const char* key_str[] = { + "duration", + "title", + "stream_url", + NULL, +}; + +struct parse_data { + int key; + char* stream_url; + long duration; + char* title; + int got_url; /* nesting level of last stream_url */ + GSList* songs; +}; + +static int handle_integer(void *ctx, + long +#ifndef HAVE_YAJL1 + long +#endif + intval) +{ + struct parse_data *data = (struct parse_data *) ctx; + + switch (data->key) { + case Duration: + data->duration = intval; + break; + default: + break; + } + + return 1; +} + +static int handle_string(void *ctx, const unsigned char* stringval, +#ifdef HAVE_YAJL1 + unsigned int +#else + size_t +#endif + stringlen) +{ + struct parse_data *data = (struct parse_data *) ctx; + const char *s = (const char *) stringval; + + switch (data->key) { + case Title: + if (data->title != NULL) + g_free(data->title); + data->title = g_strndup(s, stringlen); + break; + case Stream_URL: + if (data->stream_url != NULL) + g_free(data->stream_url); + data->stream_url = g_strndup(s, stringlen); + data->got_url = 1; + break; + default: + break; + } + + return 1; +} + +static int handle_mapkey(void *ctx, const unsigned char* stringval, +#ifdef HAVE_YAJL1 + unsigned int +#else + size_t +#endif + stringlen) +{ + struct parse_data *data = (struct parse_data *) ctx; + + int i; + data->key = Other; + + for (i = 0; i < Other; ++i) { + if (strncmp((const char *)stringval, key_str[i], stringlen) == 0) { + data->key = i; + break; + } + } + + return 1; +} + +static int handle_start_map(void *ctx) +{ + struct parse_data *data = (struct parse_data *) ctx; + + if (data->got_url > 0) + data->got_url++; + + return 1; +} + +static int handle_end_map(void *ctx) +{ + struct parse_data *data = (struct parse_data *) ctx; + + if (data->got_url > 1) { + data->got_url--; + return 1; + } + + if (data->got_url == 0) + return 1; + + /* got_url == 1, track finished, make it into a song */ + data->got_url = 0; + + struct song *s; + struct tag *t; + char *u; + + u = g_strconcat(data->stream_url, "?client_id=", soundcloud_config.apikey, NULL); + s = song_remote_new(u); + g_free(u); + t = tag_new(); + t->time = data->duration / 1000; + if (data->title != NULL) + tag_add_item(t, TAG_NAME, data->title); + s->tag = t; + + data->songs = g_slist_prepend(data->songs, s); + + return 1; +} + +static yajl_callbacks parse_callbacks = { + NULL, + NULL, + handle_integer, + NULL, + NULL, + handle_string, + handle_start_map, + handle_mapkey, + handle_end_map, + NULL, + NULL, +}; + +/** + * Read JSON data and parse it using the given YAJL parser. + * @param url URL of the JSON data. + * @param hand YAJL parser handle. + * @return -1 on error, 0 on success. + */ +static int +soundcloud_parse_json(const char *url, yajl_handle hand, GMutex* mutex, GCond* cond) +{ + struct input_stream *input_stream; + GError *error = NULL; + char buffer[4096]; + unsigned char *ubuffer = (unsigned char *)buffer; + size_t nbytes; + + input_stream = input_stream_open(url, mutex, cond, &error); + if (input_stream == NULL) { + if (error != NULL) { + g_warning("%s", error->message); + g_error_free(error); + } + return -1; + } + + g_mutex_lock(mutex); + input_stream_wait_ready(input_stream); + + yajl_status stat; + int done = 0; + + while (!done) { + nbytes = input_stream_read(input_stream, buffer, sizeof(buffer), &error); + if (nbytes == 0) { + if (error != NULL) { + g_warning("%s", error->message); + g_error_free(error); + } + if (input_stream_eof(input_stream)) { + done = true; + } else { + g_mutex_unlock(mutex); + input_stream_close(input_stream); + return -1; + } + } + + if (done) { +#ifdef HAVE_YAJL1 + stat = yajl_parse_complete(hand); +#else + stat = yajl_complete_parse(hand); +#endif + } else + stat = yajl_parse(hand, ubuffer, nbytes); + + if (stat != yajl_status_ok +#ifdef HAVE_YAJL1 + && stat != yajl_status_insufficient_data +#endif + ) + { + unsigned char *str = yajl_get_error(hand, 1, ubuffer, nbytes); + g_warning("%s", str); + yajl_free_error(hand, str); + break; + } + } + + g_mutex_unlock(mutex); + input_stream_close(input_stream); + + return 0; +} + +/** + * Parse a soundcloud:// URL and create a playlist. + * @param uri A soundcloud URL. Accepted forms: + * soundcloud://track/ + * soundcloud://playlist/ + * soundcloud://url/ + */ + +static struct playlist_provider * +soundcloud_open_uri(const char *uri, GMutex *mutex, GCond *cond) +{ + struct soundcloud_playlist *playlist = NULL; + + char *s, *p; + char *scheme, *arg, *rest; + s = g_strdup(uri); + scheme = s; + for (p = s; *p; p++) { + if (*p == ':' && *(p+1) == '/' && *(p+2) == '/') { + *p = 0; + p += 3; + break; + } + } + arg = p; + for (; *p; p++) { + if (*p == '/') { + *p = 0; + p++; + break; + } + } + rest = p; + + if (strcmp(scheme, "soundcloud") != 0) { + g_warning("incompatible scheme for soundcloud plugin: %s", scheme); + g_free(s); + return NULL; + } + + char *u = NULL; + if (strcmp(arg, "track") == 0) { + u = g_strconcat("http://api.soundcloud.com/tracks/", + rest, ".json?client_id=", soundcloud_config.apikey, NULL); + } else if (strcmp(arg, "playlist") == 0) { + u = g_strconcat("http://api.soundcloud.com/playlists/", + rest, ".json?client_id=", soundcloud_config.apikey, NULL); + } else if (strcmp(arg, "url") == 0) { + /* Translate to soundcloud resolver call. libcurl will automatically + follow the redirect to the right resource. */ + u = soundcloud_resolve(rest); + } + g_free(s); + + if (u == NULL) { + g_warning("unknown soundcloud URI"); + return NULL; + } + + yajl_handle hand; + struct parse_data data; + + data.got_url = 0; + data.songs = NULL; + data.title = NULL; + data.stream_url = NULL; +#ifdef HAVE_YAJL1 + hand = yajl_alloc(&parse_callbacks, NULL, NULL, (void *) &data); +#else + hand = yajl_alloc(&parse_callbacks, NULL, (void *) &data); +#endif + + int ret = soundcloud_parse_json(u, hand, mutex, cond); + + g_free(u); + yajl_free(hand); + if (data.title != NULL) + g_free(data.title); + if (data.stream_url != NULL) + g_free(data.stream_url); + + if (ret == -1) + return NULL; + + playlist = g_new(struct soundcloud_playlist, 1); + playlist_provider_init(&playlist->base, &soundcloud_playlist_plugin); + playlist->songs = g_slist_reverse(data.songs); + + return &playlist->base; +} + +static void +soundcloud_close(struct playlist_provider *_playlist) +{ + struct soundcloud_playlist *playlist = (struct soundcloud_playlist *)_playlist; + + g_free(playlist); +} + + +static struct song * +soundcloud_read(struct playlist_provider *_playlist) +{ + struct soundcloud_playlist *playlist = (struct soundcloud_playlist *)_playlist; + + if (playlist->songs == NULL) + return NULL; + + struct song* s; + s = (struct song *)playlist->songs->data; + playlist->songs = g_slist_remove(playlist->songs, s); + return s; +} + +static const char *const soundcloud_schemes[] = { + "soundcloud", + NULL +}; + +const struct playlist_plugin soundcloud_playlist_plugin = { + "soundcloud", + + soundcloud_init, + soundcloud_finish, + soundcloud_open_uri, + nullptr, + soundcloud_close, + soundcloud_read, + + soundcloud_schemes, + nullptr, + nullptr, +}; + + diff --git a/src/playlist/SoundCloudPlaylistPlugin.hxx b/src/playlist/SoundCloudPlaylistPlugin.hxx new file mode 100644 index 000000000..7c121328c --- /dev/null +++ b/src/playlist/SoundCloudPlaylistPlugin.hxx @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2003-2013 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef MPD_SOUNDCLOUD_PLAYLIST_PLUGIN_HXX +#define MPD_SOUNDCLOUD_PLAYLIST_PLUGIN_HXX + +extern const struct playlist_plugin soundcloud_playlist_plugin; + +#endif diff --git a/src/playlist/embcue_playlist_plugin.c b/src/playlist/embcue_playlist_plugin.c deleted file mode 100644 index 6d9a957f9..000000000 --- a/src/playlist/embcue_playlist_plugin.c +++ /dev/null @@ -1,181 +0,0 @@ -/* - * Copyright (C) 2003-2012 The Music Player Daemon Project - * http://www.musicpd.org - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -/** \file - * - * Playlist plugin that reads embedded cue sheets from the "CUESHEET" - * tag of a music file. - */ - -#include "config.h" -#include "playlist/embcue_playlist_plugin.h" -#include "playlist_plugin.h" -#include "tag.h" -#include "tag_handler.h" -#include "tag_file.h" -#include "tag_ape.h" -#include "tag_id3.h" -#include "song.h" -#include "cue/cue_parser.h" - -#include -#include -#include - -#undef G_LOG_DOMAIN -#define G_LOG_DOMAIN "cue" - -struct embcue_playlist { - struct playlist_provider base; - - /** - * This is an override for the CUE's "FILE". An embedded CUE - * sheet must always point to the song file it is contained - * in. - */ - char *filename; - - /** - * The value of the file's "CUESHEET" tag. - */ - char *cuesheet; - - /** - * The offset of the next line within "cuesheet". - */ - char *next; - - struct cue_parser *parser; -}; - -static void -embcue_tag_pair(const char *name, const char *value, void *ctx) -{ - struct embcue_playlist *playlist = ctx; - - if (playlist->cuesheet == NULL && - g_ascii_strcasecmp(name, "cuesheet") == 0) - playlist->cuesheet = g_strdup(value); -} - -static const struct tag_handler embcue_tag_handler = { - .pair = embcue_tag_pair, -}; - -static struct playlist_provider * -embcue_playlist_open_uri(const char *uri, - G_GNUC_UNUSED GMutex *mutex, - G_GNUC_UNUSED GCond *cond) -{ - if (!g_path_is_absolute(uri)) - /* only local files supported */ - return NULL; - - struct embcue_playlist *playlist = g_new(struct embcue_playlist, 1); - playlist_provider_init(&playlist->base, &embcue_playlist_plugin); - playlist->cuesheet = NULL; - - tag_file_scan(uri, &embcue_tag_handler, playlist); - if (playlist->cuesheet == NULL) { - tag_ape_scan2(uri, &embcue_tag_handler, playlist); - if (playlist->cuesheet == NULL) - tag_id3_scan(uri, &embcue_tag_handler, playlist); - } - - if (playlist->cuesheet == NULL) { - /* no "CUESHEET" tag found */ - g_free(playlist); - return NULL; - } - - playlist->filename = g_path_get_basename(uri); - - playlist->next = playlist->cuesheet; - playlist->parser = cue_parser_new(); - - return &playlist->base; -} - -static void -embcue_playlist_close(struct playlist_provider *_playlist) -{ - struct embcue_playlist *playlist = (struct embcue_playlist *)_playlist; - - cue_parser_free(playlist->parser); - g_free(playlist->cuesheet); - g_free(playlist->filename); - g_free(playlist); -} - -static struct song * -embcue_playlist_read(struct playlist_provider *_playlist) -{ - struct embcue_playlist *playlist = (struct embcue_playlist *)_playlist; - - struct song *song = cue_parser_get(playlist->parser); - if (song != NULL) - return song; - - while (*playlist->next != 0) { - const char *line = playlist->next; - char *eol = strpbrk(playlist->next, "\r\n"); - if (eol != NULL) { - /* null-terminate the line */ - *eol = 0; - playlist->next = eol + 1; - } else - /* last line; put the "next" pointer to the - end of the buffer */ - playlist->next += strlen(line); - - cue_parser_feed(playlist->parser, line); - song = cue_parser_get(playlist->parser); - if (song != NULL) - return song_replace_uri(song, playlist->filename); - } - - cue_parser_finish(playlist->parser); - song = cue_parser_get(playlist->parser); - if (song != NULL) - song = song_replace_uri(song, playlist->filename); - return song; -} - -static const char *const embcue_playlist_suffixes[] = { - /* a few codecs that are known to be supported; there are - probably many more */ - "flac", - "mp3", "mp2", - "mp4", "mp4a", "m4b", - "ape", - "wv", - "ogg", "oga", - NULL -}; - -const struct playlist_plugin embcue_playlist_plugin = { - .name = "cue", - - .open_uri = embcue_playlist_open_uri, - .close = embcue_playlist_close, - .read = embcue_playlist_read, - - .suffixes = embcue_playlist_suffixes, - .mime_types = NULL, -}; diff --git a/src/playlist/embcue_playlist_plugin.h b/src/playlist/embcue_playlist_plugin.h deleted file mode 100644 index c5f21b27e..000000000 --- a/src/playlist/embcue_playlist_plugin.h +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (C) 2003-2012 The Music Player Daemon Project - * http://www.musicpd.org - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#ifndef MPD_PLAYLIST_EMBCUE_PLAYLIST_PLUGIN_H -#define MPD_PLAYLIST_EMBCUE_PLAYLIST_PLUGIN_H - -extern const struct playlist_plugin embcue_playlist_plugin; - -#endif diff --git a/src/playlist/lastfm_playlist_plugin.c b/src/playlist/lastfm_playlist_plugin.c deleted file mode 100644 index ead14deaa..000000000 --- a/src/playlist/lastfm_playlist_plugin.c +++ /dev/null @@ -1,296 +0,0 @@ -/* - * Copyright (C) 2003-2011 The Music Player Daemon Project - * http://www.musicpd.org - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include "config.h" -#include "playlist/lastfm_playlist_plugin.h" -#include "playlist_plugin.h" -#include "playlist_list.h" -#include "conf.h" -#include "uri.h" -#include "song.h" -#include "input_stream.h" - -#include - -#include -#include - -struct lastfm_playlist { - struct playlist_provider base; - - struct input_stream *is; - - struct playlist_provider *xspf; -}; - -static struct { - char *user; - char *md5; -} lastfm_config; - -static bool -lastfm_init(const struct config_param *param) -{ - const char *user = config_get_block_string(param, "user", NULL); - const char *passwd = config_get_block_string(param, "password", NULL); - - if (user == NULL || passwd == NULL) { - g_debug("disabling the last.fm playlist plugin " - "because account is not configured"); - return false; - } - - lastfm_config.user = g_uri_escape_string(user, NULL, false); - - if (strlen(passwd) != 32) - lastfm_config.md5 = g_compute_checksum_for_string(G_CHECKSUM_MD5, - passwd, strlen(passwd)); - else - lastfm_config.md5 = g_strdup(passwd); - - return true; -} - -static void -lastfm_finish(void) -{ - g_free(lastfm_config.user); - g_free(lastfm_config.md5); -} - -/** - * Simple data fetcher. - * @param url path or url of data to fetch. - * @return data fetched, or NULL on error. Must be freed with g_free. - */ -static char * -lastfm_get(const char *url, GMutex *mutex, GCond *cond) -{ - struct input_stream *input_stream; - GError *error = NULL; - char buffer[4096]; - size_t length = 0, nbytes; - - input_stream = input_stream_open(url, mutex, cond, &error); - if (input_stream == NULL) { - if (error != NULL) { - g_warning("%s", error->message); - g_error_free(error); - } - - return NULL; - } - - g_mutex_lock(mutex); - - input_stream_wait_ready(input_stream); - - do { - nbytes = input_stream_read(input_stream, buffer + length, - sizeof(buffer) - length, &error); - if (nbytes == 0) { - if (error != NULL) { - g_warning("%s", error->message); - g_error_free(error); - } - - if (input_stream_eof(input_stream)) - break; - - /* I/O error */ - g_mutex_unlock(mutex); - input_stream_close(input_stream); - return NULL; - } - - length += nbytes; - } while (length < sizeof(buffer)); - - g_mutex_unlock(mutex); - - input_stream_close(input_stream); - return g_strndup(buffer, length); -} - -/** - * Ini-style value fetcher. - * @param response data through which to search. - * @param name name of value to search for. - * @return value for param name in param response or NULL on error. Free with g_free. - */ -static char * -lastfm_find(const char *response, const char *name) -{ - size_t name_length = strlen(name); - - while (true) { - const char *eol = strchr(response, '\n'); - if (eol == NULL) - return NULL; - - if (strncmp(response, name, name_length) == 0 && - response[name_length] == '=') { - response += name_length + 1; - return g_strndup(response, eol - response); - } - - response = eol + 1; - } -} - -static struct playlist_provider * -lastfm_open_uri(const char *uri, GMutex *mutex, GCond *cond) -{ - struct lastfm_playlist *playlist; - GError *error = NULL; - char *p, *q, *response, *session; - - /* handshake */ - - p = g_strconcat("http://ws.audioscrobbler.com/radio/handshake.php?" - "version=1.1.1&platform=linux&" - "username=", lastfm_config.user, "&" - "passwordmd5=", lastfm_config.md5, "&" - "debug=0&partner=", NULL); - response = lastfm_get(p, mutex, cond); - g_free(p); - if (response == NULL) - return NULL; - - /* extract session id from response */ - - session = lastfm_find(response, "session"); - g_free(response); - if (session == NULL) { - g_warning("last.fm handshake failed"); - return NULL; - } - - q = g_uri_escape_string(session, NULL, false); - g_free(session); - session = q; - - g_debug("session='%s'", session); - - /* "adjust" last.fm radio */ - - if (strlen(uri) > 9) { - char *escaped_uri; - - escaped_uri = g_uri_escape_string(uri, NULL, false); - - p = g_strconcat("http://ws.audioscrobbler.com/radio/adjust.php?" - "session=", session, "&url=", escaped_uri, "&debug=0", - NULL); - g_free(escaped_uri); - - response = lastfm_get(p, mutex, cond); - g_free(response); - g_free(p); - - if (response == NULL) { - g_free(session); - return NULL; - } - } - - /* create the playlist object */ - - playlist = g_new(struct lastfm_playlist, 1); - playlist_provider_init(&playlist->base, &lastfm_playlist_plugin); - - /* open the last.fm playlist */ - - p = g_strconcat("http://ws.audioscrobbler.com/radio/xspf.php?" - "sk=", session, "&discovery=0&desktop=1.5.1.31879", - NULL); - g_free(session); - - playlist->is = input_stream_open(p, mutex, cond, &error); - g_free(p); - - if (playlist->is == NULL) { - if (error != NULL) { - g_warning("Failed to load XSPF playlist: %s", - error->message); - g_error_free(error); - } else - g_warning("Failed to load XSPF playlist"); - g_free(playlist); - return NULL; - } - - g_mutex_lock(mutex); - - input_stream_wait_ready(playlist->is); - - /* last.fm does not send a MIME type, we have to fake it here - :-( */ - g_free(playlist->is->mime); - playlist->is->mime = g_strdup("application/xspf+xml"); - - g_mutex_unlock(mutex); - - /* parse the XSPF playlist */ - - playlist->xspf = playlist_list_open_stream(playlist->is, NULL); - if (playlist->xspf == NULL) { - input_stream_close(playlist->is); - g_free(playlist); - g_warning("Failed to parse XSPF playlist"); - return NULL; - } - - return &playlist->base; -} - -static void -lastfm_close(struct playlist_provider *_playlist) -{ - struct lastfm_playlist *playlist = (struct lastfm_playlist *)_playlist; - - playlist_plugin_close(playlist->xspf); - input_stream_close(playlist->is); - g_free(playlist); -} - -static struct song * -lastfm_read(struct playlist_provider *_playlist) -{ - struct lastfm_playlist *playlist = (struct lastfm_playlist *)_playlist; - - return playlist_plugin_read(playlist->xspf); -} - -static const char *const lastfm_schemes[] = { - "lastfm", - NULL -}; - -const struct playlist_plugin lastfm_playlist_plugin = { - .name = "lastfm", - - .init = lastfm_init, - .finish = lastfm_finish, - .open_uri = lastfm_open_uri, - .close = lastfm_close, - .read = lastfm_read, - - .schemes = lastfm_schemes, -}; diff --git a/src/playlist/lastfm_playlist_plugin.h b/src/playlist/lastfm_playlist_plugin.h deleted file mode 100644 index 46a8b0caf..000000000 --- a/src/playlist/lastfm_playlist_plugin.h +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (C) 2003-2011 The Music Player Daemon Project - * http://www.musicpd.org - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#ifndef MPD_PLAYLIST_LASTFM_PLAYLIST_PLUGIN_H -#define MPD_PLAYLIST_LASTFM_PLAYLIST_PLUGIN_H - -extern const struct playlist_plugin lastfm_playlist_plugin; - -#endif diff --git a/src/playlist/soundcloud_playlist_plugin.c b/src/playlist/soundcloud_playlist_plugin.c deleted file mode 100644 index 7c79f880a..000000000 --- a/src/playlist/soundcloud_playlist_plugin.c +++ /dev/null @@ -1,448 +0,0 @@ -/* - * Copyright (C) 2003-2011 The Music Player Daemon Project - * http://www.musicpd.org - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include "config.h" -#include "playlist/soundcloud_playlist_plugin.h" -#include "conf.h" -#include "input_stream.h" -#include "playlist_plugin.h" -#include "song.h" -#include "tag.h" - -#include -#include - -#include - -struct soundcloud_playlist { - struct playlist_provider base; - - GSList *songs; -}; - -static struct { - char *apikey; -} soundcloud_config; - -static bool -soundcloud_init(const struct config_param *param) -{ - soundcloud_config.apikey = - config_dup_block_string(param, "apikey", NULL); - if (soundcloud_config.apikey == NULL) { - g_debug("disabling the soundcloud playlist plugin " - "because API key is not set"); - return false; - } - - return true; -} - -static void -soundcloud_finish(void) -{ - g_free(soundcloud_config.apikey); -} - -/** - * Construct a full soundcloud resolver URL from the given fragment. - * @param uri uri of a soundcloud page (or just the path) - * @return Constructed URL. Must be freed with g_free. - */ -static char * -soundcloud_resolve(const char* uri) { - char *u, *ru; - - if (g_str_has_prefix(uri, "http://")) { - u = g_strdup(uri); - } else if (g_str_has_prefix(uri, "soundcloud.com")) { - u = g_strconcat("http://", uri, NULL); - } else { - /* assume it's just a path on soundcloud.com */ - u = g_strconcat("http://soundcloud.com/", uri, NULL); - } - - ru = g_strconcat("http://api.soundcloud.com/resolve.json?url=", - u, "&client_id=", soundcloud_config.apikey, NULL); - g_free(u); - - return ru; -} - -/* YAJL parser for track data from both /tracks/ and /playlists/ JSON */ - -enum key { - Duration, - Title, - Stream_URL, - Other, -}; - -const char* key_str[] = { - "duration", - "title", - "stream_url", - NULL, -}; - -struct parse_data { - int key; - char* stream_url; - long duration; - char* title; - int got_url; /* nesting level of last stream_url */ - GSList* songs; -}; - -static int handle_integer(void *ctx, - long -#ifndef HAVE_YAJL1 - long -#endif - intval) -{ - struct parse_data *data = (struct parse_data *) ctx; - - switch (data->key) { - case Duration: - data->duration = intval; - break; - default: - break; - } - - return 1; -} - -static int handle_string(void *ctx, const unsigned char* stringval, -#ifdef HAVE_YAJL1 - unsigned int -#else - size_t -#endif - stringlen) -{ - struct parse_data *data = (struct parse_data *) ctx; - const char *s = (const char *) stringval; - - switch (data->key) { - case Title: - if (data->title != NULL) - g_free(data->title); - data->title = g_strndup(s, stringlen); - break; - case Stream_URL: - if (data->stream_url != NULL) - g_free(data->stream_url); - data->stream_url = g_strndup(s, stringlen); - data->got_url = 1; - break; - default: - break; - } - - return 1; -} - -static int handle_mapkey(void *ctx, const unsigned char* stringval, -#ifdef HAVE_YAJL1 - unsigned int -#else - size_t -#endif - stringlen) -{ - struct parse_data *data = (struct parse_data *) ctx; - - int i; - data->key = Other; - - for (i = 0; i < Other; ++i) { - if (strncmp((const char *)stringval, key_str[i], stringlen) == 0) { - data->key = i; - break; - } - } - - return 1; -} - -static int handle_start_map(void *ctx) -{ - struct parse_data *data = (struct parse_data *) ctx; - - if (data->got_url > 0) - data->got_url++; - - return 1; -} - -static int handle_end_map(void *ctx) -{ - struct parse_data *data = (struct parse_data *) ctx; - - if (data->got_url > 1) { - data->got_url--; - return 1; - } - - if (data->got_url == 0) - return 1; - - /* got_url == 1, track finished, make it into a song */ - data->got_url = 0; - - struct song *s; - struct tag *t; - char *u; - - u = g_strconcat(data->stream_url, "?client_id=", soundcloud_config.apikey, NULL); - s = song_remote_new(u); - g_free(u); - t = tag_new(); - t->time = data->duration / 1000; - if (data->title != NULL) - tag_add_item(t, TAG_NAME, data->title); - s->tag = t; - - data->songs = g_slist_prepend(data->songs, s); - - return 1; -} - -static yajl_callbacks parse_callbacks = { - NULL, - NULL, - handle_integer, - NULL, - NULL, - handle_string, - handle_start_map, - handle_mapkey, - handle_end_map, - NULL, - NULL, -}; - -/** - * Read JSON data and parse it using the given YAJL parser. - * @param url URL of the JSON data. - * @param hand YAJL parser handle. - * @return -1 on error, 0 on success. - */ -static int -soundcloud_parse_json(const char *url, yajl_handle hand, GMutex* mutex, GCond* cond) -{ - struct input_stream *input_stream; - GError *error = NULL; - char buffer[4096]; - unsigned char *ubuffer = (unsigned char *)buffer; - size_t nbytes; - - input_stream = input_stream_open(url, mutex, cond, &error); - if (input_stream == NULL) { - if (error != NULL) { - g_warning("%s", error->message); - g_error_free(error); - } - return -1; - } - - g_mutex_lock(mutex); - input_stream_wait_ready(input_stream); - - yajl_status stat; - int done = 0; - - while (!done) { - nbytes = input_stream_read(input_stream, buffer, sizeof(buffer), &error); - if (nbytes == 0) { - if (error != NULL) { - g_warning("%s", error->message); - g_error_free(error); - } - if (input_stream_eof(input_stream)) { - done = true; - } else { - g_mutex_unlock(mutex); - input_stream_close(input_stream); - return -1; - } - } - - if (done) { -#ifdef HAVE_YAJL1 - stat = yajl_parse_complete(hand); -#else - stat = yajl_complete_parse(hand); -#endif - } else - stat = yajl_parse(hand, ubuffer, nbytes); - - if (stat != yajl_status_ok -#ifdef HAVE_YAJL1 - && stat != yajl_status_insufficient_data -#endif - ) - { - unsigned char *str = yajl_get_error(hand, 1, ubuffer, nbytes); - g_warning("%s", str); - yajl_free_error(hand, str); - break; - } - } - - g_mutex_unlock(mutex); - input_stream_close(input_stream); - - return 0; -} - -/** - * Parse a soundcloud:// URL and create a playlist. - * @param uri A soundcloud URL. Accepted forms: - * soundcloud://track/ - * soundcloud://playlist/ - * soundcloud://url/ - */ - -static struct playlist_provider * -soundcloud_open_uri(const char *uri, GMutex *mutex, GCond *cond) -{ - struct soundcloud_playlist *playlist = NULL; - - char *s, *p; - char *scheme, *arg, *rest; - s = g_strdup(uri); - scheme = s; - for (p = s; *p; p++) { - if (*p == ':' && *(p+1) == '/' && *(p+2) == '/') { - *p = 0; - p += 3; - break; - } - } - arg = p; - for (; *p; p++) { - if (*p == '/') { - *p = 0; - p++; - break; - } - } - rest = p; - - if (strcmp(scheme, "soundcloud") != 0) { - g_warning("incompatible scheme for soundcloud plugin: %s", scheme); - g_free(s); - return NULL; - } - - char *u = NULL; - if (strcmp(arg, "track") == 0) { - u = g_strconcat("http://api.soundcloud.com/tracks/", - rest, ".json?client_id=", soundcloud_config.apikey, NULL); - } else if (strcmp(arg, "playlist") == 0) { - u = g_strconcat("http://api.soundcloud.com/playlists/", - rest, ".json?client_id=", soundcloud_config.apikey, NULL); - } else if (strcmp(arg, "url") == 0) { - /* Translate to soundcloud resolver call. libcurl will automatically - follow the redirect to the right resource. */ - u = soundcloud_resolve(rest); - } - g_free(s); - - if (u == NULL) { - g_warning("unknown soundcloud URI"); - return NULL; - } - - yajl_handle hand; - struct parse_data data; - - data.got_url = 0; - data.songs = NULL; - data.title = NULL; - data.stream_url = NULL; -#ifdef HAVE_YAJL1 - hand = yajl_alloc(&parse_callbacks, NULL, NULL, (void *) &data); -#else - hand = yajl_alloc(&parse_callbacks, NULL, (void *) &data); -#endif - - int ret = soundcloud_parse_json(u, hand, mutex, cond); - - g_free(u); - yajl_free(hand); - if (data.title != NULL) - g_free(data.title); - if (data.stream_url != NULL) - g_free(data.stream_url); - - if (ret == -1) - return NULL; - - playlist = g_new(struct soundcloud_playlist, 1); - playlist_provider_init(&playlist->base, &soundcloud_playlist_plugin); - playlist->songs = g_slist_reverse(data.songs); - - return &playlist->base; -} - -static void -soundcloud_close(struct playlist_provider *_playlist) -{ - struct soundcloud_playlist *playlist = (struct soundcloud_playlist *)_playlist; - - g_free(playlist); -} - - -static struct song * -soundcloud_read(struct playlist_provider *_playlist) -{ - struct soundcloud_playlist *playlist = (struct soundcloud_playlist *)_playlist; - - if (playlist->songs == NULL) - return NULL; - - struct song* s; - s = (struct song *)playlist->songs->data; - playlist->songs = g_slist_remove(playlist->songs, s); - return s; -} - -static const char *const soundcloud_schemes[] = { - "soundcloud", - NULL -}; - -const struct playlist_plugin soundcloud_playlist_plugin = { - .name = "soundcloud", - - .init = soundcloud_init, - .finish = soundcloud_finish, - .open_uri = soundcloud_open_uri, - .close = soundcloud_close, - .read = soundcloud_read, - - .schemes = soundcloud_schemes, -}; - - diff --git a/src/playlist/soundcloud_playlist_plugin.h b/src/playlist/soundcloud_playlist_plugin.h deleted file mode 100644 index e09e2dd46..000000000 --- a/src/playlist/soundcloud_playlist_plugin.h +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (C) 2003-2011 The Music Player Daemon Project - * http://www.musicpd.org - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#ifndef MPD_PLAYLIST_SOUNDCLOUD_PLAYLIST_PLUGIN_H -#define MPD_PLAYLIST_SOUNDCLOUD_PLAYLIST_PLUGIN_H - -extern const struct playlist_plugin soundcloud_playlist_plugin; - -#endif diff --git a/src/playlist_list.c b/src/playlist_list.c deleted file mode 100644 index 9ba78f38c..000000000 --- a/src/playlist_list.c +++ /dev/null @@ -1,348 +0,0 @@ -/* - * Copyright (C) 2003-2011 The Music Player Daemon Project - * http://www.musicpd.org - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include "config.h" -#include "playlist_list.h" -#include "playlist_plugin.h" -#include "playlist/extm3u_playlist_plugin.h" -#include "playlist/m3u_playlist_plugin.h" -#include "playlist/xspf_playlist_plugin.h" -#include "playlist/lastfm_playlist_plugin.h" -#include "playlist/DespotifyPlaylistPlugin.hxx" -#include "playlist/soundcloud_playlist_plugin.h" -#include "playlist/pls_playlist_plugin.h" -#include "playlist/asx_playlist_plugin.h" -#include "playlist/rss_playlist_plugin.h" -#include "playlist/cue_playlist_plugin.h" -#include "playlist/embcue_playlist_plugin.h" -#include "input_stream.h" -#include "uri.h" -#include "string_util.h" -#include "conf.h" -#include "mpd_error.h" - -#include -#include -#include - -const struct playlist_plugin *const playlist_plugins[] = { - &extm3u_playlist_plugin, - &m3u_playlist_plugin, - &xspf_playlist_plugin, - &pls_playlist_plugin, - &asx_playlist_plugin, - &rss_playlist_plugin, -#ifdef ENABLE_DESPOTIFY - &despotify_playlist_plugin, -#endif -#ifdef ENABLE_LASTFM - &lastfm_playlist_plugin, -#endif -#ifdef ENABLE_SOUNDCLOUD - &soundcloud_playlist_plugin, -#endif - &cue_playlist_plugin, - &embcue_playlist_plugin, - NULL -}; - -/** which plugins have been initialized successfully? */ -static bool playlist_plugins_enabled[G_N_ELEMENTS(playlist_plugins)]; - -#define playlist_plugins_for_each_enabled(plugin) \ - playlist_plugins_for_each(plugin) \ - if (playlist_plugins_enabled[playlist_plugin_iterator - playlist_plugins]) - -/** - * Find the "playlist" configuration block for the specified plugin. - * - * @param plugin_name the name of the playlist plugin - * @return the configuration block, or NULL if none was configured - */ -static const struct config_param * -playlist_plugin_config(const char *plugin_name) -{ - const struct config_param *param = NULL; - - assert(plugin_name != NULL); - - while ((param = config_get_next_param(CONF_PLAYLIST_PLUGIN, param)) != NULL) { - const char *name = - config_get_block_string(param, "name", NULL); - if (name == NULL) - MPD_ERROR("playlist configuration without 'plugin' name in line %d", - param->line); - - if (strcmp(name, plugin_name) == 0) - return param; - } - - return NULL; -} - -void -playlist_list_global_init(void) -{ - for (unsigned i = 0; playlist_plugins[i] != NULL; ++i) { - const struct playlist_plugin *plugin = playlist_plugins[i]; - const struct config_param *param = - playlist_plugin_config(plugin->name); - - if (!config_get_block_bool(param, "enabled", true)) - /* the plugin is disabled in mpd.conf */ - continue; - - playlist_plugins_enabled[i] = - playlist_plugin_init(playlist_plugins[i], param); - } -} - -void -playlist_list_global_finish(void) -{ - playlist_plugins_for_each_enabled(plugin) - playlist_plugin_finish(plugin); -} - -static struct playlist_provider * -playlist_list_open_uri_scheme(const char *uri, GMutex *mutex, GCond *cond, - bool *tried) -{ - char *scheme; - struct playlist_provider *playlist = NULL; - - assert(uri != NULL); - - scheme = g_uri_parse_scheme(uri); - if (scheme == NULL) - return NULL; - - for (unsigned i = 0; playlist_plugins[i] != NULL; ++i) { - const struct playlist_plugin *plugin = playlist_plugins[i]; - - assert(!tried[i]); - - if (playlist_plugins_enabled[i] && plugin->open_uri != NULL && - plugin->schemes != NULL && - string_array_contains(plugin->schemes, scheme)) { - playlist = playlist_plugin_open_uri(plugin, uri, - mutex, cond); - if (playlist != NULL) - break; - - tried[i] = true; - } - } - - g_free(scheme); - return playlist; -} - -static struct playlist_provider * -playlist_list_open_uri_suffix(const char *uri, GMutex *mutex, GCond *cond, - const bool *tried) -{ - const char *suffix; - struct playlist_provider *playlist = NULL; - - assert(uri != NULL); - - suffix = uri_get_suffix(uri); - if (suffix == NULL) - return NULL; - - for (unsigned i = 0; playlist_plugins[i] != NULL; ++i) { - const struct playlist_plugin *plugin = playlist_plugins[i]; - - if (playlist_plugins_enabled[i] && !tried[i] && - plugin->open_uri != NULL && plugin->suffixes != NULL && - string_array_contains(plugin->suffixes, suffix)) { - playlist = playlist_plugin_open_uri(plugin, uri, - mutex, cond); - if (playlist != NULL) - break; - } - } - - return playlist; -} - -struct playlist_provider * -playlist_list_open_uri(const char *uri, GMutex *mutex, GCond *cond) -{ - struct playlist_provider *playlist; - /** this array tracks which plugins have already been tried by - playlist_list_open_uri_scheme() */ - bool tried[G_N_ELEMENTS(playlist_plugins) - 1]; - - assert(uri != NULL); - - memset(tried, false, sizeof(tried)); - - playlist = playlist_list_open_uri_scheme(uri, mutex, cond, tried); - if (playlist == NULL) - playlist = playlist_list_open_uri_suffix(uri, mutex, cond, - tried); - - return playlist; -} - -static struct playlist_provider * -playlist_list_open_stream_mime2(struct input_stream *is, const char *mime) -{ - struct playlist_provider *playlist; - - assert(is != NULL); - assert(mime != NULL); - - playlist_plugins_for_each_enabled(plugin) { - if (plugin->open_stream != NULL && - plugin->mime_types != NULL && - string_array_contains(plugin->mime_types, mime)) { - /* rewind the stream, so each plugin gets a - fresh start */ - input_stream_seek(is, 0, SEEK_SET, NULL); - - playlist = playlist_plugin_open_stream(plugin, is); - if (playlist != NULL) - return playlist; - } - } - - return NULL; -} - -static struct playlist_provider * -playlist_list_open_stream_mime(struct input_stream *is) -{ - assert(is->mime != NULL); - - const char *semicolon = strchr(is->mime, ';'); - if (semicolon == NULL) - return playlist_list_open_stream_mime2(is, is->mime); - - if (semicolon == is->mime) - return NULL; - - /* probe only the portion before the semicolon*/ - char *mime = g_strndup(is->mime, semicolon - is->mime); - struct playlist_provider *playlist = - playlist_list_open_stream_mime2(is, mime); - g_free(mime); - return playlist; -} - -static struct playlist_provider * -playlist_list_open_stream_suffix(struct input_stream *is, const char *suffix) -{ - struct playlist_provider *playlist; - - assert(is != NULL); - assert(suffix != NULL); - - playlist_plugins_for_each_enabled(plugin) { - if (plugin->open_stream != NULL && - plugin->suffixes != NULL && - string_array_contains(plugin->suffixes, suffix)) { - /* rewind the stream, so each plugin gets a - fresh start */ - input_stream_seek(is, 0, SEEK_SET, NULL); - - playlist = playlist_plugin_open_stream(plugin, is); - if (playlist != NULL) - return playlist; - } - } - - return NULL; -} - -struct playlist_provider * -playlist_list_open_stream(struct input_stream *is, const char *uri) -{ - const char *suffix; - struct playlist_provider *playlist; - - input_stream_lock_wait_ready(is); - - if (is->mime != NULL) { - playlist = playlist_list_open_stream_mime(is); - if (playlist != NULL) - return playlist; - } - - suffix = uri != NULL ? uri_get_suffix(uri) : NULL; - if (suffix != NULL) { - playlist = playlist_list_open_stream_suffix(is, suffix); - if (playlist != NULL) - return playlist; - } - - return NULL; -} - -bool -playlist_suffix_supported(const char *suffix) -{ - assert(suffix != NULL); - - playlist_plugins_for_each_enabled(plugin) { - if (plugin->suffixes != NULL && - string_array_contains(plugin->suffixes, suffix)) - return true; - } - - return false; -} - -struct playlist_provider * -playlist_list_open_path(const char *path_fs, GMutex *mutex, GCond *cond, - struct input_stream **is_r) -{ - GError *error = NULL; - const char *suffix; - struct input_stream *is; - struct playlist_provider *playlist; - - assert(path_fs != NULL); - - suffix = uri_get_suffix(path_fs); - if (suffix == NULL || !playlist_suffix_supported(suffix)) - return NULL; - - is = input_stream_open(path_fs, mutex, cond, &error); - if (is == NULL) { - if (error != NULL) { - g_warning("%s", error->message); - g_error_free(error); - } - - return NULL; - } - - input_stream_lock_wait_ready(is); - - playlist = playlist_list_open_stream_suffix(is, suffix); - if (playlist != NULL) - *is_r = is; - else - input_stream_close(is); - - return playlist; -} diff --git a/src/playlist_list.h b/src/playlist_list.h deleted file mode 100644 index c3967d5ae..000000000 --- a/src/playlist_list.h +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright (C) 2003-2011 The Music Player Daemon Project - * http://www.musicpd.org - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#ifndef MPD_PLAYLIST_LIST_H -#define MPD_PLAYLIST_LIST_H - -#include - -#include - -struct playlist_provider; -struct input_stream; - -extern const struct playlist_plugin *const playlist_plugins[]; - -#define playlist_plugins_for_each(plugin) \ - for (const struct playlist_plugin *plugin, \ - *const*playlist_plugin_iterator = &playlist_plugins[0]; \ - (plugin = *playlist_plugin_iterator) != NULL; \ - ++playlist_plugin_iterator) - -/** - * Initializes all playlist plugins. - */ -void -playlist_list_global_init(void); - -/** - * Deinitializes all playlist plugins. - */ -void -playlist_list_global_finish(void); - -/** - * Opens a playlist by its URI. - */ -struct playlist_provider * -playlist_list_open_uri(const char *uri, GMutex *mutex, GCond *cond); - -/** - * Opens a playlist from an input stream. - * - * @param is an #input_stream object which is open and ready - * @param uri optional URI which was used to open the stream; may be - * used to select the appropriate playlist plugin - */ -struct playlist_provider * -playlist_list_open_stream(struct input_stream *is, const char *uri); - -/** - * Determines if there is a playlist plugin which can handle the - * specified file name suffix. - */ -bool -playlist_suffix_supported(const char *suffix); - -/** - * Opens a playlist from a local file. - * - * @param path_fs the path of the playlist file - * @param is_r on success, an input_stream object is returned here, - * which must be closed after the playlist_provider object is freed - * @return a playlist, or NULL on error - */ -struct playlist_provider * -playlist_list_open_path(const char *path_fs, GMutex *mutex, GCond *cond, - struct input_stream **is_r); - -#endif diff --git a/test/dump_playlist.cxx b/test/dump_playlist.cxx index c95d4376e..cc749d2ad 100644 --- a/test/dump_playlist.cxx +++ b/test/dump_playlist.cxx @@ -26,11 +26,11 @@ #include "decoder_api.h" #include "InputInit.hxx" #include "IOThread.hxx" +#include "PlaylistRegistry.hxx" +#include "playlist_plugin.h" extern "C" { #include "decoder_list.h" -#include "playlist_list.h" -#include "playlist_plugin.h" } #include -- cgit v1.2.3