provide an easier way to add mixed (audio+MIDI) tracks to a session, to facilitate...
[ardour.git] / gtk2_ardour / sfdb_freesound_mootcher.cc
index bcd6c9f8304cb3fb61322f8883fb97df5e1930ff..70d04abb6670e11a6f4eefe1bb995c93a556820b 100644 (file)
@@ -1,6 +1,7 @@
 /* sfdb_freesound_mootcher.cpp **********************************************************************\r
 \r
        Adapted for Ardour by Ben Loftis, March 2008\r
+       Updated to new Freesound API by Colin Fletcher, November 2011\r
 \r
        Mootcher 23-8-2005\r
 \r
 #include "sfdb_freesound_mootcher.h"\r
 \r
 #include "pbd/xml++.h"\r
+#include "pbd/filesystem.h"\r
 \r
 #include <sys/stat.h>\r
 #include <sys/types.h>\r
+#include <iostream>\r
+\r
+#include <glib.h>\r
+#include <glib/gstdio.h>\r
+\r
+#include "i18n.h"\r
 \r
 #include "ardour/audio_library.h"\r
 \r
-#define TRUE 1\r
+static const std::string base_url = "http://www.freesound.org/api";\r
+static const std::string api_key = "9d77cb8d841b4bcfa960e1aae62224eb"; // ardour3\r
+\r
 \r
 //------------------------------------------------------------------------\r
-Mootcher::     Mootcher(const char *saveLocation)\r
-       : curl( NULL )\r
-       , connection( 0 )\r
+Mootcher::Mootcher()\r
+       : curl(curl_easy_init())\r
 {\r
-       changeWorkingDir(saveLocation);\r
+       std::string path;\r
+       path = Glib::get_home_dir() + "/Freesound/";\r
+       changeWorkingDir ( path.c_str() );\r
 };\r
 //------------------------------------------------------------------------\r
-Mootcher::     ~Mootcher()\r
+Mootcher:: ~Mootcher()\r
 {\r
-       remove( "cookiejar.txt" );\r
+       curl_easy_cleanup(curl);\r
 }\r
+\r
 //------------------------------------------------------------------------\r
-const char* Mootcher::changeWorkingDir(const char *saveLocation)\r
+void Mootcher::changeWorkingDir(const char *saveLocation)\r
 {\r
        basePath = saveLocation;\r
 #ifdef __WIN32__\r
        std::string replace = "/";\r
-       int pos = (int)basePath.find("\\");\r
+       size_t pos = basePath.find("\\");\r
        while( pos != std::string::npos ){\r
                basePath.replace(pos, 1, replace);\r
-               pos = (int)basePath.find("\\");\r
+               pos = basePath.find("\\");\r
        }\r
 #endif\r
        //\r
-       int pos2 = basePath.find_last_of("/");\r
+       size_t pos2 = basePath.find_last_of("/");\r
        if(basePath.length() != (pos2+1)) basePath += "/";\r
+}\r
 \r
-       // create Freesound directory and sound dir\r
-       std::string sndLocation = basePath;\r
-       mkdir(sndLocation.c_str(), 0777);\r
-       sndLocation += "snd";\r
-       mkdir(sndLocation.c_str(), 0777);\r
-\r
-       return basePath.c_str();\r
+void Mootcher::ensureWorkingDir ()\r
+{\r
+       PBD::sys::path p = basePath;\r
+       p /= "snd";\r
+       if (!PBD::sys::is_directory (p)) {\r
+               PBD::sys::create_directories (p);\r
+       }\r
 }\r
+       \r
 \r
 //------------------------------------------------------------------------\r
-size_t         Mootcher::WriteMemoryCallback(void *ptr, size_t size, size_t nmemb, void *data)\r
+size_t Mootcher::WriteMemoryCallback(void *ptr, size_t size, size_t nmemb, void *data)\r
 {\r
        register int realsize = (int)(size * nmemb);\r
        struct MemoryStruct *mem = (struct MemoryStruct *)data;\r
 \r
-       // There might be a realloc() out there that doesn't like\r
-       // reallocing NULL pointers, so we take care of it here\r
-       if(mem->memory) mem->memory = (char *)realloc(mem->memory, mem->size + realsize + 1);\r
-       else                    mem->memory = (char *)malloc(mem->size + realsize + 1);\r
+       mem->memory = (char *)realloc(mem->memory, mem->size + realsize + 1);\r
 \r
        if (mem->memory) {\r
                memcpy(&(mem->memory[mem->size]), ptr, realsize);\r
@@ -106,332 +117,171 @@ size_t          Mootcher::WriteMemoryCallback(void *ptr, size_t size, size_t nmemb, void
 \r
 \r
 //------------------------------------------------------------------------\r
-void           Mootcher::toLog(std::string input)\r
-{\r
-printf("%s\n", input.c_str());// for debugging\r
-}\r
 \r
+std::string Mootcher::sortMethodString(enum sortMethod sort) {\r
+// given a sort type, returns the string value to be passed to the API to\r
+// sort the results in the requested way.\r
+\r
+       switch (sort) {\r
+               case sort_duration_desc:        return "duration_desc"; \r
+               case sort_duration_asc:         return "duration_asc";\r
+               case sort_created_desc:         return "created_desc";\r
+               case sort_created_asc:          return "created_asc";\r
+               case sort_downloads_desc:       return "downloads_desc";\r
+               case sort_downloads_asc:        return "downloads_asc";\r
+               case sort_rating_desc:          return "rating_desc";\r
+               case sort_rating_asc:           return "rating_asc";\r
+               default:                        return "";      \r
+       }\r
+}\r
 \r
 //------------------------------------------------------------------------\r
-void           Mootcher::setcUrlOptions()\r
+void Mootcher::setcUrlOptions()\r
 {\r
        // basic init for curl\r
        curl_global_init(CURL_GLOBAL_ALL);\r
        // some servers don't like requests that are made without a user-agent field, so we provide one\r
        curl_easy_setopt(curl, CURLOPT_USERAGENT, "libcurl-agent/1.0");\r
        // setup curl error buffer\r
-       CURLcode res = curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, errorBuffer);\r
-       // always use the cookie with session id which is received at the login\r
-       curl_easy_setopt(curl, CURLOPT_COOKIEFILE, "cookiejar.txt");\r
+       curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, errorBuffer);\r
        // Allow redirection\r
        curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1);\r
+       \r
+       // Allow connections to time out (without using signals)\r
+       curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);\r
+       curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 30);\r
+\r
+\r
 }\r
 \r
-//------------------------------------------------------------------------\r
-int                    Mootcher::doLogin(std::string login, std::string password)\r
+std::string Mootcher::doRequest(std::string uri, std::string params)\r
 {\r
-       if(connection==1)\r
-               return 1;\r
-\r
+       std::string result;\r
        struct MemoryStruct xml_page;\r
        xml_page.memory = NULL;\r
        xml_page.size = 0;\r
 \r
-       // create the post message from the login and password\r
-       std::string postMessage;\r
-       postMessage += "username=";\r
-       postMessage += curl_escape(login.c_str(), 0);\r
-       postMessage += "&password=";\r
-       postMessage += curl_escape(password.c_str(), 0);\r
-       postMessage += "&login=";\r
-       postMessage += curl_escape("1", 0);\r
-       postMessage += "&redirect=";\r
-       postMessage += curl_escape("../tests/login.php", 0);\r
-\r
-       // Do the setup for libcurl\r
-       curl = curl_easy_init();\r
-\r
-       if(curl)\r
-       {\r
-               setcUrlOptions();\r
-               curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);\r
-               curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&xml_page);\r
-               // save the sessoin id that is given back by the server in a cookie\r
-               curl_easy_setopt(curl, CURLOPT_COOKIEJAR, "cookiejar.txt");\r
-               // use POST for login variables\r
-               curl_easy_setopt(curl, CURLOPT_POST, TRUE);\r
-               curl_easy_setopt(curl, CURLOPT_POSTFIELDS, postMessage.c_str());\r
-               curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, -1);\r
-\r
-               // the url to get\r
-               std::string login_url = "http://www.freesound.org/forum/login.php";\r
-               curl_easy_setopt(curl, CURLOPT_URL, login_url.c_str() );\r
-\r
-               // perform online request\r
-               connection = 1;\r
-               CURLcode res = curl_easy_perform(curl);\r
-               if( res != 0 ) {\r
-                       toLog("curl login error\n");\r
-                       toLog(curl_easy_strerror(res));\r
-                       connection = 0;\r
-               }\r
+       setcUrlOptions();\r
+       curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);\r
+       curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *) &xml_page);\r
 \r
-               if (connection == 1){\r
-                       std::string check_page = xml_page.memory;\r
-                       int test = (int)check_page.find("login");   //logged\r
-                       if(     strcmp(xml_page.memory, "login") == 0 )\r
-                               toLog("Logged in.\n");\r
-                       else {\r
-                               toLog("Login failed: Check username and password.\n");\r
-                               connection = 0;\r
-                       }\r
-               }\r
+       // curl_easy_setopt(curl, CURLOPT_HTTPGET, 1);\r
+       // curl_easy_setopt(curl, CURLOPT_POSTFIELDS, postMessage.c_str());\r
+       // curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, -1);\r
 \r
-               // free the memory\r
-               if(xml_page.memory){\r
-                       free( xml_page.memory );\r
-                       xml_page.memory = NULL;\r
-                       xml_page.size = 0;\r
-               }\r
+       // the url to get\r
+       std::string url = base_url + uri + "?";\r
+       if (params != "") {\r
+               url += params + "&api_key=" + api_key + "&format=xml";\r
+       } else {\r
+               url += "api_key=" + api_key + "&format=xml";\r
+       }\r
+               \r
+       curl_easy_setopt(curl, CURLOPT_URL, url.c_str() );\r
+       std::cerr << "doRequest: " << url << std::endl;\r
+       \r
+       // perform online request\r
+       CURLcode res = curl_easy_perform(curl);\r
+       if( res != 0 ) {\r
+               std::cerr << "curl error " << res << " (" << curl_easy_strerror(res) << ")" << std::endl;\r
+               return "";\r
+       }\r
 \r
-               std::cerr << "Login was cool, connection = "  << connection << std::endl;\r
-               return connection;\r
+       // free the memory\r
+       if (xml_page.memory) {\r
+               result = xml_page.memory;\r
        }\r
-       else return 3; // will be returned if a curl related problem ocurrs\r
-}\r
-//------------------------------------------------------------------------\r
-std::string    Mootcher::searchText(std::string word)\r
-{\r
-       struct MemoryStruct xml_page;\r
+       \r
+       free (xml_page.memory);\r
        xml_page.memory = NULL;\r
        xml_page.size = 0;\r
 \r
-       std::string result;\r
-\r
-       if(connection != 0)\r
-       {\r
-               // create a url encoded post message\r
-               std::string postMessage;\r
-               char tempString[ 128 ];\r
-               char *tempPointer = &tempString[0];\r
-\r
-               postMessage = "search=";\r
-               postMessage += curl_escape(word.c_str(), 0);\r
-               sprintf( tempPointer, "&searchDescriptions=1");\r
-               postMessage += tempPointer;\r
-               sprintf( tempPointer, "&searchtags=1");\r
-               postMessage += tempPointer;\r
-\r
-               // Ref: http://www.freesound.org/forum/viewtopic.php?p=19081\r
-               // const ORDER_DEFAULT = 0;\r
-               // const ORDER_DOWNLOADS_DESC = 1;\r
-               // const ORDER_DOWNLOADS_ASC = 2;\r
-               // const ORDER_USERNAME_DESC = 3;\r
-               // const ORDER_USERNAME_ASC = 4;\r
-               // const ORDER_DATE_DESC = 5;\r
-               // const ORDER_DATE_ASC = 6;\r
-               // const ORDER_DURATION_DESC = 7;\r
-               // const ORDER_DURATION_ASC = 8;\r
-               // const ORDER_FILEFORMAT_DESC = 9;\r
-               // const ORDER_FILEFORMAT_ASC = 10;\r
-               sprintf( tempPointer, "&order=1");\r
-               postMessage += tempPointer;\r
-               sprintf( tempPointer, "&start=0");\r
-               postMessage += tempPointer;\r
-               sprintf( tempPointer, "&limit=10");\r
-               postMessage += tempPointer;\r
-               // The limit of 10 samples is arbitrary, but seems\r
-               // reasonable in light of the fact that all of the returned\r
-               // samples get downloaded, and downloads are s-l-o-w.\r
-\r
-               if(curl)\r
-               {\r
-                       // basic init for curl\r
-                       setcUrlOptions();\r
-                       curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);\r
-                       curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&xml_page);\r
-                       // setup the post message\r
-                       curl_easy_setopt(curl, CURLOPT_POST, TRUE);\r
-                       curl_easy_setopt(curl, CURLOPT_POSTFIELDS, postMessage.c_str());\r
-                       curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, -1);\r
-\r
-                       // the url to get\r
-                       std::string search_url = "http://www.freesound.org/searchTextXML.php";\r
-                       curl_easy_setopt(curl, CURLOPT_URL, search_url.c_str());\r
-\r
-                       // perform the online search\r
-                       connection = 1;\r
-                       CURLcode res = curl_easy_perform(curl);\r
-                       if( res != 0 ) {\r
-                               toLog("curl login error\n");\r
-                               toLog(curl_easy_strerror(res));\r
-                               connection = 0;\r
-                       }\r
+       return result;\r
+}\r
 \r
-                       result = xml_page.memory;\r
-                       toLog( result.c_str() );\r
 \r
-                       // free the memory\r
-                       if(xml_page.memory){\r
-                               free( xml_page.memory );\r
-                               xml_page.memory = NULL;\r
-                               xml_page.size = 0;\r
-                       }\r
+std::string Mootcher::searchText(std::string query, int page, std::string filter, enum sortMethod sort)\r
+{\r
+       std::string params = "";\r
+       char buf[24];\r
 \r
-               }\r
+       if (page > 1) {\r
+               snprintf(buf, 23, "p=%d&", page);\r
+               params += buf;\r
        }\r
+       \r
+       params += "q=" + query; \r
 \r
-       return result;\r
-}\r
+       if (filter != "")\r
+               params += "&f=" + filter;\r
+       \r
+       if (sort)\r
+               params += "&s=" + sortMethodString(sort);\r
 \r
-//------------------------------------------------------------------------\r
-std::string Mootcher::changeExtension(std::string filename)\r
-{\r
-       std::string aiff = ".aiff";\r
-       std::string aif = ".aif";\r
-       std::string wav = ".wav";\r
-       std::string mp3 = ".mp3";\r
-       std::string ogg = ".ogg";\r
-       std::string flac = ".flac";\r
-\r
-       std::string replace = ".xml";\r
-       int pos = 0;\r
-\r
-       pos = (int)filename.find(aiff);\r
-       if(pos != std::string::npos) filename.replace(pos, aiff.size(), replace);\r
-       pos = (int)filename.find(aif);\r
-       if(pos != std::string::npos) filename.replace(pos, aif.size(), replace);\r
-       pos = (int)filename.find(wav);\r
-       if(pos != std::string::npos) filename.replace(pos, wav.size(), replace);\r
-       pos = (int)filename.find(mp3);\r
-       if(pos != std::string::npos) filename.replace(pos, mp3.size(), replace);\r
-       pos = (int)filename.find(ogg);\r
-       if(pos != std::string::npos) filename.replace(pos, ogg.size(), replace);\r
-       pos = (int)filename.find(flac);\r
-       if(pos != std::string::npos) filename.replace(pos, flac.size(), replace);\r
-\r
-       return filename;\r
+       params += "&fields=id,original_filename,duration,serve";        \r
+\r
+       return doRequest("/sounds/search", params);\r
 }\r
+\r
 //------------------------------------------------------------------------\r
-void           Mootcher::GetXml(std::string ID, struct MemoryStruct &xml_page)\r
+\r
+std::string Mootcher::getSoundResourceFile(std::string ID)\r
 {\r
 \r
-       if(curl) {\r
-               setcUrlOptions();\r
-               curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);\r
-               curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&xml_page);\r
+       std::string originalSoundURI;\r
+       std::string audioFileName;\r
+       std::string xml;\r
 \r
-               // URL to get\r
-               std::string getxml_url = "http://www.freesound.org/samplesViewSingleXML.php?id=";\r
-               getxml_url += ID;\r
 \r
-               curl_easy_setopt(curl, CURLOPT_URL, getxml_url.c_str() );\r
+       std::cerr << "getSoundResourceFile(" << ID << ")" << std::endl;\r
 \r
-               // get it!\r
-               connection = 1;\r
-               CURLcode res = curl_easy_perform(curl);\r
-               if( res != 0 ) {\r
-                       toLog("curl login error\n");\r
-                       toLog(curl_easy_strerror(res));\r
-                       connection = 0;\r
-               }\r
+       // download the xmlfile into xml_page\r
+       xml = doRequest("/sounds/" + ID, "");\r
+\r
+       XMLTree doc;\r
+       doc.read_buffer( xml.c_str() );\r
+       XMLNode *freesound = doc.root();\r
+\r
+       // if the page is not a valid xml document with a 'freesound' root\r
+       if (freesound == NULL) {\r
+               std::cerr << "getSoundResourceFile: There is no valid root in the xml file" << std::endl;\r
+               return "";\r
        }\r
-}\r
-//------------------------------------------------------------------------\r
-std::string    Mootcher::getXmlFile(std::string ID, int &length)\r
-{\r
-       struct MemoryStruct xml_page;\r
-       xml_page.memory = NULL;\r
-       xml_page.size = NULL;\r
 \r
-       std::string xmlFileName;\r
-       std::string audioFileName;\r
-       std::string filename;\r
-\r
-       if(connection != 0) {\r
-               // download the xmlfile into xml_page\r
-               GetXml(ID, xml_page);\r
-\r
-               // if sample ID does not exist on the freesound website\r
-               if(strcmp(xml_page.memory, "sample non existant") == 0){\r
-                       free( xml_page.memory );\r
-                       sprintf(message, "getXmlFile: sample with ID:%s does not exist!\n", ID.c_str() );\r
-                       toLog(message);\r
-                       return filename;\r
-               } else {\r
-                       XMLTree doc;\r
-                       doc.read_buffer( xml_page.memory );\r
-                       XMLNode *freesound = doc.root();\r
-\r
-                       // if the page is not a valid xml document with a 'freesound' root\r
-                       if( freesound == NULL){\r
-                               sprintf(message, "getXmlFile: There is no valid root in the xml file");\r
-                               toLog(message);\r
-                       } else {\r
-                               XMLNode *sample = freesound->child("sample");\r
-                               XMLNode *name = NULL;\r
-                               XMLNode *filesize = NULL;\r
-                               if (sample) {\r
-                                       name = sample->child("originalFilename");\r
-                                       filesize = sample->child("filesize");\r
-                               }\r
+       if (strcmp(doc.root()->name().c_str(), "response") != 0) {\r
+               std::cerr << "getSoundResourceFile: root =" << doc.root()->name() << ", != response" << std::endl;\r
+               return "";\r
+       }\r
 \r
-                               // get the file name and size from xml file\r
-                               if (sample && name && filesize) {\r
-\r
-                                       audioFileName = name->child("text")->content();\r
-                                       sprintf( message, "getXmlFile: %s needs to be downloaded\n", audioFileName.c_str() );\r
-                                       toLog(message);\r
-\r
-                                       length = atoi(filesize->child("text")->content().c_str());\r
-\r
-                                       // create new filename with the ID number\r
-                                       filename = basePath;\r
-                                       filename += "snd/";\r
-                                       filename += sample->property("id")->value();\r
-                                       filename += "-";\r
-                                       filename += audioFileName;\r
-                                       // change the extention into .xml\r
-                                       xmlFileName = changeExtension( filename );\r
-\r
-                                       sprintf(message, "getXmlFile: saving XML: %s\n", xmlFileName.c_str() );\r
-                                       toLog(message);\r
-\r
-                                       // save the xml file to disk\r
-                                       doc.write(xmlFileName.c_str());\r
-\r
-                                       //store all the tags in the database\r
-                                       XMLNode *tags = sample->child("tags");\r
-                                       if (tags) {\r
-                                               XMLNodeList children = tags->children();\r
-                                               XMLNodeConstIterator niter;\r
-                                               std::vector<std::string> strings;\r
-                                               for (niter = children.begin(); niter != children.end(); ++niter) {\r
-                                                       XMLNode *node = *niter;\r
-                                                       if( strcmp( node->name().c_str(), "tag") == 0 ) {\r
-                                                               XMLNode *text = node->child("text");\r
-                                                               if (text) strings.push_back(text->content());\r
-                                                       }\r
-                                               }\r
-                                               ARDOUR::Library->set_tags (std::string("//")+filename, strings);\r
-                                               ARDOUR::Library->save_changes ();\r
+       XMLNode *name = freesound->child("original_filename");\r
+\r
+       // get the file name and size from xml file\r
+       if (name) {\r
+\r
+               audioFileName = basePath + "snd/" + ID + "-" + name->child("text")->content();\r
+\r
+               //store all the tags in the database\r
+               XMLNode *tags = freesound->child("tags");\r
+               if (tags) {\r
+                       XMLNodeList children = tags->children();\r
+                       XMLNodeConstIterator niter;\r
+                       std::vector<std::string> strings;\r
+                       for (niter = children.begin(); niter != children.end(); ++niter) {\r
+                               XMLNode *node = *niter;\r
+                               if( strcmp( node->name().c_str(), "resource") == 0 ) {\r
+                                       XMLNode *text = node->child("text");\r
+                                       if (text) {\r
+                                               // std::cerr << "tag: " << text->content() << std::endl;\r
+                                               strings.push_back(text->content());\r
                                        }\r
                                }\r
-\r
-                               // clear the memory\r
-                               if(xml_page.memory){\r
-                                       free( xml_page.memory );\r
-                                       xml_page.memory = NULL;\r
-                                       xml_page.size = 0;\r
-                               }\r
-                               return audioFileName;\r
                        }\r
+                       ARDOUR::Library->set_tags (std::string("//")+audioFileName, strings);\r
+                       ARDOUR::Library->save_changes ();\r
                }\r
        }\r
-       else {\r
-               return audioFileName;\r
-       }\r
 \r
+       return audioFileName;\r
 }\r
 \r
 int audioFileWrite(void *buffer, size_t size, size_t nmemb, void *file)\r
@@ -440,85 +290,102 @@ int audioFileWrite(void *buffer, size_t size, size_t nmemb, void *file)
 };\r
 \r
 //------------------------------------------------------------------------\r
-std::string    Mootcher::getFile(std::string ID)\r
+std::string Mootcher::getAudioFile(std::string originalFileName, std::string ID, std::string audioURL, SoundFileBrowser *caller)\r
 {\r
-       CURLcode result_curl;\r
-\r
-       std::string audioFileName;\r
-\r
-       if(connection != 0)\r
-       {\r
-               int length;\r
-               std::string name = getXmlFile(ID, length);\r
-               if( name != "" ){\r
-\r
-                       // create new filename with the ID number\r
-                       audioFileName += basePath;\r
-                       audioFileName += "snd/";\r
-                       audioFileName += ID;\r
-                       audioFileName += "-";\r
-                       audioFileName += name;\r
-\r
-                       //check to see if audio file already exists\r
-                       FILE *testFile = fopen(audioFileName.c_str(), "r");\r
-                       if (testFile) {  //TODO:  should also check length to see if file is complete\r
-                               fseek (testFile , 0 , SEEK_END);\r
-                               if (ftell (testFile) == length) {\r
-                                       sprintf(message, "%s already exists\n", audioFileName.c_str() );\r
-                                       toLog(message);\r
-                                       fclose (testFile);\r
-                                       return audioFileName;\r
-                               } else {\r
-                                       remove( audioFileName.c_str() );  //file was not correct length, delete it and try again\r
-                               }\r
-                       }\r
-\r
-\r
-                       //now download the actual file\r
-                       if (curl) {\r
-\r
-                               FILE* theFile;\r
-                               theFile = fopen( audioFileName.c_str(), "wb" );\r
-\r
-                               // create the download url, this url will also update the download statics on the site\r
-                               std::string audioURL;\r
-                               audioURL += "http://www.freesound.org/samplesDownload.php?id=";\r
-                               audioURL += ID;\r
-\r
-                               setcUrlOptions();\r
-                               curl_easy_setopt(curl, CURLOPT_URL, audioURL.c_str() );\r
-                               curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, audioFileWrite);\r
-                               curl_easy_setopt(curl, CURLOPT_WRITEDATA, theFile);\r
+       ensureWorkingDir();\r
+       std::string audioFileName = basePath + "snd/" + ID + "-" + originalFileName;\r
+\r
+       // check to see if audio file already exists\r
+       FILE *testFile = g_fopen(audioFileName.c_str(), "r");\r
+       if (testFile) {  \r
+               fseek (testFile , 0 , SEEK_END);\r
+               if (ftell (testFile) > 256) {\r
+                       std::cerr << "audio file " << audioFileName << " already exists" << std::endl;\r
+                       fclose (testFile);\r
+                       return audioFileName;\r
+               }\r
+               \r
+               // else file was small, probably an error, delete it and try again\r
+               fclose(testFile);\r
+               remove( audioFileName.c_str() );  \r
+       }\r
 \r
-                               connection = 1;\r
-                               CURLcode res = curl_easy_perform(curl);\r
-                               if( res != 0 ) {\r
-                                       toLog("curl login error\n");\r
-                                       toLog(curl_easy_strerror(res));\r
-                                       connection = 0;\r
-                               }\r
+       if (!curl) {\r
+               return "";\r
+       }\r
 \r
-                               fclose(theFile);\r
-                       }\r
+       //if already canceling a previous download, bail out here  ( this can happen b/c getAudioFile gets called by various UI update funcs )\r
+       if ( caller->freesound_download_cancel ) {\r
+               return "";\r
+       }\r
+       \r
+       //now download the actual file\r
+       FILE* theFile;\r
+       theFile = g_fopen( audioFileName.c_str(), "wb" );\r
 \r
-/*\r
-                       bar.dlnowMoo = 0;\r
-                       bar.dltotalMoo = 0;\r
-                       curl_easy_setopt (curl, CURLOPT_NOPROGRESS, 0); // turn on the process bar thingy\r
-                       curl_easy_setopt (curl, CURLOPT_PROGRESSFUNCTION, progress_callback);\r
-                       curl_easy_setopt (curl, CURLOPT_PROGRESSDATA, &bar);\r
-*/\r
-               }\r
+       if (!theFile) {\r
+               return "";\r
+       }\r
+       \r
+       // create the download url\r
+       audioURL += "?api_key=" + api_key;\r
+\r
+       setcUrlOptions();\r
+       curl_easy_setopt(curl, CURLOPT_URL, audioURL.c_str() );\r
+       curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, audioFileWrite);\r
+       curl_easy_setopt(curl, CURLOPT_WRITEDATA, theFile);\r
+\r
+       std::cerr << "downloading " << audioFileName << " from " << audioURL << "..." << std::endl;\r
+       /* hack to get rid of the barber-pole stripes */\r
+       caller->freesound_progress_bar.hide();\r
+       caller->freesound_progress_bar.show();\r
+\r
+       std::string prog;\r
+       prog = string_compose (_("%1: [Stop]->"), originalFileName);\r
+       caller->freesound_progress_bar.set_text(prog);\r
+\r
+       curl_easy_setopt (curl, CURLOPT_NOPROGRESS, 0); // turn on the progress bar\r
+       curl_easy_setopt (curl, CURLOPT_PROGRESSFUNCTION, progress_callback);\r
+       curl_easy_setopt (curl, CURLOPT_PROGRESSDATA, caller);\r
+\r
+       CURLcode res = curl_easy_perform(curl);\r
+       fclose(theFile);\r
+\r
+       curl_easy_setopt (curl, CURLOPT_NOPROGRESS, 1); // turn off the progress bar\r
+       caller->freesound_progress_bar.set_fraction(0.0);\r
+       caller->freesound_progress_bar.set_text("");\r
+       \r
+       if( res != 0 ) {\r
+               std::cerr <<  "curl error " << res << " (" << curl_easy_strerror(res) << ")" << std::endl;\r
+               remove( audioFileName.c_str() );  \r
+               return "";\r
+       } else {\r
+               std::cerr << "done!" << std::endl;\r
+               // now download the tags &c.\r
+               getSoundResourceFile(ID);\r
        }\r
 \r
        return audioFileName;\r
 }\r
 \r
 //---------\r
-int Mootcher::progress_callback(void *bar, double dltotal, double dlnow, double ultotal, double ulnow)\r
+int Mootcher::progress_callback(void *caller, double dltotal, double dlnow, double /*ultotal*/, double /*ulnow*/)\r
 {\r
-       struct dlprocess *lbar = (struct dlprocess *) bar;\r
-       lbar->dltotalMoo = dltotal;\r
-       lbar->dlnowMoo = dlnow;\r
+\r
+SoundFileBrowser *sfb = (SoundFileBrowser *) caller;\r
+       //XXX I hope it's OK to do GTK things in this callback. Otherwise\r
+       // I'll have to do stuff like in interthread_progress_window.\r
+       if (sfb->freesound_download_cancel) {\r
+               return -1;\r
+       }\r
+       \r
+       \r
+       sfb->freesound_progress_bar.set_fraction(dlnow/dltotal);\r
+       /* Make sure the progress widget gets updated */\r
+       while (Glib::MainContext::get_default()->iteration (false)) {\r
+               /* do nothing */\r
+       }\r
+       std::cerr << "progress: " << dlnow << " of " << dltotal << " \r";\r
        return 0;\r
 }\r
+\r