initial checkin of freesound integration
[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         // add a check if the given directory exists\r
79         createResourceLocation();\r
80         return basePath.c_str();\r
81 }\r
82 \r
83 //------------------------------------------------------------------------\r
84 void            Mootcher::createResourceLocation()\r
85 {       \r
86                 // create a snd directory\r
87                 std::string sndLocation = basePath + "snd";\r
88                 mkdir(sndLocation.c_str(), 0x777);        \r
89 }\r
90 \r
91 //------------------------------------------------------------------------\r
92 size_t          Mootcher::WriteMemoryCallback(void *ptr, size_t size, size_t nmemb, void *data)\r
93 {\r
94         register int realsize = (int)(size * nmemb);\r
95         struct MemoryStruct *mem = (struct MemoryStruct *)data;\r
96 \r
97         // There might be a realloc() out there that doesn't like \r
98         // reallocing NULL pointers, so we take care of it here\r
99         if(mem->memory) mem->memory = (char *)realloc(mem->memory, mem->size + realsize + 1);\r
100         else                    mem->memory = (char *)malloc(mem->size + realsize + 1);\r
101 \r
102         if (mem->memory) {\r
103                 memcpy(&(mem->memory[mem->size]), ptr, realsize);\r
104                 mem->size += realsize;\r
105                 mem->memory[mem->size] = 0;\r
106         }\r
107         return realsize;\r
108 }\r
109 \r
110 \r
111 //------------------------------------------------------------------------\r
112 void            Mootcher::toLog(std::string input)\r
113 {\r
114 //printf("%s", input.c_str());// for debugging\r
115 }\r
116 \r
117 \r
118 //------------------------------------------------------------------------\r
119 void            Mootcher::setcUrlOptions()\r
120 {\r
121         // basic init for curl\r
122         curl_global_init(CURL_GLOBAL_ALL);\r
123         // some servers don't like requests that are made without a user-agent field, so we provide one\r
124         curl_easy_setopt(curl, CURLOPT_USERAGENT, "libcurl-agent/1.0");\r
125         // setup curl error buffer\r
126         CURLcode res = curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, errorBuffer);\r
127         // always use the cookie with session id which is received at the login\r
128         curl_easy_setopt(curl, CURLOPT_COOKIEFILE, "cookiejar.txt");\r
129         // Allow redirection\r
130         curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1);\r
131 }\r
132 \r
133 //------------------------------------------------------------------------\r
134 int                     Mootcher::doLogin(std::string login, std::string password)\r
135 {\r
136         if(connection==1)\r
137                 return 1;\r
138         \r
139         struct MemoryStruct xml_page;\r
140         xml_page.memory = NULL;\r
141         xml_page.size = NULL;\r
142 \r
143         // create the post message from the login and password\r
144         std::string postMessage;\r
145         postMessage += "username=";\r
146         postMessage += curl_escape(login.c_str(), 0);\r
147         postMessage += "&password=";\r
148         postMessage += curl_escape(password.c_str(), 0);\r
149         postMessage += "&login=";\r
150         postMessage += curl_escape("1", 0);\r
151         postMessage += "&redirect=";\r
152         postMessage += curl_escape("../tests/login.php", 0);\r
153 \r
154         // Do the setup for libcurl\r
155         curl = curl_easy_init();\r
156 \r
157         if(curl)\r
158         {\r
159                 setcUrlOptions();\r
160                 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback); \r
161                 curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&xml_page);\r
162                 // use POST for login variables\r
163                 curl_easy_setopt(curl, CURLOPT_POST, TRUE);\r
164                 curl_easy_setopt(curl, CURLOPT_POSTFIELDS, postMessage.c_str());\r
165                 curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, -1);\r
166 \r
167                 // the url to get\r
168                 std::string login_url = "http://freesound.iua.upf.edu/forum/login.php";\r
169                 curl_easy_setopt(curl, CURLOPT_URL, login_url.c_str() );\r
170 \r
171                 // perform online request\r
172                 CURLcode res = curl_easy_perform(curl);\r
173                 // if something goes wrong 'connection' is set to '0'\r
174                 if(strcmp(curl_easy_strerror(res), "no error") == 0) connection = 1;\r
175                 else connection = 0;\r
176 \r
177                 if (connection == 1){\r
178                         std::string check_page = xml_page.memory;\r
179                         int test = (int)check_page.find("login");   //logged\r
180                         if(     test != -1){\r
181                                 sprintf(message, "Login: %s\n", xml_page.memory);\r
182                                 toLog(message);\r
183                         }\r
184                         else {\r
185                                 sprintf(message, "Login: Check username and password\n");\r
186                                 toLog(message);\r
187                                 connection = 0;\r
188                         }\r
189                 }\r
190 \r
191                 // free the memory\r
192                 if(xml_page.memory){            \r
193                         free( xml_page.memory );\r
194                         xml_page.memory = NULL;\r
195                         xml_page.size = NULL;\r
196                 }\r
197 \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 = NULL;\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://freesound.iua.upf.edu/searchTextXML.php";\r
238                         curl_easy_setopt(curl, CURLOPT_URL, search_url.c_str());\r
239 \r
240                         // perform the online search \r
241                         CURLcode res = curl_easy_perform(curl);\r
242                         if(strcmp(curl_easy_strerror(res), "no error") == 0) connection = 1;\r
243                         else connection = 0;\r
244                         \r
245                         result = xml_page.memory;\r
246 //printf("%s/n", result.c_str());\r
247 \r
248                         // free the memory\r
249                         if(xml_page.memory){\r
250                                 free( xml_page.memory );\r
251                                 xml_page.memory = NULL;\r
252                                 xml_page.size = NULL;\r
253                         }\r
254 \r
255                 }\r
256         }\r
257 \r
258         return result;\r
259 }\r
260 \r
261 //------------------------------------------------------------------------\r
262 std::string Mootcher::changeExtension(std::string filename)\r
263 {\r
264         std::string aiff = ".aiff";\r
265         std::string aif = ".aif";\r
266         std::string wav = ".wav";\r
267         std::string mp3 = ".mp3";\r
268         std::string ogg = ".ogg";\r
269         std::string flac = ".flac";\r
270 \r
271         std::string replace = ".xml";\r
272         int pos = 0;\r
273 \r
274         pos = (int)filename.find(aiff);\r
275         if(pos != std::string::npos) filename.replace(pos, aiff.size(), replace); \r
276         pos = (int)filename.find(aif);\r
277         if(pos != std::string::npos) filename.replace(pos, aif.size(), replace); \r
278         pos = (int)filename.find(wav);\r
279         if(pos != std::string::npos) filename.replace(pos, wav.size(), replace); \r
280         pos = (int)filename.find(mp3);\r
281         if(pos != std::string::npos) filename.replace(pos, mp3.size(), replace); \r
282         pos = (int)filename.find(ogg);\r
283         if(pos != std::string::npos) filename.replace(pos, ogg.size(), replace); \r
284         pos = (int)filename.find(flac);\r
285         if(pos != std::string::npos) filename.replace(pos, flac.size(), replace); \r
286 \r
287         return filename;\r
288 }\r
289 //------------------------------------------------------------------------\r
290 void            Mootcher::GetXml(std::string ID, struct MemoryStruct &xml_page)\r
291 {\r
292 \r
293         if(curl) {\r
294                 setcUrlOptions();\r
295                 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback); \r
296                 curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&xml_page);\r
297 \r
298                 // URL to get\r
299                 std::string getxml_url = "http://freesound.iua.upf.edu/samplesViewSingleXML.php?id=";\r
300                 getxml_url += ID;\r
301 \r
302                 curl_easy_setopt(curl, CURLOPT_URL, getxml_url.c_str() );\r
303                 \r
304                 // get it!\r
305                 CURLcode res = curl_easy_perform(curl);\r
306                 if(strcmp(curl_easy_strerror(res), "no error") == 0) connection = 1;\r
307                 else connection = 0;\r
308         }\r
309 }\r
310 //------------------------------------------------------------------------\r
311 std::string     Mootcher::getXmlFile(std::string ID, int &length)\r
312 {\r
313         struct MemoryStruct xml_page;\r
314         xml_page.memory = NULL;\r
315         xml_page.size = NULL;\r
316 \r
317         std::string xmlFileName;\r
318         std::string audioFileName;\r
319         std::string filename;\r
320         \r
321         if(connection != 0) {\r
322                 // download the xmlfile into xml_page\r
323                 GetXml(ID, xml_page);\r
324 \r
325                 // if sample ID does not exist on the freesound website\r
326                 if(strcmp(xml_page.memory, "sample non existant") == 0){\r
327                         free( xml_page.memory );\r
328                         sprintf(message, "getXmlFile: sample with ID:%s does not exist!\n", ID.c_str() );\r
329                         toLog(message);\r
330                         return filename;\r
331                 } else {\r
332                         XMLTree doc;\r
333                         doc.read_buffer( xml_page.memory );\r
334                         XMLNode *freesound = doc.root();\r
335                         \r
336                         // if the page is not a valid xml document with a 'freesound' root\r
337                         if( freesound == NULL){\r
338                                 sprintf(message, "getXmlFile: There is no valid root in the xml file");\r
339                                 toLog(message);\r
340                         } else {\r
341                                 XMLNode *sample = freesound->child("sample");\r
342                                 XMLNode *name = NULL;\r
343                                 XMLNode *filesize = NULL;\r
344                                 if (sample) {\r
345                                         name = sample->child("originalFilename");\r
346                                         filesize = sample->child("filesize");\r
347                                 }\r
348                                 \r
349                                 // get the file name and size from xml file\r
350                                 if (sample && name && filesize) {\r
351                                         \r
352                                         audioFileName = name->child("text")->content();\r
353                                         sprintf( message, "getXmlFile: %s needs to be downloaded\n", audioFileName.c_str() );\r
354                                         toLog(message);\r
355 \r
356                                         length = atoi(filesize->child("text")->content().c_str());\r
357 \r
358                                         // create new filename with the ID number\r
359                                         filename = basePath;\r
360                                         filename += "snd/";\r
361                                         filename += sample->property("id")->value();\r
362                                         filename += "-";\r
363                                         filename += audioFileName;\r
364                                         // change the extention into .xml\r
365                                         xmlFileName = changeExtension( filename );\r
366 \r
367                                         sprintf(message, "getXmlFile: saving XML: %s\n", xmlFileName.c_str() );\r
368                                         toLog(message);\r
369                                         \r
370                                         // save the xml file to disk\r
371                                         doc.write(xmlFileName.c_str());\r
372 \r
373                                         //store all the tags in the database\r
374                                         XMLNode *tags = sample->child("tags");\r
375                                         if (tags) {\r
376                                                 XMLNodeList children = tags->children();\r
377                                                 XMLNodeConstIterator niter;\r
378                                                 vector<string> strings;\r
379                                                 for (niter = children.begin(); niter != children.end(); ++niter) {\r
380                                                         XMLNode *node = *niter;\r
381                                                         if( strcmp( node->name().c_str(), "tag") == 0 ) {\r
382                                                                 XMLNode *text = node->child("text");\r
383                                                                 if (text) strings.push_back(text->content());\r
384                                                         }\r
385                                                 }\r
386                                                 ARDOUR::Library->set_tags (string("//")+filename, strings);\r
387                                                 ARDOUR::Library->save_changes ();\r
388                                         }\r
389                                 }\r
390                                 \r
391                                 // clear the memory\r
392                                 if(xml_page.memory){\r
393                                         free( xml_page.memory );\r
394                                         xml_page.memory = NULL;\r
395                                         xml_page.size = 0;\r
396                                 }\r
397                                 return audioFileName;\r
398                         }\r
399                 }\r
400         }\r
401         else {\r
402                 return audioFileName;\r
403         }\r
404 \r
405 }\r
406 \r
407 int audioFileWrite(void *buffer, size_t size, size_t nmemb, void *file)\r
408 {\r
409         return (int)fwrite(buffer, size, nmemb, (FILE*) file);\r
410 };\r
411 \r
412 //------------------------------------------------------------------------\r
413 std::string     Mootcher::getFile(std::string ID)\r
414 {\r
415         CURLcode result_curl;\r
416 \r
417         std::string audioFileName;\r
418 \r
419         if(connection != 0)\r
420         {\r
421                 int length;\r
422                 std::string name = getXmlFile(ID, length);\r
423                 if( name != "" ){\r
424 \r
425                         // create new filename with the ID number\r
426                         audioFileName += basePath;\r
427                         audioFileName += "snd/";\r
428                         audioFileName += ID;\r
429                         audioFileName += "-";                   \r
430                         audioFileName += name;\r
431                         \r
432                         //check to see if audio file already exists\r
433                         FILE *testFile = fopen(audioFileName.c_str(), "r");\r
434                         if (testFile) {  //TODO:  should also check length to see if file is complete\r
435                                 fseek (testFile , 0 , SEEK_END);\r
436                                 if (ftell (testFile) == length) {\r
437                                         sprintf(message, "%s already exists\n", audioFileName.c_str() );\r
438                                         toLog(message);\r
439                                         fclose (testFile);\r
440                                         return audioFileName;\r
441                                 } else {\r
442                                         remove( audioFileName.c_str() );  //file was not correct length, delete it and try again\r
443                                 }                                       \r
444                         }\r
445                         \r
446 \r
447                         //now download the actual file\r
448                         if (curl) {\r
449 \r
450                                 FILE* theFile;\r
451                                 theFile = fopen( audioFileName.c_str(), "wb" );\r
452 \r
453                                 // create the download url, this url will also update the download statics on the site\r
454                                 std::string audioURL;\r
455                                 audioURL += "http://freesound.iua.upf.edu/samplesDownload.php?id=";\r
456                                 audioURL += ID;\r
457 \r
458                                 setcUrlOptions();\r
459                                 curl_easy_setopt(curl, CURLOPT_URL, audioURL.c_str() );\r
460                                 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, audioFileWrite);\r
461                                 curl_easy_setopt(curl, CURLOPT_WRITEDATA, theFile);\r
462                                 CURLcode res = curl_easy_perform(curl);\r
463                                 if(strcmp(curl_easy_strerror(res), "no error") == 0) connection = 1;\r
464                                 else connection = 0;\r
465 \r
466                                 fclose(theFile);\r
467                         }\r
468         \r
469 /*\r
470                         bar.dlnowMoo = 0;\r
471                         bar.dltotalMoo = 0;\r
472                         curl_easy_setopt (curl, CURLOPT_NOPROGRESS, 0); // turn on the process bar thingy\r
473                         curl_easy_setopt (curl, CURLOPT_PROGRESSFUNCTION, progress_callback);\r
474                         curl_easy_setopt (curl, CURLOPT_PROGRESSDATA, &bar);\r
475 */                              \r
476                 }\r
477         }\r
478 \r
479         return audioFileName;\r
480 }\r
481 \r
482 //---------\r
483 int Mootcher::progress_callback(void *bar, double dltotal, double dlnow, double ultotal, double ulnow)\r
484 {\r
485         struct dlprocess *lbar = (struct dlprocess *) bar;\r
486         lbar->dltotalMoo = dltotal;\r
487         lbar->dlnowMoo = dlnow;\r
488         return 0;\r
489 }\r