1 /* sfdb_freesound_mootcher.cpp **********************************************************************
3 Adapted for Ardour by Ben Loftis, March 2008
7 Mootcher Online Access to thefreesoundproject website
8 http://freesound.iua.upf.edu/
11 mail for questions/remarks: mootcher@twistedlemon.nl
12 or go to the freesound website forum
14 -----------------------------------------------------------------
17 curl.h (version 7.14.0)
21 -----------------------------------------------------------------
24 This program is free software; you can redistribute it and/or
25 modify it under the terms of the GNU General Public License
26 as published by the Free Software Foundation; either version 2
27 of the License, or (at your option) any later version.
29 This program is distributed in the hope that it will be useful,
30 but WITHOUT ANY WARRANTY; without even the implied warranty of
31 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
32 GNU General Public License for more details.
34 You should have received a copy of the GNU General Public License
35 along with this program; if not, write to the Free Software
36 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
39 *************************************************************************************/
40 #include "sfdb_freesound_mootcher.h"
42 #include "pbd/xml++.h"
45 #include <sys/types.h>
47 #include "ardour/audio_library.h"
51 //------------------------------------------------------------------------
52 Mootcher:: Mootcher(const char *saveLocation
)
56 changeWorkingDir(saveLocation
);
58 //------------------------------------------------------------------------
59 Mootcher:: ~Mootcher()
61 remove( "cookiejar.txt" );
63 //------------------------------------------------------------------------
64 const char* Mootcher::changeWorkingDir(const char *saveLocation
)
66 basePath
= saveLocation
;
68 std::string replace
= "/";
69 int pos
= (int)basePath
.find("\\");
70 while( pos
!= std::string::npos
){
71 basePath
.replace(pos
, 1, replace
);
72 pos
= (int)basePath
.find("\\");
76 int pos2
= basePath
.find_last_of("/");
77 if(basePath
.length() != (pos2
+1)) basePath
+= "/";
79 // create Freesound directory and sound dir
80 std::string sndLocation
= basePath
;
81 mkdir(sndLocation
.c_str(), 0777);
83 mkdir(sndLocation
.c_str(), 0777);
85 return basePath
.c_str();
88 //------------------------------------------------------------------------
89 size_t Mootcher::WriteMemoryCallback(void *ptr
, size_t size
, size_t nmemb
, void *data
)
91 register int realsize
= (int)(size
* nmemb
);
92 struct MemoryStruct
*mem
= (struct MemoryStruct
*)data
;
94 // There might be a realloc() out there that doesn't like
95 // reallocing NULL pointers, so we take care of it here
96 if(mem
->memory
) mem
->memory
= (char *)realloc(mem
->memory
, mem
->size
+ realsize
+ 1);
97 else mem
->memory
= (char *)malloc(mem
->size
+ realsize
+ 1);
100 memcpy(&(mem
->memory
[mem
->size
]), ptr
, realsize
);
101 mem
->size
+= realsize
;
102 mem
->memory
[mem
->size
] = 0;
108 //------------------------------------------------------------------------
109 void Mootcher::toLog(std::string input
)
111 printf("%s\n", input
.c_str());// for debugging
115 //------------------------------------------------------------------------
116 void Mootcher::setcUrlOptions()
118 // basic init for curl
119 curl_global_init(CURL_GLOBAL_ALL
);
120 // some servers don't like requests that are made without a user-agent field, so we provide one
121 curl_easy_setopt(curl
, CURLOPT_USERAGENT
, "libcurl-agent/1.0");
122 // setup curl error buffer
123 CURLcode res
= curl_easy_setopt(curl
, CURLOPT_ERRORBUFFER
, errorBuffer
);
124 // always use the cookie with session id which is received at the login
125 curl_easy_setopt(curl
, CURLOPT_COOKIEFILE
, "cookiejar.txt");
127 curl_easy_setopt(curl
, CURLOPT_FOLLOWLOCATION
, 1);
130 //------------------------------------------------------------------------
131 int Mootcher::doLogin(std::string login
, std::string password
)
136 struct MemoryStruct xml_page
;
137 xml_page
.memory
= NULL
;
140 // create the post message from the login and password
141 std::string postMessage
;
142 postMessage
+= "username=";
143 postMessage
+= curl_escape(login
.c_str(), 0);
144 postMessage
+= "&password=";
145 postMessage
+= curl_escape(password
.c_str(), 0);
146 postMessage
+= "&login=";
147 postMessage
+= curl_escape("1", 0);
148 postMessage
+= "&redirect=";
149 postMessage
+= curl_escape("../tests/login.php", 0);
151 // Do the setup for libcurl
152 curl
= curl_easy_init();
157 curl_easy_setopt(curl
, CURLOPT_WRITEFUNCTION
, WriteMemoryCallback
);
158 curl_easy_setopt(curl
, CURLOPT_WRITEDATA
, (void *)&xml_page
);
159 // save the sessoin id that is given back by the server in a cookie
160 curl_easy_setopt(curl
, CURLOPT_COOKIEJAR
, "cookiejar.txt");
161 // use POST for login variables
162 curl_easy_setopt(curl
, CURLOPT_POST
, TRUE
);
163 curl_easy_setopt(curl
, CURLOPT_POSTFIELDS
, postMessage
.c_str());
164 curl_easy_setopt(curl
, CURLOPT_POSTFIELDSIZE
, -1);
167 std::string login_url
= "http://www.freesound.org/forum/login.php";
168 curl_easy_setopt(curl
, CURLOPT_URL
, login_url
.c_str() );
170 // perform online request
172 CURLcode res
= curl_easy_perform(curl
);
174 toLog("curl login error\n");
175 toLog(curl_easy_strerror(res
));
179 if (connection
== 1){
180 std::string check_page
= xml_page
.memory
;
181 int test
= (int)check_page
.find("login"); //logged
182 if( strcmp(xml_page
.memory
, "login") == 0 )
183 toLog("Logged in.\n");
185 toLog("Login failed: Check username and password.\n");
192 free( xml_page
.memory
);
193 xml_page
.memory
= NULL
;
197 std::cerr
<< "Login was cool, connection = " << connection
<< std::endl
;
200 else return 3; // will be returned if a curl related problem ocurrs
202 //------------------------------------------------------------------------
203 std::string
Mootcher::searchText(std::string word
)
205 struct MemoryStruct xml_page
;
206 xml_page
.memory
= NULL
;
213 // create a url encoded post message
214 std::string postMessage
;
215 char tempString
[ 128 ];
216 char *tempPointer
= &tempString
[0];
218 postMessage
= "search=";
219 postMessage
+= curl_escape(word
.c_str(), 0);
220 sprintf( tempPointer
, "&searchDescriptions=1");
221 postMessage
+= tempPointer
;
222 sprintf( tempPointer
, "&searchtags=1");
223 postMessage
+= tempPointer
;
225 // Ref: http://www.freesound.org/forum/viewtopic.php?p=19081
226 // const ORDER_DEFAULT = 0;
227 // const ORDER_DOWNLOADS_DESC = 1;
228 // const ORDER_DOWNLOADS_ASC = 2;
229 // const ORDER_USERNAME_DESC = 3;
230 // const ORDER_USERNAME_ASC = 4;
231 // const ORDER_DATE_DESC = 5;
232 // const ORDER_DATE_ASC = 6;
233 // const ORDER_DURATION_DESC = 7;
234 // const ORDER_DURATION_ASC = 8;
235 // const ORDER_FILEFORMAT_DESC = 9;
236 // const ORDER_FILEFORMAT_ASC = 10;
237 sprintf( tempPointer
, "&order=1");
238 postMessage
+= tempPointer
;
239 sprintf( tempPointer
, "&start=0");
240 postMessage
+= tempPointer
;
241 sprintf( tempPointer
, "&limit=10");
242 postMessage
+= tempPointer
;
243 // The limit of 10 samples is arbitrary, but seems
244 // reasonable in light of the fact that all of the returned
245 // samples get downloaded, and downloads are s-l-o-w.
249 // basic init for curl
251 curl_easy_setopt(curl
, CURLOPT_WRITEFUNCTION
, WriteMemoryCallback
);
252 curl_easy_setopt(curl
, CURLOPT_WRITEDATA
, (void *)&xml_page
);
253 // setup the post message
254 curl_easy_setopt(curl
, CURLOPT_POST
, TRUE
);
255 curl_easy_setopt(curl
, CURLOPT_POSTFIELDS
, postMessage
.c_str());
256 curl_easy_setopt(curl
, CURLOPT_POSTFIELDSIZE
, -1);
259 std::string search_url
= "http://www.freesound.org/searchTextXML.php";
260 curl_easy_setopt(curl
, CURLOPT_URL
, search_url
.c_str());
262 // perform the online search
264 CURLcode res
= curl_easy_perform(curl
);
266 toLog("curl login error\n");
267 toLog(curl_easy_strerror(res
));
271 result
= xml_page
.memory
;
272 toLog( result
.c_str() );
276 free( xml_page
.memory
);
277 xml_page
.memory
= NULL
;
287 //------------------------------------------------------------------------
288 std::string
Mootcher::changeExtension(std::string filename
)
290 std::string aiff
= ".aiff";
291 std::string aif
= ".aif";
292 std::string wav
= ".wav";
293 std::string mp3
= ".mp3";
294 std::string ogg
= ".ogg";
295 std::string flac
= ".flac";
297 std::string replace
= ".xml";
300 pos
= (int)filename
.find(aiff
);
301 if(pos
!= std::string::npos
) filename
.replace(pos
, aiff
.size(), replace
);
302 pos
= (int)filename
.find(aif
);
303 if(pos
!= std::string::npos
) filename
.replace(pos
, aif
.size(), replace
);
304 pos
= (int)filename
.find(wav
);
305 if(pos
!= std::string::npos
) filename
.replace(pos
, wav
.size(), replace
);
306 pos
= (int)filename
.find(mp3
);
307 if(pos
!= std::string::npos
) filename
.replace(pos
, mp3
.size(), replace
);
308 pos
= (int)filename
.find(ogg
);
309 if(pos
!= std::string::npos
) filename
.replace(pos
, ogg
.size(), replace
);
310 pos
= (int)filename
.find(flac
);
311 if(pos
!= std::string::npos
) filename
.replace(pos
, flac
.size(), replace
);
315 //------------------------------------------------------------------------
316 void Mootcher::GetXml(std::string ID
, struct MemoryStruct
&xml_page
)
321 curl_easy_setopt(curl
, CURLOPT_WRITEFUNCTION
, WriteMemoryCallback
);
322 curl_easy_setopt(curl
, CURLOPT_WRITEDATA
, (void *)&xml_page
);
325 std::string getxml_url
= "http://www.freesound.org/samplesViewSingleXML.php?id=";
328 curl_easy_setopt(curl
, CURLOPT_URL
, getxml_url
.c_str() );
332 CURLcode res
= curl_easy_perform(curl
);
334 toLog("curl login error\n");
335 toLog(curl_easy_strerror(res
));
340 //------------------------------------------------------------------------
341 std::string
Mootcher::getXmlFile(std::string ID
, int &length
)
343 struct MemoryStruct xml_page
;
344 xml_page
.memory
= NULL
;
345 xml_page
.size
= NULL
;
347 std::string xmlFileName
;
348 std::string audioFileName
;
349 std::string filename
;
351 if(connection
!= 0) {
352 // download the xmlfile into xml_page
353 GetXml(ID
, xml_page
);
355 // if sample ID does not exist on the freesound website
356 if(strcmp(xml_page
.memory
, "sample non existant") == 0){
357 free( xml_page
.memory
);
358 sprintf(message
, "getXmlFile: sample with ID:%s does not exist!\n", ID
.c_str() );
363 doc
.read_buffer( xml_page
.memory
);
364 XMLNode
*freesound
= doc
.root();
366 // if the page is not a valid xml document with a 'freesound' root
367 if( freesound
== NULL
){
368 sprintf(message
, "getXmlFile: There is no valid root in the xml file");
371 XMLNode
*sample
= freesound
->child("sample");
372 XMLNode
*name
= NULL
;
373 XMLNode
*filesize
= NULL
;
375 name
= sample
->child("originalFilename");
376 filesize
= sample
->child("filesize");
379 // get the file name and size from xml file
380 if (sample
&& name
&& filesize
) {
382 audioFileName
= name
->child("text")->content();
383 sprintf( message
, "getXmlFile: %s needs to be downloaded\n", audioFileName
.c_str() );
386 length
= atoi(filesize
->child("text")->content().c_str());
388 // create new filename with the ID number
391 filename
+= sample
->property("id")->value();
393 filename
+= audioFileName
;
394 // change the extention into .xml
395 xmlFileName
= changeExtension( filename
);
397 sprintf(message
, "getXmlFile: saving XML: %s\n", xmlFileName
.c_str() );
400 // save the xml file to disk
401 doc
.write(xmlFileName
.c_str());
403 //store all the tags in the database
404 XMLNode
*tags
= sample
->child("tags");
406 XMLNodeList children
= tags
->children();
407 XMLNodeConstIterator niter
;
408 std::vector
<std::string
> strings
;
409 for (niter
= children
.begin(); niter
!= children
.end(); ++niter
) {
410 XMLNode
*node
= *niter
;
411 if( strcmp( node
->name().c_str(), "tag") == 0 ) {
412 XMLNode
*text
= node
->child("text");
413 if (text
) strings
.push_back(text
->content());
416 ARDOUR::Library
->set_tags (std::string("//")+filename
, strings
);
417 ARDOUR::Library
->save_changes ();
423 free( xml_page
.memory
);
424 xml_page
.memory
= NULL
;
427 return audioFileName
;
432 return audioFileName
;
437 int audioFileWrite(void *buffer
, size_t size
, size_t nmemb
, void *file
)
439 return (int)fwrite(buffer
, size
, nmemb
, (FILE*) file
);
442 //------------------------------------------------------------------------
443 std::string
Mootcher::getFile(std::string ID
)
445 CURLcode result_curl
;
447 std::string audioFileName
;
452 std::string name
= getXmlFile(ID
, length
);
455 // create new filename with the ID number
456 audioFileName
+= basePath
;
457 audioFileName
+= "snd/";
459 audioFileName
+= "-";
460 audioFileName
+= name
;
462 //check to see if audio file already exists
463 FILE *testFile
= fopen(audioFileName
.c_str(), "r");
464 if (testFile
) { //TODO: should also check length to see if file is complete
465 fseek (testFile
, 0 , SEEK_END
);
466 if (ftell (testFile
) == length
) {
467 sprintf(message
, "%s already exists\n", audioFileName
.c_str() );
470 return audioFileName
;
472 remove( audioFileName
.c_str() ); //file was not correct length, delete it and try again
477 //now download the actual file
481 theFile
= fopen( audioFileName
.c_str(), "wb" );
483 // create the download url, this url will also update the download statics on the site
484 std::string audioURL
;
485 audioURL
+= "http://www.freesound.org/samplesDownload.php?id=";
489 curl_easy_setopt(curl
, CURLOPT_URL
, audioURL
.c_str() );
490 curl_easy_setopt(curl
, CURLOPT_WRITEFUNCTION
, audioFileWrite
);
491 curl_easy_setopt(curl
, CURLOPT_WRITEDATA
, theFile
);
494 CURLcode res
= curl_easy_perform(curl
);
496 toLog("curl login error\n");
497 toLog(curl_easy_strerror(res
));
507 curl_easy_setopt (curl, CURLOPT_NOPROGRESS, 0); // turn on the process bar thingy
508 curl_easy_setopt (curl, CURLOPT_PROGRESSFUNCTION, progress_callback);
509 curl_easy_setopt (curl, CURLOPT_PROGRESSDATA, &bar);
514 return audioFileName
;
518 int Mootcher::progress_callback(void *bar
, double dltotal
, double dlnow
, double ultotal
, double ulnow
)
520 struct dlprocess
*lbar
= (struct dlprocess
*) bar
;
521 lbar
->dltotalMoo
= dltotal
;
522 lbar
->dlnowMoo
= dlnow
;