ba9b4052c86615577383ab0e754a2e01d7ced060
[ardour.git] / gtk2_ardour / sfdb_freesound_mootcher.cc
1 /* sfdb_freesound_mootcher.cpp **********************************************************************\r
2         \r
3         Adapted for Ardour by Ben Loftis, March 2008\r
4                 \r
5         Mootcher 23-8-2005\r
6 \r
7         Mootcher Online Access to thefreesoundproject website\r
8         http://freesound.iua.upf.edu/\r
9 \r
10         GPL 2005 Jorn Lemon\r
11         mail for questions/remarks: mootcher@twistedlemon.nl\r
12         or go to the freesound website forum\r
13 \r
14         -----------------------------------------------------------------\r
15 \r
16         Includes:\r
17                 curl.h    (version 7.14.0)\r
18         Librarys:\r
19                 libcurl.lib\r
20 \r
21         -----------------------------------------------------------------\r
22         Licence GPL:\r
23 \r
24         This program is free software; you can redistribute it and/or\r
25         modify it under the terms of the GNU General Public License\r
26         as published by the Free Software Foundation; either version 2\r
27         of the License, or (at your option) any later version.\r
28 \r
29         This program is distributed in the hope that it will be useful,\r
30         but WITHOUT ANY WARRANTY; without even the implied warranty of\r
31         MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
32         GNU General Public License for more details.\r
33 \r
34         You should have received a copy of the GNU General Public License\r
35         along with this program; if not, write to the Free Software\r
36         Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.\r
37 \r
38 \r
39 *************************************************************************************/\r
40 #include "sfdb_freesound_mootcher.h"\r
41 \r
42 #include <pbd/xml++.h>\r
43 \r
44 #include <sys/stat.h>\r
45 #include <sys/types.h>\r
46 \r
47 #include <ardour/audio_library.h>\r
48 \r
49 #define TRUE 1\r
50 \r
51 //------------------------------------------------------------------------\r
52 Mootcher::      Mootcher(const char *saveLocation)\r
53         : curl( NULL )\r
54         , connection( NULL )\r
55 {\r
56         changeWorkingDir(saveLocation);\r
57 };\r
58 //------------------------------------------------------------------------\r
59 Mootcher::      ~Mootcher()\r
60 {\r
61         remove( "cookiejar.txt" );\r
62 }\r
63 //------------------------------------------------------------------------\r
64 const char* Mootcher::changeWorkingDir(const char *saveLocation)\r
65 {\r
66         basePath = saveLocation;\r
67 #ifdef __WIN32__\r
68         std::string replace = "/";\r
69         int pos = (int)basePath.find("\\");\r
70         while( pos != std::string::npos ){\r
71                 basePath.replace(pos, 1, replace);\r
72                 pos = (int)basePath.find("\\");\r
73         }\r
74 #endif\r
75         // \r
76         int pos2 = basePath.find_last_of("/");\r
77         if(basePath.length() != (pos2+1)) basePath += "/";\r
78         \r
79         // create Freesound directory and sound dir\r
80         std::string sndLocation = basePath;\r
81         mkdir(sndLocation.c_str(), 0x777);        \r
82         sndLocation += "snd";\r
83         mkdir(sndLocation.c_str(), 0x777);        \r
84 \r
85         return basePath.c_str();\r
86 }\r
87 \r
88 //------------------------------------------------------------------------\r
89 size_t          Mootcher::WriteMemoryCallback(void *ptr, size_t size, size_t nmemb, void *data)\r
90 {\r
91         register int realsize = (int)(size * nmemb);\r
92         struct MemoryStruct *mem = (struct MemoryStruct *)data;\r
93 \r
94         // There might be a realloc() out there that doesn't like \r
95         // reallocing NULL pointers, so we take care of it here\r
96         if(mem->memory) mem->memory = (char *)realloc(mem->memory, mem->size + realsize + 1);\r
97         else                    mem->memory = (char *)malloc(mem->size + realsize + 1);\r
98 \r
99         if (mem->memory) {\r
100                 memcpy(&(mem->memory[mem->size]), ptr, realsize);\r
101                 mem->size += realsize;\r
102                 mem->memory[mem->size] = 0;\r
103         }\r
104         return realsize;\r
105 }\r
106 \r
107 \r
108 //------------------------------------------------------------------------\r
109 void            Mootcher::toLog(std::string input)\r
110 {\r
111 //printf("%s", input.c_str());// for debugging\r
112 }\r
113 \r
114 \r
115 //------------------------------------------------------------------------\r
116 void            Mootcher::setcUrlOptions()\r
117 {\r
118         // basic init for curl\r
119         curl_global_init(CURL_GLOBAL_ALL);\r
120         // some servers don't like requests that are made without a user-agent field, so we provide one\r
121         curl_easy_setopt(curl, CURLOPT_USERAGENT, "libcurl-agent/1.0");\r
122         // setup curl error buffer\r
123         CURLcode res = curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, errorBuffer);\r
124         // always use the cookie with session id which is received at the login\r
125         curl_easy_setopt(curl, CURLOPT_COOKIEFILE, "cookiejar.txt");\r
126         // Allow redirection\r
127         curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1);\r
128 }\r
129 \r
130 //------------------------------------------------------------------------\r
131 int                     Mootcher::doLogin(std::string login, std::string password)\r
132 {\r
133         if(connection==1)\r
134                 return 1;\r
135         \r
136         struct MemoryStruct xml_page;\r
137         xml_page.memory = NULL;\r
138         xml_page.size = NULL;\r
139 \r
140         // create the post message from the login and password\r
141         std::string postMessage;\r
142         postMessage += "username=";\r
143         postMessage += curl_escape(login.c_str(), 0);\r
144         postMessage += "&password=";\r
145         postMessage += curl_escape(password.c_str(), 0);\r
146         postMessage += "&login=";\r
147         postMessage += curl_escape("1", 0);\r
148         postMessage += "&redirect=";\r
149         postMessage += curl_escape("../tests/login.php", 0);\r
150 \r
151         // Do the setup for libcurl\r
152         curl = curl_easy_init();\r
153 \r
154         if(curl)\r
155         {\r
156                 setcUrlOptions();\r
157                 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback); \r
158                 curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&xml_page);\r
159                 // save the sessoin id that is given back by the server in a cookie\r
160                 curl_easy_setopt(curl, CURLOPT_COOKIEJAR, "cookiejar.txt");\r
161                 // use POST for login variables\r
162                 curl_easy_setopt(curl, CURLOPT_POST, TRUE);\r
163                 curl_easy_setopt(curl, CURLOPT_POSTFIELDS, postMessage.c_str());\r
164                 curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, -1);\r
165 \r
166                 // the url to get\r
167                 std::string login_url = "http://freesound.iua.upf.edu/forum/login.php";\r
168                 curl_easy_setopt(curl, CURLOPT_URL, login_url.c_str() );\r
169 \r
170                 // perform online request\r
171                 connection = 1;\r
172                 CURLcode res = curl_easy_perform(curl);\r
173                 if( res != 0 ) {\r
174                         toLog("curl login error\n");\r
175                         toLog(curl_easy_strerror(res));\r
176                         connection = 0;\r
177                 }\r
178                 \r
179                 if (connection == 1){\r
180                         std::string check_page = xml_page.memory;\r
181                         int test = (int)check_page.find("login");   //logged\r
182                         if(     test != -1){\r
183                                 sprintf(message, "Login: %s\n", xml_page.memory);\r
184                                 toLog(message);\r
185                         }\r
186                         else {\r
187                                 sprintf(message, "Login: Check username and password\n");\r
188                                 toLog(message);\r
189                                 connection = 0;\r
190                         }\r
191                 }\r
192 \r
193                 // free the memory\r
194                 if(xml_page.memory){            \r
195                         free( xml_page.memory );\r
196                         xml_page.memory = NULL;\r
197                         xml_page.size = NULL;\r
198                 }\r
199 \r
200                 return connection;\r
201         }\r
202         else return 3; // will be returned if a curl related problem ocurrs\r
203 }\r
204 //------------------------------------------------------------------------\r
205 std::string     Mootcher::searchText(std::string word)\r
206 {\r
207         struct MemoryStruct xml_page;\r
208         xml_page.memory = NULL;\r
209         xml_page.size = NULL;\r
210         \r
211         std::string result;\r
212 \r
213         if(connection != 0)\r
214         {\r
215                 // create a url encoded post message\r
216                 std::string postMessage;\r
217                 char tempString[ 128 ];\r
218                 char *tempPointer = &tempString[0];\r
219 \r
220                 postMessage = "search=";\r
221                 postMessage += curl_escape(word.c_str(), 0);\r
222                 sprintf( tempPointer, "&searchDescriptions=1");\r
223                 postMessage += tempPointer;\r
224                 sprintf( tempPointer, "&searchtags=1");\r
225                 postMessage += tempPointer;\r
226                 \r
227                 if(curl)\r
228                 {\r
229                         // basic init for curl \r
230                         setcUrlOptions();       \r
231                         curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback); \r
232                         curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&xml_page);\r
233                         // setup the post message\r
234                         curl_easy_setopt(curl, CURLOPT_POST, TRUE);\r
235                         curl_easy_setopt(curl, CURLOPT_POSTFIELDS, postMessage.c_str());\r
236                         curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, -1);\r
237                         \r
238                         // the url to get\r
239                         std::string search_url = "http://freesound.iua.upf.edu/searchTextXML.php";\r
240                         curl_easy_setopt(curl, CURLOPT_URL, search_url.c_str());\r
241 \r
242                         // perform the online search \r
243                         connection = 1;\r
244                         CURLcode res = curl_easy_perform(curl);\r
245                         if( res != 0 ) {\r
246                                 toLog("curl login error\n");\r
247                                 toLog(curl_easy_strerror(res));\r
248                                 connection = 0;\r
249                         }\r
250                         \r
251                         result = xml_page.memory;\r
252                         toLog( result.c_str() );\r
253 \r
254                         // free the memory\r
255                         if(xml_page.memory){\r
256                                 free( xml_page.memory );\r
257                                 xml_page.memory = NULL;\r
258                                 xml_page.size = NULL;\r
259                         }\r
260 \r
261                 }\r
262         }\r
263 \r
264         return result;\r
265 }\r
266 \r
267 //------------------------------------------------------------------------\r
268 std::string Mootcher::changeExtension(std::string filename)\r
269 {\r
270         std::string aiff = ".aiff";\r
271         std::string aif = ".aif";\r
272         std::string wav = ".wav";\r
273         std::string mp3 = ".mp3";\r
274         std::string ogg = ".ogg";\r
275         std::string flac = ".flac";\r
276 \r
277         std::string replace = ".xml";\r
278         int pos = 0;\r
279 \r
280         pos = (int)filename.find(aiff);\r
281         if(pos != std::string::npos) filename.replace(pos, aiff.size(), replace); \r
282         pos = (int)filename.find(aif);\r
283         if(pos != std::string::npos) filename.replace(pos, aif.size(), replace); \r
284         pos = (int)filename.find(wav);\r
285         if(pos != std::string::npos) filename.replace(pos, wav.size(), replace); \r
286         pos = (int)filename.find(mp3);\r
287         if(pos != std::string::npos) filename.replace(pos, mp3.size(), replace); \r
288         pos = (int)filename.find(ogg);\r
289         if(pos != std::string::npos) filename.replace(pos, ogg.size(), replace); \r
290         pos = (int)filename.find(flac);\r
291         if(pos != std::string::npos) filename.replace(pos, flac.size(), replace); \r
292 \r
293         return filename;\r
294 }\r
295 //------------------------------------------------------------------------\r
296 void            Mootcher::GetXml(std::string ID, struct MemoryStruct &xml_page)\r
297 {\r
298 \r
299         if(curl) {\r
300                 setcUrlOptions();\r
301                 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback); \r
302                 curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&xml_page);\r
303 \r
304                 // URL to get\r
305                 std::string getxml_url = "http://freesound.iua.upf.edu/samplesViewSingleXML.php?id=";\r
306                 getxml_url += ID;\r
307 \r
308                 curl_easy_setopt(curl, CURLOPT_URL, getxml_url.c_str() );\r
309                 \r
310                 // get it!\r
311                 connection = 1;\r
312                 CURLcode res = curl_easy_perform(curl);\r
313                 if( res != 0 ) {\r
314                         toLog("curl login error\n");\r
315                         toLog(curl_easy_strerror(res));\r
316                         connection = 0;\r
317                 }\r
318         }\r
319 }\r
320 //------------------------------------------------------------------------\r
321 std::string     Mootcher::getXmlFile(std::string ID, int &length)\r
322 {\r
323         struct MemoryStruct xml_page;\r
324         xml_page.memory = NULL;\r
325         xml_page.size = NULL;\r
326 \r
327         std::string xmlFileName;\r
328         std::string audioFileName;\r
329         std::string filename;\r
330         \r
331         if(connection != 0) {\r
332                 // download the xmlfile into xml_page\r
333                 GetXml(ID, xml_page);\r
334 \r
335                 // if sample ID does not exist on the freesound website\r
336                 if(strcmp(xml_page.memory, "sample non existant") == 0){\r
337                         free( xml_page.memory );\r
338                         sprintf(message, "getXmlFile: sample with ID:%s does not exist!\n", ID.c_str() );\r
339                         toLog(message);\r
340                         return filename;\r
341                 } else {\r
342                         XMLTree doc;\r
343                         doc.read_buffer( xml_page.memory );\r
344                         XMLNode *freesound = doc.root();\r
345                         \r
346                         // if the page is not a valid xml document with a 'freesound' root\r
347                         if( freesound == NULL){\r
348                                 sprintf(message, "getXmlFile: There is no valid root in the xml file");\r
349                                 toLog(message);\r
350                         } else {\r
351                                 XMLNode *sample = freesound->child("sample");\r
352                                 XMLNode *name = NULL;\r
353                                 XMLNode *filesize = NULL;\r
354                                 if (sample) {\r
355                                         name = sample->child("originalFilename");\r
356                                         filesize = sample->child("filesize");\r
357                                 }\r
358                                 \r
359                                 // get the file name and size from xml file\r
360                                 if (sample && name && filesize) {\r
361                                         \r
362                                         audioFileName = name->child("text")->content();\r
363                                         sprintf( message, "getXmlFile: %s needs to be downloaded\n", audioFileName.c_str() );\r
364                                         toLog(message);\r
365 \r
366                                         length = atoi(filesize->child("text")->content().c_str());\r
367 \r
368                                         // create new filename with the ID number\r
369                                         filename = basePath;\r
370                                         filename += "snd/";\r
371                                         filename += sample->property("id")->value();\r
372                                         filename += "-";\r
373                                         filename += audioFileName;\r
374                                         // change the extention into .xml\r
375                                         xmlFileName = changeExtension( filename );\r
376 \r
377                                         sprintf(message, "getXmlFile: saving XML: %s\n", xmlFileName.c_str() );\r
378                                         toLog(message);\r
379                                         \r
380                                         // save the xml file to disk\r
381                                         doc.write(xmlFileName.c_str());\r
382 \r
383                                         //store all the tags in the database\r
384                                         XMLNode *tags = sample->child("tags");\r
385                                         if (tags) {\r
386                                                 XMLNodeList children = tags->children();\r
387                                                 XMLNodeConstIterator niter;\r
388                                                 vector<string> strings;\r
389                                                 for (niter = children.begin(); niter != children.end(); ++niter) {\r
390                                                         XMLNode *node = *niter;\r
391                                                         if( strcmp( node->name().c_str(), "tag") == 0 ) {\r
392                                                                 XMLNode *text = node->child("text");\r
393                                                                 if (text) strings.push_back(text->content());\r
394                                                         }\r
395                                                 }\r
396                                                 ARDOUR::Library->set_tags (string("//")+filename, strings);\r
397                                                 ARDOUR::Library->save_changes ();\r
398                                         }\r
399                                 }\r
400                                 \r
401                                 // clear the memory\r
402                                 if(xml_page.memory){\r
403                                         free( xml_page.memory );\r
404                                         xml_page.memory = NULL;\r
405                                         xml_page.size = 0;\r
406                                 }\r
407                                 return audioFileName;\r
408                         }\r
409                 }\r
410         }\r
411         else {\r
412                 return audioFileName;\r
413         }\r
414 \r
415 }\r
416 \r
417 int audioFileWrite(void *buffer, size_t size, size_t nmemb, void *file)\r
418 {\r
419         return (int)fwrite(buffer, size, nmemb, (FILE*) file);\r
420 };\r
421 \r
422 //------------------------------------------------------------------------\r
423 std::string     Mootcher::getFile(std::string ID)\r
424 {\r
425         CURLcode result_curl;\r
426 \r
427         std::string audioFileName;\r
428 \r
429         if(connection != 0)\r
430         {\r
431                 int length;\r
432                 std::string name = getXmlFile(ID, length);\r
433                 if( name != "" ){\r
434 \r
435                         // create new filename with the ID number\r
436                         audioFileName += basePath;\r
437                         audioFileName += "snd/";\r
438                         audioFileName += ID;\r
439                         audioFileName += "-";                   \r
440                         audioFileName += name;\r
441                         \r
442                         //check to see if audio file already exists\r
443                         FILE *testFile = fopen(audioFileName.c_str(), "r");\r
444                         if (testFile) {  //TODO:  should also check length to see if file is complete\r
445                                 fseek (testFile , 0 , SEEK_END);\r
446                                 if (ftell (testFile) == length) {\r
447                                         sprintf(message, "%s already exists\n", audioFileName.c_str() );\r
448                                         toLog(message);\r
449                                         fclose (testFile);\r
450                                         return audioFileName;\r
451                                 } else {\r
452                                         remove( audioFileName.c_str() );  //file was not correct length, delete it and try again\r
453                                 }                                       \r
454                         }\r
455                         \r
456 \r
457                         //now download the actual file\r
458                         if (curl) {\r
459 \r
460                                 FILE* theFile;\r
461                                 theFile = fopen( audioFileName.c_str(), "wb" );\r
462 \r
463                                 // create the download url, this url will also update the download statics on the site\r
464                                 std::string audioURL;\r
465                                 audioURL += "http://freesound.iua.upf.edu/samplesDownload.php?id=";\r
466                                 audioURL += ID;\r
467 \r
468                                 setcUrlOptions();\r
469                                 curl_easy_setopt(curl, CURLOPT_URL, audioURL.c_str() );\r
470                                 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, audioFileWrite);\r
471                                 curl_easy_setopt(curl, CURLOPT_WRITEDATA, theFile);\r
472 \r
473                                 connection = 1;\r
474                                 CURLcode res = curl_easy_perform(curl);\r
475                                 if( res != 0 ) {\r
476                                         toLog("curl login error\n");\r
477                                         toLog(curl_easy_strerror(res));\r
478                                         connection = 0;\r
479                                 }\r
480 \r
481                                 fclose(theFile);\r
482                         }\r
483         \r
484 /*\r
485                         bar.dlnowMoo = 0;\r
486                         bar.dltotalMoo = 0;\r
487                         curl_easy_setopt (curl, CURLOPT_NOPROGRESS, 0); // turn on the process bar thingy\r
488                         curl_easy_setopt (curl, CURLOPT_PROGRESSFUNCTION, progress_callback);\r
489                         curl_easy_setopt (curl, CURLOPT_PROGRESSDATA, &bar);\r
490 */                              \r
491                 }\r
492         }\r
493 \r
494         return audioFileName;\r
495 }\r
496 \r
497 //---------\r
498 int Mootcher::progress_callback(void *bar, double dltotal, double dlnow, double ultotal, double ulnow)\r
499 {\r
500         struct dlprocess *lbar = (struct dlprocess *) bar;\r
501         lbar->dltotalMoo = dltotal;\r
502         lbar->dlnowMoo = dlnow;\r
503         return 0;\r
504 }\r