diff options
Diffstat (limited to '')
-rw-r--r-- | src/DatabasePlaylist.hxx (renamed from src/db/simple_db_plugin.h) | 26 | ||||
-rw-r--r-- | src/DatabasePrint.hxx (renamed from src/db_print.h) | 30 | ||||
-rw-r--r-- | src/DatabaseQueue.hxx (renamed from src/dbUtils.h) | 32 | ||||
-rw-r--r-- | src/DatabaseSelection.hxx (renamed from src/db_selection.h) | 32 | ||||
-rw-r--r-- | src/DatabaseVisitor.hxx (renamed from src/db_visitor.h) | 38 | ||||
-rw-r--r-- | src/db/ProxyDatabasePlugin.cxx | 482 | ||||
-rw-r--r-- | src/db/ProxyDatabasePlugin.hxx | 27 | ||||
-rw-r--r-- | src/db/SimpleDatabasePlugin.cxx (renamed from src/db/simple_db_plugin.c) | 275 | ||||
-rw-r--r-- | src/db/SimpleDatabasePlugin.hxx | 99 | ||||
-rw-r--r-- | src/dbUtils.c | 209 | ||||
-rw-r--r-- | src/db_plugin.h | 156 | ||||
-rw-r--r-- | src/db_print.c | 393 | ||||
-rw-r--r-- | src/db_save.h | 3 | ||||
-rw-r--r-- | src/time_print.h (renamed from src/db_internal.h) | 20 |
14 files changed, 805 insertions, 1017 deletions
diff --git a/src/db/simple_db_plugin.h b/src/DatabasePlaylist.hxx index 511505846..7c6952ffa 100644 --- a/src/db/simple_db_plugin.h +++ b/src/DatabasePlaylist.hxx @@ -17,26 +17,18 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -#ifndef MPD_SIMPLE_DB_PLUGIN_H -#define MPD_SIMPLE_DB_PLUGIN_H +#ifndef MPD_DATABASE_PLAYLIST_HXX +#define MPD_DATABASE_PLAYLIST_HXX -#include <glib.h> -#include <stdbool.h> -#include <time.h> +#include "gcc.h" +#include "gerror.h" -extern const struct db_plugin simple_db_plugin; - -struct db; - -G_GNUC_PURE -struct directory * -simple_db_get_root(struct db *db); +class SongFilter; +gcc_nonnull(1,2) bool -simple_db_save(struct db *db, GError **error_r); - -G_GNUC_PURE -time_t -simple_db_get_mtime(const struct db *db); +search_add_to_playlist(const char *uri, const char *path_utf8, + const SongFilter *filter, + GError **error_r); #endif diff --git a/src/db_print.h b/src/DatabasePrint.hxx index 1b957da18..4aacd9363 100644 --- a/src/db_print.h +++ b/src/DatabasePrint.hxx @@ -21,18 +21,18 @@ #define MPD_DB_PRINT_H #include "gcc.h" +#include "gerror.h" -#include <glib.h> #include <stdbool.h> struct client; -struct locate_item_list; -struct db_selection; +class SongFilter; +struct DatabaseSelection; struct db_visitor; -gcc_nonnull(1,2) +gcc_nonnull(1) bool -db_selection_print(struct client *client, const struct db_selection *selection, +db_selection_print(struct client *client, const DatabaseSelection &selection, bool full, GError **error_r); gcc_nonnull(1,2) @@ -44,28 +44,16 @@ bool printInfoForAllIn(struct client *client, const char *uri_utf8, GError **error_r); -gcc_nonnull(1,2,3) -bool -searchForSongsIn(struct client *client, const char *name, - const struct locate_item_list *criteria, - GError **error_r); - -gcc_nonnull(1,2,3) -bool -findSongsIn(struct client *client, const char *name, - const struct locate_item_list *criteria, - GError **error_r); - -gcc_nonnull(1,2,3) +gcc_nonnull(1,2) bool searchStatsForSongsIn(struct client *client, const char *name, - const struct locate_item_list *criteria, + const SongFilter *filter, GError **error_r); -gcc_nonnull(1,3) +gcc_nonnull(1) bool listAllUniqueTags(struct client *client, int type, - const struct locate_item_list *criteria, + const SongFilter *filter, GError **error_r); #endif diff --git a/src/dbUtils.h b/src/DatabaseQueue.hxx index 706c807fd..21ffe0fb0 100644 --- a/src/dbUtils.h +++ b/src/DatabaseQueue.hxx @@ -17,40 +17,18 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -#ifndef MPD_DB_UTILS_H -#define MPD_DB_UTILS_H +#ifndef MPD_DATABASE_QUEUE_HXX +#define MPD_DATABASE_QUEUE_HXX #include "gcc.h" +#include "gerror.h" -#include <glib.h> -#include <stdbool.h> - -struct locate_item_list; +class SongFilter; struct player_control; gcc_nonnull(1,2) bool -addAllIn(struct player_control *pc, const char *uri, GError **error_r); - -gcc_nonnull(1,2) -bool -addAllInToStoredPlaylist(const char *uri_utf8, const char *path_utf8, - GError **error_r); - -gcc_nonnull(1,2,3) -bool findAddIn(struct player_control *pc, const char *name, - const struct locate_item_list *criteria, GError **error_r); - -gcc_nonnull(1,2,3) -bool -search_add_songs(struct player_control *pc, const char *uri, - const struct locate_item_list *criteria, GError **error_r); - -gcc_nonnull(1,2,3) -bool -search_add_to_playlist(const char *uri, const char *path_utf8, - const struct locate_item_list *criteria, - GError **error_r); + const SongFilter *filter, GError **error_r); #endif diff --git a/src/db_selection.h b/src/DatabaseSelection.hxx index 2cebb4907..3a81c01ec 100644 --- a/src/db_selection.h +++ b/src/DatabaseSelection.hxx @@ -17,17 +17,18 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -#ifndef MPD_DB_SELECTION_H -#define MPD_DB_SELECTION_H +#ifndef MPD_DATABASE_SELECTION_HXX +#define MPD_DATABASE_SELECTION_HXX #include "gcc.h" #include <assert.h> +#include <stddef.h> -struct directory; +class SongFilter; struct song; -struct db_selection { +struct DatabaseSelection { /** * The base URI of the search (UTF-8). Must not begin or end * with a slash. NULL or an empty string searches the whole @@ -39,18 +40,17 @@ struct db_selection { * Recursively search all sub directories? */ bool recursive; -}; -gcc_nonnull(1,2) -static inline void -db_selection_init(struct db_selection *selection, - const char *uri, bool recursive) -{ - assert(selection != NULL); - assert(uri != NULL); - - selection->uri = uri; - selection->recursive = recursive; -} + const SongFilter *filter; + + DatabaseSelection(const char *_uri, bool _recursive, + const SongFilter *_filter=nullptr) + :uri(_uri), recursive(_recursive), filter(_filter) { + assert(uri != NULL); + } + + gcc_pure + bool Match(const song &song) const; +}; #endif diff --git a/src/db_visitor.h b/src/DatabaseVisitor.hxx index 6b90c1868..10f907cef 100644 --- a/src/db_visitor.h +++ b/src/DatabaseVisitor.hxx @@ -17,38 +17,22 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -#ifndef MPD_DB_VISITOR_H -#define MPD_DB_VISITOR_H +#ifndef MPD_DATABASE_VISITOR_HXX +#define MPD_DATABASE_VISITOR_HXX + +#include "gerror.h" + +#include <functional> struct directory; struct song; struct playlist_metadata; -struct db_visitor { - /** - * Visit a directory. Optional method. - * - * @return true to continue the operation, false on error (set error_r) - */ - bool (*directory)(const struct directory *directory, void *ctx, - GError **error_r); - - /** - * Visit a song. Optional method. - * - * @return true to continue the operation, false on error (set error_r) - */ - bool (*song)(struct song *song, void *ctx, GError **error_r); +typedef std::function<bool(const directory &, GError **)> VisitDirectory; +typedef std::function<bool(struct song &, GError **)> VisitSong; +typedef std::function<bool(const playlist_metadata &, const directory &, + GError **)> VisitPlaylist; - /** - * Visit a playlist. Optional method. - * - * @param directory the directory the playlist resides in - * @return true to continue the operation, false on error (set error_r) - */ - bool (*playlist)(const struct playlist_metadata *playlist, - const struct directory *directory, void *ctx, - GError **error_r); -}; +typedef std::function<bool(const char *, GError **)> VisitString; #endif diff --git a/src/db/ProxyDatabasePlugin.cxx b/src/db/ProxyDatabasePlugin.cxx new file mode 100644 index 000000000..d36cebcfd --- /dev/null +++ b/src/db/ProxyDatabasePlugin.cxx @@ -0,0 +1,482 @@ +/* + * 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 "conf.h" +#include "song.h" +#include "tag.h" +} + +#include "directory.h" +#include "playlist_vector.h" + +#undef MPD_DIRECTORY_H +#undef MPD_SONG_H +#include <mpd/client.h> + +#include <cassert> +#include <string> +#include <list> + +class ProxyDatabase : public Database { + std::string host; + unsigned port; + + struct mpd_connection *connection; + struct directory *root; + +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 void ReturnSong(struct song *song) const; + + virtual bool Visit(const DatabaseSelection &selection, + VisitDirectory visit_directory, + VisitSong visit_song, + VisitPlaylist visit_playlist, + GError **error_r) const override; + + virtual bool VisitUniqueTags(const DatabaseSelection &selection, + enum tag_type tag_type, + VisitString visit_string, + GError **error_r) const override; + + virtual bool GetStats(const DatabaseSelection &selection, + DatabaseStats &stats, + 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 constexpr struct { + enum tag_type d; + enum mpd_tag_type s; +} tag_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 } +}; + +G_GNUC_CONST +static enum mpd_tag_type +Convert(enum tag_type tag_type) +{ + for (auto i = tag_table; i->d != TAG_NUM_OF_ITEM_TYPES; ++i) + if (i->d == tag_type) + return i->s; + + return MPD_TAG_COUNT; +} + +static bool +CheckError(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)); + mpd_connection_clear_error(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; + } + + root = directory_new_root(); + + return true; +} + +void +ProxyDatabase::Close() +{ + assert(connection != nullptr); + + directory_free(root); + mpd_connection_free(connection); +} + +static song * +Convert(const struct mpd_song *song); + +struct song * +ProxyDatabase::GetSong(const char *uri, GError **error_r) const +{ + // TODO: implement + // TODO: auto-reconnect + + if (!mpd_send_list_meta(connection, uri)) { + CheckError(connection, error_r); + return nullptr; + } + + struct mpd_song *song = mpd_recv_song(connection); + struct song *song2 = song != nullptr + ? Convert(song) + : nullptr; + mpd_song_free(song); + if (!mpd_response_finish(connection)) { + if (song2 != nullptr) + song_free(song2); + + CheckError(connection, error_r); + return nullptr; + } + + if (song2 == nullptr) + g_set_error(error_r, db_quark(), DB_NOT_FOUND, + "No such song: %s", uri); + + return song2; +} + +void +ProxyDatabase::ReturnSong(struct song *song) const +{ + assert(song != nullptr); + assert(song_in_database(song)); + assert(song_is_detached(song)); + + song_free(song); +} + +static bool +Visit(struct mpd_connection *connection, const char *uri, + bool recursive, VisitDirectory visit_directory, VisitSong visit_song, + VisitPlaylist visit_playlist, GError **error_r); + +static bool +Visit(struct mpd_connection *connection, + bool recursive, const struct mpd_directory *directory, + VisitDirectory visit_directory, VisitSong visit_song, + VisitPlaylist visit_playlist, GError **error_r) +{ + const char *path = mpd_directory_get_path(directory); + + if (visit_directory) { + struct directory *d = + directory_new(path, &detached_root); + bool success = visit_directory(*d, error_r); + directory_free(d); + if (!success) + return false; + } + + if (recursive && + !Visit(connection, path, recursive, + visit_directory, visit_song, visit_playlist, error_r)) + return false; + + return true; +} + +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(const struct mpd_song *song) +{ + struct song *s = song_detached_new(mpd_song_get_uri(song)); + + 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); + + tag_begin_add(tag); + for (auto i = tag_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(const struct mpd_song *song, + VisitSong visit_song, GError **error_r) +{ + if (!visit_song) + return true; + + struct song *s = Convert(song); + bool success = visit_song(*s, error_r); + song_free(s); + + return success; +} + +static bool +Visit(const struct mpd_playlist *playlist, + VisitPlaylist visit_playlist, GError **error_r) +{ + if (!visit_playlist) + return true; + + struct playlist_metadata p; + p.name = g_strdup(mpd_playlist_get_path(playlist)); + p.mtime = mpd_playlist_get_last_modified(playlist); + + bool success = visit_playlist(p, detached_root, error_r); + g_free(p.name); + + return success; +} + +class ProxyEntity { + struct mpd_entity *entity; + +public: + explicit ProxyEntity(struct mpd_entity *_entity) + :entity(_entity) {} + + ProxyEntity(const ProxyEntity &other) = delete; + + ProxyEntity(ProxyEntity &&other) + :entity(other.entity) { + other.entity = nullptr; + } + + ~ProxyEntity() { + if (entity != nullptr) + mpd_entity_free(entity); + } + + ProxyEntity &operator=(const ProxyEntity &other) = delete; + + operator const struct mpd_entity *() const { + return entity; + } +}; + +static std::list<ProxyEntity> +ReceiveEntities(struct mpd_connection *connection) +{ + std::list<ProxyEntity> entities; + struct mpd_entity *entity; + while ((entity = mpd_recv_entity(connection)) != NULL) + entities.push_back(ProxyEntity(entity)); + + mpd_response_finish(connection); + return entities; +} + +static bool +Visit(struct mpd_connection *connection, const char *uri, + bool recursive, VisitDirectory visit_directory, VisitSong visit_song, + VisitPlaylist visit_playlist, GError **error_r) +{ + if (!mpd_send_list_meta(connection, uri)) + return CheckError(connection, error_r); + + std::list<ProxyEntity> entities(ReceiveEntities(connection)); + if (!CheckError(connection, error_r)) + return false; + + for (const auto &entity : entities) { + switch (mpd_entity_get_type(entity)) { + case MPD_ENTITY_TYPE_UNKNOWN: + break; + + case MPD_ENTITY_TYPE_DIRECTORY: + if (!Visit(connection, recursive, + mpd_entity_get_directory(entity), + visit_directory, visit_song, visit_playlist, + error_r)) + return false; + break; + + case MPD_ENTITY_TYPE_SONG: + if (!Visit(mpd_entity_get_song(entity), visit_song, + error_r)) + return false; + break; + + case MPD_ENTITY_TYPE_PLAYLIST: + if (!Visit(mpd_entity_get_playlist(entity), + visit_playlist, error_r)) + return false; + break; + } + } + + 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 + + return ::Visit(connection, selection.uri, selection.recursive, + visit_directory, visit_song, visit_playlist, + error_r); +} + +bool +ProxyDatabase::VisitUniqueTags(const DatabaseSelection &selection, + enum tag_type tag_type, + VisitString visit_string, + GError **error_r) const +{ + enum mpd_tag_type tag_type2 = Convert(tag_type); + if (tag_type2 == MPD_TAG_COUNT) { + g_set_error_literal(error_r, libmpdclient_quark(), 0, + "Unsupported tag"); + return false; + } + + if (!mpd_search_db_tags(connection, tag_type2)) + return CheckError(connection, error_r); + + // TODO: match + (void)selection; + + if (!mpd_search_commit(connection)) + return CheckError(connection, error_r); + + bool result = true; + + struct mpd_pair *pair; + while (result && + (pair = mpd_recv_pair_tag(connection, tag_type2)) != nullptr) { + result = visit_string(pair->value, error_r); + mpd_return_pair(connection, pair); + } + + return mpd_response_finish(connection) && + CheckError(connection, error_r) && + result; +} + +bool +ProxyDatabase::GetStats(const DatabaseSelection &selection, + DatabaseStats &stats, GError **error_r) const +{ + // TODO: match + (void)selection; + + struct mpd_stats *stats2 = + mpd_run_stats(connection); + if (stats2 == nullptr) + return CheckError(connection, error_r); + + stats.song_count = mpd_stats_get_number_of_songs(stats2); + stats.total_duration = mpd_stats_get_db_play_time(stats2); + stats.artist_count = mpd_stats_get_number_of_artists(stats2); + stats.album_count = mpd_stats_get_number_of_albums(stats2); + mpd_stats_free(stats2); + + return true; +} + +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 000000000..8e878baca --- /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 diff --git a/src/db/simple_db_plugin.c b/src/db/SimpleDatabasePlugin.cxx index 697e8da5f..54441f4cd 100644 --- a/src/db/simple_db_plugin.c +++ b/src/db/SimpleDatabasePlugin.cxx @@ -18,14 +18,18 @@ */ #include "config.h" -#include "simple_db_plugin.h" -#include "db_internal.h" +#include "SimpleDatabasePlugin.hxx" +#include "DatabaseSelection.hxx" +#include "DatabaseHelpers.hxx" +#include "SongFilter.hxx" + +extern "C" { #include "db_error.h" -#include "db_selection.h" -#include "db_visitor.h" #include "db_save.h" #include "db_lock.h" #include "conf.h" +} + #include "directory.h" #include <sys/types.h> @@ -33,16 +37,6 @@ #include <unistd.h> #include <errno.h> -struct simple_db { - struct db base; - - char *path; - - struct directory *root; - - time_t mtime; -}; - G_GNUC_CONST static inline GQuark simple_db_quark(void) @@ -50,63 +44,50 @@ simple_db_quark(void) return g_quark_from_static_string("simple_db"); } -G_GNUC_PURE -static const struct directory * -simple_db_lookup_directory(const struct simple_db *db, const char *uri) +Database * +SimpleDatabase::Create(const struct config_param *param, GError **error_r) { - assert(db != NULL); - assert(db->root != NULL); - assert(uri != NULL); + SimpleDatabase *db = new SimpleDatabase(); + if (!db->Configure(param, error_r)) { + delete db; + db = NULL; + } - db_lock(); - struct directory *directory = - directory_lookup_directory(db->root, uri); - db_unlock(); - return directory; + return db; } -static struct db * -simple_db_init(const struct config_param *param, GError **error_r) +bool +SimpleDatabase::Configure(const struct config_param *param, GError **error_r) { - struct simple_db *db = g_malloc(sizeof(*db)); - db_base_init(&db->base, &simple_db_plugin); - GError *error = NULL; - db->path = config_dup_block_path(param, "path", &error); - if (db->path == NULL) { - g_free(db); + + char *_path = config_dup_block_path(param, "path", &error); + if (_path == NULL) { if (error != NULL) g_propagate_error(error_r, error); else g_set_error(error_r, simple_db_quark(), 0, "No \"path\" parameter specified"); - return NULL; + return false; } - return &db->base; -} - -static void -simple_db_finish(struct db *_db) -{ - struct simple_db *db = (struct simple_db *)_db; + path = _path; + free(_path); - g_free(db->path); - g_free(db); + return true; } -static bool -simple_db_check(struct simple_db *db, GError **error_r) +bool +SimpleDatabase::Check(GError **error_r) const { - assert(db != NULL); - assert(db->path != NULL); + assert(!path.empty()); /* Check if the file exists */ - if (access(db->path, F_OK)) { + if (access(path.c_str(), F_OK)) { /* If the file doesn't exist, we can't check if we can write * it, so we are going to try to get the directory path, and * see if we can write a file in that */ - char *dirPath = g_path_get_dirname(db->path); + char *dirPath = g_path_get_dirname(path.c_str()); /* Check that the parent part of the path is a directory */ struct stat st; @@ -115,7 +96,7 @@ simple_db_check(struct simple_db *db, GError **error_r) g_set_error(error_r, simple_db_quark(), errno, "Couldn't stat parent directory of db file " "\"%s\": %s", - db->path, g_strerror(errno)); + path.c_str(), g_strerror(errno)); return false; } @@ -124,7 +105,7 @@ simple_db_check(struct simple_db *db, GError **error_r) g_set_error(error_r, simple_db_quark(), 0, "Couldn't create db file \"%s\" because the " "parent path is not a directory", - db->path); + path.c_str()); return false; } @@ -144,47 +125,46 @@ simple_db_check(struct simple_db *db, GError **error_r) /* Path exists, now check if it's a regular file */ struct stat st; - if (stat(db->path, &st) < 0) { + if (stat(path.c_str(), &st) < 0) { g_set_error(error_r, simple_db_quark(), errno, "Couldn't stat db file \"%s\": %s", - db->path, g_strerror(errno)); + path.c_str(), g_strerror(errno)); return false; } if (!S_ISREG(st.st_mode)) { g_set_error(error_r, simple_db_quark(), 0, "db file \"%s\" is not a regular file", - db->path); + path.c_str()); return false; } /* And check that we can write to it */ - if (access(db->path, R_OK | W_OK)) { + if (access(path.c_str(), R_OK | W_OK)) { g_set_error(error_r, simple_db_quark(), errno, "Can't open db file \"%s\" for reading/writing: %s", - db->path, g_strerror(errno)); + path.c_str(), g_strerror(errno)); return false; } return true; } -static bool -simple_db_load(struct simple_db *db, GError **error_r) +bool +SimpleDatabase::Load(GError **error_r) { - assert(db != NULL); - assert(db->path != NULL); - assert(db->root != NULL); + assert(!path.empty()); + assert(root != NULL); - FILE *fp = fopen(db->path, "r"); + FILE *fp = fopen(path.c_str(), "r"); if (fp == NULL) { g_set_error(error_r, simple_db_quark(), errno, "Failed to open database file \"%s\": %s", - db->path, g_strerror(errno)); + path.c_str(), g_strerror(errno)); return false; } - if (!db_load_internal(fp, db->root, error_r)) { + if (!db_load_internal(fp, root, error_r)) { fclose(fp); return false; } @@ -192,141 +172,164 @@ simple_db_load(struct simple_db *db, GError **error_r) fclose(fp); struct stat st; - if (stat(db->path, &st) == 0) - db->mtime = st.st_mtime; + if (stat(path.c_str(), &st) == 0) + mtime = st.st_mtime; return true; } -static bool -simple_db_open(struct db *_db, G_GNUC_UNUSED GError **error_r) +bool +SimpleDatabase::Open(GError **error_r) { - struct simple_db *db = (struct simple_db *)_db; + root = directory_new_root(); + mtime = 0; - db->root = directory_new_root(); - db->mtime = 0; +#ifndef NDEBUG + borrowed_song_count = 0; +#endif GError *error = NULL; - if (!simple_db_load(db, &error)) { - directory_free(db->root); + if (!Load(&error)) { + directory_free(root); g_warning("Failed to load database: %s", error->message); g_error_free(error); - if (!simple_db_check(db, error_r)) + if (!Check(error_r)) return false; - db->root = directory_new_root(); + root = directory_new_root(); } return true; } -static void -simple_db_close(struct db *_db) +void +SimpleDatabase::Close() { - struct simple_db *db = (struct simple_db *)_db; - - assert(db->root != NULL); + assert(root != NULL); + assert(borrowed_song_count == 0); - directory_free(db->root); + directory_free(root); } -static struct song * -simple_db_get_song(struct db *_db, const char *uri, GError **error_r) +struct song * +SimpleDatabase::GetSong(const char *uri, GError **error_r) const { - struct simple_db *db = (struct simple_db *)_db; - - assert(db->root != NULL); + assert(root != NULL); db_lock(); - struct song *song = directory_lookup_song(db->root, uri); + struct song *song = directory_lookup_song(root, uri); db_unlock(); if (song == NULL) g_set_error(error_r, db_quark(), DB_NOT_FOUND, "No such song: %s", uri); +#ifndef NDEBUG + else + ++const_cast<unsigned &>(borrowed_song_count); +#endif return song; } -static bool -simple_db_visit(struct db *_db, const struct db_selection *selection, - const struct db_visitor *visitor, void *ctx, - GError **error_r) +void +SimpleDatabase::ReturnSong(gcc_unused struct song *song) const { - const struct simple_db *db = (const struct simple_db *)_db; - const struct directory *directory = - simple_db_lookup_directory(db, selection->uri); + assert(song != nullptr); + +#ifndef NDEBUG + assert(borrowed_song_count > 0); + --const_cast<unsigned &>(borrowed_song_count); +#endif +} + +G_GNUC_PURE +const struct directory * +SimpleDatabase::LookupDirectory(const char *uri) const +{ + assert(root != NULL); + assert(uri != NULL); + + db_lock(); + struct directory *directory = + directory_lookup_directory(root, uri); + db_unlock(); + return directory; +} + +bool +SimpleDatabase::Visit(const DatabaseSelection &selection, + VisitDirectory visit_directory, + VisitSong visit_song, + VisitPlaylist visit_playlist, + GError **error_r) const +{ + const struct directory *directory = LookupDirectory(selection.uri); if (directory == NULL) { struct song *song; - if (visitor->song != NULL && - (song = simple_db_get_song(_db, selection->uri, NULL)) != NULL) - return visitor->song(song, ctx, error_r); + if (visit_song && + (song = GetSong(selection.uri, NULL)) != NULL && + selection.Match(*song)) + return visit_song(*song, error_r); g_set_error(error_r, db_quark(), DB_NOT_FOUND, "No such directory"); return false; } - if (selection->recursive && visitor->directory != NULL && - !visitor->directory(directory, ctx, error_r)) + if (selection.recursive && visit_directory && + !visit_directory(*directory, error_r)) return false; db_lock(); - bool ret = directory_walk(directory, selection->recursive, - visitor, ctx, error_r); + bool ret = directory->Walk(selection.recursive, selection.filter, + visit_directory, visit_song, visit_playlist, + error_r); db_unlock(); return ret; } -const struct db_plugin simple_db_plugin = { - .name = "simple", - .init = simple_db_init, - .finish = simple_db_finish, - .open = simple_db_open, - .close = simple_db_close, - .get_song = simple_db_get_song, - .visit = simple_db_visit, -}; - -struct directory * -simple_db_get_root(struct db *_db) +bool +SimpleDatabase::VisitUniqueTags(const DatabaseSelection &selection, + enum tag_type tag_type, + VisitString visit_string, + GError **error_r) const { - struct simple_db *db = (struct simple_db *)_db; - - assert(db != NULL); - assert(db->root != NULL); - - return db->root; + return ::VisitUniqueTags(*this, selection, tag_type, visit_string, + error_r); } bool -simple_db_save(struct db *_db, GError **error_r) +SimpleDatabase::GetStats(const DatabaseSelection &selection, + DatabaseStats &stats, GError **error_r) const { - struct simple_db *db = (struct simple_db *)_db; - struct directory *music_root = db->root; + return ::GetStats(*this, selection, stats, error_r); +} +bool +SimpleDatabase::Save(GError **error_r) +{ db_lock(); g_debug("removing empty directories from DB"); - directory_prune_empty(music_root); + directory_prune_empty(root); g_debug("sorting DB"); - directory_sort(music_root); + directory_sort(root); db_unlock(); g_debug("writing DB"); - FILE *fp = fopen(db->path, "w"); + FILE *fp = fopen(path.c_str(), "w"); if (!fp) { g_set_error(error_r, simple_db_quark(), errno, "unable to write to db file \"%s\": %s", - db->path, g_strerror(errno)); + path.c_str(), g_strerror(errno)); return false; } - db_save_internal(fp, music_root); + db_save_internal(fp, root); if (ferror(fp)) { g_set_error(error_r, simple_db_quark(), errno, @@ -339,19 +342,13 @@ simple_db_save(struct db *_db, GError **error_r) fclose(fp); struct stat st; - if (stat(db->path, &st) == 0) - db->mtime = st.st_mtime; + if (stat(path.c_str(), &st) == 0) + mtime = st.st_mtime; return true; } -time_t -simple_db_get_mtime(const struct db *_db) -{ - const struct simple_db *db = (const struct simple_db *)_db; - - assert(db != NULL); - assert(db->root != NULL); - - return db->mtime; -} +const DatabasePlugin simple_db_plugin = { + "simple", + SimpleDatabase::Create, +}; diff --git a/src/db/SimpleDatabasePlugin.hxx b/src/db/SimpleDatabasePlugin.hxx new file mode 100644 index 000000000..2ea5c4925 --- /dev/null +++ b/src/db/SimpleDatabasePlugin.hxx @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2003-2011 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef MPD_SIMPLE_DATABASE_PLUGIN_HXX +#define MPD_SIMPLE_DATABASE_PLUGIN_HXX + +#include "DatabasePlugin.hxx" +#include "gcc.h" + +#include <cassert> +#include <string> + +#include <stdbool.h> +#include <time.h> + +struct directory; + +class SimpleDatabase : public Database { + std::string path; + + struct directory *root; + + time_t mtime; + +#ifndef NDEBUG + unsigned borrowed_song_count; +#endif + +public: + gcc_pure + struct directory *GetRoot() { + assert(root != NULL); + + return root; + } + + bool Save(GError **error_r); + + gcc_pure + time_t GetLastModified() const { + return mtime; + } + + 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 void ReturnSong(struct song *song) const; + + virtual bool Visit(const DatabaseSelection &selection, + VisitDirectory visit_directory, + VisitSong visit_song, + VisitPlaylist visit_playlist, + GError **error_r) const override; + + virtual bool VisitUniqueTags(const DatabaseSelection &selection, + enum tag_type tag_type, + VisitString visit_string, + GError **error_r) const override; + + virtual bool GetStats(const DatabaseSelection &selection, + DatabaseStats &stats, + GError **error_r) const override; + +protected: + bool Configure(const struct config_param *param, GError **error_r); + + gcc_pure + bool Check(GError **error_r) const; + + bool Load(GError **error_r); + + gcc_pure + const struct directory *LookupDirectory(const char *uri) const; +}; + +extern const DatabasePlugin simple_db_plugin; + +#endif diff --git a/src/dbUtils.c b/src/dbUtils.c deleted file mode 100644 index c212d9f9c..000000000 --- a/src/dbUtils.c +++ /dev/null @@ -1,209 +0,0 @@ -/* - * Copyright (C) 2003-2011 The Music Player Daemon Project - * http://www.musicpd.org - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include "config.h" -#include "dbUtils.h" -#include "locate.h" -#include "database.h" -#include "db_visitor.h" -#include "playlist.h" -#include "stored_playlist.h" - -#include <glib.h> - -static bool -add_to_queue_song(struct song *song, void *ctx, GError **error_r) -{ - struct player_control *pc = ctx; - - enum playlist_result result = - playlist_append_song(&g_playlist, pc, song, NULL); - if (result != PLAYLIST_RESULT_SUCCESS) { - g_set_error(error_r, playlist_quark(), result, - "Playlist error"); - return false; - } - - return true; -} - -static const struct db_visitor add_to_queue_visitor = { - .song = add_to_queue_song, -}; - -bool -addAllIn(struct player_control *pc, const char *uri, GError **error_r) -{ - return db_walk(uri, &add_to_queue_visitor, pc, error_r); -} - -struct add_data { - const char *path; -}; - -static bool -add_to_spl_song(struct song *song, void *ctx, GError **error_r) -{ - struct add_data *data = ctx; - - if (!spl_append_song(data->path, song, error_r)) - return false; - - return true; -} - -static const struct db_visitor add_to_spl_visitor = { - .song = add_to_spl_song, -}; - -bool -addAllInToStoredPlaylist(const char *uri_utf8, const char *path_utf8, - GError **error_r) -{ - struct add_data data = { - .path = path_utf8, - }; - - return db_walk(uri_utf8, &add_to_spl_visitor, &data, error_r); -} - -struct find_add_data { - struct player_control *pc; - const struct locate_item_list *criteria; -}; - -static bool -find_add_song(struct song *song, void *ctx, GError **error_r) -{ - struct find_add_data *data = ctx; - - if (!locate_song_match(song, data->criteria)) - return true; - - enum playlist_result result = - playlist_append_song(&g_playlist, data->pc, - song, NULL); - if (result != PLAYLIST_RESULT_SUCCESS) { - g_set_error(error_r, playlist_quark(), result, - "Playlist error"); - return false; - } - - return true; -} - -static const struct db_visitor find_add_visitor = { - .song = find_add_song, -}; - -bool -findAddIn(struct player_control *pc, const char *name, - const struct locate_item_list *criteria, GError **error_r) -{ - struct find_add_data data; - data.pc = pc; - data.criteria = criteria; - - return db_walk(name, &find_add_visitor, &data, error_r); -} - -static bool -searchadd_visitor_song(struct song *song, void *_data, GError **error_r) -{ - struct find_add_data *data = _data; - - if (!locate_song_search(song, data->criteria)) - return true; - - enum playlist_result result = - playlist_append_song(&g_playlist, data->pc, song, NULL); - if (result != PLAYLIST_RESULT_SUCCESS) { - g_set_error(error_r, playlist_quark(), result, - "Playlist error"); - return false; - } - - return true; -} - -static const struct db_visitor searchadd_visitor = { - .song = searchadd_visitor_song, -}; - -bool -search_add_songs(struct player_control *pc, const char *uri, - const struct locate_item_list *criteria, - GError **error_r) -{ - struct locate_item_list *new_list = - locate_item_list_casefold(criteria); - struct find_add_data data = { - .pc = pc, - .criteria = new_list, - }; - - bool success = db_walk(uri, &searchadd_visitor, &data, error_r); - - locate_item_list_free(new_list); - - return success; -} - -struct search_add_playlist_data { - const char *playlist; - const struct locate_item_list *criteria; -}; - -static bool -searchaddpl_visitor_song(struct song *song, void *_data, - G_GNUC_UNUSED GError **error_r) -{ - struct search_add_playlist_data *data = _data; - - if (!locate_song_search(song, data->criteria)) - return true; - - if (!spl_append_song(data->playlist, song, error_r)) - return false; - - return true; -} - -static const struct db_visitor searchaddpl_visitor = { - .song = searchaddpl_visitor_song, -}; - -bool -search_add_to_playlist(const char *uri, const char *path_utf8, - const struct locate_item_list *criteria, - GError **error_r) -{ - struct locate_item_list *new_list - = locate_item_list_casefold(criteria); - struct search_add_playlist_data data = { - .playlist = path_utf8, - .criteria = new_list, - }; - - bool success = db_walk(uri, &searchaddpl_visitor, &data, error_r); - - locate_item_list_free(new_list); - - return success; -} diff --git a/src/db_plugin.h b/src/db_plugin.h deleted file mode 100644 index 1c7e14ede..000000000 --- a/src/db_plugin.h +++ /dev/null @@ -1,156 +0,0 @@ -/* - * Copyright (C) 2003-2011 The Music Player Daemon Project - * http://www.musicpd.org - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -/** \file - * - * This header declares the db_plugin class. It describes a - * plugin API for databases of song metadata. - */ - -#ifndef MPD_DB_PLUGIN_H -#define MPD_DB_PLUGIN_H - -#include <glib.h> -#include <assert.h> -#include <stdbool.h> - -struct config_param; -struct db_selection; -struct db_visitor; - -struct db { - const struct db_plugin *plugin; -}; - -struct db_plugin { - const char *name; - - /** - * Allocates and configures a database. - */ - struct db *(*init)(const struct config_param *param, GError **error_r); - - /** - * Free instance data. - */ - void (*finish)(struct db *db); - - /** - * Open the database. Read it into memory if applicable. - */ - bool (*open)(struct db *db, GError **error_r); - - /** - * Close the database, free allocated memory. - */ - void (*close)(struct db *db); - - /** - * Look up a song (including tag data) in the database. - * - * @param the URI of the song within the music directory - * (UTF-8) - */ - struct song *(*get_song)(struct db *db, const char *uri, - GError **error_r); - - /** - * Visit the selected entities. - */ - bool (*visit)(struct db *db, const struct db_selection *selection, - const struct db_visitor *visitor, void *ctx, - GError **error_r); -}; - -G_GNUC_MALLOC -static inline struct db * -db_plugin_new(const struct db_plugin *plugin, const struct config_param *param, - GError **error_r) -{ - assert(plugin != NULL); - assert(plugin->init != NULL); - assert(plugin->finish != NULL); - assert(plugin->get_song != NULL); - assert(plugin->visit != NULL); - assert(error_r == NULL || *error_r == NULL); - - struct db *db = plugin->init(param, error_r); - assert(db == NULL || db->plugin == plugin); - assert(db != NULL || error_r == NULL || *error_r != NULL); - - return db; -} - -static inline void -db_plugin_free(struct db *db) -{ - assert(db != NULL); - assert(db->plugin != NULL); - assert(db->plugin->finish != NULL); - - db->plugin->finish(db); -} - -static inline bool -db_plugin_open(struct db *db, GError **error_r) -{ - assert(db != NULL); - assert(db->plugin != NULL); - - return db->plugin->open != NULL - ? db->plugin->open(db, error_r) - : true; -} - -static inline void -db_plugin_close(struct db *db) -{ - assert(db != NULL); - assert(db->plugin != NULL); - - if (db->plugin->close != NULL) - db->plugin->close(db); -} - -static inline struct song * -db_plugin_get_song(struct db *db, const char *uri, GError **error_r) -{ - assert(db != NULL); - assert(db->plugin != NULL); - assert(db->plugin->get_song != NULL); - assert(uri != NULL); - - return db->plugin->get_song(db, uri, error_r); -} - -static inline bool -db_plugin_visit(struct db *db, const struct db_selection *selection, - const struct db_visitor *visitor, void *ctx, - GError **error_r) -{ - assert(db != NULL); - assert(db->plugin != NULL); - assert(selection != NULL); - assert(visitor != NULL); - assert(error_r == NULL || *error_r == NULL); - - return db->plugin->visit(db, selection, visitor, ctx, error_r); -} - -#endif diff --git a/src/db_print.c b/src/db_print.c deleted file mode 100644 index 4d7e3f5ef..000000000 --- a/src/db_print.c +++ /dev/null @@ -1,393 +0,0 @@ -/* - * Copyright (C) 2003-2011 The Music Player Daemon Project - * http://www.musicpd.org - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include "config.h" -#include "db_print.h" -#include "db_selection.h" -#include "db_visitor.h" -#include "locate.h" -#include "directory.h" -#include "database.h" -#include "client.h" -#include "song.h" -#include "song_print.h" -#include "playlist_vector.h" -#include "tag.h" -#include "strset.h" - -#include <glib.h> - -typedef struct _ListCommandItem { - int8_t tagType; - const struct locate_item_list *criteria; -} ListCommandItem; - -typedef struct _SearchStats { - const struct locate_item_list *criteria; - int numberOfSongs; - unsigned long playTime; -} SearchStats; - -static bool -print_visitor_directory(const struct directory *directory, void *data, - G_GNUC_UNUSED GError **error_r) -{ - struct client *client = data; - - if (!directory_is_root(directory)) - client_printf(client, "directory: %s\n", directory_get_path(directory)); - - return true; -} - -static void -print_playlist_in_directory(struct client *client, - const struct directory *directory, - const char *name_utf8) -{ - if (directory_is_root(directory)) - client_printf(client, "playlist: %s\n", name_utf8); - else - client_printf(client, "playlist: %s/%s\n", - directory_get_path(directory), name_utf8); -} - -static bool -print_visitor_song(struct song *song, void *data, - G_GNUC_UNUSED GError **error_r) -{ - assert(song != NULL); - assert(song->parent != NULL); - - struct client *client = data; - song_print_uri(client, song); - - if (song->tag != NULL && song->tag->has_playlist) - /* this song file has an embedded CUE sheet */ - print_playlist_in_directory(client, song->parent, - song->uri); - - return true; -} - -static bool -print_visitor_song_info(struct song *song, void *data, - G_GNUC_UNUSED GError **error_r) -{ - assert(song != NULL); - assert(song->parent != NULL); - - struct client *client = data; - song_print_info(client, song); - - if (song->tag != NULL && song->tag->has_playlist) - /* this song file has an embedded CUE sheet */ - print_playlist_in_directory(client, song->parent, - song->uri); - - return true; -} - -static bool -print_visitor_playlist(const struct playlist_metadata *playlist, - const struct directory *directory, void *ctx, - G_GNUC_UNUSED GError **error_r) -{ - struct client *client = ctx; - print_playlist_in_directory(client, directory, playlist->name); - return true; -} - -static bool -print_visitor_playlist_info(const struct playlist_metadata *playlist, - const struct directory *directory, - void *ctx, G_GNUC_UNUSED GError **error_r) -{ - struct client *client = ctx; - print_playlist_in_directory(client, directory, playlist->name); - -#ifndef G_OS_WIN32 - struct tm tm; -#endif - char timestamp[32]; - time_t t = playlist->mtime; - strftime(timestamp, sizeof(timestamp), -#ifdef G_OS_WIN32 - "%Y-%m-%dT%H:%M:%SZ", - gmtime(&t) -#else - "%FT%TZ", - gmtime_r(&t, &tm) -#endif - ); - client_printf(client, "Last-Modified: %s\n", timestamp); - - return true; -} - -static const struct db_visitor print_visitor = { - .directory = print_visitor_directory, - .song = print_visitor_song, - .playlist = print_visitor_playlist, -}; - -static const struct db_visitor print_info_visitor = { - .directory = print_visitor_directory, - .song = print_visitor_song_info, - .playlist = print_visitor_playlist_info, -}; - -bool -db_selection_print(struct client *client, const struct db_selection *selection, - bool full, GError **error_r) -{ - return db_visit(selection, full ? &print_info_visitor : &print_visitor, - client, error_r); -} - -struct search_data { - struct client *client; - const struct locate_item_list *criteria; -}; - -static bool -search_visitor_song(struct song *song, void *_data, - G_GNUC_UNUSED GError **error_r) -{ - struct search_data *data = _data; - - if (locate_song_search(song, data->criteria)) - song_print_info(data->client, song); - - return true; -} - -static const struct db_visitor search_visitor = { - .song = search_visitor_song, -}; - -bool -searchForSongsIn(struct client *client, const char *name, - const struct locate_item_list *criteria, - GError **error_r) -{ - struct locate_item_list *new_list - = locate_item_list_casefold(criteria); - struct search_data data; - - data.client = client; - data.criteria = new_list; - - bool success = db_walk(name, &search_visitor, &data, error_r); - - locate_item_list_free(new_list); - - return success; -} - -static bool -find_visitor_song(struct song *song, void *_data, - G_GNUC_UNUSED GError **error_r) -{ - struct search_data *data = _data; - - if (locate_song_match(song, data->criteria)) - song_print_info(data->client, song); - - return true; -} - -static const struct db_visitor find_visitor = { - .song = find_visitor_song, -}; - -bool -findSongsIn(struct client *client, const char *name, - const struct locate_item_list *criteria, - GError **error_r) -{ - struct search_data data; - - data.client = client; - data.criteria = criteria; - - return db_walk(name, &find_visitor, &data, error_r); -} - -static void printSearchStats(struct client *client, SearchStats *stats) -{ - client_printf(client, "songs: %i\n", stats->numberOfSongs); - client_printf(client, "playtime: %li\n", stats->playTime); -} - -static bool -stats_visitor_song(struct song *song, void *data, - G_GNUC_UNUSED GError **error_r) -{ - SearchStats *stats = data; - - if (locate_song_match(song, stats->criteria)) { - stats->numberOfSongs++; - stats->playTime += song_get_duration(song); - } - - return true; -} - -static const struct db_visitor stats_visitor = { - .song = stats_visitor_song, -}; - -bool -searchStatsForSongsIn(struct client *client, const char *name, - const struct locate_item_list *criteria, - GError **error_r) -{ - SearchStats stats; - - stats.criteria = criteria; - stats.numberOfSongs = 0; - stats.playTime = 0; - - if (!db_walk(name, &stats_visitor, &stats, error_r)) - return false; - - printSearchStats(client, &stats); - return true; -} - -bool -printAllIn(struct client *client, const char *uri_utf8, GError **error_r) -{ - struct db_selection selection; - db_selection_init(&selection, uri_utf8, true); - return db_selection_print(client, &selection, false, error_r); -} - -bool -printInfoForAllIn(struct client *client, const char *uri_utf8, - GError **error_r) -{ - struct db_selection selection; - db_selection_init(&selection, uri_utf8, true); - return db_selection_print(client, &selection, true, error_r); -} - -static ListCommandItem * -newListCommandItem(int tagType, const struct locate_item_list *criteria) -{ - ListCommandItem *item = g_new(ListCommandItem, 1); - - item->tagType = tagType; - item->criteria = criteria; - - return item; -} - -static void freeListCommandItem(ListCommandItem * item) -{ - g_free(item); -} - -static void -visitTag(struct client *client, struct strset *set, - struct song *song, enum tag_type tagType) -{ - struct tag *tag = song->tag; - bool found = false; - - if (tagType == LOCATE_TAG_FILE_TYPE) { - song_print_uri(client, song); - return; - } - - if (!tag) - return; - - for (unsigned i = 0; i < tag->num_items; i++) { - if (tag->items[i]->type == tagType) { - strset_add(set, tag->items[i]->value); - found = true; - } - } - - if (!found) - strset_add(set, ""); -} - -struct list_tags_data { - struct client *client; - ListCommandItem *item; - struct strset *set; -}; - -static bool -unique_tags_visitor_song(struct song *song, void *_data, - G_GNUC_UNUSED GError **error_r) -{ - struct list_tags_data *data = _data; - ListCommandItem *item = data->item; - - if (locate_song_match(song, item->criteria)) - visitTag(data->client, data->set, song, item->tagType); - - return true; -} - -static const struct db_visitor unique_tags_visitor = { - .song = unique_tags_visitor_song, -}; - -bool -listAllUniqueTags(struct client *client, int type, - const struct locate_item_list *criteria, - GError **error_r) -{ - ListCommandItem *item = newListCommandItem(type, criteria); - struct list_tags_data data = { - .client = client, - .item = item, - }; - - if (type >= 0 && type <= TAG_NUM_OF_ITEM_TYPES) { - data.set = strset_new(); - } - - if (!db_walk("", &unique_tags_visitor, &data, error_r)) { - freeListCommandItem(item); - return false; - } - - if (type >= 0 && type <= TAG_NUM_OF_ITEM_TYPES) { - const char *value; - - strset_rewind(data.set); - - while ((value = strset_next(data.set)) != NULL) - client_printf(client, "%s: %s\n", - tag_item_names[type], - value); - - strset_free(data.set); - } - - freeListCommandItem(item); - - return true; -} diff --git a/src/db_save.h b/src/db_save.h index e760ec881..d232d7331 100644 --- a/src/db_save.h +++ b/src/db_save.h @@ -20,7 +20,8 @@ #ifndef MPD_DB_SAVE_H #define MPD_DB_SAVE_H -#include <glib.h> +#include "gerror.h" + #include <stdbool.h> #include <stdio.h> diff --git a/src/db_internal.h b/src/time_print.h index a33351524..7eff446b2 100644 --- a/src/db_internal.h +++ b/src/time_print.h @@ -17,19 +17,17 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -#ifndef MPD_DB_INTERNAL_H -#define MPD_DB_INTERNAL_H +#ifndef MPD_TIME_PRINT_H +#define MPD_TIME_PRINT_H -#include "db_plugin.h" +#include <time.h> -#include <assert.h> +struct client; -static inline void -db_base_init(struct db *db, const struct db_plugin *plugin) -{ - assert(plugin != NULL); - - db->plugin = plugin; -} +/** + * Write a line with a time stamp to the client. + */ +void +time_print(struct client *client, const char *name, time_t t); #endif |