#include "music.h" #include "decoder.h" #include "encoder.h" #include "tag.h" #include "database.h" #include "cache.h" #include #include #include #include #include #include #include #include #include fs::path music::root_directory; void music::init(std::string root) { // remove trailing slash if(boost::algorithm::ends_with(root, "/")) root = root.substr(0, root.size()-1); root_directory = root; } /** 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. */ MusicListing::p music::get(const HTTP::Connection::PathList& path) { // prefix path with our root_directory fs::path p = root_directory; for(HTTP::Connection::PathList::const_iterator it = path.begin(); it != path.end(); it++) { // don't allow requests with /../ in the path if(*it == "..") { return MusicListing::p(); } p /= *it; } if(fs::is_directory(p)) { boost::shared_ptr ml(new MusicDirectory(p)); return ml; } else if(fs::is_regular_file(p)) { boost::shared_ptr ml(new MusicTrack(p)); return ml; } return MusicListing::p(); } /** 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) { HTTP::Connection::PathList path_vector; boost::algorithm::split(path_vector, path, boost::algorithm::is_any_of("/\\")); return get(path_vector); } /** Fetches a directory. * This is a helper function which returns a null pointer if the fetched MusicListing isn't a directory. */ MusicDirectory::p music::get_directory(const std::string& path) { MusicListing::p ml = get(path); if(!ml || !fs::is_directory(ml->path)) { return MusicDirectory::p(); } MusicDirectory::p dir(boost::dynamic_pointer_cast(ml)); return dir; } /** Find tracks in the database. * Does a search on specific fields given by \p search. */ std::vector music::find(std::map search) { Database db; return db.find(search); } /** Find tracks in the database. * Returns tracks where title, artist, album or filename matches \p search. */ std::vector music::find(std::string search) { Database db; return db.find(search); } /** 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; if(dir) { Database db; dir->update(db); } } /** Recursively update \p dir and its subdirectories. */ void MusicDirectory::update(Database& db) { BOOST_FOREACH(fs::path track, tracks) { std::cout << "track " << track << std::endl; Tag::p tag = Tag::load(track.string()); BOOST_FOREACH(Tag::Fields::value_type& f, tag->fields) { std::cout << boost::format(" %s: %s") % f.first % f.second << std::endl; } db.update(track, tag); } for(PathListings::iterator it = directories.begin(); it != directories.end(); it++) { MusicDirectory dir(*it); dir.update(db); } } void MusicDirectory::render(HTTP::Connection::p req) { req->add_header("content-type", "text/html"); std::string base_path = boost::algorithm::join(req->base_path, "/"); // strip root_directory from the absolute file path std::string rel_base(path.string().substr(music::root_directory.string().size())); // set relative base to "/" if we're at the root_directory if(!rel_base.size()) rel_base = "/"; req->send_data(boost::str(boost::format("

%s

") % rel_base)); // link to parent directory if we're not at the root_directory if(rel_base != "/") { std::string rel_parent(rel_base.substr(0, rel_base.find_last_of('/'))); req->send_data(boost::str(boost::format("..
") % base_path % rel_parent)); } for(PathListings::iterator it = directories.begin(); it != directories.end(); it++) { std::string rel_path = it->string().substr(music::root_directory.string().size()); req->send_data(boost::str(boost::format("%s
") % base_path % rel_path % it->filename())); } for(PathListings::iterator it = tracks.begin(); it != tracks.end(); it++) { std::string rel_path = it->string().substr(music::root_directory.string().size()); req->send_data(boost::str(boost::format("%s
") % base_path % rel_path % it->filename())); } } MusicTrack::MusicTrack(const fs::path path) { std::cout << path << std::endl; this->path = path; } MusicDirectory::MusicDirectory(const fs::path root) { this->path = root; std::cout << this->path << std::endl; fs::directory_iterator end_itr; for(fs::directory_iterator it(this->path); it != end_itr; it++) { if(fs::is_directory(it->status())) { directories.push_back(it->path()); } else if(fs::is_regular_file(it->status())) { tracks.push_back(it->path()); } } } void MusicTrack::render(HTTP::Connection::p req) { fs::path file_path = path; if(req->args.count("decoder") && req->args.count("encoder")) { Decoder::p decoder = Decoder::get(req->args["decoder"], path.string()); Encoder::p encoder = Encoder::get(req->args["encoder"], decoder); EncodedCache ec(path, decoder, encoder); file_path = ec.get_path(); if(!fs::exists(file_path)) ec.create_cache(); req->add_header("Content-Type", encoder->get_mime_type()); } else { // TODO: Send correct Content-Type. req->add_header("Content-Type", "application/octet-stream"); } req->send_file(file_path); }