From 63a2ac21e1d8190448057fbf24d5ab948d3daff8 Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Mon, 5 Sep 2011 22:53:46 +0200 Subject: ProxyDatabasePlugin: new database plugin using libmpdclient Implementation incomplete, but sort-of-works. DumpDatabase works, but MPD is still hard-coded on the "simple" plugin. --- src/db/ProxyDatabasePlugin.cxx | 345 +++++++++++++++++++++++++++++++++++++++++ src/db/ProxyDatabasePlugin.hxx | 27 ++++ 2 files changed, 372 insertions(+) create mode 100644 src/db/ProxyDatabasePlugin.cxx create mode 100644 src/db/ProxyDatabasePlugin.hxx (limited to 'src/db') diff --git a/src/db/ProxyDatabasePlugin.cxx b/src/db/ProxyDatabasePlugin.cxx new file mode 100644 index 00000000..3b2fb36a --- /dev/null +++ b/src/db/ProxyDatabasePlugin.cxx @@ -0,0 +1,345 @@ +/* + * Copyright (C) 2003-2012 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "config.h" +#include "ProxyDatabasePlugin.hxx" +#include "DatabasePlugin.hxx" +#include "DatabaseSelection.hxx" +#include "gcc.h" + +extern "C" { +#include "db_error.h" +#include "db_visitor.h" +#include "db_save.h" +#include "db_lock.h" +#include "conf.h" +#include "song.h" +#include "tag.h" +} + +#include "directory.h" +#include "playlist_vector.h" + +#undef MPD_DIRECTORY_H +#undef MPD_SONG_H +#include + +#include +#include +#include + +class ProxyDatabase : public Database { + std::string host; + unsigned port; + + struct mpd_connection *connection; + +public: + static Database *Create(const struct config_param *param, + GError **error_r); + + virtual bool Open(GError **error_r) override; + virtual void Close() override; + virtual struct song *GetSong(const char *uri_utf8, + GError **error_r) const override; + virtual bool Visit(const DatabaseSelection &selection, + VisitDirectory visit_directory, + VisitSong visit_song, + VisitPlaylist visit_playlist, + GError **error_r) const override; + +protected: + bool Configure(const struct config_param *param, GError **error_r); +}; + +G_GNUC_CONST +static inline GQuark +libmpdclient_quark(void) +{ + return g_quark_from_static_string("libmpdclient"); +} + +static bool +CheckError(const struct mpd_connection *connection, GError **error_r) +{ + const auto error = mpd_connection_get_error(connection); + if (error == MPD_ERROR_SUCCESS) + return true; + + g_set_error_literal(error_r, libmpdclient_quark(), (int)error, + mpd_connection_get_error_message(connection)); + return false; +} + +Database * +ProxyDatabase::Create(const struct config_param *param, GError **error_r) +{ + ProxyDatabase *db = new ProxyDatabase(); + if (!db->Configure(param, error_r)) { + delete db; + db = NULL; + } + + return db; +} + +bool +ProxyDatabase::Configure(const struct config_param *param, GError **) +{ + host = config_get_block_string(param, "host", ""); + port = config_get_block_unsigned(param, "port", 0); + + return true; +} + +bool +ProxyDatabase::Open(GError **error_r) +{ + connection = mpd_connection_new(host.empty() ? NULL : host.c_str(), + port, 0); + if (connection == NULL) { + g_set_error_literal(error_r, libmpdclient_quark(), + (int)MPD_ERROR_OOM, "Out of memory"); + return false; + } + + if (!CheckError(connection, error_r)) { + mpd_connection_free(connection); + return false; + } + + return true; +} + +void +ProxyDatabase::Close() +{ + assert(connection != nullptr); + + mpd_connection_free(connection); +} + +struct song * +ProxyDatabase::GetSong(const char *uri, GError **error_r) const +{ + // TODO: implement + // TODO: auto-reconnect + + g_set_error(error_r, db_quark(), DB_NOT_FOUND, + "No such song: %s", uri); + return nullptr; +} + +static bool +Visit(struct mpd_connection *connection, directory &parent, + bool recursive, VisitDirectory visit_directory, VisitSong visit_song, + VisitPlaylist visit_playlist, GError **error_r); + +static bool +Visit(struct mpd_connection *connection, directory &parent, + bool recursive, const struct mpd_directory *directory, + VisitDirectory visit_directory, VisitSong visit_song, + VisitPlaylist visit_playlist, GError **error_r) +{ + if (!recursive && !visit_directory) + return true; + + struct directory *d = + directory_new(mpd_directory_get_path(directory), &parent); + + bool success = (!visit_directory || visit_directory(*d, error_r)) && + Visit(connection, *d, recursive, + visit_directory, visit_song, visit_playlist, error_r); + directory_free(d); + + return success; +} + +static void +Copy(struct tag *tag, enum tag_type d_tag, + const struct mpd_song *song, enum mpd_tag_type s_tag) +{ + + for (unsigned i = 0;; ++i) { + const char *value = mpd_song_get_tag(song, s_tag, i); + if (value == NULL) + break; + + tag_add_item(tag, d_tag, value); + } +} + +static song * +Convert(struct directory &parent, const struct mpd_song *song) +{ + char *name = g_path_get_basename(mpd_song_get_uri(song)); + struct song *s = song_file_new(name, &parent); + g_free(name); + + s->mtime = mpd_song_get_last_modified(song); + s->start_ms = mpd_song_get_start(song) * 1000; + s->end_ms = mpd_song_get_end(song) * 1000; + + struct tag *tag = tag_new(); + tag->time = mpd_song_get_duration(song); + + static constexpr struct { + enum tag_type d; + enum mpd_tag_type s; + } table[] = { + { TAG_ARTIST, MPD_TAG_ARTIST }, + { TAG_ALBUM, MPD_TAG_ALBUM }, + { TAG_ALBUM_ARTIST, MPD_TAG_ALBUM_ARTIST }, + { TAG_TITLE, MPD_TAG_TITLE }, + { TAG_TRACK, MPD_TAG_TRACK }, + { TAG_NAME, MPD_TAG_NAME }, + { TAG_GENRE, MPD_TAG_GENRE }, + { TAG_DATE, MPD_TAG_DATE }, + { TAG_COMPOSER, MPD_TAG_COMPOSER }, + { TAG_PERFORMER, MPD_TAG_PERFORMER }, + { TAG_COMMENT, MPD_TAG_COMMENT }, + { TAG_DISC, MPD_TAG_DISC }, + { TAG_MUSICBRAINZ_ARTISTID, MPD_TAG_MUSICBRAINZ_ARTISTID }, + { TAG_MUSICBRAINZ_ALBUMID, MPD_TAG_MUSICBRAINZ_ALBUMID }, + { TAG_MUSICBRAINZ_ALBUMARTISTID, + MPD_TAG_MUSICBRAINZ_ALBUMARTISTID }, + { TAG_MUSICBRAINZ_TRACKID, MPD_TAG_MUSICBRAINZ_TRACKID }, + { TAG_NUM_OF_ITEM_TYPES, MPD_TAG_COUNT } + }; + + tag_begin_add(tag); + for (auto i = table; i->d != TAG_NUM_OF_ITEM_TYPES; ++i) + Copy(tag, i->d, song, i->s); + tag_end_add(tag); + + s->tag = tag; + + return s; +} + +static bool +Visit(struct directory &parent, const struct mpd_song *song, + VisitSong visit_song, GError **error_r) +{ + if (!visit_song) + return true; + + struct song *s = Convert(parent, song); + bool success = visit_song(*s, error_r); + song_free(s); + + return success; +} + +static bool +Visit(struct directory &parent, const struct mpd_playlist *playlist, + VisitPlaylist visit_playlist, GError **error_r) +{ + if (!visit_playlist) + return true; + + char *name = g_path_get_basename(mpd_playlist_get_path(playlist)); + struct playlist_metadata p; + p.name = name; + p.mtime = mpd_playlist_get_last_modified(playlist); + + bool success = visit_playlist(p, parent, error_r); + g_free(name); + + return success; +} + +static std::list +ReceiveEntities(struct mpd_connection *connection) +{ + std::list entities; + struct mpd_entity *entity; + while ((entity = mpd_recv_entity(connection)) != NULL) + entities.push_back(entity); + + mpd_response_finish(connection); + return entities; +} + +static bool +Visit(struct mpd_connection *connection, struct directory &parent, + bool recursive, VisitDirectory visit_directory, VisitSong visit_song, + VisitPlaylist visit_playlist, GError **error_r) +{ + if (!mpd_send_list_meta(connection, directory_get_path(&parent))) + return CheckError(connection, error_r); + + std::list entities = ReceiveEntities(connection); + if (!CheckError(connection, error_r)) { + for (auto entity : entities) + mpd_entity_free(entity); + return false; + } + + for (auto entity : entities) { + switch (mpd_entity_get_type(entity)) { + case MPD_ENTITY_TYPE_UNKNOWN: + break; + + case MPD_ENTITY_TYPE_DIRECTORY: + Visit(connection, parent, recursive, + mpd_entity_get_directory(entity), + visit_directory, visit_song, visit_playlist, + error_r); + break; + + case MPD_ENTITY_TYPE_SONG: + Visit(parent, mpd_entity_get_song(entity), visit_song, + error_r); + break; + + case MPD_ENTITY_TYPE_PLAYLIST: + Visit(parent, mpd_entity_get_playlist(entity), + visit_playlist, error_r); + break; + } + + mpd_entity_free(entity); + } + + return CheckError(connection, error_r); +} + +bool +ProxyDatabase::Visit(const DatabaseSelection &selection, + VisitDirectory visit_directory, + VisitSong visit_song, + VisitPlaylist visit_playlist, + GError **error_r) const +{ + // TODO: match + // TODO: auto-reconnect + + struct directory *parent = directory_new(selection.uri, nullptr); + bool success = ::Visit(connection, *parent, selection.recursive, + visit_directory, visit_song, visit_playlist, + error_r); + directory_free(parent); + return success; +} + +const DatabasePlugin proxy_db_plugin = { + "proxy", + ProxyDatabase::Create, +}; diff --git a/src/db/ProxyDatabasePlugin.hxx b/src/db/ProxyDatabasePlugin.hxx new file mode 100644 index 00000000..8e878bac --- /dev/null +++ b/src/db/ProxyDatabasePlugin.hxx @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2003-2012 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef MPD_PROXY_DATABASE_PLUGIN_HXX +#define MPD_PROXY_DATABASE_PLUGIN_HXX + +struct DatabasePlugin; + +extern const DatabasePlugin proxy_db_plugin; + +#endif -- cgit v1.2.3