summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--commands.cpp45
-rw-r--r--commands.h4
-rw-r--r--music.cpp70
-rw-r--r--music.h3
4 files changed, 97 insertions, 25 deletions
diff --git a/commands.cpp b/commands.cpp
index 84c1e5f..76ce2ff 100644
--- a/commands.cpp
+++ b/commands.cpp
@@ -3,9 +3,13 @@
#include <boost/format.hpp>
#include <boost/algorithm/string/join.hpp>
#include <boost/bind.hpp>
+#include <boost/spirit/include/qi.hpp>
+#include <boost/fusion/include/std_pair.hpp>
#include <iostream>
+namespace qi = boost::spirit::qi;
+
std::vector<std::string> Commands::ls() {
if(args.size() != 2) {
throw CommandException("usage: ls DIR");
@@ -29,24 +33,39 @@ std::vector<std::string> Commands::ls() {
return result;
}
+typedef std::map<std::string, std::string> stringmap;
+
+static bool find_parse(std::string str, stringmap& foo) {
+ typedef std::string::const_iterator Iterator;
+
+ Iterator begin = str.begin();
+ Iterator end = str.end();
+
+ return qi::parse(begin, end,
+ // pairs wrapped in "quotes"
+ (('"' >> +(qi::char_ - ':') >> ':' >> +(qi::char_ - '"') >> '"') |
+ // simple key:value pairs
+ (+(qi::char_ - ':' - ' ') >> ':' >> +(qi::char_ - ' ')))
+ // skip spaces between pairs
+ % +qi::char_(' '),
+ foo) && begin == end;
+}
+
std::vector<std::string> Commands::find() {
- if(args.size() != 3) {
- throw CommandException("usage: find TYPE SEARCH");
+ if(args.size() < 2) {
+ throw CommandException("usage: find SEARCH");
}
- FindFunction ff;
- if(find_handlers.find(args[1]) != find_handlers.end()) {
- ff = find_handlers[args[1]];
+ stringmap find_args;
+ args.erase(args.begin());
+ std::string search = boost::algorithm::join(args, " ");
+ std::vector<MusicListing::p> ml;
+ if(find_parse(search, find_args)) {
+ ml = music::find(find_args);
} else {
- std::vector<std::string> types;
- for(std::map<std::string, FindFunction>::iterator it = find_handlers.begin(); it != find_handlers.end(); it++) {
- types.push_back(it->first);
- }
- std::string s = boost::str(boost::format("unknown search type, must be one of %s") % boost::algorithm::join(types, ", "));
- throw CommandException(s.c_str());
+ ml = music::find(search);
}
- std::vector<MusicListing::p> ml = ff(args[2]);
if(!ml.size()) {
throw CommandException("no results");
}
@@ -56,7 +75,6 @@ std::vector<std::string> Commands::find() {
result.push_back((*it)->path.string());
}
-
return result;
}
@@ -74,7 +92,6 @@ std::vector<std::string> Commands::update() {
Commands::Commands(boost::asio::io_service& io_service_, std::vector<std::string>& args_) : io_service(io_service_), args(args_) {
handlers["ls"] = &Commands::ls;
handlers["find"] = &Commands::find;
- find_handlers["artist"] = music::find_artist;
handlers["update"] = &Commands::update;
}
diff --git a/commands.h b/commands.h
index 78ecf48..3c351cf 100644
--- a/commands.h
+++ b/commands.h
@@ -24,10 +24,6 @@ class Commands {
typedef boost::function<std::vector<std::string> (Commands*)> Handler;
std::map<std::string, Handler> handlers;
- typedef boost::function<std::vector<MusicListing::p> (const std::string artist)> FindFunction;
- //! Handlers for the find command.
- std::map<std::string, FindFunction> find_handlers;
-
boost::asio::io_service& io_service;
//! Command arguments.
std::vector<std::string>& args;
diff --git a/music.cpp b/music.cpp
index eb16226..7356274 100644
--- a/music.cpp
+++ b/music.cpp
@@ -24,7 +24,7 @@ void music::init(std::string root) {
root_directory = root;
}
-/** Fetches a MusicListing object from the given path.
+/** Fetches a MusicListing object from the given \p path.
* Prefixes the given path with the music root directory.
* This can be either a track (file) or a directory.
*/
@@ -52,7 +52,7 @@ MusicListing::p music::get(const HTTP::Connection::PathList& path) {
return MusicListing::p();
}
-/** Fetches a MusicListing object from the given path.
+/** Fetches a MusicListing object from the given \p path.
* Splits the given string and calls the above function.
*/
MusicListing::p music::get(const std::string& path) {
@@ -74,21 +74,76 @@ MusicDirectory::p music::get_directory(const std::string& path) {
return dir;
}
-std::vector<MusicListing::p> music::find_artist(const std::string artist) {
+/** Find tracks in the database.
+ * Does a search on specific fields given by \p search.
+ */
+std::vector<MusicListing::p> music::find(std::map<std::string, std::string> search) {
+ soci::session sql(config::vm["audist.database"].as<std::string>());
+ soci::statement st(sql);
+ std::string filename, artist, album, title;
+ st.exchange(soci::into(filename));
+ std::string query = "SELECT file_name FROM tracks WHERE ";
+ std::vector<std::string> where_conditions;
+
+ if(search.find("artist") != search.end()) {
+ where_conditions.push_back("artist_id IN (SELECT id FROM artists WHERE name LIKE :artist)");
+ artist = "%"+search["artist"]+"%";
+ st.exchange(soci::use(artist, "artist"));
+ }
+
+ if(search.find("album") != search.end()) {
+ where_conditions.push_back("album_id IN (SELECT id FROM albums WHERE name LIKE :album)");
+ album = "%"+search["album"]+"%";
+ st.exchange(soci::use(album, "album"));
+ }
+
+ if(search.find("title") != search.end()) {
+ where_conditions.push_back("name LIKE :title");
+ artist = "%"+search["title"]+"%";
+ st.exchange(soci::use(title, "title"));
+ }
+
+ query += boost::algorithm::join(where_conditions, " AND ");
+ st.alloc();
+ st.prepare(query);
+ st.define_and_bind();
+ std::cout << "query: " << query << std::endl;
+ std::cout << "st.execute: " << st.execute(true) << std::endl;
+ std::cout << "st.got_data: " << st.got_data() << std::endl;
+
+ std::vector<MusicListing::p> results;
+ while(st.fetch()) {
+ MusicListing::p ml(new MusicTrack(filename));
+ results.push_back(ml);
+ }
+ sql.close();
+
+ return results;
+}
+
+/** Find tracks in the database.
+ * Returns tracks where title, artist, album or filename matches \p search.
+ */
+std::vector<MusicListing::p> music::find(std::string search) {
soci::session sql(config::vm["audist.database"].as<std::string>());
- soci::rowset<std::string> rs = (sql.prepare << "SELECT file_name FROM tracks WHERE artist_id IN (SELECT id FROM artists WHERE name LIKE :name)",
- soci::use("%"+artist+"%"));
+ search = "%"+search+"%";
+ soci::rowset<std::string> rs = (sql.prepare << "SELECT DISTINCT file_name FROM tracks WHERE name LIKE :search OR file_name LIKE :search OR "
+ "artist_id IN (SELECT id FROM artists WHERE name LIKE :search) OR album_id IN (SELECT id FROM albums WHERE name LIKE :search)",
+ soci::use(search, "search"));
std::vector<MusicListing::p> results;
for(soci::rowset<std::string>::const_iterator it = rs.begin(); it != rs.end(); it++) {
- boost::shared_ptr<MusicListing> ml(new MusicTrack(*it));
+ MusicListing::p ml(new MusicTrack(*it));
results.push_back(ml);
}
return results;
}
+/** Initiate an update on \p path and its subdirs.
+ * music::update does the actual work.
+ */
void music::begin_update(const std::string path) {
MusicDirectory::p dir = get_directory(path);
std::cout << boost::format("updater(%s) called") % path << std::endl;
@@ -97,7 +152,10 @@ void music::begin_update(const std::string path) {
}
}
+/** Recursively update \p dir and its subdirectories.
+ */
void music::update(const MusicDirectory& dir) {
+ // TODO: Fix engine-specific SQL syntax inside this function.
soci::session sql(config::vm["audist.database"].as<std::string>());
BOOST_FOREACH(fs::path t, dir.tracks) {
diff --git a/music.h b/music.h
index ed47657..f7d06bd 100644
--- a/music.h
+++ b/music.h
@@ -43,7 +43,8 @@ namespace music {
MusicListing::p get(const HTTP::Connection::PathList& path);
MusicListing::p get(const std::string& path);
MusicDirectory::p get_directory(const std::string& path);
- std::vector<MusicListing::p> find_artist(const std::string artist);
+ std::vector<MusicListing::p> find(const std::map<std::string, std::string> search);
+ std::vector<MusicListing::p> find(std::string search);
void begin_update(const std::string path);
void update(const MusicDirectory& dir);
};