Missing namespaces
[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                 if(curl)\r
226                 {\r
227                         // basic init for curl \r
228                         setcUrlOptions();       \r
229                         curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback); \r
230                         curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&xml_page);\r
231                         // setup the post message\r
232                         curl_easy_setopt(curl, CURLOPT_POST, TRUE);\r
233                         curl_easy_setopt(curl, CURLOPT_POSTFIELDS, postMessage.c_str());\r
234                         curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, -1);\r
235                         \r
236                         // the url to get\r
237                         std::string search_url = "http://www.freesound.org/searchTextXML.php";\r
238                         curl_easy_setopt(curl, CURLOPT_URL, search_url.c_str());\r
239 \r
240                         // perform the online search \r
241                         connection = 1;\r
242                         CURLcode res = curl_easy_perform(curl);\r
243                         if( res != 0 ) {\r
244                                 toLog("curl login error\n");\r
245                                 toLog(curl_easy_strerror(res));\r
246                                 connection = 0;\r
247                         }\r
248                         \r
249                         result = xml_page.memory;\r
250                         toLog( result.c_str() );\r
251 \r
252                         // free the memory\r
253                         if(xml_page.memory){\r
254                                 free( xml_page.memory );\r
255                                 xml_page.memory = NULL;\r
256                                 xml_page.size = 0;\r
257                         }\r
258 \r
259                 }\r
260         }\r
261 \r
262         return result;\r
263 }\r
264 \r
265 //------------------------------------------------------------------------\r
266 std::string Mootcher::changeExtension(std::string filename)\r
267 {\r
268         std::string aiff = ".aiff";\r
269         std::string aif = ".aif";\r
270         std::string wav = ".wav";\r
271         std::string mp3 = ".mp3";\r
272         std::string ogg = ".ogg";\r
273         std::string flac = ".flac";\r
274 \r
275         std::string replace = ".xml";\r
276         int pos = 0;\r
277 \r
278         pos = (int)filename.find(aiff);\r
279         if(pos != std::string::npos) filename.replace(pos, aiff.size(), replace); \r
280         pos = (int)filename.find(aif);\r
281         if(pos != std::string::npos) filename.replace(pos, aif.size(), replace); \r
282         pos = (int)filename.find(wav);\r
283         if(pos != std::string::npos) filename.replace(pos, wav.size(), replace); \r
284         pos = (int)filename.find(mp3);\r
285         if(pos != std::string::npos) filename.replace(pos, mp3.size(), replace); \r
286         pos = (int)filename.find(ogg);\r
287         if(pos != std::string::npos) filename.replace(pos, ogg.size(), replace); \r
288         pos = (int)filename.find(flac);\r
289         if(pos != std::string::npos) filename.replace(pos, flac.size(), replace); \r
290 \r
291         return filename;\r
292 }\r
293 //------------------------------------------------------------------------\r
294 void            Mootcher::GetXml(std::string ID, struct MemoryStruct &xml_page)\r
295 {\r
296 \r
297         if(curl) {\r
298                 setcUrlOptions();\r
299                 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback); \r
300                 curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&xml_page);\r
301 \r
302                 // URL to get\r
303                 std::string getxml_url = "http://www.freesound.org/samplesViewSingleXML.php?id=";\r
304                 getxml_url += ID;\r
305 \r
306                 curl_easy_setopt(curl, CURLOPT_URL, getxml_url.c_str() );\r
307                 \r
308                 // get it!\r
309                 connection = 1;\r
310                 CURLcode res = curl_easy_perform(curl);\r
311                 if( res != 0 ) {\r
312                         toLog("curl login error\n");\r
313                         toLog(curl_easy_strerror(res));\r
314                         connection = 0;\r
315                 }\r
316         }\r
317 }\r
318 //------------------------------------------------------------------------\r
319 std::string     Mootcher::getXmlFile(std::string ID, int &length)\r
320 {\r
321         struct MemoryStruct xml_page;\r
322         xml_page.memory = NULL;\r
323         xml_page.size = NULL;\r
324 \r
325         std::string xmlFileName;\r
326         std::string audioFileName;\r
327         std::string filename;\r
328         \r
329         if(connection != 0) {\r
330                 // download the xmlfile into xml_page\r
331                 GetXml(ID, xml_page);\r
332 \r
333                 // if sample ID does not exist on the freesound website\r
334                 if(strcmp(xml_page.memory, "sample non existant") == 0){\r
335                         free( xml_page.memory );\r
336                         sprintf(message, "getXmlFile: sample with ID:%s does not exist!\n", ID.c_str() );\r
337                         toLog(message);\r
338                         return filename;\r
339                 } else {\r
340                         XMLTree doc;\r
341                         doc.read_buffer( xml_page.memory );\r
342                         XMLNode *freesound = doc.root();\r
343                         \r
344                         // if the page is not a valid xml document with a 'freesound' root\r
345                         if( freesound == NULL){\r
346                                 sprintf(message, "getXmlFile: There is no valid root in the xml file");\r
347                                 toLog(message);\r
348                         } else {\r
349                                 XMLNode *sample = freesound->child("sample");\r
350                                 XMLNode *name = NULL;\r
351                                 XMLNode *filesize = NULL;\r
352                                 if (sample) {\r
353                                         name = sample->child("originalFilename");\r
354                                         filesize = sample->child("filesize");\r
355                                 }\r
356                                 \r
357                                 // get the file name and size from xml file\r
358                                 if (sample && name && filesize) {\r
359                                         \r
360                                         audioFileName = name->child("text")->content();\r
361                                         sprintf( message, "getXmlFile: %s needs to be downloaded\n", audioFileName.c_str() );\r
362                                         toLog(message);\r
363 \r
364                                         length = atoi(filesize->child("text")->content().c_str());\r
365 \r
366                                         // create new filename with the ID number\r
367                                         filename = basePath;\r
368                                         filename += "snd/";\r
369                                         filename += sample->property("id")->value();\r
370                                         filename += "-";\r
371                                         filename += audioFileName;\r
372                                         // change the extention into .xml\r
373                                         xmlFileName = changeExtension( filename );\r
374 \r
375                                         sprintf(message, "getXmlFile: saving XML: %s\n", xmlFileName.c_str() );\r
376                                         toLog(message);\r
377                                         \r
378                                         // save the xml file to disk\r
379                                         doc.write(xmlFileName.c_str());\r
380 \r
381                                         //store all the tags in the database\r
382                                         XMLNode *tags = sample->child("tags");\r
383                                         if (tags) {\r
384                                                 XMLNodeList children = tags->children();\r
385                                                 XMLNodeConstIterator niter;\r
386                                                 std::vector<std::string> strings;\r
387                                                 for (niter = children.begin(); niter != children.end(); ++niter) {\r
388                                                         XMLNode *node = *niter;\r
389                                                         if( strcmp( node->name().c_str(), "tag") == 0 ) {\r
390                                                                 XMLNode *text = node->child("text");\r
391                                                                 if (text) strings.push_back(text->content());\r
392                                                         }\r
393                                                 }\r
394                                                 ARDOUR::Library->set_tags (std::string("//")+filename, strings);\r
395                                                 ARDOUR::Library->save_changes ();\r
396                                         }\r
397                                 }\r
398                                 \r
399                                 // clear the memory\r
400                                 if(xml_page.memory){\r
401                                         free( xml_page.memory );\r
402                                         xml_page.memory = NULL;\r
403                                         xml_page.size = 0;\r
404                                 }\r
405                                 return audioFileName;\r
406                         }\r
407                 }\r
408         }\r
409         else {\r
410                 return audioFileName;\r
411         }\r
412 \r
413 }\r
414 \r
415 int audioFileWrite(void *buffer, size_t size, size_t nmemb, void *file)\r
416 {\r
417         return (int)fwrite(buffer, size, nmemb, (FILE*) file);\r
418 };\r
419 \r
420 //------------------------------------------------------------------------\r
421 std::string     Mootcher::getFile(std::string ID)\r
422 {\r
423         CURLcode result_curl;\r
424 \r
425         std::string audioFileName;\r
426 \r
427         if(connection != 0)\r
428         {\r
429                 int length;\r
430                 std::string name = getXmlFile(ID, length);\r
431                 if( name != "" ){\r
432 \r
433                         // create new filename with the ID number\r
434                         audioFileName += basePath;\r
435                         audioFileName += "snd/";\r
436                         audioFileName += ID;\r
437                         audioFileName += "-";                   \r
438                         audioFileName += name;\r
439                         \r
440                         //check to see if audio file already exists\r
441                         FILE *testFile = fopen(audioFileName.c_str(), "r");\r
442                         if (testFile) {  //TODO:  should also check length to see if file is complete\r
443                                 fseek (testFile , 0 , SEEK_END);\r
444                                 if (ftell (testFile) == length) {\r
445                                         sprintf(message, "%s already exists\n", audioFileName.c_str() );\r
446                                         toLog(message);\r
447                                         fclose (testFile);\r
448                                         return audioFileName;\r
449                                 } else {\r
450                                         remove( audioFileName.c_str() );  //file was not correct length, delete it and try again\r
451                                 }                                       \r
452                         }\r
453                         \r
454 \r
455                         //now download the actual file\r
456                         if (curl) {\r
457 \r
458                                 FILE* theFile;\r
459                                 theFile = fopen( audioFileName.c_str(), "wb" );\r
460 \r
461                                 // create the download url, this url will also update the download statics on the site\r
462                                 std::string audioURL;\r
463                                 audioURL += "http://www.freesound.org/samplesDownload.php?id=";\r
464                                 audioURL += ID;\r
465 \r
466                                 setcUrlOptions();\r
467                                 curl_easy_setopt(curl, CURLOPT_URL, audioURL.c_str() );\r
468                                 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, audioFileWrite);\r
469                                 curl_easy_setopt(curl, CURLOPT_WRITEDATA, theFile);\r
470 \r
471                                 connection = 1;\r
472                                 CURLcode res = curl_easy_perform(curl);\r
473                                 if( res != 0 ) {\r
474                                         toLog("curl login error\n");\r
475                                         toLog(curl_easy_strerror(res));\r
476                                         connection = 0;\r
477                                 }\r
478 \r
479                                 fclose(theFile);\r
480                         }\r
481         \r
482 /*\r
483                         bar.dlnowMoo = 0;\r
484                         bar.dltotalMoo = 0;\r
485                         curl_easy_setopt (curl, CURLOPT_NOPROGRESS, 0); // turn on the process bar thingy\r
486                         curl_easy_setopt (curl, CURLOPT_PROGRESSFUNCTION, progress_callback);\r
487                         curl_easy_setopt (curl, CURLOPT_PROGRESSDATA, &bar);\r
488 */                              \r
489                 }\r
490         }\r
491 \r
492         return audioFileName;\r
493 }\r
494 \r
495 //---------\r
496 int Mootcher::progress_callback(void *bar, double dltotal, double dlnow, double ultotal, double ulnow)\r
497 {\r
498         struct dlprocess *lbar = (struct dlprocess *) bar;\r
499         lbar->dltotalMoo = dltotal;\r
500         lbar->dlnowMoo = dlnow;\r
501         return 0;\r
502 }\r