aboutsummaryrefslogtreecommitdiffstats
path: root/src/playlist
diff options
context:
space:
mode:
Diffstat (limited to 'src/playlist')
-rw-r--r--src/playlist/MemorySongEnumerator.cxx32
-rw-r--r--src/playlist/MemorySongEnumerator.hxx38
-rw-r--r--src/playlist/PlaylistAny.cxx69
-rw-r--r--src/playlist/PlaylistAny.hxx41
-rw-r--r--src/playlist/PlaylistMapper.cxx102
-rw-r--r--src/playlist/PlaylistMapper.hxx40
-rw-r--r--src/playlist/PlaylistPlugin.hxx109
-rw-r--r--src/playlist/PlaylistQueue.cxx90
-rw-r--r--src/playlist/PlaylistQueue.hxx59
-rw-r--r--src/playlist/PlaylistRegistry.cxx335
-rw-r--r--src/playlist/PlaylistRegistry.hxx83
-rw-r--r--src/playlist/PlaylistSong.cxx131
-rw-r--r--src/playlist/PlaylistSong.hxx37
-rw-r--r--src/playlist/Print.cxx71
-rw-r--r--src/playlist/Print.hxx36
-rw-r--r--src/playlist/SongEnumerator.hxx41
-rw-r--r--src/playlist/plugins/AsxPlaylistPlugin.cxx (renamed from src/playlist/AsxPlaylistPlugin.cxx)4
-rw-r--r--src/playlist/plugins/AsxPlaylistPlugin.hxx (renamed from src/playlist/AsxPlaylistPlugin.hxx)0
-rw-r--r--src/playlist/plugins/CuePlaylistPlugin.cxx (renamed from src/playlist/CuePlaylistPlugin.cxx)4
-rw-r--r--src/playlist/plugins/CuePlaylistPlugin.hxx (renamed from src/playlist/CuePlaylistPlugin.hxx)0
-rw-r--r--src/playlist/plugins/DespotifyPlaylistPlugin.cxx (renamed from src/playlist/DespotifyPlaylistPlugin.cxx)4
-rw-r--r--src/playlist/plugins/DespotifyPlaylistPlugin.hxx (renamed from src/playlist/DespotifyPlaylistPlugin.hxx)0
-rw-r--r--src/playlist/plugins/EmbeddedCuePlaylistPlugin.cxx (renamed from src/playlist/EmbeddedCuePlaylistPlugin.cxx)4
-rw-r--r--src/playlist/plugins/EmbeddedCuePlaylistPlugin.hxx (renamed from src/playlist/EmbeddedCuePlaylistPlugin.hxx)0
-rw-r--r--src/playlist/plugins/ExtM3uPlaylistPlugin.cxx (renamed from src/playlist/ExtM3uPlaylistPlugin.cxx)4
-rw-r--r--src/playlist/plugins/ExtM3uPlaylistPlugin.hxx (renamed from src/playlist/ExtM3uPlaylistPlugin.hxx)0
-rw-r--r--src/playlist/plugins/M3uPlaylistPlugin.cxx (renamed from src/playlist/M3uPlaylistPlugin.cxx)4
-rw-r--r--src/playlist/plugins/M3uPlaylistPlugin.hxx (renamed from src/playlist/M3uPlaylistPlugin.hxx)0
-rw-r--r--src/playlist/plugins/PlsPlaylistPlugin.cxx (renamed from src/playlist/PlsPlaylistPlugin.cxx)4
-rw-r--r--src/playlist/plugins/PlsPlaylistPlugin.hxx (renamed from src/playlist/PlsPlaylistPlugin.hxx)0
-rw-r--r--src/playlist/plugins/RssPlaylistPlugin.cxx (renamed from src/playlist/RssPlaylistPlugin.cxx)4
-rw-r--r--src/playlist/plugins/RssPlaylistPlugin.hxx (renamed from src/playlist/RssPlaylistPlugin.hxx)0
-rw-r--r--src/playlist/plugins/SoundCloudPlaylistPlugin.cxx (renamed from src/playlist/SoundCloudPlaylistPlugin.cxx)4
-rw-r--r--src/playlist/plugins/SoundCloudPlaylistPlugin.hxx (renamed from src/playlist/SoundCloudPlaylistPlugin.hxx)0
-rw-r--r--src/playlist/plugins/XspfPlaylistPlugin.cxx (renamed from src/playlist/XspfPlaylistPlugin.cxx)4
-rw-r--r--src/playlist/plugins/XspfPlaylistPlugin.hxx (renamed from src/playlist/XspfPlaylistPlugin.hxx)0
36 files changed, 1334 insertions, 20 deletions
diff --git a/src/playlist/MemorySongEnumerator.cxx b/src/playlist/MemorySongEnumerator.cxx
new file mode 100644
index 000000000..c3127c2bf
--- /dev/null
+++ b/src/playlist/MemorySongEnumerator.cxx
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2003-2014 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 "MemorySongEnumerator.hxx"
+
+DetachedSong *
+MemorySongEnumerator::NextSong()
+{
+ if (songs.empty())
+ return nullptr;
+
+ auto result = new DetachedSong(std::move(songs.front()));
+ songs.pop_front();
+ return result;
+}
diff --git a/src/playlist/MemorySongEnumerator.hxx b/src/playlist/MemorySongEnumerator.hxx
new file mode 100644
index 000000000..e87a4f6dd
--- /dev/null
+++ b/src/playlist/MemorySongEnumerator.hxx
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2003-2014 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_MEMORY_PLAYLIST_PROVIDER_HXX
+#define MPD_MEMORY_PLAYLIST_PROVIDER_HXX
+
+#include "SongEnumerator.hxx"
+#include "DetachedSong.hxx"
+
+#include <forward_list>
+
+class MemorySongEnumerator final : public SongEnumerator {
+ std::forward_list<DetachedSong> songs;
+
+public:
+ MemorySongEnumerator(std::forward_list<DetachedSong> &&_songs)
+ :songs(std::move(_songs)) {}
+
+ virtual DetachedSong *NextSong() override;
+};
+
+#endif
diff --git a/src/playlist/PlaylistAny.cxx b/src/playlist/PlaylistAny.cxx
new file mode 100644
index 000000000..8d748cada
--- /dev/null
+++ b/src/playlist/PlaylistAny.cxx
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2003-2014 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 "PlaylistAny.hxx"
+#include "PlaylistMapper.hxx"
+#include "PlaylistRegistry.hxx"
+#include "util/UriUtil.hxx"
+#include "util/Error.hxx"
+#include "InputStream.hxx"
+#include "Log.hxx"
+
+#include <assert.h>
+
+static SongEnumerator *
+playlist_open_remote(const char *uri, Mutex &mutex, Cond &cond,
+ InputStream **is_r)
+{
+ assert(uri_has_scheme(uri));
+
+ SongEnumerator *playlist = playlist_list_open_uri(uri, mutex, cond);
+ if (playlist != nullptr) {
+ *is_r = nullptr;
+ return playlist;
+ }
+
+ Error error;
+ InputStream *is = InputStream::OpenReady(uri, mutex, cond, error);
+ if (is == nullptr) {
+ if (error.IsDefined())
+ FormatError(error, "Failed to open %s", uri);
+
+ return nullptr;
+ }
+
+ playlist = playlist_list_open_stream(*is, uri);
+ if (playlist == nullptr) {
+ is->Close();
+ return nullptr;
+ }
+
+ *is_r = is;
+ return playlist;
+}
+
+SongEnumerator *
+playlist_open_any(const char *uri, Mutex &mutex, Cond &cond,
+ InputStream **is_r)
+{
+ return uri_has_scheme(uri)
+ ? playlist_open_remote(uri, mutex, cond, is_r)
+ : playlist_mapper_open(uri, mutex, cond, is_r);
+}
diff --git a/src/playlist/PlaylistAny.hxx b/src/playlist/PlaylistAny.hxx
new file mode 100644
index 000000000..c472afb31
--- /dev/null
+++ b/src/playlist/PlaylistAny.hxx
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2003-2014 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_PLAYLIST_ANY_HXX
+#define MPD_PLAYLIST_ANY_HXX
+
+class Mutex;
+class Cond;
+class SongEnumerator;
+struct InputStream;
+
+/**
+ * Opens a playlist from the specified URI, which can be either an
+ * absolute remote URI (with a scheme) or a relative path to the
+ * music orplaylist directory.
+ *
+ * @param is_r on success, an input_stream object may be returned
+ * here, which must be closed after the playlist_provider object is
+ * freed
+ */
+SongEnumerator *
+playlist_open_any(const char *uri, Mutex &mutex, Cond &cond,
+ InputStream **is_r);
+
+#endif
diff --git a/src/playlist/PlaylistMapper.cxx b/src/playlist/PlaylistMapper.cxx
new file mode 100644
index 000000000..0df0bc61f
--- /dev/null
+++ b/src/playlist/PlaylistMapper.cxx
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2003-2014 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 "PlaylistMapper.hxx"
+#include "PlaylistFile.hxx"
+#include "PlaylistRegistry.hxx"
+#include "Mapper.hxx"
+#include "fs/AllocatedPath.hxx"
+#include "util/UriUtil.hxx"
+
+#include <assert.h>
+
+static SongEnumerator *
+playlist_open_path(const char *path_fs, Mutex &mutex, Cond &cond,
+ InputStream **is_r)
+{
+ auto playlist = playlist_list_open_uri(path_fs, mutex, cond);
+ if (playlist != nullptr)
+ *is_r = nullptr;
+ else
+ playlist = playlist_list_open_path(path_fs, mutex, cond, is_r);
+
+ return playlist;
+}
+
+/**
+ * Load a playlist from the configured playlist directory.
+ */
+static SongEnumerator *
+playlist_open_in_playlist_dir(const char *uri, Mutex &mutex, Cond &cond,
+ InputStream **is_r)
+{
+ assert(spl_valid_name(uri));
+
+ const auto &playlist_directory_fs = map_spl_path();
+ if (playlist_directory_fs.IsNull())
+ return nullptr;
+
+ const auto uri_fs = AllocatedPath::FromUTF8(uri);
+ if (uri_fs.IsNull())
+ return nullptr;
+
+ const auto path_fs =
+ AllocatedPath::Build(playlist_directory_fs, uri_fs);
+ assert(!path_fs.IsNull());
+
+ return playlist_open_path(path_fs.c_str(), mutex, cond, is_r);
+}
+
+/**
+ * Load a playlist from the configured music directory.
+ */
+static SongEnumerator *
+playlist_open_in_music_dir(const char *uri, Mutex &mutex, Cond &cond,
+ InputStream **is_r)
+{
+ assert(uri_safe_local(uri));
+
+ const auto path = map_uri_fs(uri);
+ if (path.IsNull())
+ return nullptr;
+
+ return playlist_open_path(path.c_str(), mutex, cond, is_r);
+}
+
+SongEnumerator *
+playlist_mapper_open(const char *uri, Mutex &mutex, Cond &cond,
+ InputStream **is_r)
+{
+ if (spl_valid_name(uri)) {
+ auto playlist = playlist_open_in_playlist_dir(uri, mutex, cond,
+ is_r);
+ if (playlist != nullptr)
+ return playlist;
+ }
+
+ if (uri_safe_local(uri)) {
+ auto playlist = playlist_open_in_music_dir(uri, mutex, cond,
+ is_r);
+ if (playlist != nullptr)
+ return playlist;
+ }
+
+ return nullptr;
+}
diff --git a/src/playlist/PlaylistMapper.hxx b/src/playlist/PlaylistMapper.hxx
new file mode 100644
index 000000000..a460cb124
--- /dev/null
+++ b/src/playlist/PlaylistMapper.hxx
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2003-2014 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_PLAYLIST_MAPPER_HXX
+#define MPD_PLAYLIST_MAPPER_HXX
+
+class Mutex;
+class Cond;
+class SongEnumerator;
+struct InputStream;
+
+/**
+ * Opens a playlist from an URI relative to the playlist or music
+ * directory.
+ *
+ * @param is_r on success, an input_stream object may be returned
+ * here, which must be closed after the playlist_provider object is
+ * freed
+ */
+SongEnumerator *
+playlist_mapper_open(const char *uri, Mutex &mutex, Cond &cond,
+ InputStream **is_r);
+
+#endif
diff --git a/src/playlist/PlaylistPlugin.hxx b/src/playlist/PlaylistPlugin.hxx
new file mode 100644
index 000000000..d3c44f1f4
--- /dev/null
+++ b/src/playlist/PlaylistPlugin.hxx
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2003-2014 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_PLAYLIST_PLUGIN_HXX
+#define MPD_PLAYLIST_PLUGIN_HXX
+
+struct config_param;
+struct InputStream;
+struct Tag;
+class Mutex;
+class Cond;
+class SongEnumerator;
+
+struct playlist_plugin {
+ const char *name;
+
+ /**
+ * Initialize the plugin. Optional method.
+ *
+ * @param param a configuration block for this plugin, or nullptr
+ * if none is configured
+ * @return true if the plugin was initialized successfully,
+ * false if the plugin is not available
+ */
+ bool (*init)(const config_param &param);
+
+ /**
+ * Deinitialize a plugin which was initialized successfully.
+ * Optional method.
+ */
+ void (*finish)(void);
+
+ /**
+ * Opens the playlist on the specified URI. This URI has
+ * either matched one of the schemes or one of the suffixes.
+ */
+ SongEnumerator *(*open_uri)(const char *uri,
+ Mutex &mutex, Cond &cond);
+
+ /**
+ * Opens the playlist in the specified input stream. It has
+ * either matched one of the suffixes or one of the MIME
+ * types.
+ */
+ SongEnumerator *(*open_stream)(InputStream &is);
+
+ const char *const*schemes;
+ const char *const*suffixes;
+ const char *const*mime_types;
+};
+
+/**
+ * Initialize a plugin.
+ *
+ * @param param a configuration block for this plugin, or nullptr if none
+ * is configured
+ * @return true if the plugin was initialized successfully, false if
+ * the plugin is not available
+ */
+static inline bool
+playlist_plugin_init(const struct playlist_plugin *plugin,
+ const config_param &param)
+{
+ return plugin->init != nullptr
+ ? plugin->init(param)
+ : true;
+}
+
+/**
+ * Deinitialize a plugin which was initialized successfully.
+ */
+static inline void
+playlist_plugin_finish(const struct playlist_plugin *plugin)
+{
+ if (plugin->finish != nullptr)
+ plugin->finish();
+}
+
+static inline SongEnumerator *
+playlist_plugin_open_uri(const struct playlist_plugin *plugin, const char *uri,
+ Mutex &mutex, Cond &cond)
+{
+ return plugin->open_uri(uri, mutex, cond);
+}
+
+static inline SongEnumerator *
+playlist_plugin_open_stream(const struct playlist_plugin *plugin,
+ InputStream &is)
+{
+ return plugin->open_stream(is);
+}
+
+#endif
diff --git a/src/playlist/PlaylistQueue.cxx b/src/playlist/PlaylistQueue.cxx
new file mode 100644
index 000000000..0a45920e3
--- /dev/null
+++ b/src/playlist/PlaylistQueue.cxx
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2003-2014 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 "PlaylistQueue.hxx"
+#include "PlaylistAny.hxx"
+#include "PlaylistSong.hxx"
+#include "Playlist.hxx"
+#include "InputStream.hxx"
+#include "SongEnumerator.hxx"
+#include "DetachedSong.hxx"
+#include "thread/Cond.hxx"
+#include "fs/Traits.hxx"
+
+PlaylistResult
+playlist_load_into_queue(const char *uri, SongEnumerator &e,
+ unsigned start_index, unsigned end_index,
+ playlist &dest, PlayerControl &pc,
+ bool secure)
+{
+ const std::string base_uri = uri != nullptr
+ ? PathTraitsUTF8::GetParent(uri)
+ : std::string(".");
+
+ DetachedSong *song;
+ for (unsigned i = 0;
+ i < end_index && (song = e.NextSong()) != nullptr;
+ ++i) {
+ if (i < start_index) {
+ /* skip songs before the start index */
+ delete song;
+ continue;
+ }
+
+ if (!playlist_check_translate_song(*song, base_uri.c_str(),
+ secure)) {
+ delete song;
+ continue;
+ }
+
+ PlaylistResult result = dest.AppendSong(pc, std::move(*song));
+ delete song;
+ if (result != PlaylistResult::SUCCESS)
+ return result;
+ }
+
+ return PlaylistResult::SUCCESS;
+}
+
+PlaylistResult
+playlist_open_into_queue(const char *uri,
+ unsigned start_index, unsigned end_index,
+ playlist &dest, PlayerControl &pc,
+ bool secure)
+{
+ Mutex mutex;
+ Cond cond;
+
+ InputStream *is;
+ auto playlist = playlist_open_any(uri, mutex, cond, &is);
+ if (playlist == nullptr)
+ return PlaylistResult::NO_SUCH_LIST;
+
+ PlaylistResult result =
+ playlist_load_into_queue(uri, *playlist,
+ start_index, end_index,
+ dest, pc, secure);
+ delete playlist;
+
+ if (is != nullptr)
+ is->Close();
+
+ return result;
+}
diff --git a/src/playlist/PlaylistQueue.hxx b/src/playlist/PlaylistQueue.hxx
new file mode 100644
index 000000000..075191ced
--- /dev/null
+++ b/src/playlist/PlaylistQueue.hxx
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2003-2014 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+/*! \file
+ * \brief Glue between playlist plugin and the play queue
+ */
+
+#ifndef MPD_PLAYLIST_QUEUE_HXX
+#define MPD_PLAYLIST_QUEUE_HXX
+
+#include "PlaylistError.hxx"
+
+class SongEnumerator;
+struct playlist;
+struct PlayerControl;
+
+/**
+ * Loads the contents of a playlist and append it to the specified
+ * play queue.
+ *
+ * @param uri the URI of the playlist, used to resolve relative song
+ * URIs
+ * @param start_index the index of the first song
+ * @param end_index the index of the last song (excluding)
+ */
+PlaylistResult
+playlist_load_into_queue(const char *uri, SongEnumerator &e,
+ unsigned start_index, unsigned end_index,
+ playlist &dest, PlayerControl &pc,
+ bool secure);
+
+/**
+ * Opens a playlist with a playlist plugin and append to the specified
+ * play queue.
+ */
+PlaylistResult
+playlist_open_into_queue(const char *uri,
+ unsigned start_index, unsigned end_index,
+ playlist &dest, PlayerControl &pc,
+ bool secure);
+
+#endif
+
diff --git a/src/playlist/PlaylistRegistry.cxx b/src/playlist/PlaylistRegistry.cxx
new file mode 100644
index 000000000..94244f14c
--- /dev/null
+++ b/src/playlist/PlaylistRegistry.cxx
@@ -0,0 +1,335 @@
+/*
+ * Copyright (C) 2003-2014 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 "PlaylistPlugin.hxx"
+#include "plugins/ExtM3uPlaylistPlugin.hxx"
+#include "plugins/M3uPlaylistPlugin.hxx"
+#include "plugins/XspfPlaylistPlugin.hxx"
+#include "plugins/DespotifyPlaylistPlugin.hxx"
+#include "plugins/SoundCloudPlaylistPlugin.hxx"
+#include "plugins/PlsPlaylistPlugin.hxx"
+#include "plugins/AsxPlaylistPlugin.hxx"
+#include "plugins/RssPlaylistPlugin.hxx"
+#include "plugins/CuePlaylistPlugin.hxx"
+#include "plugins/EmbeddedCuePlaylistPlugin.hxx"
+#include "InputStream.hxx"
+#include "util/UriUtil.hxx"
+#include "util/StringUtil.hxx"
+#include "util/Error.hxx"
+#include "util/Macros.hxx"
+#include "ConfigGlobal.hxx"
+#include "ConfigData.hxx"
+#include "system/FatalError.hxx"
+#include "Log.hxx"
+
+#include <assert.h>
+#include <string.h>
+
+const struct playlist_plugin *const playlist_plugins[] = {
+ &extm3u_playlist_plugin,
+ &m3u_playlist_plugin,
+ &pls_playlist_plugin,
+#ifdef HAVE_EXPAT
+ &xspf_playlist_plugin,
+ &asx_playlist_plugin,
+ &rss_playlist_plugin,
+#endif
+#ifdef ENABLE_DESPOTIFY
+ &despotify_playlist_plugin,
+#endif
+#ifdef ENABLE_SOUNDCLOUD
+ &soundcloud_playlist_plugin,
+#endif
+ &cue_playlist_plugin,
+ &embcue_playlist_plugin,
+ nullptr
+};
+
+static constexpr unsigned n_playlist_plugins =
+ ARRAY_SIZE(playlist_plugins) - 1;
+
+/** which plugins have been initialized successfully? */
+static bool playlist_plugins_enabled[n_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 nullptr if none was configured
+ */
+static const struct config_param *
+playlist_plugin_config(const char *plugin_name)
+{
+ const struct config_param *param = nullptr;
+
+ assert(plugin_name != nullptr);
+
+ while ((param = config_get_next_param(CONF_PLAYLIST_PLUGIN, param)) != nullptr) {
+ const char *name = param->GetBlockValue("name");
+ if (name == nullptr)
+ FormatFatalError("playlist configuration without 'plugin' name in line %d",
+ param->line);
+
+ if (strcmp(name, plugin_name) == 0)
+ return param;
+ }
+
+ return nullptr;
+}
+
+void
+playlist_list_global_init(void)
+{
+ const config_param empty;
+
+ for (unsigned i = 0; playlist_plugins[i] != nullptr; ++i) {
+ const struct playlist_plugin *plugin = playlist_plugins[i];
+ const struct config_param *param =
+ playlist_plugin_config(plugin->name);
+ if (param == nullptr)
+ param = &empty;
+ else if (!param->GetBlockValue("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 SongEnumerator *
+playlist_list_open_uri_scheme(const char *uri, Mutex &mutex, Cond &cond,
+ bool *tried)
+{
+ SongEnumerator *playlist = nullptr;
+
+ assert(uri != nullptr);
+
+ const auto scheme = uri_get_scheme(uri);
+ if (scheme.empty())
+ return nullptr;
+
+ for (unsigned i = 0; playlist_plugins[i] != nullptr; ++i) {
+ const struct playlist_plugin *plugin = playlist_plugins[i];
+
+ assert(!tried[i]);
+
+ if (playlist_plugins_enabled[i] && plugin->open_uri != nullptr &&
+ plugin->schemes != nullptr &&
+ string_array_contains(plugin->schemes, scheme.c_str())) {
+ playlist = playlist_plugin_open_uri(plugin, uri,
+ mutex, cond);
+ if (playlist != nullptr)
+ break;
+
+ tried[i] = true;
+ }
+ }
+
+ return playlist;
+}
+
+static SongEnumerator *
+playlist_list_open_uri_suffix(const char *uri, Mutex &mutex, Cond &cond,
+ const bool *tried)
+{
+ const char *suffix;
+ SongEnumerator *playlist = nullptr;
+
+ assert(uri != nullptr);
+
+ suffix = uri_get_suffix(uri);
+ if (suffix == nullptr)
+ return nullptr;
+
+ for (unsigned i = 0; playlist_plugins[i] != nullptr; ++i) {
+ const struct playlist_plugin *plugin = playlist_plugins[i];
+
+ if (playlist_plugins_enabled[i] && !tried[i] &&
+ plugin->open_uri != nullptr && plugin->suffixes != nullptr &&
+ string_array_contains(plugin->suffixes, suffix)) {
+ playlist = playlist_plugin_open_uri(plugin, uri,
+ mutex, cond);
+ if (playlist != nullptr)
+ break;
+ }
+ }
+
+ return playlist;
+}
+
+SongEnumerator *
+playlist_list_open_uri(const char *uri, Mutex &mutex, Cond &cond)
+{
+ /** this array tracks which plugins have already been tried by
+ playlist_list_open_uri_scheme() */
+ bool tried[n_playlist_plugins];
+
+ assert(uri != nullptr);
+
+ memset(tried, false, sizeof(tried));
+
+ auto playlist = playlist_list_open_uri_scheme(uri, mutex, cond, tried);
+ if (playlist == nullptr)
+ playlist = playlist_list_open_uri_suffix(uri, mutex, cond,
+ tried);
+
+ return playlist;
+}
+
+static SongEnumerator *
+playlist_list_open_stream_mime2(InputStream &is, const char *mime)
+{
+ assert(mime != nullptr);
+
+ playlist_plugins_for_each_enabled(plugin) {
+ if (plugin->open_stream != nullptr &&
+ plugin->mime_types != nullptr &&
+ string_array_contains(plugin->mime_types, mime)) {
+ /* rewind the stream, so each plugin gets a
+ fresh start */
+ is.Rewind(IgnoreError());
+
+ auto playlist = playlist_plugin_open_stream(plugin,
+ is);
+ if (playlist != nullptr)
+ return playlist;
+ }
+ }
+
+ return nullptr;
+}
+
+static SongEnumerator *
+playlist_list_open_stream_mime(InputStream &is, const char *full_mime)
+{
+ assert(full_mime != nullptr);
+
+ const char *semicolon = strchr(full_mime, ';');
+ if (semicolon == nullptr)
+ return playlist_list_open_stream_mime2(is, full_mime);
+
+ if (semicolon == full_mime)
+ return nullptr;
+
+ /* probe only the portion before the semicolon*/
+ const std::string mime(full_mime, semicolon);
+ return playlist_list_open_stream_mime2(is, mime.c_str());
+}
+
+static SongEnumerator *
+playlist_list_open_stream_suffix(InputStream &is, const char *suffix)
+{
+ assert(suffix != nullptr);
+
+ playlist_plugins_for_each_enabled(plugin) {
+ if (plugin->open_stream != nullptr &&
+ plugin->suffixes != nullptr &&
+ string_array_contains(plugin->suffixes, suffix)) {
+ /* rewind the stream, so each plugin gets a
+ fresh start */
+ is.Rewind(IgnoreError());
+
+ auto playlist = playlist_plugin_open_stream(plugin, is);
+ if (playlist != nullptr)
+ return playlist;
+ }
+ }
+
+ return nullptr;
+}
+
+SongEnumerator *
+playlist_list_open_stream(InputStream &is, const char *uri)
+{
+ assert(is.ready);
+
+ const char *const mime = is.GetMimeType();
+ if (mime != nullptr) {
+ auto playlist = playlist_list_open_stream_mime(is, mime);
+ if (playlist != nullptr)
+ return playlist;
+ }
+
+ const char *suffix = uri != nullptr ? uri_get_suffix(uri) : nullptr;
+ if (suffix != nullptr) {
+ auto playlist = playlist_list_open_stream_suffix(is, suffix);
+ if (playlist != nullptr)
+ return playlist;
+ }
+
+ return nullptr;
+}
+
+bool
+playlist_suffix_supported(const char *suffix)
+{
+ assert(suffix != nullptr);
+
+ playlist_plugins_for_each_enabled(plugin) {
+ if (plugin->suffixes != nullptr &&
+ string_array_contains(plugin->suffixes, suffix))
+ return true;
+ }
+
+ return false;
+}
+
+SongEnumerator *
+playlist_list_open_path(const char *path_fs, Mutex &mutex, Cond &cond,
+ InputStream **is_r)
+{
+ const char *suffix;
+
+ assert(path_fs != nullptr);
+
+ suffix = uri_get_suffix(path_fs);
+ if (suffix == nullptr || !playlist_suffix_supported(suffix))
+ return nullptr;
+
+ Error error;
+ InputStream *is = InputStream::OpenReady(path_fs, mutex, cond, error);
+ if (is == nullptr) {
+ if (error.IsDefined())
+ LogError(error);
+
+ return nullptr;
+ }
+
+ auto playlist = playlist_list_open_stream_suffix(*is, suffix);
+ if (playlist != nullptr)
+ *is_r = is;
+ else
+ is->Close();
+
+ return playlist;
+}
diff --git a/src/playlist/PlaylistRegistry.hxx b/src/playlist/PlaylistRegistry.hxx
new file mode 100644
index 000000000..0079fa68e
--- /dev/null
+++ b/src/playlist/PlaylistRegistry.hxx
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2003-2014 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
+
+class Mutex;
+class Cond;
+class SongEnumerator;
+struct InputStream;
+
+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) != nullptr; \
+ ++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.
+ */
+SongEnumerator *
+playlist_list_open_uri(const char *uri, Mutex &mutex, Cond &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
+ */
+SongEnumerator *
+playlist_list_open_stream(InputStream &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 nullptr on error
+ */
+SongEnumerator *
+playlist_list_open_path(const char *path_fs, Mutex &mutex, Cond &cond,
+ InputStream **is_r);
+
+#endif
diff --git a/src/playlist/PlaylistSong.cxx b/src/playlist/PlaylistSong.cxx
new file mode 100644
index 000000000..bcbdc30be
--- /dev/null
+++ b/src/playlist/PlaylistSong.cxx
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2003-2014 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 "PlaylistSong.hxx"
+#include "Mapper.hxx"
+#include "DatabaseSong.hxx"
+#include "ls.hxx"
+#include "tag/Tag.hxx"
+#include "tag/TagBuilder.hxx"
+#include "fs/AllocatedPath.hxx"
+#include "fs/Traits.hxx"
+#include "util/UriUtil.hxx"
+#include "util/Error.hxx"
+#include "DetachedSong.hxx"
+
+#include <assert.h>
+#include <string.h>
+
+static void
+merge_song_metadata(DetachedSong &add, const DetachedSong &base)
+{
+ {
+ TagBuilder builder(add.GetTag());
+ builder.Complement(base.GetTag());
+ add.SetTag(builder.Commit());
+ }
+
+ add.SetLastModified(base.GetLastModified());
+}
+
+static void
+apply_song_metadata(DetachedSong &dest, const DetachedSong &src)
+{
+ if (!src.GetTag().IsDefined() &&
+ src.GetStartMS() == 0 && src.GetEndMS() == 0)
+ return;
+
+ merge_song_metadata(dest, src);
+
+ if (dest.GetTag().IsDefined() && dest.GetTag().time > 0 &&
+ src.GetStartMS() > 0 && src.GetEndMS() == 0 &&
+ src.GetStartMS() / 1000 < (unsigned)dest.GetTag().time)
+ /* the range is open-ended, and the playlist plugin
+ did not know the total length of the song file
+ (e.g. last track on a CUE file); fix it up here */
+ dest.WritableTag().time =
+ dest.GetTag().time - src.GetStartMS() / 1000;
+}
+
+static bool
+playlist_check_load_song(DetachedSong &song)
+{
+ const char *const uri = song.GetURI();
+
+ if (uri_has_scheme(uri)) {
+ return true;
+ } else if (PathTraitsUTF8::IsAbsolute(uri)) {
+ DetachedSong tmp(uri);
+ if (!tmp.Update())
+ return false;
+
+ apply_song_metadata(song, tmp);
+ return true;
+ } else {
+ DetachedSong *tmp = DatabaseDetachSong(uri, IgnoreError());
+ if (tmp == nullptr)
+ return false;
+
+ apply_song_metadata(song, *tmp);
+ delete tmp;
+ return true;
+ }
+}
+
+bool
+playlist_check_translate_song(DetachedSong &song, const char *base_uri,
+ bool secure)
+{
+ const char *const uri = song.GetURI();
+
+ if (uri_has_scheme(uri))
+ /* valid remote song? */
+ return uri_supported_scheme(uri);
+
+ if (base_uri != nullptr && strcmp(base_uri, ".") == 0)
+ /* PathTraitsUTF8::GetParent() returns "." when there
+ is no directory name in the given path; clear that
+ now, because it would break the database lookup
+ functions */
+ base_uri = nullptr;
+
+ if (PathTraitsUTF8::IsAbsolute(uri)) {
+ /* XXX fs_charset vs utf8? */
+ const char *suffix = map_to_relative_path(uri);
+ assert(suffix != nullptr);
+
+ if (suffix != uri)
+ song.SetURI(std::string(suffix));
+ else if (!secure)
+ /* local files must be relative to the music
+ directory when "secure" is enabled */
+ return false;
+
+ base_uri = nullptr;
+ }
+
+ if (base_uri != nullptr) {
+ song.SetURI(PathTraitsUTF8::Build(base_uri, uri));
+ /* repeat the above checks */
+ return playlist_check_translate_song(song, nullptr, secure);
+ }
+
+ return playlist_check_load_song(song);
+}
diff --git a/src/playlist/PlaylistSong.hxx b/src/playlist/PlaylistSong.hxx
new file mode 100644
index 000000000..2a47b28db
--- /dev/null
+++ b/src/playlist/PlaylistSong.hxx
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2003-2014 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_PLAYLIST_SONG_HXX
+#define MPD_PLAYLIST_SONG_HXX
+
+class DetachedSong;
+
+/**
+ * Verifies the song, returns false if it is unsafe. Translate the
+ * song to a song within the database, if it is a local file.
+ *
+ * @param secure if true, then local files are only allowed if they
+ * are relative to base_uri
+ * @return true on success, false if the song should not be used
+ */
+bool
+playlist_check_translate_song(DetachedSong &song, const char *base_uri,
+ bool secure);
+
+#endif
diff --git a/src/playlist/Print.cxx b/src/playlist/Print.cxx
new file mode 100644
index 000000000..7f13d5544
--- /dev/null
+++ b/src/playlist/Print.cxx
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2003-2014 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 "Print.hxx"
+#include "PlaylistAny.hxx"
+#include "PlaylistSong.hxx"
+#include "SongEnumerator.hxx"
+#include "SongPrint.hxx"
+#include "InputStream.hxx"
+#include "DetachedSong.hxx"
+#include "fs/Traits.hxx"
+#include "thread/Cond.hxx"
+
+static void
+playlist_provider_print(Client &client, const char *uri,
+ SongEnumerator &e, bool detail)
+{
+ const std::string base_uri = uri != nullptr
+ ? PathTraitsUTF8::GetParent(uri)
+ : std::string(".");
+
+ DetachedSong *song;
+ while ((song = e.NextSong()) != nullptr) {
+ if (playlist_check_translate_song(*song, base_uri.c_str(),
+ false)) {
+ if (detail)
+ song_print_info(client, *song);
+ else
+ song_print_uri(client, *song);
+ }
+
+ delete song;
+ }
+}
+
+bool
+playlist_file_print(Client &client, const char *uri, bool detail)
+{
+ Mutex mutex;
+ Cond cond;
+
+ InputStream *is;
+ SongEnumerator *playlist = playlist_open_any(uri, mutex, cond, &is);
+ if (playlist == nullptr)
+ return false;
+
+ playlist_provider_print(client, uri, *playlist, detail);
+ delete playlist;
+
+ if (is != nullptr)
+ is->Close();
+
+ return true;
+}
diff --git a/src/playlist/Print.hxx b/src/playlist/Print.hxx
new file mode 100644
index 000000000..c2fff5475
--- /dev/null
+++ b/src/playlist/Print.hxx
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2003-2014 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__PRINT_HXX
+#define MPD_PLAYLIST__PRINT_HXX
+
+class Client;
+
+/**
+ * Send the playlist file to the client.
+ *
+ * @param client the client which requested the playlist
+ * @param uri the URI of the playlist file in UTF-8 encoding
+ * @param detail true if all details should be printed
+ * @return true on success, false if the playlist does not exist
+ */
+bool
+playlist_file_print(Client &client, const char *uri, bool detail);
+
+#endif
diff --git a/src/playlist/SongEnumerator.hxx b/src/playlist/SongEnumerator.hxx
new file mode 100644
index 000000000..75295add1
--- /dev/null
+++ b/src/playlist/SongEnumerator.hxx
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2003-2014 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_SONG_ENUMERATOR_HXX
+#define MPD_SONG_ENUMERATOR_HXX
+
+class DetachedSong;
+
+/**
+ * An object which provides serial access to a number of #Song
+ * objects. It is used to enumerate the contents of a playlist file.
+ */
+class SongEnumerator {
+public:
+ virtual ~SongEnumerator() {}
+
+ /**
+ * Obtain the next song. The caller is responsible for
+ * freeing the returned #Song object. Returns nullptr if
+ * there are no more songs.
+ */
+ virtual DetachedSong *NextSong() = 0;
+};
+
+#endif
diff --git a/src/playlist/AsxPlaylistPlugin.cxx b/src/playlist/plugins/AsxPlaylistPlugin.cxx
index 24eb26077..5434c8e85 100644
--- a/src/playlist/AsxPlaylistPlugin.cxx
+++ b/src/playlist/plugins/AsxPlaylistPlugin.cxx
@@ -19,8 +19,8 @@
#include "config.h"
#include "AsxPlaylistPlugin.hxx"
-#include "PlaylistPlugin.hxx"
-#include "MemorySongEnumerator.hxx"
+#include "../PlaylistPlugin.hxx"
+#include "../MemorySongEnumerator.hxx"
#include "tag/TagBuilder.hxx"
#include "util/ASCII.hxx"
#include "util/Error.hxx"
diff --git a/src/playlist/AsxPlaylistPlugin.hxx b/src/playlist/plugins/AsxPlaylistPlugin.hxx
index 63371be0f..63371be0f 100644
--- a/src/playlist/AsxPlaylistPlugin.hxx
+++ b/src/playlist/plugins/AsxPlaylistPlugin.hxx
diff --git a/src/playlist/CuePlaylistPlugin.cxx b/src/playlist/plugins/CuePlaylistPlugin.cxx
index 505c0c5d8..d52d41c07 100644
--- a/src/playlist/CuePlaylistPlugin.cxx
+++ b/src/playlist/plugins/CuePlaylistPlugin.cxx
@@ -19,8 +19,8 @@
#include "config.h"
#include "CuePlaylistPlugin.hxx"
-#include "PlaylistPlugin.hxx"
-#include "SongEnumerator.hxx"
+#include "../PlaylistPlugin.hxx"
+#include "../SongEnumerator.hxx"
#include "cue/CueParser.hxx"
#include "TextInputStream.hxx"
diff --git a/src/playlist/CuePlaylistPlugin.hxx b/src/playlist/plugins/CuePlaylistPlugin.hxx
index 4d833bfc2..4d833bfc2 100644
--- a/src/playlist/CuePlaylistPlugin.hxx
+++ b/src/playlist/plugins/CuePlaylistPlugin.hxx
diff --git a/src/playlist/DespotifyPlaylistPlugin.cxx b/src/playlist/plugins/DespotifyPlaylistPlugin.cxx
index 7d73a64bc..1e8de0130 100644
--- a/src/playlist/DespotifyPlaylistPlugin.cxx
+++ b/src/playlist/plugins/DespotifyPlaylistPlugin.cxx
@@ -20,8 +20,8 @@
#include "config.h"
#include "DespotifyPlaylistPlugin.hxx"
#include "DespotifyUtils.hxx"
-#include "PlaylistPlugin.hxx"
-#include "MemorySongEnumerator.hxx"
+#include "../PlaylistPlugin.hxx"
+#include "../MemorySongEnumerator.hxx"
#include "tag/Tag.hxx"
#include "DetachedSong.hxx"
#include "Log.hxx"
diff --git a/src/playlist/DespotifyPlaylistPlugin.hxx b/src/playlist/plugins/DespotifyPlaylistPlugin.hxx
index 6acfd40f4..6acfd40f4 100644
--- a/src/playlist/DespotifyPlaylistPlugin.hxx
+++ b/src/playlist/plugins/DespotifyPlaylistPlugin.hxx
diff --git a/src/playlist/EmbeddedCuePlaylistPlugin.cxx b/src/playlist/plugins/EmbeddedCuePlaylistPlugin.cxx
index 4d9eb4b9b..53f3feda0 100644
--- a/src/playlist/EmbeddedCuePlaylistPlugin.cxx
+++ b/src/playlist/plugins/EmbeddedCuePlaylistPlugin.cxx
@@ -25,8 +25,8 @@
#include "config.h"
#include "EmbeddedCuePlaylistPlugin.hxx"
-#include "PlaylistPlugin.hxx"
-#include "SongEnumerator.hxx"
+#include "../PlaylistPlugin.hxx"
+#include "../SongEnumerator.hxx"
#include "tag/TagHandler.hxx"
#include "tag/TagId3.hxx"
#include "tag/ApeTag.hxx"
diff --git a/src/playlist/EmbeddedCuePlaylistPlugin.hxx b/src/playlist/plugins/EmbeddedCuePlaylistPlugin.hxx
index 5eedf3f13..5eedf3f13 100644
--- a/src/playlist/EmbeddedCuePlaylistPlugin.hxx
+++ b/src/playlist/plugins/EmbeddedCuePlaylistPlugin.hxx
diff --git a/src/playlist/ExtM3uPlaylistPlugin.cxx b/src/playlist/plugins/ExtM3uPlaylistPlugin.cxx
index 1a975c081..ac27fb136 100644
--- a/src/playlist/ExtM3uPlaylistPlugin.cxx
+++ b/src/playlist/plugins/ExtM3uPlaylistPlugin.cxx
@@ -19,8 +19,8 @@
#include "config.h"
#include "ExtM3uPlaylistPlugin.hxx"
-#include "PlaylistPlugin.hxx"
-#include "SongEnumerator.hxx"
+#include "../PlaylistPlugin.hxx"
+#include "../SongEnumerator.hxx"
#include "DetachedSong.hxx"
#include "tag/Tag.hxx"
#include "tag/TagBuilder.hxx"
diff --git a/src/playlist/ExtM3uPlaylistPlugin.hxx b/src/playlist/plugins/ExtM3uPlaylistPlugin.hxx
index 5743ded43..5743ded43 100644
--- a/src/playlist/ExtM3uPlaylistPlugin.hxx
+++ b/src/playlist/plugins/ExtM3uPlaylistPlugin.hxx
diff --git a/src/playlist/M3uPlaylistPlugin.cxx b/src/playlist/plugins/M3uPlaylistPlugin.cxx
index fe7d8a17f..a87e3edc7 100644
--- a/src/playlist/M3uPlaylistPlugin.cxx
+++ b/src/playlist/plugins/M3uPlaylistPlugin.cxx
@@ -19,8 +19,8 @@
#include "config.h"
#include "M3uPlaylistPlugin.hxx"
-#include "PlaylistPlugin.hxx"
-#include "SongEnumerator.hxx"
+#include "../PlaylistPlugin.hxx"
+#include "../SongEnumerator.hxx"
#include "DetachedSong.hxx"
#include "util/StringUtil.hxx"
#include "TextInputStream.hxx"
diff --git a/src/playlist/M3uPlaylistPlugin.hxx b/src/playlist/plugins/M3uPlaylistPlugin.hxx
index f1ad14069..f1ad14069 100644
--- a/src/playlist/M3uPlaylistPlugin.hxx
+++ b/src/playlist/plugins/M3uPlaylistPlugin.hxx
diff --git a/src/playlist/PlsPlaylistPlugin.cxx b/src/playlist/plugins/PlsPlaylistPlugin.cxx
index 839098a73..0abd7172b 100644
--- a/src/playlist/PlsPlaylistPlugin.cxx
+++ b/src/playlist/plugins/PlsPlaylistPlugin.cxx
@@ -19,8 +19,8 @@
#include "config.h"
#include "PlsPlaylistPlugin.hxx"
-#include "PlaylistPlugin.hxx"
-#include "MemorySongEnumerator.hxx"
+#include "../PlaylistPlugin.hxx"
+#include "../MemorySongEnumerator.hxx"
#include "InputStream.hxx"
#include "DetachedSong.hxx"
#include "tag/TagBuilder.hxx"
diff --git a/src/playlist/PlsPlaylistPlugin.hxx b/src/playlist/plugins/PlsPlaylistPlugin.hxx
index 1a3f33873..1a3f33873 100644
--- a/src/playlist/PlsPlaylistPlugin.hxx
+++ b/src/playlist/plugins/PlsPlaylistPlugin.hxx
diff --git a/src/playlist/RssPlaylistPlugin.cxx b/src/playlist/plugins/RssPlaylistPlugin.cxx
index 550a4630e..076d82f14 100644
--- a/src/playlist/RssPlaylistPlugin.cxx
+++ b/src/playlist/plugins/RssPlaylistPlugin.cxx
@@ -19,8 +19,8 @@
#include "config.h"
#include "RssPlaylistPlugin.hxx"
-#include "PlaylistPlugin.hxx"
-#include "MemorySongEnumerator.hxx"
+#include "../PlaylistPlugin.hxx"
+#include "../MemorySongEnumerator.hxx"
#include "tag/TagBuilder.hxx"
#include "util/ASCII.hxx"
#include "util/Error.hxx"
diff --git a/src/playlist/RssPlaylistPlugin.hxx b/src/playlist/plugins/RssPlaylistPlugin.hxx
index a00a5a898..a00a5a898 100644
--- a/src/playlist/RssPlaylistPlugin.hxx
+++ b/src/playlist/plugins/RssPlaylistPlugin.hxx
diff --git a/src/playlist/SoundCloudPlaylistPlugin.cxx b/src/playlist/plugins/SoundCloudPlaylistPlugin.cxx
index bf68acd3b..fac052d19 100644
--- a/src/playlist/SoundCloudPlaylistPlugin.cxx
+++ b/src/playlist/plugins/SoundCloudPlaylistPlugin.cxx
@@ -19,8 +19,8 @@
#include "config.h"
#include "SoundCloudPlaylistPlugin.hxx"
-#include "PlaylistPlugin.hxx"
-#include "MemorySongEnumerator.hxx"
+#include "../PlaylistPlugin.hxx"
+#include "../MemorySongEnumerator.hxx"
#include "ConfigData.hxx"
#include "InputStream.hxx"
#include "tag/TagBuilder.hxx"
diff --git a/src/playlist/SoundCloudPlaylistPlugin.hxx b/src/playlist/plugins/SoundCloudPlaylistPlugin.hxx
index b355b477a..b355b477a 100644
--- a/src/playlist/SoundCloudPlaylistPlugin.hxx
+++ b/src/playlist/plugins/SoundCloudPlaylistPlugin.hxx
diff --git a/src/playlist/XspfPlaylistPlugin.cxx b/src/playlist/plugins/XspfPlaylistPlugin.cxx
index e726ad338..12888b3bc 100644
--- a/src/playlist/XspfPlaylistPlugin.cxx
+++ b/src/playlist/plugins/XspfPlaylistPlugin.cxx
@@ -19,8 +19,8 @@
#include "config.h"
#include "XspfPlaylistPlugin.hxx"
-#include "PlaylistPlugin.hxx"
-#include "MemorySongEnumerator.hxx"
+#include "../PlaylistPlugin.hxx"
+#include "../MemorySongEnumerator.hxx"
#include "DetachedSong.hxx"
#include "InputStream.hxx"
#include "tag/TagBuilder.hxx"
diff --git a/src/playlist/XspfPlaylistPlugin.hxx b/src/playlist/plugins/XspfPlaylistPlugin.hxx
index 6b08a6be6..6b08a6be6 100644
--- a/src/playlist/XspfPlaylistPlugin.hxx
+++ b/src/playlist/plugins/XspfPlaylistPlugin.hxx