From 1d984b7985ad6f717fe7de206a6189fbf019b9eb Mon Sep 17 00:00:00 2001 From: Paul Davis Date: Mon, 14 Nov 2011 22:04:14 +0000 Subject: the return of basic FreeSound mootcher functionality, c/o colinf. lots of bugs and workflow issues to still resolve, but good to play with. remember that ardour can't play mp3's git-svn-id: svn://localhost/ardour2/branches/3.0@10596 d708f5d6-7413-0410-9779-e7cbd77b26cf --- gtk2_ardour/sfdb_freesound_mootcher.cc | 557 ++++++++++++--------------------- 1 file changed, 203 insertions(+), 354 deletions(-) (limited to 'gtk2_ardour/sfdb_freesound_mootcher.cc') diff --git a/gtk2_ardour/sfdb_freesound_mootcher.cc b/gtk2_ardour/sfdb_freesound_mootcher.cc index bcd6c9f830..6833bae768 100644 --- a/gtk2_ardour/sfdb_freesound_mootcher.cc +++ b/gtk2_ardour/sfdb_freesound_mootcher.cc @@ -1,6 +1,7 @@ /* sfdb_freesound_mootcher.cpp ********************************************************************** Adapted for Ardour by Ben Loftis, March 2008 + Updated to new Freesound API by Colin Fletcher, November 2011 Mootcher 23-8-2005 @@ -43,37 +44,39 @@ #include #include +#include #include "ardour/audio_library.h" -#define TRUE 1 +static const std::string base_url = "http://www.freesound.org/api"; +static const std::string api_key = "9d77cb8d841b4bcfa960e1aae62224eb"; // ardour3 + //------------------------------------------------------------------------ -Mootcher:: Mootcher(const char *saveLocation) - : curl( NULL ) - , connection( 0 ) +Mootcher::Mootcher(const char *saveLocation) + : curl(curl_easy_init()) { changeWorkingDir(saveLocation); }; //------------------------------------------------------------------------ -Mootcher:: ~Mootcher() +Mootcher:: ~Mootcher() { - remove( "cookiejar.txt" ); } + //------------------------------------------------------------------------ const char* Mootcher::changeWorkingDir(const char *saveLocation) { basePath = saveLocation; #ifdef __WIN32__ std::string replace = "/"; - int pos = (int)basePath.find("\\"); + size_t pos = basePath.find("\\"); while( pos != std::string::npos ){ basePath.replace(pos, 1, replace); - pos = (int)basePath.find("\\"); + pos = basePath.find("\\"); } #endif // - int pos2 = basePath.find_last_of("/"); + size_t pos2 = basePath.find_last_of("/"); if(basePath.length() != (pos2+1)) basePath += "/"; // create Freesound directory and sound dir @@ -86,15 +89,12 @@ const char* Mootcher::changeWorkingDir(const char *saveLocation) } //------------------------------------------------------------------------ -size_t Mootcher::WriteMemoryCallback(void *ptr, size_t size, size_t nmemb, void *data) +size_t Mootcher::WriteMemoryCallback(void *ptr, size_t size, size_t nmemb, void *data) { register int realsize = (int)(size * nmemb); struct MemoryStruct *mem = (struct MemoryStruct *)data; - // There might be a realloc() out there that doesn't like - // reallocing NULL pointers, so we take care of it here - if(mem->memory) mem->memory = (char *)realloc(mem->memory, mem->size + realsize + 1); - else mem->memory = (char *)malloc(mem->size + realsize + 1); + mem->memory = (char *)realloc(mem->memory, mem->size + realsize + 1); if (mem->memory) { memcpy(&(mem->memory[mem->size]), ptr, realsize); @@ -106,332 +106,183 @@ size_t Mootcher::WriteMemoryCallback(void *ptr, size_t size, size_t nmemb, void //------------------------------------------------------------------------ -void Mootcher::toLog(std::string input) -{ -printf("%s\n", input.c_str());// for debugging -} +std::string Mootcher::sortMethodString(enum sortMethod sort) { + + switch (sort) { + case sort_duration_desc: return "duration_desc"; + case sort_duration_asc: return "duration_asc"; + case sort_created_desc: return "created_desc"; + case sort_created_asc: return "created_asc"; + case sort_downloads_desc: return "downloads_desc"; + case sort_downloads_asc: return "downloads_asc"; + case sort_rating_desc: return "rating_desc"; + case sort_rating_asc: return "rating_asc"; + default: return ""; + } +} //------------------------------------------------------------------------ -void Mootcher::setcUrlOptions() +void Mootcher::setcUrlOptions() { // basic init for curl curl_global_init(CURL_GLOBAL_ALL); // some servers don't like requests that are made without a user-agent field, so we provide one curl_easy_setopt(curl, CURLOPT_USERAGENT, "libcurl-agent/1.0"); // setup curl error buffer - CURLcode res = curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, errorBuffer); - // always use the cookie with session id which is received at the login - curl_easy_setopt(curl, CURLOPT_COOKIEFILE, "cookiejar.txt"); + curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, errorBuffer); // Allow redirection curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1); + + // Allow connections to time out (without using signals) + curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1); + curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 10); + + } -//------------------------------------------------------------------------ -int Mootcher::doLogin(std::string login, std::string password) +std::string Mootcher::doRequest(std::string uri, std::string params) { - if(connection==1) - return 1; - + std::string result; struct MemoryStruct xml_page; xml_page.memory = NULL; xml_page.size = 0; - // create the post message from the login and password - std::string postMessage; - postMessage += "username="; - postMessage += curl_escape(login.c_str(), 0); - postMessage += "&password="; - postMessage += curl_escape(password.c_str(), 0); - postMessage += "&login="; - postMessage += curl_escape("1", 0); - postMessage += "&redirect="; - postMessage += curl_escape("../tests/login.php", 0); - - // Do the setup for libcurl - curl = curl_easy_init(); - - if(curl) - { - setcUrlOptions(); - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback); - curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&xml_page); - // save the sessoin id that is given back by the server in a cookie - curl_easy_setopt(curl, CURLOPT_COOKIEJAR, "cookiejar.txt"); - // use POST for login variables - curl_easy_setopt(curl, CURLOPT_POST, TRUE); - curl_easy_setopt(curl, CURLOPT_POSTFIELDS, postMessage.c_str()); - curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, -1); - - // the url to get - std::string login_url = "http://www.freesound.org/forum/login.php"; - curl_easy_setopt(curl, CURLOPT_URL, login_url.c_str() ); - - // perform online request - connection = 1; - CURLcode res = curl_easy_perform(curl); - if( res != 0 ) { - toLog("curl login error\n"); - toLog(curl_easy_strerror(res)); - connection = 0; - } + setcUrlOptions(); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *) &xml_page); - if (connection == 1){ - std::string check_page = xml_page.memory; - int test = (int)check_page.find("login"); //logged - if( strcmp(xml_page.memory, "login") == 0 ) - toLog("Logged in.\n"); - else { - toLog("Login failed: Check username and password.\n"); - connection = 0; - } - } + // curl_easy_setopt(curl, CURLOPT_HTTPGET, 1); + // curl_easy_setopt(curl, CURLOPT_POSTFIELDS, postMessage.c_str()); + // curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, -1); - // free the memory - if(xml_page.memory){ - free( xml_page.memory ); - xml_page.memory = NULL; - xml_page.size = 0; - } + // the url to get + std::string url = base_url + uri + "?"; + if (params != "") { + url += params + "&api_key=" + api_key + "&format=xml"; + } else { + url += "api_key=" + api_key + "&format=xml"; + } + + curl_easy_setopt(curl, CURLOPT_URL, url.c_str() ); + std::cerr << "doRequest: " << url << std::endl; + + // perform online request + CURLcode res = curl_easy_perform(curl); + if( res != 0 ) { + std::cerr << "curl error " << res << " (" << curl_easy_strerror(res) << ")" << std::endl; + return ""; + } - std::cerr << "Login was cool, connection = " << connection << std::endl; - return connection; + result = xml_page.memory; + // free the memory + if(xml_page.memory){ + free( xml_page.memory ); + xml_page.memory = NULL; + xml_page.size = 0; } - else return 3; // will be returned if a curl related problem ocurrs -} -//------------------------------------------------------------------------ -std::string Mootcher::searchText(std::string word) -{ - struct MemoryStruct xml_page; - xml_page.memory = NULL; - xml_page.size = 0; - std::string result; + return result; - if(connection != 0) - { - // create a url encoded post message - std::string postMessage; - char tempString[ 128 ]; - char *tempPointer = &tempString[0]; - - postMessage = "search="; - postMessage += curl_escape(word.c_str(), 0); - sprintf( tempPointer, "&searchDescriptions=1"); - postMessage += tempPointer; - sprintf( tempPointer, "&searchtags=1"); - postMessage += tempPointer; - - // Ref: http://www.freesound.org/forum/viewtopic.php?p=19081 - // const ORDER_DEFAULT = 0; - // const ORDER_DOWNLOADS_DESC = 1; - // const ORDER_DOWNLOADS_ASC = 2; - // const ORDER_USERNAME_DESC = 3; - // const ORDER_USERNAME_ASC = 4; - // const ORDER_DATE_DESC = 5; - // const ORDER_DATE_ASC = 6; - // const ORDER_DURATION_DESC = 7; - // const ORDER_DURATION_ASC = 8; - // const ORDER_FILEFORMAT_DESC = 9; - // const ORDER_FILEFORMAT_ASC = 10; - sprintf( tempPointer, "&order=1"); - postMessage += tempPointer; - sprintf( tempPointer, "&start=0"); - postMessage += tempPointer; - sprintf( tempPointer, "&limit=10"); - postMessage += tempPointer; - // The limit of 10 samples is arbitrary, but seems - // reasonable in light of the fact that all of the returned - // samples get downloaded, and downloads are s-l-o-w. - - if(curl) - { - // basic init for curl - setcUrlOptions(); - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback); - curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&xml_page); - // setup the post message - curl_easy_setopt(curl, CURLOPT_POST, TRUE); - curl_easy_setopt(curl, CURLOPT_POSTFIELDS, postMessage.c_str()); - curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, -1); - - // the url to get - std::string search_url = "http://www.freesound.org/searchTextXML.php"; - curl_easy_setopt(curl, CURLOPT_URL, search_url.c_str()); - - // perform the online search - connection = 1; - CURLcode res = curl_easy_perform(curl); - if( res != 0 ) { - toLog("curl login error\n"); - toLog(curl_easy_strerror(res)); - connection = 0; - } +} - result = xml_page.memory; - toLog( result.c_str() ); - // free the memory - if(xml_page.memory){ - free( xml_page.memory ); - xml_page.memory = NULL; - xml_page.size = 0; - } +std::string Mootcher::searchText(std::string query, int page, std::string filter, enum sortMethod sort) +{ + std::string params = ""; + char buf[24]; - } + if (page > 1) { + snprintf(buf, 23, "p=%d&", page); + params += buf; } + + params += "q=" + query; - return result; -} + if (filter != "") + params += "&f=" + filter; + + if (sort) + params += "&s=" + sortMethodString(sort); -//------------------------------------------------------------------------ -std::string Mootcher::changeExtension(std::string filename) -{ - std::string aiff = ".aiff"; - std::string aif = ".aif"; - std::string wav = ".wav"; - std::string mp3 = ".mp3"; - std::string ogg = ".ogg"; - std::string flac = ".flac"; - - std::string replace = ".xml"; - int pos = 0; - - pos = (int)filename.find(aiff); - if(pos != std::string::npos) filename.replace(pos, aiff.size(), replace); - pos = (int)filename.find(aif); - if(pos != std::string::npos) filename.replace(pos, aif.size(), replace); - pos = (int)filename.find(wav); - if(pos != std::string::npos) filename.replace(pos, wav.size(), replace); - pos = (int)filename.find(mp3); - if(pos != std::string::npos) filename.replace(pos, mp3.size(), replace); - pos = (int)filename.find(ogg); - if(pos != std::string::npos) filename.replace(pos, ogg.size(), replace); - pos = (int)filename.find(flac); - if(pos != std::string::npos) filename.replace(pos, flac.size(), replace); - - return filename; + return doRequest("/sounds/search", params); } + //------------------------------------------------------------------------ -void Mootcher::GetXml(std::string ID, struct MemoryStruct &xml_page) + +std::string Mootcher::getSoundResourceFile(std::string ID) { - if(curl) { - setcUrlOptions(); - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback); - curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&xml_page); + std::string originalSoundURI; + std::string audioFileName; + std::string xmlFileName; + std::string xml; - // URL to get - std::string getxml_url = "http://www.freesound.org/samplesViewSingleXML.php?id="; - getxml_url += ID; - curl_easy_setopt(curl, CURLOPT_URL, getxml_url.c_str() ); + std::cerr << "getSoundResourceFile(" << ID << ")" << std::endl; - // get it! - connection = 1; - CURLcode res = curl_easy_perform(curl); - if( res != 0 ) { - toLog("curl login error\n"); - toLog(curl_easy_strerror(res)); - connection = 0; - } + // download the xmlfile into xml_page + xml = doRequest("/sounds/" + ID, ""); + + XMLTree doc; + doc.read_buffer( xml.c_str() ); + XMLNode *freesound = doc.root(); + + // if the page is not a valid xml document with a 'freesound' root + if (freesound == NULL) { + std::cerr << "getSoundResourceFile: There is no valid root in the xml file" << std::endl; + return ""; } -} -//------------------------------------------------------------------------ -std::string Mootcher::getXmlFile(std::string ID, int &length) -{ - struct MemoryStruct xml_page; - xml_page.memory = NULL; - xml_page.size = NULL; - std::string xmlFileName; - std::string audioFileName; - std::string filename; - - if(connection != 0) { - // download the xmlfile into xml_page - GetXml(ID, xml_page); - - // if sample ID does not exist on the freesound website - if(strcmp(xml_page.memory, "sample non existant") == 0){ - free( xml_page.memory ); - sprintf(message, "getXmlFile: sample with ID:%s does not exist!\n", ID.c_str() ); - toLog(message); - return filename; - } else { - XMLTree doc; - doc.read_buffer( xml_page.memory ); - XMLNode *freesound = doc.root(); - - // if the page is not a valid xml document with a 'freesound' root - if( freesound == NULL){ - sprintf(message, "getXmlFile: There is no valid root in the xml file"); - toLog(message); - } else { - XMLNode *sample = freesound->child("sample"); - XMLNode *name = NULL; - XMLNode *filesize = NULL; - if (sample) { - name = sample->child("originalFilename"); - filesize = sample->child("filesize"); - } + if (strcmp(doc.root()->name().c_str(), "response") != 0) { + std::cerr << "getSoundResourceFile: root =" << doc.root()->name() << ", != response" << std::endl; + return ""; + } - // get the file name and size from xml file - if (sample && name && filesize) { - - audioFileName = name->child("text")->content(); - sprintf( message, "getXmlFile: %s needs to be downloaded\n", audioFileName.c_str() ); - toLog(message); - - length = atoi(filesize->child("text")->content().c_str()); - - // create new filename with the ID number - filename = basePath; - filename += "snd/"; - filename += sample->property("id")->value(); - filename += "-"; - filename += audioFileName; - // change the extention into .xml - xmlFileName = changeExtension( filename ); - - sprintf(message, "getXmlFile: saving XML: %s\n", xmlFileName.c_str() ); - toLog(message); - - // save the xml file to disk - doc.write(xmlFileName.c_str()); - - //store all the tags in the database - XMLNode *tags = sample->child("tags"); - if (tags) { - XMLNodeList children = tags->children(); - XMLNodeConstIterator niter; - std::vector strings; - for (niter = children.begin(); niter != children.end(); ++niter) { - XMLNode *node = *niter; - if( strcmp( node->name().c_str(), "tag") == 0 ) { - XMLNode *text = node->child("text"); - if (text) strings.push_back(text->content()); - } - } - ARDOUR::Library->set_tags (std::string("//")+filename, strings); - ARDOUR::Library->save_changes (); + XMLNode *name = freesound->child("original_filename"); + XMLNode *filesize = freesound->child("filesize"); + + + // get the file name and size from xml file + if (name && filesize) { + + audioFileName = basePath + "snd/" + ID + "-" + name->child("text")->content(); + + // create new filename with the ID number + xmlFileName = basePath; + xmlFileName += "snd/"; + xmlFileName += freesound->child("id")->child("text")->content(); + xmlFileName += "-"; + xmlFileName += name->child("text")->content(); + xmlFileName += ".xml"; + + // std::cerr << "getSoundResourceFile: saving XML: " << xmlFileName << std::endl; + + // save the xml file to disk + doc.write(xmlFileName.c_str()); + + //store all the tags in the database + XMLNode *tags = freesound->child("tags"); + if (tags) { + XMLNodeList children = tags->children(); + XMLNodeConstIterator niter; + std::vector strings; + for (niter = children.begin(); niter != children.end(); ++niter) { + XMLNode *node = *niter; + if( strcmp( node->name().c_str(), "resource") == 0 ) { + XMLNode *text = node->child("text"); + if (text) { + // std::cerr << "tag: " << text->content() << std::endl; + strings.push_back(text->content()); } } - - // clear the memory - if(xml_page.memory){ - free( xml_page.memory ); - xml_page.memory = NULL; - xml_page.size = 0; - } - return audioFileName; } + ARDOUR::Library->set_tags (std::string("//")+audioFileName, strings); + ARDOUR::Library->save_changes (); } } - else { - return audioFileName; - } + return audioFileName; } int audioFileWrite(void *buffer, size_t size, size_t nmemb, void *file) @@ -440,74 +291,63 @@ int audioFileWrite(void *buffer, size_t size, size_t nmemb, void *file) }; //------------------------------------------------------------------------ -std::string Mootcher::getFile(std::string ID) +std::string Mootcher::getAudioFile(std::string originalFileName, std::string ID, std::string audioURL, Gtk::ProgressBar *progress_bar) { - CURLcode result_curl; - std::string audioFileName; + std::string audioFileName = basePath + "snd/" + ID + "-" + originalFileName; - if(connection != 0) - { - int length; - std::string name = getXmlFile(ID, length); - if( name != "" ){ - - // create new filename with the ID number - audioFileName += basePath; - audioFileName += "snd/"; - audioFileName += ID; - audioFileName += "-"; - audioFileName += name; - - //check to see if audio file already exists - FILE *testFile = fopen(audioFileName.c_str(), "r"); - if (testFile) { //TODO: should also check length to see if file is complete - fseek (testFile , 0 , SEEK_END); - if (ftell (testFile) == length) { - sprintf(message, "%s already exists\n", audioFileName.c_str() ); - toLog(message); - fclose (testFile); - return audioFileName; - } else { - remove( audioFileName.c_str() ); //file was not correct length, delete it and try again - } - } + //check to see if audio file already exists + FILE *testFile = fopen(audioFileName.c_str(), "r"); + if (testFile) { + fseek (testFile , 0 , SEEK_END); + if (ftell (testFile) > 256) { + std::cerr << "audio file " << audioFileName << " already exists" << std::endl; + fclose (testFile); + return audioFileName; + } + + // else file was small, probably an error, delete it and try again + fclose(testFile); + remove( audioFileName.c_str() ); + } + //now download the actual file + if (curl) { - //now download the actual file - if (curl) { + FILE* theFile; + theFile = fopen( audioFileName.c_str(), "wb" ); - FILE* theFile; - theFile = fopen( audioFileName.c_str(), "wb" ); + if (theFile) { + + // create the download url + audioURL += "?api_key=" + api_key; + + setcUrlOptions(); + curl_easy_setopt(curl, CURLOPT_URL, audioURL.c_str() ); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, audioFileWrite); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, theFile); - // create the download url, this url will also update the download statics on the site - std::string audioURL; - audioURL += "http://www.freesound.org/samplesDownload.php?id="; - audioURL += ID; + std::cerr << "downloading " << audioFileName << " from " << audioURL << "..." << std::endl; - setcUrlOptions(); - curl_easy_setopt(curl, CURLOPT_URL, audioURL.c_str() ); - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, audioFileWrite); - curl_easy_setopt(curl, CURLOPT_WRITEDATA, theFile); + curl_easy_setopt (curl, CURLOPT_NOPROGRESS, 0); // turn on the progress bar + curl_easy_setopt (curl, CURLOPT_PROGRESSFUNCTION, progress_callback); + curl_easy_setopt (curl, CURLOPT_PROGRESSDATA, progress_bar); - connection = 1; - CURLcode res = curl_easy_perform(curl); - if( res != 0 ) { - toLog("curl login error\n"); - toLog(curl_easy_strerror(res)); - connection = 0; - } + CURLcode res = curl_easy_perform(curl); + fclose(theFile); - fclose(theFile); - } + curl_easy_setopt (curl, CURLOPT_NOPROGRESS, 1); // turn off the progress bar + progress_bar->set_fraction(0.0); -/* - bar.dlnowMoo = 0; - bar.dltotalMoo = 0; - curl_easy_setopt (curl, CURLOPT_NOPROGRESS, 0); // turn on the process bar thingy - curl_easy_setopt (curl, CURLOPT_PROGRESSFUNCTION, progress_callback); - curl_easy_setopt (curl, CURLOPT_PROGRESSDATA, &bar); -*/ + if( res != 0 ) { + std::cerr << "curl error " << res << " (" << curl_easy_strerror(res) << ")" << std::endl; + remove( audioFileName.c_str() ); + return ""; + } else { + std::cerr << "done!" << std::endl; + // now download the tags &c. + getSoundResourceFile(ID); + } } } @@ -517,8 +357,17 @@ std::string Mootcher::getFile(std::string ID) //--------- int Mootcher::progress_callback(void *bar, double dltotal, double dlnow, double ultotal, double ulnow) { - struct dlprocess *lbar = (struct dlprocess *) bar; - lbar->dltotalMoo = dltotal; - lbar->dlnowMoo = dlnow; + + //XXX I hope it's OK to do GTK things in this callback. Otherwise + // I'll have to do stuff like in interthread_progress_window. + + Gtk::ProgressBar *progress_bar = (Gtk::ProgressBar *) bar; + progress_bar->set_fraction(dlnow/dltotal); + /* Make sure the progress widget gets updated */ + while (Glib::MainContext::get_default()->iteration (false)) { + /* do nothing */ + } + std::cerr << "progress: " << dlnow << " of " << dltotal << " \r"; return 0; } + -- cgit v1.2.3