From f1f19841bdd291c055f59b6603f69278c66366d8 Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Thu, 23 Jan 2014 23:30:12 +0100 Subject: playlist/*: move to playlist/plugins/ --- src/CommandLine.cxx | 4 +- src/Main.cxx | 2 +- src/MemorySongEnumerator.cxx | 32 -- src/MemorySongEnumerator.hxx | 38 -- src/PlaylistAny.cxx | 69 ---- src/PlaylistAny.hxx | 41 --- src/PlaylistMapper.cxx | 102 ------ src/PlaylistMapper.hxx | 40 -- src/PlaylistPlugin.hxx | 109 ------ src/PlaylistPrint.cxx | 45 --- src/PlaylistPrint.hxx | 12 - src/PlaylistQueue.cxx | 90 ----- src/PlaylistQueue.hxx | 59 --- src/PlaylistRegistry.cxx | 335 ----------------- src/PlaylistRegistry.hxx | 83 ----- src/PlaylistSong.cxx | 131 ------- src/PlaylistSong.hxx | 37 -- src/SongEnumerator.hxx | 41 --- src/UpdateWalk.cxx | 2 +- src/command/PlaylistCommands.cxx | 3 +- src/playlist/AsxPlaylistPlugin.cxx | 186 ---------- src/playlist/AsxPlaylistPlugin.hxx | 25 -- src/playlist/CuePlaylistPlugin.cxx | 88 ----- src/playlist/CuePlaylistPlugin.hxx | 25 -- src/playlist/DespotifyPlaylistPlugin.cxx | 141 -------- src/playlist/DespotifyPlaylistPlugin.hxx | 25 -- src/playlist/EmbeddedCuePlaylistPlugin.cxx | 184 ---------- src/playlist/EmbeddedCuePlaylistPlugin.hxx | 25 -- src/playlist/ExtM3uPlaylistPlugin.cxx | 149 -------- src/playlist/ExtM3uPlaylistPlugin.hxx | 25 -- src/playlist/M3uPlaylistPlugin.cxx | 83 ----- src/playlist/M3uPlaylistPlugin.hxx | 25 -- src/playlist/MemorySongEnumerator.cxx | 32 ++ src/playlist/MemorySongEnumerator.hxx | 38 ++ src/playlist/PlaylistAny.cxx | 69 ++++ src/playlist/PlaylistAny.hxx | 41 +++ src/playlist/PlaylistMapper.cxx | 102 ++++++ src/playlist/PlaylistMapper.hxx | 40 ++ src/playlist/PlaylistPlugin.hxx | 109 ++++++ src/playlist/PlaylistQueue.cxx | 90 +++++ src/playlist/PlaylistQueue.hxx | 59 +++ src/playlist/PlaylistRegistry.cxx | 335 +++++++++++++++++ src/playlist/PlaylistRegistry.hxx | 83 +++++ src/playlist/PlaylistSong.cxx | 131 +++++++ src/playlist/PlaylistSong.hxx | 37 ++ src/playlist/PlsPlaylistPlugin.cxx | 163 --------- src/playlist/PlsPlaylistPlugin.hxx | 25 -- src/playlist/Print.cxx | 71 ++++ src/playlist/Print.hxx | 36 ++ src/playlist/RssPlaylistPlugin.cxx | 185 ---------- src/playlist/RssPlaylistPlugin.hxx | 25 -- src/playlist/SongEnumerator.hxx | 41 +++ src/playlist/SoundCloudPlaylistPlugin.cxx | 401 --------------------- src/playlist/SoundCloudPlaylistPlugin.hxx | 25 -- src/playlist/XspfPlaylistPlugin.cxx | 234 ------------ src/playlist/XspfPlaylistPlugin.hxx | 25 -- src/playlist/plugins/AsxPlaylistPlugin.cxx | 186 ++++++++++ src/playlist/plugins/AsxPlaylistPlugin.hxx | 25 ++ src/playlist/plugins/CuePlaylistPlugin.cxx | 88 +++++ src/playlist/plugins/CuePlaylistPlugin.hxx | 25 ++ src/playlist/plugins/DespotifyPlaylistPlugin.cxx | 141 ++++++++ src/playlist/plugins/DespotifyPlaylistPlugin.hxx | 25 ++ src/playlist/plugins/EmbeddedCuePlaylistPlugin.cxx | 184 ++++++++++ src/playlist/plugins/EmbeddedCuePlaylistPlugin.hxx | 25 ++ src/playlist/plugins/ExtM3uPlaylistPlugin.cxx | 149 ++++++++ src/playlist/plugins/ExtM3uPlaylistPlugin.hxx | 25 ++ src/playlist/plugins/M3uPlaylistPlugin.cxx | 83 +++++ src/playlist/plugins/M3uPlaylistPlugin.hxx | 25 ++ src/playlist/plugins/PlsPlaylistPlugin.cxx | 163 +++++++++ src/playlist/plugins/PlsPlaylistPlugin.hxx | 25 ++ src/playlist/plugins/RssPlaylistPlugin.cxx | 185 ++++++++++ src/playlist/plugins/RssPlaylistPlugin.hxx | 25 ++ src/playlist/plugins/SoundCloudPlaylistPlugin.cxx | 401 +++++++++++++++++++++ src/playlist/plugins/SoundCloudPlaylistPlugin.hxx | 25 ++ src/playlist/plugins/XspfPlaylistPlugin.cxx | 234 ++++++++++++ src/playlist/plugins/XspfPlaylistPlugin.hxx | 25 ++ 76 files changed, 3384 insertions(+), 3333 deletions(-) delete mode 100644 src/MemorySongEnumerator.cxx delete mode 100644 src/MemorySongEnumerator.hxx delete mode 100644 src/PlaylistAny.cxx delete mode 100644 src/PlaylistAny.hxx delete mode 100644 src/PlaylistMapper.cxx delete mode 100644 src/PlaylistMapper.hxx delete mode 100644 src/PlaylistPlugin.hxx delete mode 100644 src/PlaylistQueue.cxx delete mode 100644 src/PlaylistQueue.hxx delete mode 100644 src/PlaylistRegistry.cxx delete mode 100644 src/PlaylistRegistry.hxx delete mode 100644 src/PlaylistSong.cxx delete mode 100644 src/PlaylistSong.hxx delete mode 100644 src/SongEnumerator.hxx delete mode 100644 src/playlist/AsxPlaylistPlugin.cxx delete mode 100644 src/playlist/AsxPlaylistPlugin.hxx delete mode 100644 src/playlist/CuePlaylistPlugin.cxx delete mode 100644 src/playlist/CuePlaylistPlugin.hxx delete mode 100644 src/playlist/DespotifyPlaylistPlugin.cxx delete mode 100644 src/playlist/DespotifyPlaylistPlugin.hxx delete mode 100644 src/playlist/EmbeddedCuePlaylistPlugin.cxx delete mode 100644 src/playlist/EmbeddedCuePlaylistPlugin.hxx delete mode 100644 src/playlist/ExtM3uPlaylistPlugin.cxx delete mode 100644 src/playlist/ExtM3uPlaylistPlugin.hxx delete mode 100644 src/playlist/M3uPlaylistPlugin.cxx delete mode 100644 src/playlist/M3uPlaylistPlugin.hxx create mode 100644 src/playlist/MemorySongEnumerator.cxx create mode 100644 src/playlist/MemorySongEnumerator.hxx create mode 100644 src/playlist/PlaylistAny.cxx create mode 100644 src/playlist/PlaylistAny.hxx create mode 100644 src/playlist/PlaylistMapper.cxx create mode 100644 src/playlist/PlaylistMapper.hxx create mode 100644 src/playlist/PlaylistPlugin.hxx create mode 100644 src/playlist/PlaylistQueue.cxx create mode 100644 src/playlist/PlaylistQueue.hxx create mode 100644 src/playlist/PlaylistRegistry.cxx create mode 100644 src/playlist/PlaylistRegistry.hxx create mode 100644 src/playlist/PlaylistSong.cxx create mode 100644 src/playlist/PlaylistSong.hxx delete mode 100644 src/playlist/PlsPlaylistPlugin.cxx delete mode 100644 src/playlist/PlsPlaylistPlugin.hxx create mode 100644 src/playlist/Print.cxx create mode 100644 src/playlist/Print.hxx delete mode 100644 src/playlist/RssPlaylistPlugin.cxx delete mode 100644 src/playlist/RssPlaylistPlugin.hxx create mode 100644 src/playlist/SongEnumerator.hxx delete mode 100644 src/playlist/SoundCloudPlaylistPlugin.cxx delete mode 100644 src/playlist/SoundCloudPlaylistPlugin.hxx delete mode 100644 src/playlist/XspfPlaylistPlugin.cxx delete mode 100644 src/playlist/XspfPlaylistPlugin.hxx create mode 100644 src/playlist/plugins/AsxPlaylistPlugin.cxx create mode 100644 src/playlist/plugins/AsxPlaylistPlugin.hxx create mode 100644 src/playlist/plugins/CuePlaylistPlugin.cxx create mode 100644 src/playlist/plugins/CuePlaylistPlugin.hxx create mode 100644 src/playlist/plugins/DespotifyPlaylistPlugin.cxx create mode 100644 src/playlist/plugins/DespotifyPlaylistPlugin.hxx create mode 100644 src/playlist/plugins/EmbeddedCuePlaylistPlugin.cxx create mode 100644 src/playlist/plugins/EmbeddedCuePlaylistPlugin.hxx create mode 100644 src/playlist/plugins/ExtM3uPlaylistPlugin.cxx create mode 100644 src/playlist/plugins/ExtM3uPlaylistPlugin.hxx create mode 100644 src/playlist/plugins/M3uPlaylistPlugin.cxx create mode 100644 src/playlist/plugins/M3uPlaylistPlugin.hxx create mode 100644 src/playlist/plugins/PlsPlaylistPlugin.cxx create mode 100644 src/playlist/plugins/PlsPlaylistPlugin.hxx create mode 100644 src/playlist/plugins/RssPlaylistPlugin.cxx create mode 100644 src/playlist/plugins/RssPlaylistPlugin.hxx create mode 100644 src/playlist/plugins/SoundCloudPlaylistPlugin.cxx create mode 100644 src/playlist/plugins/SoundCloudPlaylistPlugin.hxx create mode 100644 src/playlist/plugins/XspfPlaylistPlugin.cxx create mode 100644 src/playlist/plugins/XspfPlaylistPlugin.hxx (limited to 'src') diff --git a/src/CommandLine.cxx b/src/CommandLine.cxx index 9430d0df8..0667844f6 100644 --- a/src/CommandLine.cxx +++ b/src/CommandLine.cxx @@ -31,8 +31,8 @@ #include "OutputPlugin.hxx" #include "InputRegistry.hxx" #include "InputPlugin.hxx" -#include "PlaylistRegistry.hxx" -#include "PlaylistPlugin.hxx" +#include "playlist/PlaylistRegistry.hxx" +#include "playlist/PlaylistPlugin.hxx" #include "fs/AllocatedPath.hxx" #include "fs/Traits.hxx" #include "fs/FileSystem.hxx" diff --git a/src/Main.cxx b/src/Main.cxx index 10a9091a1..8808a78d9 100644 --- a/src/Main.cxx +++ b/src/Main.cxx @@ -51,7 +51,7 @@ #include "fs/AllocatedPath.hxx" #include "fs/Config.hxx" #include "fs/StandardDirectory.hxx" -#include "PlaylistRegistry.hxx" +#include "playlist/PlaylistRegistry.hxx" #include "ZeroconfGlue.hxx" #include "DecoderList.hxx" #include "AudioConfig.hxx" diff --git a/src/MemorySongEnumerator.cxx b/src/MemorySongEnumerator.cxx deleted file mode 100644 index c3127c2bf..000000000 --- a/src/MemorySongEnumerator.cxx +++ /dev/null @@ -1,32 +0,0 @@ -/* - * 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/MemorySongEnumerator.hxx b/src/MemorySongEnumerator.hxx deleted file mode 100644 index e87a4f6dd..000000000 --- a/src/MemorySongEnumerator.hxx +++ /dev/null @@ -1,38 +0,0 @@ -/* - * 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 - -class MemorySongEnumerator final : public SongEnumerator { - std::forward_list songs; - -public: - MemorySongEnumerator(std::forward_list &&_songs) - :songs(std::move(_songs)) {} - - virtual DetachedSong *NextSong() override; -}; - -#endif diff --git a/src/PlaylistAny.cxx b/src/PlaylistAny.cxx deleted file mode 100644 index 8d748cada..000000000 --- a/src/PlaylistAny.cxx +++ /dev/null @@ -1,69 +0,0 @@ -/* - * 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 - -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/PlaylistAny.hxx b/src/PlaylistAny.hxx deleted file mode 100644 index c472afb31..000000000 --- a/src/PlaylistAny.hxx +++ /dev/null @@ -1,41 +0,0 @@ -/* - * 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/PlaylistMapper.cxx b/src/PlaylistMapper.cxx deleted file mode 100644 index 0df0bc61f..000000000 --- a/src/PlaylistMapper.cxx +++ /dev/null @@ -1,102 +0,0 @@ -/* - * 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 - -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/PlaylistMapper.hxx b/src/PlaylistMapper.hxx deleted file mode 100644 index a460cb124..000000000 --- a/src/PlaylistMapper.hxx +++ /dev/null @@ -1,40 +0,0 @@ -/* - * 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/PlaylistPlugin.hxx b/src/PlaylistPlugin.hxx deleted file mode 100644 index d3c44f1f4..000000000 --- a/src/PlaylistPlugin.hxx +++ /dev/null @@ -1,109 +0,0 @@ -/* - * 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 ¶m); - - /** - * 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 ¶m) -{ - 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/PlaylistPrint.cxx b/src/PlaylistPrint.cxx index 38d63f4e8..63972f712 100644 --- a/src/PlaylistPrint.cxx +++ b/src/PlaylistPrint.cxx @@ -20,11 +20,8 @@ #include "config.h" #include "PlaylistPrint.hxx" #include "PlaylistFile.hxx" -#include "PlaylistAny.hxx" -#include "PlaylistSong.hxx" #include "Playlist.hxx" #include "QueuePrint.hxx" -#include "SongEnumerator.hxx" #include "SongPrint.hxx" #include "DatabaseGlue.hxx" #include "DatabasePlugin.hxx" @@ -145,45 +142,3 @@ spl_print(Client &client, const char *name_utf8, bool detail, return true; } - -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/PlaylistPrint.hxx b/src/PlaylistPrint.hxx index a1cf185cc..38a4cc7cf 100644 --- a/src/PlaylistPrint.hxx +++ b/src/PlaylistPrint.hxx @@ -1,4 +1,3 @@ - /* * Copyright (C) 2003-2014 The Music Player Daemon Project * http://www.musicpd.org @@ -96,15 +95,4 @@ bool spl_print(Client &client, const char *name_utf8, bool detail, Error &error); -/** - * 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/PlaylistQueue.cxx b/src/PlaylistQueue.cxx deleted file mode 100644 index 0a45920e3..000000000 --- a/src/PlaylistQueue.cxx +++ /dev/null @@ -1,90 +0,0 @@ -/* - * 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/PlaylistQueue.hxx b/src/PlaylistQueue.hxx deleted file mode 100644 index 075191ced..000000000 --- a/src/PlaylistQueue.hxx +++ /dev/null @@ -1,59 +0,0 @@ -/* - * 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/PlaylistRegistry.cxx b/src/PlaylistRegistry.cxx deleted file mode 100644 index 2fd3a71a6..000000000 --- a/src/PlaylistRegistry.cxx +++ /dev/null @@ -1,335 +0,0 @@ -/* - * 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 "playlist/ExtM3uPlaylistPlugin.hxx" -#include "playlist/M3uPlaylistPlugin.hxx" -#include "playlist/XspfPlaylistPlugin.hxx" -#include "playlist/DespotifyPlaylistPlugin.hxx" -#include "playlist/SoundCloudPlaylistPlugin.hxx" -#include "playlist/PlsPlaylistPlugin.hxx" -#include "playlist/AsxPlaylistPlugin.hxx" -#include "playlist/RssPlaylistPlugin.hxx" -#include "playlist/CuePlaylistPlugin.hxx" -#include "playlist/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 -#include - -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 = ∅ - 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/PlaylistRegistry.hxx b/src/PlaylistRegistry.hxx deleted file mode 100644 index 0079fa68e..000000000 --- a/src/PlaylistRegistry.hxx +++ /dev/null @@ -1,83 +0,0 @@ -/* - * 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/PlaylistSong.cxx b/src/PlaylistSong.cxx deleted file mode 100644 index bcbdc30be..000000000 --- a/src/PlaylistSong.cxx +++ /dev/null @@ -1,131 +0,0 @@ -/* - * 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 -#include - -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/PlaylistSong.hxx b/src/PlaylistSong.hxx deleted file mode 100644 index 2a47b28db..000000000 --- a/src/PlaylistSong.hxx +++ /dev/null @@ -1,37 +0,0 @@ -/* - * 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/SongEnumerator.hxx b/src/SongEnumerator.hxx deleted file mode 100644 index 75295add1..000000000 --- a/src/SongEnumerator.hxx +++ /dev/null @@ -1,41 +0,0 @@ -/* - * 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/UpdateWalk.cxx b/src/UpdateWalk.cxx index fa3611b1b..430e63af0 100644 --- a/src/UpdateWalk.cxx +++ b/src/UpdateWalk.cxx @@ -29,7 +29,7 @@ #include "Directory.hxx" #include "Song.hxx" #include "PlaylistVector.hxx" -#include "PlaylistRegistry.hxx" +#include "playlist/PlaylistRegistry.hxx" #include "Mapper.hxx" #include "ExcludeList.hxx" #include "ConfigGlobal.hxx" diff --git a/src/command/PlaylistCommands.cxx b/src/command/PlaylistCommands.cxx index e3f7faa05..2f203678b 100644 --- a/src/command/PlaylistCommands.cxx +++ b/src/command/PlaylistCommands.cxx @@ -25,7 +25,8 @@ #include "PlaylistSave.hxx" #include "PlaylistFile.hxx" #include "PlaylistVector.hxx" -#include "PlaylistQueue.hxx" +#include "playlist/PlaylistQueue.hxx" +#include "playlist/Print.hxx" #include "TimePrint.hxx" #include "Client.hxx" #include "protocol/ArgParser.hxx" diff --git a/src/playlist/AsxPlaylistPlugin.cxx b/src/playlist/AsxPlaylistPlugin.cxx deleted file mode 100644 index 24eb26077..000000000 --- a/src/playlist/AsxPlaylistPlugin.cxx +++ /dev/null @@ -1,186 +0,0 @@ -/* - * 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 "AsxPlaylistPlugin.hxx" -#include "PlaylistPlugin.hxx" -#include "MemorySongEnumerator.hxx" -#include "tag/TagBuilder.hxx" -#include "util/ASCII.hxx" -#include "util/Error.hxx" -#include "Expat.hxx" -#include "Log.hxx" - -/** - * This is the state object for the GLib XML parser. - */ -struct AsxParser { - /** - * The list of songs (in reverse order because that's faster - * while adding). - */ - std::forward_list songs; - - /** - * The current position in the XML file. - */ - enum { - ROOT, ENTRY, - } state; - - /** - * The current tag within the "entry" element. This is only - * valid if state==ENTRY. TAG_NUM_OF_ITEM_TYPES means there - * is no (known) tag. - */ - TagType tag_type; - - /** - * The current song URI. It is set by the "ref" element. - */ - std::string location; - - TagBuilder tag_builder; - - AsxParser() - :state(ROOT) {} - -}; - -static void XMLCALL -asx_start_element(void *user_data, const XML_Char *element_name, - const XML_Char **atts) -{ - AsxParser *parser = (AsxParser *)user_data; - - switch (parser->state) { - case AsxParser::ROOT: - if (StringEqualsCaseASCII(element_name, "entry")) { - parser->state = AsxParser::ENTRY; - parser->location.clear(); - parser->tag_type = TAG_NUM_OF_ITEM_TYPES; - } - - break; - - case AsxParser::ENTRY: - if (StringEqualsCaseASCII(element_name, "ref")) { - const char *href = - ExpatParser::GetAttributeCase(atts, "href"); - if (href != nullptr) - parser->location = href; - } else if (StringEqualsCaseASCII(element_name, "author")) - /* is that correct? or should it be COMPOSER - or PERFORMER? */ - parser->tag_type = TAG_ARTIST; - else if (StringEqualsCaseASCII(element_name, "title")) - parser->tag_type = TAG_TITLE; - - break; - } -} - -static void XMLCALL -asx_end_element(void *user_data, const XML_Char *element_name) -{ - AsxParser *parser = (AsxParser *)user_data; - - switch (parser->state) { - case AsxParser::ROOT: - break; - - case AsxParser::ENTRY: - if (StringEqualsCaseASCII(element_name, "entry")) { - if (!parser->location.empty()) - parser->songs.emplace_front(std::move(parser->location), - parser->tag_builder.Commit()); - - parser->state = AsxParser::ROOT; - } else - parser->tag_type = TAG_NUM_OF_ITEM_TYPES; - - break; - } -} - -static void XMLCALL -asx_char_data(void *user_data, const XML_Char *s, int len) -{ - AsxParser *parser = (AsxParser *)user_data; - - switch (parser->state) { - case AsxParser::ROOT: - break; - - case AsxParser::ENTRY: - if (parser->tag_type != TAG_NUM_OF_ITEM_TYPES) - parser->tag_builder.AddItem(parser->tag_type, s, len); - - break; - } -} - -/* - * The playlist object - * - */ - -static SongEnumerator * -asx_open_stream(InputStream &is) -{ - AsxParser parser; - - { - ExpatParser expat(&parser); - expat.SetElementHandler(asx_start_element, asx_end_element); - expat.SetCharacterDataHandler(asx_char_data); - - Error error; - if (!expat.Parse(is, error)) { - LogError(error); - return nullptr; - } - } - - parser.songs.reverse(); - return new MemorySongEnumerator(std::move(parser.songs)); -} - -static const char *const asx_suffixes[] = { - "asx", - nullptr -}; - -static const char *const asx_mime_types[] = { - "video/x-ms-asf", - nullptr -}; - -const struct playlist_plugin asx_playlist_plugin = { - "asx", - - nullptr, - nullptr, - nullptr, - asx_open_stream, - - nullptr, - asx_suffixes, - asx_mime_types, -}; diff --git a/src/playlist/AsxPlaylistPlugin.hxx b/src/playlist/AsxPlaylistPlugin.hxx deleted file mode 100644 index 63371be0f..000000000 --- a/src/playlist/AsxPlaylistPlugin.hxx +++ /dev/null @@ -1,25 +0,0 @@ -/* - * 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_ASX_PLAYLIST_PLUGIN_HXX -#define MPD_ASX_PLAYLIST_PLUGIN_HXX - -extern const struct playlist_plugin asx_playlist_plugin; - -#endif diff --git a/src/playlist/CuePlaylistPlugin.cxx b/src/playlist/CuePlaylistPlugin.cxx deleted file mode 100644 index 505c0c5d8..000000000 --- a/src/playlist/CuePlaylistPlugin.cxx +++ /dev/null @@ -1,88 +0,0 @@ -/* - * 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 "CuePlaylistPlugin.hxx" -#include "PlaylistPlugin.hxx" -#include "SongEnumerator.hxx" -#include "cue/CueParser.hxx" -#include "TextInputStream.hxx" - -#include - -class CuePlaylist final : public SongEnumerator { - InputStream &is; - TextInputStream tis; - CueParser parser; - - public: - CuePlaylist(InputStream &_is) - :is(_is), tis(is) { - } - - virtual DetachedSong *NextSong() override; -}; - -static SongEnumerator * -cue_playlist_open_stream(InputStream &is) -{ - return new CuePlaylist(is); -} - -DetachedSong * -CuePlaylist::NextSong() -{ - DetachedSong *song = parser.Get(); - if (song != nullptr) - return song; - - std::string line; - while (tis.ReadLine(line)) { - parser.Feed(line.c_str()); - song = parser.Get(); - if (song != nullptr) - return song; - } - - parser.Finish(); - return parser.Get(); -} - -static const char *const cue_playlist_suffixes[] = { - "cue", - nullptr -}; - -static const char *const cue_playlist_mime_types[] = { - "application/x-cue", - nullptr -}; - -const struct playlist_plugin cue_playlist_plugin = { - "cue", - - nullptr, - nullptr, - nullptr, - cue_playlist_open_stream, - - nullptr, - cue_playlist_suffixes, - cue_playlist_mime_types, -}; diff --git a/src/playlist/CuePlaylistPlugin.hxx b/src/playlist/CuePlaylistPlugin.hxx deleted file mode 100644 index 4d833bfc2..000000000 --- a/src/playlist/CuePlaylistPlugin.hxx +++ /dev/null @@ -1,25 +0,0 @@ -/* - * 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_CUE_PLAYLIST_PLUGIN_HXX -#define MPD_CUE_PLAYLIST_PLUGIN_HXX - -extern const struct playlist_plugin cue_playlist_plugin; - -#endif diff --git a/src/playlist/DespotifyPlaylistPlugin.cxx b/src/playlist/DespotifyPlaylistPlugin.cxx deleted file mode 100644 index 7d73a64bc..000000000 --- a/src/playlist/DespotifyPlaylistPlugin.cxx +++ /dev/null @@ -1,141 +0,0 @@ -/* - * 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 "DespotifyPlaylistPlugin.hxx" -#include "DespotifyUtils.hxx" -#include "PlaylistPlugin.hxx" -#include "MemorySongEnumerator.hxx" -#include "tag/Tag.hxx" -#include "DetachedSong.hxx" -#include "Log.hxx" - -extern "C" { -#include -} - -#include -#include - -static void -add_song(std::forward_list &songs, ds_track &track) -{ - const char *dsp_scheme = despotify_playlist_plugin.schemes[0]; - char uri[128]; - char *ds_uri; - - /* Create a spt://... URI for MPD */ - snprintf(uri, sizeof(uri), "%s://", dsp_scheme); - ds_uri = uri + strlen(dsp_scheme) + 3; - - if (despotify_track_to_uri(&track, ds_uri) != ds_uri) { - /* Should never really fail, but let's be sure */ - FormatDebug(despotify_domain, - "Can't add track %s", track.title); - return; - } - - songs.emplace_front(uri, mpd_despotify_tag_from_track(track)); -} - -static bool -parse_track(struct despotify_session *session, - std::forward_list &songs, - struct ds_link *link) -{ - struct ds_track *track = despotify_link_get_track(session, link); - if (track == nullptr) - return false; - - add_song(songs, *track); - return true; -} - -static bool -parse_playlist(struct despotify_session *session, - std::forward_list &songs, - struct ds_link *link) -{ - ds_playlist *playlist = despotify_link_get_playlist(session, link); - if (playlist == nullptr) - return false; - - for (ds_track *track = playlist->tracks; track != nullptr; - track = track->next) - add_song(songs, *track); - - return true; -} - -static SongEnumerator * -despotify_playlist_open_uri(const char *url, - gcc_unused Mutex &mutex, gcc_unused Cond &cond) -{ - despotify_session *session = mpd_despotify_get_session(); - if (session == nullptr) - return nullptr; - - /* Get link without spt:// */ - ds_link *link = - despotify_link_from_uri(url + strlen(despotify_playlist_plugin.schemes[0]) + 3); - if (link == nullptr) { - FormatDebug(despotify_domain, "Can't find %s\n", url); - return nullptr; - } - - std::forward_list songs; - - bool parse_result; - switch (link->type) { - case LINK_TYPE_TRACK: - parse_result = parse_track(session, songs, link); - break; - case LINK_TYPE_PLAYLIST: - parse_result = parse_playlist(session, songs, link); - break; - default: - parse_result = false; - break; - } - - despotify_free_link(link); - if (!parse_result) - return nullptr; - - songs.reverse(); - return new MemorySongEnumerator(std::move(songs)); -} - -static const char *const despotify_schemes[] = { - "spt", - nullptr -}; - -const struct playlist_plugin despotify_playlist_plugin = { - "despotify", - - nullptr, - nullptr, - despotify_playlist_open_uri, - nullptr, - - despotify_schemes, - nullptr, - nullptr, -}; diff --git a/src/playlist/DespotifyPlaylistPlugin.hxx b/src/playlist/DespotifyPlaylistPlugin.hxx deleted file mode 100644 index 6acfd40f4..000000000 --- a/src/playlist/DespotifyPlaylistPlugin.hxx +++ /dev/null @@ -1,25 +0,0 @@ -/* - * 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_DESPOTIFY_PLAYLIST_PLUGIN_HXX -#define MPD_PLAYLIST_DESPOTIFY_PLAYLIST_PLUGIN_HXX - -extern const struct playlist_plugin despotify_playlist_plugin; - -#endif diff --git a/src/playlist/EmbeddedCuePlaylistPlugin.cxx b/src/playlist/EmbeddedCuePlaylistPlugin.cxx deleted file mode 100644 index 4d9eb4b9b..000000000 --- a/src/playlist/EmbeddedCuePlaylistPlugin.cxx +++ /dev/null @@ -1,184 +0,0 @@ -/* - * 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 - * - * Playlist plugin that reads embedded cue sheets from the "CUESHEET" - * tag of a music file. - */ - -#include "config.h" -#include "EmbeddedCuePlaylistPlugin.hxx" -#include "PlaylistPlugin.hxx" -#include "SongEnumerator.hxx" -#include "tag/TagHandler.hxx" -#include "tag/TagId3.hxx" -#include "tag/ApeTag.hxx" -#include "DetachedSong.hxx" -#include "TagFile.hxx" -#include "cue/CueParser.hxx" -#include "fs/Traits.hxx" -#include "fs/AllocatedPath.hxx" -#include "util/ASCII.hxx" - -#include - -class EmbeddedCuePlaylist final : public SongEnumerator { -public: - /** - * This is an override for the CUE's "FILE". An embedded CUE - * sheet must always point to the song file it is contained - * in. - */ - std::string filename; - - /** - * The value of the file's "CUESHEET" tag. - */ - std::string cuesheet; - - /** - * The offset of the next line within "cuesheet". - */ - char *next; - - CueParser *parser; - -public: - EmbeddedCuePlaylist() - :parser(nullptr) { - } - - virtual ~EmbeddedCuePlaylist() { - delete parser; - } - - virtual DetachedSong *NextSong() override; -}; - -static void -embcue_tag_pair(const char *name, const char *value, void *ctx) -{ - EmbeddedCuePlaylist *playlist = (EmbeddedCuePlaylist *)ctx; - - if (playlist->cuesheet.empty() && - StringEqualsCaseASCII(name, "cuesheet")) - playlist->cuesheet = value; -} - -static const struct tag_handler embcue_tag_handler = { - nullptr, - nullptr, - embcue_tag_pair, -}; - -static SongEnumerator * -embcue_playlist_open_uri(const char *uri, - gcc_unused Mutex &mutex, - gcc_unused Cond &cond) -{ - if (!PathTraitsUTF8::IsAbsolute(uri)) - /* only local files supported */ - return nullptr; - - const auto path_fs = AllocatedPath::FromUTF8(uri); - if (path_fs.IsNull()) - return nullptr; - - const auto playlist = new EmbeddedCuePlaylist(); - - tag_file_scan(path_fs, embcue_tag_handler, playlist); - if (playlist->cuesheet.empty()) { - tag_ape_scan2(path_fs, &embcue_tag_handler, playlist); - if (playlist->cuesheet.empty()) - tag_id3_scan(path_fs, &embcue_tag_handler, playlist); - } - - if (playlist->cuesheet.empty()) { - /* no "CUESHEET" tag found */ - delete playlist; - return nullptr; - } - - playlist->filename = PathTraitsUTF8::GetBase(uri); - - playlist->next = &playlist->cuesheet[0]; - playlist->parser = new CueParser(); - - return playlist; -} - -DetachedSong * -EmbeddedCuePlaylist::NextSong() -{ - DetachedSong *song = parser->Get(); - if (song != nullptr) - return song; - - while (*next != 0) { - const char *line = next; - char *eol = strpbrk(next, "\r\n"); - if (eol != nullptr) { - /* null-terminate the line */ - *eol = 0; - next = eol + 1; - } else - /* last line; put the "next" pointer to the - end of the buffer */ - next += strlen(line); - - parser->Feed(line); - song = parser->Get(); - if (song != nullptr) { - song->SetURI(filename); - return song; - } - } - - parser->Finish(); - song = parser->Get(); - if (song != nullptr) - song->SetURI(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", - nullptr -}; - -const struct playlist_plugin embcue_playlist_plugin = { - "cue", - - nullptr, - nullptr, - embcue_playlist_open_uri, - nullptr, - - embcue_playlist_suffixes, - nullptr, - nullptr, -}; diff --git a/src/playlist/EmbeddedCuePlaylistPlugin.hxx b/src/playlist/EmbeddedCuePlaylistPlugin.hxx deleted file mode 100644 index 5eedf3f13..000000000 --- a/src/playlist/EmbeddedCuePlaylistPlugin.hxx +++ /dev/null @@ -1,25 +0,0 @@ -/* - * 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_EMBCUE_PLAYLIST_PLUGIN_HXX -#define MPD_EMBCUE_PLAYLIST_PLUGIN_HXX - -extern const struct playlist_plugin embcue_playlist_plugin; - -#endif diff --git a/src/playlist/ExtM3uPlaylistPlugin.cxx b/src/playlist/ExtM3uPlaylistPlugin.cxx deleted file mode 100644 index 1a975c081..000000000 --- a/src/playlist/ExtM3uPlaylistPlugin.cxx +++ /dev/null @@ -1,149 +0,0 @@ -/* - * 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 "ExtM3uPlaylistPlugin.hxx" -#include "PlaylistPlugin.hxx" -#include "SongEnumerator.hxx" -#include "DetachedSong.hxx" -#include "tag/Tag.hxx" -#include "tag/TagBuilder.hxx" -#include "util/StringUtil.hxx" -#include "TextInputStream.hxx" - -#include -#include - -class ExtM3uPlaylist final : public SongEnumerator { - TextInputStream tis; - -public: - ExtM3uPlaylist(InputStream &is) - :tis(is) { - } - - bool CheckFirstLine() { - std::string line; - return tis.ReadLine(line) && - strcmp(line.c_str(), "#EXTM3U") == 0; - } - - virtual DetachedSong *NextSong() override; -}; - -static SongEnumerator * -extm3u_open_stream(InputStream &is) -{ - ExtM3uPlaylist *playlist = new ExtM3uPlaylist(is); - - if (!playlist->CheckFirstLine()) { - /* no EXTM3U header: fall back to the plain m3u - plugin */ - delete playlist; - return NULL; - } - - return playlist; -} - -/** - * Parse a EXTINF line. - * - * @param line the rest of the input line after the colon - */ -static Tag -extm3u_parse_tag(const char *line) -{ - long duration; - char *endptr; - const char *name; - - duration = strtol(line, &endptr, 10); - if (endptr[0] != ',') - /* malformed line */ - return Tag(); - - if (duration < 0) - /* 0 means unknown duration */ - duration = 0; - - name = strchug_fast(endptr + 1); - if (*name == 0 && duration == 0) - /* no information available; don't allocate a tag - object */ - return Tag(); - - TagBuilder tag; - tag.SetTime(duration); - - /* unfortunately, there is no real specification for the - EXTM3U format, so we must assume that the string after the - comma is opaque, and is just the song name*/ - if (*name != 0) - tag.AddItem(TAG_NAME, name); - - return tag.Commit(); -} - -DetachedSong * -ExtM3uPlaylist::NextSong() -{ - Tag tag; - std::string line; - const char *line_s; - - do { - if (!tis.ReadLine(line)) - return NULL; - - line_s = line.c_str(); - - if (StringStartsWith(line_s, "#EXTINF:")) { - tag = extm3u_parse_tag(line_s + 8); - continue; - } - - line_s = strchug_fast(line_s); - } while (line_s[0] == '#' || *line_s == 0); - - return new DetachedSong(line_s, std::move(tag)); -} - -static const char *const extm3u_suffixes[] = { - "m3u", - NULL -}; - -static const char *const extm3u_mime_types[] = { - "audio/x-mpegurl", - NULL -}; - -const struct playlist_plugin extm3u_playlist_plugin = { - "extm3u", - - nullptr, - nullptr, - nullptr, - extm3u_open_stream, - - nullptr, - extm3u_suffixes, - extm3u_mime_types, -}; diff --git a/src/playlist/ExtM3uPlaylistPlugin.hxx b/src/playlist/ExtM3uPlaylistPlugin.hxx deleted file mode 100644 index 5743ded43..000000000 --- a/src/playlist/ExtM3uPlaylistPlugin.hxx +++ /dev/null @@ -1,25 +0,0 @@ -/* - * 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_EXTM3U_PLAYLIST_PLUGIN_HXX -#define MPD_EXTM3U_PLAYLIST_PLUGIN_HXX - -extern const struct playlist_plugin extm3u_playlist_plugin; - -#endif diff --git a/src/playlist/M3uPlaylistPlugin.cxx b/src/playlist/M3uPlaylistPlugin.cxx deleted file mode 100644 index fe7d8a17f..000000000 --- a/src/playlist/M3uPlaylistPlugin.cxx +++ /dev/null @@ -1,83 +0,0 @@ -/* - * 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 "M3uPlaylistPlugin.hxx" -#include "PlaylistPlugin.hxx" -#include "SongEnumerator.hxx" -#include "DetachedSong.hxx" -#include "util/StringUtil.hxx" -#include "TextInputStream.hxx" - -class M3uPlaylist final : public SongEnumerator { - TextInputStream tis; - -public: - M3uPlaylist(InputStream &is) - :tis(is) { - } - - virtual DetachedSong *NextSong() override; -}; - -static SongEnumerator * -m3u_open_stream(InputStream &is) -{ - return new M3uPlaylist(is); -} - -DetachedSong * -M3uPlaylist::NextSong() -{ - std::string line; - const char *line_s; - - do { - if (!tis.ReadLine(line)) - return nullptr; - - line_s = line.c_str(); - line_s = strchug_fast(line_s); - } while (line_s[0] == '#' || *line_s == 0); - - return new DetachedSong(line_s); -} - -static const char *const m3u_suffixes[] = { - "m3u", - nullptr -}; - -static const char *const m3u_mime_types[] = { - "audio/x-mpegurl", - nullptr -}; - -const struct playlist_plugin m3u_playlist_plugin = { - "m3u", - - nullptr, - nullptr, - nullptr, - m3u_open_stream, - - nullptr, - m3u_suffixes, - m3u_mime_types, -}; diff --git a/src/playlist/M3uPlaylistPlugin.hxx b/src/playlist/M3uPlaylistPlugin.hxx deleted file mode 100644 index f1ad14069..000000000 --- a/src/playlist/M3uPlaylistPlugin.hxx +++ /dev/null @@ -1,25 +0,0 @@ -/* - * 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_M3U_PLAYLIST_PLUGIN_HXX -#define MPD_M3U_PLAYLIST_PLUGIN_HXX - -extern const struct playlist_plugin m3u_playlist_plugin; - -#endif 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 + +class MemorySongEnumerator final : public SongEnumerator { + std::forward_list songs; + +public: + MemorySongEnumerator(std::forward_list &&_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 + +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 + +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 ¶m); + + /** + * 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 ¶m) +{ + 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 +#include + +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 = ∅ + 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 +#include + +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/PlsPlaylistPlugin.cxx b/src/playlist/PlsPlaylistPlugin.cxx deleted file mode 100644 index 839098a73..000000000 --- a/src/playlist/PlsPlaylistPlugin.cxx +++ /dev/null @@ -1,163 +0,0 @@ -/* - * 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 "PlsPlaylistPlugin.hxx" -#include "PlaylistPlugin.hxx" -#include "MemorySongEnumerator.hxx" -#include "InputStream.hxx" -#include "DetachedSong.hxx" -#include "tag/TagBuilder.hxx" -#include "util/Error.hxx" -#include "util/Domain.hxx" -#include "Log.hxx" - -#include - -#include - -#include - -static constexpr Domain pls_domain("pls"); - -static void -pls_parser(GKeyFile *keyfile, std::forward_list &songs) -{ - gchar *value; - GError *error = nullptr; - int num_entries = g_key_file_get_integer(keyfile, "playlist", - "NumberOfEntries", &error); - if (error) { - FormatError(pls_domain, - "Invalid PLS file: '%s'", error->message); - g_error_free(error); - error = nullptr; - - /* Hack to work around shoutcast failure to comform to spec */ - num_entries = g_key_file_get_integer(keyfile, "playlist", - "numberofentries", &error); - if (error) { - g_error_free(error); - error = nullptr; - } - } - - for (; num_entries > 0; --num_entries) { - char key[64]; - sprintf(key, "File%u", num_entries); - char *uri = g_key_file_get_string(keyfile, "playlist", key, - &error); - if(error) { - FormatError(pls_domain, "Invalid PLS entry %s: '%s'", - key, error->message); - g_error_free(error); - return; - } - - TagBuilder tag; - - sprintf(key, "Title%u", num_entries); - value = g_key_file_get_string(keyfile, "playlist", key, - nullptr); - if (value != nullptr) - tag.AddItem(TAG_TITLE, value); - - g_free(value); - - sprintf(key, "Length%u", num_entries); - int length = g_key_file_get_integer(keyfile, "playlist", key, - nullptr); - if (length > 0) - tag.SetTime(length); - - songs.emplace_front(uri, tag.Commit()); - g_free(uri); - } - -} - -static SongEnumerator * -pls_open_stream(InputStream &is) -{ - GError *error = nullptr; - Error error2; - - std::string kf_data; - - do { - char buffer[1024]; - size_t nbytes = is.LockRead(buffer, sizeof(buffer), error2); - if (nbytes == 0) { - if (error2.IsDefined()) { - LogError(error2); - return nullptr; - } - - break; - } - - kf_data.append(buffer, nbytes); - /* Limit to 64k */ - } while (kf_data.length() < 65536); - - if (kf_data.empty()) { - LogWarning(pls_domain, "KeyFile parser failed: No Data"); - return nullptr; - } - - GKeyFile *keyfile = g_key_file_new(); - if (!g_key_file_load_from_data(keyfile, - kf_data.data(), kf_data.length(), - G_KEY_FILE_NONE, &error)) { - FormatError(pls_domain, - "KeyFile parser failed: %s", error->message); - g_error_free(error); - g_key_file_free(keyfile); - return nullptr; - } - - std::forward_list songs; - pls_parser(keyfile, songs); - g_key_file_free(keyfile); - - return new MemorySongEnumerator(std::move(songs)); -} - -static const char *const pls_suffixes[] = { - "pls", - nullptr -}; - -static const char *const pls_mime_types[] = { - "audio/x-scpls", - nullptr -}; - -const struct playlist_plugin pls_playlist_plugin = { - "pls", - - nullptr, - nullptr, - nullptr, - pls_open_stream, - - nullptr, - pls_suffixes, - pls_mime_types, -}; diff --git a/src/playlist/PlsPlaylistPlugin.hxx b/src/playlist/PlsPlaylistPlugin.hxx deleted file mode 100644 index 1a3f33873..000000000 --- a/src/playlist/PlsPlaylistPlugin.hxx +++ /dev/null @@ -1,25 +0,0 @@ -/* - * 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_PLS_PLAYLIST_PLUGIN_HXX -#define MPD_PLS_PLAYLIST_PLUGIN_HXX - -extern const struct playlist_plugin pls_playlist_plugin; - -#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/RssPlaylistPlugin.cxx b/src/playlist/RssPlaylistPlugin.cxx deleted file mode 100644 index 550a4630e..000000000 --- a/src/playlist/RssPlaylistPlugin.cxx +++ /dev/null @@ -1,185 +0,0 @@ -/* - * 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 "RssPlaylistPlugin.hxx" -#include "PlaylistPlugin.hxx" -#include "MemorySongEnumerator.hxx" -#include "tag/TagBuilder.hxx" -#include "util/ASCII.hxx" -#include "util/Error.hxx" -#include "Expat.hxx" -#include "Log.hxx" - -/** - * This is the state object for the GLib XML parser. - */ -struct RssParser { - /** - * The list of songs (in reverse order because that's faster - * while adding). - */ - std::forward_list songs; - - /** - * The current position in the XML file. - */ - enum { - ROOT, ITEM, - } state; - - /** - * The current tag within the "entry" element. This is only - * valid if state==ITEM. TAG_NUM_OF_ITEM_TYPES means there - * is no (known) tag. - */ - TagType tag_type; - - /** - * The current song URI. It is set by the "enclosure" - * element. - */ - std::string location; - - TagBuilder tag_builder; - - RssParser() - :state(ROOT) {} -}; - -static void XMLCALL -rss_start_element(void *user_data, const XML_Char *element_name, - const XML_Char **atts) -{ - RssParser *parser = (RssParser *)user_data; - - switch (parser->state) { - case RssParser::ROOT: - if (StringEqualsCaseASCII(element_name, "item")) { - parser->state = RssParser::ITEM; - parser->location.clear(); - parser->tag_type = TAG_NUM_OF_ITEM_TYPES; - } - - break; - - case RssParser::ITEM: - if (StringEqualsCaseASCII(element_name, "enclosure")) { - const char *href = - ExpatParser::GetAttributeCase(atts, "url"); - if (href != nullptr) - parser->location = href; - } else if (StringEqualsCaseASCII(element_name, "title")) - parser->tag_type = TAG_TITLE; - else if (StringEqualsCaseASCII(element_name, "itunes:author")) - parser->tag_type = TAG_ARTIST; - - break; - } -} - -static void XMLCALL -rss_end_element(void *user_data, const XML_Char *element_name) -{ - RssParser *parser = (RssParser *)user_data; - - switch (parser->state) { - case RssParser::ROOT: - break; - - case RssParser::ITEM: - if (StringEqualsCaseASCII(element_name, "item")) { - if (!parser->location.empty()) - parser->songs.emplace_front(std::move(parser->location), - parser->tag_builder.Commit()); - - parser->state = RssParser::ROOT; - } else - parser->tag_type = TAG_NUM_OF_ITEM_TYPES; - - break; - } -} - -static void XMLCALL -rss_char_data(void *user_data, const XML_Char *s, int len) -{ - RssParser *parser = (RssParser *)user_data; - - switch (parser->state) { - case RssParser::ROOT: - break; - - case RssParser::ITEM: - if (parser->tag_type != TAG_NUM_OF_ITEM_TYPES) - parser->tag_builder.AddItem(parser->tag_type, s, len); - - break; - } -} - -/* - * The playlist object - * - */ - -static SongEnumerator * -rss_open_stream(InputStream &is) -{ - RssParser parser; - - { - ExpatParser expat(&parser); - expat.SetElementHandler(rss_start_element, rss_end_element); - expat.SetCharacterDataHandler(rss_char_data); - - Error error; - if (!expat.Parse(is, error)) { - LogError(error); - return nullptr; - } - } - - parser.songs.reverse(); - return new MemorySongEnumerator(std::move(parser.songs)); -} - -static const char *const rss_suffixes[] = { - "rss", - nullptr -}; - -static const char *const rss_mime_types[] = { - "application/rss+xml", - "text/xml", - nullptr -}; - -const struct playlist_plugin rss_playlist_plugin = { - "rss", - - nullptr, - nullptr, - nullptr, - rss_open_stream, - - nullptr, - rss_suffixes, - rss_mime_types, -}; diff --git a/src/playlist/RssPlaylistPlugin.hxx b/src/playlist/RssPlaylistPlugin.hxx deleted file mode 100644 index a00a5a898..000000000 --- a/src/playlist/RssPlaylistPlugin.hxx +++ /dev/null @@ -1,25 +0,0 @@ -/* - * 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_RSS_PLAYLIST_PLUGIN_HXX -#define MPD_RSS_PLAYLIST_PLUGIN_HXX - -extern const struct playlist_plugin rss_playlist_plugin; - -#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/SoundCloudPlaylistPlugin.cxx b/src/playlist/SoundCloudPlaylistPlugin.cxx deleted file mode 100644 index bf68acd3b..000000000 --- a/src/playlist/SoundCloudPlaylistPlugin.cxx +++ /dev/null @@ -1,401 +0,0 @@ -/* - * 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 "SoundCloudPlaylistPlugin.hxx" -#include "PlaylistPlugin.hxx" -#include "MemorySongEnumerator.hxx" -#include "ConfigData.hxx" -#include "InputStream.hxx" -#include "tag/TagBuilder.hxx" -#include "util/StringUtil.hxx" -#include "util/Error.hxx" -#include "util/Domain.hxx" -#include "Log.hxx" - -#include -#include - -#include - -#include - -static struct { - std::string apikey; -} soundcloud_config; - -static constexpr Domain soundcloud_domain("soundcloud"); - -static bool -soundcloud_init(const config_param ¶m) -{ - // APIKEY for MPD application, registered under DarkFox' account. - soundcloud_config.apikey = param.GetBlockValue("apikey", "a25e51780f7f86af0afa91f241d091f8"); - if (soundcloud_config.apikey.empty()) { - LogDebug(soundcloud_domain, - "disabling the soundcloud playlist plugin " - "because API key is not set"); - return false; - } - - return true; -} - -/** - * 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 (StringStartsWith(uri, "https://")) { - u = g_strdup(uri); - } else if (StringStartsWith(uri, "soundcloud.com")) { - u = g_strconcat("https://", uri, nullptr); - } else { - /* assume it's just a path on soundcloud.com */ - u = g_strconcat("https://soundcloud.com/", uri, nullptr); - } - - ru = g_strconcat("https://api.soundcloud.com/resolve.json?url=", - u, "&client_id=", - soundcloud_config.apikey.c_str(), nullptr); - 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", - nullptr, -}; - -struct parse_data { - int key; - char* stream_url; - long duration; - char* title; - int got_url; /* nesting level of last stream_url */ - - std::forward_list 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: - g_free(data->title); - data->title = g_strndup(s, stringlen); - break; - case Stream_URL: - 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 (memcmp((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; - - char *u = g_strconcat(data->stream_url, "?client_id=", - soundcloud_config.apikey.c_str(), nullptr); - - TagBuilder tag; - tag.SetTime(data->duration / 1000); - if (data->title != nullptr) - tag.AddItem(TAG_NAME, data->title); - - data->songs.emplace_front(u, tag.Commit()); - g_free(u); - - return 1; -} - -static yajl_callbacks parse_callbacks = { - nullptr, - nullptr, - handle_integer, - nullptr, - nullptr, - handle_string, - handle_start_map, - handle_mapkey, - handle_end_map, - nullptr, - nullptr, -}; - -/** - * Read JSON data and parse it using the given YAJL parser. - * @param url URL of the JSON data. - * @param hand YAJL parser handle. - * @return -1 on error, 0 on success. - */ -static int -soundcloud_parse_json(const char *url, yajl_handle hand, - Mutex &mutex, Cond &cond) -{ - Error error; - InputStream *input_stream = InputStream::OpenReady(url, mutex, cond, - error); - if (input_stream == nullptr) { - if (error.IsDefined()) - LogError(error); - return -1; - } - - mutex.lock(); - - yajl_status stat; - int done = 0; - - while (!done) { - char buffer[4096]; - unsigned char *ubuffer = (unsigned char *)buffer; - const size_t nbytes = - input_stream->Read(buffer, sizeof(buffer), error); - if (nbytes == 0) { - if (error.IsDefined()) - LogError(error); - - if (input_stream->IsEOF()) { - done = true; - } else { - mutex.unlock(); - input_stream->Close(); - return -1; - } - } - - if (done) { -#ifdef HAVE_YAJL1 - stat = yajl_parse_complete(hand); -#else - stat = yajl_complete_parse(hand); -#endif - } else - stat = yajl_parse(hand, ubuffer, nbytes); - - if (stat != yajl_status_ok -#ifdef HAVE_YAJL1 - && stat != yajl_status_insufficient_data -#endif - ) - { - unsigned char *str = yajl_get_error(hand, 1, ubuffer, nbytes); - LogError(soundcloud_domain, (const char *)str); - yajl_free_error(hand, str); - break; - } - } - - mutex.unlock(); - input_stream->Close(); - - return 0; -} - -/** - * Parse a soundcloud:// URL and create a playlist. - * @param uri A soundcloud URL. Accepted forms: - * soundcloud://track/ - * soundcloud://playlist/ - * soundcloud://url/ - */ -static SongEnumerator * -soundcloud_open_uri(const char *uri, Mutex &mutex, Cond &cond) -{ - assert(memcmp(uri, "soundcloud://", 13) == 0); - uri += 13; - - char *u = nullptr; - if (memcmp(uri, "track/", 6) == 0) { - const char *rest = uri + 6; - u = g_strconcat("https://api.soundcloud.com/tracks/", - rest, ".json?client_id=", - soundcloud_config.apikey.c_str(), nullptr); - } else if (memcmp(uri, "playlist/", 9) == 0) { - const char *rest = uri + 9; - u = g_strconcat("https://api.soundcloud.com/playlists/", - rest, ".json?client_id=", - soundcloud_config.apikey.c_str(), nullptr); - } else if (memcmp(uri, "user/", 5) == 0) { - const char *rest = uri + 5; - u = g_strconcat("https://api.soundcloud.com/users/", - rest, "/tracks.json?client_id=", - soundcloud_config.apikey.c_str(), nullptr); - } else if (memcmp(uri, "search/", 7) == 0) { - const char *rest = uri + 7; - u = g_strconcat("https://api.soundcloud.com/tracks.json?q=", - rest, "&client_id=", - soundcloud_config.apikey.c_str(), nullptr); - } else if (memcmp(uri, "url/", 4) == 0) { - const char *rest = uri + 4; - /* Translate to soundcloud resolver call. libcurl will automatically - follow the redirect to the right resource. */ - u = soundcloud_resolve(rest); - } - - if (u == nullptr) { - LogWarning(soundcloud_domain, "unknown soundcloud URI"); - return nullptr; - } - - struct parse_data data; - data.got_url = 0; - data.title = nullptr; - data.stream_url = nullptr; -#ifdef HAVE_YAJL1 - yajl_handle hand = yajl_alloc(&parse_callbacks, nullptr, nullptr, - &data); -#else - yajl_handle hand = yajl_alloc(&parse_callbacks, nullptr, &data); -#endif - - int ret = soundcloud_parse_json(u, hand, mutex, cond); - - g_free(u); - yajl_free(hand); - g_free(data.title); - g_free(data.stream_url); - - if (ret == -1) - return nullptr; - - data.songs.reverse(); - return new MemorySongEnumerator(std::move(data.songs)); -} - -static const char *const soundcloud_schemes[] = { - "soundcloud", - nullptr -}; - -const struct playlist_plugin soundcloud_playlist_plugin = { - "soundcloud", - - soundcloud_init, - nullptr, - soundcloud_open_uri, - nullptr, - - soundcloud_schemes, - nullptr, - nullptr, -}; - - diff --git a/src/playlist/SoundCloudPlaylistPlugin.hxx b/src/playlist/SoundCloudPlaylistPlugin.hxx deleted file mode 100644 index b355b477a..000000000 --- a/src/playlist/SoundCloudPlaylistPlugin.hxx +++ /dev/null @@ -1,25 +0,0 @@ -/* - * 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_SOUNDCLOUD_PLAYLIST_PLUGIN_HXX -#define MPD_SOUNDCLOUD_PLAYLIST_PLUGIN_HXX - -extern const struct playlist_plugin soundcloud_playlist_plugin; - -#endif diff --git a/src/playlist/XspfPlaylistPlugin.cxx b/src/playlist/XspfPlaylistPlugin.cxx deleted file mode 100644 index e726ad338..000000000 --- a/src/playlist/XspfPlaylistPlugin.cxx +++ /dev/null @@ -1,234 +0,0 @@ -/* - * 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 "XspfPlaylistPlugin.hxx" -#include "PlaylistPlugin.hxx" -#include "MemorySongEnumerator.hxx" -#include "DetachedSong.hxx" -#include "InputStream.hxx" -#include "tag/TagBuilder.hxx" -#include "util/Error.hxx" -#include "util/Domain.hxx" -#include "Expat.hxx" -#include "Log.hxx" - -#include - -static constexpr Domain xspf_domain("xspf"); - -/** - * This is the state object for the GLib XML parser. - */ -struct XspfParser { - /** - * The list of songs (in reverse order because that's faster - * while adding). - */ - std::forward_list songs; - - /** - * The current position in the XML file. - */ - enum { - ROOT, PLAYLIST, TRACKLIST, TRACK, - LOCATION, - } state; - - /** - * The current tag within the "track" element. This is only - * valid if state==TRACK. TAG_NUM_OF_ITEM_TYPES means there - * is no (known) tag. - */ - TagType tag_type; - - /** - * The current song URI. It is set by the "location" element. - */ - std::string location; - - TagBuilder tag_builder; - - XspfParser() - :state(ROOT) {} -}; - -static void XMLCALL -xspf_start_element(void *user_data, const XML_Char *element_name, - gcc_unused const XML_Char **atts) -{ - XspfParser *parser = (XspfParser *)user_data; - - switch (parser->state) { - case XspfParser::ROOT: - if (strcmp(element_name, "playlist") == 0) - parser->state = XspfParser::PLAYLIST; - - break; - - case XspfParser::PLAYLIST: - if (strcmp(element_name, "trackList") == 0) - parser->state = XspfParser::TRACKLIST; - - break; - - case XspfParser::TRACKLIST: - if (strcmp(element_name, "track") == 0) { - parser->state = XspfParser::TRACK; - parser->location.clear(); - parser->tag_type = TAG_NUM_OF_ITEM_TYPES; - } - - break; - - case XspfParser::TRACK: - if (strcmp(element_name, "location") == 0) - parser->state = XspfParser::LOCATION; - else if (strcmp(element_name, "title") == 0) - parser->tag_type = TAG_TITLE; - else if (strcmp(element_name, "creator") == 0) - /* TAG_COMPOSER would be more correct - according to the XSPF spec */ - parser->tag_type = TAG_ARTIST; - else if (strcmp(element_name, "annotation") == 0) - parser->tag_type = TAG_COMMENT; - else if (strcmp(element_name, "album") == 0) - parser->tag_type = TAG_ALBUM; - else if (strcmp(element_name, "trackNum") == 0) - parser->tag_type = TAG_TRACK; - - break; - - case XspfParser::LOCATION: - break; - } -} - -static void XMLCALL -xspf_end_element(void *user_data, const XML_Char *element_name) -{ - XspfParser *parser = (XspfParser *)user_data; - - switch (parser->state) { - case XspfParser::ROOT: - break; - - case XspfParser::PLAYLIST: - if (strcmp(element_name, "playlist") == 0) - parser->state = XspfParser::ROOT; - - break; - - case XspfParser::TRACKLIST: - if (strcmp(element_name, "tracklist") == 0) - parser->state = XspfParser::PLAYLIST; - - break; - - case XspfParser::TRACK: - if (strcmp(element_name, "track") == 0) { - if (!parser->location.empty()) - parser->songs.emplace_front(std::move(parser->location), - parser->tag_builder.Commit()); - - parser->state = XspfParser::TRACKLIST; - } else - parser->tag_type = TAG_NUM_OF_ITEM_TYPES; - - break; - - case XspfParser::LOCATION: - parser->state = XspfParser::TRACK; - break; - } -} - -static void XMLCALL -xspf_char_data(void *user_data, const XML_Char *s, int len) -{ - XspfParser *parser = (XspfParser *)user_data; - - switch (parser->state) { - case XspfParser::ROOT: - case XspfParser::PLAYLIST: - case XspfParser::TRACKLIST: - break; - - case XspfParser::TRACK: - if (!parser->location.empty() && - parser->tag_type != TAG_NUM_OF_ITEM_TYPES) - parser->tag_builder.AddItem(parser->tag_type, s, len); - - break; - - case XspfParser::LOCATION: - parser->location.assign(s, len); - - break; - } -} - -/* - * The playlist object - * - */ - -static SongEnumerator * -xspf_open_stream(InputStream &is) -{ - XspfParser parser; - - { - ExpatParser expat(&parser); - expat.SetElementHandler(xspf_start_element, xspf_end_element); - expat.SetCharacterDataHandler(xspf_char_data); - - Error error; - if (!expat.Parse(is, error)) { - LogError(error); - return nullptr; - } - } - - parser.songs.reverse(); - return new MemorySongEnumerator(std::move(parser.songs)); -} - -static const char *const xspf_suffixes[] = { - "xspf", - nullptr -}; - -static const char *const xspf_mime_types[] = { - "application/xspf+xml", - nullptr -}; - -const struct playlist_plugin xspf_playlist_plugin = { - "xspf", - - nullptr, - nullptr, - nullptr, - xspf_open_stream, - - nullptr, - xspf_suffixes, - xspf_mime_types, -}; diff --git a/src/playlist/XspfPlaylistPlugin.hxx b/src/playlist/XspfPlaylistPlugin.hxx deleted file mode 100644 index 6b08a6be6..000000000 --- a/src/playlist/XspfPlaylistPlugin.hxx +++ /dev/null @@ -1,25 +0,0 @@ -/* - * 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_XSPF_PLAYLIST_PLUGIN_HXX -#define MPD_XSPF_PLAYLIST_PLUGIN_HXX - -extern const struct playlist_plugin xspf_playlist_plugin; - -#endif diff --git a/src/playlist/plugins/AsxPlaylistPlugin.cxx b/src/playlist/plugins/AsxPlaylistPlugin.cxx new file mode 100644 index 000000000..5434c8e85 --- /dev/null +++ b/src/playlist/plugins/AsxPlaylistPlugin.cxx @@ -0,0 +1,186 @@ +/* + * 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 "AsxPlaylistPlugin.hxx" +#include "../PlaylistPlugin.hxx" +#include "../MemorySongEnumerator.hxx" +#include "tag/TagBuilder.hxx" +#include "util/ASCII.hxx" +#include "util/Error.hxx" +#include "Expat.hxx" +#include "Log.hxx" + +/** + * This is the state object for the GLib XML parser. + */ +struct AsxParser { + /** + * The list of songs (in reverse order because that's faster + * while adding). + */ + std::forward_list songs; + + /** + * The current position in the XML file. + */ + enum { + ROOT, ENTRY, + } state; + + /** + * The current tag within the "entry" element. This is only + * valid if state==ENTRY. TAG_NUM_OF_ITEM_TYPES means there + * is no (known) tag. + */ + TagType tag_type; + + /** + * The current song URI. It is set by the "ref" element. + */ + std::string location; + + TagBuilder tag_builder; + + AsxParser() + :state(ROOT) {} + +}; + +static void XMLCALL +asx_start_element(void *user_data, const XML_Char *element_name, + const XML_Char **atts) +{ + AsxParser *parser = (AsxParser *)user_data; + + switch (parser->state) { + case AsxParser::ROOT: + if (StringEqualsCaseASCII(element_name, "entry")) { + parser->state = AsxParser::ENTRY; + parser->location.clear(); + parser->tag_type = TAG_NUM_OF_ITEM_TYPES; + } + + break; + + case AsxParser::ENTRY: + if (StringEqualsCaseASCII(element_name, "ref")) { + const char *href = + ExpatParser::GetAttributeCase(atts, "href"); + if (href != nullptr) + parser->location = href; + } else if (StringEqualsCaseASCII(element_name, "author")) + /* is that correct? or should it be COMPOSER + or PERFORMER? */ + parser->tag_type = TAG_ARTIST; + else if (StringEqualsCaseASCII(element_name, "title")) + parser->tag_type = TAG_TITLE; + + break; + } +} + +static void XMLCALL +asx_end_element(void *user_data, const XML_Char *element_name) +{ + AsxParser *parser = (AsxParser *)user_data; + + switch (parser->state) { + case AsxParser::ROOT: + break; + + case AsxParser::ENTRY: + if (StringEqualsCaseASCII(element_name, "entry")) { + if (!parser->location.empty()) + parser->songs.emplace_front(std::move(parser->location), + parser->tag_builder.Commit()); + + parser->state = AsxParser::ROOT; + } else + parser->tag_type = TAG_NUM_OF_ITEM_TYPES; + + break; + } +} + +static void XMLCALL +asx_char_data(void *user_data, const XML_Char *s, int len) +{ + AsxParser *parser = (AsxParser *)user_data; + + switch (parser->state) { + case AsxParser::ROOT: + break; + + case AsxParser::ENTRY: + if (parser->tag_type != TAG_NUM_OF_ITEM_TYPES) + parser->tag_builder.AddItem(parser->tag_type, s, len); + + break; + } +} + +/* + * The playlist object + * + */ + +static SongEnumerator * +asx_open_stream(InputStream &is) +{ + AsxParser parser; + + { + ExpatParser expat(&parser); + expat.SetElementHandler(asx_start_element, asx_end_element); + expat.SetCharacterDataHandler(asx_char_data); + + Error error; + if (!expat.Parse(is, error)) { + LogError(error); + return nullptr; + } + } + + parser.songs.reverse(); + return new MemorySongEnumerator(std::move(parser.songs)); +} + +static const char *const asx_suffixes[] = { + "asx", + nullptr +}; + +static const char *const asx_mime_types[] = { + "video/x-ms-asf", + nullptr +}; + +const struct playlist_plugin asx_playlist_plugin = { + "asx", + + nullptr, + nullptr, + nullptr, + asx_open_stream, + + nullptr, + asx_suffixes, + asx_mime_types, +}; diff --git a/src/playlist/plugins/AsxPlaylistPlugin.hxx b/src/playlist/plugins/AsxPlaylistPlugin.hxx new file mode 100644 index 000000000..63371be0f --- /dev/null +++ b/src/playlist/plugins/AsxPlaylistPlugin.hxx @@ -0,0 +1,25 @@ +/* + * 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_ASX_PLAYLIST_PLUGIN_HXX +#define MPD_ASX_PLAYLIST_PLUGIN_HXX + +extern const struct playlist_plugin asx_playlist_plugin; + +#endif diff --git a/src/playlist/plugins/CuePlaylistPlugin.cxx b/src/playlist/plugins/CuePlaylistPlugin.cxx new file mode 100644 index 000000000..d52d41c07 --- /dev/null +++ b/src/playlist/plugins/CuePlaylistPlugin.cxx @@ -0,0 +1,88 @@ +/* + * 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 "CuePlaylistPlugin.hxx" +#include "../PlaylistPlugin.hxx" +#include "../SongEnumerator.hxx" +#include "cue/CueParser.hxx" +#include "TextInputStream.hxx" + +#include + +class CuePlaylist final : public SongEnumerator { + InputStream &is; + TextInputStream tis; + CueParser parser; + + public: + CuePlaylist(InputStream &_is) + :is(_is), tis(is) { + } + + virtual DetachedSong *NextSong() override; +}; + +static SongEnumerator * +cue_playlist_open_stream(InputStream &is) +{ + return new CuePlaylist(is); +} + +DetachedSong * +CuePlaylist::NextSong() +{ + DetachedSong *song = parser.Get(); + if (song != nullptr) + return song; + + std::string line; + while (tis.ReadLine(line)) { + parser.Feed(line.c_str()); + song = parser.Get(); + if (song != nullptr) + return song; + } + + parser.Finish(); + return parser.Get(); +} + +static const char *const cue_playlist_suffixes[] = { + "cue", + nullptr +}; + +static const char *const cue_playlist_mime_types[] = { + "application/x-cue", + nullptr +}; + +const struct playlist_plugin cue_playlist_plugin = { + "cue", + + nullptr, + nullptr, + nullptr, + cue_playlist_open_stream, + + nullptr, + cue_playlist_suffixes, + cue_playlist_mime_types, +}; diff --git a/src/playlist/plugins/CuePlaylistPlugin.hxx b/src/playlist/plugins/CuePlaylistPlugin.hxx new file mode 100644 index 000000000..4d833bfc2 --- /dev/null +++ b/src/playlist/plugins/CuePlaylistPlugin.hxx @@ -0,0 +1,25 @@ +/* + * 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_CUE_PLAYLIST_PLUGIN_HXX +#define MPD_CUE_PLAYLIST_PLUGIN_HXX + +extern const struct playlist_plugin cue_playlist_plugin; + +#endif diff --git a/src/playlist/plugins/DespotifyPlaylistPlugin.cxx b/src/playlist/plugins/DespotifyPlaylistPlugin.cxx new file mode 100644 index 000000000..1e8de0130 --- /dev/null +++ b/src/playlist/plugins/DespotifyPlaylistPlugin.cxx @@ -0,0 +1,141 @@ +/* + * 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 "DespotifyPlaylistPlugin.hxx" +#include "DespotifyUtils.hxx" +#include "../PlaylistPlugin.hxx" +#include "../MemorySongEnumerator.hxx" +#include "tag/Tag.hxx" +#include "DetachedSong.hxx" +#include "Log.hxx" + +extern "C" { +#include +} + +#include +#include + +static void +add_song(std::forward_list &songs, ds_track &track) +{ + const char *dsp_scheme = despotify_playlist_plugin.schemes[0]; + char uri[128]; + char *ds_uri; + + /* Create a spt://... URI for MPD */ + snprintf(uri, sizeof(uri), "%s://", dsp_scheme); + ds_uri = uri + strlen(dsp_scheme) + 3; + + if (despotify_track_to_uri(&track, ds_uri) != ds_uri) { + /* Should never really fail, but let's be sure */ + FormatDebug(despotify_domain, + "Can't add track %s", track.title); + return; + } + + songs.emplace_front(uri, mpd_despotify_tag_from_track(track)); +} + +static bool +parse_track(struct despotify_session *session, + std::forward_list &songs, + struct ds_link *link) +{ + struct ds_track *track = despotify_link_get_track(session, link); + if (track == nullptr) + return false; + + add_song(songs, *track); + return true; +} + +static bool +parse_playlist(struct despotify_session *session, + std::forward_list &songs, + struct ds_link *link) +{ + ds_playlist *playlist = despotify_link_get_playlist(session, link); + if (playlist == nullptr) + return false; + + for (ds_track *track = playlist->tracks; track != nullptr; + track = track->next) + add_song(songs, *track); + + return true; +} + +static SongEnumerator * +despotify_playlist_open_uri(const char *url, + gcc_unused Mutex &mutex, gcc_unused Cond &cond) +{ + despotify_session *session = mpd_despotify_get_session(); + if (session == nullptr) + return nullptr; + + /* Get link without spt:// */ + ds_link *link = + despotify_link_from_uri(url + strlen(despotify_playlist_plugin.schemes[0]) + 3); + if (link == nullptr) { + FormatDebug(despotify_domain, "Can't find %s\n", url); + return nullptr; + } + + std::forward_list songs; + + bool parse_result; + switch (link->type) { + case LINK_TYPE_TRACK: + parse_result = parse_track(session, songs, link); + break; + case LINK_TYPE_PLAYLIST: + parse_result = parse_playlist(session, songs, link); + break; + default: + parse_result = false; + break; + } + + despotify_free_link(link); + if (!parse_result) + return nullptr; + + songs.reverse(); + return new MemorySongEnumerator(std::move(songs)); +} + +static const char *const despotify_schemes[] = { + "spt", + nullptr +}; + +const struct playlist_plugin despotify_playlist_plugin = { + "despotify", + + nullptr, + nullptr, + despotify_playlist_open_uri, + nullptr, + + despotify_schemes, + nullptr, + nullptr, +}; diff --git a/src/playlist/plugins/DespotifyPlaylistPlugin.hxx b/src/playlist/plugins/DespotifyPlaylistPlugin.hxx new file mode 100644 index 000000000..6acfd40f4 --- /dev/null +++ b/src/playlist/plugins/DespotifyPlaylistPlugin.hxx @@ -0,0 +1,25 @@ +/* + * 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_DESPOTIFY_PLAYLIST_PLUGIN_HXX +#define MPD_PLAYLIST_DESPOTIFY_PLAYLIST_PLUGIN_HXX + +extern const struct playlist_plugin despotify_playlist_plugin; + +#endif diff --git a/src/playlist/plugins/EmbeddedCuePlaylistPlugin.cxx b/src/playlist/plugins/EmbeddedCuePlaylistPlugin.cxx new file mode 100644 index 000000000..53f3feda0 --- /dev/null +++ b/src/playlist/plugins/EmbeddedCuePlaylistPlugin.cxx @@ -0,0 +1,184 @@ +/* + * 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 + * + * Playlist plugin that reads embedded cue sheets from the "CUESHEET" + * tag of a music file. + */ + +#include "config.h" +#include "EmbeddedCuePlaylistPlugin.hxx" +#include "../PlaylistPlugin.hxx" +#include "../SongEnumerator.hxx" +#include "tag/TagHandler.hxx" +#include "tag/TagId3.hxx" +#include "tag/ApeTag.hxx" +#include "DetachedSong.hxx" +#include "TagFile.hxx" +#include "cue/CueParser.hxx" +#include "fs/Traits.hxx" +#include "fs/AllocatedPath.hxx" +#include "util/ASCII.hxx" + +#include + +class EmbeddedCuePlaylist final : public SongEnumerator { +public: + /** + * This is an override for the CUE's "FILE". An embedded CUE + * sheet must always point to the song file it is contained + * in. + */ + std::string filename; + + /** + * The value of the file's "CUESHEET" tag. + */ + std::string cuesheet; + + /** + * The offset of the next line within "cuesheet". + */ + char *next; + + CueParser *parser; + +public: + EmbeddedCuePlaylist() + :parser(nullptr) { + } + + virtual ~EmbeddedCuePlaylist() { + delete parser; + } + + virtual DetachedSong *NextSong() override; +}; + +static void +embcue_tag_pair(const char *name, const char *value, void *ctx) +{ + EmbeddedCuePlaylist *playlist = (EmbeddedCuePlaylist *)ctx; + + if (playlist->cuesheet.empty() && + StringEqualsCaseASCII(name, "cuesheet")) + playlist->cuesheet = value; +} + +static const struct tag_handler embcue_tag_handler = { + nullptr, + nullptr, + embcue_tag_pair, +}; + +static SongEnumerator * +embcue_playlist_open_uri(const char *uri, + gcc_unused Mutex &mutex, + gcc_unused Cond &cond) +{ + if (!PathTraitsUTF8::IsAbsolute(uri)) + /* only local files supported */ + return nullptr; + + const auto path_fs = AllocatedPath::FromUTF8(uri); + if (path_fs.IsNull()) + return nullptr; + + const auto playlist = new EmbeddedCuePlaylist(); + + tag_file_scan(path_fs, embcue_tag_handler, playlist); + if (playlist->cuesheet.empty()) { + tag_ape_scan2(path_fs, &embcue_tag_handler, playlist); + if (playlist->cuesheet.empty()) + tag_id3_scan(path_fs, &embcue_tag_handler, playlist); + } + + if (playlist->cuesheet.empty()) { + /* no "CUESHEET" tag found */ + delete playlist; + return nullptr; + } + + playlist->filename = PathTraitsUTF8::GetBase(uri); + + playlist->next = &playlist->cuesheet[0]; + playlist->parser = new CueParser(); + + return playlist; +} + +DetachedSong * +EmbeddedCuePlaylist::NextSong() +{ + DetachedSong *song = parser->Get(); + if (song != nullptr) + return song; + + while (*next != 0) { + const char *line = next; + char *eol = strpbrk(next, "\r\n"); + if (eol != nullptr) { + /* null-terminate the line */ + *eol = 0; + next = eol + 1; + } else + /* last line; put the "next" pointer to the + end of the buffer */ + next += strlen(line); + + parser->Feed(line); + song = parser->Get(); + if (song != nullptr) { + song->SetURI(filename); + return song; + } + } + + parser->Finish(); + song = parser->Get(); + if (song != nullptr) + song->SetURI(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", + nullptr +}; + +const struct playlist_plugin embcue_playlist_plugin = { + "cue", + + nullptr, + nullptr, + embcue_playlist_open_uri, + nullptr, + + embcue_playlist_suffixes, + nullptr, + nullptr, +}; diff --git a/src/playlist/plugins/EmbeddedCuePlaylistPlugin.hxx b/src/playlist/plugins/EmbeddedCuePlaylistPlugin.hxx new file mode 100644 index 000000000..5eedf3f13 --- /dev/null +++ b/src/playlist/plugins/EmbeddedCuePlaylistPlugin.hxx @@ -0,0 +1,25 @@ +/* + * 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_EMBCUE_PLAYLIST_PLUGIN_HXX +#define MPD_EMBCUE_PLAYLIST_PLUGIN_HXX + +extern const struct playlist_plugin embcue_playlist_plugin; + +#endif diff --git a/src/playlist/plugins/ExtM3uPlaylistPlugin.cxx b/src/playlist/plugins/ExtM3uPlaylistPlugin.cxx new file mode 100644 index 000000000..ac27fb136 --- /dev/null +++ b/src/playlist/plugins/ExtM3uPlaylistPlugin.cxx @@ -0,0 +1,149 @@ +/* + * 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 "ExtM3uPlaylistPlugin.hxx" +#include "../PlaylistPlugin.hxx" +#include "../SongEnumerator.hxx" +#include "DetachedSong.hxx" +#include "tag/Tag.hxx" +#include "tag/TagBuilder.hxx" +#include "util/StringUtil.hxx" +#include "TextInputStream.hxx" + +#include +#include + +class ExtM3uPlaylist final : public SongEnumerator { + TextInputStream tis; + +public: + ExtM3uPlaylist(InputStream &is) + :tis(is) { + } + + bool CheckFirstLine() { + std::string line; + return tis.ReadLine(line) && + strcmp(line.c_str(), "#EXTM3U") == 0; + } + + virtual DetachedSong *NextSong() override; +}; + +static SongEnumerator * +extm3u_open_stream(InputStream &is) +{ + ExtM3uPlaylist *playlist = new ExtM3uPlaylist(is); + + if (!playlist->CheckFirstLine()) { + /* no EXTM3U header: fall back to the plain m3u + plugin */ + delete playlist; + return NULL; + } + + return playlist; +} + +/** + * Parse a EXTINF line. + * + * @param line the rest of the input line after the colon + */ +static Tag +extm3u_parse_tag(const char *line) +{ + long duration; + char *endptr; + const char *name; + + duration = strtol(line, &endptr, 10); + if (endptr[0] != ',') + /* malformed line */ + return Tag(); + + if (duration < 0) + /* 0 means unknown duration */ + duration = 0; + + name = strchug_fast(endptr + 1); + if (*name == 0 && duration == 0) + /* no information available; don't allocate a tag + object */ + return Tag(); + + TagBuilder tag; + tag.SetTime(duration); + + /* unfortunately, there is no real specification for the + EXTM3U format, so we must assume that the string after the + comma is opaque, and is just the song name*/ + if (*name != 0) + tag.AddItem(TAG_NAME, name); + + return tag.Commit(); +} + +DetachedSong * +ExtM3uPlaylist::NextSong() +{ + Tag tag; + std::string line; + const char *line_s; + + do { + if (!tis.ReadLine(line)) + return NULL; + + line_s = line.c_str(); + + if (StringStartsWith(line_s, "#EXTINF:")) { + tag = extm3u_parse_tag(line_s + 8); + continue; + } + + line_s = strchug_fast(line_s); + } while (line_s[0] == '#' || *line_s == 0); + + return new DetachedSong(line_s, std::move(tag)); +} + +static const char *const extm3u_suffixes[] = { + "m3u", + NULL +}; + +static const char *const extm3u_mime_types[] = { + "audio/x-mpegurl", + NULL +}; + +const struct playlist_plugin extm3u_playlist_plugin = { + "extm3u", + + nullptr, + nullptr, + nullptr, + extm3u_open_stream, + + nullptr, + extm3u_suffixes, + extm3u_mime_types, +}; diff --git a/src/playlist/plugins/ExtM3uPlaylistPlugin.hxx b/src/playlist/plugins/ExtM3uPlaylistPlugin.hxx new file mode 100644 index 000000000..5743ded43 --- /dev/null +++ b/src/playlist/plugins/ExtM3uPlaylistPlugin.hxx @@ -0,0 +1,25 @@ +/* + * 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_EXTM3U_PLAYLIST_PLUGIN_HXX +#define MPD_EXTM3U_PLAYLIST_PLUGIN_HXX + +extern const struct playlist_plugin extm3u_playlist_plugin; + +#endif diff --git a/src/playlist/plugins/M3uPlaylistPlugin.cxx b/src/playlist/plugins/M3uPlaylistPlugin.cxx new file mode 100644 index 000000000..a87e3edc7 --- /dev/null +++ b/src/playlist/plugins/M3uPlaylistPlugin.cxx @@ -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. + */ + +#include "config.h" +#include "M3uPlaylistPlugin.hxx" +#include "../PlaylistPlugin.hxx" +#include "../SongEnumerator.hxx" +#include "DetachedSong.hxx" +#include "util/StringUtil.hxx" +#include "TextInputStream.hxx" + +class M3uPlaylist final : public SongEnumerator { + TextInputStream tis; + +public: + M3uPlaylist(InputStream &is) + :tis(is) { + } + + virtual DetachedSong *NextSong() override; +}; + +static SongEnumerator * +m3u_open_stream(InputStream &is) +{ + return new M3uPlaylist(is); +} + +DetachedSong * +M3uPlaylist::NextSong() +{ + std::string line; + const char *line_s; + + do { + if (!tis.ReadLine(line)) + return nullptr; + + line_s = line.c_str(); + line_s = strchug_fast(line_s); + } while (line_s[0] == '#' || *line_s == 0); + + return new DetachedSong(line_s); +} + +static const char *const m3u_suffixes[] = { + "m3u", + nullptr +}; + +static const char *const m3u_mime_types[] = { + "audio/x-mpegurl", + nullptr +}; + +const struct playlist_plugin m3u_playlist_plugin = { + "m3u", + + nullptr, + nullptr, + nullptr, + m3u_open_stream, + + nullptr, + m3u_suffixes, + m3u_mime_types, +}; diff --git a/src/playlist/plugins/M3uPlaylistPlugin.hxx b/src/playlist/plugins/M3uPlaylistPlugin.hxx new file mode 100644 index 000000000..f1ad14069 --- /dev/null +++ b/src/playlist/plugins/M3uPlaylistPlugin.hxx @@ -0,0 +1,25 @@ +/* + * 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_M3U_PLAYLIST_PLUGIN_HXX +#define MPD_M3U_PLAYLIST_PLUGIN_HXX + +extern const struct playlist_plugin m3u_playlist_plugin; + +#endif diff --git a/src/playlist/plugins/PlsPlaylistPlugin.cxx b/src/playlist/plugins/PlsPlaylistPlugin.cxx new file mode 100644 index 000000000..0abd7172b --- /dev/null +++ b/src/playlist/plugins/PlsPlaylistPlugin.cxx @@ -0,0 +1,163 @@ +/* + * 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 "PlsPlaylistPlugin.hxx" +#include "../PlaylistPlugin.hxx" +#include "../MemorySongEnumerator.hxx" +#include "InputStream.hxx" +#include "DetachedSong.hxx" +#include "tag/TagBuilder.hxx" +#include "util/Error.hxx" +#include "util/Domain.hxx" +#include "Log.hxx" + +#include + +#include + +#include + +static constexpr Domain pls_domain("pls"); + +static void +pls_parser(GKeyFile *keyfile, std::forward_list &songs) +{ + gchar *value; + GError *error = nullptr; + int num_entries = g_key_file_get_integer(keyfile, "playlist", + "NumberOfEntries", &error); + if (error) { + FormatError(pls_domain, + "Invalid PLS file: '%s'", error->message); + g_error_free(error); + error = nullptr; + + /* Hack to work around shoutcast failure to comform to spec */ + num_entries = g_key_file_get_integer(keyfile, "playlist", + "numberofentries", &error); + if (error) { + g_error_free(error); + error = nullptr; + } + } + + for (; num_entries > 0; --num_entries) { + char key[64]; + sprintf(key, "File%u", num_entries); + char *uri = g_key_file_get_string(keyfile, "playlist", key, + &error); + if(error) { + FormatError(pls_domain, "Invalid PLS entry %s: '%s'", + key, error->message); + g_error_free(error); + return; + } + + TagBuilder tag; + + sprintf(key, "Title%u", num_entries); + value = g_key_file_get_string(keyfile, "playlist", key, + nullptr); + if (value != nullptr) + tag.AddItem(TAG_TITLE, value); + + g_free(value); + + sprintf(key, "Length%u", num_entries); + int length = g_key_file_get_integer(keyfile, "playlist", key, + nullptr); + if (length > 0) + tag.SetTime(length); + + songs.emplace_front(uri, tag.Commit()); + g_free(uri); + } + +} + +static SongEnumerator * +pls_open_stream(InputStream &is) +{ + GError *error = nullptr; + Error error2; + + std::string kf_data; + + do { + char buffer[1024]; + size_t nbytes = is.LockRead(buffer, sizeof(buffer), error2); + if (nbytes == 0) { + if (error2.IsDefined()) { + LogError(error2); + return nullptr; + } + + break; + } + + kf_data.append(buffer, nbytes); + /* Limit to 64k */ + } while (kf_data.length() < 65536); + + if (kf_data.empty()) { + LogWarning(pls_domain, "KeyFile parser failed: No Data"); + return nullptr; + } + + GKeyFile *keyfile = g_key_file_new(); + if (!g_key_file_load_from_data(keyfile, + kf_data.data(), kf_data.length(), + G_KEY_FILE_NONE, &error)) { + FormatError(pls_domain, + "KeyFile parser failed: %s", error->message); + g_error_free(error); + g_key_file_free(keyfile); + return nullptr; + } + + std::forward_list songs; + pls_parser(keyfile, songs); + g_key_file_free(keyfile); + + return new MemorySongEnumerator(std::move(songs)); +} + +static const char *const pls_suffixes[] = { + "pls", + nullptr +}; + +static const char *const pls_mime_types[] = { + "audio/x-scpls", + nullptr +}; + +const struct playlist_plugin pls_playlist_plugin = { + "pls", + + nullptr, + nullptr, + nullptr, + pls_open_stream, + + nullptr, + pls_suffixes, + pls_mime_types, +}; diff --git a/src/playlist/plugins/PlsPlaylistPlugin.hxx b/src/playlist/plugins/PlsPlaylistPlugin.hxx new file mode 100644 index 000000000..1a3f33873 --- /dev/null +++ b/src/playlist/plugins/PlsPlaylistPlugin.hxx @@ -0,0 +1,25 @@ +/* + * 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_PLS_PLAYLIST_PLUGIN_HXX +#define MPD_PLS_PLAYLIST_PLUGIN_HXX + +extern const struct playlist_plugin pls_playlist_plugin; + +#endif diff --git a/src/playlist/plugins/RssPlaylistPlugin.cxx b/src/playlist/plugins/RssPlaylistPlugin.cxx new file mode 100644 index 000000000..076d82f14 --- /dev/null +++ b/src/playlist/plugins/RssPlaylistPlugin.cxx @@ -0,0 +1,185 @@ +/* + * 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 "RssPlaylistPlugin.hxx" +#include "../PlaylistPlugin.hxx" +#include "../MemorySongEnumerator.hxx" +#include "tag/TagBuilder.hxx" +#include "util/ASCII.hxx" +#include "util/Error.hxx" +#include "Expat.hxx" +#include "Log.hxx" + +/** + * This is the state object for the GLib XML parser. + */ +struct RssParser { + /** + * The list of songs (in reverse order because that's faster + * while adding). + */ + std::forward_list songs; + + /** + * The current position in the XML file. + */ + enum { + ROOT, ITEM, + } state; + + /** + * The current tag within the "entry" element. This is only + * valid if state==ITEM. TAG_NUM_OF_ITEM_TYPES means there + * is no (known) tag. + */ + TagType tag_type; + + /** + * The current song URI. It is set by the "enclosure" + * element. + */ + std::string location; + + TagBuilder tag_builder; + + RssParser() + :state(ROOT) {} +}; + +static void XMLCALL +rss_start_element(void *user_data, const XML_Char *element_name, + const XML_Char **atts) +{ + RssParser *parser = (RssParser *)user_data; + + switch (parser->state) { + case RssParser::ROOT: + if (StringEqualsCaseASCII(element_name, "item")) { + parser->state = RssParser::ITEM; + parser->location.clear(); + parser->tag_type = TAG_NUM_OF_ITEM_TYPES; + } + + break; + + case RssParser::ITEM: + if (StringEqualsCaseASCII(element_name, "enclosure")) { + const char *href = + ExpatParser::GetAttributeCase(atts, "url"); + if (href != nullptr) + parser->location = href; + } else if (StringEqualsCaseASCII(element_name, "title")) + parser->tag_type = TAG_TITLE; + else if (StringEqualsCaseASCII(element_name, "itunes:author")) + parser->tag_type = TAG_ARTIST; + + break; + } +} + +static void XMLCALL +rss_end_element(void *user_data, const XML_Char *element_name) +{ + RssParser *parser = (RssParser *)user_data; + + switch (parser->state) { + case RssParser::ROOT: + break; + + case RssParser::ITEM: + if (StringEqualsCaseASCII(element_name, "item")) { + if (!parser->location.empty()) + parser->songs.emplace_front(std::move(parser->location), + parser->tag_builder.Commit()); + + parser->state = RssParser::ROOT; + } else + parser->tag_type = TAG_NUM_OF_ITEM_TYPES; + + break; + } +} + +static void XMLCALL +rss_char_data(void *user_data, const XML_Char *s, int len) +{ + RssParser *parser = (RssParser *)user_data; + + switch (parser->state) { + case RssParser::ROOT: + break; + + case RssParser::ITEM: + if (parser->tag_type != TAG_NUM_OF_ITEM_TYPES) + parser->tag_builder.AddItem(parser->tag_type, s, len); + + break; + } +} + +/* + * The playlist object + * + */ + +static SongEnumerator * +rss_open_stream(InputStream &is) +{ + RssParser parser; + + { + ExpatParser expat(&parser); + expat.SetElementHandler(rss_start_element, rss_end_element); + expat.SetCharacterDataHandler(rss_char_data); + + Error error; + if (!expat.Parse(is, error)) { + LogError(error); + return nullptr; + } + } + + parser.songs.reverse(); + return new MemorySongEnumerator(std::move(parser.songs)); +} + +static const char *const rss_suffixes[] = { + "rss", + nullptr +}; + +static const char *const rss_mime_types[] = { + "application/rss+xml", + "text/xml", + nullptr +}; + +const struct playlist_plugin rss_playlist_plugin = { + "rss", + + nullptr, + nullptr, + nullptr, + rss_open_stream, + + nullptr, + rss_suffixes, + rss_mime_types, +}; diff --git a/src/playlist/plugins/RssPlaylistPlugin.hxx b/src/playlist/plugins/RssPlaylistPlugin.hxx new file mode 100644 index 000000000..a00a5a898 --- /dev/null +++ b/src/playlist/plugins/RssPlaylistPlugin.hxx @@ -0,0 +1,25 @@ +/* + * 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_RSS_PLAYLIST_PLUGIN_HXX +#define MPD_RSS_PLAYLIST_PLUGIN_HXX + +extern const struct playlist_plugin rss_playlist_plugin; + +#endif diff --git a/src/playlist/plugins/SoundCloudPlaylistPlugin.cxx b/src/playlist/plugins/SoundCloudPlaylistPlugin.cxx new file mode 100644 index 000000000..fac052d19 --- /dev/null +++ b/src/playlist/plugins/SoundCloudPlaylistPlugin.cxx @@ -0,0 +1,401 @@ +/* + * 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 "SoundCloudPlaylistPlugin.hxx" +#include "../PlaylistPlugin.hxx" +#include "../MemorySongEnumerator.hxx" +#include "ConfigData.hxx" +#include "InputStream.hxx" +#include "tag/TagBuilder.hxx" +#include "util/StringUtil.hxx" +#include "util/Error.hxx" +#include "util/Domain.hxx" +#include "Log.hxx" + +#include +#include + +#include + +#include + +static struct { + std::string apikey; +} soundcloud_config; + +static constexpr Domain soundcloud_domain("soundcloud"); + +static bool +soundcloud_init(const config_param ¶m) +{ + // APIKEY for MPD application, registered under DarkFox' account. + soundcloud_config.apikey = param.GetBlockValue("apikey", "a25e51780f7f86af0afa91f241d091f8"); + if (soundcloud_config.apikey.empty()) { + LogDebug(soundcloud_domain, + "disabling the soundcloud playlist plugin " + "because API key is not set"); + return false; + } + + return true; +} + +/** + * 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 (StringStartsWith(uri, "https://")) { + u = g_strdup(uri); + } else if (StringStartsWith(uri, "soundcloud.com")) { + u = g_strconcat("https://", uri, nullptr); + } else { + /* assume it's just a path on soundcloud.com */ + u = g_strconcat("https://soundcloud.com/", uri, nullptr); + } + + ru = g_strconcat("https://api.soundcloud.com/resolve.json?url=", + u, "&client_id=", + soundcloud_config.apikey.c_str(), nullptr); + 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", + nullptr, +}; + +struct parse_data { + int key; + char* stream_url; + long duration; + char* title; + int got_url; /* nesting level of last stream_url */ + + std::forward_list 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: + g_free(data->title); + data->title = g_strndup(s, stringlen); + break; + case Stream_URL: + 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 (memcmp((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; + + char *u = g_strconcat(data->stream_url, "?client_id=", + soundcloud_config.apikey.c_str(), nullptr); + + TagBuilder tag; + tag.SetTime(data->duration / 1000); + if (data->title != nullptr) + tag.AddItem(TAG_NAME, data->title); + + data->songs.emplace_front(u, tag.Commit()); + g_free(u); + + return 1; +} + +static yajl_callbacks parse_callbacks = { + nullptr, + nullptr, + handle_integer, + nullptr, + nullptr, + handle_string, + handle_start_map, + handle_mapkey, + handle_end_map, + nullptr, + nullptr, +}; + +/** + * Read JSON data and parse it using the given YAJL parser. + * @param url URL of the JSON data. + * @param hand YAJL parser handle. + * @return -1 on error, 0 on success. + */ +static int +soundcloud_parse_json(const char *url, yajl_handle hand, + Mutex &mutex, Cond &cond) +{ + Error error; + InputStream *input_stream = InputStream::OpenReady(url, mutex, cond, + error); + if (input_stream == nullptr) { + if (error.IsDefined()) + LogError(error); + return -1; + } + + mutex.lock(); + + yajl_status stat; + int done = 0; + + while (!done) { + char buffer[4096]; + unsigned char *ubuffer = (unsigned char *)buffer; + const size_t nbytes = + input_stream->Read(buffer, sizeof(buffer), error); + if (nbytes == 0) { + if (error.IsDefined()) + LogError(error); + + if (input_stream->IsEOF()) { + done = true; + } else { + mutex.unlock(); + input_stream->Close(); + return -1; + } + } + + if (done) { +#ifdef HAVE_YAJL1 + stat = yajl_parse_complete(hand); +#else + stat = yajl_complete_parse(hand); +#endif + } else + stat = yajl_parse(hand, ubuffer, nbytes); + + if (stat != yajl_status_ok +#ifdef HAVE_YAJL1 + && stat != yajl_status_insufficient_data +#endif + ) + { + unsigned char *str = yajl_get_error(hand, 1, ubuffer, nbytes); + LogError(soundcloud_domain, (const char *)str); + yajl_free_error(hand, str); + break; + } + } + + mutex.unlock(); + input_stream->Close(); + + return 0; +} + +/** + * Parse a soundcloud:// URL and create a playlist. + * @param uri A soundcloud URL. Accepted forms: + * soundcloud://track/ + * soundcloud://playlist/ + * soundcloud://url/ + */ +static SongEnumerator * +soundcloud_open_uri(const char *uri, Mutex &mutex, Cond &cond) +{ + assert(memcmp(uri, "soundcloud://", 13) == 0); + uri += 13; + + char *u = nullptr; + if (memcmp(uri, "track/", 6) == 0) { + const char *rest = uri + 6; + u = g_strconcat("https://api.soundcloud.com/tracks/", + rest, ".json?client_id=", + soundcloud_config.apikey.c_str(), nullptr); + } else if (memcmp(uri, "playlist/", 9) == 0) { + const char *rest = uri + 9; + u = g_strconcat("https://api.soundcloud.com/playlists/", + rest, ".json?client_id=", + soundcloud_config.apikey.c_str(), nullptr); + } else if (memcmp(uri, "user/", 5) == 0) { + const char *rest = uri + 5; + u = g_strconcat("https://api.soundcloud.com/users/", + rest, "/tracks.json?client_id=", + soundcloud_config.apikey.c_str(), nullptr); + } else if (memcmp(uri, "search/", 7) == 0) { + const char *rest = uri + 7; + u = g_strconcat("https://api.soundcloud.com/tracks.json?q=", + rest, "&client_id=", + soundcloud_config.apikey.c_str(), nullptr); + } else if (memcmp(uri, "url/", 4) == 0) { + const char *rest = uri + 4; + /* Translate to soundcloud resolver call. libcurl will automatically + follow the redirect to the right resource. */ + u = soundcloud_resolve(rest); + } + + if (u == nullptr) { + LogWarning(soundcloud_domain, "unknown soundcloud URI"); + return nullptr; + } + + struct parse_data data; + data.got_url = 0; + data.title = nullptr; + data.stream_url = nullptr; +#ifdef HAVE_YAJL1 + yajl_handle hand = yajl_alloc(&parse_callbacks, nullptr, nullptr, + &data); +#else + yajl_handle hand = yajl_alloc(&parse_callbacks, nullptr, &data); +#endif + + int ret = soundcloud_parse_json(u, hand, mutex, cond); + + g_free(u); + yajl_free(hand); + g_free(data.title); + g_free(data.stream_url); + + if (ret == -1) + return nullptr; + + data.songs.reverse(); + return new MemorySongEnumerator(std::move(data.songs)); +} + +static const char *const soundcloud_schemes[] = { + "soundcloud", + nullptr +}; + +const struct playlist_plugin soundcloud_playlist_plugin = { + "soundcloud", + + soundcloud_init, + nullptr, + soundcloud_open_uri, + nullptr, + + soundcloud_schemes, + nullptr, + nullptr, +}; + + diff --git a/src/playlist/plugins/SoundCloudPlaylistPlugin.hxx b/src/playlist/plugins/SoundCloudPlaylistPlugin.hxx new file mode 100644 index 000000000..b355b477a --- /dev/null +++ b/src/playlist/plugins/SoundCloudPlaylistPlugin.hxx @@ -0,0 +1,25 @@ +/* + * 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_SOUNDCLOUD_PLAYLIST_PLUGIN_HXX +#define MPD_SOUNDCLOUD_PLAYLIST_PLUGIN_HXX + +extern const struct playlist_plugin soundcloud_playlist_plugin; + +#endif diff --git a/src/playlist/plugins/XspfPlaylistPlugin.cxx b/src/playlist/plugins/XspfPlaylistPlugin.cxx new file mode 100644 index 000000000..12888b3bc --- /dev/null +++ b/src/playlist/plugins/XspfPlaylistPlugin.cxx @@ -0,0 +1,234 @@ +/* + * 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 "XspfPlaylistPlugin.hxx" +#include "../PlaylistPlugin.hxx" +#include "../MemorySongEnumerator.hxx" +#include "DetachedSong.hxx" +#include "InputStream.hxx" +#include "tag/TagBuilder.hxx" +#include "util/Error.hxx" +#include "util/Domain.hxx" +#include "Expat.hxx" +#include "Log.hxx" + +#include + +static constexpr Domain xspf_domain("xspf"); + +/** + * This is the state object for the GLib XML parser. + */ +struct XspfParser { + /** + * The list of songs (in reverse order because that's faster + * while adding). + */ + std::forward_list songs; + + /** + * The current position in the XML file. + */ + enum { + ROOT, PLAYLIST, TRACKLIST, TRACK, + LOCATION, + } state; + + /** + * The current tag within the "track" element. This is only + * valid if state==TRACK. TAG_NUM_OF_ITEM_TYPES means there + * is no (known) tag. + */ + TagType tag_type; + + /** + * The current song URI. It is set by the "location" element. + */ + std::string location; + + TagBuilder tag_builder; + + XspfParser() + :state(ROOT) {} +}; + +static void XMLCALL +xspf_start_element(void *user_data, const XML_Char *element_name, + gcc_unused const XML_Char **atts) +{ + XspfParser *parser = (XspfParser *)user_data; + + switch (parser->state) { + case XspfParser::ROOT: + if (strcmp(element_name, "playlist") == 0) + parser->state = XspfParser::PLAYLIST; + + break; + + case XspfParser::PLAYLIST: + if (strcmp(element_name, "trackList") == 0) + parser->state = XspfParser::TRACKLIST; + + break; + + case XspfParser::TRACKLIST: + if (strcmp(element_name, "track") == 0) { + parser->state = XspfParser::TRACK; + parser->location.clear(); + parser->tag_type = TAG_NUM_OF_ITEM_TYPES; + } + + break; + + case XspfParser::TRACK: + if (strcmp(element_name, "location") == 0) + parser->state = XspfParser::LOCATION; + else if (strcmp(element_name, "title") == 0) + parser->tag_type = TAG_TITLE; + else if (strcmp(element_name, "creator") == 0) + /* TAG_COMPOSER would be more correct + according to the XSPF spec */ + parser->tag_type = TAG_ARTIST; + else if (strcmp(element_name, "annotation") == 0) + parser->tag_type = TAG_COMMENT; + else if (strcmp(element_name, "album") == 0) + parser->tag_type = TAG_ALBUM; + else if (strcmp(element_name, "trackNum") == 0) + parser->tag_type = TAG_TRACK; + + break; + + case XspfParser::LOCATION: + break; + } +} + +static void XMLCALL +xspf_end_element(void *user_data, const XML_Char *element_name) +{ + XspfParser *parser = (XspfParser *)user_data; + + switch (parser->state) { + case XspfParser::ROOT: + break; + + case XspfParser::PLAYLIST: + if (strcmp(element_name, "playlist") == 0) + parser->state = XspfParser::ROOT; + + break; + + case XspfParser::TRACKLIST: + if (strcmp(element_name, "tracklist") == 0) + parser->state = XspfParser::PLAYLIST; + + break; + + case XspfParser::TRACK: + if (strcmp(element_name, "track") == 0) { + if (!parser->location.empty()) + parser->songs.emplace_front(std::move(parser->location), + parser->tag_builder.Commit()); + + parser->state = XspfParser::TRACKLIST; + } else + parser->tag_type = TAG_NUM_OF_ITEM_TYPES; + + break; + + case XspfParser::LOCATION: + parser->state = XspfParser::TRACK; + break; + } +} + +static void XMLCALL +xspf_char_data(void *user_data, const XML_Char *s, int len) +{ + XspfParser *parser = (XspfParser *)user_data; + + switch (parser->state) { + case XspfParser::ROOT: + case XspfParser::PLAYLIST: + case XspfParser::TRACKLIST: + break; + + case XspfParser::TRACK: + if (!parser->location.empty() && + parser->tag_type != TAG_NUM_OF_ITEM_TYPES) + parser->tag_builder.AddItem(parser->tag_type, s, len); + + break; + + case XspfParser::LOCATION: + parser->location.assign(s, len); + + break; + } +} + +/* + * The playlist object + * + */ + +static SongEnumerator * +xspf_open_stream(InputStream &is) +{ + XspfParser parser; + + { + ExpatParser expat(&parser); + expat.SetElementHandler(xspf_start_element, xspf_end_element); + expat.SetCharacterDataHandler(xspf_char_data); + + Error error; + if (!expat.Parse(is, error)) { + LogError(error); + return nullptr; + } + } + + parser.songs.reverse(); + return new MemorySongEnumerator(std::move(parser.songs)); +} + +static const char *const xspf_suffixes[] = { + "xspf", + nullptr +}; + +static const char *const xspf_mime_types[] = { + "application/xspf+xml", + nullptr +}; + +const struct playlist_plugin xspf_playlist_plugin = { + "xspf", + + nullptr, + nullptr, + nullptr, + xspf_open_stream, + + nullptr, + xspf_suffixes, + xspf_mime_types, +}; diff --git a/src/playlist/plugins/XspfPlaylistPlugin.hxx b/src/playlist/plugins/XspfPlaylistPlugin.hxx new file mode 100644 index 000000000..6b08a6be6 --- /dev/null +++ b/src/playlist/plugins/XspfPlaylistPlugin.hxx @@ -0,0 +1,25 @@ +/* + * 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_XSPF_PLAYLIST_PLUGIN_HXX +#define MPD_XSPF_PLAYLIST_PLUGIN_HXX + +extern const struct playlist_plugin xspf_playlist_plugin; + +#endif -- cgit v1.2.3