Delete trailing whitespace
[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( 0 )\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(), 0777);\r
82         sndLocation += "snd";\r
83         mkdir(sndLocation.c_str(), 0777);\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\n", 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 = 0;\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://www.freesound.org/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(     strcmp(xml_page.memory, "login") == 0 )\r
183                                 toLog("Logged in.\n");\r
184                         else {\r
185                                 toLog("Login failed: Check username and password.\n");\r
186                                 connection = 0;\r
187                         }\r
188                 }\r
189 \r
190                 // free the memory\r
191                 if(xml_page.memory){\r
192                         free( xml_page.memory );\r
193                         xml_page.memory = NULL;\r
194                         xml_page.size = 0;\r
195                 }\r
196 \r
197                 std::cerr << "Login was cool, connection = "  << connection << std::endl;\r
198                 return connection;\r
199         }\r
200         else return 3; // will be returned if a curl related problem ocurrs\r
201 }\r
202 //------------------------------------------------------------------------\r
203 std::string     Mootcher::searchText(std::string word)\r
204 {\r
205         struct MemoryStruct xml_page;\r
206         xml_page.memory = NULL;\r
207         xml_page.size = 0;\r
208 \r
209         std::string result;\r
210 \r
211         if(connection != 0)\r
212         {\r
213                 // create a url encoded post message\r
214                 std::string postMessage;\r
215                 char tempString[ 128 ];\r
216                 char *tempPointer = &tempString[0];\r
217 \r
218                 postMessage = "search=";\r
219                 postMessage += curl_escape(word.c_str(), 0);\r
220                 sprintf( tempPointer, "&searchDescriptions=1");\r
221                 postMessage += tempPointer;\r
222                 sprintf( tempPointer, "&searchtags=1");\r
223                 postMessage += tempPointer;\r
224 \r
225                 // Ref: http://www.freesound.org/forum/viewtopic.php?p=19081\r
226                 // const ORDER_DEFAULT = 0;\r
227                 // const ORDER_DOWNLOADS_DESC = 1;\r
228                 // const ORDER_DOWNLOADS_ASC = 2;\r
229                 // const ORDER_USERNAME_DESC = 3;\r
230                 // const ORDER_USERNAME_ASC = 4;\r
231                 // const ORDER_DATE_DESC = 5;\r
232                 // const ORDER_DATE_ASC = 6;\r
233                 // const ORDER_DURATION_DESC = 7;\r
234                 // const ORDER_DURATION_ASC = 8;\r
235                 // const ORDER_FILEFORMAT_DESC = 9;\r
236                 // const ORDER_FILEFORMAT_ASC = 10;\r
237                 sprintf( tempPointer, "&order=1");\r
238                 postMessage += tempPointer;\r
239                 sprintf( tempPointer, "&start=0");\r
240                 postMessage += tempPointer;\r
241                 sprintf( tempPointer, "&limit=10");\r
242                 postMessage += tempPointer;\r
243                 // The limit of 10 samples is arbitrary, but seems\r
244                 // reasonable in light of the fact that all of the returned\r
245                 // samples get downloaded, and downloads are s-l-o-w.\r
246 \r
247                 if(curl)\r
248                 {\r
249                         // basic init for curl\r
250                         setcUrlOptions();\r
251                         curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);\r
252                         curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&xml_page);\r
253                         // setup the post message\r
254                         curl_easy_setopt(curl, CURLOPT_POST, TRUE);\r
255                         curl_easy_setopt(curl, CURLOPT_POSTFIELDS, postMessage.c_str());\r
256                         curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, -1);\r
257 \r
258                         // the url to get\r
259                         std::string search_url = "http://www.freesound.org/searchTextXML.php";\r
260                         curl_easy_setopt(curl, CURLOPT_URL, search_url.c_str());\r
261 \r
262                         // perform the online search\r
263                         connection = 1;\r
264                         CURLcode res = curl_easy_perform(curl);\r
265                         if( res != 0 ) {\r
266                                 toLog("curl login error\n");\r
267                                 toLog(curl_easy_strerror(res));\r
268                                 connection = 0;\r
269                         }\r
270 \r
271                         result = xml_page.memory;\r
272                         toLog( result.c_str() );\r
273 \r
274                         // free the memory\r
275                         if(xml_page.memory){\r
276                                 free( xml_page.memory );\r
277                                 xml_page.memory = NULL;\r
278                                 xml_page.size = 0;\r
279                         }\r
280 \r
281                 }\r
282         }\r
283 \r
284         return result;\r
285 }\r
286 \r
287 //------------------------------------------------------------------------\r
288 std::string Mootcher::changeExtension(std::string filename)\r
289 {\r
290         std::string aiff = ".aiff";\r
291         std::string aif = ".aif";\r
292         std::string wav = ".wav";\r
293         std::string mp3 = ".mp3";\r
294         std::string ogg = ".ogg";\r
295         std::string flac = ".flac";\r
296 \r
297         std::string replace = ".xml";\r
298         int pos = 0;\r
299 \r
300         pos = (int)filename.find(aiff);\r
301         if(pos != std::string::npos) filename.replace(pos, aiff.size(), replace);\r
302         pos = (int)filename.find(aif);\r
303         if(pos != std::string::npos) filename.replace(pos, aif.size(), replace);\r
304         pos = (int)filename.find(wav);\r
305         if(pos != std::string::npos) filename.replace(pos, wav.size(), replace);\r
306         pos = (int)filename.find(mp3);\r
307         if(pos != std::string::npos) filename.replace(pos, mp3.size(), replace);\r
308         pos = (int)filename.find(ogg);\r
309         if(pos != std::string::npos) filename.replace(pos, ogg.size(), replace);\r
310         pos = (int)filename.find(flac);\r
311         if(pos != std::string::npos) filename.replace(pos, flac.size(), replace);\r
312 \r
313         return filename;\r
314 }\r
315 //------------------------------------------------------------------------\r
316 void            Mootcher::GetXml(std::string ID, struct MemoryStruct &xml_page)\r
317 {\r
318 \r
319         if(curl) {\r
320                 setcUrlOptions();\r
321                 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);\r
322                 curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&xml_page);\r
323 \r
324                 // URL to get\r
325                 std::string getxml_url = "http://www.freesound.org/samplesViewSingleXML.php?id=";\r
326                 getxml_url += ID;\r
327 \r
328                 curl_easy_setopt(curl, CURLOPT_URL, getxml_url.c_str() );\r
329 \r
330                 // get it!\r
331                 connection = 1;\r
332                 CURLcode res = curl_easy_perform(curl);\r
333                 if( res != 0 ) {\r
334                         toLog("curl login error\n");\r
335                         toLog(curl_easy_strerror(res));\r
336                         connection = 0;\r
337                 }\r
338         }\r
339 }\r
340 //------------------------------------------------------------------------\r
341 std::string     Mootcher::getXmlFile(std::string ID, int &length)\r
342 {\r
343         struct MemoryStruct xml_page;\r
344         xml_page.memory = NULL;\r
345         xml_page.size = NULL;\r
346 \r
347         std::string xmlFileName;\r
348         std::string audioFileName;\r
349         std::string filename;\r
350 \r
351         if(connection != 0) {\r
352                 // download the xmlfile into xml_page\r
353                 GetXml(ID, xml_page);\r
354 \r
355                 // if sample ID does not exist on the freesound website\r
356                 if(strcmp(xml_page.memory, "sample non existant") == 0){\r
357                         free( xml_page.memory );\r
358                         sprintf(message, "getXmlFile: sample with ID:%s does not exist!\n", ID.c_str() );\r
359                         toLog(message);\r
360                         return filename;\r
361                 } else {\r
362                         XMLTree doc;\r
363                         doc.read_buffer( xml_page.memory );\r
364                         XMLNode *freesound = doc.root();\r
365 \r
366                         // if the page is not a valid xml document with a 'freesound' root\r
367                         if( freesound == NULL){\r
368                                 sprintf(message, "getXmlFile: There is no valid root in the xml file");\r
369                                 toLog(message);\r
370                         } else {\r
371                                 XMLNode *sample = freesound->child("sample");\r
372                                 XMLNode *name = NULL;\r
373                                 XMLNode *filesize = NULL;\r
374                                 if (sample) {\r
375                                         name = sample->child("originalFilename");\r
376                                         filesize = sample->child("filesize");\r
377                                 }\r
378 \r
379                                 // get the file name and size from xml file\r
380                                 if (sample && name && filesize) {\r
381 \r
382                                         audioFileName = name->child("text")->content();\r
383                                         sprintf( message, "getXmlFile: %s needs to be downloaded\n", audioFileName.c_str() );\r
384                                         toLog(message);\r
385 \r
386                                         length = atoi(filesize->child("text")->content().c_str());\r
387 \r
388                                         // create new filename with the ID number\r
389                                         filename = basePath;\r
390                                         filename += "snd/";\r
391                                         filename += sample->property("id")->value();\r
392                                         filename += "-";\r
393                                         filename += audioFileName;\r
394                                         // change the extention into .xml\r
395                                         xmlFileName = changeExtension( filename );\r
396 \r
397                                         sprintf(message, "getXmlFile: saving XML: %s\n", xmlFileName.c_str() );\r
398                                         toLog(message);\r
399 \r
400                                         // save the xml file to disk\r
401                                         doc.write(xmlFileName.c_str());\r
402 \r
403                                         //store all the tags in the database\r
404                                         XMLNode *tags = sample->child("tags");\r
405                                         if (tags) {\r
406                                                 XMLNodeList children = tags->children();\r
407                                                 XMLNodeConstIterator niter;\r
408                                                 std::vector<std::string> strings;\r
409                                                 for (niter = children.begin(); niter != children.end(); ++niter) {\r
410                                                         XMLNode *node = *niter;\r
411                                                         if( strcmp( node->name().c_str(), "tag") == 0 ) {\r
412                                                                 XMLNode *text = node->child("text");\r
413                                                                 if (text) strings.push_back(text->content());\r
414                                                         }\r
415                                                 }\r
416                                                 ARDOUR::Library->set_tags (std::string("//")+filename, strings);\r
417                                                 ARDOUR::Library->save_changes ();\r
418                                         }\r
419                                 }\r
420 \r
421                                 // clear the memory\r
422                                 if(xml_page.memory){\r
423                                         free( xml_page.memory );\r
424                                         xml_page.memory = NULL;\r
425                                         xml_page.size = 0;\r
426                                 }\r
427                                 return audioFileName;\r
428                         }\r
429                 }\r
430         }\r
431         else {\r
432                 return audioFileName;\r
433         }\r
434 \r
435 }\r
436 \r
437 int audioFileWrite(void *buffer, size_t size, size_t nmemb, void *file)\r
438 {\r
439         return (int)fwrite(buffer, size, nmemb, (FILE*) file);\r
440 };\r
441 \r
442 //------------------------------------------------------------------------\r
443 std::string     Mootcher::getFile(std::string ID)\r
444 {\r
445         CURLcode result_curl;\r
446 \r
447         std::string audioFileName;\r
448 \r
449         if(connection != 0)\r
450         {\r
451                 int length;\r
452                 std::string name = getXmlFile(ID, length);\r
453                 if( name != "" ){\r
454 \r
455                         // create new filename with the ID number\r
456                         audioFileName += basePath;\r
457                         audioFileName += "snd/";\r
458                         audioFileName += ID;\r
459                         audioFileName += "-";\r
460                         audioFileName += name;\r
461 \r
462                         //check to see if audio file already exists\r
463                         FILE *testFile = fopen(audioFileName.c_str(), "r");\r
464                         if (testFile) {  //TODO:  should also check length to see if file is complete\r
465                                 fseek (testFile , 0 , SEEK_END);\r
466                                 if (ftell (testFile) == length) {\r
467                                         sprintf(message, "%s already exists\n", audioFileName.c_str() );\r
468                                         toLog(message);\r
469                                         fclose (testFile);\r
470                                         return audioFileName;\r
471                                 } else {\r
472                                         remove( audioFileName.c_str() );  //file was not correct length, delete it and try again\r
473                                 }\r
474                         }\r
475 \r
476 \r
477                         //now download the actual file\r
478                         if (curl) {\r
479 \r
480                                 FILE* theFile;\r
481                                 theFile = fopen( audioFileName.c_str(), "wb" );\r
482 \r
483                                 // create the download url, this url will also update the download statics on the site\r
484                                 std::string audioURL;\r
485                                 audioURL += "http://www.freesound.org/samplesDownload.php?id=";\r
486                                 audioURL += ID;\r
487 \r
488                                 setcUrlOptions();\r
489                                 curl_easy_setopt(curl, CURLOPT_URL, audioURL.c_str() );\r
490                                 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, audioFileWrite);\r
491                                 curl_easy_setopt(curl, CURLOPT_WRITEDATA, theFile);\r
492 \r
493                                 connection = 1;\r
494                                 CURLcode res = curl_easy_perform(curl);\r
495                                 if( res != 0 ) {\r
496                                         toLog("curl login error\n");\r
497                                         toLog(curl_easy_strerror(res));\r
498                                         connection = 0;\r
499                                 }\r
500 \r
501                                 fclose(theFile);\r
502                         }\r
503 \r
504 /*\r
505                         bar.dlnowMoo = 0;\r
506                         bar.dltotalMoo = 0;\r
507                         curl_easy_setopt (curl, CURLOPT_NOPROGRESS, 0); // turn on the process bar thingy\r
508                         curl_easy_setopt (curl, CURLOPT_PROGRESSFUNCTION, progress_callback);\r
509                         curl_easy_setopt (curl, CURLOPT_PROGRESSDATA, &bar);\r
510 */\r
511                 }\r
512         }\r
513 \r
514         return audioFileName;\r
515 }\r
516 \r
517 //---------\r
518 int Mootcher::progress_callback(void *bar, double dltotal, double dlnow, double ultotal, double ulnow)\r
519 {\r
520         struct dlprocess *lbar = (struct dlprocess *) bar;\r
521         lbar->dltotalMoo = dltotal;\r
522         lbar->dlnowMoo = dlnow;\r
523         return 0;\r
524 }\r